diff --git a/Everything_SDK-2003.sln b/Everything_SDK-2003.sln index 7909ae42..17e23ecb 100644 --- a/Everything_SDK-2003.sln +++ b/Everything_SDK-2003.sln @@ -110,14 +110,20 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "tier1", "tier1\tier1-2003.v ProjectSection(ProjectDependencies) = postProject EndProjectSection EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "phonemeextractor", "utils\phonemeextractor\phonemeextractor-2003.vcproj", "{8A35F55B-B5C2-47A0-8C4A-5857A8E20385}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "choreoobjects", "choreoobjects\choreoobjects-2003.vcproj", "{E1DA8DB8-FB4C-4B14-91A6-98BCED6B9720}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "hlmv", "utils\hlmv\hlmv-2003.vcproj", "{F3704DBF-8055-413C-B027-399E5DBCA4DD}" ProjectSection(ProjectDependencies) = postProject {E1DA8DB8-FB4C-4B14-91A6-98BCED6B9720} = {E1DA8DB8-FB4C-4B14-91A6-98BCED6B9720} {E1DA9FB8-FB4C-4B14-91A6-98BCED6B9720} = {E1DA9FB8-FB4C-4B14-91A6-98BCED6B9720} EndProjectSection EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "hlmv", "utils\hlmv\hlmv-2003.vcproj", "{F3704DBF-8055-413C-B027-399E5DBCA4DD}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "phonemeextractor", "utils\phonemeextractor\phonemeextractor-2003.vcproj", "{8A35F55B-B5C2-47A0-8C4A-5857A8E20385}" ProjectSection(ProjectDependencies) = postProject + {E1DA8DB8-FB4C-4B14-91A6-98BCED6B9720} = {E1DA8DB8-FB4C-4B14-91A6-98BCED6B9720} + {E1DA9FB8-FB4C-4B14-91A6-98BCED6B9720} = {E1DA9FB8-FB4C-4B14-91A6-98BCED6B9720} EndProjectSection EndProject Global @@ -212,14 +218,18 @@ Global {E1DA8DB8-FB4C-4B14-91A6-98BCED6B9720}.Debug.Build.0 = Debug|Win32 {E1DA8DB8-FB4C-4B14-91A6-98BCED6B9720}.Release.ActiveCfg = Release|Win32 {E1DA8DB8-FB4C-4B14-91A6-98BCED6B9720}.Release.Build.0 = Release|Win32 - {8A35F55B-B5C2-47A0-8C4A-5857A8E20385}.Debug.ActiveCfg = Debug|Win32 - {8A35F55B-B5C2-47A0-8C4A-5857A8E20385}.Debug.Build.0 = Debug|Win32 - {8A35F55B-B5C2-47A0-8C4A-5857A8E20385}.Release.ActiveCfg = Release|Win32 - {8A35F55B-B5C2-47A0-8C4A-5857A8E20385}.Release.Build.0 = Release|Win32 + {E1DA8DB8-FB4C-4B14-91A6-98BCED6B9720}.Debug.ActiveCfg = Debug|Win32 + {E1DA8DB8-FB4C-4B14-91A6-98BCED6B9720}.Debug.Build.0 = Debug|Win32 + {E1DA8DB8-FB4C-4B14-91A6-98BCED6B9720}.Release.ActiveCfg = Release|Win32 + {E1DA8DB8-FB4C-4B14-91A6-98BCED6B9720}.Release.Build.0 = Release|Win32 {F3704DBF-8055-413C-B027-399E5DBCA4DD}.Debug.ActiveCfg = Debug|Win32 {F3704DBF-8055-413C-B027-399E5DBCA4DD}.Debug.Build.0 = Debug|Win32 {F3704DBF-8055-413C-B027-399E5DBCA4DD}.Release.ActiveCfg = Release|Win32 {F3704DBF-8055-413C-B027-399E5DBCA4DD}.Release.Build.0 = Release|Win32 + {8A35F55B-B5C2-47A0-8C4A-5857A8E20385}.Debug.ActiveCfg = Debug|Win32 + {8A35F55B-B5C2-47A0-8C4A-5857A8E20385}.Debug.Build.0 = Debug|Win32 + {8A35F55B-B5C2-47A0-8C4A-5857A8E20385}.Release.ActiveCfg = Release|Win32 + {8A35F55B-B5C2-47A0-8C4A-5857A8E20385}.Release.Build.0 = Release|Win32 EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution EndGlobalSection diff --git a/Everything_SDK-2005.sln b/Everything_SDK-2005.sln index 18e77466..cfea827d 100644 --- a/Everything_SDK-2005.sln +++ b/Everything_SDK-2005.sln @@ -2,38 +2,38 @@ Microsoft Visual Studio Solution File, Format Version 9.00 # Visual Studio 2005 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "glview", "utils\glview\glview-2005.vcproj", "{BD1604CA-F401-4C4B-821C-251F5AE157FE}" ProjectSection(ProjectDependencies) = postProject - {E1DA9FB8-FB4C-4B14-91A6-98BCED6B9720} = {E1DA9FB8-FB4C-4B14-91A6-98BCED6B9720} {E1DA8DB8-FB4C-4B14-91A6-98BCED6B9720} = {E1DA8DB8-FB4C-4B14-91A6-98BCED6B9720} + {E1DA9FB8-FB4C-4B14-91A6-98BCED6B9720} = {E1DA9FB8-FB4C-4B14-91A6-98BCED6B9720} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "height2normal", "utils\height2normal\height2normal-2005.vcproj", "{0FDD99E4-130F-493C-8202-4C0236CC47EA}" ProjectSection(ProjectDependencies) = postProject - {E1DA8DB8-FB4C-4B14-91A6-98BCED6B9720} = {E1DA8DB8-FB4C-4B14-91A6-98BCED6B9720} {E1DA9FB8-FB4C-4B14-91A6-98BCED6B9720} = {E1DA9FB8-FB4C-4B14-91A6-98BCED6B9720} + {E1DA8DB8-FB4C-4B14-91A6-98BCED6B9720} = {E1DA8DB8-FB4C-4B14-91A6-98BCED6B9720} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "serverplugin_empty", "utils\serverplugin_sample\serverplugin_empty-2005.vcproj", "{B6572CBE-7C7F-4FFF-94DE-AFBBBAB11C64}" ProjectSection(ProjectDependencies) = postProject - {E1DA8DB8-FB4C-4B14-91A6-98BCED6B9720} = {E1DA8DB8-FB4C-4B14-91A6-98BCED6B9720} {E1DA9FB8-FB4C-4B14-91A6-98BCED6B9720} = {E1DA9FB8-FB4C-4B14-91A6-98BCED6B9720} + {E1DA8DB8-FB4C-4B14-91A6-98BCED6B9720} = {E1DA8DB8-FB4C-4B14-91A6-98BCED6B9720} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "studiomdl", "utils\studiomdl\studiomdl-2005.vcproj", "{AE28536E-885A-41BF-99A4-41A7E424B869}" ProjectSection(ProjectDependencies) = postProject - {E1DA9FB8-FB4C-4B14-91A6-98BCED6B9720} = {E1DA9FB8-FB4C-4B14-91A6-98BCED6B9720} {E1DA8DB8-FB4C-4B14-91A6-98BCED6B9720} = {E1DA8DB8-FB4C-4B14-91A6-98BCED6B9720} + {E1DA9FB8-FB4C-4B14-91A6-98BCED6B9720} = {E1DA9FB8-FB4C-4B14-91A6-98BCED6B9720} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "tgadiff", "utils\tgadiff\tgadiff-2005.vcproj", "{0CE0AF8A-A977-4538-9D63-BCB76DE1BAC6}" ProjectSection(ProjectDependencies) = postProject - {E1DA8DB8-FB4C-4B14-91A6-98BCED6B9720} = {E1DA8DB8-FB4C-4B14-91A6-98BCED6B9720} {E1DA9FB8-FB4C-4B14-91A6-98BCED6B9720} = {E1DA9FB8-FB4C-4B14-91A6-98BCED6B9720} + {E1DA8DB8-FB4C-4B14-91A6-98BCED6B9720} = {E1DA8DB8-FB4C-4B14-91A6-98BCED6B9720} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "vbsp", "utils\vbsp\vbsp-2005.vcproj", "{B78B6271-B19A-4CF6-926E-40B643548E23}" ProjectSection(ProjectDependencies) = postProject - {E1DA9FB8-FB4C-4B14-91A6-98BCED6B9720} = {E1DA9FB8-FB4C-4B14-91A6-98BCED6B9720} {E1DA8DB8-FB4C-4B14-91A6-98BCED6B9720} = {E1DA8DB8-FB4C-4B14-91A6-98BCED6B9720} + {E1DA9FB8-FB4C-4B14-91A6-98BCED6B9720} = {E1DA9FB8-FB4C-4B14-91A6-98BCED6B9720} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "vice", "utils\vice\vice-2005.vcproj", "{3CE6E7A9-89EC-4304-8D72-5B602B4DBD09}" @@ -48,8 +48,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "vprojtomake", "utils\vprojt EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "vrad", "utils\vrad\vrad-2005.vcproj", "{FC0F5DE3-F09F-4EF6-98F9-BA762FFF268D}" ProjectSection(ProjectDependencies) = postProject - {E1DA8DB8-FB4C-4B14-91A6-98BCED6B9720} = {E1DA8DB8-FB4C-4B14-91A6-98BCED6B9720} {E1DA9FB8-FB4C-4B14-91A6-98BCED6B9720} = {E1DA9FB8-FB4C-4B14-91A6-98BCED6B9720} + {E1DA8DB8-FB4C-4B14-91A6-98BCED6B9720} = {E1DA8DB8-FB4C-4B14-91A6-98BCED6B9720} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "vrad_launcher", "utils\vrad_launcher\vrad_launcher-2005.vcproj", "{914F19DF-64EC-4E7D-8B01-76477BF06479}" @@ -59,20 +59,20 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "vrad_launcher", "utils\vrad EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "vtf2tga", "utils\vtf2tga\vtf2tga-2005.vcproj", "{2A1F656C-053C-46A4-AE33-304C1D865E0C}" ProjectSection(ProjectDependencies) = postProject - {E1DA8DB8-FB4C-4B14-91A6-98BCED6B9720} = {E1DA8DB8-FB4C-4B14-91A6-98BCED6B9720} {E1DA9FB8-FB4C-4B14-91A6-98BCED6B9720} = {E1DA9FB8-FB4C-4B14-91A6-98BCED6B9720} + {E1DA8DB8-FB4C-4B14-91A6-98BCED6B9720} = {E1DA8DB8-FB4C-4B14-91A6-98BCED6B9720} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "vtfdiff", "utils\vtfdiff\vtfdiff-2005.vcproj", "{0A368DE7-D34A-48D3-B517-996BFF2D0D5D}" ProjectSection(ProjectDependencies) = postProject - {E1DA9FB8-FB4C-4B14-91A6-98BCED6B9720} = {E1DA9FB8-FB4C-4B14-91A6-98BCED6B9720} {E1DA8DB8-FB4C-4B14-91A6-98BCED6B9720} = {E1DA8DB8-FB4C-4B14-91A6-98BCED6B9720} + {E1DA9FB8-FB4C-4B14-91A6-98BCED6B9720} = {E1DA9FB8-FB4C-4B14-91A6-98BCED6B9720} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "vvis", "utils\vvis\vvis-2005.vcproj", "{5B065E70-6EE0-4B47-BA64-113D2F81220D}" ProjectSection(ProjectDependencies) = postProject - {E1DA8DB8-FB4C-4B14-91A6-98BCED6B9720} = {E1DA8DB8-FB4C-4B14-91A6-98BCED6B9720} {E1DA9FB8-FB4C-4B14-91A6-98BCED6B9720} = {E1DA9FB8-FB4C-4B14-91A6-98BCED6B9720} + {E1DA8DB8-FB4C-4B14-91A6-98BCED6B9720} = {E1DA8DB8-FB4C-4B14-91A6-98BCED6B9720} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "vvis_launcher", "utils\vvis_launcher\vvis_launcher-2005.vcproj", "{4C0B9915-E8FF-4089-8927-FC934BFC1E4A}" @@ -91,8 +91,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "demoinfo", "utils\demoinfo\ EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "motionmapper", "utils\motionmapper\motionmapper-2005.vcproj", "{A882FC08-8B92-4D4F-89BF-75BCEC2BAE11}" ProjectSection(ProjectDependencies) = postProject - {E1DA9FB8-FB4C-4B14-91A6-98BCED6B9720} = {E1DA9FB8-FB4C-4B14-91A6-98BCED6B9720} {E1DA8DB8-FB4C-4B14-91A6-98BCED6B9720} = {E1DA8DB8-FB4C-4B14-91A6-98BCED6B9720} + {E1DA9FB8-FB4C-4B14-91A6-98BCED6B9720} = {E1DA9FB8-FB4C-4B14-91A6-98BCED6B9720} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "QC_Eyes", "utils\qc_eyes\QC_Eyes-2005.vcproj", "{D373436F-7DBF-468B-A3E4-601FB8556544}" @@ -101,14 +101,20 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mathlib", "mathlib\mathlib- EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "tier1", "tier1\tier1-2005.vcproj", "{E1DA8DB8-FB4C-4B14-91A6-98BCED6B9720}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "choreoobjects", "choreoobjects\choreoobjects-2005.vcproj", "{0A940BE3-0C70-4D6F-A9F8-C39B483E3796}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "hlmv", "utils\hlmv\hlmv-2005.vcproj", "{F3704DBF-8055-413C-B027-399E5DBCA4DD}" + ProjectSection(ProjectDependencies) = postProject + {E1DA9FB8-FB4C-4B14-91A6-98BCED6B9720} = {E1DA9FB8-FB4C-4B14-91A6-98BCED6B9720} + {E1DA8DB8-FB4C-4B14-91A6-98BCED6B9720} = {E1DA8DB8-FB4C-4B14-91A6-98BCED6B9720} + EndProjectSection +EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "phonemeextractor", "utils\phonemeextractor\phonemeextractor-2005.vcproj", "{8A35F55B-B5C2-47A0-8C4A-5857A8E20385}" ProjectSection(ProjectDependencies) = postProject {E1DA8DB8-FB4C-4B14-91A6-98BCED6B9720} = {E1DA8DB8-FB4C-4B14-91A6-98BCED6B9720} {E1DA9FB8-FB4C-4B14-91A6-98BCED6B9720} = {E1DA9FB8-FB4C-4B14-91A6-98BCED6B9720} EndProjectSection EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "hlmv", "utils\hlmv\hlmv-2005.vcproj", "{F3704DBF-8055-413C-B027-399E5DBCA4DD}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 @@ -199,14 +205,18 @@ Global {E1DA8DB8-FB4C-4B14-91A6-98BCED6B9720}.Debug|Win32.Build.0 = Debug|Win32 {E1DA8DB8-FB4C-4B14-91A6-98BCED6B9720}.Release|Win32.ActiveCfg = Release|Win32 {E1DA8DB8-FB4C-4B14-91A6-98BCED6B9720}.Release|Win32.Build.0 = Release|Win32 - {8A35F55B-B5C2-47A0-8C4A-5857A8E20385}.Debug|Win32.ActiveCfg = Debug|Win32 - {8A35F55B-B5C2-47A0-8C4A-5857A8E20385}.Debug|Win32.Build.0 = Debug|Win32 - {8A35F55B-B5C2-47A0-8C4A-5857A8E20385}.Release|Win32.ActiveCfg = Release|Win32 - {8A35F55B-B5C2-47A0-8C4A-5857A8E20385}.Release|Win32.Build.0 = Release|Win32 + {0A940BE3-0C70-4D6F-A9F8-C39B483E3796}.Debug|Win32.ActiveCfg = Debug|Win32 + {0A940BE3-0C70-4D6F-A9F8-C39B483E3796}.Debug|Win32.Build.0 = Debug|Win32 + {0A940BE3-0C70-4D6F-A9F8-C39B483E3796}.Release|Win32.ActiveCfg = Release|Win32 + {0A940BE3-0C70-4D6F-A9F8-C39B483E3796}.Release|Win32.Build.0 = Release|Win32 {F3704DBF-8055-413C-B027-399E5DBCA4DD}.Debug|Win32.ActiveCfg = Debug|Win32 {F3704DBF-8055-413C-B027-399E5DBCA4DD}.Debug|Win32.Build.0 = Debug|Win32 {F3704DBF-8055-413C-B027-399E5DBCA4DD}.Release|Win32.ActiveCfg = Release|Win32 {F3704DBF-8055-413C-B027-399E5DBCA4DD}.Release|Win32.Build.0 = Release|Win32 + {8A35F55B-B5C2-47A0-8C4A-5857A8E20385}.Debug|Win32.ActiveCfg = Debug|Win32 + {8A35F55B-B5C2-47A0-8C4A-5857A8E20385}.Debug|Win32.Build.0 = Debug|Win32 + {8A35F55B-B5C2-47A0-8C4A-5857A8E20385}.Release|Win32.ActiveCfg = Release|Win32 + {8A35F55B-B5C2-47A0-8C4A-5857A8E20385}.Release|Win32.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/choreoobjects/choreoactor.cpp b/choreoobjects/choreoactor.cpp new file mode 100644 index 00000000..cc63861a --- /dev/null +++ b/choreoobjects/choreoactor.cpp @@ -0,0 +1,293 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// +#include +#include +#include +#include "choreoactor.h" +#include "choreochannel.h" +#include "choreoscene.h" +#include "tier1/utlbuffer.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CChoreoActor::CChoreoActor( void ) +{ + Init(); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *name - +//----------------------------------------------------------------------------- +CChoreoActor::CChoreoActor( const char *name ) +{ + Init(); + SetName( name ); +} + +//----------------------------------------------------------------------------- +// Purpose: // Assignment +// Input : src - +// Output : CChoreoActor& +//----------------------------------------------------------------------------- +CChoreoActor& CChoreoActor::operator=( const CChoreoActor& src ) +{ + m_bActive = src.m_bActive; + + Q_strncpy( m_szName, src.m_szName, sizeof( m_szName ) ); + Q_strncpy( m_szFacePoserModelName, src.m_szFacePoserModelName, sizeof( m_szFacePoserModelName ) ); + + for ( int i = 0; i < src.m_Channels.Size(); i++ ) + { + CChoreoChannel *c = src.m_Channels[ i ]; + CChoreoChannel *newChannel = new CChoreoChannel(); + newChannel->SetActor( this ); + *newChannel = *c; + AddChannel( newChannel ); + } + + return *this; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CChoreoActor::Init( void ) +{ + m_szName[ 0 ] = 0; + m_szFacePoserModelName[ 0 ] = 0; + m_bActive = true; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *name - +//----------------------------------------------------------------------------- +void CChoreoActor::SetName( const char *name ) +{ + assert( strlen( name ) < MAX_ACTOR_NAME ); + Q_strncpy( m_szName, name, sizeof( m_szName ) ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : const char +//----------------------------------------------------------------------------- +const char *CChoreoActor::GetName( void ) +{ + return m_szName; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : int +//----------------------------------------------------------------------------- +int CChoreoActor::GetNumChannels( void ) +{ + return m_Channels.Size(); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : channel - +// Output : CChoreoChannel +//----------------------------------------------------------------------------- +CChoreoChannel *CChoreoActor::GetChannel( int channel ) +{ + if ( channel < 0 || channel >= m_Channels.Size() ) + { + return NULL; + } + + return m_Channels[ channel ]; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *channel - +//----------------------------------------------------------------------------- +void CChoreoActor::AddChannel( CChoreoChannel *channel ) +{ + m_Channels.AddToTail( channel ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *channel - +//----------------------------------------------------------------------------- +void CChoreoActor::RemoveChannel( CChoreoChannel *channel ) +{ + int idx = FindChannelIndex( channel ); + if ( idx == -1 ) + return; + + m_Channels.Remove( idx ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CChoreoActor::RemoveAllChannels() +{ + m_Channels.RemoveAll(); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : c1 - +// c2 - +//----------------------------------------------------------------------------- +void CChoreoActor::SwapChannels( int c1, int c2 ) +{ + CChoreoChannel *temp; + + temp = m_Channels[ c1 ]; + m_Channels[ c1 ] = m_Channels[ c2 ]; + m_Channels[ c2 ] = temp; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *channel - +// Output : int +//----------------------------------------------------------------------------- +int CChoreoActor::FindChannelIndex( CChoreoChannel *channel ) +{ + for ( int i = 0; i < m_Channels.Size(); i++ ) + { + if ( channel == m_Channels[ i ] ) + { + return i; + } + } + return -1; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *name - +//----------------------------------------------------------------------------- +void CChoreoActor::SetFacePoserModelName( const char *name ) +{ + Q_strncpy( m_szFacePoserModelName, name, sizeof( m_szFacePoserModelName ) ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : char const +//----------------------------------------------------------------------------- +const char *CChoreoActor::GetFacePoserModelName( void ) const +{ + return m_szFacePoserModelName; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : active - +//----------------------------------------------------------------------------- +void CChoreoActor::SetActive( bool active ) +{ + m_bActive = active; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CChoreoActor::GetActive( void ) const +{ + return m_bActive; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CChoreoActor::MarkForSaveAll( bool mark ) +{ + SetMarkedForSave( mark ); + + int c = GetNumChannels(); + for ( int i = 0; i < c; i++ ) + { + CChoreoChannel *channel = GetChannel( i ); + channel->MarkForSaveAll( mark ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *name - +// Output : CChoreoChannel +//----------------------------------------------------------------------------- +CChoreoChannel *CChoreoActor::FindChannel( const char *name ) +{ + int c = GetNumChannels(); + for ( int i = 0; i < c; i++ ) + { + CChoreoChannel *channel = GetChannel( i ); + if ( !Q_stricmp( channel->GetName(), name ) ) + return channel; + } + + return NULL; +} + +void CChoreoActor::SaveToBuffer( CUtlBuffer& buf, CChoreoScene *pScene ) +{ + int i, c; + buf.PutString( GetName() ); + + c = GetNumChannels(); + buf.PutShort( c ); + for ( i = 0; i < c; i++ ) + { + CChoreoChannel *channel = GetChannel( i ); + Assert( channel ); + channel->SaveToBuffer( buf, pScene ); + } + + /* + if ( Q_strlen( a->GetFacePoserModelName() ) > 0 ) + { + FilePrintf( buf, level + 1, "faceposermodel \"%s\"\n", a->GetFacePoserModelName() ); + } + */ + buf.PutChar( GetActive() ? 1 : 0 ); +} + +bool CChoreoActor::RestoreFromBuffer( CUtlBuffer& buf, CChoreoScene *pScene ) +{ + char sz[ 256 ]; + buf.GetString( sz, sizeof( sz ) ); + + SetName( sz ); + + int i; + int c = buf.GetShort(); + for ( i = 0; i < c; i++ ) + { + CChoreoChannel *channel = pScene->AllocChannel(); + Assert( channel ); + if ( channel->RestoreFromBuffer( buf, pScene, this ) ) + { + AddChannel( channel ); + channel->SetActor( this ); + continue; + } + + return false; + } + + SetActive( buf.GetChar() == 1 ? true : false ); + + return true; +} + diff --git a/choreoobjects/choreochannel.cpp b/choreoobjects/choreochannel.cpp new file mode 100644 index 00000000..fac0e4fd --- /dev/null +++ b/choreoobjects/choreochannel.cpp @@ -0,0 +1,562 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + +#include +#include +#include +#include "choreochannel.h" +#include "choreoevent.h" +#include "choreoscene.h" +#include "utlrbtree.h" +#include "tier1/utlbuffer.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CChoreoChannel::CChoreoChannel( void ) +{ + Init(); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *name - +//----------------------------------------------------------------------------- +CChoreoChannel::CChoreoChannel(const char *name ) +{ + Init(); + SetName( name ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Assignment +// Input : src - +//----------------------------------------------------------------------------- +CChoreoChannel& CChoreoChannel::operator=( const CChoreoChannel& src ) +{ + m_bActive = src.m_bActive; + Q_strncpy( m_szName, src.m_szName, sizeof( m_szName ) ); + for ( int i = 0; i < src.m_Events.Size(); i++ ) + { + CChoreoEvent *e = src.m_Events[ i ]; + CChoreoEvent *newEvent = new CChoreoEvent( e->GetScene() ); + *newEvent = *e; + AddEvent( newEvent ); + newEvent->SetChannel( this ); + newEvent->SetActor( m_pActor ); + } + + return *this; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *name - +//----------------------------------------------------------------------------- +void CChoreoChannel::SetName( const char *name ) +{ + assert( Q_strlen( name ) < MAX_CHANNEL_NAME ); + Q_strncpy( m_szName, name, sizeof( m_szName ) ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : const char +//----------------------------------------------------------------------------- +const char *CChoreoChannel::GetName( void ) +{ + return m_szName; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : int +//----------------------------------------------------------------------------- +int CChoreoChannel::GetNumEvents( void ) +{ + return m_Events.Size(); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : event - +// Output : CChoreoEvent +//----------------------------------------------------------------------------- +CChoreoEvent *CChoreoChannel::GetEvent( int event ) +{ + if ( event < 0 || event >= m_Events.Size() ) + { + return NULL; + } + + return m_Events[ event ]; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *event - +//----------------------------------------------------------------------------- +void CChoreoChannel::AddEvent( CChoreoEvent *event ) +{ + m_Events.AddToTail( event ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *event - +//----------------------------------------------------------------------------- +void CChoreoChannel::RemoveEvent( CChoreoEvent *event ) +{ + int idx = FindEventIndex( event ); + if ( idx == -1 ) + return; + + m_Events.Remove( idx ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CChoreoChannel::RemoveAllEvents() +{ + m_Events.RemoveAll(); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *event - +// Output : int +//----------------------------------------------------------------------------- +int CChoreoChannel::FindEventIndex( CChoreoEvent *event ) +{ + for ( int i = 0; i < m_Events.Size(); i++ ) + { + if ( event == m_Events[ i ] ) + { + return i; + } + } + return -1; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CChoreoChannel::Init( void ) +{ + m_szName[ 0 ] = 0; + SetActor( NULL ); + m_bActive = true; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : CChoreoActor +//----------------------------------------------------------------------------- +CChoreoActor *CChoreoChannel::GetActor( void ) +{ + return m_pActor; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *actor - +//----------------------------------------------------------------------------- +void CChoreoChannel::SetActor( CChoreoActor *actor ) +{ + m_pActor = actor; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : active - +//----------------------------------------------------------------------------- +void CChoreoChannel::SetActive( bool active ) +{ + m_bActive = active; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CChoreoChannel::GetActive( void ) const +{ + return m_bActive; +} + +static bool ChoreEventStartTimeLessFunc( CChoreoEvent * const &p1, CChoreoEvent * const &p2 ) +{ + CChoreoEvent *e1; + CChoreoEvent *e2; + + e1 = const_cast< CChoreoEvent * >( p1 ); + e2 = const_cast< CChoreoEvent * >( p2 ); + + return e1->GetStartTime() < e2->GetStartTime(); +} + +void CChoreoChannel::ReconcileGestureTimes() +{ + // Sort gesture events within channel by starting time + CUtlRBTree< CChoreoEvent * > sortedGestures( 0, 0, ChoreEventStartTimeLessFunc ); + int i; + // Sort items + int c = GetNumEvents(); + for ( i = 0; i < c; i++ ) + { + CChoreoEvent *e = GetEvent( i ); + Assert( e ); + if ( e->GetType() != CChoreoEvent::GESTURE ) + continue; + + sortedGestures.Insert( e ); + } + + // Now walk list of gestures + if ( !sortedGestures.Count() ) + return; + + CChoreoEvent *previous = NULL; + + for ( i = sortedGestures.FirstInorder(); i != sortedGestures.InvalidIndex(); i = sortedGestures.NextInorder( i ) ) + { + CChoreoEvent *event = sortedGestures[ i ]; + + if ( !previous ) + { + // event->SetStartTime( 0.0f ); + } + else if ( previous->GetSyncToFollowingGesture() ) + { + // TODO: ask the sequence for what tags to match + + CEventAbsoluteTag *pEntryTag = event->FindEntryTag( CChoreoEvent::PLAYBACK ); + CEventAbsoluteTag *pExitTag = previous->FindExitTag( CChoreoEvent::PLAYBACK ); + + if (pEntryTag && pExitTag) + { + float entryTime = pEntryTag->GetAbsoluteTime( ); + + // get current decay rate of previous gesture + float duration = previous->GetDuration(); + float decayTime = (1.0 - pExitTag->GetPercentage()) * duration; + + // adjust the previous gestures end time to current apex + existing decay rate + previous->RescaleGestureTimes( previous->GetStartTime(), entryTime + decayTime ); + previous->SetEndTime( entryTime + decayTime ); + + // set the previous gestures end tag to the current apex + pExitTag->SetAbsoluteTime( entryTime ); + + event->PreventTagOverlap( ); + previous->PreventTagOverlap( ); + } + // BUG: Tracker 3298: ywb 1/31/04 + // I think this fixes the issue with abutting past NULL gestures on paste: + // Here's the bug report: + // ------------------------- + // When copying and pasteing posture and gesture clips in face poser the beginings of the clips stretch + // to the begining of the scene even if there is a null gesture in place at the begining. + // ------------------------- + /* + else if ( pEntryTag && !Q_stricmp( previous->GetName(), "NULL" ) ) + { + // If the previous was a null event, then do a bit of fixup + event->SetStartTime( previous->GetEndTime() ); + + event->PreventTagOverlap( ); + } + */ + + // The previous event decays from it's end dispaly end time to the current event's display start time + // The next event starts just after the display end time of the previous event + } + + previous = event; + } + + if ( previous ) + { + CChoreoScene *scene = previous->GetScene(); + if ( scene ) + { + // HACK: Could probably do better by allowing user to drag the blue "end time" bar + //float finish = scene->FindStopTime(); + //previous->RescaleGestureTimes( previous->GetStartTime(), finish ); + //previous->SetEndTime( finish ); + } + } + + /* + c = 0; + for ( i = sortedGestures.FirstInorder(); i != sortedGestures.InvalidIndex(); i = sortedGestures.NextInorder( i ) ) + { + CChoreoEvent *event = sortedGestures[ i ]; + + Msg( "event %i start %f disp %f dispend %f end %f\n", + c + 1, + event->GetStartTime( CChoreoEvent::SIMULATION ), + event->GetStartTime( CChoreoEvent::DISPLAY ), + event->GetEndTime( CChoreoEvent::DISPLAY ), + event->GetEndTime( CChoreoEvent::SIMULATION ) + ); + c++; + } + */ +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CChoreoChannel::MarkForSaveAll( bool mark ) +{ + SetMarkedForSave( mark ); + + int c = GetNumEvents(); + for ( int i = 0; i < c; i++ ) + { + CChoreoEvent *e = GetEvent( i ); + e->SetMarkedForSave( mark ); + } +} + + +struct EventGroup +{ + EventGroup() : + timeSortedEvents( 0, 0, ChoreEventStartTimeLessFunc ) + { + } + + EventGroup( const EventGroup& src ) + : + timeSortedEvents( 0, 0, ChoreEventStartTimeLessFunc ) + { + timeSortedEvents.RemoveAll(); + int i = src.timeSortedEvents.FirstInorder(); + while ( i != src.timeSortedEvents.InvalidIndex() ) + { + timeSortedEvents.Insert( src.timeSortedEvents[ i ] ); + + i = src.timeSortedEvents.NextInorder( i ); + } + } + + EventGroup & operator=( const EventGroup& src ) + { + if ( this == &src ) + return *this; + + timeSortedEvents.RemoveAll(); + int i = src.timeSortedEvents.FirstInorder(); + while ( i != src.timeSortedEvents.InvalidIndex() ) + { + timeSortedEvents.Insert( src.timeSortedEvents[ i ] ); + + i = src.timeSortedEvents.NextInorder( i ); + } + return *this; + } + + CUtlRBTree< CChoreoEvent * > timeSortedEvents; +}; + +// Compute master/slave, count, endtime info for close captioning data +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CChoreoChannel::ReconcileCloseCaption() +{ + // Create a dictionary based on the combined token name + CUtlDict< EventGroup, int > validSpeakEventsGroupedByName; + + int i; + // Sort items + int c = GetNumEvents(); + for ( i = 0; i < c; i++ ) + { + CChoreoEvent *e = GetEvent( i ); + Assert( e ); + if ( e->GetType() != CChoreoEvent::SPEAK ) + continue; + + CChoreoEvent::CLOSECAPTION type; + + type = e->GetCloseCaptionType(); + if ( type == CChoreoEvent::CC_DISABLED ) + { + e->SetUsingCombinedFile( false ); + e->SetRequiredCombinedChecksum( 0 ); + e->SetNumSlaves( 0 ); + e->SetLastSlaveEndTime( 0.0f ); + continue; + } + + char const *name = e->GetCloseCaptionToken(); + if ( !name || !name[0] ) + { + // Fixup invalid slave tag + if ( type == CChoreoEvent::CC_SLAVE ) + { + e->SetCloseCaptionType( CChoreoEvent::CC_MASTER ); + e->SetUsingCombinedFile( false ); + e->SetRequiredCombinedChecksum( 0 ); + e->SetNumSlaves( 0 ); + e->SetLastSlaveEndTime( 0.0f ); + } + continue; + } + + int idx = validSpeakEventsGroupedByName.Find( name ); + if ( idx == validSpeakEventsGroupedByName.InvalidIndex() ) + { + EventGroup eg; + eg.timeSortedEvents.Insert( e ); + validSpeakEventsGroupedByName.Insert( name, eg ); + } + else + { + EventGroup & eg = validSpeakEventsGroupedByName[ idx ]; + eg.timeSortedEvents.Insert( e ); + } + } + + c = validSpeakEventsGroupedByName.Count(); + // Now walk list of events by group + if ( !c ) + { + return; + } + + for ( i = 0; i < c; ++i ) + { + EventGroup & eg = validSpeakEventsGroupedByName[ i ]; + int sortedEventInGroup = eg.timeSortedEvents.Count(); + // If there's only one, just mark it valid + if ( sortedEventInGroup <= 1 ) + { + CChoreoEvent *e = eg.timeSortedEvents[ 0 ]; + Assert( e ); + // Make sure it's the master + e->SetCloseCaptionType( CChoreoEvent::CC_MASTER ); + // Since it's by itself, can't be using "combined" file + e->SetUsingCombinedFile( false ); + e->SetRequiredCombinedChecksum( 0 ); + e->SetNumSlaves( 0 ); + e->SetLastSlaveEndTime( 0.0f ); + continue; + } + + // Okay, read them back in of start time + int j = eg.timeSortedEvents.FirstInorder(); + CChoreoEvent *master = NULL; + while ( j != eg.timeSortedEvents.InvalidIndex() ) + { + CChoreoEvent *e = eg.timeSortedEvents[ j ]; + if ( !master ) + { + master = e; + e->SetCloseCaptionType( CChoreoEvent::CC_MASTER ); + //e->SetUsingCombinedFile( true ); + e->SetRequiredCombinedChecksum( 0 ); + e->SetNumSlaves( sortedEventInGroup - 1 ); + e->SetLastSlaveEndTime( e->GetEndTime() ); + } + else + { + // Keep bumping out the end time + master->SetLastSlaveEndTime( e->GetEndTime() ); + e->SetCloseCaptionType( CChoreoEvent::CC_SLAVE ); + e->SetUsingCombinedFile( master->IsUsingCombinedFile() ); + e->SetRequiredCombinedChecksum( 0 ); + e->SetLastSlaveEndTime( 0.0f ); + } + + j = eg.timeSortedEvents.NextInorder( j ); + } + } +} + +bool CChoreoChannel::GetSortedCombinedEventList( char const *cctoken, CUtlRBTree< CChoreoEvent * >& events ) +{ + events.RemoveAll(); + + int i; + // Sort items + int c = GetNumEvents(); + for ( i = 0; i < c; i++ ) + { + CChoreoEvent *e = GetEvent( i ); + Assert( e ); + if ( e->GetType() != CChoreoEvent::SPEAK ) + continue; + + if ( e->GetCloseCaptionType() == CChoreoEvent::CC_DISABLED ) + continue; + + // A master with no slaves is not a combined event + if ( e->GetCloseCaptionType() == CChoreoEvent::CC_MASTER && + e->GetNumSlaves() == 0 ) + continue; + + char const *token = e->GetCloseCaptionToken(); + if ( Q_stricmp( token, cctoken ) ) + continue; + + events.Insert( e ); + } + + return ( events.Count() > 0 ) ? true : false; +} + +void CChoreoChannel::SaveToBuffer( CUtlBuffer& buf, CChoreoScene *pScene ) +{ + buf.PutString( GetName() ); + int c = GetNumEvents(); + Assert( c <= 65535 ); + + buf.PutShort( c ); + for ( int i = 0; i < c; i++ ) + { + CChoreoEvent *e = GetEvent( i ); + Assert( e ); + e->SaveToBuffer( buf, pScene ); + } + + buf.PutChar( GetActive() ? 1 : 0 ); +} + +bool CChoreoChannel::RestoreFromBuffer( CUtlBuffer& buf, CChoreoScene *pScene, CChoreoActor *pActor ) +{ + char sz[ 256 ]; + buf.GetString( sz, sizeof( sz ) ); + SetName( sz ); + + int numEvents = (int)buf.GetShort(); + for ( int i = 0 ; i < numEvents; ++i ) + { + CChoreoEvent *e = pScene->AllocEvent(); + if ( e->RestoreFromBuffer( buf, pScene ) ) + { + AddEvent( e ); + e->SetChannel( this ); + e->SetActor( pActor ); + continue; + } + return false; + } + + SetActive( buf.GetChar() == 1 ? true : false ); + + return true; +} diff --git a/choreoobjects/choreoevent.cpp b/choreoobjects/choreoevent.cpp new file mode 100644 index 00000000..3cc1c217 --- /dev/null +++ b/choreoobjects/choreoevent.cpp @@ -0,0 +1,4204 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include "tier0/dbg.h" +#include +#include +#include +#include "choreoevent.h" +#include "choreoactor.h" +#include "choreochannel.h" +#include "minmax.h" +#include "mathlib.h" +#include "vstdlib/strtools.h" +#include "choreoscene.h" +#include "ichoreoeventcallback.h" +#include "tier1/utlbuffer.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +int CChoreoEvent::s_nGlobalID = 1; + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *owner - +// *name - +// percentage - +//----------------------------------------------------------------------------- +CEventRelativeTag::CEventRelativeTag( CChoreoEvent *owner, const char *name, float percentage ) +{ + Assert( owner ); + Assert( name ); + Assert( percentage >= 0.0f ); + Assert( percentage <= 1.0f ); + + m_Name = name; + m_flPercentage = percentage; + m_pOwner = owner; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : src - +//----------------------------------------------------------------------------- +CEventRelativeTag::CEventRelativeTag( const CEventRelativeTag& src ) +{ + m_Name = src.m_Name; + m_flPercentage = src.m_flPercentage; + m_pOwner = src.m_pOwner; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : const char +//----------------------------------------------------------------------------- +const char *CEventRelativeTag::GetName( void ) +{ + return m_Name.c_str(); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : float +//----------------------------------------------------------------------------- +float CEventRelativeTag::GetPercentage( void ) +{ + return m_flPercentage; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : percentage - +//----------------------------------------------------------------------------- +void CEventRelativeTag::SetPercentage( float percentage ) +{ + m_flPercentage = percentage; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : CChoreoEvent +//----------------------------------------------------------------------------- +CChoreoEvent *CEventRelativeTag::GetOwner( void ) +{ + return m_pOwner; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *event - +//----------------------------------------------------------------------------- +void CEventRelativeTag::SetOwner( CChoreoEvent *event ) +{ + m_pOwner = event; +} + +//----------------------------------------------------------------------------- +// Purpose: Returns the corrected time based on the owner's length and start time +// Output : float +//----------------------------------------------------------------------------- +float CEventRelativeTag::GetStartTime( void ) +{ + Assert( m_pOwner ); + if ( !m_pOwner ) + { + return 0.0f; + } + + float ownerstart = m_pOwner->GetStartTime(); + float ownerduration = m_pOwner->GetDuration(); + + return ( ownerstart + ownerduration * m_flPercentage ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *owner - +// *name - +// percentage - +//----------------------------------------------------------------------------- +CFlexTimingTag::CFlexTimingTag( CChoreoEvent *owner, const char *name, float percentage, bool locked ) +: BaseClass( owner, name, percentage ) +{ + m_bLocked = locked; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : src - +//----------------------------------------------------------------------------- +CFlexTimingTag::CFlexTimingTag( const CFlexTimingTag& src ) +: BaseClass( src ) +{ + m_bLocked = src.m_bLocked; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CFlexTimingTag::GetLocked( void ) +{ + return m_bLocked; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : locked - +//----------------------------------------------------------------------------- +void CFlexTimingTag::SetLocked( bool locked ) +{ + m_bLocked = locked; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *owner - +// *name - +// percentage - +//----------------------------------------------------------------------------- +CEventAbsoluteTag::CEventAbsoluteTag( CChoreoEvent *owner, const char *name, float t ) +{ + Assert( owner ); + Assert( name ); + Assert( t >= 0.0f ); + + m_Name = name; + m_flPercentage = t; + m_pOwner = owner; + m_bLocked = false; + m_bLinear = false; + m_bEntry = false; + m_bExit = false; + +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : src - +//----------------------------------------------------------------------------- +CEventAbsoluteTag::CEventAbsoluteTag( const CEventAbsoluteTag& src ) +{ + m_Name = src.m_Name; + m_flPercentage = src.m_flPercentage; + m_pOwner = src.m_pOwner; + m_bLocked = src.m_bLocked; + m_bLinear = src.m_bLinear; + m_bEntry = src.m_bEntry; + m_bExit = src.m_bExit; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : const char +//----------------------------------------------------------------------------- +const char *CEventAbsoluteTag::GetName( void ) +{ + return m_Name.c_str(); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : float +//----------------------------------------------------------------------------- +float CEventAbsoluteTag::GetPercentage( void ) +{ + return m_flPercentage; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : percentage - +//----------------------------------------------------------------------------- +void CEventAbsoluteTag::SetPercentage( float percentage ) +{ + m_flPercentage = percentage; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : float +//----------------------------------------------------------------------------- +float CEventAbsoluteTag::GetEventTime( void ) +{ + Assert( m_pOwner ); + if ( !m_pOwner ) + { + return 0.0f; + } + + float ownerduration = m_pOwner->GetDuration(); + + return (m_flPercentage * ownerduration); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : percentage - +//----------------------------------------------------------------------------- +void CEventAbsoluteTag::SetEventTime( float t ) +{ + Assert( m_pOwner ); + if ( !m_pOwner ) + { + return; + } + + float ownerduration = m_pOwner->GetDuration(); + + m_flPercentage = (t / ownerduration); +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Output : float +//----------------------------------------------------------------------------- +float CEventAbsoluteTag::GetAbsoluteTime( void ) +{ + Assert( m_pOwner ); + if ( !m_pOwner ) + { + return 0.0f; + } + + float ownerstart = m_pOwner->GetStartTime(); + float ownerduration = m_pOwner->GetDuration(); + + return (ownerstart + m_flPercentage * ownerduration); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : percentage - +//----------------------------------------------------------------------------- +void CEventAbsoluteTag::SetAbsoluteTime( float t ) +{ + Assert( m_pOwner ); + if ( !m_pOwner ) + { + return; + } + + float ownerstart = m_pOwner->GetStartTime(); + float ownerduration = m_pOwner->GetDuration(); + + m_flPercentage = (t - ownerstart) / ownerduration; +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Output : CChoreoEvent +//----------------------------------------------------------------------------- +CChoreoEvent *CEventAbsoluteTag::GetOwner( void ) +{ + return m_pOwner; +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *event - +//----------------------------------------------------------------------------- +void CEventAbsoluteTag::SetOwner( CChoreoEvent *event ) +{ + m_pOwner = event; +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *event - +//----------------------------------------------------------------------------- +void CEventAbsoluteTag::SetLocked( bool bLocked ) +{ + m_bLocked = bLocked; +} + + + +//----------------------------------------------------------------------------- +// Purpose: +// Output : CChoreoEvent +//----------------------------------------------------------------------------- +bool CEventAbsoluteTag::GetLocked( void ) +{ + return m_bLocked; +} + + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *event - +//----------------------------------------------------------------------------- +void CEventAbsoluteTag::SetLinear( bool bLinear ) +{ + m_bLinear = bLinear; +} + + + +//----------------------------------------------------------------------------- +// Purpose: +// Output : CChoreoEvent +//----------------------------------------------------------------------------- +bool CEventAbsoluteTag::GetLinear( void ) +{ + return m_bLinear; +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *event - +//----------------------------------------------------------------------------- +void CEventAbsoluteTag::SetEntry( bool bEntry ) +{ + m_bEntry = bEntry; +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Output : CChoreoEvent +//----------------------------------------------------------------------------- +bool CEventAbsoluteTag::GetEntry( void ) +{ + return m_bEntry; +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *event - +//----------------------------------------------------------------------------- +void CEventAbsoluteTag::SetExit( bool bExit ) +{ + m_bExit = bExit; +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Output : CChoreoEvent +//----------------------------------------------------------------------------- +bool CEventAbsoluteTag::GetExit( void ) +{ + return m_bExit; +} + + + + +// FLEX ANIMATIONS +//----------------------------------------------------------------------------- +// Purpose: Constructor +// Input : *event - +//----------------------------------------------------------------------------- +CFlexAnimationTrack::CFlexAnimationTrack( CChoreoEvent *event ) +{ + m_pEvent = event; + m_pControllerName = NULL; + m_bActive = false; + m_bCombo = false; + m_bServerSide = false; + m_nFlexControllerIndex[ 0 ] = m_nFlexControllerIndex[ 1 ] = -1; + m_nFlexControllerIndexRaw[ 0 ] = m_nFlexControllerIndexRaw[ 1 ] = -1; + + // base track has range, combo is always 0..1 + m_flMin = 0.0f; + m_flMax = 0.0f; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : src - +//----------------------------------------------------------------------------- +CFlexAnimationTrack::CFlexAnimationTrack( const CFlexAnimationTrack* src ) +{ + m_pControllerName = NULL; + SetFlexControllerName( src->m_pControllerName ? src->m_pControllerName : "" ); + + m_bActive = src->m_bActive; + m_bCombo = src->m_bCombo; + m_bServerSide = src->m_bServerSide; + + for ( int t = 0; t < 2; t++ ) + { + m_Samples[ t ].Purge(); + for ( int i = 0 ;i < src->m_Samples[ t ].Size(); i++ ) + { + CExpressionSample s = src->m_Samples[ t ][ i ]; + m_Samples[ t ].AddToTail( s ); + } + } + + for ( int side = 0; side < 2; side++ ) + { + m_nFlexControllerIndex[ side ] = src->m_nFlexControllerIndex[ side ]; + m_nFlexControllerIndexRaw[ side ] = src->m_nFlexControllerIndexRaw[ side ]; + } + + m_flMin = src->m_flMin; + m_flMax = src->m_flMax; + + m_EdgeInfo[ 0 ] = src->m_EdgeInfo[ 0 ]; + m_EdgeInfo[ 1 ] = src->m_EdgeInfo[ 1 ]; + + m_pEvent = NULL; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CFlexAnimationTrack::~CFlexAnimationTrack( void ) +{ + delete[] m_pControllerName; + + for ( int t = 0; t < 2; t++ ) + { + m_Samples[ t ].Purge(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *event - +//----------------------------------------------------------------------------- +void CFlexAnimationTrack::SetEvent( CChoreoEvent *event ) +{ + m_pEvent = event; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CFlexAnimationTrack::Clear( void ) +{ + for ( int t = 0; t < 2; t++ ) + { + m_Samples[ t ].RemoveAll(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : index - +//----------------------------------------------------------------------------- +void CFlexAnimationTrack::RemoveSample( int index, int type /*=0*/ ) +{ + Assert( type == 0 || type == 1 ); + + m_Samples[ type ].Remove( index ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *name - +//----------------------------------------------------------------------------- +void CFlexAnimationTrack::SetFlexControllerName( const char *name ) +{ + delete[] m_pControllerName; + size_t len = Q_strlen( name ) + 1; + m_pControllerName = new char[ len ]; + Q_strncpy( m_pControllerName, name, len ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : char const +//----------------------------------------------------------------------------- +const char *CFlexAnimationTrack::GetFlexControllerName( void ) +{ + return m_pControllerName ? m_pControllerName : ""; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : int +//----------------------------------------------------------------------------- +int CFlexAnimationTrack::GetNumSamples( int type /*=0*/ ) +{ + Assert( type == 0 || type == 1 ); + + return m_Samples[ type ].Size(); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : index - +// Output : CExpressionSample +//----------------------------------------------------------------------------- +CExpressionSample *CFlexAnimationTrack::GetSample( int index, int type /*=0*/ ) +{ + Assert( type == 0 || type == 1 ); + + if ( index < 0 || index >= GetNumSamples( type ) ) + return NULL; + return &m_Samples[ type ][ index ]; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CFlexAnimationTrack::IsTrackActive( void ) +{ + return m_bActive; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : active - +//----------------------------------------------------------------------------- +void CFlexAnimationTrack::SetTrackActive( bool active ) +{ + m_bActive = active; +} + +void CFlexAnimationTrack::SetEdgeInfo( bool leftEdge, int curveType, float zero ) +{ + int idx = leftEdge ? 0 : 1; + m_EdgeInfo[ idx ].m_CurveType = curveType; + m_EdgeInfo[ idx ].m_flZeroPos = zero; +} + +void CFlexAnimationTrack::GetEdgeInfo( bool leftEdge, int& curveType, float& zero ) const +{ + int idx = leftEdge ? 0 : 1; + curveType = m_EdgeInfo[ idx ].m_CurveType; + zero = m_EdgeInfo[ idx ].m_flZeroPos; +} + +void CFlexAnimationTrack::SetEdgeActive( bool leftEdge, bool state ) +{ + int idx = leftEdge ? 0 : 1; + m_EdgeInfo[ idx ].m_bActive = state; +} + +bool CFlexAnimationTrack::IsEdgeActive( bool leftEdge ) const +{ + int idx = leftEdge ? 0 : 1; + return m_EdgeInfo[ idx ].m_bActive; +} + +int CFlexAnimationTrack::GetEdgeCurveType( bool leftEdge ) const +{ + if ( !IsEdgeActive( leftEdge ) ) + { + return CURVE_DEFAULT; + } + + int idx = leftEdge ? 0 : 1; + return m_EdgeInfo[ idx ].m_CurveType; +} + +float CFlexAnimationTrack::GetEdgeZeroValue( bool leftEdge ) const +{ + if ( !IsEdgeActive( leftEdge ) ) + { + return 0.0f; + } + + int idx = leftEdge ? 0 : 1; + return m_EdgeInfo[ idx ].m_flZeroPos; +} + +float CFlexAnimationTrack::GetDefaultEdgeZeroPos() const +{ + float zero = 0.0f; + if ( m_flMin != m_flMax ) + { + zero = ( 0.0f - m_flMin ) / ( m_flMax - m_flMin ); + } + return zero; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +float CFlexAnimationTrack::GetZeroValue( int type, bool leftSide ) +{ + // Stereo track is always clamped to 0.5 and doesn't care about l/r settings + if ( type == 1 ) + { + return 0.5f; + } + + if ( IsEdgeActive( leftSide ) ) + { + return GetEdgeZeroValue( leftSide ); + } + + return GetDefaultEdgeZeroPos(); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : number - +// Output : CExpressionSample +//----------------------------------------------------------------------------- +CExpressionSample *CFlexAnimationTrack::GetBoundedSample( int number, bool& bClamped, int type /*=0*/ ) +{ + Assert( type == 0 || type == 1 ); + + if ( number < 0 ) + { + // Search for two samples which span time f + static CExpressionSample nullstart; + nullstart.time = 0.0f; + nullstart.value = GetZeroValue( type, true ); + if ( type == 0 ) + { + nullstart.SetCurveType( GetEdgeCurveType( true ) ); + } + else + { + nullstart.SetCurveType( CURVE_DEFAULT ); + } + bClamped = true; + return &nullstart; + } + else if ( number >= GetNumSamples( type ) ) + { + static CExpressionSample nullend; + nullend.time = m_pEvent->GetDuration(); + nullend.value = GetZeroValue( type, false ); + if ( type == 0 ) + { + nullend.SetCurveType( GetEdgeCurveType( false ) ); + } + else + { + nullend.SetCurveType( CURVE_DEFAULT ); + } + bClamped = true; + return &nullend; + } + + bClamped = false; + return GetSample( number, type ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : time - +// type - +// Output : float +//----------------------------------------------------------------------------- +float CFlexAnimationTrack::GetIntensityInternal( float time, int type ) +{ + Assert( type == 0 || type == 1 ); + + float retval = 0.0f; + + // find samples that span the time + if ( !m_pEvent || !m_pEvent->HasEndTime() || time < m_pEvent->GetStartTime() ) + { + retval = GetZeroValue( type, true );; + } + else if ( time > m_pEvent->GetEndTime() ) + { + retval = GetZeroValue( type, false );; + } + else + { + float elapsed = time - m_pEvent->GetStartTime(); + retval = GetFracIntensity( elapsed, type ); + } + + // scale + if (type == 0 && m_flMin != m_flMax) + { + retval = retval * (m_flMax - m_flMin) + m_flMin; + } + return retval; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : time - +// type - +// Output : float +//----------------------------------------------------------------------------- +float CFlexAnimationTrack::GetFracIntensity( float time, int type ) +{ + float zeroValueLeft = GetZeroValue( type, true ); + + Assert( type == 0 || type == 1 ); + + // find samples that span the time + if ( !m_pEvent || !m_pEvent->HasEndTime() ) + return zeroValueLeft; + + int rampCount = GetNumSamples( type ); + if ( rampCount < 1 ) + { + return zeroValueLeft; + } + + CExpressionSample *esStart = NULL; + CExpressionSample *esEnd = NULL; + + // do binary search for sample in time period + int j = max( rampCount / 2, 1 ); + int i = j; + while ( i > -2 && i < rampCount + 1 ) + { + bool dummy; + esStart = GetBoundedSample( i, dummy, type ); + esEnd = GetBoundedSample( i + 1, dummy, type ); + + j = max( j / 2, 1 ); + if ( time < esStart->time) + { + i -= j; + } + else if ( time > esEnd->time) + { + i += j; + } + else + { + if ( time == esEnd->time ) + { + ++i; + esStart = GetBoundedSample( i, dummy, type ); + esEnd = GetBoundedSample( i + 1, dummy, type ); + } + break; + } + } + + if (!esStart) + { + return zeroValueLeft; + } + + int prev = i - 1; + int next = i + 2; + + prev = max( -1, prev ); + next = min( next, rampCount ); + + bool clamp[ 2 ]; + CExpressionSample *esPre = GetBoundedSample( prev, clamp[ 0 ], type ); + CExpressionSample *esNext = GetBoundedSample( next, clamp[ 1 ], type ); + + float dt = esEnd->time - esStart->time; + + Vector vPre( esPre->time, esPre->value, 0 ); + Vector vStart( esStart->time, esStart->value, 0 ); + Vector vEnd( esEnd->time, esEnd->value, 0 ); + Vector vNext( esNext->time, esNext->value, 0 ); + + float f2 = 0.0f; + if ( dt > 0.0f ) + { + f2 = ( time - esStart->time ) / ( dt ); + } + f2 = clamp( f2, 0.0f, 1.0f ); + + Vector vOut; + int dummy; + int earlypart, laterpart; + + // Not holding out value of previous curve... + Interpolator_CurveInterpolatorsForType( esStart->GetCurveType(), dummy, earlypart ); + Interpolator_CurveInterpolatorsForType( esEnd->GetCurveType(), laterpart, dummy ); + + if ( earlypart == INTERPOLATE_HOLD ) + { + // Hold "out" of previous sample (can cause a discontinuity) + VectorLerp( vStart, vEnd, f2, vOut ); + vOut.y = vStart.y; + } + else if ( laterpart == INTERPOLATE_HOLD ) + { + // Hold "out" of previous sample (can cause a discontinuity) + VectorLerp( vStart, vEnd, f2, vOut ); + vOut.y = vEnd.y; + } + else + { + bool sameCurveType = earlypart == laterpart ? true : false; + if ( sameCurveType ) + { + Interpolator_CurveInterpolate( laterpart, vPre, vStart, vEnd, vNext, f2, vOut ); + } + else // curves differ, sigh + { + Vector vOut1, vOut2; + + Interpolator_CurveInterpolate( earlypart, vPre, vStart, vEnd, vNext, f2, vOut1 ); + Interpolator_CurveInterpolate( laterpart, vPre, vStart, vEnd, vNext, f2, vOut2 ); + + VectorLerp( vOut1, vOut2, f2, vOut ); + } + } + + float retval = clamp( vOut.y, 0.0f, 1.0f ); + return retval; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : time - +// Output : float +//----------------------------------------------------------------------------- +float CFlexAnimationTrack::GetSampleIntensity( float time ) +{ + return GetIntensityInternal( time, 0 ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : time - +// Output : float +//----------------------------------------------------------------------------- +float CFlexAnimationTrack::GetBalanceIntensity( float time ) +{ + if ( IsComboType() ) + { + return GetIntensityInternal( time, 1 ); + } + + return 1.0f; +} + +// For a given time, computes 0->1 intensity value for the slider +//----------------------------------------------------------------------------- +// Purpose: +// Input : time - +// Output : float +//----------------------------------------------------------------------------- +float CFlexAnimationTrack::GetIntensity( float time, int side ) +{ + float mag = GetSampleIntensity( time ); + + float scale = 1.0f; + + if ( IsComboType() ) + { + float balance = GetBalanceIntensity( time ); + + // Asking for left but balance is to right, then fall off as we go + // further right + if ( side == 0 && balance > 0.5f ) + { + scale = (1.0f - balance ) / 0.5f; + } + // Asking for right, but balance is left, fall off as we go left. + else if ( side == 1 && balance < 0.5f ) + { + scale = ( balance / 0.5f ); + } + } + + return mag * scale; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : time - +// value - +//----------------------------------------------------------------------------- +CExpressionSample *CFlexAnimationTrack::AddSample( float time, float value, int type /*=0*/ ) +{ + Assert( type == 0 || type == 1 ); + + CExpressionSample sample; + sample.time = time; + sample.value = value; + sample.selected = false; + + int idx = m_Samples[ type ].AddToTail( sample ); + + // Resort( type ); + return &m_Samples[ type ][ idx ]; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CFlexAnimationTrack::Resort( int type /*=0*/ ) +{ + Assert( type == 0 || type == 1 ); + + for ( int i = 0; i < m_Samples[ type ].Size(); i++ ) + { + for ( int j = i + 1; j < m_Samples[ type ].Size(); j++ ) + { + CExpressionSample src = m_Samples[ type ][ i ]; + CExpressionSample dest = m_Samples[ type ][ j ]; + + if ( src.time > dest.time ) + { + m_Samples[ type ][ i ] = dest; + m_Samples[ type ][ j ] = src; + } + } + } + + // Make sure nothing is out of range + RemoveOutOfRangeSamples( 0 ); + RemoveOutOfRangeSamples( 1 ); +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Output : CChoreoEvent +//----------------------------------------------------------------------------- +CChoreoEvent *CFlexAnimationTrack::GetEvent( void ) +{ + return m_pEvent; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : side - +// Output : int +//----------------------------------------------------------------------------- +int CFlexAnimationTrack::GetFlexControllerIndex( int side /*= 0*/ ) +{ + Assert( side == 0 || side == 1 ); + + if ( IsComboType() ) + { + return m_nFlexControllerIndex[ side ]; + } + + return m_nFlexControllerIndex[ 0 ]; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : side - +// Output : int +//----------------------------------------------------------------------------- +int CFlexAnimationTrack::GetRawFlexControllerIndex( int side /*= 0*/ ) +{ + Assert( side == 0 || side == 1 ); + + if ( IsComboType() ) + { + return m_nFlexControllerIndexRaw[ side ]; + } + + return m_nFlexControllerIndexRaw[ 0 ]; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : index - +// side - +//----------------------------------------------------------------------------- +void CFlexAnimationTrack::SetFlexControllerIndex( int raw, int index, int side /*= 0*/ ) +{ + Assert( side == 0 || side == 1 ); + + m_nFlexControllerIndex[ side ] = index; + // Model specific + m_nFlexControllerIndexRaw[ side ] = raw; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : combo - +//----------------------------------------------------------------------------- +void CFlexAnimationTrack::SetComboType( bool combo ) +{ + m_bCombo = combo; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CFlexAnimationTrack::IsComboType( void ) +{ + return m_bCombo; +} + +//----------------------------------------------------------------------------- +// Purpose: True if this should be simulated on the server side always +// Input : state - +//----------------------------------------------------------------------------- +void CFlexAnimationTrack::SetServerSide( bool state ) +{ + m_bServerSide = state; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CFlexAnimationTrack::IsServerSide() const +{ + return m_bServerSide; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CFlexAnimationTrack::SetMin( float value ) +{ + m_flMin = value; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CFlexAnimationTrack::SetMax( float value ) +{ + m_flMax = value; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +float CFlexAnimationTrack::GetMin( int type ) +{ + if (type == 0) + return m_flMin; + else + return 0.0f; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +float CFlexAnimationTrack::GetMax( int type ) +{ + if (type == 0) + return m_flMax; + else + return 1.0f; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CFlexAnimationTrack::IsInverted( void ) +{ + if (m_bInverted) + return true; + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CFlexAnimationTrack::SetInverted( bool isInverted ) +{ + m_bInverted = isInverted; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : float +//----------------------------------------------------------------------------- +void CFlexAnimationTrack::RemoveOutOfRangeSamples( int type ) +{ + Assert( m_pEvent ); + if ( !m_pEvent ) + return; + + Assert( m_pEvent->HasEndTime() ); + float duration = m_pEvent->GetDuration(); + + int c = m_Samples[ type ].Size(); + for ( int i = c-1; i >= 0; i-- ) + { + CExpressionSample src = m_Samples[ type ][ i ]; + if ( src.time < 0 || + src.time > duration ) + { + m_Samples[ type ].Remove( i ); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CChoreoEvent::CChoreoEvent( CChoreoScene *scene ) +{ + Init( scene ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : type - +// *name - +//----------------------------------------------------------------------------- +CChoreoEvent::CChoreoEvent( CChoreoScene *scene, EVENTTYPE type, const char *name ) +{ + Init( scene ); + SetType( type ); + SetName( name ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : type - +// *name - +// *param - +//----------------------------------------------------------------------------- +CChoreoEvent::CChoreoEvent( CChoreoScene *scene, EVENTTYPE type, const char *name, const char *param ) +{ + Init( scene ); + SetType( type ); + SetName( name ); + SetParameters( param ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CChoreoEvent::~CChoreoEvent( void ) +{ + RemoveAllTracks(); + ClearEventDependencies(); + delete m_pSubScene; +} + +//----------------------------------------------------------------------------- +// Purpose: Assignment +// Input : src - +// Output : CChoreoEvent& +//----------------------------------------------------------------------------- +CChoreoEvent& CChoreoEvent::operator=( const CChoreoEvent& src ) +{ + MEM_ALLOC_CREDIT(); + + // Copy global id when copying entity + m_nGlobalID = src.m_nGlobalID; + + m_pActor = NULL; + m_pChannel = NULL; + + m_fType = src.m_fType; + m_Name = src.m_Name; + m_Parameters = src.m_Parameters; + m_Parameters2= src.m_Parameters2; + m_flStartTime = src.m_flStartTime; + m_flEndTime = src.m_flEndTime; + + m_bFixedLength = src.m_bFixedLength; + m_flGestureSequenceDuration = src.m_flGestureSequenceDuration; + m_bResumeCondition = src.m_bResumeCondition; + m_bLockBodyFacing = src.m_bLockBodyFacing; + m_flDistanceToTarget = src.m_flDistanceToTarget; + m_bForceShortMovement = src.m_bForceShortMovement; + m_bUsesTag = src.m_bUsesTag; + m_TagName = src.m_TagName; + m_TagWavName = src.m_TagWavName; + + ClearAllRelativeTags(); + ClearAllTimingTags(); + int t; + for ( t = 0; t < NUM_ABS_TAG_TYPES; t++ ) + { + ClearAllAbsoluteTags( (AbsTagType)t ); + } + + int i; + for ( i = 0; i < src.m_RelativeTags.Size(); i++ ) + { + CEventRelativeTag newtag( src.m_RelativeTags[ i ] ); + newtag.SetOwner( this ); + m_RelativeTags.AddToTail( newtag ); + } + + for ( i = 0; i < src.m_TimingTags.Size(); i++ ) + { + CFlexTimingTag newtag( src.m_TimingTags[ i ] ); + newtag.SetOwner( this ); + m_TimingTags.AddToTail( newtag ); + } + for ( t = 0; t < NUM_ABS_TAG_TYPES; t++ ) + { + for ( i = 0; i < src.m_AbsoluteTags[ t ].Size(); i++ ) + { + CEventAbsoluteTag newtag( src.m_AbsoluteTags[ t ][ i ] ); + newtag.SetOwner( this ); + m_AbsoluteTags[ t ].AddToTail( newtag ); + } + } + + RemoveAllTracks(); + + for ( i = 0 ; i < src.m_FlexAnimationTracks.Size(); i++ ) + { + CFlexAnimationTrack *newtrack = new CFlexAnimationTrack( src.m_FlexAnimationTracks[ i ] ); + newtrack->SetEvent( this ); + m_FlexAnimationTracks.AddToTail( newtrack ); + } + + m_bTrackLookupSet = src.m_bTrackLookupSet; + + // FIXME: Use a safe handle? + //m_pSubScene = src.m_pSubScene; + + m_bProcessing = src.m_bProcessing; + m_pMixer = src.m_pMixer; + + m_pScene = src.m_pScene; + + m_nPitch = src.m_nPitch; + m_nYaw = src.m_nYaw; + + m_nNumLoops = src.m_nNumLoops; + m_nLoopsRemaining = src.m_nLoopsRemaining; + + // Copy ramp over + m_Ramp.RemoveAll(); + for ( i = 0; i < src.m_Ramp.Count(); i++ ) + { + CExpressionSample sample = src.m_Ramp[ i ]; + AddRamp( sample.time, sample.value, sample.selected ); + } + + m_RampEdgeInfo[ 0 ] = src.m_RampEdgeInfo[ 0 ]; + m_RampEdgeInfo[ 1 ] = src.m_RampEdgeInfo[ 1 ]; + + m_ccType = src.m_ccType; + m_CCToken = src.m_CCToken; + m_bUsingCombinedSoundFile = src.m_bUsingCombinedSoundFile; + m_uRequiredCombinedChecksum = src.m_uRequiredCombinedChecksum; + m_nNumSlaves = src.m_nNumSlaves; + m_flLastSlaveEndTime = src.m_flLastSlaveEndTime; + m_bCCTokenValid = src.m_bCCTokenValid; + m_bCombinedUsingGenderToken = src.m_bCombinedUsingGenderToken; + + m_bSuppressCaptionAttenuation = src.m_bSuppressCaptionAttenuation; + + return *this; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CChoreoEvent::Init( CChoreoScene *scene ) +{ + m_nGlobalID = s_nGlobalID++; + + m_fType = UNSPECIFIED; + m_Name.clear(); + m_Parameters.clear(); + m_Parameters2.clear(); + + m_flStartTime = 0.0f; + m_flEndTime = -1.0f; + + m_pActor = NULL; + m_pChannel = NULL; + m_pScene = scene; + + m_bFixedLength = false; + m_bResumeCondition = false; + SetUsingRelativeTag( false, 0, 0 ); + + m_bTrackLookupSet = false; + + m_bLockBodyFacing = false; + m_flDistanceToTarget = 0.0f; + m_bForceShortMovement = false; + m_bSyncToFollowingGesture = false; + + m_pSubScene = NULL; + m_bProcessing = false; + m_pMixer = NULL; + m_flGestureSequenceDuration = 0.0f; + + m_nPitch = m_nYaw = 0; + + m_nNumLoops = -1; + m_nLoopsRemaining = 0; + + // Close captioning/localization support + m_CCToken.clear(); + m_ccType = CC_MASTER; + m_bUsingCombinedSoundFile = false; + m_uRequiredCombinedChecksum = 0; + m_nNumSlaves = 0; + m_flLastSlaveEndTime = 0.0f; + m_bCCTokenValid = false; + m_bCombinedUsingGenderToken = false; + m_bSuppressCaptionAttenuation = false; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : int +//----------------------------------------------------------------------------- +CChoreoEvent::EVENTTYPE CChoreoEvent::GetType( void ) +{ + return (EVENTTYPE)m_fType; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : type - +//----------------------------------------------------------------------------- +void CChoreoEvent::SetType( EVENTTYPE type ) +{ + m_fType = type; + + if ( m_fType == SPEAK || + m_fType == SUBSCENE ) + { + m_bFixedLength = true; + } + else + { + m_bFixedLength = false; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *name - +//----------------------------------------------------------------------------- +void CChoreoEvent::SetName( const char *name ) +{ + m_Name = name; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : const char +//----------------------------------------------------------------------------- +const char *CChoreoEvent::GetName( void ) +{ + return m_Name.c_str(); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *param - +//----------------------------------------------------------------------------- +void CChoreoEvent::SetParameters( const char *param ) +{ + m_Parameters = param; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : const char +//----------------------------------------------------------------------------- +const char *CChoreoEvent::GetParameters( void ) +{ + return m_Parameters.c_str(); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *param - +//----------------------------------------------------------------------------- +void CChoreoEvent::SetParameters2( const char *param ) +{ + int iLength = strlen( param ); + m_Parameters2 = param; + + // HACK: Remove trailing " " until faceposer is fixed + if ( iLength > 0 ) + { + if ( param[iLength-1] == ' ' ) + { + m_Parameters2.erase(iLength-1); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : const char +//----------------------------------------------------------------------------- +const char *CChoreoEvent::GetParameters2( void ) +{ + return m_Parameters2.c_str(); +} + +//----------------------------------------------------------------------------- +// Purpose: debugging description +// Output : const char +//----------------------------------------------------------------------------- +const char *CChoreoEvent::GetDescription( void ) +{ + static char description[ 256 ]; + + description[ 0 ] = 0; + + if ( !GetActor() ) + { + Q_snprintf( description,sizeof(description), "global %s", m_Name.c_str() ); + } + else + { + Assert( m_pChannel ); + Q_snprintf( description,sizeof(description), "%s : %s : %s -- %s \"%s\"", m_pActor->GetName(), m_pChannel->GetName(), GetName(), NameForType( GetType() ), GetParameters() ); + if ( GetType() == EXPRESSION ) + { + char sz[ 256 ]; + + Q_snprintf( sz,sizeof(sz), " \"%s\"", GetParameters2() ); + Q_strncat( description, sz, sizeof(description), COPY_ALL_CHARACTERS ); + } + } + + return description; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : starttime - +//----------------------------------------------------------------------------- +void CChoreoEvent::SetStartTime( float starttime ) +{ + m_flStartTime = starttime; + if ( m_flEndTime != -1.0f ) + { + if ( m_flEndTime < m_flStartTime ) + { + m_flEndTime = m_flStartTime; + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : float +//----------------------------------------------------------------------------- +float CChoreoEvent::GetStartTime( ) +{ + return m_flStartTime; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : endtime - +//----------------------------------------------------------------------------- +void CChoreoEvent::SetEndTime( float endtime ) +{ + bool changed = m_flEndTime != endtime; + + m_flEndTime = endtime; + + if ( endtime != -1.0f ) + { + if ( m_flEndTime < m_flStartTime ) + { + m_flEndTime = m_flStartTime; + } + + if ( changed ) + { + OnEndTimeChanged(); + } + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Output : float +//----------------------------------------------------------------------------- +float CChoreoEvent::GetEndTime( ) +{ + return m_flEndTime; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CChoreoEvent::HasEndTime( void ) +{ + return m_flEndTime != -1.0f ? true : false; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : float +//----------------------------------------------------------------------------- +float CChoreoEvent::GetCompletion( float time ) +{ + float t = (time - GetStartTime()) / (GetEndTime() - GetStartTime()); + + if (t < 0.0f) + return 0.0f; + else if (t > 1.0f) + return 1.0f; + + return t; +} + +// ICurveDataAccessor method +bool CChoreoEvent::CurveHasEndTime() +{ + return HasEndTime(); +} + +// ICurveDataAccessor method +int CChoreoEvent::CurveGetSampleCount() +{ + return GetRampCount(); +} + +// ICurveDataAccessor method +CExpressionSample *CChoreoEvent::CurveGetBoundedSample( int idx, bool& bClamped ) +{ + return GetBoundedRamp( idx, bClamped ); +} + +int CChoreoEvent::GetDefaultCurveType() +{ + return CURVE_CATMULL_ROM_TO_CATMULL_ROM; +} + +// Static method!!! +float CChoreoEvent::GetRampIntensity( ICurveDataAccessor *data, float time ) +{ + float zeroValue = 0.0f; + + // find samples that span the time + if ( !data->CurveHasEndTime() ) + { + return zeroValue; + } + + int rampCount = data->CurveGetSampleCount(); + if ( rampCount < 1 ) + { + // Full intensity + return 1.0f; + } + + CExpressionSample *esStart = NULL; + CExpressionSample *esEnd = NULL; + + // do binary search for sample in time period + int j = max( rampCount / 2, 1 ); + int i = j; + while ( i > -2 && i < rampCount + 1 ) + { + bool dummy; + esStart = data->CurveGetBoundedSample( i, dummy ); + esEnd = data->CurveGetBoundedSample( i + 1, dummy ); + + j = max( j / 2, 1 ); + if ( time < esStart->time) + { + i -= j; + } + else if ( time > esEnd->time) + { + i += j; + } + else + { + break; + } + } + + if (!esStart) + { + return 1.0f; + } + + int prev = i - 1; + int next = i + 2; + + prev = max( -1, prev ); + next = min( next, rampCount ); + + bool clamp[ 2 ]; + CExpressionSample *esPre = data->CurveGetBoundedSample( prev, clamp[ 0 ] ); + CExpressionSample *esNext = data->CurveGetBoundedSample( next, clamp[ 1 ] ); + + float dt = esEnd->time - esStart->time; + + Vector vPre( esPre->time, esPre->value, 0 ); + Vector vStart( esStart->time, esStart->value, 0 ); + Vector vEnd( esEnd->time, esEnd->value, 0 ); + Vector vNext( esNext->time, esNext->value, 0 ); + + if ( clamp[ 0 ] ) + { + vPre.x = vStart.x; + } + + if ( clamp[ 1 ] ) + { + vNext.x = vEnd.x; + } + + float f2 = 0.0f; + if ( dt > 0.0f ) + { + f2 = ( time - esStart->time ) / ( dt ); + } + f2 = clamp( f2, 0.0f, 1.0f ); + + Vector vOut; + int dummy; + int earlypart, laterpart; + + int startCurve = esStart->GetCurveType(); + int endCurve = esEnd->GetCurveType(); + + if ( startCurve == CURVE_DEFAULT ) + { + startCurve = data->GetDefaultCurveType(); + } + if ( endCurve == CURVE_DEFAULT ) + { + endCurve = data->GetDefaultCurveType(); + } + + // Not holding out value of previous curve... + Interpolator_CurveInterpolatorsForType( startCurve, dummy, earlypart ); + Interpolator_CurveInterpolatorsForType( endCurve, laterpart, dummy ); + + if ( earlypart == INTERPOLATE_HOLD ) + { + // Hold "out" of previous sample (can cause a discontinuity) + VectorLerp( vStart, vEnd, f2, vOut ); + vOut.y = vStart.y; + } + else if ( laterpart == INTERPOLATE_HOLD ) + { + // Hold "out" of previous sample (can cause a discontinuity) + VectorLerp( vStart, vEnd, f2, vOut ); + vOut.y = vEnd.y; + } + else + { + bool sameCurveType = earlypart == laterpart ? true : false; + if ( sameCurveType ) + { + Interpolator_CurveInterpolate( laterpart, vPre, vStart, vEnd, vNext, f2, vOut ); + } + else // curves differ, sigh + { + Vector vOut1, vOut2; + + Interpolator_CurveInterpolate( earlypart, vPre, vStart, vEnd, vNext, f2, vOut1 ); + Interpolator_CurveInterpolate( laterpart, vPre, vStart, vEnd, vNext, f2, vOut2 ); + + VectorLerp( vOut1, vOut2, f2, vOut ); + } + } + + float retval = clamp( vOut.y, 0.0f, 1.0f ); + return retval; +} + +//----------------------------------------------------------------------------- +// Purpose: Get intensity for event, bounded by scene global intensity +// Output : float +//----------------------------------------------------------------------------- +float CChoreoEvent::GetIntensity( ICurveDataAccessor *data, float scenetime ) +{ + float global_intensity = 1.0f; + if ( m_pScene ) + { + global_intensity = m_pScene->GetSceneRampIntensity( scenetime ); + } + else + { + Assert( 0 ); + } + + float event_intensity = _GetIntensity( data, scenetime ); + + return global_intensity * event_intensity; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : float +//----------------------------------------------------------------------------- +float CChoreoEvent::_GetIntensity( ICurveDataAccessor *data, float scenetime ) +{ + // Convert to event local time + float time = scenetime - GetStartTime(); + return GetRampIntensity( data, time ); +} + + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : dt - +//----------------------------------------------------------------------------- +void CChoreoEvent::OffsetStartTime( float dt ) +{ + SetStartTime( GetStartTime() + dt ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : dt - +//----------------------------------------------------------------------------- +void CChoreoEvent::OffsetEndTime( float dt ) +{ + if ( HasEndTime() ) + { + SetEndTime( GetEndTime() + dt ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : dt - +//----------------------------------------------------------------------------- +void CChoreoEvent::OffsetTime( float dt ) +{ + if ( HasEndTime() ) + { + m_flEndTime += dt; + } + m_flStartTime += dt; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *actor - +//----------------------------------------------------------------------------- +void CChoreoEvent::SetActor( CChoreoActor *actor ) +{ + m_pActor = actor; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : CChoreoActor +//----------------------------------------------------------------------------- +CChoreoActor *CChoreoEvent::GetActor( void ) +{ + return m_pActor; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *channel - +//----------------------------------------------------------------------------- +void CChoreoEvent::SetChannel( CChoreoChannel *channel ) +{ + m_pChannel = channel; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : CChoreoChannel +//----------------------------------------------------------------------------- +CChoreoChannel *CChoreoEvent::GetChannel( void ) +{ + return m_pChannel; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *scene - +//----------------------------------------------------------------------------- +void CChoreoEvent::SetSubScene( CChoreoScene *scene ) +{ + m_pSubScene = scene; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : CChoreoScene +//----------------------------------------------------------------------------- +CChoreoScene *CChoreoEvent::GetSubScene( void ) +{ + return m_pSubScene; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +struct EventNameMap_t +{ + CChoreoEvent::EVENTTYPE type; + char const *name; +}; + +static EventNameMap_t g_NameMap[] = +{ + { CChoreoEvent::UNSPECIFIED, "unspecified" }, // error condition!!! + { CChoreoEvent::SECTION, "section" }, + { CChoreoEvent::EXPRESSION, "expression" }, + { CChoreoEvent::LOOKAT, "lookat" }, + { CChoreoEvent::MOVETO, "moveto" }, + { CChoreoEvent::SPEAK, "speak" }, + { CChoreoEvent::GESTURE, "gesture" }, + { CChoreoEvent::SEQUENCE, "sequence" }, + { CChoreoEvent::FACE, "face" }, + { CChoreoEvent::FIRETRIGGER, "firetrigger" }, + { CChoreoEvent::FLEXANIMATION, "flexanimation" }, + { CChoreoEvent::SUBSCENE, "subscene" }, + { CChoreoEvent::LOOP, "loop" }, + { CChoreoEvent::INTERRUPT, "interrupt" }, + { CChoreoEvent::STOPPOINT, "stoppoint" }, + { CChoreoEvent::PERMIT_RESPONSES, "permitresponses" }, + { CChoreoEvent::GENERIC, "generic" }, +}; + +//----------------------------------------------------------------------------- +// Purpose: A simple class to verify the names data above at runtime +//----------------------------------------------------------------------------- +class CCheckEventNames +{ +public: + CCheckEventNames() + { + if ( ARRAYSIZE( g_NameMap ) != CChoreoEvent::NUM_TYPES ) + { + Error( "g_NameMap contains %i entries, CChoreoEvent::NUM_TYPES == %i!", + ARRAYSIZE( g_NameMap ), CChoreoEvent::NUM_TYPES ); + } + for ( int i = 0; i < CChoreoEvent::NUM_TYPES; ++i ) + { + if ( !g_NameMap[ i ].name ) + { + Error( "g_NameMap: Event type at %i has NULL name string!", i ); + } + + if ( (CChoreoEvent::EVENTTYPE)(i) == g_NameMap[ i ].type ) + continue; + + Error( "g_NameMap: Event type at %i has wrong value (%i)!", + i, (int)g_NameMap[ i ].type ); + } + } +}; +static CCheckEventNames g_CheckNamesSingleton; + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *name - +// Output : int +//----------------------------------------------------------------------------- +CChoreoEvent::EVENTTYPE CChoreoEvent::TypeForName( const char *name ) +{ + for ( int i = 0; i < NUM_TYPES; ++i ) + { + EventNameMap_t *slot = &g_NameMap[ i ]; + if ( !Q_stricmp( name, slot->name ) ) + return slot->type; + } + + Assert( !"CChoreoEvent::TypeForName failed!!!" ); + return UNSPECIFIED; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : type - +// Output : const char +//----------------------------------------------------------------------------- +const char *CChoreoEvent::NameForType( EVENTTYPE type ) +{ + int i = (int)type; + if ( i < 0 || i >= NUM_TYPES ) + { + Assert( "!CChoreoEvent::NameForType: bogus type!" ); + // returns "unspecified!!!"; + return g_NameMap[ 0 ].name; + } + + return g_NameMap[ i ].name; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +struct CCNameMap_t +{ + CChoreoEvent::CLOSECAPTION type; + char const *name; +}; + +static CCNameMap_t g_CCNameMap[] = +{ + { CChoreoEvent::CC_MASTER, "cc_master" }, // error condition!!! + { CChoreoEvent::CC_SLAVE, "cc_slave" }, + { CChoreoEvent::CC_DISABLED, "cc_disabled" }, +}; + +//----------------------------------------------------------------------------- +// Purpose: A simple class to verify the names data above at runtime +//----------------------------------------------------------------------------- +class CCheckCCNames +{ +public: + CCheckCCNames() + { + if ( ARRAYSIZE( g_CCNameMap ) != CChoreoEvent::NUM_CC_TYPES ) + { + Error( "g_CCNameMap contains %i entries, CChoreoEvent::NUM_CC_TYPES == %i!", + ARRAYSIZE( g_CCNameMap ), CChoreoEvent::NUM_CC_TYPES ); + } + for ( int i = 0; i < CChoreoEvent::NUM_CC_TYPES; ++i ) + { + if ( !g_CCNameMap[ i ].name ) + { + Error( "g_NameMap: CC type at %i has NULL name string!", i ); + } + + if ( (CChoreoEvent::CLOSECAPTION)(i) == g_CCNameMap[ i ].type ) + continue; + + Error( "g_CCNameMap: Event type at %i has wrong value (%i)!", + i, (int)g_CCNameMap[ i ].type ); + } + } +}; +static CCheckCCNames g_CheckCCNamesSingleton; + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *name - +// Output : CLOSECAPTION +//----------------------------------------------------------------------------- +CChoreoEvent::CLOSECAPTION CChoreoEvent::CCTypeForName( const char *name ) +{ + for ( int i = 0; i < NUM_CC_TYPES; ++i ) + { + CCNameMap_t *slot = &g_CCNameMap[ i ]; + if ( !Q_stricmp( name, slot->name ) ) + return slot->type; + } + + Assert( !"CChoreoEvent::TypeForName failed!!!" ); + return CC_MASTER; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : type - +// Output : const char +//----------------------------------------------------------------------------- +const char *CChoreoEvent::NameForCCType( CLOSECAPTION type ) +{ + int i = (int)type; + if ( i < 0 || i >= NUM_CC_TYPES ) + { + Assert( "!CChoreoEvent::NameForType: bogus type!" ); + // returns "unspecified!!!"; + return g_CCNameMap[ 0 ].name; + } + + return g_CCNameMap[ i ].name; +} + +//----------------------------------------------------------------------------- +// Purpose: Is the event something that can be sized ( a wave file, e.g. ) +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CChoreoEvent::IsFixedLength( void ) +{ + return m_bFixedLength; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : isfixedlength - +//----------------------------------------------------------------------------- +void CChoreoEvent::SetFixedLength( bool isfixedlength ) +{ + m_bFixedLength = isfixedlength; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : resumecondition - +//----------------------------------------------------------------------------- +void CChoreoEvent::SetResumeCondition( bool resumecondition ) +{ + m_bResumeCondition = resumecondition; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CChoreoEvent::IsResumeCondition( void ) +{ + return m_bResumeCondition; +} + + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : lockbodyfacing - +//----------------------------------------------------------------------------- +void CChoreoEvent::SetLockBodyFacing( bool lockbodyfacing ) +{ + m_bLockBodyFacing = lockbodyfacing; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CChoreoEvent::IsLockBodyFacing( void ) +{ + return m_bLockBodyFacing; +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : distancetotarget - +//----------------------------------------------------------------------------- +void CChoreoEvent::SetDistanceToTarget( float distancetotarget ) +{ + m_flDistanceToTarget = distancetotarget; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : Returns ideal distance to target +//----------------------------------------------------------------------------- +float CChoreoEvent::GetDistanceToTarget( void ) +{ + return m_flDistanceToTarget; +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : set if small (sub-1/2 bbox) movements are forced +//----------------------------------------------------------------------------- + +void CChoreoEvent::SetForceShortMovement( bool bForceShortMovement ) +{ + m_bForceShortMovement = bForceShortMovement; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : get if small (sub-1/2 bbox) movements are forced +//----------------------------------------------------------------------------- + +bool CChoreoEvent::GetForceShortMovement( void ) +{ + return m_bForceShortMovement; +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : set if the gesture should sync its exit tag with the following gestures entry tag +//----------------------------------------------------------------------------- + +void CChoreoEvent::SetSyncToFollowingGesture( bool bSyncToFollowingGesture ) +{ + m_bSyncToFollowingGesture = bSyncToFollowingGesture; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : get if the gesture should sync its exit tag with the following gestures entry tag +//----------------------------------------------------------------------------- + +bool CChoreoEvent::GetSyncToFollowingGesture( void ) +{ + return m_bSyncToFollowingGesture; +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Output : float +//----------------------------------------------------------------------------- +float CChoreoEvent::GetDuration( void ) +{ + if ( HasEndTime() ) + { + return GetEndTime() - GetStartTime(); + } + + return 0.0f; +} + + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CChoreoEvent::ClearAllRelativeTags( void ) +{ + m_RelativeTags.Purge(); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : int +//----------------------------------------------------------------------------- +int CChoreoEvent::GetNumRelativeTags( void ) +{ + return m_RelativeTags.Size(); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : tagnum - +// Output : CEventRelativeTag +//----------------------------------------------------------------------------- +CEventRelativeTag *CChoreoEvent::GetRelativeTag( int tagnum ) +{ + Assert( tagnum >= 0 && tagnum < m_RelativeTags.Size() ); + return &m_RelativeTags[ tagnum ]; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *tagname - +// percentage - +//----------------------------------------------------------------------------- +void CChoreoEvent::AddRelativeTag( const char *tagname, float percentage ) +{ + CEventRelativeTag rt( this, tagname, percentage ); + m_RelativeTags.AddToTail( rt ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *tagname - +//----------------------------------------------------------------------------- +void CChoreoEvent::RemoveRelativeTag( const char *tagname ) +{ + for ( int i = 0; i < m_RelativeTags.Size(); i++ ) + { + CEventRelativeTag *prt = &m_RelativeTags[ i ]; + if ( !prt ) + continue; + + if ( !stricmp( prt->GetName(), tagname ) ) + { + m_RelativeTags.Remove( i ); + return; + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *tagname - +// Output : CEventRelativeTag * +//----------------------------------------------------------------------------- +CEventRelativeTag * CChoreoEvent::FindRelativeTag( const char *tagname ) +{ + for ( int i = 0; i < m_RelativeTags.Size(); i++ ) + { + CEventRelativeTag *prt = &m_RelativeTags[ i ]; + if ( !prt ) + continue; + + if ( !stricmp( prt->GetName(), tagname ) ) + { + return prt; + } + } + return NULL; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CChoreoEvent::IsUsingRelativeTag( void ) +{ + return m_bUsesTag; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : usetag - +// 0 - +//----------------------------------------------------------------------------- +void CChoreoEvent::SetUsingRelativeTag( bool usetag, const char *tagname /*= 0*/, + const char *wavname /* = 0 */ ) +{ + m_bUsesTag = usetag; + if ( tagname ) + { + m_TagName = tagname; + } + else + { + m_TagName.clear(); + } + if ( wavname ) + { + m_TagWavName = wavname; + } + else + { + m_TagWavName.clear(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : const char +//----------------------------------------------------------------------------- +const char *CChoreoEvent::GetRelativeTagName( void ) +{ + return m_TagName.c_str(); +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Output : const char +//----------------------------------------------------------------------------- +const char *CChoreoEvent::GetRelativeWavName( void ) +{ + return m_TagWavName.c_str(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CChoreoEvent::ClearAllTimingTags( void ) +{ + m_TimingTags.Purge(); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : int +//----------------------------------------------------------------------------- +int CChoreoEvent::GetNumTimingTags( void ) +{ + return m_TimingTags.Size(); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : tagnum - +// Output : CEventRelativeTag +//----------------------------------------------------------------------------- +CFlexTimingTag *CChoreoEvent::GetTimingTag( int tagnum ) +{ + Assert( tagnum >= 0 && tagnum < m_TimingTags.Size() ); + return &m_TimingTags[ tagnum ]; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *tagname - +// percentage - +//----------------------------------------------------------------------------- +void CChoreoEvent::AddTimingTag( const char *tagname, float percentage, bool locked ) +{ + CFlexTimingTag tt( this, tagname, percentage, locked ); + m_TimingTags.AddToTail( tt ); + + // Sort tags + CFlexTimingTag temp( (CChoreoEvent *)0x1, "", 0.0f, false ); + + // ugly bubble sort + for ( int i = 0; i < m_TimingTags.Size(); i++ ) + { + for ( int j = i + 1; j < m_TimingTags.Size(); j++ ) + { + CFlexTimingTag *t1 = &m_TimingTags[ i ]; + CFlexTimingTag *t2 = &m_TimingTags[ j ]; + + if ( t1->GetPercentage() > t2->GetPercentage() ) + { + temp = *t1; + *t1 = *t2; + *t2 = temp; + } + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *tagname - +//----------------------------------------------------------------------------- +void CChoreoEvent::RemoveTimingTag( const char *tagname ) +{ + for ( int i = 0; i < m_TimingTags.Size(); i++ ) + { + CFlexTimingTag *ptt = &m_TimingTags[ i ]; + if ( !ptt ) + continue; + + if ( !stricmp( ptt->GetName(), tagname ) ) + { + m_TimingTags.Remove( i ); + return; + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *tagname - +// Output : CEventRelativeTag * +//----------------------------------------------------------------------------- +CFlexTimingTag * CChoreoEvent::FindTimingTag( const char *tagname ) +{ + for ( int i = 0; i < m_TimingTags.Size(); i++ ) + { + CFlexTimingTag *ptt = &m_TimingTags[ i ]; + if ( !ptt ) + continue; + + if ( !stricmp( ptt->GetName(), tagname ) ) + { + return ptt; + } + } + return NULL; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CChoreoEvent::OnEndTimeChanged( void ) +{ + int c = GetNumFlexAnimationTracks(); + for ( int i = 0; i < c; i++ ) + { + CFlexAnimationTrack *track = GetFlexAnimationTrack( i ); + Assert( track ); + if ( !track ) + continue; + + track->Resort( 0 ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : int +//----------------------------------------------------------------------------- +int CChoreoEvent::GetNumFlexAnimationTracks( void ) +{ + return m_FlexAnimationTracks.Size(); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : index - +// Output : CFlexAnimationTrack +//----------------------------------------------------------------------------- +CFlexAnimationTrack *CChoreoEvent::GetFlexAnimationTrack( int index ) +{ + if ( index < 0 || index >= GetNumFlexAnimationTracks() ) + return NULL; + return m_FlexAnimationTracks[ index ]; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *controllername - +// Output : CFlexAnimationTrack +//----------------------------------------------------------------------------- +CFlexAnimationTrack *CChoreoEvent::AddTrack( const char *controllername ) +{ + CFlexAnimationTrack *newTrack = new CFlexAnimationTrack( this ); + newTrack->SetFlexControllerName( controllername ); + + m_FlexAnimationTracks.AddToTail( newTrack ); + + return newTrack; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : index - +//----------------------------------------------------------------------------- +void CChoreoEvent::RemoveTrack( int index ) +{ + CFlexAnimationTrack *track = GetFlexAnimationTrack( index ); + if ( !track ) + return; + + m_FlexAnimationTracks.Remove( index ); + delete track; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CChoreoEvent::RemoveAllTracks( void ) +{ + while ( GetNumFlexAnimationTracks() > 0 ) + { + RemoveTrack( 0 ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *controllername - +// Output : CFlexAnimationTrack +//----------------------------------------------------------------------------- +CFlexAnimationTrack *CChoreoEvent::FindTrack( const char *controllername ) +{ + for ( int i = 0; i < GetNumFlexAnimationTracks(); i++ ) + { + CFlexAnimationTrack *t = GetFlexAnimationTrack( i ); + if ( t && !stricmp( t->GetFlexControllerName(), controllername ) ) + { + return t; + } + } + return NULL; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CChoreoEvent::GetTrackLookupSet( void ) +{ + return m_bTrackLookupSet; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : set - +//----------------------------------------------------------------------------- +void CChoreoEvent::SetTrackLookupSet( bool set ) +{ + m_bTrackLookupSet = set; +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CChoreoEvent::IsProcessing( void ) const +{ + return m_bProcessing; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *cb - +// t - +//----------------------------------------------------------------------------- +void CChoreoEvent::StartProcessing( IChoreoEventCallback *cb, CChoreoScene *scene, float t ) +{ + Assert( !m_bProcessing ); + m_bProcessing = true; + if ( cb ) + { + cb->StartEvent( t, scene, this ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *cb - +// t - +//----------------------------------------------------------------------------- +void CChoreoEvent::ContinueProcessing( IChoreoEventCallback *cb, CChoreoScene *scene, float t ) +{ + Assert( m_bProcessing ); + if ( cb ) + { + cb->ProcessEvent( t, scene, this ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *cb - +// t - +//----------------------------------------------------------------------------- +void CChoreoEvent::StopProcessing( IChoreoEventCallback *cb, CChoreoScene *scene, float t ) +{ + Assert( m_bProcessing ); + if ( cb ) + { + cb->EndEvent( t, scene, this ); + } + m_bProcessing = false; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *cb - +// t - +//----------------------------------------------------------------------------- +bool CChoreoEvent::CheckProcessing( IChoreoEventCallback *cb, CChoreoScene *scene, float t ) +{ + //Assert( !m_bProcessing ); + if ( cb ) + { + return cb->CheckEvent( t, scene, this ); + } + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CChoreoEvent::ResetProcessing( void ) +{ + if ( GetType() == LOOP ) + { + m_nLoopsRemaining = m_nNumLoops; + } + + m_bProcessing = false; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *mixer - +//----------------------------------------------------------------------------- +void CChoreoEvent::SetMixer( CAudioMixer *mixer ) +{ + m_pMixer = mixer; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : CAudioMixer +//----------------------------------------------------------------------------- +CAudioMixer *CChoreoEvent::GetMixer( void ) const +{ + return m_pMixer; +} + +// Snap to scene framerate +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CChoreoEvent::SnapTimes() +{ + if ( HasEndTime() && !IsFixedLength() ) + { + m_flEndTime = SnapTime( m_flEndTime ); + } + float oldstart = m_flStartTime; + m_flStartTime = SnapTime( m_flStartTime ); + + // Don't snap end time for fixed length events, just set based on new start time + if ( IsFixedLength() ) + { + float dt = m_flStartTime - oldstart; + m_flEndTime += dt; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : t - +// Output : float +//----------------------------------------------------------------------------- +float CChoreoEvent::SnapTime( float t ) +{ + CChoreoScene *scene = GetScene(); + if ( !scene) + { + Assert( 0 ); + return t; + } + + return scene->SnapTime( t ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : CChoreoScene +//----------------------------------------------------------------------------- +CChoreoScene *CChoreoEvent::GetScene( void ) +{ + return m_pScene; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *scene - +//----------------------------------------------------------------------------- +void CChoreoEvent::SetScene( CChoreoScene *scene ) +{ + m_pScene = scene; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : t - +// Output : char const +//----------------------------------------------------------------------------- +const char *CChoreoEvent::NameForAbsoluteTagType( AbsTagType t ) +{ + switch ( t ) + { + case PLAYBACK: + return "playback_time"; + case ORIGINAL: + return "shifted_time"; + default: + break; + } + + return "AbsTagType(unknown)"; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *name - +// Output : AbsTagType +//----------------------------------------------------------------------------- +CChoreoEvent::AbsTagType CChoreoEvent::TypeForAbsoluteTagName( const char *name ) +{ + if ( !Q_strcasecmp( name, "playback_time" ) ) + { + return PLAYBACK; + } + else if ( !Q_strcasecmp( name, "shifted_time" ) ) + { + return ORIGINAL; + } + + return (AbsTagType)-1; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : type - +//----------------------------------------------------------------------------- +void CChoreoEvent::ClearAllAbsoluteTags( AbsTagType type ) +{ + m_AbsoluteTags[ type ].Purge(); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : type - +// Output : int +//----------------------------------------------------------------------------- +int CChoreoEvent::GetNumAbsoluteTags( AbsTagType type ) +{ + return m_AbsoluteTags[ type ].Size(); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : type - +// tagnum - +// Output : CEventAbsoluteTag +//----------------------------------------------------------------------------- +CEventAbsoluteTag *CChoreoEvent::GetAbsoluteTag( AbsTagType type, int tagnum ) +{ + Assert( tagnum >= 0 && tagnum < m_AbsoluteTags[ type ].Size() ); + return &m_AbsoluteTags[ type ][ tagnum ]; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : type - +// *tagname - +// Output : CEventAbsoluteTag +//----------------------------------------------------------------------------- +CEventAbsoluteTag *CChoreoEvent::FindAbsoluteTag( AbsTagType type, const char *tagname ) +{ + for ( int i = 0; i < m_AbsoluteTags[ type ].Size(); i++ ) + { + CEventAbsoluteTag *ptag = &m_AbsoluteTags[ type ][ i ]; + if ( !ptag ) + continue; + + if ( !stricmp( ptag->GetName(), tagname ) ) + { + return ptag; + } + } + return NULL; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : type - +// *tagname - +// t - +//----------------------------------------------------------------------------- +void CChoreoEvent::AddAbsoluteTag( AbsTagType type, const char *tagname, float t ) +{ + CEventAbsoluteTag at( this, tagname, t ); + m_AbsoluteTags[ type ].AddToTail( at ); + + // Sort tags + CEventAbsoluteTag temp( (CChoreoEvent *)0x1, "", 0.0f ); + + // ugly bubble sort + for ( int i = 0; i < m_AbsoluteTags[ type ].Size(); i++ ) + { + for ( int j = i + 1; j < m_AbsoluteTags[ type ].Size(); j++ ) + { + CEventAbsoluteTag *t1 = &m_AbsoluteTags[ type ][ i ]; + CEventAbsoluteTag *t2 = &m_AbsoluteTags[ type ][ j ]; + + if ( t1->GetPercentage() > t2->GetPercentage() ) + { + temp = *t1; + *t1 = *t2; + *t2 = temp; + } + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : type - +// *tagname - +//----------------------------------------------------------------------------- +void CChoreoEvent::RemoveAbsoluteTag( AbsTagType type, const char *tagname ) +{ + for ( int i = 0; i < m_AbsoluteTags[ type ].Size(); i++ ) + { + CEventAbsoluteTag *ptag = &m_AbsoluteTags[ type ][ i ]; + if ( !ptag ) + continue; + + if ( !stricmp( ptag->GetName(), tagname ) ) + { + m_AbsoluteTags[ type ].Remove( i ); + return; + } + } +} + + + +//----------------------------------------------------------------------------- +// Purpose: makes sure tags in PLAYBACK are in the same order as ORIGINAL +// Input : +// Output : true if they were in order, false if it has to reorder them +//----------------------------------------------------------------------------- +bool CChoreoEvent::VerifyTagOrder( ) +{ + bool bInOrder = true; + + // Sort tags + CEventAbsoluteTag temp( (CChoreoEvent *)0x1, "", 0.0f ); + + for ( int i = 0; i < m_AbsoluteTags[ CChoreoEvent::ORIGINAL ].Size(); i++ ) + { + CEventAbsoluteTag *ptag = &m_AbsoluteTags[ CChoreoEvent::ORIGINAL ][ i ]; + if ( !ptag ) + continue; + + CEventAbsoluteTag *t1 = &m_AbsoluteTags[ CChoreoEvent::PLAYBACK ][ i ]; + + if ( stricmp( ptag->GetName(), t1->GetName() ) == 0) + continue; + + bInOrder = false; + for ( int j = i + 1; j < m_AbsoluteTags[ CChoreoEvent::PLAYBACK ].Size(); j++ ) + { + CEventAbsoluteTag *t2 = &m_AbsoluteTags[ CChoreoEvent::PLAYBACK ][ j ]; + + if ( stricmp( ptag->GetName(), t2->GetName() ) == 0 ) + { + temp = *t1; + *t1 = *t2; + *t2 = temp; + break; + } + } + } + return bInOrder; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : type - +// *tagname - +//----------------------------------------------------------------------------- + +float CChoreoEvent::GetBoundedAbsoluteTagPercentage( AbsTagType type, int tagnum ) +{ + if ( tagnum <= -2 ) + { + /* + if (GetNumAbsoluteTags( type ) >= 1) + { + CEventAbsoluteTag *tag = GetAbsoluteTag( type, 0 ); + Assert( tag ); + return -tag->GetTime(); + } + */ + return 0.0f; // -0.5f; + } + else if ( tagnum == -1 ) + { + return 0.0f; + } + else if ( tagnum == GetNumAbsoluteTags( type ) ) + { + return 1.0; + } + else if ( tagnum > GetNumAbsoluteTags( type ) ) + { + /* + if (GetNumAbsoluteTags( type ) >= 1) + { + CEventAbsoluteTag *tag = GetAbsoluteTag( type, tagnum - 2 ); + Assert( tag ); + return 2.0 - tag->GetTime(); + } + */ + return 1.0; // 1.5; + } + + /* + { + float duration = GetDuration(); + + if ( type == SHIFTED ) + { + float seqduration; + GetGestureSequenceDuration( seqduration ); + return seqduration; + } + return duration; + } + */ + + CEventAbsoluteTag *tag = GetAbsoluteTag( type, tagnum ); + Assert( tag ); + return tag->GetPercentage(); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : t - +// Output : float +//----------------------------------------------------------------------------- +float CChoreoEvent::GetOriginalPercentageFromPlaybackPercentage( float t ) +{ + Assert( GetType() == GESTURE ); + if ( GetType() != GESTURE ) + return t; + + int count = GetNumAbsoluteTags( PLAYBACK ); + + if ( count != GetNumAbsoluteTags( ORIGINAL ) ) + { + return t; + } + + if ( count <= 0 ) + { + return t; + } + + if ( t <= 0.0f ) + return 0.0f; + + float s = 0.0f, n = 0.0f; + + // find what tags this is between + int i; + for ( i = -1 ; i < count; i++ ) + { + s = GetBoundedAbsoluteTagPercentage( PLAYBACK, i ); + n = GetBoundedAbsoluteTagPercentage( PLAYBACK, i + 1 ); + + if ( t >= s && t <= n ) + { + break; + } + } + + int prev = i - 1; + int start = i; + int end = i + 1; + int next = i + 2; + + prev = max( -2, prev ); + start = max( -1, start ); + end = min( end, count ); + next = min( next, count + 1 ); + + CEventAbsoluteTag *pStartTag = NULL; + CEventAbsoluteTag *pEndTag = NULL; + + // check for linear portion of lookup + if (start >= 0 && start < count) + { + pStartTag = GetAbsoluteTag( PLAYBACK, start ); + } + if (end >= 0 && end < count) + { + pEndTag = GetAbsoluteTag( PLAYBACK, end ); + } + + if (pStartTag && pEndTag) + { + if (pStartTag->GetLinear() && pEndTag->GetLinear()) + { + CEventAbsoluteTag *pOrigStartTag = GetAbsoluteTag( ORIGINAL, start ); + CEventAbsoluteTag *pOrigEndTag = GetAbsoluteTag( ORIGINAL, end ); + + if (pOrigStartTag && pOrigEndTag) + { + s = ( t - pStartTag->GetPercentage() ) / (pEndTag->GetPercentage() - pStartTag->GetPercentage()); + return (1 - s) * pOrigStartTag->GetPercentage() + s * pOrigEndTag->GetPercentage(); + } + } + } + + float dt = n - s; + + Vector vPre( GetBoundedAbsoluteTagPercentage( PLAYBACK, prev ), GetBoundedAbsoluteTagPercentage( ORIGINAL, prev ), 0 ); + Vector vStart( GetBoundedAbsoluteTagPercentage( PLAYBACK, start ), GetBoundedAbsoluteTagPercentage( ORIGINAL, start ), 0 ); + Vector vEnd( GetBoundedAbsoluteTagPercentage( PLAYBACK, end ), GetBoundedAbsoluteTagPercentage( ORIGINAL, end ), 0 ); + Vector vNext( GetBoundedAbsoluteTagPercentage( PLAYBACK, next ), GetBoundedAbsoluteTagPercentage( ORIGINAL, next ), 0 ); + + // simulate sections of either side of "linear" portion of ramp as linear slope + if (pStartTag && pStartTag->GetLinear()) + { + vPre.Init( vStart.x - (vEnd.x - vStart.x), vStart.y - (vEnd.y - vStart.y), 0 ); + } + + if (pEndTag && pEndTag->GetLinear()) + { + vNext.Init( vEnd.x + (vEnd.x - vStart.x), vEnd.y + (vEnd.y - vStart.y), 0 ); + } + + + float f2 = 0.0f; + if ( dt > 0.0f ) + { + f2 = ( t - s ) / ( dt ); + } + f2 = clamp( f2, 0.0f, 1.0f ); + + Vector vOut; + Catmull_Rom_Spline_NormalizeX( + vPre, + vStart, + vEnd, + vNext, + f2, + vOut ); + + return vOut.y; + + /* + float duration; + GetGestureSequenceDuration( duration ); + + float retval = clamp( vOut.y, 0.0f, duration ); + return retval; + */ +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : t - +// Output : float +//----------------------------------------------------------------------------- +float CChoreoEvent::GetPlaybackPercentageFromOriginalPercentage( float t ) +{ + Assert( GetType() == GESTURE ); + if ( GetType() != GESTURE ) + return t; + + int count = GetNumAbsoluteTags( PLAYBACK ); + + if ( count != GetNumAbsoluteTags( ORIGINAL ) ) + { + return t; + } + + if ( count <= 0 ) + { + return t; + } + + if ( t <= 0.0f ) + return 0.0f; + + float s = 0.0f, n = 0.0f; + + // find what tags this is between + int i; + for ( i = -1 ; i < count; i++ ) + { + s = GetBoundedAbsoluteTagPercentage( PLAYBACK, i ); + n = GetBoundedAbsoluteTagPercentage( PLAYBACK, i + 1 ); + + if ( t >= s && t <= n ) + { + break; + } + } + + int prev = i - 1; + int start = i; + int end = i + 1; + int next = i + 2; + + prev = max( -2, prev ); + start = max( -1, start ); + end = min( end, count ); + next = min( next, count + 1 ); + + CEventAbsoluteTag *pStartTag = NULL; + CEventAbsoluteTag *pEndTag = NULL; + + // check for linear portion of lookup + if (start >= 0 && start < count) + { + pStartTag = GetAbsoluteTag( ORIGINAL, start ); + } + if (end >= 0 && end < count) + { + pEndTag = GetAbsoluteTag( ORIGINAL, end ); + } + + // check for linear portion of lookup + if (pStartTag && pEndTag) + { + if (pStartTag->GetLinear() && pEndTag->GetLinear()) + { + CEventAbsoluteTag *pPlaybackStartTag = GetAbsoluteTag( PLAYBACK, start ); + CEventAbsoluteTag *pPlaybackEndTag = GetAbsoluteTag( PLAYBACK, end ); + + if (pPlaybackStartTag && pPlaybackEndTag) + { + s = ( t - pStartTag->GetPercentage() ) / (pEndTag->GetPercentage() - pStartTag->GetPercentage()); + return (1 - s) * pPlaybackStartTag->GetPercentage() + s * pPlaybackEndTag->GetPercentage(); + } + } + } + + float dt = n - s; + + Vector vPre( GetBoundedAbsoluteTagPercentage( ORIGINAL, prev ), GetBoundedAbsoluteTagPercentage( PLAYBACK, prev ), 0 ); + Vector vStart( GetBoundedAbsoluteTagPercentage( ORIGINAL, start ), GetBoundedAbsoluteTagPercentage( PLAYBACK, start ), 0 ); + Vector vEnd( GetBoundedAbsoluteTagPercentage( ORIGINAL, end ), GetBoundedAbsoluteTagPercentage( PLAYBACK, end ), 0 ); + Vector vNext( GetBoundedAbsoluteTagPercentage( ORIGINAL, next ), GetBoundedAbsoluteTagPercentage( PLAYBACK, next ), 0 ); + + // simulate sections of either side of "linear" portion of ramp as linear slope + if (pStartTag && pStartTag->GetLinear()) + { + vPre.Init( vStart.x - (vEnd.x - vStart.x), vStart.y - (vEnd.y - vStart.y), 0 ); + } + + if (pEndTag && pEndTag->GetLinear()) + { + vNext.Init( vEnd.x + (vEnd.x - vStart.x), vEnd.y + (vEnd.y - vStart.y), 0 ); + } + + float f2 = 0.0f; + if ( dt > 0.0f ) + { + f2 = ( t - s ) / ( dt ); + } + f2 = clamp( f2, 0.0f, 1.0f ); + + Vector vOut; + Catmull_Rom_Spline_NormalizeX( + vPre, + vStart, + vEnd, + vNext, + f2, + vOut ); + + return vOut.y; + + /* + float duration; + GetGestureSequenceDuration( duration ); + + float retval = clamp( vOut.y, 0.0f, duration ); + return retval; + */ +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : duration - +//----------------------------------------------------------------------------- +void CChoreoEvent::SetGestureSequenceDuration( float duration ) +{ + m_flGestureSequenceDuration = duration; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : duration - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CChoreoEvent::GetGestureSequenceDuration( float& duration ) +{ + bool valid = m_flGestureSequenceDuration != 0.0f; + + if ( !valid ) + { + duration = GetDuration(); + } + else + { + duration = m_flGestureSequenceDuration; + } + + return valid; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : pitch - +//----------------------------------------------------------------------------- +int CChoreoEvent::GetPitch( void ) const +{ + return m_nPitch; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : pitch - +//----------------------------------------------------------------------------- +void CChoreoEvent::SetPitch( int pitch ) +{ + m_nPitch = pitch; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : yaw - +//----------------------------------------------------------------------------- +int CChoreoEvent::GetYaw( void ) const +{ + return m_nYaw; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : yaw - +//----------------------------------------------------------------------------- +void CChoreoEvent::SetYaw( int yaw ) +{ + m_nYaw = yaw; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : t - +// -1 - +//----------------------------------------------------------------------------- +void CChoreoEvent::SetLoopCount( int numloops ) +{ + Assert( GetType() == LOOP ); + // Never below -1 + m_nNumLoops = max( numloops, -1 ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : int +//----------------------------------------------------------------------------- +int CChoreoEvent::GetNumLoopsRemaining( void ) +{ + Assert( GetType() == LOOP ); + + return m_nLoopsRemaining; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : loops - +//----------------------------------------------------------------------------- +void CChoreoEvent::SetNumLoopsRemaining( int loops ) +{ + Assert( GetType() == LOOP ); + + m_nLoopsRemaining = loops; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : int +//----------------------------------------------------------------------------- +int CChoreoEvent::GetLoopCount( void ) +{ + Assert( GetType() == LOOP ); + return m_nNumLoops; +} + +EdgeInfo_t *CChoreoEvent::GetRampEdgeInfo( int idx ) +{ + return &m_RampEdgeInfo[ idx ]; +} + +int CChoreoEvent::GetRampCount( void ) +{ + return m_Ramp.Count(); +} + +CExpressionSample *CChoreoEvent::GetRamp( int index ) +{ + if ( index < 0 || index >= GetRampCount() ) + return NULL; + + return &m_Ramp[ index ]; +} + +CExpressionSample *CChoreoEvent::AddRamp( float time, float value, bool selected ) +{ + CExpressionSample sample; + + sample.time = time; + sample.value = value; + sample.selected = selected; + + int idx = m_Ramp.AddToTail( sample ); + return &m_Ramp[ idx ]; +} + +void CChoreoEvent::DeleteRamp( int index ) +{ + if ( index < 0 || index >= GetRampCount() ) + return; + + m_Ramp.Remove( index ); +} + +void CChoreoEvent::ClearRamp( void ) +{ + m_Ramp.RemoveAll(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CChoreoEvent::ResortRamp( void ) +{ + for ( int i = 0; i < m_Ramp.Size(); i++ ) + { + for ( int j = i + 1; j < m_Ramp.Size(); j++ ) + { + CExpressionSample src = m_Ramp[ i ]; + CExpressionSample dest = m_Ramp[ j ]; + + if ( src.time > dest.time ) + { + m_Ramp[ i ] = dest; + m_Ramp[ j ] = src; + } + } + } + + RemoveOutOfRangeRampSamples(); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : number - +// Output : CExpressionSample +//----------------------------------------------------------------------------- +CExpressionSample *CChoreoEvent::GetBoundedRamp( int number, bool& bClamped ) +{ + // Search for two samples which span time f + if ( number < 0 ) + { + static CExpressionSample nullstart; + nullstart.time = 0.0f; + nullstart.value = RampGetEdgeZeroValue( true ); + nullstart.SetCurveType( RampGetEdgeCurveType( true ) ); + bClamped = true; + return &nullstart; + } + else if ( number >= GetRampCount() ) + { + static CExpressionSample nullend; + nullend.time = GetDuration(); + nullend.value = RampGetEdgeZeroValue( false ); + nullend.SetCurveType( RampGetEdgeCurveType( false ) ); + bClamped = true; + return &nullend; + } + + bClamped = false; + return GetRamp( number ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CChoreoEvent::RemoveOutOfRangeRampSamples( void ) +{ + float duration = GetDuration(); + + int c = GetRampCount(); + for ( int i = c-1; i >= 0; i-- ) + { + CExpressionSample src = m_Ramp[ i ]; + if ( src.time < 0 || + src.time > duration + 0.01 ) + { + m_Ramp.Remove( i ); + } + } +} + + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CChoreoEvent::RescaleGestureTimes( float newstart, float newend ) +{ + if ( GetType() != CChoreoEvent::GESTURE ) + return; + + // Did it actually change + if ( newstart == GetStartTime() && + newend == GetEndTime() ) + { + return; + } + + float newduration = newend - newstart; + + float dt = 0.0f; + //If the end is moving, leave tags stay where they are (dt == 0.0f) + if ( newstart != GetStartTime() ) + { + // Otherwise, if the new start is later, then tags need to be shifted backwards + dt -= ( newstart - GetStartTime() ); + } + + int i; + int count = GetNumAbsoluteTags( CChoreoEvent::PLAYBACK ); + for ( i = 0; i < count; i++ ) + { + CEventAbsoluteTag *tag = GetAbsoluteTag( CChoreoEvent::PLAYBACK, i ); + float tagtime = tag->GetPercentage() * GetDuration(); + + tagtime += dt; + + tagtime = clamp( tagtime / newduration, 0.0f, 1.0f ); + + tag->SetPercentage( tagtime ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Make sure tags aren't co-located or out of order +//----------------------------------------------------------------------------- +bool CChoreoEvent::PreventTagOverlap( void ) +{ + bool bHadOverlap = false; + + // FIXME: limit to single frame? + float minDp = 0.01; + + float minP = 1.00; + + int count = GetNumAbsoluteTags( CChoreoEvent::PLAYBACK ); + for ( int i = count - 1; i >= 0; i-- ) + { + CEventAbsoluteTag *tag = GetAbsoluteTag( CChoreoEvent::PLAYBACK, i ); + + if (tag->GetPercentage() > minP) + { + tag->SetPercentage( minP ); + + minDp = min( 0.01, minP / (i + 1) ); + bHadOverlap = true; + } + else + { + minP = tag->GetPercentage(); + } + minP = max( minP - minDp, 0 ); + } + + return bHadOverlap; +} + + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : type - +// Output : CEventAbsoluteTag +//----------------------------------------------------------------------------- +CEventAbsoluteTag *CChoreoEvent::FindEntryTag( AbsTagType type ) +{ + for ( int i = 0; i < m_AbsoluteTags[ type ].Size(); i++ ) + { + CEventAbsoluteTag *ptag = &m_AbsoluteTags[ type ][ i ]; + if ( !ptag ) + continue; + + if ( ptag->GetEntry() ) + { + return ptag; + } + } + return NULL; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : type - +// Output : CEventAbsoluteTag +//----------------------------------------------------------------------------- +CEventAbsoluteTag *CChoreoEvent::FindExitTag( AbsTagType type ) +{ + for ( int i = 0; i < m_AbsoluteTags[ type ].Size(); i++ ) + { + CEventAbsoluteTag *ptag = &m_AbsoluteTags[ type ][ i ]; + if ( !ptag ) + continue; + + if ( ptag->GetExit() ) + { + return ptag; + } + } + return NULL; +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *style - +// maxlen - +//----------------------------------------------------------------------------- +void CChoreoEvent::GetMovementStyle( char *style, int maxlen ) +{ + Assert( GetType() == MOVETO ); + + style[0] = 0; + + const char *in = m_Parameters2.c_str(); + char *out = style; + + while ( *in && *in != '\0' && *in != ' ' ) + { + if ( out - style >= maxlen - 1 ) + break; + *out++ = *in++; + } + + *out = 0; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *style - +// maxlen - +//----------------------------------------------------------------------------- +void CChoreoEvent::GetDistanceStyle( char *style, int maxlen ) +{ + Assert( GetType() == MOVETO ); + + style[0]= 0; + + const char *in = Q_strstr( m_Parameters2.c_str(), " " ); + if ( !in ) + return; + + in++; + char *out = style; + + while ( *in && *in != '\0' ) + { + if ( out - style >= maxlen - 1 ) + break; + *out++ = *in++; + } + + *out = 0; +} + +void CChoreoEvent::SetCloseCaptionType( CLOSECAPTION type ) +{ + Assert( m_fType == SPEAK ); + m_ccType = type; +} + +CChoreoEvent::CLOSECAPTION CChoreoEvent::GetCloseCaptionType() const +{ + Assert( m_fType == SPEAK ); + return (CLOSECAPTION)m_ccType; +} + +void CChoreoEvent::SetCloseCaptionToken( char const *token ) +{ + Assert( m_fType == SPEAK ); + Assert( token ); + m_CCToken = token; +} + +char const *CChoreoEvent::GetCloseCaptionToken() const +{ + Assert( m_fType == SPEAK ); + return m_CCToken.c_str(); +} + +bool CChoreoEvent::GetPlaybackCloseCaptionToken( char *dest, int destlen ) +{ + dest[0] = 0; + + Assert( m_fType == SPEAK ); + + switch ( m_ccType ) + { + default: + case CC_DISABLED: + { + return false; + } + case CC_SLAVE: + { + // If it's a slave, then only disable if we're not using the combined wave + if ( IsUsingCombinedFile() ) + { + return false; + } + + if ( m_CCToken[ 0 ] != 0 ) + { + Q_strncpy( dest, m_CCToken.c_str(), destlen ); + } + else + { + Q_strncpy( dest, m_Parameters.c_str(), destlen ); + } + return true; + } + case CC_MASTER: + { + // Always use the override if we're the master, otherwise always use the default + // parameter + if ( m_CCToken[ 0 ] != 0 ) + { + Q_strncpy( dest, m_CCToken.c_str(), destlen ); + } + else + { + Q_strncpy( dest, m_Parameters.c_str(), destlen ); + } + return true; + } + } + + return false; +} + + +void CChoreoEvent::SetUsingCombinedFile( bool isusing ) +{ + Assert( m_fType == SPEAK ); + m_bUsingCombinedSoundFile = isusing; +} + +bool CChoreoEvent::IsUsingCombinedFile() const +{ + Assert( m_fType == SPEAK ); + return m_bUsingCombinedSoundFile; +} + +void CChoreoEvent::SetRequiredCombinedChecksum( unsigned int checksum ) +{ + Assert( m_fType == SPEAK ); + m_uRequiredCombinedChecksum = checksum; +} + +unsigned int CChoreoEvent::GetRequiredCombinedChecksum() +{ + Assert( m_fType == SPEAK ); + return m_uRequiredCombinedChecksum; +} + +void CChoreoEvent::SetNumSlaves( int num ) +{ + Assert( m_fType == SPEAK ); + Assert( num >= 0 ); + m_nNumSlaves = num; +} + +int CChoreoEvent::GetNumSlaves() const +{ + Assert( m_fType == SPEAK ); + return m_nNumSlaves; +} + +void CChoreoEvent::SetLastSlaveEndTime( float t ) +{ + Assert( m_fType == SPEAK ); + m_flLastSlaveEndTime = t; +} + +float CChoreoEvent::GetLastSlaveEndTime() const +{ + Assert( m_fType == SPEAK ); + return m_flLastSlaveEndTime; +} + +void CChoreoEvent::SetCloseCaptionTokenValid( bool valid ) +{ + Assert( m_fType == SPEAK ); + m_bCCTokenValid = valid; +} + +bool CChoreoEvent::GetCloseCaptionTokenValid() const +{ + Assert( m_fType == SPEAK ); + return m_bCCTokenValid; +} + + +//----------------------------------------------------------------------------- +// Purpose: Removes characters which can't appear in windows filenames +// Input : *in - +// *dest - +// destlen - +// Output : static void +//----------------------------------------------------------------------------- +static void CleanupTokenName( char const *in, char *dest, int destlen ) +{ + char *out = dest; + while ( *in && ( out - dest ) < destlen ) + { + if ( isalnum( *in ) || // lowercase, uppercase, digits and underscore are valid + *in == '_' ) + { + *out++ = *in; + } + else + { + *out++ = '_'; // Put underscores in for bogus characters + } + in++; + } + *out = 0; +} + +bool CChoreoEvent::ComputeCombinedBaseFileName( char *dest, int destlen, bool creategenderwildcard ) +{ + if ( m_fType != SPEAK ) + return false; + + if ( m_ccType != CC_MASTER ) + return false; + + if ( GetNumSlaves() == 0 ) + return false; + + if ( !m_pScene ) + return false; + + char vcdpath[ 512 ]; + char cleanedtoken[ MAX_CCTOKEN_STRING ]; + CleanupTokenName( m_CCToken.c_str(), cleanedtoken, sizeof( cleanedtoken ) ); + + if ( Q_strlen( cleanedtoken ) <= 0 ) + return false; + + Q_strncpy( vcdpath, m_pScene->GetFilename(), sizeof( vcdpath ) ); + Q_StripFilename( vcdpath ); + Q_FixSlashes( vcdpath, '/' ); + + char *pvcd = vcdpath; + + char *offset = Q_strstr( vcdpath, "scenes" ); + if ( offset ) + { + pvcd = offset + 6; + if ( *pvcd == '/' ) + { + ++pvcd; + } + } + + size_t len = Q_strlen( pvcd ); + + if ( len > 0 && ( len + 1 ) < ( sizeof( vcdpath ) - 1 ) ) + { + pvcd[ len ] = '/'; + pvcd[ len + 1 ] = 0; + } + + Assert( !Q_strstr( pvcd, ":" ) ); + + if ( creategenderwildcard ) + { + Q_snprintf( dest, destlen, "sound/combined/%s%s_$gender.wav", pvcd, cleanedtoken ); + } + else + { + Q_snprintf( dest, destlen, "sound/combined/%s%s.wav", pvcd, cleanedtoken ); + } + return true; +} + +bool CChoreoEvent::IsCombinedUsingGenderToken() const +{ + return m_bCombinedUsingGenderToken; +} + +void CChoreoEvent::SetCombinedUsingGenderToken( bool using_gender ) +{ + m_bCombinedUsingGenderToken = using_gender; +} + + +int CChoreoEvent::ValidateCombinedFile() +{ + + return 0; +} + +bool CChoreoEvent::IsSuppressingCaptionAttenuation() const +{ + return m_bSuppressCaptionAttenuation; +} + +void CChoreoEvent::SetSuppressingCaptionAttenuation( bool suppress ) +{ + m_bSuppressCaptionAttenuation = suppress; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CChoreoEvent::ClearEventDependencies() +{ + m_Dependencies.RemoveAll(); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *other - +//----------------------------------------------------------------------------- +void CChoreoEvent::AddEventDependency( CChoreoEvent *other ) +{ + if ( m_Dependencies.Find( other ) == m_Dependencies.InvalidIndex() ) + { + m_Dependencies.AddToTail( other ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : list - +//----------------------------------------------------------------------------- +void CChoreoEvent::GetEventDependencies( CUtlVector< CChoreoEvent * >& list ) +{ + int c = m_Dependencies.Count(); + for ( int i = 0; i < c; ++i ) + { + list.AddToTail( m_Dependencies[ i ] ); + } +} + +void CChoreoEvent::RampSetEdgeInfo( bool leftEdge, int curveType, float zero ) +{ + int idx = leftEdge ? 0 : 1; + m_RampEdgeInfo[ idx ].m_CurveType = curveType; + m_RampEdgeInfo[ idx ].m_flZeroPos = zero; +} + +void CChoreoEvent::RampGetEdgeInfo( bool leftEdge, int& curveType, float& zero ) const +{ + int idx = leftEdge ? 0 : 1; + curveType = m_RampEdgeInfo[ idx ].m_CurveType; + zero = m_RampEdgeInfo[ idx ].m_flZeroPos; +} + +void CChoreoEvent::RampSetEdgeActive( bool leftEdge, bool state ) +{ + int idx = leftEdge ? 0 : 1; + m_RampEdgeInfo[ idx ].m_bActive = state; +} + +bool CChoreoEvent::RampIsEdgeActive( bool leftEdge ) const +{ + int idx = leftEdge ? 0 : 1; + return m_RampEdgeInfo[ idx ].m_bActive; +} + +int CChoreoEvent::RampGetEdgeCurveType( bool leftEdge ) const +{ + if ( !RampIsEdgeActive( leftEdge ) ) + { + return CURVE_DEFAULT; + } + + int idx = leftEdge ? 0 : 1; + return m_RampEdgeInfo[ idx ].m_CurveType; +} + +float CChoreoEvent::RampGetEdgeZeroValue( bool leftEdge ) const +{ + if ( !RampIsEdgeActive( leftEdge ) ) + { + return 0.0f; + } + + int idx = leftEdge ? 0 : 1; + return m_RampEdgeInfo[ idx ].m_flZeroPos; +} + + +void CChoreoEvent::SaveToBuffer( CUtlBuffer& buf, CChoreoScene *pScene ) +{ + buf.PutChar( GetType() ); + buf.PutString( GetName() ); + + float st, et; + st = GetStartTime(); + et = GetEndTime(); + + buf.PutFloat( st ); + buf.PutFloat( et ); + + buf.PutString( GetParameters() ); + buf.PutString( GetParameters2() ); + + SaveRampToBuffer( buf ); + + buf.PutChar( IsResumeCondition() ? 1 : 0 ); + buf.PutChar( IsLockBodyFacing() ? 1 : 0 ); + buf.PutFloat( GetDistanceToTarget() ); + buf.PutChar( IsFixedLength() ? 1 : 0 ); + + buf.PutShort( GetNumRelativeTags() ); + + for ( int t = 0; t < GetNumRelativeTags(); t++ ) + { + CEventRelativeTag *rt = GetRelativeTag( t ); + Assert( rt ); + buf.PutString( rt->GetName() ); + buf.PutFloat( rt->GetPercentage() ); + } + + buf.PutShort( GetNumTimingTags() ); + + for ( int t = 0; t < GetNumTimingTags(); t++ ) + { + CFlexTimingTag *tt = GetTimingTag( t ); + Assert( tt ); + buf.PutString( tt->GetName() ); + buf.PutFloat( tt->GetPercentage() ); + // Don't save locked state, it's only used by the editor tt->GetLocked() + } + + int tagtype; + for ( tagtype = 0; tagtype < CChoreoEvent::NUM_ABS_TAG_TYPES; tagtype++ ) + { + int num = GetNumAbsoluteTags( (CChoreoEvent::AbsTagType)tagtype ); + buf.PutShort( num ); + for ( int i = 0; i < num ; ++i ) + { + CEventAbsoluteTag *abstag = GetAbsoluteTag( (CChoreoEvent::AbsTagType)tagtype, i ); + Assert( abstag ); + buf.PutString( abstag->GetName() ); + buf.PutFloat( abstag->GetPercentage() ); + } + } + + if ( GetType() == CChoreoEvent::GESTURE ) + { + float duration; + if ( GetGestureSequenceDuration( duration ) ) + { + buf.PutFloat( duration ); + } + else + { + buf.PutFloat( -1.0f ); + } + } + + buf.PutChar( IsUsingRelativeTag() ? 1 : 0 ); + if ( IsUsingRelativeTag() ) + { + buf.PutString( GetRelativeTagName() ); + buf.PutString( GetRelativeWavName() ); + } + + SaveFlexAnimationsToBuffer( buf ); + + if ( GetType() == LOOP ) + { + buf.PutChar( GetLoopCount() ); + } + + if ( GetType() == CChoreoEvent::SPEAK ) + { + buf.PutChar( GetCloseCaptionType() ); + buf.PutString( GetCloseCaptionToken() ); + int flags = 0; + + if ( GetCloseCaptionType() != CChoreoEvent::CC_DISABLED && + IsUsingCombinedFile() ) + { + flags |= ( 1<<0 ); + } + if ( IsCombinedUsingGenderToken() ) + { + flags |= ( 1<<1 ); + } + if ( IsSuppressingCaptionAttenuation() ) + { + flags |= ( 1<<2 ); + } + + buf.PutChar( flags ); + } +} + +bool CChoreoEvent::RestoreFromBuffer( CUtlBuffer& buf, CChoreoScene *pScene ) +{ + SetType( (EVENTTYPE)buf.GetChar() ); + char sz[ 256 ]; + buf.GetString( sz, sizeof( sz ) ); + SetName( sz ); + + SetStartTime( buf.GetFloat() ); + SetEndTime( buf.GetFloat() ); + + char params[ 2048 ]; + buf.GetString( params, sizeof( params ) ); + SetParameters( params ); + buf.GetString( params, sizeof( params ) ); + SetParameters2( params ); + + if ( !RestoreRampFromBuffer( buf ) ) + return false; + + SetResumeCondition( buf.GetChar() == 1 ? true : false ); + SetLockBodyFacing( buf.GetChar() == 1 ? true : false ); + SetDistanceToTarget( buf.GetFloat() ); + SetFixedLength( buf.GetChar() == 1 ? true : false ); + + int numRelTags = buf.GetShort(); + for ( int i = 0 ;i < numRelTags; ++i ) + { + char tagName[ 256 ]; + buf.GetString( tagName, sizeof( tagName ) ); + float percentage = buf.GetFloat(); + AddRelativeTag( tagName, percentage ); + } + + int numTimingTags = buf.GetShort(); + for ( int i = 0 ;i < numTimingTags; ++i ) + { + char tagName[ 256 ]; + buf.GetString( tagName, sizeof( tagName ) ); + float percentage = buf.GetFloat(); + // Don't parse locked state, only used by editors + AddTimingTag( tagName, percentage, false ); + } + + int tagtype; + for ( tagtype = 0; tagtype < CChoreoEvent::NUM_ABS_TAG_TYPES; tagtype++ ) + { + int num = buf.GetShort(); + for ( int i = 0 ; i < num ; ++i ) + { + char tagName[ 256 ]; + buf.GetString( tagName, sizeof( tagName ) ); + float percentage = buf.GetFloat(); + // Don't parse locked state, only used by editors + AddAbsoluteTag( (CChoreoEvent::AbsTagType)tagtype, tagName, percentage ); + } + } + + if ( GetType() == CChoreoEvent::GESTURE ) + { + float duration = buf.GetFloat(); + if ( duration != -1 ) + { + SetGestureSequenceDuration( duration ); + } + } + + if ( buf.GetChar() == 1 ) + { + char tagname[ 256 ]; + char wavname[ 256 ]; + buf.GetString( tagname, sizeof( tagname ) ); + buf.GetString( wavname, sizeof( wavname ) ); + + SetUsingRelativeTag( true, tagname, wavname ); + } + + if ( !RestoreFlexAnimationsFromBuffer( buf ) ) + return false; + + if ( GetType() == LOOP ) + { + SetLoopCount( buf.GetChar() ); + } + + if ( GetType() == CChoreoEvent::SPEAK ) + { + SetCloseCaptionType( (CLOSECAPTION)buf.GetChar() ); + char cctoken[ 256 ]; + buf.GetString( cctoken, sizeof( cctoken ) ); + SetCloseCaptionToken( cctoken ); + int flags = buf.GetChar(); + if ( flags & ( 1<<0 ) ) + { + SetUsingCombinedFile( true ); + } + if ( flags & ( 1<<1 ) ) + { + SetCombinedUsingGenderToken( true ); + } + if ( flags & ( 1<<2 ) ) + { + SetSuppressingCaptionAttenuation( true ); + } + } + + return true; +} + +void CChoreoEvent::SaveRampToBuffer( CUtlBuffer& buf ) +{ + int c = GetRampCount(); + buf.PutInt( c ); + if ( c <= 0 ) + return; + + for ( int i = 0; i < c; i++ ) + { + CExpressionSample *sample = GetRamp( i ); + buf.PutFloat( sample->time ); + buf.PutFloat( sample->value ); + } +} + +bool CChoreoEvent::RestoreRampFromBuffer( CUtlBuffer& buf ) +{ + int c = buf.GetInt(); + for ( int i = 0; i < c; i++ ) + { + float t, v; + t = buf.GetFloat(); + v = buf.GetFloat(); + + AddRamp( t, v, false ); + } + + return true; +} + +void CChoreoEvent::SaveFlexAnimationsToBuffer( CUtlBuffer& buf ) +{ + buf.PutShort( GetNumFlexAnimationTracks() ); + for ( int i = 0; i < GetNumFlexAnimationTracks(); i++ ) + { + CFlexAnimationTrack *track = GetFlexAnimationTrack( i ); + + buf.PutString( track->GetFlexControllerName() ); + buf.PutChar( track->IsTrackActive() ? 1 : 0 ); + buf.PutChar( track->IsComboType() ? 1 : 0 ); + buf.PutFloat( track->GetMin() ); + buf.PutFloat( track->GetMax() ); + + buf.PutShort( track->GetNumSamples( 0 ) ); + for ( int j = 0 ; j < track->GetNumSamples( 0 ) ; j++ ) + { + CExpressionSample *s = track->GetSample( j, 0 ); + if ( !s ) + continue; + + buf.PutFloat( s->time ); + buf.PutFloat( s->value ); + } + + // Write out combo samples + if ( track->IsComboType() ) + { + buf.PutShort( track->GetNumSamples( 1 ) ); + for ( int j = 0 ; j < track->GetNumSamples( 1) ; j++ ) + { + CExpressionSample *s = track->GetSample( j, 1 ); + if ( !s ) + continue; + + buf.PutFloat( s->time ); + buf.PutFloat( s->value ); + } + } + } +} + +bool CChoreoEvent::RestoreFlexAnimationsFromBuffer( CUtlBuffer& buf ) +{ + int numTracks = buf.GetShort(); + + for ( int i = 0; i < numTracks; i++ ) + { + char name[ 256 ]; + buf.GetString( name, sizeof( name ) ); + + CFlexAnimationTrack *track = AddTrack( name ); + track->SetTrackActive( buf.GetChar() == 1 ? true : false ); + track->SetComboType( buf.GetChar() == 1 ? true : false ); + track->SetMin( buf.GetFloat() ); + track->SetMax( buf.GetFloat() ); + + int s = buf.GetShort(); + for ( int j = 0; j < s; ++j ) + { + float t, v; + t = buf.GetFloat(); + v = buf.GetFloat(); + + track->AddSample( t, v, 0 ); + } + + if ( track->IsComboType() ) + { + int s = buf.GetShort(); + for ( int j = 0; j < s; ++j ) + { + float t, v; + t = buf.GetFloat(); + v = buf.GetFloat(); + + track->AddSample( t, v, 1 ); + } + } + } + + return true; +} diff --git a/choreoobjects/choreoobjects-2003.vcproj b/choreoobjects/choreoobjects-2003.vcproj new file mode 100644 index 00000000..277a2cc3 --- /dev/null +++ b/choreoobjects/choreoobjects-2003.vcproj @@ -0,0 +1,161 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/choreoobjects/choreoobjects-2005.vcproj b/choreoobjects/choreoobjects-2005.vcproj new file mode 100644 index 00000000..7571eb70 --- /dev/null +++ b/choreoobjects/choreoobjects-2005.vcproj @@ -0,0 +1,213 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/choreoobjects/choreoscene.cpp b/choreoobjects/choreoscene.cpp new file mode 100644 index 00000000..2498d2cb --- /dev/null +++ b/choreoobjects/choreoscene.cpp @@ -0,0 +1,3987 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + + +#if defined(_WIN32) && !defined(_XBOX) +#include +#endif + +#include "basetypes.h" +#include +#include "choreoscene.h" +#include "choreoevent.h" +#include "choreochannel.h" +#include "choreoactor.h" +#include "ichoreoeventcallback.h" +#include "iscenetokenprocessor.h" +#include "utlbuffer.h" +#include "filesystem.h" +#include "utlrbtree.h" +#include "mathlib.h" +#include "vstdlib/strtools.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +extern IFileSystem *SceneFileSystem(); + +#ifdef _MSC_VER +#pragma warning( disable : 4127 ) +#endif + +// Let scene linger for 1/4 second so blends can finish +#define SCENE_LINGER_TIME 0.25f + +// The engine turns this to true in dlls/sceneentity.cpp at bool SceneCacheInit()!!! +bool CChoreoScene::s_bEditingDisabled = false; + +//----------------------------------------------------------------------------- +// Purpose: Debug printout +// Input : level - +// *fmt - +// ... - +//----------------------------------------------------------------------------- +void CChoreoScene::choreoprintf( int level, const char *fmt, ... ) +{ + char string[ 2048 ]; + va_list argptr; + va_start( argptr, fmt ); + Q_vsnprintf( string, sizeof(string), fmt, argptr ); + va_end( argptr ); + + while ( level-- > 0 ) + { + if (m_pfnPrint ) + { + (*m_pfnPrint)( " " ); + } + else + { + printf( " " ); + } + Msg( " " ); + } + + if ( m_pfnPrint ) + { + (*m_pfnPrint)( string ); + } + else + { + printf( string ); + } + + Msg( "%s", string ); +} + +//----------------------------------------------------------------------------- +// Purpose: Creates scene from a file +// Input : *filename - +// *pfn - +// Output : CChoreoScene +//----------------------------------------------------------------------------- +CChoreoScene *ChoreoLoadScene +( + char const *filename, + IChoreoEventCallback *callback, + ISceneTokenProcessor *tokenizer, + void ( *pfn ) ( const char *fmt, ... ) +) +{ + MEM_ALLOC_CREDIT_CLASS(); + CChoreoScene *scene = new CChoreoScene( callback ); + Assert( scene ); + scene->ParseFromBuffer( filename, tokenizer ); + scene->SetPrintFunc( pfn ); + return scene; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CChoreoScene::CChoreoScene( IChoreoEventCallback *callback ) +{ + Init( callback ); +} + + +//----------------------------------------------------------------------------- +// Purpose: // Assignment +// Input : src - +// Output : CChoreoScene& +//----------------------------------------------------------------------------- +CChoreoScene& CChoreoScene::operator=( const CChoreoScene& src ) +{ + Init( src.m_pIChoreoEventCallback ); + + // Delete existing + int i; + for ( i = 0; i < m_Actors.Size(); i++ ) + { + CChoreoActor *a = m_Actors[ i ]; + Assert( a ); + delete a; + } + + m_Actors.RemoveAll(); + + for ( i = 0; i < m_Events.Size(); i++ ) + { + CChoreoEvent *e = m_Events[ i ]; + Assert( e ); + delete e; + } + + m_Events.RemoveAll(); + + for ( i = 0 ; i < m_Channels.Size(); i++ ) + { + CChoreoChannel *c = m_Channels[ i ]; + Assert( c ); + delete c; + } + + m_Channels.RemoveAll(); + + m_pTokenizer = src.m_pTokenizer; + + m_flCurrentTime = src.m_flCurrentTime; + m_flStartTime = src.m_flStartTime; + m_flEndTime = src.m_flEndTime; + m_flSoundSystemLatency = src.m_flSoundSystemLatency; + m_pfnPrint = src.m_pfnPrint; + m_flLastActiveTime = src.m_flLastActiveTime; + m_pTokenizer = src.m_pTokenizer; + m_bSubScene = src.m_bSubScene; + m_nSceneFPS = src.m_nSceneFPS; + m_bUseFrameSnap = src.m_bUseFrameSnap; + + // Now copy the object tree + // First copy the global events + + for ( i = 0; i < src.m_Events.Size(); i++ ) + { + CChoreoEvent *event = src.m_Events[ i ]; + if ( event->GetActor() == NULL ) + { + MEM_ALLOC_CREDIT(); + + // Copy it + CChoreoEvent *newEvent = AllocEvent(); + *newEvent = *event; + } + } + + // Finally, push actors, channels, events onto global stacks + for ( i = 0; i < src.m_Actors.Size(); i++ ) + { + CChoreoActor *actor = src.m_Actors[ i ]; + CChoreoActor *newActor = AllocActor(); + *newActor = *actor; + + for ( int j = 0; j < newActor->GetNumChannels() ; j++ ) + { + CChoreoChannel *ch = newActor->GetChannel( j ); + m_Channels.AddToTail( ch ); + + for ( int k = 0; k < ch->GetNumEvents(); k++ ) + { + CChoreoEvent *ev = ch->GetEvent( k ); + m_Events.AddToTail( ev ); + ev->SetScene( this ); + } + } + } + + Q_strncpy( m_szMapname, src.m_szMapname, sizeof( m_szMapname ) ); + + // Copy ramp over + m_SceneRamp.RemoveAll(); + for ( i = 0; i < src.m_SceneRamp.Count(); i++ ) + { + CExpressionSample sample = src.m_SceneRamp[ i ]; + AddSceneRamp( sample.time, sample.value, sample.selected ); + } + m_SceneRampEdgeInfo[ 0 ] = src.m_SceneRampEdgeInfo[ 0 ]; + m_SceneRampEdgeInfo[ 1 ] = src.m_SceneRampEdgeInfo[ 1 ]; + + m_TimeZoomLookup.RemoveAll(); + for ( i = 0; i < (int)src.m_TimeZoomLookup.Count(); i++ ) + { + m_TimeZoomLookup.Insert( src.m_TimeZoomLookup.GetElementName( i ), src.m_TimeZoomLookup[ i ] ); + } + + Q_strncpy( m_szFileName, src.m_szFileName, sizeof( m_szFileName ) ); + + m_nLastPauseEvent = src.m_nLastPauseEvent; + m_flPrecomputedStopTime = src.m_flPrecomputedStopTime; + + m_bitvecHasEventOfType = src.m_bitvecHasEventOfType; + + return *this; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CChoreoScene::Init( IChoreoEventCallback *callback ) +{ + m_flPrecomputedStopTime = 0.0f; + m_pTokenizer = NULL; + m_szMapname[ 0 ] = 0; + + m_flCurrentTime = 0.0f; + m_flStartTime = 0.0f; + m_flEndTime = 0.0f; + m_flSoundSystemLatency = 0.0f; + m_pfnPrint = NULL; + m_flLastActiveTime = 0.0f; + m_flEarliestTime = 0.0f; + m_flLatestTime = 0.0f; + m_nActiveEvents = 0; + + m_pIChoreoEventCallback = callback; + + m_bSubScene = false; + m_nSceneFPS = DEFAULT_SCENE_FPS; + m_bUseFrameSnap = false; + m_szFileName[0] = 0; + + m_bIsBackground = false; + m_bitvecHasEventOfType.ClearAll(); + m_nLastPauseEvent = -1; +} + +//----------------------------------------------------------------------------- +// Purpose: Destroy objects and queues +//----------------------------------------------------------------------------- +CChoreoScene::~CChoreoScene( void ) +{ + int i; + for ( i = 0; i < m_Actors.Size(); i++ ) + { + CChoreoActor *a = m_Actors[ i ]; + Assert( a ); + delete a; + } + + m_Actors.RemoveAll(); + + for ( i = 0; i < m_Events.Size(); i++ ) + { + CChoreoEvent *e = m_Events[ i ]; + Assert( e ); + delete e; + } + + m_Events.RemoveAll(); + + for ( i = 0 ; i < m_Channels.Size(); i++ ) + { + CChoreoChannel *c = m_Channels[ i ]; + Assert( c ); + delete c; + } + + m_Channels.RemoveAll(); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *callback - +//----------------------------------------------------------------------------- +void CChoreoScene::SetEventCallbackInterface( IChoreoEventCallback *callback ) +{ + m_pIChoreoEventCallback = callback; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : level - +// *e - +//----------------------------------------------------------------------------- +void CChoreoScene::PrintEvent( int level, CChoreoEvent *e ) +{ + choreoprintf( level, "event %s \"%s\"\n", CChoreoEvent::NameForType( e->GetType() ), e->GetName() ); + choreoprintf( level, "{\n" ); + choreoprintf( level + 1, "time %f %f\n", e->GetStartTime(), e->GetEndTime() ); + choreoprintf( level + 1, "param \"%s\"\n", e->GetParameters() ); + if ( strlen( e->GetParameters2() ) > 0 ) + { + choreoprintf( level + 1, "param2 \"%s\"\n", e->GetParameters2() ); + } + choreoprintf( level, "}\n" ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : level - +// *c - +//----------------------------------------------------------------------------- +void CChoreoScene::PrintChannel( int level, CChoreoChannel *c ) +{ + choreoprintf( level, "channel \"%s\"\n", c->GetName() ); + choreoprintf( level, "{\n" ); + + for ( int i = 0; i < c->GetNumEvents(); i++ ) + { + CChoreoEvent *e = c->GetEvent( i ); + if ( e ) + { + PrintEvent( level + 1, e ); + } + } + + choreoprintf( level, "}\n" ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : level - +// *a - +//----------------------------------------------------------------------------- +void CChoreoScene::PrintActor( int level, CChoreoActor *a ) +{ + choreoprintf( level, "actor \"%s\"\n", a->GetName() ); + choreoprintf( level, "{\n" ); + + for ( int i = 0; i < a->GetNumChannels(); i++ ) + { + CChoreoChannel *c = a->GetChannel( i ); + if ( c ) + { + PrintChannel( level + 1, c ); + } + } + + choreoprintf( level, "}\n\n" ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CChoreoScene::Print( void ) +{ + // Look for events that don't have actor/channel set + int i; + + for ( i = 0 ; i < m_Events.Size(); i++ ) + { + CChoreoEvent *e = m_Events[ i ]; + if ( e->GetActor() ) + continue; + + PrintEvent( 0, e ); + } + + for ( i = 0 ; i < m_Actors.Size(); i++ ) + { + CChoreoActor *a = m_Actors[ i ]; + if ( !a ) + continue; + + PrintActor( 0, a ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: prints if m_pfnPrint is active +// Output : +//----------------------------------------------------------------------------- + +void CChoreoScene::SceneMsg( const char *pFormat, ... ) +{ + char string[ 2048 ]; + va_list argptr; + va_start( argptr, pFormat ); + Q_vsnprintf( string, sizeof(string), pFormat, argptr ); + va_end( argptr ); + + if ( m_pfnPrint ) + { + (*m_pfnPrint)( string ); + } + else + { + Msg( "%s", string ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : CChoreoEvent +//----------------------------------------------------------------------------- +CChoreoEvent *CChoreoScene::AllocEvent( void ) +{ + MEM_ALLOC_CREDIT_CLASS(); + CChoreoEvent *e = new CChoreoEvent( this ); + Assert( e ); + m_Events.AddToTail( e ); + return e; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : CChoreoChannel +//----------------------------------------------------------------------------- +CChoreoChannel *CChoreoScene::AllocChannel( void ) +{ + MEM_ALLOC_CREDIT_CLASS(); + CChoreoChannel *c = new CChoreoChannel(); + Assert( c ); + m_Channels.AddToTail( c ); + return c; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : CChoreoActor +//----------------------------------------------------------------------------- +CChoreoActor *CChoreoScene::AllocActor( void ) +{ + MEM_ALLOC_CREDIT_CLASS(); + CChoreoActor *a = new CChoreoActor; + Assert( a ); + m_Actors.AddToTail( a ); + return a; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *name - +// Output : CChoreoActor +//----------------------------------------------------------------------------- +CChoreoActor *CChoreoScene::FindActor( const char *name ) +{ + for ( int i = 0; i < m_Actors.Size(); i++ ) + { + CChoreoActor *a = m_Actors[ i ]; + if ( !a ) + continue; + + if ( !Q_stricmp( a->GetName(), name ) ) + return a; + } + + return NULL; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : int +//----------------------------------------------------------------------------- +int CChoreoScene::GetNumEvents( void ) +{ + return m_Events.Size(); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : event - +// Output : CChoreoEvent +//----------------------------------------------------------------------------- +CChoreoEvent *CChoreoScene::GetEvent( int event ) +{ + if ( event < 0 || event >= m_Events.Size() ) + return NULL; + + return m_Events[ event ]; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : int +//----------------------------------------------------------------------------- +int CChoreoScene::GetNumActors( void ) +{ + return m_Actors.Size(); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : actor - +// Output : CChoreoActor +//----------------------------------------------------------------------------- +CChoreoActor *CChoreoScene::GetActor( int actor ) +{ + if ( actor < 0 || actor >= GetNumActors() ) + return NULL; + return m_Actors[ actor ]; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : int +//----------------------------------------------------------------------------- +int CChoreoScene::GetNumChannels( void ) +{ + return m_Channels.Size(); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : channel - +// Output : CChoreoChannel +//----------------------------------------------------------------------------- +CChoreoChannel *CChoreoScene::GetChannel( int channel ) +{ + if ( channel < 0 || channel >= GetNumChannels() ) + return NULL; + return m_Channels[ channel ]; +} + +void CChoreoScene::ParseRamp( ISceneTokenProcessor *tokenizer, CChoreoEvent *e ) +{ + e->ClearRamp(); + + tokenizer->GetToken( true ); + + if ( !Q_stricmp( tokenizer->CurrentToken(), "leftedge" ) ) + { + ParseEdgeInfo( tokenizer, e->GetRampEdgeInfo( 0 ) ); + } + + if ( !Q_stricmp( tokenizer->CurrentToken(), "rightedge" ) ) + { + ParseEdgeInfo( tokenizer, e->GetRampEdgeInfo( 1 ) ); + } + + if ( stricmp( tokenizer->CurrentToken(), "{" ) ) + tokenizer->Error( "expecting {\n" ); + + while ( 1 ) + { + // Parse until } + tokenizer->GetToken( true ); + + if ( strlen( tokenizer->CurrentToken() ) <= 0 ) + { + tokenizer->Error( "expecting ramp data\n" ); + break; + } + + if ( !Q_stricmp( tokenizer->CurrentToken(), "}" ) ) + break; + + CUtlVector< CExpressionSample > samples; + + float time = (float)atof( tokenizer->CurrentToken() ); + tokenizer->GetToken( false ); + float value = (float)atof( tokenizer->CurrentToken() ); + + // Add to counter + int idx = samples.AddToTail(); + CExpressionSample *s = &samples[ idx ]; + + s->time = time; + s->value = value; + + // If there are more tokens on this line, then it's a new format curve name + if ( tokenizer->TokenAvailable() ) + { + tokenizer->GetToken( false ); + int curveType = Interpolator_CurveTypeForName( tokenizer->CurrentToken() ); + s->SetCurveType( curveType ); + } + + if ( samples.Size() >= 1 ) + { + for ( int i = 0; i < samples.Size(); i++ ) + { + CExpressionSample sample = samples[ i ]; + + CExpressionSample *newSample = e->AddRamp( sample.time, sample.value, false ); + newSample->SetCurveType( sample.GetCurveType() ); + } + } + } + + e->ResortRamp(); +} + +void CChoreoScene::ParseSceneRamp( ISceneTokenProcessor *tokenizer, CChoreoScene *scene ) +{ + scene->ClearSceneRamp(); + + tokenizer->GetToken( true ); + + if ( !Q_stricmp( tokenizer->CurrentToken(), "leftedge" ) ) + { + ParseEdgeInfo( tokenizer, scene->GetSceneRampEdgeInfo( 0 ) ); + } + + if ( !Q_stricmp( tokenizer->CurrentToken(), "rightedge" ) ) + { + ParseEdgeInfo( tokenizer, scene->GetSceneRampEdgeInfo( 1 ) ); + } + + if ( stricmp( tokenizer->CurrentToken(), "{" ) ) + tokenizer->Error( "expecting {\n" ); + + while ( 1 ) + { + // Parse until } + tokenizer->GetToken( true ); + + if ( strlen( tokenizer->CurrentToken() ) <= 0 ) + { + tokenizer->Error( "expecting ramp data\n" ); + break; + } + + if ( !Q_stricmp( tokenizer->CurrentToken(), "}" ) ) + break; + + CUtlVector< CExpressionSample > samples; + + float time = (float)atof( tokenizer->CurrentToken() ); + tokenizer->GetToken( false ); + float value = (float)atof( tokenizer->CurrentToken() ); + + // Add to counter + int idx = samples.AddToTail(); + CExpressionSample *s = &samples[ idx ]; + + s->time = time; + s->value = value; + + // If there are more tokens on this line, then it's a new format curve name + if ( tokenizer->TokenAvailable() ) + { + tokenizer->GetToken( false ); + int curveType = Interpolator_CurveTypeForName( tokenizer->CurrentToken() ); + s->SetCurveType( curveType ); + } + + if ( samples.Size() >= 1 ) + { + for ( int i = 0; i < samples.Size(); i++ ) + { + CExpressionSample sample = samples[ i ]; + + CExpressionSample *newSample = scene->AddSceneRamp( sample.time, sample.value, false ); + newSample->SetCurveType( sample.GetCurveType() ); + } + } + } + + scene->ResortSceneRamp(); +} + +//----------------------------------------------------------------------------- +// Purpose: Helper for restoring edge info +// Input : *edgeinfo - +//----------------------------------------------------------------------------- +void CChoreoScene::ParseEdgeInfo( ISceneTokenProcessor *tokenizer, EdgeInfo_t *edgeinfo ) +{ + Assert( edgeinfo ); + Assert( tokenizer ); + + tokenizer->GetToken( false ); + edgeinfo->m_bActive = true; + edgeinfo->m_CurveType = Interpolator_CurveTypeForName( tokenizer->CurrentToken() ); + tokenizer->GetToken( false ); + edgeinfo->m_flZeroPos = atof( tokenizer->CurrentToken() ); + tokenizer->GetToken( true ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *tokenizer - +// *e - +//----------------------------------------------------------------------------- +void CChoreoScene::ParseFlexAnimations( ISceneTokenProcessor *tokenizer, CChoreoEvent *e, bool removeold /*= true*/ ) +{ + Assert( e ); + + if ( removeold ) + { + // Make sure there's nothing already there... + e->RemoveAllTracks(); + // Make it re-index + e->SetTrackLookupSet( false ); + } + + // BACKWARD COMPATABILITY + // in the old system the samples were 0.0 to 1.0 mapped to endtime - starttime + // if samples_use_time is true, then samples are actually offsets of time from starttime + bool samples_use_realtime = false; + // Parse tags between { } + // + tokenizer->GetToken( true ); + + Assert( e->HasEndTime() ); + + float endtime = e->GetEndTime(); + float starttime = e->GetStartTime(); + float event_time = endtime - starttime; + + // Is it the new file format? + if ( !Q_stricmp( tokenizer->CurrentToken(), "samples_use_time" ) ) + { + samples_use_realtime = true; + tokenizer->GetToken( true ); + } + + if ( stricmp( tokenizer->CurrentToken(), "{" ) ) + tokenizer->Error( "expecting {\n" ); + + while ( 1 ) + { + // Parse until } + tokenizer->GetToken( true ); + + if ( strlen( tokenizer->CurrentToken() ) <= 0 ) + { + tokenizer->Error( "expecting flex animation data\n" ); + break; + } + + if ( !Q_stricmp( tokenizer->CurrentToken(), "}" ) ) + break; + + char flexcontroller[ CFlexAnimationTrack::MAX_CONTROLLER_NAME ]; + Q_strncpy( flexcontroller, tokenizer->CurrentToken(), sizeof( flexcontroller ) ); + + // Animations default to active + bool active = true; + bool combo = false; + float range_min = 0.0f; + float range_max = 1.0f; + tokenizer->GetToken( true ); + + EdgeInfo_t edgeinfo[ 2 ]; + + if ( !Q_stricmp( tokenizer->CurrentToken(), "disabled" ) ) + { + active = false; + tokenizer->GetToken( true ); + } + + if ( !Q_stricmp( tokenizer->CurrentToken(), "combo" ) ) + { + combo = true; + tokenizer->GetToken( true ); + } + + if ( !Q_stricmp( tokenizer->CurrentToken(), "range" ) ) + { + tokenizer->GetToken( false ); + range_min = atof( tokenizer->CurrentToken() ); + tokenizer->GetToken( false ); + range_max = atof( tokenizer->CurrentToken() ); + tokenizer->GetToken( true ); + } + + if ( !Q_stricmp( tokenizer->CurrentToken(), "leftedge" ) ) + { + ParseEdgeInfo( tokenizer, &edgeinfo[ 0 ] ); + } + + if ( !Q_stricmp( tokenizer->CurrentToken(), "rightedge" ) ) + { + ParseEdgeInfo( tokenizer, &edgeinfo[ 1 ] ); + } + + CUtlVector< CExpressionSample > samples[2]; + + for ( int samplecount = 0; samplecount < ( combo ? 2 : 1 ); samplecount++ ) + { + if ( stricmp( tokenizer->CurrentToken(), "{" ) ) + { + tokenizer->Error( "expecting {\n" ); + } + + while ( 1 ) + { + tokenizer->GetToken( true ); + + if ( strlen( tokenizer->CurrentToken() ) <= 0 ) + { + tokenizer->Error( "expecting flex animation data\n" ); + break; + } + + if ( !Q_stricmp( tokenizer->CurrentToken(), "}" ) ) + break; + + float time = (float)atof( tokenizer->CurrentToken() ); + tokenizer->GetToken( false ); + float value = (float)atof( tokenizer->CurrentToken() ); + + // Add to counter + int idx = samples[ samplecount ].AddToTail(); + + CExpressionSample *s = &samples[ samplecount ][ idx ]; + + if ( samples_use_realtime ) + { + s->time = time; + } + else + { + // Time is an old style fraction (0 to 1) map into real time + s->time = time * event_time; + } + + s->value = value; + + // If there are more tokens on this line, then it's a new format curve name + if ( tokenizer->TokenAvailable() ) + { + tokenizer->GetToken( false ); + int curveType = Interpolator_CurveTypeForName( tokenizer->CurrentToken() ); + s->SetCurveType( curveType ); + } + } + + if ( combo && samplecount == 0 ) + { + tokenizer->GetToken( true ); + } + } + + if ( active || samples[ 0 ].Size() >= 1 ) + { + // Add it in + CFlexAnimationTrack *track = e->AddTrack( flexcontroller ); + Assert( track ); + track->SetTrackActive( active ); + track->SetComboType( combo ); + + track->SetMin( range_min ); + track->SetMax( range_max ); + + for ( int t = 0; t < ( combo ? 2 : 1 ); t++ ) + { + for ( int i = 0; i < samples[ t ].Size(); i++ ) + { + CExpressionSample *sample = &samples[ t ][ i ]; + + CExpressionSample *added = track->AddSample( sample->time, sample->value, t ); + Assert( added ); + added->SetCurveType( sample->GetCurveType() ); + } + } + + for ( int edge = 0; edge < 2; ++edge ) + { + if ( !edgeinfo[ edge ].m_bActive ) + continue; + + track->SetEdgeActive( edge == 0 ? true : false, true ); + track->SetEdgeInfo( edge == 0 ? true : false, edgeinfo[ edge ].m_CurveType, edgeinfo[ edge ].m_flZeroPos ); + } + + track->Resort( 0 ); + track->Resort( 1 ); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *actor - +// *channel - +// Output : CChoreoEvent +//----------------------------------------------------------------------------- +CChoreoEvent *CChoreoScene::ParseEvent( CChoreoActor *actor, CChoreoChannel *channel ) +{ + // For conversion of old style attack/sustain/decay ramps + bool hadramp = false; + float attack = 1.0f, sustain = 1.0f, decay = 1.0f; + + CChoreoEvent *e; + { + MEM_ALLOC_CREDIT(); + e = AllocEvent(); + } + + MEM_ALLOC_CREDIT(); + + Assert( e ); + + // read event type + m_pTokenizer->GetToken( false ); + + e->SetType( CChoreoEvent::TypeForName( m_pTokenizer->CurrentToken() ) ); + + m_pTokenizer->GetToken( false ); + e->SetName( m_pTokenizer->CurrentToken() ); + + m_pTokenizer->GetToken( true ); + if ( stricmp( m_pTokenizer->CurrentToken(), "{" ) ) + m_pTokenizer->Error( "expecting {\n" ); + + while ( 1 ) + { + m_pTokenizer->GetToken( true ); + if ( !Q_stricmp( m_pTokenizer->CurrentToken(), "}" ) ) + break; + + if ( strlen( m_pTokenizer->CurrentToken() ) <= 0 ) + { + m_pTokenizer->Error( "expecting more tokens!" ); + break; + } + + if ( !Q_stricmp( m_pTokenizer->CurrentToken(), "time" ) ) + { + float start, end = 1.0f; + + m_pTokenizer->GetToken( false ); + start = (float)atof( m_pTokenizer->CurrentToken() ); + if ( m_pTokenizer->TokenAvailable() ) + { + m_pTokenizer->GetToken( false ); + end = (float)atof( m_pTokenizer->CurrentToken() ); + } + + e->SetStartTime( start ); + e->SetEndTime( end ); + } + else if ( !Q_stricmp( m_pTokenizer->CurrentToken(), "ramp" ) ) + { + hadramp = true; + + m_pTokenizer->GetToken( false ); + attack = (float)atof( m_pTokenizer->CurrentToken() ); + if ( m_pTokenizer->TokenAvailable() ) + { + m_pTokenizer->GetToken( false ); + sustain = (float)atof( m_pTokenizer->CurrentToken() ); + } + if ( m_pTokenizer->TokenAvailable() ) + { + m_pTokenizer->GetToken( false ); + decay = (float)atof( m_pTokenizer->CurrentToken() ); + } + } + else if ( !Q_stricmp( m_pTokenizer->CurrentToken(), "param" ) ) + { + m_pTokenizer->GetToken( false ); + + e->SetParameters( m_pTokenizer->CurrentToken() ); + } + else if ( !Q_stricmp( m_pTokenizer->CurrentToken(), "param2" ) ) + { + m_pTokenizer->GetToken( false ); + + e->SetParameters2( m_pTokenizer->CurrentToken() ); + } + else if ( !Q_stricmp( m_pTokenizer->CurrentToken(), "pitch" ) ) + { + m_pTokenizer->GetToken( false ); + e->SetPitch( atoi( m_pTokenizer->CurrentToken() ) ); + } + else if ( !Q_stricmp( m_pTokenizer->CurrentToken(), "yaw" ) ) + { + m_pTokenizer->GetToken( false ); + e->SetYaw( atoi( m_pTokenizer->CurrentToken() ) ); + } + else if ( !Q_stricmp( m_pTokenizer->CurrentToken(), "loopcount" ) ) + { + m_pTokenizer->GetToken( false ); + e->SetLoopCount( atoi( m_pTokenizer->CurrentToken() ) ); + } + else if ( !Q_stricmp( m_pTokenizer->CurrentToken(), "resumecondition" ) ) + { + e->SetResumeCondition( true ); + } + else if ( !Q_stricmp( m_pTokenizer->CurrentToken(), "fixedlength" ) ) + { + e->SetFixedLength( true ); + } + else if ( !Q_stricmp( m_pTokenizer->CurrentToken(), "lockbodyfacing" ) ) + { + e->SetLockBodyFacing( true ); + } + else if ( !Q_stricmp( m_pTokenizer->CurrentToken(), "distancetotarget" ) ) + { + m_pTokenizer->GetToken( false ); + e->SetDistanceToTarget( atof( m_pTokenizer->CurrentToken() ) ); + } + else if ( !Q_stricmp( m_pTokenizer->CurrentToken(), "forceshortmovement" ) ) + { + e->SetForceShortMovement( true ); + } + else if ( !Q_stricmp( m_pTokenizer->CurrentToken(), "synctofollowinggesture" ) ) + { + e->SetSyncToFollowingGesture( true ); + } + else if ( !Q_stricmp( m_pTokenizer->CurrentToken(), "tags" ) ) + { + // Parse tags between { } + // + m_pTokenizer->GetToken( true ); + if ( stricmp( m_pTokenizer->CurrentToken(), "{" ) ) + m_pTokenizer->Error( "expecting {\n" ); + + while ( 1 ) + { + // Parse until } + m_pTokenizer->GetToken( true ); + + if ( strlen( m_pTokenizer->CurrentToken() ) <= 0 ) + { + m_pTokenizer->Error( "expecting relative tag\n" ); + break; + } + + if ( !Q_stricmp( m_pTokenizer->CurrentToken(), "}" ) ) + break; + + char tagname[ CEventRelativeTag::MAX_EVENTTAG_LENGTH ]; + float percentage; + + Q_strncpy( tagname, m_pTokenizer->CurrentToken(), sizeof( tagname ) ); + m_pTokenizer->GetToken( false ); + percentage = (float)atof( m_pTokenizer->CurrentToken() ); + + e->AddRelativeTag( tagname, percentage ); + } + } + else if ( !Q_stricmp( m_pTokenizer->CurrentToken(), "sequenceduration" ) ) + { + float duration = 0.0f; + + m_pTokenizer->GetToken( false ); + duration = (float)atof( m_pTokenizer->CurrentToken() ); + + e->SetGestureSequenceDuration( duration ); + } + else if ( !Q_stricmp( m_pTokenizer->CurrentToken(), "absolutetags" ) ) + { + m_pTokenizer->GetToken( true ); + CChoreoEvent::AbsTagType tagtype; + + tagtype = CChoreoEvent::TypeForAbsoluteTagName( m_pTokenizer->CurrentToken() ); + + if ( tagtype == (CChoreoEvent::AbsTagType) -1 ) + { + m_pTokenizer->Error( "expecting valid tag type!!!" ); + } + + // Parse tags between { } + // + m_pTokenizer->GetToken( true ); + if ( stricmp( m_pTokenizer->CurrentToken(), "{" ) ) + m_pTokenizer->Error( "expecting {\n" ); + + while ( 1 ) + { + // Parse until } + m_pTokenizer->GetToken( true ); + + if ( strlen( m_pTokenizer->CurrentToken() ) <= 0 ) + { + m_pTokenizer->Error( "expecting relative tag\n" ); + break; + } + + if ( !Q_stricmp( m_pTokenizer->CurrentToken(), "}" ) ) + break; + + char tagname[ CFlexTimingTag::MAX_EVENTTAG_LENGTH ]; + float t; + + Q_strncpy( tagname, m_pTokenizer->CurrentToken(), sizeof( tagname ) ); + m_pTokenizer->GetToken( false ); + t = (float)atof( m_pTokenizer->CurrentToken() ); + + e->AddAbsoluteTag( tagtype, tagname, t ); + } + } + else if ( !Q_stricmp( m_pTokenizer->CurrentToken(), "flextimingtags" ) ) + { + // Parse tags between { } + // + m_pTokenizer->GetToken( true ); + if ( stricmp( m_pTokenizer->CurrentToken(), "{" ) ) + m_pTokenizer->Error( "expecting {\n" ); + + while ( 1 ) + { + // Parse until } + m_pTokenizer->GetToken( true ); + + if ( strlen( m_pTokenizer->CurrentToken() ) <= 0 ) + { + m_pTokenizer->Error( "expecting relative tag\n" ); + break; + } + + if ( !Q_stricmp( m_pTokenizer->CurrentToken(), "}" ) ) + break; + + char tagname[ CFlexTimingTag::MAX_EVENTTAG_LENGTH ]; + float percentage; + bool locked; + + Q_strncpy( tagname, m_pTokenizer->CurrentToken(), sizeof( tagname ) ); + m_pTokenizer->GetToken( false ); + percentage = (float)atof( m_pTokenizer->CurrentToken() ); + + m_pTokenizer->GetToken( false ); + locked = atoi( m_pTokenizer->CurrentToken() ) ? true : false; + + e->AddTimingTag( tagname, percentage, locked ); + } + } + else if ( !Q_stricmp( m_pTokenizer->CurrentToken(), "relativetag" ) ) + { + char tagname[ CChoreoEvent::MAX_TAGNAME_STRING ]; + char wavname[ CChoreoEvent::MAX_TAGNAME_STRING ]; + + m_pTokenizer->GetToken( false ); + Q_strncpy( tagname, m_pTokenizer->CurrentToken(), sizeof( tagname ) ); + m_pTokenizer->GetToken( false ); + Q_strncpy( wavname, m_pTokenizer->CurrentToken(), sizeof( wavname ) ); + + e->SetUsingRelativeTag( true, tagname, wavname ); + } + else if ( !Q_stricmp( m_pTokenizer->CurrentToken(), "flexanimations" ) ) + { + ParseFlexAnimations( m_pTokenizer, e ); + } + else if ( !Q_stricmp( m_pTokenizer->CurrentToken(), "event_ramp" ) ) + { + ParseRamp( m_pTokenizer, e ); + } + else if ( !Q_stricmp( m_pTokenizer->CurrentToken(), "cctype" ) ) + { + m_pTokenizer->GetToken( false ); + e->SetCloseCaptionType( CChoreoEvent::CCTypeForName( m_pTokenizer->CurrentToken() ) ); + } + else if ( !Q_stricmp( m_pTokenizer->CurrentToken(), "cctoken" ) ) + { + m_pTokenizer->GetToken( false ); + e->SetCloseCaptionToken( m_pTokenizer->CurrentToken() ); + } + else if ( !Q_stricmp( m_pTokenizer->CurrentToken(), "cc_usingcombinedfile" ) ) + { + e->SetUsingCombinedFile( true ); + } + else if ( !Q_stricmp( m_pTokenizer->CurrentToken(), "cc_combinedusesgender" ) ) + { + e->SetCombinedUsingGenderToken( true ); + } + else if( !Q_stricmp( m_pTokenizer->CurrentToken(), "cc_noattenuate" ) ) + { + e->SetSuppressingCaptionAttenuation( true ); + } + } + + if ( channel ) + { + channel->AddEvent( e ); + } + + e->SetActor( actor ); + e->SetChannel( channel ); + + // It had old sytle ramp and none of the new style stuff + // Convert it + if ( hadramp && !e->GetRampCount() ) + { + // Only retrofit if something was changed by user + if ( attack != 1.0f || + sustain != 1.0f || + decay != 1.0f ) + { + float attacktime = ( 1.0f - attack ) * e->GetDuration(); + float decaytime = decay * e->GetDuration(); + float midpoint = ( attacktime + decaytime ) * 0.5f; + + e->AddRamp( attacktime, sustain, false ); + e->AddRamp( midpoint, sustain, false ); + e->AddRamp( decaytime, sustain, false ); + e->ResortRamp(); + } + } + + return e; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : CChoreoActor +//----------------------------------------------------------------------------- +CChoreoActor *CChoreoScene::ParseActor( void ) +{ + CChoreoActor *a = AllocActor(); + Assert( a ); + + m_pTokenizer->GetToken( false ); + a->SetName( m_pTokenizer->CurrentToken() ); + + m_pTokenizer->GetToken( true ); + if ( stricmp( m_pTokenizer->CurrentToken(), "{" ) ) + m_pTokenizer->Error( "expecting {" ); + + // Parse channels + while ( 1 ) + { + m_pTokenizer->GetToken( true ); + if ( !Q_stricmp( m_pTokenizer->CurrentToken(), "}" ) ) + break; + + if ( !Q_stricmp( m_pTokenizer->CurrentToken(), "channel" ) ) + { + ParseChannel( a ); + } + else if ( !Q_stricmp( m_pTokenizer->CurrentToken(), "faceposermodel" ) ) + { + ParseFacePoserModel( a ); + } + else if ( !Q_stricmp( m_pTokenizer->CurrentToken(), "active" ) ) + { + m_pTokenizer->GetToken( true ); + a->SetActive( atoi( m_pTokenizer->CurrentToken() ) ? true : false ); + } + else + { + m_pTokenizer->Error( "expecting channel got %s\n", m_pTokenizer->CurrentToken() ); + } + } + + return a; +} + +//----------------------------------------------------------------------------- +// Output : char const +//----------------------------------------------------------------------------- +const char *CChoreoScene::GetMapname( void ) +{ + return m_szMapname; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *name - +//----------------------------------------------------------------------------- +void CChoreoScene::SetMapname( const char *name ) +{ + Q_strncpy( m_szMapname, name, sizeof( m_szMapname ) ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CChoreoScene::ParseMapname( void ) +{ + m_szMapname[ 0 ] = 0; + + m_pTokenizer->GetToken( true ); + Q_strncpy( m_szMapname, m_pTokenizer->CurrentToken(), sizeof( m_szMapname ) ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CChoreoScene::ParseFPS( void ) +{ + m_pTokenizer->GetToken( true ); + m_nSceneFPS = atoi( m_pTokenizer->CurrentToken() ); + // Clamp to valid range + m_nSceneFPS = clamp( m_nSceneFPS, MIN_SCENE_FPS, MAX_SCENE_FPS); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CChoreoScene::ParseSnap( void ) +{ + m_pTokenizer->GetToken( true ); + m_bUseFrameSnap = !Q_stricmp( m_pTokenizer->CurrentToken(), "on" ) ? true : false; +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *actor - +//----------------------------------------------------------------------------- +void CChoreoScene::ParseFacePoserModel( CChoreoActor *actor ) +{ + m_pTokenizer->GetToken( true ); + actor->SetFacePoserModelName( m_pTokenizer->CurrentToken() ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *actor - +// Output : CChoreoChannel +//----------------------------------------------------------------------------- +CChoreoChannel *CChoreoScene::ParseChannel( CChoreoActor *actor ) +{ + CChoreoChannel *c = AllocChannel(); + Assert( c ); + + m_pTokenizer->GetToken( false ); + c->SetName( m_pTokenizer->CurrentToken() ); + + m_pTokenizer->GetToken( true ); + if ( stricmp( m_pTokenizer->CurrentToken(), "{" ) ) + m_pTokenizer->Error( "expecting {" ); + + // Parse channels + while ( 1 ) + { + m_pTokenizer->GetToken( true ); + if ( !Q_stricmp( m_pTokenizer->CurrentToken(), "}" ) ) + break; + + if ( !Q_stricmp( m_pTokenizer->CurrentToken(), "event" ) ) + { + ParseEvent( actor, c ); + } + else if ( !Q_stricmp( m_pTokenizer->CurrentToken(), "active" ) ) + { + m_pTokenizer->GetToken( true ); + c->SetActive( atoi( m_pTokenizer->CurrentToken() ) ? true : false ); + } + else + { + m_pTokenizer->Error( "expecting event got %s\n", m_pTokenizer->CurrentToken() ); + } + } + + Assert( actor ); + if ( actor ) + { + actor->AddChannel( c ); + c->SetActor( actor ); + } + + return c; +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CChoreoScene::ParseFromBuffer( char const *filenae, ISceneTokenProcessor *tokenizer ) +{ + Q_strncpy( m_szFileName, filenae, sizeof( m_szFileName ) ); + + m_pTokenizer = tokenizer; + + while ( 1 ) + { + if ( !m_pTokenizer->GetToken( true ) ) + { + break; + } + + if ( strlen( m_pTokenizer->CurrentToken() ) <= 0 ) + break; + + if ( !Q_stricmp( m_pTokenizer->CurrentToken(), "event" ) ) + { + ParseEvent( NULL, NULL ); + } + else if ( !Q_stricmp( m_pTokenizer->CurrentToken(), "actor" ) ) + { + ParseActor(); + } + else if ( !Q_stricmp( m_pTokenizer->CurrentToken(), "mapname" ) ) + { + ParseMapname(); + } + else if ( !Q_stricmp( m_pTokenizer->CurrentToken(), "fps" ) ) + { + ParseFPS(); + } + else if ( !Q_stricmp( m_pTokenizer->CurrentToken(), "snap" ) ) + { + ParseSnap(); + } + else if ( !Q_stricmp( m_pTokenizer->CurrentToken(), "scene_ramp" ) ) + { + ParseSceneRamp( m_pTokenizer, this ); + } + else if ( !Q_stricmp( m_pTokenizer->CurrentToken(), "scalesettings" ) ) + { + ParseScaleSettings( m_pTokenizer, this ); + } + else + { + m_pTokenizer->Error( "unexpected token %s\n", m_pTokenizer->CurrentToken() ); + break; + } + } + + // Fixup time tags + ReconcileTags(); + + ReconcileGestureTimes(); + + ReconcileCloseCaption(); + + InternalDetermineEventTypes(); + + if ( CChoreoScene::s_bEditingDisabled ) + { + m_flPrecomputedStopTime = FindStopTime(); + } + + return true; +} + +void CChoreoScene::RemoveEventsExceptTypes( int* typeList, int count ) +{ + int i; + for ( i = 0 ; i < m_Actors.Count(); i++ ) + { + CChoreoActor *a = m_Actors[ i ]; + if ( !a ) + continue; + + for ( int j = 0; j < a->GetNumChannels(); j++ ) + { + CChoreoChannel *c = a->GetChannel( j ); + if ( !c ) + continue; + + int num = c->GetNumEvents(); + for ( int k = num - 1 ; k >= 0; --k ) + { + CChoreoEvent *e = c->GetEvent( k ); + if ( !e ) + continue; + + bool found = false; + for ( int idx = 0; idx < count; ++idx ) + { + if ( e->GetType() == ( CChoreoEvent::EVENTTYPE )typeList[ idx ] ) + { + found = true; + break; + } + } + + if ( !found ) + { + c->RemoveEvent( e ); + DeleteReferencedObjects( e ); + } + } + } + } + + // Remvoe non-matching global events, too + for ( i = m_Events.Count() - 1 ; i >= 0; --i ) + { + CChoreoEvent *e = m_Events[ i ]; + + // This was already dealt with above... + if ( e->GetActor() ) + continue; + + bool found = false; + for ( int idx = 0; idx < count; ++idx ) + { + if ( e->GetType() == ( CChoreoEvent::EVENTTYPE )typeList[ idx ] ) + { + found = true; + break; + } + } + + if ( !found ) + { + DeleteReferencedObjects( e ); + } + } +} + +void CChoreoScene::InternalDetermineEventTypes() +{ + m_bitvecHasEventOfType.ClearAll(); + + for ( int i = 0 ; i < m_Actors.Size(); i++ ) + { + CChoreoActor *a = m_Actors[ i ]; + if ( !a ) + continue; + + for ( int j = 0; j < a->GetNumChannels(); j++ ) + { + CChoreoChannel *c = a->GetChannel( j ); + if ( !c ) + continue; + + for ( int k = 0 ; k < c->GetNumEvents(); k++ ) + { + CChoreoEvent *e = c->GetEvent( k ); + if ( !e ) + continue; + + m_bitvecHasEventOfType.Set( e->GetType(), true ); + } + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : float +//----------------------------------------------------------------------------- +float CChoreoScene::FindStopTime( void ) +{ + if ( m_flPrecomputedStopTime != 0.0f ) + { + return m_flPrecomputedStopTime; + } + + float lasttime = 0.0f; + + int c = m_Events.Count(); + for ( int i = 0; i < c ; i++ ) + { + CChoreoEvent *e = m_Events[ i ]; + Assert( e ); + + float checktime = e->HasEndTime() ? e->GetEndTime() : e->GetStartTime(); + if ( checktime > lasttime ) + { + lasttime = checktime; + } + } + + return lasttime; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *fp - +// level - +// *fmt - +// ... - +//----------------------------------------------------------------------------- +void CChoreoScene::FilePrintf( CUtlBuffer& buf, int level, const char *fmt, ... ) +{ + va_list argptr; + va_start( argptr, fmt ); + + while ( level-- > 0 ) + { + buf.Printf( " " ); + } + + buf.VaPrintf( fmt, argptr ); + va_end( argptr ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *fp - +//----------------------------------------------------------------------------- +void CChoreoScene::FileSaveHeader( CUtlBuffer& buf ) +{ + FilePrintf( buf, 0, "// Choreo version 1\n" ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : mark - +//----------------------------------------------------------------------------- +void CChoreoScene::MarkForSaveAll( bool mark ) +{ + int i; + + // Mark global events + for ( i = 0 ; i < m_Events.Size(); i++ ) + { + CChoreoEvent *e = m_Events[ i ]; + if ( e->GetActor() ) + continue; + + e->SetMarkedForSave( mark ); + } + + // Recursively mark everything else + for ( i = 0 ; i < m_Actors.Size(); i++ ) + { + CChoreoActor *a = m_Actors[ i ]; + if ( !a ) + continue; + + a->MarkForSaveAll( mark ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *filename - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CChoreoScene::ExportMarkedToFile( const char *filename ) +{ + // Create a serialization buffer + CUtlBuffer buf( 0, 0, true ); + FileSaveHeader( buf ); + + // Look for events that don't have actor/channel set + int i; + for ( i = 0 ; i < m_Events.Size(); i++ ) + { + CChoreoEvent *e = m_Events[ i ]; + if ( e->GetActor() ) + continue; + + FileSaveEvent( buf, 0, e ); + } + + for ( i = 0 ; i < m_Actors.Size(); i++ ) + { + CChoreoActor *a = m_Actors[ i ]; + if ( !a ) + continue; + + FileSaveActor( buf, 0, a ); + } + + // Write it out baby + FileHandle_t fh = SceneFileSystem()->Open( filename, "wt" ); + if (fh) + { + SceneFileSystem()->Write( buf.Base(), buf.TellPut(), fh ); + SceneFileSystem()->Close(fh); + return true; + } + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *filename - +//----------------------------------------------------------------------------- +bool CChoreoScene::SaveToFile( const char *filename ) +{ + // Create a serialization buffer + CUtlBuffer buf( 0, 0, true ); + FileSaveHeader( buf ); + + MarkForSaveAll( true ); + + // Look for events that don't have actor/channel set + int i; + for ( i = 0 ; i < m_Events.Size(); i++ ) + { + CChoreoEvent *e = m_Events[ i ]; + if ( e->GetActor() ) + continue; + + FileSaveEvent( buf, 0, e ); + } + + for ( i = 0 ; i < m_Actors.Size(); i++ ) + { + CChoreoActor *a = m_Actors[ i ]; + if ( !a ) + continue; + + FileSaveActor( buf, 0, a ); + } + + if ( m_szMapname[ 0 ] ) + { + FilePrintf( buf, 0, "mapname \"%s\"\n", m_szMapname ); + } + + FileSaveSceneRamp( buf, 0, this ); + FileSaveScaleSettings( buf, 0, this ); + + FilePrintf( buf, 0, "fps %i\n", m_nSceneFPS ); + FilePrintf( buf, 0, "snap %s\n", m_bUseFrameSnap ? "on" : "off" ); + + // Write it out baby + FileHandle_t fh = SceneFileSystem()->Open( filename, "wt" ); + if (fh) + { + SceneFileSystem()->Write( buf.Base(), buf.TellPut(), fh ); + SceneFileSystem()->Close(fh); + return true; + } + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : buf - +// level - +// *e - +//----------------------------------------------------------------------------- +void CChoreoScene::FileSaveRamp( CUtlBuffer& buf, int level, CChoreoEvent *e ) +{ + // Nothing to save? + int c = e->GetRampCount(); + if ( c <= 0 && + !e->RampIsEdgeActive( true ) && + !e->RampIsEdgeActive( false ) ) + return; + + char line[ 1024 ]; + Q_strncpy( line, "event_ramp ", sizeof( line ) ); + + if ( e->RampIsEdgeActive( true ) || e->RampIsEdgeActive( false ) ) + { + if ( e->RampIsEdgeActive( true ) ) + { + char sz[ 256 ]; + Q_snprintf( sz, sizeof( sz ), "leftedge %s %.3f ", Interpolator_NameForCurveType( e->RampGetEdgeCurveType( true ), false ), e->RampGetEdgeZeroValue( true ) ); + Q_strncat( line, sz, sizeof( line ), COPY_ALL_CHARACTERS ); + } + if ( e->RampIsEdgeActive( false ) ) + { + char sz[ 256 ]; + Q_snprintf( sz, sizeof( sz ), "rightedge %s %.3f", Interpolator_NameForCurveType( e->RampGetEdgeCurveType( false ), false ), e->RampGetEdgeZeroValue( false ) ); + Q_strncat( line, sz, sizeof( line ), COPY_ALL_CHARACTERS ); + } + } + + FilePrintf( buf, level, "%s\n", line ); + FilePrintf( buf, level, "{\n" ); + + for ( int i = 0; i < c; i++ ) + { + CExpressionSample *sample = e->GetRamp( i ); + if ( sample->GetCurveType() != CURVE_DEFAULT ) + { + FilePrintf( buf, level + 1, "%.4f %.4f \"%s\"\n", + sample->time, + sample->value, + Interpolator_NameForCurveType( sample->GetCurveType(), false ) ); + } + else + { + FilePrintf( buf, level + 1, "%.4f %.4f\n", + sample->time, + sample->value ); + } + } + + FilePrintf( buf, level, "}\n" ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : buf - +// level - +// *e - +//----------------------------------------------------------------------------- +void CChoreoScene::FileSaveSceneRamp( CUtlBuffer& buf, int level, CChoreoScene *scene ) +{ + // Nothing to save? + int c = scene->GetSceneRampCount(); + if ( c <= 0 && + !scene->SceneRampIsEdgeActive( true ) && + !scene->SceneRampIsEdgeActive( false ) ) + return; + + char line[ 1024 ]; + Q_strncpy( line, "scene_ramp ", sizeof( line ) ); + + if ( scene->SceneRampIsEdgeActive( true ) || scene->SceneRampIsEdgeActive( false ) ) + { + if ( scene->SceneRampIsEdgeActive( true ) ) + { + char sz[ 256 ]; + Q_snprintf( sz, sizeof( sz ), "leftedge %s %.3f ", Interpolator_NameForCurveType( scene->SceneRampGetEdgeCurveType( true ), false ), scene->SceneRampGetEdgeZeroValue( true ) ); + Q_strncat( line, sz, sizeof( line ), COPY_ALL_CHARACTERS ); + } + if ( scene->SceneRampIsEdgeActive( false ) ) + { + char sz[ 256 ]; + Q_snprintf( sz, sizeof( sz ),"rightedge %s %.3f", Interpolator_NameForCurveType( scene->SceneRampGetEdgeCurveType( false ), false ), scene->SceneRampGetEdgeZeroValue( false ) ); + Q_strncat( line, sz, sizeof( line ), COPY_ALL_CHARACTERS ); + } + } + + FilePrintf( buf, level, "%s\n", line ); + FilePrintf( buf, level, "{\n" ); + + for ( int i = 0; i < c; i++ ) + { + CExpressionSample *sample = scene->GetSceneRamp( i ); + if ( sample->GetCurveType() != CURVE_DEFAULT ) + { + FilePrintf( buf, level + 1, "%.4f %.4f \"%s\"\n", + sample->time, + sample->value, + Interpolator_NameForCurveType( sample->GetCurveType(), false ) ); + } + else + { + FilePrintf( buf, level + 1, "%.4f %.4f\n", + sample->time, + sample->value ); + } + } + + FilePrintf( buf, level, "}\n" ); +} + +void CChoreoScene::FileSaveScaleSettings( CUtlBuffer& buf, int level, CChoreoScene *scene ) +{ + // Nothing to save? + int c = scene->m_TimeZoomLookup.Count(); + if ( c <= 0 ) + return; + + FilePrintf( buf, level, "scalesettings\n" ); + FilePrintf( buf, level, "{\n" ); + + for ( int i = 0; i < c; i++ ) + { + int value = scene->m_TimeZoomLookup[ i ]; + + FilePrintf( buf, level + 1, "\"%s\" \"%i\"\n", + scene->m_TimeZoomLookup.GetElementName( i ), + value ); + } + + FilePrintf( buf, level, "}\n" ); +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : buf - +// level - +// *track - +//----------------------------------------------------------------------------- +void CChoreoScene::FileSaveFlexAnimationTrack( CUtlBuffer& buf, int level, CFlexAnimationTrack *track ) +{ + if ( !track ) + return; + + if ( !track->IsTrackActive() && track->GetNumSamples() <= 0 ) + return; + + char line[ 1024 ]; + Q_snprintf( line, sizeof( line ), "\"%s\" ", track->GetFlexControllerName() ); + if ( !track->IsTrackActive() ) + { + char sz[ 256 ]; + Q_snprintf( sz, sizeof( sz ), "disabled " ); + Q_strncat( line, sz, sizeof( line ), COPY_ALL_CHARACTERS ); + } + if ( track->IsComboType() ) + { + char sz[ 256 ]; + Q_snprintf( sz, sizeof( sz ), "combo " ); + Q_strncat( line, sz, sizeof( line ), COPY_ALL_CHARACTERS ); + } + if ( track->GetMin() != 0.0f || track->GetMax() != 1.0f) + { + char sz[ 256 ]; + Q_snprintf( sz, sizeof( sz ), "range %.1f %.1f ", track->GetMin(), track->GetMax() ); + Q_strncat( line, sz, sizeof( line ), COPY_ALL_CHARACTERS ); + } + if ( track->IsEdgeActive( true ) || track->IsEdgeActive( false ) ) + { + char edgestr[ 512 ]; + edgestr[ 0 ] = 0; + + if ( track->IsEdgeActive( true ) ) + { + char sz[ 256 ]; + Q_snprintf( sz, sizeof( sz ), "leftedge %s %.3f ", Interpolator_NameForCurveType( track->GetEdgeCurveType( true ), false ), track->GetEdgeZeroValue( true ) ); + Q_strncat( edgestr, sz, sizeof( edgestr ), COPY_ALL_CHARACTERS ); + } + if ( track->IsEdgeActive( false ) ) + { + char sz[ 256 ]; + Q_snprintf( sz, sizeof( sz ), "rightedge %s %.3f ", Interpolator_NameForCurveType( track->GetEdgeCurveType( false ), false ), track->GetEdgeZeroValue( false ) ); + Q_strncat( edgestr, sz, sizeof( edgestr ), COPY_ALL_CHARACTERS ); + } + + Q_strncat( line, edgestr, sizeof( line ), COPY_ALL_CHARACTERS ); + } + + + FilePrintf( buf, level + 2, "%s\n", line ); + + // Write out samples + FilePrintf( buf, level + 2, "{\n" ); + + for ( int j = 0 ; j < track->GetNumSamples( 0 ) ; j++ ) + { + CExpressionSample *s = track->GetSample( j, 0 ); + if ( !s ) + continue; + + if ( s->GetCurveType() != CURVE_DEFAULT ) + { + FilePrintf( buf, level + 3, "%.4f %.4f \"%s\"\n", + s->time, + s->value, + Interpolator_NameForCurveType( s->GetCurveType(), false ) ); + } + else + { + FilePrintf( buf, level + 3, "%.4f %.4f\n", + s->time, + s->value ); + } + } + + FilePrintf( buf, level + 2, "}\n" ); + + // Write out combo samples + if ( track->IsComboType() ) + { + FilePrintf( buf, level + 2, "{\n" ); + + for ( int j = 0 ; j < track->GetNumSamples( 1) ; j++ ) + { + CExpressionSample *s = track->GetSample( j, 1 ); + if ( !s ) + continue; + + if ( s->GetCurveType() != CURVE_DEFAULT ) + { + FilePrintf( buf, level + 3, "%.4f %.4f \"%s\"\n", + s->time, + s->value, + Interpolator_NameForCurveType( s->GetCurveType(), false ) ); + } + else + { + FilePrintf( buf, level + 3, "%.4f %.4f\n", + s->time, + s->value ); + } + } + + FilePrintf( buf, level + 2, "}\n" ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : buf - +// level - +// *e - +//----------------------------------------------------------------------------- +void CChoreoScene::FileSaveFlexAnimations( CUtlBuffer& buf, int level, CChoreoEvent *e ) +{ + // Nothing to save + if ( e->GetNumFlexAnimationTracks() <= 0 ) + return; + + FilePrintf( buf, level + 1, "flexanimations samples_use_time\n" ); + FilePrintf( buf, level + 1, "{\n" ); + + for ( int i = 0; i < e->GetNumFlexAnimationTracks(); i++ ) + { + CFlexAnimationTrack *track = e->GetFlexAnimationTrack( i ); + FileSaveFlexAnimationTrack( buf, level, track ); + } + + FilePrintf( buf, level + 1, "}\n" ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *fp - +// level - +// *e - +//----------------------------------------------------------------------------- +void CChoreoScene::FileSaveEvent( CUtlBuffer& buf, int level, CChoreoEvent *e ) +{ + if ( !e->IsMarkedForSave() ) + return; + + FilePrintf( buf, level, "event %s \"%s\"\n", CChoreoEvent::NameForType( e->GetType() ), e->GetName() ); + FilePrintf( buf, level, "{\n" ); + + float st, et; + st = e->GetStartTime(); + et = e->GetEndTime(); + + FilePrintf( buf, level + 1, "time %f %f\n", st, et ); + FilePrintf( buf, level + 1, "param \"%s\"\n", e->GetParameters() ); + if ( strlen( e->GetParameters2() ) > 0 ) + { + FilePrintf( buf, level + 1, "param2 \"%s\"\n", e->GetParameters2() ); + } + if ( e->GetRampCount() > 0 ) + { + FileSaveRamp( buf, level + 1, e ); + } + if ( e->GetPitch() != 0 ) + { + FilePrintf( buf, level + 1, "pitch \"%i\"\n", e->GetPitch() ); + } + if ( e->GetYaw() != 0 ) + { + FilePrintf( buf, level + 1, "yaw \"%i\"\n", e->GetYaw() ); + } + if ( e->IsResumeCondition() ) + { + FilePrintf( buf, level + 1, "resumecondition\n" ); + } + if ( e->IsLockBodyFacing() ) + { + FilePrintf( buf, level + 1, "lockbodyfacing\n" ); + } + if ( e->GetDistanceToTarget() > 0.0f ) + { + FilePrintf( buf, level + 1, "distancetotarget %.2f\n", e->GetDistanceToTarget() ); + } + if ( e->GetForceShortMovement() ) + { + FilePrintf( buf, level + 1, "forceshortmovement\n" ); + } + if ( e->GetSyncToFollowingGesture() ) + { + FilePrintf( buf, level + 1, "synctofollowinggesture\n" ); + } + if ( e->IsFixedLength() ) + { + FilePrintf( buf, level + 1, "fixedlength\n" ); + } + if ( e->GetNumRelativeTags() > 0 ) + { + FilePrintf( buf, level + 1, "tags\n" ); + FilePrintf( buf, level + 1, "{\n" ); + for ( int t = 0; t < e->GetNumRelativeTags(); t++ ) + { + CEventRelativeTag *rt = e->GetRelativeTag( t ); + Assert( rt ); + FilePrintf( buf, level + 2, "\"%s\" %f\n", rt->GetName(), rt->GetPercentage() ); + } + FilePrintf( buf, level + 1, "}\n" ); + } + if ( e->GetNumTimingTags() > 0 ) + { + FilePrintf( buf, level + 1, "flextimingtags\n" ); + FilePrintf( buf, level + 1, "{\n" ); + for ( int t = 0; t < e->GetNumTimingTags(); t++ ) + { + CFlexTimingTag *tt = e->GetTimingTag( t ); + Assert( tt ); + FilePrintf( buf, level + 2, "\"%s\" %f %i\n", tt->GetName(), tt->GetPercentage(), tt->GetLocked() ? 1 : 0 ); + } + FilePrintf( buf, level + 1, "}\n" ); + } + int tagtype; + for ( tagtype = 0; tagtype < CChoreoEvent::NUM_ABS_TAG_TYPES; tagtype++ ) + { + if ( e->GetNumAbsoluteTags( (CChoreoEvent::AbsTagType)tagtype ) > 0 ) + { + FilePrintf( buf, level + 1, "absolutetags %s\n", CChoreoEvent::NameForAbsoluteTagType( (CChoreoEvent::AbsTagType)tagtype ) ); + FilePrintf( buf, level + 1, "{\n" ); + for ( int t = 0; t < e->GetNumAbsoluteTags( (CChoreoEvent::AbsTagType)tagtype ); t++ ) + { + CEventAbsoluteTag *abstag = e->GetAbsoluteTag( (CChoreoEvent::AbsTagType)tagtype, t ); + Assert( abstag ); + FilePrintf( buf, level + 2, "\"%s\" %f\n", abstag->GetName(), abstag->GetPercentage() ); + } + FilePrintf( buf, level + 1, "}\n" ); + } + } + + if ( e->GetType() == CChoreoEvent::GESTURE ) + { + float duration; + if ( e->GetGestureSequenceDuration( duration ) ) + { + FilePrintf( buf, level + 1, "sequenceduration %f\n", duration ); + } + } + + if ( e->IsUsingRelativeTag() ) + { + FilePrintf( buf, level + 1, "relativetag \"%s\" \"%s\"\n", + e->GetRelativeTagName(), e->GetRelativeWavName() ); + } + + if ( e->GetNumFlexAnimationTracks() > 0 ) + { + FileSaveFlexAnimations( buf, level, e ); + } + + if ( e->GetType() == CChoreoEvent::LOOP ) + { + FilePrintf( buf, level + 1, "loopcount \"%i\"\n", e->GetLoopCount() ); + } + + if ( e->GetType() == CChoreoEvent::SPEAK ) + { + FilePrintf( buf, level + 1, "cctype \"%s\"\n", CChoreoEvent::NameForCCType( e->GetCloseCaptionType() ) ); + FilePrintf( buf, level + 1, "cctoken \"%s\"\n", e->GetCloseCaptionToken() ); + if ( e->GetCloseCaptionType() != CChoreoEvent::CC_DISABLED && + e->IsUsingCombinedFile() ) + { + FilePrintf( buf, level + 1, "cc_usingcombinedfile\n" ); + } + if ( e->IsCombinedUsingGenderToken() ) + { + FilePrintf( buf, level + 1, "cc_combinedusesgender\n" ); + } + if ( e->IsSuppressingCaptionAttenuation() ) + { + FilePrintf( buf, level + 1, "cc_noattenuate\n" ); + } + } + + FilePrintf( buf, level, "}\n" ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *fp - +// level - +// *c - +//----------------------------------------------------------------------------- +void CChoreoScene::FileSaveChannel( CUtlBuffer& buf, int level, CChoreoChannel *c ) +{ + if ( !c->IsMarkedForSave() ) + return; + + FilePrintf( buf, level, "channel \"%s\"\n", c->GetName() ); + FilePrintf( buf, level, "{\n" ); + + for ( int i = 0; i < c->GetNumEvents(); i++ ) + { + CChoreoEvent *e = c->GetEvent( i ); + if ( e ) + { + FileSaveEvent( buf, level + 1, e ); + } + } + + if ( !c->GetActive() ) + { + // Only write out inactive + FilePrintf( buf, level + 1, "active \"0\"\n" ); + } + + FilePrintf( buf, level, "}\n" ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *fp - +// level - +// *a - +//----------------------------------------------------------------------------- +void CChoreoScene::FileSaveActor( CUtlBuffer& buf, int level, CChoreoActor *a ) +{ + if ( !a->IsMarkedForSave() ) + return; + + FilePrintf( buf, level, "actor \"%s\"\n", a->GetName() ); + FilePrintf( buf, level, "{\n" ); + + for ( int i = 0; i < a->GetNumChannels(); i++ ) + { + CChoreoChannel *c = a->GetChannel( i ); + if ( c ) + { + FileSaveChannel( buf, level + 1, c ); + } + } + + if ( Q_strlen( a->GetFacePoserModelName() ) > 0 ) + { + FilePrintf( buf, level + 1, "faceposermodel \"%s\"\n", a->GetFacePoserModelName() ); + } + + if ( !a->GetActive() ) + { + // Only write out inactive + FilePrintf( buf, level + 1, "active \"0\"\n" ); + } + + FilePrintf( buf, level, "}\n\n" ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : float +//----------------------------------------------------------------------------- +float CChoreoScene::FindAdjustedStartTime( void ) +{ + float earliest_time = 0.0f; + + CChoreoEvent *e; + + for ( int i = 0; i < m_Events.Size(); i++ ) + { + e = m_Events[ i ]; + + float starttime = e->GetStartTime(); + + // If it's a wav file, pre-queue the starting time by the sound system's + // current latency + if ( e->GetType() == CChoreoEvent::SPEAK ) + { + starttime -= m_flSoundSystemLatency; + } + + if ( starttime < earliest_time ) + { + earliest_time = starttime; + } + } + + return earliest_time; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : float +//----------------------------------------------------------------------------- +float CChoreoScene::FindAdjustedEndTime( void ) +{ + float latest_time = 0.0f; + + CChoreoEvent *e; + + for ( int i = 0; i < m_Events.Size(); i++ ) + { + e = m_Events[ i ]; + + float endtime = e->GetStartTime(); + if ( e->HasEndTime() ) + { + endtime = e->GetEndTime(); + } + + // If it's a wav file, pre-queue the starting time by the sound system's + // current latency + if ( e->GetType() == CChoreoEvent::SPEAK ) + { + endtime += m_flSoundSystemLatency; + } + + if ( endtime > latest_time ) + { + latest_time = endtime; + } + } + + return latest_time; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CChoreoScene::ResetSimulation( bool forward /*= true*/, float starttime /*= 0.0f*/, float endtime /*= 0.0f*/ ) +{ + CChoreoEvent *e; + + m_ActiveResumeConditions.RemoveAll(); + m_ResumeConditions.RemoveAll(); + m_PauseEvents.RemoveAll(); + + // Put all items into the pending queue + for ( int i = 0; i < m_Events.Size(); i++ ) + { + e = m_Events[ i ]; + e->ResetProcessing(); + + if ( e->GetType() == CChoreoEvent::SECTION ) + { + m_PauseEvents.AddToTail( e ); + continue; + } + + if ( e->IsResumeCondition() ) + { + m_ResumeConditions.AddToTail( e ); + continue; + } + } + + // Find earliest adjusted start time + m_flEarliestTime = FindAdjustedStartTime(); + m_flLatestTime = FindAdjustedEndTime(); + + m_flCurrentTime = forward ? m_flEarliestTime : m_flLatestTime; + + // choreoprintf( 0, "Start time %f\n", m_flCurrentTime ); + + m_flLastActiveTime = 0.0f; + m_nActiveEvents = m_Events.Size(); + + m_flStartTime = starttime; + m_flEndTime = endtime; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CChoreoScene::CheckEventCompletion( void ) +{ + CChoreoEvent *e; + + bool bAllCompleted = true; + // check all items in the active pending queue + for ( int i = 0; i < m_ActiveResumeConditions.Size(); i++ ) + { + e = m_ActiveResumeConditions[ i ]; + + bAllCompleted = bAllCompleted && e->CheckProcessing( m_pIChoreoEventCallback, this, m_flCurrentTime ); + } + return bAllCompleted; +} + + + + +//----------------------------------------------------------------------------- +// Purpose: +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CChoreoScene::SimulationFinished( void ) +{ + // Scene's linger for a little bit to allow things to settle + // check for events that are still active... + + if ( m_flCurrentTime > m_flLatestTime ) + { + if ( m_nActiveEvents != 0 ) + { + return false; + } + + return true; + } + if ( m_flCurrentTime < m_flEarliestTime ) + { + return true; + } + + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CChoreoEvent *CChoreoScene::FindPauseBetweenTimes( float starttime, float endtime ) +{ + CChoreoEvent *e; + + // Iterate through all events in the scene + for ( int i = 0; i < m_PauseEvents.Size(); i++ ) + { + e = m_PauseEvents[ i ]; + if ( !e ) + continue; + + Assert( e->GetType() == CChoreoEvent::SECTION ); + + int time_is = IsTimeInRange( e->GetStartTime(), starttime, endtime ); + if ( IN_RANGE != time_is ) + continue; + + // Found a pause in between start and end time + return e; + } + + // No pause inside the specified time span + return NULL; +} + +int CChoreoScene::IsTimeInRange( float t, float starttime, float endtime ) +{ + if ( t > endtime ) + { + return AFTER_RANGE; + } + else if ( t < starttime ) + { + return BEFORE_RANGE; + } + + return IN_RANGE; +} + +int CChoreoScene::EventThink( CChoreoEvent *e, float frame_start_time, float frame_end_time, bool playing_forward, PROCESSING_TYPE& disposition ) +{ + disposition = PROCESSING_TYPE_IGNORE; + int iret = 0; + + bool hasend = e->HasEndTime(); + float starttime, endtime; + + starttime = e->GetStartTime(); + endtime = hasend ? e->GetEndTime() : e->GetStartTime(); + + if ( !playing_forward ) + { + // Swap intervals + float temp = frame_start_time; + frame_start_time = frame_end_time; + frame_end_time = temp; + } + + bool suppressed = false; + + // Special processing + switch ( e->GetType() ) + { + default: + break; + case CChoreoEvent::SPEAK: + // If it's a wav file, pre-queue the starting/endtime time by the sound system's + // current latency + { + if ( playing_forward ) + { + starttime -= m_flSoundSystemLatency; + + + // Search for pause condition in between the original time and the + // adjusted start time, but make sure that the pause event hasn't already triggered... + CChoreoEvent *pauseEvent = FindPauseBetweenTimes( starttime, starttime + m_flSoundSystemLatency ); + if ( pauseEvent && + ( frame_start_time <= pauseEvent->GetStartTime() ) ) + { + pauseEvent->AddEventDependency( e ); + + suppressed = true; + } + } + /* + else + // Don't bother if playing backward!!! + { + endtime += m_flSoundSystemLatency; + + // Search for pause condition in between the original time and the + // adjusted start time + CChoreoEvent *pauseEvent = FindPauseBetweenTimes( endtime - m_flSoundSystemLatency, endtime ); + if ( pauseEvent ) + { + pauseEvent->AddEventDependency( e ); + + suppressed = true; + } + } + */ + } + break; + case CChoreoEvent::SUBSCENE: + { + if ( IsSubScene() ) + { + suppressed = true; + } + } + break; + } + + if ( suppressed ) + { + if ( e->IsProcessing() ) + { + disposition = PROCESSING_TYPE_STOP; + } + return iret; + } + + int where_is_event; + + if ( e->IsProcessing() ) + { + where_is_event = IsTimeInRange( frame_start_time, starttime, endtime ); + if ( IN_RANGE == where_is_event ) + { + disposition = PROCESSING_TYPE_CONTINUE; + iret = 1; + } + else + { + disposition = PROCESSING_TYPE_STOP; + } + } + else + { + + // Is the event supposed to be active at this time + where_is_event = IsTimeInRange( frame_start_time, starttime, endtime ); + + if ( IN_RANGE == where_is_event ) + { + if ( e->IsResumeCondition() ) + { + disposition = PROCESSING_TYPE_START_RESUMECONDITION; + } + else + { + disposition = PROCESSING_TYPE_START; + } + iret = 1; + } + // See if it's a single fire event which should occur during this frame + else if ( !hasend ) + { + where_is_event = IsTimeInRange( starttime, frame_start_time, frame_end_time ); + if ( IN_RANGE == where_is_event ) + { + disposition = PROCESSING_TYPE_START; + iret = 1; + } + + } + } + + return iret; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : &e0 - +// &e1 - +// Output : static bool +//----------------------------------------------------------------------------- +bool CChoreoScene::EventLess( const CChoreoScene::ActiveList &al0, const CChoreoScene::ActiveList &al1 ) +{ + CChoreoEvent *event0, *event1; + event0 = const_cast< CChoreoEvent * >( al0.e ); + event1 = const_cast< CChoreoEvent * >( al1.e ); + + if ( event0->GetStartTime() < event1->GetStartTime() ) + { + return true; + } + + if ( event0->GetStartTime() > event1->GetStartTime() ) + { + return false; + } + + // Check for end time overlap + if ( event0->HasEndTime() && event1->HasEndTime() ) + { + if ( event0->GetEndTime() > event1->GetEndTime() ) + return true; + else if ( event0->GetEndTime() < event1->GetEndTime() ) + return false; + } + + CChoreoActor *a0, *a1; + a0 = event0->GetActor(); + a1 = event1->GetActor(); + + // Start time equal, go to order in channel + if ( !a0 || !a1 || a0 != a1 ) + { + return strcmp( event0->GetName(), event1->GetName() ) == -1; + } + + CChoreoChannel *c0 = event0->GetChannel(); + CChoreoChannel *c1 = event1->GetChannel(); + + if ( !c0 || !c1 || c0 != c1 ) + { + return strcmp( event0->GetName(), event1->GetName() ) == -1; + } + + // Go by slot within channel + int index0 = a0->FindChannelIndex( c0 ); + int index1 = a1->FindChannelIndex( c1 ); + + return ( index0 < index1 ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CChoreoScene::ClearPauseEventDependencies() +{ + int c = m_PauseEvents.Count(); + for ( int i = 0 ; i < c; ++i ) + { + CChoreoEvent *pause = m_PauseEvents[ i ]; + Assert( pause ); + pause->ClearEventDependencies(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *pauseEvent - +// *suppressed - +//----------------------------------------------------------------------------- +void CChoreoScene::AddPauseEventDependency( CChoreoEvent *pauseEvent, CChoreoEvent *suppressed ) +{ + Assert( pauseEvent ); + Assert( pauseEvent != suppressed ); + pauseEvent->AddEventDependency( suppressed ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : dt - +//----------------------------------------------------------------------------- +void CChoreoScene::Think( float curtime ) +{ + CChoreoEvent *e; + + float oldt = m_flCurrentTime; + float dt = curtime - oldt; + + bool playing_forward = ( dt >= 0.0f ) ? true : false; + + m_nActiveEvents = 0; + + ClearPauseEventDependencies(); + + CUtlRBTree< ActiveList, int > pending(0,0,EventLess); + + // Iterate through all events in the scene + int i; + for ( i = 0; i < m_Events.Size(); i++ ) + { + e = m_Events[ i ]; + if ( !e ) + continue; + + PROCESSING_TYPE disposition; + m_nActiveEvents += EventThink( e, m_flCurrentTime, curtime, playing_forward, disposition ); + + if ( disposition != PROCESSING_TYPE_IGNORE ) + { + + ActiveList entry; + + entry.e = e; + entry.pt = disposition; + + pending.Insert( entry ); + } + } + + // Events are sorted start time and then by channel and actor slot or by name if those aren't equal + bool dump = false; + + i = pending.FirstInorder(); + while ( i != pending.InvalidIndex() ) + { + ActiveList *entry = &pending[ i ]; + + Assert( entry->e ); + + if ( dump ) + { + Msg( "%f == %s starting at %f (actor %p channel %p)\n", + m_flCurrentTime, entry->e->GetName(), entry->e->GetStartTime(), + entry->e->GetActor(), entry->e->GetChannel() ); + } + + switch ( entry->pt ) + { + default: + case PROCESSING_TYPE_IGNORE: + { + Assert( 0 ); + } + break; + case PROCESSING_TYPE_START: + case PROCESSING_TYPE_START_RESUMECONDITION: + { + entry->e->StartProcessing( m_pIChoreoEventCallback, this, m_flCurrentTime ); + + if ( entry->pt == PROCESSING_TYPE_START_RESUMECONDITION ) + { + Assert( entry->e->IsResumeCondition() ); + m_ActiveResumeConditions.AddToTail( entry->e ); + } + + // This event can "pause" the scene, so we need to remember who "paused" the scene so that + // when we resume we can resume any suppressed events dependent on this pauser... + if ( entry->e->GetType() == CChoreoEvent::SECTION ) + { + // So this event should be in the pauseevents list, otherwise this'll be -1 + m_nLastPauseEvent = m_PauseEvents.Find( entry->e ); + } + } + break; + case PROCESSING_TYPE_CONTINUE: + { + entry->e->ContinueProcessing( m_pIChoreoEventCallback, this, m_flCurrentTime ); + } + break; + case PROCESSING_TYPE_STOP: + { + entry->e->StopProcessing( m_pIChoreoEventCallback, this, m_flCurrentTime ); + } + break; + } + + i = pending.NextInorder( i ); + } + + if ( dump ) + { + Msg( "\n" ); + } + + // If a Process call slams this time, don't override it!!! + if ( oldt == m_flCurrentTime ) + { + m_flCurrentTime = curtime; + } + + // Still processing? + if ( m_nActiveEvents ) + { + m_flLastActiveTime = m_flCurrentTime; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : float +//----------------------------------------------------------------------------- +float CChoreoScene::GetTime( void ) +{ + return m_flCurrentTime; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : t - +//----------------------------------------------------------------------------- +void CChoreoScene::SetTime( float t ) +{ + m_flCurrentTime = t; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : t - +//----------------------------------------------------------------------------- +void CChoreoScene::LoopToTime( float t ) +{ + m_flCurrentTime = t; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *pfn - +//----------------------------------------------------------------------------- +void CChoreoScene::SetPrintFunc( void ( *pfn ) ( const char *fmt, ... ) ) +{ + m_pfnPrint = pfn; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *actor - +//----------------------------------------------------------------------------- +void CChoreoScene::RemoveActor( CChoreoActor *actor ) +{ + int idx = FindActorIndex( actor ); + if ( idx == -1 ) + return; + + m_Actors.Remove( idx ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *actor - +// Output : int +//----------------------------------------------------------------------------- +int CChoreoScene::FindActorIndex( CChoreoActor *actor ) +{ + for ( int i = 0; i < m_Actors.Size(); i++ ) + { + if ( actor == m_Actors[ i ] ) + { + return i; + } + } + return -1; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : a1 - +// a2 - +//----------------------------------------------------------------------------- +void CChoreoScene::SwapActors( int a1, int a2 ) +{ + CChoreoActor *temp; + + temp = m_Actors[ a1 ]; + m_Actors[ a1 ] = m_Actors[ a2 ]; + m_Actors[ a2 ] = temp; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *actor - +//----------------------------------------------------------------------------- +void CChoreoScene::DeleteReferencedObjects( CChoreoActor *actor ) +{ + for ( int i = 0; i < actor->GetNumChannels(); i++ ) + { + CChoreoChannel *channel = actor->GetChannel( i ); + actor->RemoveChannel( channel ); + + DeleteReferencedObjects( channel ); + } + + DestroyActor( actor ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *channel - +//----------------------------------------------------------------------------- +void CChoreoScene::DeleteReferencedObjects( CChoreoChannel *channel ) +{ + for ( int i = 0; i < channel->GetNumEvents(); i++ ) + { + CChoreoEvent *event = channel->GetEvent( i ); + channel->RemoveEvent( event ); + + DeleteReferencedObjects( event ); + } + + DestroyChannel( channel ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *event - +//----------------------------------------------------------------------------- +void CChoreoScene::DeleteReferencedObjects( CChoreoEvent *event ) +{ + int idx = m_PauseEvents.Find( event ); + if ( idx != m_PauseEvents.InvalidIndex() ) + { + m_PauseEvents.Remove( idx ); + } + // Events don't reference anything lower + DestroyEvent( event ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *actor - +//----------------------------------------------------------------------------- +void CChoreoScene::DestroyActor( CChoreoActor *actor ) +{ + int size = m_Actors.Size(); + for ( int i = size - 1; i >= 0; i-- ) + { + CChoreoActor *a = m_Actors[ i ]; + if ( a == actor ) + { + m_Actors.Remove( i ); + } + } + + delete actor; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *channel - +//----------------------------------------------------------------------------- +void CChoreoScene::DestroyChannel( CChoreoChannel *channel ) +{ + int size = m_Channels.Size(); + for ( int i = size - 1; i >= 0; i-- ) + { + CChoreoChannel *c = m_Channels[ i ]; + if ( c == channel ) + { + m_Channels.Remove( i ); + } + } + + delete channel; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *event - +//----------------------------------------------------------------------------- +void CChoreoScene::DestroyEvent( CChoreoEvent *event ) +{ + int size = m_Events.Size(); + for ( int i = size - 1; i >= 0; i-- ) + { + CChoreoEvent *e = m_Events[ i ]; + if ( e == event ) + { + m_Events.Remove( i ); + } + } + + delete event; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CChoreoScene::ResumeSimulation( void ) +{ + // If the thing that paused us was a SECTION pause event, then this will be set + if ( m_nLastPauseEvent >= 0 && + m_nLastPauseEvent < m_PauseEvents.Count() ) + { + // Start any suppressed dependencies immediately, should only be .wav files!!! + // These are .wav files which are placed at or just after the SECTION pause event + // in the .vcd, but due to the user's sound system latency, they would have triggered before the + // pause (we pre-queue sounds). Since we suppressed that, we need to unsupress / start these sounds + // now that the SECTION pause is being resumed from + CUtlVector< CChoreoEvent * > deps; + CChoreoEvent *pauseEvent = m_PauseEvents[ m_nLastPauseEvent ]; + Assert( pauseEvent ); + + // Sanity check ( this should be about 1 tick usually 15 msec) + float timeSincePaused = m_flCurrentTime - pauseEvent->GetStartTime(); + if ( fabs( timeSincePaused ) > 1.0f ) + { + Assert( !"Resume simulation with unexpected pause event" ); + } + + // Snag any sounds which were suppressed by this issue + pauseEvent->GetEventDependencies( deps ); + for ( int j = 0; j < deps.Count(); ++j ) + { + CChoreoEvent *startEvent = deps[ j ]; + Assert( startEvent ); + // Start them now. Yes, they won't pre-queue, but it's better than totally skipping the sound!!! + startEvent->StartProcessing( m_pIChoreoEventCallback, this, m_flCurrentTime ); + } + } + + // Reset section pause signal + m_nLastPauseEvent = -1; + + m_ActiveResumeConditions.RemoveAll(); +} + +// Sound system needs to have sounds pre-queued by this much time +void CChoreoScene::SetSoundFileStartupLatency( float time ) +{ + Assert( time >= 0 ); + m_flSoundSystemLatency = time; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : start - +// end - +//----------------------------------------------------------------------------- +void CChoreoScene::GetSceneTimes( float& start, float& end ) +{ + start = m_flStartTime; + end = m_flEndTime; +} + +//----------------------------------------------------------------------------- +// Purpose: Do housekeeping on times that are relative to tags +//----------------------------------------------------------------------------- +void CChoreoScene::ReconcileTags( void ) +{ + for ( int i = 0 ; i < m_Actors.Size(); i++ ) + { + CChoreoActor *a = m_Actors[ i ]; + if ( !a ) + continue; + + for ( int j = 0; j < a->GetNumChannels(); j++ ) + { + CChoreoChannel *c = a->GetChannel( j ); + if ( !c ) + continue; + + for ( int k = 0 ; k < c->GetNumEvents(); k++ ) + { + CChoreoEvent *e = c->GetEvent( k ); + if ( !e ) + continue; + + if ( !e->IsUsingRelativeTag() ) + continue; + + CEventRelativeTag *tag = FindTagByName( + e->GetRelativeWavName(), + e->GetRelativeTagName() ); + + if ( tag ) + { + // Determine correct starting time based on tag + float starttime = tag->GetStartTime(); + + // Figure out delta + float dt = starttime - e->GetStartTime(); + + // Fix up start and possible end time + e->OffsetTime( dt ); + } + else + { + // The tag was missing!!! unflag it + choreoprintf( 0, "Event %s was missing tag %s for wav %s\n", + e->GetName(), e->GetRelativeWavName(), e->GetRelativeTagName() ); + + e->SetUsingRelativeTag( false, "", "" ); + } + } + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *wavname - +// *name - +// Output : CChoreoEvent +//----------------------------------------------------------------------------- +CChoreoEvent *CChoreoScene::FindTargetingEvent( const char *wavname, const char *name ) +{ + for ( int i = 0 ; i < m_Actors.Size(); i++ ) + { + CChoreoActor *a = m_Actors[ i ]; + if ( !a ) + continue; + + for ( int j = 0; j < a->GetNumChannels(); j++ ) + { + CChoreoChannel *c = a->GetChannel( j ); + if ( !c ) + continue; + + for ( int k = 0 ; k < c->GetNumEvents(); k++ ) + { + CChoreoEvent *e = c->GetEvent( k ); + if ( !e ) + continue; + + if ( !e->IsUsingRelativeTag() ) + continue; + + if ( stricmp( wavname, e->GetRelativeWavName() ) ) + continue; + + if ( stricmp( name, e->GetRelativeTagName() ) ) + continue; + + return e; + } + } + } + return NULL; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *wavname - +// *name - +// Output : CEventRelativeTag +//----------------------------------------------------------------------------- +CEventRelativeTag *CChoreoScene::FindTagByName( const char *wavname, const char *name ) +{ + for ( int i = 0 ; i < m_Actors.Size(); i++ ) + { + CChoreoActor *a = m_Actors[ i ]; + if ( !a ) + continue; + + for ( int j = 0; j < a->GetNumChannels(); j++ ) + { + CChoreoChannel *c = a->GetChannel( j ); + if ( !c ) + continue; + + for ( int k = 0 ; k < c->GetNumEvents(); k++ ) + { + CChoreoEvent *e = c->GetEvent( k ); + if ( !e ) + continue; + + if ( e->GetType() != CChoreoEvent::SPEAK ) + continue; + + // Search for tag by name + if ( !strstr( e->GetParameters(), wavname ) ) + continue; + + CEventRelativeTag *tag = e->FindRelativeTag( name ); + if ( !tag ) + continue; + + return tag; + } + } + } + return NULL; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *filename - +//----------------------------------------------------------------------------- +void CChoreoScene::ExportEvents( const char *filename, CUtlVector< CChoreoEvent * >& events ) +{ + if ( events.Size() <= 0 ) + return; + + // Create a serialization buffer + CUtlBuffer buf( 0, 0, true ); + FilePrintf( buf, 0, "// Choreo version 1: <%i> Exported Events\n", events.Size() ); + + // Save out the selected events. + int i; + for ( i = 0 ; i < events.Size(); i++ ) + { + CChoreoEvent *e = events[ i ]; + if ( !e->GetActor() ) + continue; + + FileSaveEvent( buf, 0, e ); + } + + // Write it out baby + FileHandle_t fh = SceneFileSystem()->Open( filename, "wt" ); + if (fh) + { + SceneFileSystem()->Write( buf.Base(), buf.TellPut(), fh ); + SceneFileSystem()->Close(fh); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *actor - +// *channel - +// starttime - +//----------------------------------------------------------------------------- +void CChoreoScene::ImportEvents( ISceneTokenProcessor *tokenizer, CChoreoActor *actor, CChoreoChannel *channel ) +{ + m_pTokenizer = tokenizer; + + while ( 1 ) + { + if ( !m_pTokenizer->GetToken( true ) ) + { + break; + } + + if ( strlen( m_pTokenizer->CurrentToken() ) <= 0 ) + break; + + if ( !Q_stricmp( m_pTokenizer->CurrentToken(), "event" ) ) + { + ParseEvent( actor, channel ); + } + else + { + m_pTokenizer->Error( "unexpected token %s\n", m_pTokenizer->CurrentToken() ); + break; + } + } + + // Fixup time tags + ReconcileTags(); +} + +void CChoreoScene::SetSubScene( bool sub ) +{ + m_bSubScene = sub; +} + +bool CChoreoScene::IsSubScene( void ) const +{ + return m_bSubScene; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : int +//----------------------------------------------------------------------------- +int CChoreoScene::GetSceneFPS( void ) const +{ + return m_nSceneFPS; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : fps - +//----------------------------------------------------------------------------- +void CChoreoScene::SetSceneFPS( int fps ) +{ + m_nSceneFPS = fps; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CChoreoScene::IsUsingFrameSnap( void ) const +{ + return m_bUseFrameSnap; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : snap - +//----------------------------------------------------------------------------- +void CChoreoScene::SetUsingFrameSnap( bool snap ) +{ + m_bUseFrameSnap = snap; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : t - +// Output : float +//----------------------------------------------------------------------------- +float CChoreoScene::SnapTime( float t ) +{ + if ( !IsUsingFrameSnap() ) + return t; + + float fps = (float)GetSceneFPS(); + Assert( fps > 0 ); + + int itime = (int)( t * fps + 0.5f ); + + t = (float)itime / fps; + + // FIXME: If FPS is set and "using grid", snap to proper fractional time value + return t; +} + +EdgeInfo_t *CChoreoScene::GetSceneRampEdgeInfo( int idx ) +{ + return &m_SceneRampEdgeInfo[ idx ]; +} + +float CChoreoScene::GetSceneRampIntensity( float time ) +{ + return CChoreoEvent::GetRampIntensity( this, time ); +} + +int CChoreoScene::GetSceneRampCount( void ) +{ + return m_SceneRamp.Count(); +} + +CExpressionSample *CChoreoScene::GetSceneRamp( int index ) +{ + if ( index < 0 || index >= GetSceneRampCount() ) + return NULL; + + return &m_SceneRamp[ index ]; +} + +CExpressionSample *CChoreoScene::AddSceneRamp( float time, float value, bool selected ) +{ + CExpressionSample sample; + + sample.time = time; + sample.value = value; + sample.selected = selected; + + int idx = m_SceneRamp.AddToTail( sample ); + return &m_SceneRamp[ idx ]; +} + +void CChoreoScene::DeleteSceneRamp( int index ) +{ + if ( index < 0 || index >= GetSceneRampCount() ) + return; + + m_SceneRamp.Remove( index ); +} + +void CChoreoScene::ClearSceneRamp( void ) +{ + m_SceneRamp.RemoveAll(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CChoreoScene::ResortSceneRamp( void ) +{ + for ( int i = 0; i < m_SceneRamp.Size(); i++ ) + { + for ( int j = i + 1; j < m_SceneRamp.Size(); j++ ) + { + CExpressionSample src = m_SceneRamp[ i ]; + CExpressionSample dest = m_SceneRamp[ j ]; + + if ( src.time > dest.time ) + { + m_SceneRamp[ i ] = dest; + m_SceneRamp[ j ] = src; + } + } + } + + RemoveOutOfRangeSceneRampSamples(); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : number - +// Output : CExpressionSample +//----------------------------------------------------------------------------- +CExpressionSample *CChoreoScene::GetBoundedSceneRamp( int number, bool& bClamped ) +{ + if ( number < 0 ) + { + // Search for two samples which span time f + static CExpressionSample nullstart; + nullstart.time = 0.0f; + nullstart.value = SceneRampGetEdgeZeroValue( true ); + nullstart.SetCurveType( SceneRampGetEdgeCurveType( true ) ); + bClamped = true; + return &nullstart; + } + else if ( number >= GetSceneRampCount() ) + { + static CExpressionSample nullend; + nullend.time = FindStopTime(); + nullend.value = SceneRampGetEdgeZeroValue( false ); + nullend.SetCurveType( SceneRampGetEdgeCurveType( false ) ); + bClamped = true; + return &nullend; + } + + bClamped = false; + return GetSceneRamp( number ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CChoreoScene::RemoveOutOfRangeSceneRampSamples( void ) +{ + float duration = FindStopTime(); + + int c = GetSceneRampCount(); + for ( int i = c-1; i >= 0; i-- ) + { + CExpressionSample src = m_SceneRamp[ i ]; + if ( src.time < 0 || + src.time > duration + 0.01 ) + { + m_SceneRamp.Remove( i ); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CChoreoScene::ReconcileGestureTimes() +{ + for ( int i = 0 ; i < m_Actors.Size(); i++ ) + { + CChoreoActor *a = m_Actors[ i ]; + if ( !a ) + continue; + + for ( int j = 0; j < a->GetNumChannels(); j++ ) + { + CChoreoChannel *c = a->GetChannel( j ); + if ( !c ) + continue; + + c->ReconcileGestureTimes(); + } + } +} + +int CChoreoScene::TimeZoomFirst() +{ + return m_TimeZoomLookup.First(); +} + +int CChoreoScene::TimeZoomNext( int i ) +{ + return m_TimeZoomLookup.Next( i ); +} +int CChoreoScene::TimeZoomInvalid() const +{ + return m_TimeZoomLookup.InvalidIndex(); +} +char const *CChoreoScene::TimeZoomName( int i ) +{ + return m_TimeZoomLookup.GetElementName( i ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *tool - +// Output : int +//----------------------------------------------------------------------------- +int CChoreoScene::GetTimeZoom( char const *tool ) +{ + // If not present add it + int idx = m_TimeZoomLookup.Find( tool ); + if ( idx == m_TimeZoomLookup.InvalidIndex() ) + { + idx = m_TimeZoomLookup.Insert( tool, 100 ); + } + + return m_TimeZoomLookup[ idx ]; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *tool - +// tz - +//----------------------------------------------------------------------------- +void CChoreoScene::SetTimeZoom( char const *tool, int tz ) +{ + // If not present add it + int idx = m_TimeZoomLookup.Find( tool ); + if ( idx == m_TimeZoomLookup.InvalidIndex() ) + { + idx = m_TimeZoomLookup.Insert( tool, 100 ); + } + + m_TimeZoomLookup[ idx ] = tz; +} + +void CChoreoScene::ParseScaleSettings( ISceneTokenProcessor *tokenizer, CChoreoScene *scene ) +{ + tokenizer->GetToken( true ); + + if ( stricmp( tokenizer->CurrentToken(), "{" ) ) + tokenizer->Error( "expecting {\n" ); + + while ( 1 ) + { + // Parse until } + tokenizer->GetToken( true ); + + if ( strlen( tokenizer->CurrentToken() ) <= 0 ) + { + tokenizer->Error( "expecting scalesettings data\n" ); + break; + } + + if ( !Q_stricmp( tokenizer->CurrentToken(), "}" ) ) + break; + + char tool[ 256 ]; + Q_strncpy( tool, tokenizer->CurrentToken(), sizeof( tool ) ); + + tokenizer->GetToken( false ); + + int tz = Q_atoi( tokenizer->CurrentToken() ); + if ( tz <= 0 ) + tz = 100; + + scene->SetTimeZoom( tool, tz ); + } +} + +// Merges two .vcd's together +bool CChoreoScene::Merge( CChoreoScene *other ) +{ + int acount = 0; + int ccount = 0; + int ecount = 0; + + // Look for events that don't have actor/channel set + int i; + for ( i = 0 ; i < other->m_Events.Size(); i++ ) + { + CChoreoEvent *e = other->m_Events[ i ]; + if ( e->GetActor() ) + continue; + + MEM_ALLOC_CREDIT(); + // Make a copy of the other event and add it to this scene + CChoreoEvent *newEvent = AllocEvent(); + *newEvent = *e; + newEvent->SetScene( this ); + ecount++; + } + + for ( i = 0 ; i < other->m_Actors.Size(); i++ ) + { + CChoreoActor *a = other->m_Actors[ i ]; + + // See if that actor already exists + bool newActor = false; + CChoreoActor *destActor = FindActor( a->GetName() ); + if ( !destActor ) + { + newActor = true; + destActor = AllocActor(); + *destActor = *a; + destActor->RemoveAllChannels(); + acount++; + } + + // Now we have a destination actor, work on channels + for ( int j = 0; j < a->GetNumChannels(); j++ ) + { + CChoreoChannel *ch = a->GetChannel( j ); + + bool newChannel = false; + CChoreoChannel *destChannel = NULL; + destChannel = destActor->FindChannel( ch->GetName() ); + if ( !destChannel ) + { + destChannel = AllocChannel(); + *destChannel = *ch; + destChannel->RemoveAllEvents(); + newChannel = true; + ccount++; + } + + if ( newChannel ) + { + destActor->AddChannel( destChannel ); + destChannel->SetActor( destActor ); + } + + // Now we have a destination channel, work on events themselves + for ( int k = 0 ; k < ch->GetNumEvents(); k++ ) + { + CChoreoEvent *e = ch->GetEvent( k ); + + // Just import them wholesale, no checking + MEM_ALLOC_CREDIT(); + CChoreoEvent *newEvent = AllocEvent(); + *newEvent = *e; + newEvent->SetScene( this ); + + destChannel->AddEvent( newEvent ); + + newEvent->SetChannel( destChannel ); + newEvent->SetActor( destActor ); + + ecount++; + } + } + } + + Msg( "Merged in (%i) actors, (%i) channels, and (%i) events\n", + acount, ccount, ecount ); + + return ( ecount || acount || ccount ); +} + +//----------------------------------------------------------------------------- +// Purpose: Updates master/slave status info per channel +//----------------------------------------------------------------------------- +void CChoreoScene::ReconcileCloseCaption() +{ + for ( int i = 0 ; i < m_Actors.Size(); i++ ) + { + CChoreoActor *a = m_Actors[ i ]; + if ( !a ) + continue; + + for ( int j = 0; j < a->GetNumChannels(); j++ ) + { + CChoreoChannel *c = a->GetChannel( j ); + if ( !c ) + continue; + + c->ReconcileCloseCaption(); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : char const +//----------------------------------------------------------------------------- +char const *CChoreoScene::GetFilename() const +{ + return m_szFileName; +} + + +void CChoreoScene::SetFileName( char const *fn ) +{ + Q_strncpy( m_szFileName, fn, sizeof( m_szFileName ) ); +} + +//----------------------------------------------------------------------------- +// Purpose: Returns true if this scene has speech events that haven't played yet +//----------------------------------------------------------------------------- +bool CChoreoScene::HasUnplayedSpeech() +{ + for ( int i = 0; i < m_Events.Size(); i++ ) + { + CChoreoEvent *e = m_Events[ i ]; + if ( e->GetType() == CChoreoEvent::SPEAK ) + { + // Have we played it yet? + if ( m_flCurrentTime < e->GetStartTime() ) + return true; + } + } + + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: Returns true if this scene has flex animation events that are playing +//----------------------------------------------------------------------------- +bool CChoreoScene::HasFlexAnimation() +{ + for ( int i = 0; i < m_Events.Size(); i++ ) + { + CChoreoEvent *e = m_Events[ i ]; + if ( e->GetType() == CChoreoEvent::FLEXANIMATION ) + { + // Have we played it yet? + if ( m_flCurrentTime >= e->GetStartTime() && m_flCurrentTime <= e->GetEndTime() ) + return true; + } + } + + return false; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CChoreoScene::SetBackground( bool bIsBackground ) +{ + m_bIsBackground = bIsBackground; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CChoreoScene::IsBackground( ) +{ + return m_bIsBackground; +} + +bool CChoreoScene::HasEventsOfType( CChoreoEvent::EVENTTYPE type ) const +{ + return m_bitvecHasEventOfType.IsBitSet( type ); +} + +void CChoreoScene::SceneRampSetEdgeInfo( bool leftEdge, int curveType, float zero ) +{ + int idx = leftEdge ? 0 : 1; + m_SceneRampEdgeInfo[ idx ].m_CurveType = curveType; + m_SceneRampEdgeInfo[ idx ].m_flZeroPos = zero; +} + +void CChoreoScene::SceneRampGetEdgeInfo( bool leftEdge, int& curveType, float& zero ) const +{ + int idx = leftEdge ? 0 : 1; + curveType = m_SceneRampEdgeInfo[ idx ].m_CurveType; + zero = m_SceneRampEdgeInfo[ idx ].m_flZeroPos; +} + +void CChoreoScene::SceneRampSetEdgeActive( bool leftEdge, bool state ) +{ + int idx = leftEdge ? 0 : 1; + m_SceneRampEdgeInfo[ idx ].m_bActive = state; +} + +bool CChoreoScene::SceneRampIsEdgeActive( bool leftEdge ) const +{ + int idx = leftEdge ? 0 : 1; + return m_SceneRampEdgeInfo[ idx ].m_bActive; +} + +int CChoreoScene::SceneRampGetEdgeCurveType( bool leftEdge ) const +{ + if ( !SceneRampIsEdgeActive( leftEdge ) ) + { + return CURVE_DEFAULT; + } + + int idx = leftEdge ? 0 : 1; + return m_SceneRampEdgeInfo[ idx ].m_CurveType; +} + +float CChoreoScene::SceneRampGetEdgeZeroValue( bool leftEdge ) const +{ + if ( !SceneRampIsEdgeActive( leftEdge ) ) + { + return 0.0f; + } + + int idx = leftEdge ? 0 : 1; + return m_SceneRampEdgeInfo[ idx ].m_flZeroPos; +} + +// ICurveDataAccessor method +bool CChoreoScene::CurveHasEndTime() +{ + return true; +} + +// ICurveDataAccessor method +int CChoreoScene::CurveGetSampleCount() +{ + return GetSceneRampCount(); +} + +// ICurveDataAccessor method +CExpressionSample *CChoreoScene::CurveGetBoundedSample( int idx, bool& bClamped ) +{ + return GetBoundedSceneRamp( idx, bClamped ); +} + +int CChoreoScene::GetDefaultCurveType() +{ + return CURVE_CATMULL_ROM_TO_CATMULL_ROM; +} + +#define SCENE_BINARY_VERSION 0x01 +#define SCENE_TAG MAKEID( 'x', 'v', 'c', 'd' ) + +bool CChoreoScene::SaveBinary( char const *pszBinaryFileName, char const *pPathID, unsigned int nTextVersionCRC ) +{ + bool bret = false; + CUtlBuffer buf; + + SaveToBuffer( buf, nTextVersionCRC ); + + if ( SceneFileSystem()->FileExists( pszBinaryFileName, pPathID ) && + !SceneFileSystem()->IsFileWritable( pszBinaryFileName, pPathID ) ) + { + Warning( "Forcing '%s' to be writable!!!\n", pszBinaryFileName ); + SceneFileSystem()->SetFileWritable( pszBinaryFileName, true, pPathID ); + } + + FileHandle_t fh = SceneFileSystem()->Open( pszBinaryFileName, "wb", pPathID ); + if ( FILESYSTEM_INVALID_HANDLE != fh ) + { + SceneFileSystem()->Write( buf.Base(), buf.TellPut(), fh ); + + // pad to 512 byte sector size + int align = (buf.TellPut() + 511) & ~511; + int padLength = align - buf.TellPut(); + char padByte = 0; + for ( int i=0; iWrite( &padByte, 1, fh ); + } + + SceneFileSystem()->Close( fh ); + + // Success + bret = true; + } + else + { + Warning( "Unable to open '%s' for writing!!!\n", pszBinaryFileName ); + } + + return bret; +} + +void CChoreoScene::SaveToBuffer( CUtlBuffer& buf, unsigned int nTextVersionCRC ) +{ + buf.PutInt( SCENE_TAG ); + buf.PutChar( SCENE_BINARY_VERSION ); + buf.PutInt( nTextVersionCRC ); + + CUtlVector< CChoreoEvent * > eventList; + + // Look for events that don't have actor/channel set + int i; + for ( i = 0 ; i < m_Events.Size(); i++ ) + { + CChoreoEvent *e = m_Events[ i ]; + if ( e->GetActor() ) + continue; + + eventList.AddToTail( e ); + } + + int c = eventList.Count(); + buf.PutShort( c ); + for ( i = 0; i < c; ++i ) + { + CChoreoEvent *e = eventList[ i ]; + e->SaveToBuffer( buf, this ); + } + + // Now serialize the actors themselves + CUtlVector< CChoreoActor * > actorList; + for ( i = 0 ; i < m_Actors.Size(); i++ ) + { + CChoreoActor *a = m_Actors[ i ]; + if ( !a ) + continue; + + actorList.AddToTail( a ); + } + + c = actorList.Count(); + buf.PutShort( c ); + for ( i = 0; i < c; ++i ) + { + CChoreoActor *a = actorList[ i ]; + a->SaveToBuffer( buf, this ); + } + + /* + // compiled version strips out map name, only used by editor + if ( m_szMapname[ 0 ] ) + { + FilePrintf( buf, 0, "mapname \"%s\"\n", m_szMapname ); + } + */ + + SaveSceneRampToBuffer( buf ); + + /* + // compiled version strips out scale settings fps and snap, only used by editor + FileSaveScaleSettings( buf, 0, this ); + FilePrintf( buf, 0, "fps %i\n", m_nSceneFPS ); + FilePrintf( buf, 0, "snap %s\n", m_bUseFrameSnap ? "on" : "off" ); + */ +} + +//----------------------------------------------------------------------------- +// Purpose: Static method to extract just the CRC from a binary .xcd file +// Input : buf - +// crc - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CChoreoScene::GetCRCFromBuffer( CUtlBuffer& buf, unsigned int& crc ) +{ + bool bret = false; + + int pos = buf.TellGet(); + + int tag = buf.GetInt(); + if ( tag == SCENE_TAG ) + { + byte ver = buf.GetChar(); + if ( ver == SCENE_BINARY_VERSION ) + { + bret = true; + crc = (unsigned int)buf.GetInt(); + } + } + + buf.SeekGet( CUtlBuffer::SEEK_HEAD, pos ); + + return bret; +} + +bool CChoreoScene::RestoreFromBuffer( CUtlBuffer& buf, char const *filename ) +{ + Q_strncpy( m_szFileName, filename, sizeof( m_szFileName ) ); + + int tag = buf.GetInt(); + if ( tag != SCENE_TAG ) + return false; + + byte ver = buf.GetChar(); + if ( ver != SCENE_BINARY_VERSION ) + return false; + + // Skip the CRC + buf.GetInt(); + + int i; + int eventCount = buf.GetShort(); + for ( i = 0; i < eventCount; ++i ) + { + MEM_ALLOC_CREDIT(); + CChoreoEvent *e = AllocEvent(); + Assert( e ); + + if ( e->RestoreFromBuffer( buf, this ) ) + { + continue; + } + + return false; + } + + int actorCount = buf.GetShort(); + for ( i = 0; i < actorCount; ++i ) + { + CChoreoActor *a = AllocActor(); + Assert( a ); + if ( a->RestoreFromBuffer( buf, this ) ) + { + continue; + } + + return false; + } + + if ( !ParseSceneRampFromBuffer( buf ) ) + { + return false; + } + +// FIXME: Are these ever needed on restore? +// ReconcileTags(); +// ReconcileGestureTimes(); + + ReconcileCloseCaption(); + + if ( CChoreoScene::s_bEditingDisabled ) + { + m_flPrecomputedStopTime = FindStopTime(); + } + + return true; +} + +void CChoreoScene::SaveSceneRampToBuffer( CUtlBuffer& buf ) +{ + // Nothing to save? + int c = GetSceneRampCount(); + buf.PutInt( c ); + if ( c <= 0 ) + return; + + for ( int i = 0; i < c; i++ ) + { + CExpressionSample *sample = GetSceneRamp( i ); + buf.PutFloat( sample->time ); + buf.PutFloat( sample->value ); + } +} + +bool CChoreoScene::ParseSceneRampFromBuffer( CUtlBuffer& buf ) +{ + int c = buf.GetInt(); + if ( c <= 0 ) + return true; + + for ( int i = 0; i < c; i++ ) + { + float t = buf.GetFloat(); + float v = buf.GetFloat(); + + AddSceneRamp( t, v, false ); + } + + return true; +} + diff --git a/cl_dll/client_hl2-2003.vcproj b/cl_dll/client_hl2-2003.vcproj index f0686a4b..42c31ff2 100644 --- a/cl_dll/client_hl2-2003.vcproj +++ b/cl_dll/client_hl2-2003.vcproj @@ -55,7 +55,7 @@ copy "$(TargetDir)"client.dll "..\..\game\hl2\bin" LinkIncremental="2" SuppressStartupBanner="TRUE" AdditionalLibraryDirectories="..\lib-vc7\public" - IgnoreDefaultLibraryNames="LIBC,LIBCD" + IgnoreDefaultLibraryNames="LIBC,LIBCD,LIBCMTD" GenerateDebugInformation="TRUE" ProgramDatabaseFile="$(IntDir)/client.pdb" GenerateMapFile="TRUE" @@ -147,7 +147,7 @@ if exist "$(TargetDir)"client.pdb copy "$(TargetDir)"client. LinkIncremental="2" SuppressStartupBanner="TRUE" AdditionalLibraryDirectories="..\lib-vc7\public" - IgnoreDefaultLibraryNames="LIBCMTD,LIBCD,LIBC" + IgnoreDefaultLibraryNames="LIBC,LIBCD,LIBCMTD" GenerateDebugInformation="TRUE" ProgramDatabaseFile="$(IntDir)/client.pdb" GenerateMapFile="TRUE" @@ -507,66 +507,6 @@ if exist "$(TargetDir)"client.pdb copy "$(TargetDir)"client. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -2721,10 +2661,10 @@ if exist "$(TargetDir)"client.pdb copy "$(TargetDir)"client. RelativePath="..\public\vgui\IImage.h"> + RelativePath="iinput.h"> + RelativePath="..\public\vgui\IInput.h"> @@ -2964,10 +2904,10 @@ if exist "$(TargetDir)"client.pdb copy "$(TargetDir)"client. RelativePath="..\public\tier1\mempool.h"> + RelativePath="..\public\vgui_controls\Menu.h"> + RelativePath="menu.h"> @@ -3531,27 +3471,34 @@ if exist "$(TargetDir)"client.pdb copy "$(TargetDir)"client. - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + diff --git a/cl_dll/client_hl2-2005.vcproj b/cl_dll/client_hl2-2005.vcproj index 58bd306c..6eecabd0 100644 --- a/cl_dll/client_hl2-2005.vcproj +++ b/cl_dll/client_hl2-2005.vcproj @@ -92,7 +92,7 @@ SuppressStartupBanner="true" AdditionalLibraryDirectories="..\lib\public" IgnoreAllDefaultLibraries="false" - IgnoreDefaultLibraryNames="LIBC,LIBCD" + IgnoreDefaultLibraryNames="LIBC,LIBCD,LIBCMT" GenerateDebugInformation="true" ProgramDatabaseFile="$(IntDir)/client.pdb" GenerateMapFile="true" @@ -208,7 +208,7 @@ LinkIncremental="1" SuppressStartupBanner="true" AdditionalLibraryDirectories="..\lib\public" - IgnoreDefaultLibraryNames="LIBC,LIBCD" + IgnoreDefaultLibraryNames="LIBC,LIBCD,LIBCMTD" GenerateDebugInformation="true" ProgramDatabaseFile="$(IntDir)/client.pdb" GenerateMapFile="true" @@ -670,86 +670,6 @@ RelativePath="cdll_util.cpp" > - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -4684,34 +4604,42 @@ - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + diff --git a/cl_dll/client_hl2mp-2003.vcproj b/cl_dll/client_hl2mp-2003.vcproj index 6da63cbc..f8f4da3f 100644 --- a/cl_dll/client_hl2mp-2003.vcproj +++ b/cl_dll/client_hl2mp-2003.vcproj @@ -26,7 +26,7 @@ AdditionalIncludeDirectories=".\hl2mp\UI,.\hl2mp,..\game_shared\hl2mp,.\hl2_hud,.\hl2_hud\elements,../game_shared/hl2,.,.\..\Public,.\..\Public\tier1,.\..\vgui2\include,.\..\vgui2\controls,../game_shared,.\game_controls" PreprocessorDefinitions="HL2_EPISODIC;HL2MP;HL2_CLIENT_DLL;_DEBUG;fopen=dont_use_fopen;NO_STRING_T;CLIENT_DLL;_WINDOWS;VECTOR;strncpy=use_Q_strncpy_instead;_snprintf=use_Q_snprintf_instead;_WIN32;PROTECTED_THINGS_ENABLE" ExceptionHandling="FALSE" - RuntimeLibrary="5" + RuntimeLibrary="1" ForceConformanceInForLoopScope="TRUE" RuntimeTypeInfo="TRUE" UsePrecompiledHeader="3" @@ -55,7 +55,7 @@ copy "$(TargetDir)"client.dll "..\..\game\hl2mp\bin" LinkIncremental="2" SuppressStartupBanner="TRUE" AdditionalLibraryDirectories="..\lib-vc7\public" - IgnoreDefaultLibraryNames="LIBCMTD,LIBC,LIBCMT" + IgnoreDefaultLibraryNames="LIBC,LIBCD,LIBCMT" GenerateDebugInformation="TRUE" ProgramDatabaseFile="$(IntDir)/client.pdb" GenerateMapFile="TRUE" @@ -114,7 +114,7 @@ copy "$(TargetDir)"client.dll "..\..\game\hl2mp\bin" PreprocessorDefinitions="HL2_EPISODIC;HL2MP;HL2_CLIENT_DLL;NDEBUG;NO_STRING_T;CLIENT_DLL;_WINDOWS;VECTOR;strncpy=use_Q_strncpy_instead;_snprintf=use_Q_snprintf_instead;_WIN32;PROTECTED_THINGS_ENABLE" StringPooling="TRUE" ExceptionHandling="FALSE" - RuntimeLibrary="4" + RuntimeLibrary="0" BufferSecurityCheck="FALSE" EnableFunctionLevelLinking="TRUE" ForceConformanceInForLoopScope="TRUE" @@ -147,7 +147,7 @@ if exist "$(TargetDir)"client.pdb copy "$(TargetDir)"client. LinkIncremental="2" SuppressStartupBanner="TRUE" AdditionalLibraryDirectories="..\lib-vc7\public" - IgnoreDefaultLibraryNames="libcd.lib,LIBCD,LIBCMT,libcmtd" + IgnoreDefaultLibraryNames="LIBC,LIBCD,LIBCMTD" GenerateDebugInformation="TRUE" ProgramDatabaseFile="$(IntDir)/client.pdb" GenerateMapFile="TRUE" @@ -507,66 +507,6 @@ if exist "$(TargetDir)"client.pdb copy "$(TargetDir)"client. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -2144,16 +2084,6 @@ if exist "$(TargetDir)"client.pdb copy "$(TargetDir)"client. - - - - - - @@ -2169,80 +2099,30 @@ if exist "$(TargetDir)"client.pdb copy "$(TargetDir)"client. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -2252,47 +2132,15 @@ if exist "$(TargetDir)"client.pdb copy "$(TargetDir)"client. - - - - - - - - - - - - - - - - - - @@ -2306,18 +2154,6 @@ if exist "$(TargetDir)"client.pdb copy "$(TargetDir)"client. Filter=""> - - - - - - @@ -2348,18 +2184,6 @@ if exist "$(TargetDir)"client.pdb copy "$(TargetDir)"client. - - - - - - @@ -2990,10 +2814,10 @@ if exist "$(TargetDir)"client.pdb copy "$(TargetDir)"client. RelativePath="..\public\vgui\IImage.h"> + RelativePath="..\public\vgui\IInput.h"> + RelativePath="iinput.h"> @@ -3236,10 +3060,10 @@ if exist "$(TargetDir)"client.pdb copy "$(TargetDir)"client. RelativePath="..\public\tier1\mempool.h"> + RelativePath="..\public\vgui_controls\Menu.h"> + RelativePath="menu.h"> @@ -3793,27 +3617,34 @@ if exist "$(TargetDir)"client.pdb copy "$(TargetDir)"client. - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + diff --git a/cl_dll/client_hl2mp-2005.vcproj b/cl_dll/client_hl2mp-2005.vcproj index b10ebd41..aec97236 100644 --- a/cl_dll/client_hl2mp-2005.vcproj +++ b/cl_dll/client_hl2mp-2005.vcproj @@ -203,7 +203,7 @@ LinkIncremental="1" SuppressStartupBanner="true" AdditionalLibraryDirectories="..\lib\public" - IgnoreDefaultLibraryNames="LIBC,LIBCD,LIBCMT" + IgnoreDefaultLibraryNames="LIBC,LIBCD,LIBCMTD" GenerateDebugInformation="true" ProgramDatabaseFile="$(IntDir)/client.pdb" GenerateMapFile="true" @@ -665,86 +665,6 @@ RelativePath="cdll_util.cpp" > - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -2836,20 +2756,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -3965,11 +3725,11 @@ > - - - - - - - - - + - - - - - - - - - - - + - - - + - - - + + + + + + + + + + diff --git a/cl_dll/client_scratch-2003.vcproj b/cl_dll/client_scratch-2003.vcproj index be3352e2..07231198 100644 --- a/cl_dll/client_scratch-2003.vcproj +++ b/cl_dll/client_scratch-2003.vcproj @@ -56,7 +56,7 @@ copy "$(TargetDir)"client.dll "..\..\game\sdksample\bin" LinkIncremental="2" SuppressStartupBanner="TRUE" AdditionalLibraryDirectories="..\lib-vc7\public" - IgnoreDefaultLibraryNames="LIBCMT,LIBC" + IgnoreDefaultLibraryNames="LIBC,LIBCD,LIBCMT" GenerateDebugInformation="TRUE" ProgramDatabaseFile="$(IntDir)/client.pdb" GenerateMapFile="TRUE" @@ -149,7 +149,7 @@ if exist "$(TargetDir)"client.pdb copy "$(TargetDir)"client. LinkIncremental="2" SuppressStartupBanner="TRUE" AdditionalLibraryDirectories="..\lib-vc7\public" - IgnoreDefaultLibraryNames="libcd.lib,LIBCD,LIBCMT,libcmtd" + IgnoreDefaultLibraryNames="LIBC,LIBCD,LIBCMTD" GenerateDebugInformation="TRUE" ProgramDatabaseFile="$(IntDir)/client.pdb" GenerateMapFile="TRUE" @@ -506,66 +506,6 @@ if exist "$(TargetDir)"client.pdb copy "$(TargetDir)"client. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -1859,79 +1799,27 @@ if exist "$(TargetDir)"client.pdb copy "$(TargetDir)"client. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -1941,16 +1829,6 @@ if exist "$(TargetDir)"client.pdb copy "$(TargetDir)"client. - - - - - - @@ -1963,16 +1841,6 @@ if exist "$(TargetDir)"client.pdb copy "$(TargetDir)"client. - - - - - - @@ -1982,16 +1850,6 @@ if exist "$(TargetDir)"client.pdb copy "$(TargetDir)"client. - - - - - - @@ -2013,32 +1871,12 @@ if exist "$(TargetDir)"client.pdb copy "$(TargetDir)"client. - - - - - - - - - - - - @@ -2048,80 +1886,30 @@ if exist "$(TargetDir)"client.pdb copy "$(TargetDir)"client. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -2131,16 +1919,6 @@ if exist "$(TargetDir)"client.pdb copy "$(TargetDir)"client. - - - - - - @@ -2820,10 +2598,10 @@ if exist "$(TargetDir)"client.pdb copy "$(TargetDir)"client. RelativePath="..\public\vgui\IImage.h"> + RelativePath="iinput.h"> + RelativePath="..\public\vgui\IInput.h"> @@ -3066,10 +2844,10 @@ if exist "$(TargetDir)"client.pdb copy "$(TargetDir)"client. RelativePath="..\public\tier1\mempool.h"> + RelativePath="menu.h"> + RelativePath="..\public\vgui_controls\Menu.h"> @@ -3635,27 +3413,34 @@ if exist "$(TargetDir)"client.pdb copy "$(TargetDir)"client. - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + diff --git a/cl_dll/client_scratch-2005.vcproj b/cl_dll/client_scratch-2005.vcproj index d556aa2f..f6f6bd78 100644 --- a/cl_dll/client_scratch-2005.vcproj +++ b/cl_dll/client_scratch-2005.vcproj @@ -89,7 +89,7 @@ LinkIncremental="2" SuppressStartupBanner="true" AdditionalLibraryDirectories="..\lib\public" - IgnoreDefaultLibraryNames="LIBC,LIBCD" + IgnoreDefaultLibraryNames="LIBC,LIBCD,LIBCMT" GenerateDebugInformation="true" ProgramDatabaseFile="$(IntDir)/client.pdb" GenerateMapFile="true" @@ -205,7 +205,7 @@ LinkIncremental="1" SuppressStartupBanner="true" AdditionalLibraryDirectories="..\lib\public" - IgnoreDefaultLibraryNames="LIBC,LIBCD" + IgnoreDefaultLibraryNames="LIBC,LIBCD,LIBCMTD" GenerateDebugInformation="true" ProgramDatabaseFile="$(IntDir)/client.pdb" GenerateMapFile="true" @@ -663,86 +663,6 @@ RelativePath="cdll_util.cpp" > - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -2502,7 +2422,7 @@ > @@ -4827,88 +4747,96 @@ - - - - - - - - - + - - - - - - - - - + - - - + - - - - - + + + + + + + - - - + - - - + + + + + + + + + + + + + + + + + + diff --git a/cl_dll/game_controls/SpectatorGUI.cpp b/cl_dll/game_controls/SpectatorGUI.cpp index 11e4e9c4..af9792c0 100644 --- a/cl_dll/game_controls/SpectatorGUI.cpp +++ b/cl_dll/game_controls/SpectatorGUI.cpp @@ -505,7 +505,7 @@ void CSpectatorGUI::Update() wchar_t playerText[ 80 ], playerName[ 64 ], health[ 10 ]; wcscpy( playerText, L"Unable to find #Spec_PlayerItem*" ); - memset( playerName, 0x0, sizeof( playerName ) * sizeof( wchar_t ) ); + memset( playerName, 0x0, sizeof( playerName ) ); localize()->ConvertANSIToUnicode( UTIL_SafeName(gr->GetPlayerName( playernum )), playerName, sizeof( playerName ) ); int iHealth = gr->GetHealth( playernum ); diff --git a/cl_dll/hl2_hud/hud_autoaim.cpp b/cl_dll/hl2_hud/hud_autoaim.cpp index 5f7f97ea..68a9e203 100644 --- a/cl_dll/hl2_hud/hud_autoaim.cpp +++ b/cl_dll/hl2_hud/hud_autoaim.cpp @@ -199,7 +199,7 @@ void CHUDAutoAim::OnThink() QAngle targetangles; QAngle delta; - float dist = (pLocalPlayer->m_HL2Local.m_vecAutoAimPoint - pLocalPlayer->GetAbsOrigin()).Length(); +// float dist = (pLocalPlayer->m_HL2Local.m_vecAutoAimPoint - pLocalPlayer->GetAbsOrigin()).Length(); engine->GetViewAngles( viewangles ); diff --git a/dlls/AI_ResponseSystem.cpp b/dlls/AI_ResponseSystem.cpp index 6b153dc8..67911a7f 100644 --- a/dlls/AI_ResponseSystem.cpp +++ b/dlls/AI_ResponseSystem.cpp @@ -1,3070 +1,3070 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -//=============================================================================// - - -#include "cbase.h" -#include "SoundEmitterSystem/isoundemittersystembase.h" -#include "AI_ResponseSystem.h" -#include "igamesystem.h" -#include "AI_Criteria.h" -#include -#include "filesystem.h" -#include "utldict.h" -#include "ai_speech.h" -#include "vstdlib/ICommandLine.h" -#include -#include "sceneentity.h" -#include "isaverestore.h" -#include "utlbuffer.h" -#include "stringpool.h" - -// memdbgon must be the last include file in a .cpp file!!! -#include "tier0/memdbgon.h" - -ConVar rr_debugresponses( "rr_debugresponses", "0", FCVAR_NONE, "Show verbose matching output (1 for simple, 2 for rule scoring). If set to 3, it will only show response success/failure for npc_selected NPCs." ); -ConVar rr_debugrule( "rr_debugrule", "", FCVAR_NONE, "If set to the name of the rule, that rule's score will be shown whenever a concept is passed into the response rules system."); -ConVar rr_dumpresponses( "rr_dumpresponses", "0", FCVAR_NONE, "Dump all response_rules.txt and rules (requires restart)" ); - -static CUtlSymbolTable g_RS; - -inline static char *CopyString( const char *in ) -{ - if ( !in ) - return NULL; - - int len = Q_strlen( in ); - char *out = new char[ len + 1 ]; - Q_memcpy( out, in, len ); - out[ len ] = 0; - return out; -} - -#pragma pack(1) -class Matcher -{ -public: - Matcher() - { - valid = false; - isnumeric = false; - notequal = false; - usemin = false; - minequals = false; - usemax = false; - maxequals = false; - maxval = 0.0f; - minval = 0.0f; - - token = UTL_INVAL_SYMBOL; - rawtoken = UTL_INVAL_SYMBOL; - } - - void Describe( void ) - { - if ( !valid ) - { - DevMsg( " invalid!\n" ); - return; - } - char sz[ 128 ]; - - sz[ 0] = 0; - int minmaxcount = 0; - if ( usemin ) - { - Q_snprintf( sz, sizeof( sz ), ">%s%.3f", minequals ? "=" : "", minval ); - minmaxcount++; - } - if ( usemax ) - { - char sz2[ 128 ]; - Q_snprintf( sz2, sizeof( sz2 ), "<%s%.3f", maxequals ? "=" : "", maxval ); - - if ( minmaxcount > 0 ) - { - Q_strncat( sz, " and ", sizeof( sz ), COPY_ALL_CHARACTERS ); - } - Q_strncat( sz, sz2, sizeof( sz ), COPY_ALL_CHARACTERS ); - minmaxcount++; - } - - if ( minmaxcount >= 1 ) - { - DevMsg( " matcher: %s\n", sz ); - return; - } - - if ( notequal ) - { - DevMsg( " matcher: !=%s\n", GetToken() ); - return; - } - - DevMsg( " matcher: ==%s\n", GetToken() ); - } - - float maxval; - float minval; - - bool valid : 1; //1 - bool isnumeric : 1; //2 - bool notequal : 1; //3 - bool usemin : 1; //4 - bool minequals : 1; //5 - bool usemax : 1; //6 - bool maxequals : 1; //7 - - void SetToken( char const *s ) - { - token = g_RS.AddString( s ); - } - - char const *GetToken() - { - if ( token.IsValid() ) - { - return g_RS.String( token ); - } - return ""; - } - void SetRaw( char const *raw ) - { - rawtoken = g_RS.AddString( raw ); - } - char const *GetRaw() - { - if ( rawtoken.IsValid() ) - { - return g_RS.String( rawtoken ); - } - return ""; - } - -private: - CUtlSymbol token; - CUtlSymbol rawtoken; -}; - -struct Response -{ - DECLARE_SIMPLE_DATADESC(); - - Response() - { - type = RESPONSE_NONE; - value = NULL; - weight.SetFloat( 1.0f ); - depletioncount = 0; - first = false; - last = false; - } - - Response( const Response& src ) - { - weight = src.weight; - type = src.type; - value = CopyString( src.value ); - depletioncount = src.depletioncount; - first = src.first; - last = src.last; - } - - Response& operator =( const Response& src ) - { - if ( this == &src ) - return *this; - weight = src.weight; - type = src.type; - value = CopyString( src.value ); - depletioncount = src.depletioncount; - first = src.first; - last = src.last; - return *this; - } - - ~Response() - { - delete[] value; - } - - ResponseType_t GetType() { return (ResponseType_t)type; } - - char *value; // fixed up value spot // 4 - float16 weight; // 6 - - byte depletioncount; // 7 - byte type : 6; // 8 - byte first : 1; // - byte last : 1; // -}; - -struct ResponseGroup -{ - DECLARE_SIMPLE_DATADESC(); - - ResponseGroup() - { - // By default visit all nodes before repeating - m_bSequential = false; - m_bNoRepeat = false; - m_bEnabled = true; - m_nCurrentIndex = 0; - m_bDepleteBeforeRepeat = true; - m_nDepletionCount = 1; - m_bHasFirst = false; - m_bHasLast = false; - } - - ResponseGroup( const ResponseGroup& src ) - { - int c = src.group.Count(); - for ( int i = 0; i < c; i++ ) - { - group.AddToTail( src.group[ i ] ); - } - - rp = src.rp; - m_bDepleteBeforeRepeat = src.m_bDepleteBeforeRepeat; - m_nDepletionCount = src.m_nDepletionCount; - m_bHasFirst = src.m_bHasFirst; - m_bHasLast = src.m_bHasLast; - m_bSequential = src.m_bSequential; - m_bNoRepeat = src.m_bNoRepeat; - m_bEnabled = src.m_bEnabled; - m_nCurrentIndex = src.m_nCurrentIndex; - } - - ResponseGroup& operator=( const ResponseGroup& src ) - { - if ( this == &src ) - return *this; - int c = src.group.Count(); - for ( int i = 0; i < c; i++ ) - { - group.AddToTail( src.group[ i ] ); - } - - rp = src.rp; - m_bDepleteBeforeRepeat = src.m_bDepleteBeforeRepeat; - m_nDepletionCount = src.m_nDepletionCount; - m_bHasFirst = src.m_bHasFirst; - m_bHasLast = src.m_bHasLast; - m_bSequential = src.m_bSequential; - m_bNoRepeat = src.m_bNoRepeat; - m_bEnabled = src.m_bEnabled; - m_nCurrentIndex = src.m_nCurrentIndex; - return *this; - } - - bool HasUndepletedChoices() const - { - if ( !m_bDepleteBeforeRepeat ) - return true; - - int c = group.Count(); - for ( int i = 0; i < c; i++ ) - { - if ( group[ i ].depletioncount != m_nDepletionCount ) - return true; - } - - return false; - } - - void MarkResponseUsed( int idx ) - { - if ( !m_bDepleteBeforeRepeat ) - return; - - if ( idx < 0 || idx >= group.Count() ) - { - Assert( 0 ); - return; - } - - group[ idx ].depletioncount = m_nDepletionCount; - } - - void ResetDepletionCount() - { - if ( !m_bDepleteBeforeRepeat ) - return; - ++m_nDepletionCount; - } - - void Reset() - { - ResetDepletionCount(); - SetEnabled( true ); - SetCurrentIndex( 0 ); - m_nDepletionCount = 1; - - for ( int i = 0; i < group.Count(); ++i ) - { - group[ i ].depletioncount = 0; - } - } - - bool HasUndepletedFirst( int& index ) - { - index = -1; - - if ( !m_bDepleteBeforeRepeat ) - return false; - - int c = group.Count(); - for ( int i = 0; i < c; i++ ) - { - Response *r = &group[ i ]; - - if ( ( r->depletioncount != m_nDepletionCount ) && r->first ) - { - index = i; - return true; - } - } - - return false; - } - - bool HasUndepletedLast( int& index ) - { - index = -1; - - if ( !m_bDepleteBeforeRepeat ) - return false; - - int c = group.Count(); - for ( int i = 0; i < c; i++ ) - { - Response *r = &group[ i ]; - - if ( ( r->depletioncount != m_nDepletionCount ) && r->last ) - { - index = i; - return true; - } - } - - return false; - } - - bool ShouldCheckRepeats() const { return m_bDepleteBeforeRepeat; } - int GetDepletionCount() const { return m_nDepletionCount; } - - bool IsSequential() const { return m_bSequential; } - void SetSequential( bool seq ) { m_bSequential = seq; } - - bool IsNoRepeat() const { return m_bNoRepeat; } - void SetNoRepeat( bool norepeat ) { m_bNoRepeat = norepeat; } - - bool IsEnabled() const { return m_bEnabled; } - void SetEnabled( bool enabled ) { m_bEnabled = enabled; } - - int GetCurrentIndex() const { return m_nCurrentIndex; } - void SetCurrentIndex( byte idx ) { m_nCurrentIndex = idx; } - - CUtlVector< Response > group; - - AI_ResponseParams rp; - - bool m_bEnabled; - - byte m_nCurrentIndex; - // Invalidation counter - byte m_nDepletionCount; - - // Use all slots before repeating any - bool m_bDepleteBeforeRepeat : 1; - bool m_bHasFirst : 1; - bool m_bHasLast : 1; - bool m_bSequential : 1; - bool m_bNoRepeat : 1; - -}; - -struct Criteria -{ - Criteria() - { - name = NULL; - value = NULL; - weight.SetFloat( 1.0f ); - required = false; - } - Criteria& operator =(const Criteria& src ) - { - if ( this == &src ) - return *this; - - name = CopyString( src.name ); - value = CopyString( src.value ); - weight = src.weight; - required = src.required; - - matcher = src.matcher; - - int c = src.subcriteria.Count(); - for ( int i = 0; i < c; i++ ) - { - subcriteria.AddToTail( src.subcriteria[ i ] ); - } - - return *this; - } - Criteria(const Criteria& src ) - { - name = CopyString( src.name ); - value = CopyString( src.value ); - weight = src.weight; - required = src.required; - - matcher = src.matcher; - - int c = src.subcriteria.Count(); - for ( int i = 0; i < c; i++ ) - { - subcriteria.AddToTail( src.subcriteria[ i ] ); - } - } - ~Criteria() - { - delete[] name; - delete[] value; - } - - bool IsSubCriteriaType() const - { - return ( subcriteria.Count() > 0 ) ? true : false; - } - - char *name; - char *value; - float16 weight; - bool required; - - Matcher matcher; - - // Indices into sub criteria - CUtlVector< unsigned short > subcriteria; -}; - -struct Rule -{ - Rule() - { - m_bMatchOnce = false; - m_bEnabled = true; - m_szContext = NULL; - } - - Rule& operator =( const Rule& src ) - { - if ( this == &src ) - return *this; - - int i; - int c; - - c = src.m_Criteria.Count(); - for ( i = 0; i < c; i++ ) - { - m_Criteria.AddToTail( src.m_Criteria[ i ] ); - } - - c = src.m_Responses.Count(); - for ( i = 0; i < c; i++ ) - { - m_Responses.AddToTail( src.m_Responses[ i ] ); - } - - SetContext( src.m_szContext ); - m_bMatchOnce = src.m_bMatchOnce; - m_bEnabled = src.m_bEnabled; - return *this; - } - - Rule( const Rule& src ) - { - int i; - int c; - - c = src.m_Criteria.Count(); - for ( i = 0; i < c; i++ ) - { - m_Criteria.AddToTail( src.m_Criteria[ i ] ); - } - - c = src.m_Responses.Count(); - for ( i = 0; i < c; i++ ) - { - m_Responses.AddToTail( src.m_Responses[ i ] ); - } - - SetContext( src.m_szContext ); - m_bMatchOnce = src.m_bMatchOnce; - m_bEnabled = src.m_bEnabled; - } - - ~Rule() - { - delete[] m_szContext; - } - - void SetContext( const char *context ) - { - delete[] m_szContext; - m_szContext = CopyString( context ); - } - - const char *GetContext( void ) const { return m_szContext; } - - bool IsEnabled() const { return m_bEnabled; } - void Disable() { m_bEnabled = false; } - bool IsMatchOnce() const { return m_bMatchOnce; } - - // Indices into underlying criteria and response dictionaries - CUtlVector< unsigned short > m_Criteria; - CUtlVector< unsigned short> m_Responses; - - char *m_szContext; - - bool m_bMatchOnce : 1; - bool m_bEnabled : 1; -}; -#pragma pack() - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -abstract_class CResponseSystem : public IResponseSystem -{ -public: - CResponseSystem(); - ~CResponseSystem(); - - // IResponseSystem - virtual bool FindBestResponse( const AI_CriteriaSet& set, AI_Response& response, IResponseFilter *pFilter = NULL ); - virtual void GetAllResponses( CUtlVector *pResponses ); - - virtual void Release() = 0; - - virtual void DumpRules(); - - virtual void Precache(); - - virtual void PrecacheResponses( bool bEnable ) - { - m_bPrecache = bEnable; - } - - bool ShouldPrecache() { return m_bPrecache; } - - void Clear(); - -protected: - - virtual const char *GetScriptFile( void ) = 0; - void LoadRuleSet( const char *setname ); - - void ResetResponseGroups(); -public: - - - - -private: - - struct Enumeration - { - float value; - }; - - struct ResponseSearchResult - { - ResponseSearchResult() - { - group = NULL; - action = NULL; - } - - ResponseGroup *group; - Response *action; - }; - - inline bool ParseToken( void ) - { - if ( m_bUnget ) - { - m_bUnget = false; - return true; - } - if ( m_ScriptStack.Count() <= 0 ) - { - Assert( 0 ); - return false; - } - - m_ScriptStack[ 0 ].currenttoken = engine->ParseFile( m_ScriptStack[ 0 ].currenttoken, token, sizeof( token ) ); - m_ScriptStack[ 0 ].tokencount++; - return m_ScriptStack[ 0 ].currenttoken != NULL ? true : false; - } - - inline void Unget() - { - m_bUnget = true; - } - - inline bool TokenWaiting( void ) - { - if ( m_ScriptStack.Count() <= 0 ) - { - Assert( 0 ); - return false; - } - - const char *p = m_ScriptStack[ 0 ].currenttoken; - - if ( !p ) - { - Error( "AI_ResponseSystem: Unxpected TokenWaiting() with NULL buffer in %s", m_ScriptStack[ 0 ].name ); - return false; - } - - - while ( *p && *p!='\n') - { - // Special handler for // comment blocks - if ( *p == '/' && *(p+1) == '/' ) - return false; - - if ( !isspace( *p ) || isalnum( *p ) ) - return true; - - p++; - } - - return false; - } - - void ParseOneResponse( const char *responseGroupName, ResponseGroup& group ); - - void ParseInclude( CStringPool &includedFiles ); - void ParseResponse( void ); - void ParseCriterion( void ); - void ParseRule( void ); - void ParseEnumeration( void ); - - int ParseOneCriterion( const char *criterionName ); - - bool Compare( const char *setValue, Criteria *c, bool verbose = false ); - bool CompareUsingMatcher( const char *setValue, Matcher& m, bool verbose = false ); - void ComputeMatcher( Criteria *c, Matcher& matcher ); - void ResolveToken( Matcher& matcher, char *token, size_t bufsize, char const *rawtoken ); - float LookupEnumeration( const char *name, bool& found ); - - int FindBestMatchingRule( const AI_CriteriaSet& set, bool verbose ); - - float ScoreCriteriaAgainstRule( const AI_CriteriaSet& set, int irule, bool verbose = false ); - float RecursiveScoreSubcriteriaAgainstRule( const AI_CriteriaSet& set, Criteria *parent, bool& exclude, bool verbose /*=false*/ ); - float ScoreCriteriaAgainstRuleCriteria( const AI_CriteriaSet& set, int icriterion, bool& exclude, bool verbose = false ); - bool GetBestResponse( ResponseSearchResult& result, Rule *rule, bool verbose = false, IResponseFilter *pFilter = NULL ); - bool ResolveResponse( ResponseSearchResult& result, int depth, const char *name, bool verbose = false, IResponseFilter *pFilter = NULL ); - int SelectWeightedResponseFromResponseGroup( ResponseGroup *g, IResponseFilter *pFilter ); - void DescribeResponseGroup( ResponseGroup *group, int selected, int depth ); - void DebugPrint( int depth, const char *fmt, ... ); - - void LoadFromBuffer( const char *scriptfile, const char *buffer, CStringPool &includedFiles ); - -// void TouchReferencedScenes(); - - void GetCurrentScript( char *buf, size_t buflen ); - int GetCurrentToken() const; - void SetCurrentScript( const char *script ); - bool IsRootCommand(); - - void PushScript( const char *scriptfile, unsigned char *buffer ); - void PopScript(void); - - void ResponseWarning( const char *fmt, ... ); - - CUtlDict< ResponseGroup, short > m_Responses; - CUtlDict< Criteria, short > m_Criteria; - CUtlDict< Rule, short > m_Rules; - CUtlDict< Enumeration, short > m_Enumerations; - - char token[ 1204 ]; - - bool m_bUnget; - bool m_bPrecache; - - struct ScriptEntry - { - unsigned char *buffer; - FileNameHandle_t name; - const char *currenttoken; - int tokencount; - }; - - CUtlVector< ScriptEntry > m_ScriptStack; - - friend class CDefaultResponseSystemSaveRestoreBlockHandler; - friend class CResponseSystemSaveRestoreOps; -}; - -BEGIN_SIMPLE_DATADESC( Response ) - // DEFINE_FIELD( type, FIELD_INTEGER ), - // DEFINE_ARRAY( value, FIELD_CHARACTER ), - // DEFINE_FIELD( weight, FIELD_FLOAT ), - DEFINE_FIELD( depletioncount, FIELD_CHARACTER ), - // DEFINE_FIELD( first, FIELD_BOOLEAN ), - // DEFINE_FIELD( last, FIELD_BOOLEAN ), -END_DATADESC() - -BEGIN_SIMPLE_DATADESC( ResponseGroup ) - // DEFINE_FIELD( group, FIELD_UTLVECTOR ), - // DEFINE_FIELD( rp, FIELD_EMBEDDED ), - // DEFINE_FIELD( m_bDepleteBeforeRepeat, FIELD_BOOLEAN ), - DEFINE_FIELD( m_nDepletionCount, FIELD_CHARACTER ), - // DEFINE_FIELD( m_bHasFirst, FIELD_BOOLEAN ), - // DEFINE_FIELD( m_bHasLast, FIELD_BOOLEAN ), - // DEFINE_FIELD( m_bSequential, FIELD_BOOLEAN ), - // DEFINE_FIELD( m_bNoRepeat, FIELD_BOOLEAN ), - DEFINE_FIELD( m_bEnabled, FIELD_BOOLEAN ), - DEFINE_FIELD( m_nCurrentIndex, FIELD_CHARACTER ), -END_DATADESC() - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -CResponseSystem::CResponseSystem() -{ - token[0] = 0; - m_bUnget = false; - m_bPrecache = true; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -CResponseSystem::~CResponseSystem() -{ -} - -//----------------------------------------------------------------------------- -// Purpose: -// Output : char const -//----------------------------------------------------------------------------- -void CResponseSystem::GetCurrentScript( char *buf, size_t buflen ) -{ - Assert( buf ); - buf[ 0 ] = 0; - if ( m_ScriptStack.Count() <= 0 ) - return; - - if ( filesystem->String( m_ScriptStack[ 0 ].name, buf, buflen ) ) - { - return; - } - buf[ 0 ] = 0; -} - -void CResponseSystem::PushScript( const char *scriptfile, unsigned char *buffer ) -{ - ScriptEntry e; - e.name = filesystem->FindOrAddFileName( scriptfile ); - e.buffer = buffer; - e.currenttoken = (char *)e.buffer; - e.tokencount = 0; - m_ScriptStack.AddToHead( e ); -} - -void CResponseSystem::PopScript(void) -{ - Assert( m_ScriptStack.Count() >= 1 ); - if ( m_ScriptStack.Count() <= 0 ) - return; - - m_ScriptStack.Remove( 0 ); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CResponseSystem::Clear() -{ - m_Responses.RemoveAll(); - m_Criteria.RemoveAll(); - m_Rules.RemoveAll(); - m_Enumerations.RemoveAll(); -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *name - -// found - -// Output : float -//----------------------------------------------------------------------------- -float CResponseSystem::LookupEnumeration( const char *name, bool& found ) -{ - int idx = m_Enumerations.Find( name ); - if ( idx == m_Enumerations.InvalidIndex() ) - { - found = false; - return 0.0f; - } - - - found = true; - return m_Enumerations[ idx ].value; -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : matcher - -//----------------------------------------------------------------------------- -void CResponseSystem::ResolveToken( Matcher& matcher, char *token, size_t bufsize, char const *rawtoken ) -{ - if ( rawtoken[0] != '[' ) - { - Q_strncpy( token, rawtoken, bufsize ); - return; - } - - // Now lookup enumeration - bool found = false; - float f = LookupEnumeration( rawtoken, found ); - if ( !found ) - { - Q_strncpy( token, rawtoken, bufsize ); - ResponseWarning( "No such enumeration '%s'\n", token ); - return; - } - - Q_snprintf( token, bufsize, "%f", f ); -} - - -static bool AppearsToBeANumber( char const *token ) -{ - if ( atof( token ) != 0.0f ) - return true; - - char const *p = token; - while ( *p ) - { - if ( *p != '0' ) - return false; - - p++; - } - - return true; -} - -void CResponseSystem::ComputeMatcher( Criteria *c, Matcher& matcher ) -{ - const char *s = c->value; - if ( !s ) - { - matcher.valid = false; - return; - } - - const char *in = s; - - char token[ 128 ]; - char rawtoken[ 128 ]; - - token[ 0 ] = 0; - rawtoken[ 0 ] = 0; - - int n = 0; - - bool gt = false; - bool lt = false; - bool eq = false; - bool nt = false; - - bool done = false; - while ( !done ) - { - switch( *in ) - { - case '>': - { - gt = true; - Assert( !lt ); // Can't be both - } - break; - case '<': - { - lt = true; - Assert( !gt ); // Can't be both - } - break; - case '=': - { - eq = true; - } - break; - case ',': - case '\0': - { - rawtoken[ n ] = 0; - n = 0; - - // Convert raw token to real token in case token is an enumerated type specifier - ResolveToken( matcher, token, sizeof( token ), rawtoken ); - - // Fill in first data set - if ( gt ) - { - matcher.usemin = true; - matcher.minequals = eq; - matcher.minval = (float)atof( token ); - - matcher.isnumeric = true; - } - else if ( lt ) - { - matcher.usemax = true; - matcher.maxequals = eq; - matcher.maxval = (float)atof( token ); - - matcher.isnumeric = true; - } - else - { - if ( *in == ',' ) - { - // If there's a comma, this better have been a less than or a gt key - Assert( 0 ); - } - - matcher.notequal = nt; - - matcher.isnumeric = AppearsToBeANumber( token ); - } - - gt = lt = eq = nt = false; - - if ( !(*in) ) - { - done = true; - } - } - break; - case '!': - nt = true; - break; - default: - rawtoken[ n++ ] = *in; - break; - } - - in++; - } - - matcher.SetToken( token ); - matcher.SetRaw( rawtoken ); - matcher.valid = true; -} - -bool CResponseSystem::CompareUsingMatcher( const char *setValue, Matcher& m, bool verbose /*=false*/ ) -{ - if ( !m.valid ) - return false; - - float v = (float)atof( setValue ); - if ( setValue[0] == '[' ) - { - bool found = false; - v = LookupEnumeration( setValue, found ); - } - - int minmaxcount = 0; - - if ( m.usemin ) - { - if ( m.minequals ) - { - if ( v < m.minval ) - return false; - } - else - { - if ( v <= m.minval ) - return false; - } - - ++minmaxcount; - } - - if ( m.usemax ) - { - if ( m.maxequals ) - { - if ( v > m.maxval ) - return false; - } - else - { - if ( v >= m.maxval ) - return false; - } - - ++minmaxcount; - } - - // Had one or both criteria and met them - if ( minmaxcount >= 1 ) - { - return true; - } - - if ( m.notequal ) - { - if ( m.isnumeric ) - { - if ( v == (float)atof( m.GetToken() ) ) - return false; - } - else - { - if ( !Q_stricmp( setValue, m.GetToken() ) ) - return false; - } - - return true; - } - - if ( m.isnumeric ) - { - // If the setValue is "", the NPC doesn't have the key at all, - // in which case we shouldn't match "0". - if ( !setValue || !setValue[0] ) - return false; - - return v == (float)atof( m.GetToken() ); - } - - return !Q_stricmp( setValue, m.GetToken() ) ? true : false; -} - -bool CResponseSystem::Compare( const char *setValue, Criteria *c, bool verbose /*= false*/ ) -{ - Assert( c ); - Assert( setValue ); - - bool bret = CompareUsingMatcher( setValue, c->matcher, verbose ); - - if ( verbose ) - { - DevMsg( "'%20s' vs. '%20s' = ", setValue, c->value ); - - { - //DevMsg( "\n" ); - //m.Describe(); - } - } - return bret; -} - -float CResponseSystem::RecursiveScoreSubcriteriaAgainstRule( const AI_CriteriaSet& set, Criteria *parent, bool& exclude, bool verbose /*=false*/ ) -{ - float score = 0.0f; - int subcount = parent->subcriteria.Count(); - for ( int i = 0; i < subcount; i++ ) - { - int icriterion = parent->subcriteria[ i ]; - - bool excludesubrule = false; - if (verbose) - { - DevMsg( "\n" ); - } - score += ScoreCriteriaAgainstRuleCriteria( set, icriterion, excludesubrule, verbose ); - } - - exclude = ( parent->required && score == 0.0f ) ? true : false; - - return score * parent->weight.GetFloat(); -} - -float CResponseSystem::ScoreCriteriaAgainstRuleCriteria( const AI_CriteriaSet& set, int icriterion, bool& exclude, bool verbose /*=false*/ ) -{ - Criteria *c = &m_Criteria[ icriterion ]; - - if ( c->IsSubCriteriaType() ) - { - return RecursiveScoreSubcriteriaAgainstRule( set, c, exclude, verbose ); - } - - if ( verbose ) - { - DevMsg( " criterion '%25s':'%15s' ", m_Criteria.GetElementName( icriterion ), c->name ); - } - - exclude = false; - - float score = 0.0f; - - const char *actualValue = ""; - - int found = set.FindCriterionIndex( c->name ); - if ( found != -1 ) - { - actualValue = set.GetValue( found ); - if ( !actualValue ) - { - Assert( 0 ); - return score; - } - } - - Assert( actualValue ); - - if ( Compare( actualValue, c, verbose ) ) - { - float w = set.GetWeight( found ); - score = w * c->weight.GetFloat(); - - if ( verbose ) - { - DevMsg( "matched, weight %4.2f (s %4.2f x c %4.2f)", - score, w, c->weight ); - } - } - else - { - if ( c->required ) - { - exclude = true; - if ( verbose ) - { - DevMsg( "failed (+exclude rule)" ); - } - } - else - { - if ( verbose ) - { - DevMsg( "failed" ); - } - } - } - - return score; -} - -float CResponseSystem::ScoreCriteriaAgainstRule( const AI_CriteriaSet& set, int irule, bool verbose /*=false*/ ) -{ - Rule *rule = &m_Rules[ irule ]; - float score = 0.0f; - - bool bBeingWatched = false; - - // See if we're trying to debug this rule - const char *pszText = rr_debugrule.GetString(); - if ( pszText && pszText[0] && !Q_stricmp( pszText, m_Rules.GetElementName( irule ) ) ) - { - bBeingWatched = true; - } - - if ( !rule->IsEnabled() ) - { - if ( bBeingWatched ) - { - DevMsg("Rule '%s' is disabled.\n" ); - } - return 0.0f; - } - - if ( bBeingWatched ) - { - verbose = true; - } - - if ( verbose ) - { - DevMsg( "Scoring rule '%s' (%i)\n{\n", m_Rules.GetElementName( irule ), irule+1 ); - } - - // Iterate set criteria - int count = rule->m_Criteria.Count(); - int i; - for ( i = 0; i < count; i++ ) - { - int icriterion = rule->m_Criteria[ i ]; - - bool exclude = false; - score += ScoreCriteriaAgainstRuleCriteria( set, icriterion, exclude, verbose ); - - if ( verbose ) - { - DevMsg( ", score %4.2f\n", score ); - } - - if ( exclude ) - { - score = 0.0f; - break; - } - } - - if ( verbose ) - { - DevMsg( "}\n" ); - } - - return score; -} - -void CResponseSystem::DebugPrint( int depth, const char *fmt, ... ) -{ - int indentchars = 3 * depth; - char *indent = (char *)_alloca( indentchars + 1); - indent[ indentchars ] = 0; - while ( --indentchars >= 0 ) - { - indent[ indentchars ] = ' '; - } - - // Dump text to debugging console. - va_list argptr; - char szText[1024]; - - va_start (argptr, fmt); - Q_vsnprintf (szText, sizeof( szText ), fmt, argptr); - va_end (argptr); - - DevMsg( "%s%s", indent, szText ); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CResponseSystem::ResetResponseGroups() -{ - int i; - int c = m_Responses.Count(); - for ( i = 0; i < c; i++ ) - { - m_Responses[ i ].Reset(); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *g - -// Output : int -//----------------------------------------------------------------------------- -int CResponseSystem::SelectWeightedResponseFromResponseGroup( ResponseGroup *g, IResponseFilter *pFilter ) -{ - int c = g->group.Count(); - if ( !c ) - { - Assert( !"Expecting response group with >= 1 elements" ); - return -1; - } - - int i; - - // Fake depletion of unavailable choices - CUtlVector fakedDepletes; - if ( pFilter && g->ShouldCheckRepeats() ) - { - for ( i = 0; i < c; i++ ) - { - Response *r = &g->group[ i ]; - if ( r->depletioncount != g->GetDepletionCount() && !pFilter->IsValidResponse( r->GetType(), r->value ) ) - { - fakedDepletes.AddToTail( i ); - g->MarkResponseUsed( i ); - } - } - } - - if ( !g->HasUndepletedChoices() ) - { - g->ResetDepletionCount(); - - if ( pFilter && g->ShouldCheckRepeats() ) - { - fakedDepletes.RemoveAll(); - for ( i = 0; i < c; i++ ) - { - Response *r = &g->group[ i ]; - if ( !pFilter->IsValidResponse( r->GetType(), r->value ) ) - { - fakedDepletes.AddToTail( i ); - g->MarkResponseUsed( i ); - } - } - } - - if ( !g->HasUndepletedChoices() ) - return -1; - - // Disable the group if we looped through all the way - if ( g->IsNoRepeat() ) - { - g->SetEnabled( false ); - return -1; - } - } - - bool checkrepeats = g->ShouldCheckRepeats(); - int depletioncount = g->GetDepletionCount(); - - float totalweight = 0.0f; - int slot = -1; - - if ( checkrepeats ) - { - int check= -1; - // Snag the first slot right away - if ( g->HasUndepletedFirst( check ) && check != -1 ) - { - slot = check; - } - - if ( slot == -1 && g->HasUndepletedLast( check ) && check != -1 ) - { - // If this is the only undepleted one, use it now - for ( i = 0; i < c; i++ ) - { - Response *r = &g->group[ i ]; - if ( checkrepeats && - ( r->depletioncount == depletioncount ) ) - { - continue; - } - - if ( r->last ) - { - Assert( i == check ); - continue; - } - - // There's still another undepleted entry - break; - } - - // No more undepleted so use the r->last slot - if ( i >= c ) - { - slot = check; - } - } - } - - if ( slot == -1 ) - { - for ( i = 0; i < c; i++ ) - { - Response *r = &g->group[ i ]; - if ( checkrepeats && - ( r->depletioncount == depletioncount ) ) - { - continue; - } - - // Always skip last entry here since we will deal with it above - if ( checkrepeats && r->last ) - continue; - - int prevSlot = slot; - - if ( !totalweight ) - { - slot = i; - } - - // Always assume very first slot will match - totalweight += r->weight.GetFloat(); - if ( !totalweight || random->RandomFloat(0,totalweight) < r->weight.GetFloat() ) - { - slot = i; - } - - if ( !checkrepeats && slot != prevSlot && pFilter && !pFilter->IsValidResponse( r->GetType(), r->value ) ) - { - slot = prevSlot; - totalweight -= r->weight.GetFloat(); - } - } - } - - if ( slot != -1 ) - g->MarkResponseUsed( slot ); - - // Revert fake depletion of unavailable choices - if ( pFilter && g->ShouldCheckRepeats() ) - { - for ( i = 0; i < fakedDepletes.Count(); i++ ) - { - g->group[ fakedDepletes[ i ] ].depletioncount = 0;; - } - } - - return slot; -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : searchResult - -// depth - -// *name - -// verbose - -// Output : Returns true on success, false on failure. -//----------------------------------------------------------------------------- -bool CResponseSystem::ResolveResponse( ResponseSearchResult& searchResult, int depth, const char *name, bool verbose /*= false*/, IResponseFilter *pFilter ) -{ - int responseIndex = m_Responses.Find( name ); - if ( responseIndex == m_Responses.InvalidIndex() ) - return false; - - ResponseGroup *g = &m_Responses[ responseIndex ]; - // Group has been disabled - if ( !g->IsEnabled() ) - return false; - - int c = g->group.Count(); - if ( !c ) - return false; - - int idx = 0; - - if ( g->IsSequential() ) - { - // See if next index is valid - int initialIndex = g->GetCurrentIndex(); - bool bFoundValid = false; - - do - { - idx = g->GetCurrentIndex(); - g->SetCurrentIndex( idx + 1 ); - if ( idx >= c ) - { - if ( g->IsNoRepeat() ) - { - g->SetEnabled( false ); - return false; - } - idx = 0; - g->SetCurrentIndex( 0 ); - } - - if ( !pFilter || pFilter->IsValidResponse( g->group[idx].GetType(), g->group[idx].value ) ) - { - bFoundValid = true; - break; - } - - } while ( g->GetCurrentIndex() != initialIndex ); - - if ( !bFoundValid ) - return false; - } - else - { - idx = SelectWeightedResponseFromResponseGroup( g, pFilter ); - if ( idx < 0 ) - return false; - } - - if ( verbose ) - { - DebugPrint( depth, "%s\n", m_Responses.GetElementName( responseIndex ) ); - DebugPrint( depth, "{\n" ); - DescribeResponseGroup( g, idx, depth ); - } - - bool bret = true; - - Response *result = &g->group[ idx ]; - if ( result->type == RESPONSE_RESPONSE ) - { - // Recurse - bret = ResolveResponse( searchResult, depth + 1, result->value, verbose, pFilter ); - } - else - { - searchResult.action = result; - searchResult.group = g; - } - - if( verbose ) - { - DebugPrint( depth, "}\n" ); - } - - return bret; -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *group - -// selected - -// depth - -//----------------------------------------------------------------------------- -void CResponseSystem::DescribeResponseGroup( ResponseGroup *group, int selected, int depth ) -{ - int c = group->group.Count(); - - for ( int i = 0; i < c ; i++ ) - { - Response *r = &group->group[ i ]; - DebugPrint( depth + 1, "%s%20s : %40s %5.3f\n", - i == selected ? "-> " : " ", - AI_Response::DescribeResponse( r->GetType() ), - r->value, - r->weight ); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *rule - -// Output : CResponseSystem::Response -//----------------------------------------------------------------------------- -bool CResponseSystem::GetBestResponse( ResponseSearchResult& searchResult, Rule *rule, bool verbose /*=false*/, IResponseFilter *pFilter ) -{ - int c = rule->m_Responses.Count(); - if ( !c ) - return false; - - int index = random->RandomInt( 0, c - 1 ); - int groupIndex = rule->m_Responses[ index ]; - - ResponseGroup *g = &m_Responses[ groupIndex ]; - - // Group has been disabled - if ( !g->IsEnabled() ) - return false; - - int count = g->group.Count(); - if ( !count ) - return false; - - int responseIndex = 0; - - if ( g->IsSequential() ) - { - // See if next index is valid - int initialIndex = g->GetCurrentIndex(); - bool bFoundValid = false; - - do - { - responseIndex = g->GetCurrentIndex(); - g->SetCurrentIndex( responseIndex + 1 ); - if ( responseIndex >= count ) - { - if ( g->IsNoRepeat() ) - { - g->SetEnabled( false ); - return false; - } - responseIndex = 0; - g->SetCurrentIndex( 0 ); - } - - if ( !pFilter || pFilter->IsValidResponse( g->group[responseIndex].GetType(), g->group[responseIndex].value ) ) - { - bFoundValid = true; - break; - } - - } while ( g->GetCurrentIndex() != initialIndex ); - - if ( !bFoundValid ) - return false; - } - else - { - responseIndex = SelectWeightedResponseFromResponseGroup( g, pFilter ); - if ( responseIndex < 0 ) - return false; - } - - - Response *r = &g->group[ responseIndex ]; - - int depth = 0; - - if ( verbose ) - { - DebugPrint( depth, "%s\n", m_Responses.GetElementName( groupIndex ) ); - DebugPrint( depth, "{\n" ); - - DescribeResponseGroup( g, responseIndex, depth ); - } - - bool bret = true; - - if ( r->type == RESPONSE_RESPONSE ) - { - bret = ResolveResponse( searchResult, depth + 1, r->value, verbose, pFilter ); - } - else - { - searchResult.action = r; - searchResult.group = g; - } - - if ( verbose ) - { - DebugPrint( depth, "}\n" ); - } - - return bret; -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : set - -// verbose - -// Output : int -//----------------------------------------------------------------------------- -int CResponseSystem::FindBestMatchingRule( const AI_CriteriaSet& set, bool verbose ) -{ - CUtlVector< int > bestrules; - float bestscore = 0.001f; - - int c = m_Rules.Count(); - int i; - for ( i = 0; i < c; i++ ) - { - float score = ScoreCriteriaAgainstRule( set, i, verbose ); - // Check equals so that we keep track of all matching rules - if ( score >= bestscore ) - { - // Reset bucket - if( score != bestscore ) - { - bestscore = score; - bestrules.RemoveAll(); - } - - // Add to bucket - bestrules.AddToTail( i ); - } - } - - int bestCount = bestrules.Count(); - if ( bestCount <= 0 ) - return -1; - - if ( bestCount == 1 ) - return bestrules[ 0 ]; - - // Randomly pick one of the tied matching rules - int idx = random->RandomInt( 0, bestCount - 1 ); - if ( verbose ) - { - DevMsg( "Found %i matching rules, selecting slot %i\n", bestCount, idx ); - } - return bestrules[ idx ]; -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : set - -// Output : AI_Response -//----------------------------------------------------------------------------- -bool CResponseSystem::FindBestResponse( const AI_CriteriaSet& set, AI_Response& response, IResponseFilter *pFilter ) -{ - bool valid = false; - - int iDbgResponse = rr_debugresponses.GetInt(); - bool showRules = ( iDbgResponse == 2 ); - bool showResult = ( iDbgResponse == 1 || iDbgResponse == 2 ); - - // Look for match - int bestRule = FindBestMatchingRule( set, showRules ); - - ResponseType_t responseType = RESPONSE_NONE; - AI_ResponseParams rp; - - char ruleName[ 128 ]; - char responseName[ 128 ]; - const char *context; - ruleName[ 0 ] = 0; - responseName[ 0 ] = 0; - context = NULL; - if ( bestRule != -1 ) - { - Rule *r = &m_Rules[ bestRule ]; - - ResponseSearchResult result; - if ( GetBestResponse( result, r, showResult, pFilter ) ) - { - Q_strncpy( responseName, result.action->value, sizeof( responseName ) ); - responseType = result.action->GetType(); - rp = result.group->rp; - } - - Q_strncpy( ruleName, m_Rules.GetElementName( bestRule ), sizeof( ruleName ) ); - - // Disable the rule if it only allows for matching one time - if ( r->IsMatchOnce() ) - { - r->Disable(); - } - context = r->GetContext(); - - valid = true; - } - - response.Init( responseType, responseName, set, rp, ruleName, context ); - - if ( showResult ) - { - if ( valid ) - { - // Rescore the winner and dump to console - ScoreCriteriaAgainstRule( set, bestRule, true ); - } - - if ( valid || showRules ) - { - // Describe the response, too - response.Describe(); - } - } - - return valid; -} - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -void CResponseSystem::GetAllResponses( CUtlVector *pResponses ) -{ - for ( int i = 0; i < (int)m_Responses.Count(); i++ ) - { - ResponseGroup &group = m_Responses[i]; - - for ( int j = 0; j < group.group.Count(); j++) - { - Response &response = group.group[j]; - if ( response.type != RESPONSE_RESPONSE ) - { - AI_Response *pResponse = new AI_Response; - pResponse->Init( response.GetType(), response.value, AI_CriteriaSet(), group.rp, NULL, NULL ); - pResponses->AddToTail(pResponse); - } - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CResponseSystem::Precache() -{ - // enumerate and mark all the scripts so we know they're referenced - for ( int i = 0; i < (int)m_Responses.Count(); i++ ) - { - ResponseGroup &group = m_Responses[i]; - - for ( int j = 0; j < group.group.Count(); j++) - { - Response &response = group.group[j]; - switch ( response.type ) - { - default: - break; - case RESPONSE_SCENE: - { - // fixup $gender references - char file[_MAX_PATH]; - Q_strncpy( file, response.value, sizeof(file) ); - char *gender = strstr( file, "$gender" ); - if ( gender ) - { - // replace with male & female - const char *postGender = gender + strlen("$gender"); - *gender = 0; - char genderFile[_MAX_PATH]; - // male - Q_snprintf( genderFile, sizeof(genderFile), "%smale%s", file, postGender); - - PrecacheInstancedScene( genderFile ); - - Q_snprintf( genderFile, sizeof(genderFile), "%sfemale%s", file, postGender); - - PrecacheInstancedScene( genderFile ); - } - else - { - PrecacheInstancedScene( file ); - } - } - break; - case RESPONSE_SPEAK: - { - CBaseEntity::PrecacheScriptSound( response.value ); - } - break; - } - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: Does any necessary resource allocation for resources references in the script file -//----------------------------------------------------------------------------- -/* -I think this is obsolete in view of having to precache all of the scenes, etc. -void CResponseSystem::TouchReferencedScenes() -{ - if (CommandLine()->CheckParm("-makereslists")) - { - // enumerate and mark all the scripts so we know they're referenced - for ( int i = 0; i < (int)m_Responses.Count(); i++ ) - { - ResponseGroup &group = m_Responses[i]; - - for ( int j = 0; j < group.group.Count(); j++) - { - Response &response = group.group[j]; - if (response.type == RESPONSE_SCENE) - { - // fixup $gender references - char file[_MAX_PATH]; - Q_strncpy( file, response.value, sizeof(file) ); - char *gender = strstr( file, "$gender" ); - if ( gender ) - { - // replace with male & female - const char *postGender = gender + strlen("$gender"); - *gender = 0; - char genderFile[_MAX_PATH]; - // male - Q_snprintf( genderFile, sizeof(genderFile), "%smale%s", file, postGender); - FileHandle_t f = filesystem->Open(genderFile, "rb"); - if (f) - { - filesystem->Close(f); - } - // female - Q_snprintf( genderFile, sizeof(genderFile), "%sfemale%s", file, postGender); - f = filesystem->Open(genderFile, "rb"); - if (f) - { - filesystem->Close(f); - } - } - else - { - // just force the file open and closed so the filesystem can log it - FileHandle_t f = filesystem->Open(file, "rb"); - if (f) - { - filesystem->Close(f); - } - } - } - } - } - } -} -*/ - -void CResponseSystem::ParseInclude( CStringPool &includedFiles ) -{ - char includefile[ 256 ]; - ParseToken(); - Q_snprintf( includefile, sizeof( includefile ), "scripts/%s", token ); - - // check if the file is already included - if ( includedFiles.Find( includefile ) != NULL ) - { - return; - } - - // Try and load it - CUtlBuffer buf; - if ( !filesystem->ReadFile( includefile, "GAME", buf ) ) - { - DevMsg( "Unable to load #included script %s\n", includefile ); - return; - } - - LoadFromBuffer( includefile, (const char *)buf.PeekGet(), includedFiles ); -} - -void CResponseSystem::LoadFromBuffer( const char *scriptfile, const char *buffer, CStringPool &includedFiles ) -{ - includedFiles.Allocate( scriptfile ); - PushScript( scriptfile, (unsigned char * )buffer ); - - if( rr_dumpresponses.GetBool() ) - { - DevMsg("Reading: %s\n", scriptfile ); - } - - while ( 1 ) - { - ParseToken(); - if ( !token[0] ) - { - break; - } - - if ( !Q_stricmp( token, "#include" ) ) - { - ParseInclude( includedFiles ); - } - else if ( !Q_stricmp( token, "response" ) ) - { - ParseResponse(); - } - else if ( !Q_stricmp( token, "criterion" ) || - !Q_stricmp( token, "criteria" ) ) - { - ParseCriterion(); - } - else if ( !Q_stricmp( token, "rule" ) ) - { - ParseRule(); - } - else if ( !Q_stricmp( token, "enumeration" ) ) - { - ParseEnumeration(); - } - else - { - int byteoffset = m_ScriptStack[ 0 ].currenttoken - (const char *)m_ScriptStack[ 0 ].buffer; - - Error( "CResponseSystem::LoadFromBuffer: Unknown entry type '%s', expecting 'response', 'criterion', 'enumeration' or 'rules' in file %s(offset:%i)\n", - token, scriptfile, byteoffset ); - break; - } - } - - if ( m_ScriptStack.Count() == 1 ) - { - char cur[ 256 ]; - GetCurrentScript( cur, sizeof( cur ) ); - DevMsg( 1, "CResponseSystem: %s (%i rules, %i criteria, and %i responses)\n", - cur, m_Rules.Count(), m_Criteria.Count(), m_Responses.Count() ); - - if( rr_dumpresponses.GetBool() ) - { - DumpRules(); - } - } - - PopScript(); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CResponseSystem::LoadRuleSet( const char *basescript ) -{ - int length = 0; - unsigned char *buffer = (unsigned char *)UTIL_LoadFileForMe( basescript, &length ); - if ( length <= 0 || !buffer ) - { - DevMsg( 1, "CResponseSystem: failed to load %s\n", basescript ); - return; - } - - CStringPool includedFiles; - - LoadFromBuffer( basescript, (const char *)buffer, includedFiles ); - - UTIL_FreeFile( buffer ); - - Assert( m_ScriptStack.Count() == 0 ); - - //TouchReferencedScenes(); -} - -static ResponseType_t ComputeResponseType( const char *s ) -{ - if ( !Q_stricmp( s, "scene" ) ) - { - return RESPONSE_SCENE; - } - else if ( !Q_stricmp( s, "sentence" ) ) - { - return RESPONSE_SENTENCE; - } - else if ( !Q_stricmp( s, "speak" ) ) - { - return RESPONSE_SPEAK; - } - else if ( !Q_stricmp( s, "response" ) ) - { - return RESPONSE_RESPONSE; - } - else if ( !Q_stricmp( s, "print" ) ) - { - return RESPONSE_PRINT; - } - - return RESPONSE_NONE; -} - -void CResponseSystem::ParseOneResponse( const char *responseGroupName, ResponseGroup& group ) -{ - Response newResponse; - newResponse.weight.SetFloat( 1.0f ); - AI_ResponseParams *rp = &group.rp; - - newResponse.type = ComputeResponseType( token ); - if ( RESPONSE_NONE == newResponse.type ) - { - ResponseWarning( "response entry '%s' with unknown response type '%s'\n", responseGroupName, token ); - return; - } - - ParseToken(); - newResponse.value = CopyString( token ); - - while ( TokenWaiting() ) - { - ParseToken(); - if ( !Q_stricmp( token, "weight" ) ) - { - ParseToken(); - newResponse.weight.SetFloat( (float)atof( token ) ); - continue; - } - - if ( !Q_stricmp( token, "nodelay" ) ) - { - ParseToken(); - rp->flags |= AI_ResponseParams::RG_DELAYAFTERSPEAK; - rp->delay.start = 0; - rp->delay.range = 0; - continue; - } - - if ( !Q_stricmp( token, "defaultdelay" ) ) - { - rp->flags |= AI_ResponseParams::RG_DELAYAFTERSPEAK; - rp->delay.start = AIS_DEF_MIN_DELAY; - rp->delay.range = ( AIS_DEF_MAX_DELAY - AIS_DEF_MIN_DELAY ); - continue; - } - - if ( !Q_stricmp( token, "delay" ) ) - { - ParseToken(); - rp->flags |= AI_ResponseParams::RG_DELAYAFTERSPEAK; - rp->delay.FromInterval( ReadInterval( token ) ); - continue; - } - - if ( !Q_stricmp( token, "speakonce" ) ) - { - rp->flags |= AI_ResponseParams::RG_SPEAKONCE; - continue; - } - - if ( !Q_stricmp( token, "noscene" ) ) - { - rp->flags |= AI_ResponseParams::RG_DONT_USE_SCENE; - continue; - } - - if ( !Q_stricmp( token, "stop_on_nonidle" ) ) - { - rp->flags |= AI_ResponseParams::RG_STOP_ON_NONIDLE; - continue; - } - - if ( !Q_stricmp( token, "odds" ) ) - { - ParseToken(); - rp->flags |= AI_ResponseParams::RG_ODDS; - rp->odds = clamp( atoi( token ), 0, 100 ); - continue; - } - - if ( !Q_stricmp( token, "respeakdelay" ) ) - { - ParseToken(); - rp->flags |= AI_ResponseParams::RG_RESPEAKDELAY; - rp->respeakdelay.FromInterval( ReadInterval( token ) ); - continue; - } - - if ( !Q_stricmp( token, "weapondelay" ) ) - { - ParseToken(); - rp->flags |= AI_ResponseParams::RG_WEAPONDELAY; - rp->weapondelay.FromInterval( ReadInterval( token ) ); - continue; - } - - if ( !Q_stricmp( token, "soundlevel" ) ) - { - ParseToken(); - rp->flags |= AI_ResponseParams::RG_SOUNDLEVEL; - rp->soundlevel = (soundlevel_t)TextToSoundLevel( token ); - continue; - } - - if ( !Q_stricmp( token, "displayfirst" ) ) - { - newResponse.first = true; - group.m_bHasFirst = true; - continue; - } - - if ( !Q_stricmp( token, "displaylast" ) ) - { - newResponse.last = true; - group.m_bHasLast= true; - continue; - } - } - - group.group.AddToTail( newResponse ); -} - -//----------------------------------------------------------------------------- -// Purpose: -// Output : Returns true on success, false on failure. -//----------------------------------------------------------------------------- -bool CResponseSystem::IsRootCommand() -{ - if ( !Q_stricmp( token, "#include" ) ) - return true; - if ( !Q_stricmp( token, "response" ) ) - return true; - if ( !Q_stricmp( token, "enumeration" ) ) - return true; - if ( !Q_stricmp( token, "criteria" ) ) - return true; - if ( !Q_stricmp( token, "criterion" ) ) - return true; - if ( !Q_stricmp( token, "rule" ) ) - return true; - return false; -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *kv - -//----------------------------------------------------------------------------- -void CResponseSystem::ParseResponse( void ) -{ - // Should have groupname at start - char responseGroupName[ 128 ]; - - ResponseGroup newGroup; - AI_ResponseParams *rp = &newGroup.rp; - - // Response Group Name - ParseToken(); - Q_strncpy( responseGroupName, token, sizeof( responseGroupName ) ); - - while ( 1 ) - { - ParseToken(); - - // Oops, part of next definition - if( IsRootCommand() ) - { - Unget(); - break; - } - - if ( !Q_stricmp( token, "{" ) ) - { - while ( 1 ) - { - ParseToken(); - if ( !Q_stricmp( token, "}" ) ) - break; - - if ( !Q_stricmp( token, "permitrepeats" ) ) - { - newGroup.m_bDepleteBeforeRepeat = false; - continue; - } - else if ( !Q_stricmp( token, "sequential" ) ) - { - newGroup.SetSequential( true ); - continue; - } - else if ( !Q_stricmp( token, "norepeat" ) ) - { - newGroup.SetNoRepeat( true ); - continue; - } - - ParseOneResponse( responseGroupName, newGroup ); - } - break; - } - - if ( !Q_stricmp( token, "nodelay" ) ) - { - ParseToken(); - rp->flags |= AI_ResponseParams::RG_DELAYAFTERSPEAK; - rp->delay.start = 0; - rp->delay.range = 0; - continue; - } - - if ( !Q_stricmp( token, "defaultdelay" ) ) - { - rp->flags |= AI_ResponseParams::RG_DELAYAFTERSPEAK; - rp->delay.start = AIS_DEF_MIN_DELAY; - rp->delay.range = ( AIS_DEF_MAX_DELAY - AIS_DEF_MIN_DELAY ); - continue; - } - - if ( !Q_stricmp( token, "delay" ) ) - { - ParseToken(); - rp->flags |= AI_ResponseParams::RG_DELAYAFTERSPEAK; - rp->delay.FromInterval( ReadInterval( token ) ); - continue; - } - - if ( !Q_stricmp( token, "speakonce" ) ) - { - rp->flags |= AI_ResponseParams::RG_SPEAKONCE; - continue; - } - - if ( !Q_stricmp( token, "noscene" ) ) - { - rp->flags |= AI_ResponseParams::RG_DONT_USE_SCENE; - continue; - } - - if ( !Q_stricmp( token, "stop_on_nonidle" ) ) - { - rp->flags |= AI_ResponseParams::RG_STOP_ON_NONIDLE; - continue; - } - - if ( !Q_stricmp( token, "odds" ) ) - { - ParseToken(); - rp->flags |= AI_ResponseParams::RG_ODDS; - rp->odds = clamp( atoi( token ), 0, 100 ); - continue; - } - - if ( !Q_stricmp( token, "respeakdelay" ) ) - { - ParseToken(); - rp->flags |= AI_ResponseParams::RG_RESPEAKDELAY; - rp->respeakdelay.FromInterval( ReadInterval( token ) ); - continue; - } - - if ( !Q_stricmp( token, "weapondelay" ) ) - { - ParseToken(); - rp->flags |= AI_ResponseParams::RG_WEAPONDELAY; - rp->weapondelay.FromInterval( ReadInterval( token ) ); - continue; - } - - if ( !Q_stricmp( token, "soundlevel" ) ) - { - ParseToken(); - rp->flags |= AI_ResponseParams::RG_SOUNDLEVEL; - rp->soundlevel = (soundlevel_t)TextToSoundLevel( token ); - continue; - } - - ParseOneResponse( responseGroupName, newGroup ); - } - - m_Responses.Insert( responseGroupName, newGroup ); -} - - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *criterion - -//----------------------------------------------------------------------------- -int CResponseSystem::ParseOneCriterion( const char *criterionName ) -{ - char key[ 128 ]; - char value[ 128 ]; - - Criteria newCriterion; - - bool gotbody = false; - - while ( TokenWaiting() || !gotbody ) - { - ParseToken(); - - // Oops, part of next definition - if( IsRootCommand() ) - { - Unget(); - break; - } - - if ( !Q_stricmp( token, "{" ) ) - { - gotbody = true; - - while ( 1 ) - { - ParseToken(); - if ( !Q_stricmp( token, "}" ) ) - break; - - // Look up subcriteria index - int idx = m_Criteria.Find( token ); - if ( idx != m_Criteria.InvalidIndex() ) - { - newCriterion.subcriteria.AddToTail( idx ); - } - else - { - ResponseWarning( "Skipping unrecongized subcriterion '%s' in '%s'\n", token, criterionName ); - } - } - continue; - } - else if ( !Q_stricmp( token, "required" ) ) - { - newCriterion.required = true; - } - else if ( !Q_stricmp( token, "weight" ) ) - { - ParseToken(); - newCriterion.weight.SetFloat( (float)atof( token ) ); - } - else - { - Assert( newCriterion.subcriteria.Count() == 0 ); - - // Assume it's the math info for a non-subcriteria resposne - Q_strncpy( key, token, sizeof( key ) ); - ParseToken(); - Q_strncpy( value, token, sizeof( value ) ); - - newCriterion.name = CopyString( key ); - newCriterion.value = CopyString( value ); - - gotbody = true; - } - } - - if ( !newCriterion.IsSubCriteriaType() ) - { - ComputeMatcher( &newCriterion, newCriterion.matcher ); - } - - if ( m_Criteria.Find( criterionName ) != m_Criteria.InvalidIndex() ) - { - ResponseWarning( "Multiple definitions for criteria '%s'\n", criterionName ); - return m_Criteria.InvalidIndex(); - } - - int idx = m_Criteria.Insert( criterionName, newCriterion ); - return idx; -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *kv - -//----------------------------------------------------------------------------- -void CResponseSystem::ParseCriterion( void ) -{ - // Should have groupname at start - char criterionName[ 128 ]; - ParseToken(); - Q_strncpy( criterionName, token, sizeof( criterionName ) ); - - ParseOneCriterion( criterionName ); -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *kv - -//----------------------------------------------------------------------------- -void CResponseSystem::ParseEnumeration( void ) -{ - char enumerationName[ 128 ]; - ParseToken(); - Q_strncpy( enumerationName, token, sizeof( enumerationName ) ); - - ParseToken(); - if ( Q_stricmp( token, "{" ) ) - { - ResponseWarning( "Expecting '{' in enumeration '%s', got '%s'\n", enumerationName, token ); - return; - } - - while ( 1 ) - { - ParseToken(); - if ( !Q_stricmp( token, "}" ) ) - break; - - if ( Q_strlen( token ) <= 0 ) - { - ResponseWarning( "Expecting more tokens in enumeration '%s'\n", enumerationName ); - break; - } - - char key[ 128 ]; - - Q_strncpy( key, token, sizeof( key ) ); - ParseToken(); - float value = (float)atof( token ); - - char sz[ 128 ]; - Q_snprintf( sz, sizeof( sz ), "[%s::%s]", enumerationName, key ); - Q_strlower( sz ); - - Enumeration newEnum; - newEnum.value = value; - - if ( m_Enumerations.Find( sz ) == m_Enumerations.InvalidIndex() ) - { - m_Enumerations.Insert( sz, newEnum ); - } - else - { - ResponseWarning( "Ignoring duplication enumeration '%s'\n", sz ); - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *kv - -//----------------------------------------------------------------------------- -void CResponseSystem::ParseRule( void ) -{ - static int instancedCriteria = 0; - - char ruleName[ 128 ]; - ParseToken(); - Q_strncpy( ruleName, token, sizeof( ruleName ) ); - - ParseToken(); - if ( Q_stricmp( token, "{" ) ) - { - ResponseWarning( "Expecting '{' in rule '%s', got '%s'\n", ruleName, token ); - return; - } - - // entries are "criteria", "response" or an in-line criteria to instance - Rule newRule; - - char sz[ 128 ]; - - bool validRule = true; - while ( 1 ) - { - ParseToken(); - if ( !Q_stricmp( token, "}" ) ) - { - break; - } - - if ( Q_strlen( token ) <= 0 ) - { - ResponseWarning( "Expecting more tokens in rule '%s'\n", ruleName ); - break; - } - - if ( !Q_stricmp( token, "matchonce" ) ) - { - newRule.m_bMatchOnce = true; - continue; - } - - if ( !Q_stricmp( token, "applyContext" ) ) - { - ParseToken(); - if ( newRule.GetContext() == NULL ) - { - newRule.SetContext( token ); - } - else - { - CFmtStrN<1024> newContext( "%s,%s", newRule.GetContext(), token ); - newRule.SetContext( newContext ); - } - continue; - } - - if ( !Q_stricmp( token, "response" ) ) - { - // Read them until we run out. - while ( TokenWaiting() ) - { - ParseToken(); - int idx = m_Responses.Find( token ); - if ( idx != m_Responses.InvalidIndex() ) - { - MEM_ALLOC_CREDIT(); - newRule.m_Responses.AddToTail( idx ); - } - else - { - validRule = false; - ResponseWarning( "No such response '%s' for rule '%s'\n", token, ruleName ); - } - } - continue; - } - - if ( !Q_stricmp( token, "criteria" ) || - !Q_stricmp( token, "criterion" ) ) - { - // Read them until we run out. - while ( TokenWaiting() ) - { - ParseToken(); - - int idx = m_Criteria.Find( token ); - if ( idx != m_Criteria.InvalidIndex() ) - { - MEM_ALLOC_CREDIT(); - newRule.m_Criteria.AddToTail( idx ); - } - else - { - validRule = false; - ResponseWarning( "No such criterion '%s' for rule '%s'\n", token, ruleName ); - } - } - continue; - } - - // It's an inline criteria, generate a name and parse it in - Q_snprintf( sz, sizeof( sz ), "[%s%03i]", ruleName, ++instancedCriteria ); - Unget(); - int idx = ParseOneCriterion( sz ); - if ( idx != m_Criteria.InvalidIndex() ) - { - newRule.m_Criteria.AddToTail( idx ); - } - } - - if ( validRule ) - { - m_Rules.Insert( ruleName, newRule ); - } - else - { - DevMsg( "Discarded rule %s\n", ruleName ); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -int CResponseSystem::GetCurrentToken() const -{ - if ( m_ScriptStack.Count() <= 0 ) - return -1; - - return m_ScriptStack[ 0 ].tokencount; -} - - -void CResponseSystem::ResponseWarning( const char *fmt, ... ) -{ - va_list argptr; -#ifndef _XBOX - static char string[1024]; -#else - char string[1024]; -#endif - - va_start (argptr, fmt); - Q_vsnprintf(string, sizeof(string), fmt,argptr); - va_end (argptr); - - char cur[ 256 ]; - GetCurrentScript( cur, sizeof( cur ) ); - DevMsg( 1, "%s(token %i) : %s", cur, GetCurrentToken(), string ); -} - - -//----------------------------------------------------------------------------- -// Purpose: A special purpose response system associated with a custom entity -//----------------------------------------------------------------------------- -class CInstancedResponseSystem : public CResponseSystem -{ - typedef CResponseSystem BaseClass; - -public: - CInstancedResponseSystem( const char *scriptfile ) : - m_pszScriptFile( 0 ) - { - Assert( scriptfile ); - - int len = Q_strlen( scriptfile ) + 1; - m_pszScriptFile = new char[ len ]; - Assert( m_pszScriptFile ); - Q_strncpy( m_pszScriptFile, scriptfile, len ); - } - - ~CInstancedResponseSystem() - { - delete[] m_pszScriptFile; - } - virtual const char *GetScriptFile( void ) - { - Assert( m_pszScriptFile ); - return m_pszScriptFile; - } - - // CAutoGameSystem - virtual bool Init() - { - const char *basescript = GetScriptFile(); - LoadRuleSet( basescript ); - return true; - } - - virtual void LevelInitPostEntity() - { - ResetResponseGroups(); - } - - virtual void Release() - { - Clear(); - delete this; - } -private: - - char *m_pszScriptFile; -}; - -//----------------------------------------------------------------------------- -// Purpose: The default response system for expressive AIs -//----------------------------------------------------------------------------- -class CDefaultResponseSystem : public CResponseSystem, public CAutoGameSystem -{ - typedef CAutoGameSystem BaseClass; - -public: - CDefaultResponseSystem() : CAutoGameSystem( "CDefaultResponseSystem" ) - { - } - - virtual const char *GetScriptFile( void ) - { - return "scripts/talker/response_rules.txt"; - } - - // CAutoServerSystem - virtual bool Init(); - virtual void Shutdown(); - - virtual void LevelInitPostEntity() - { - } - - virtual void Release() - { - Assert( 0 ); - } - - void AddInstancedResponseSystem( const char *scriptfile, CInstancedResponseSystem *sys ) - { - m_InstancedSystems.Insert( scriptfile, sys ); - } - - CInstancedResponseSystem *FindResponseSystem( const char *scriptfile ) - { - int idx = m_InstancedSystems.Find( scriptfile ); - if ( idx == m_InstancedSystems.InvalidIndex() ) - return NULL; - return m_InstancedSystems[ idx ]; - } - - IResponseSystem *PrecacheCustomResponseSystem( const char *scriptfile ) - { - CInstancedResponseSystem *sys = ( CInstancedResponseSystem * )FindResponseSystem( scriptfile ); - if ( !sys ) - { - sys = new CInstancedResponseSystem( scriptfile ); - if ( !sys ) - { - Error( "Failed to load response system data from %s", scriptfile ); - } - - if ( !sys->Init() ) - { - Error( "CInstancedResponseSystem: Failed to init response system from %s!", scriptfile ); - } - - AddInstancedResponseSystem( scriptfile, sys ); - } - - sys->Precache(); - - return ( IResponseSystem * )sys; - } - - virtual void LevelInitPreEntity() - { - // This will precache the default system - // All user installed systems are init'd by PrecacheCustomResponseSystem which will call sys->Precache() on the ones being used - - // FIXME: This is SLOW the first time you run the engine (can take 3 - 10 seconds!!!) - if ( ShouldPrecache() ) - { - Precache(); - } - - ResetResponseGroups(); - } - - void ReloadAllResponseSystems() - { - Clear(); - Init(); - - int c = m_InstancedSystems.Count(); - for ( int i = c - 1 ; i >= 0; i-- ) - { - CInstancedResponseSystem *sys = m_InstancedSystems[ i ]; - sys->Clear(); - sys->Init(); - } - - } - -private: - - void ClearInstanced() - { - int c = m_InstancedSystems.Count(); - for ( int i = c - 1 ; i >= 0; i-- ) - { - CInstancedResponseSystem *sys = m_InstancedSystems[ i ]; - sys->Release(); - } - m_InstancedSystems.RemoveAll(); - } - - CUtlDict< CInstancedResponseSystem *, int > m_InstancedSystems; -}; - -static CDefaultResponseSystem defaultresponsesytem; -IResponseSystem *g_pResponseSystem = &defaultresponsesytem; - -CON_COMMAND( rr_reloadresponsesystems, "Reload all response system scripts." ) -{ - defaultresponsesytem.ReloadAllResponseSystems(); -} - -static short RESPONSESYSTEM_SAVE_RESTORE_VERSION = 1; - -// note: this won't save/restore settings from instanced response systems. Could add that with a CDefSaveRestoreOps implementation if needed -// -class CDefaultResponseSystemSaveRestoreBlockHandler : public CDefSaveRestoreBlockHandler -{ -public: - const char *GetBlockName() - { - return "ResponseSystem"; - } - - void WriteSaveHeaders( ISave *pSave ) - { - pSave->WriteShort( &RESPONSESYSTEM_SAVE_RESTORE_VERSION ); - } - - void ReadRestoreHeaders( IRestore *pRestore ) - { - // No reason why any future version shouldn't try to retain backward compatability. The default here is to not do so. - short version; - pRestore->ReadShort( &version ); - m_fDoLoad = ( version == RESPONSESYSTEM_SAVE_RESTORE_VERSION ); - } - - void Save( ISave *pSave ) - { - CDefaultResponseSystem& rs = defaultresponsesytem; - - int count = rs.m_Responses.Count(); - pSave->WriteInt( &count ); - for ( int i = 0; i < count; ++i ) - { - pSave->StartBlock( "ResponseGroup" ); - - pSave->WriteString( rs.m_Responses.GetElementName( i ) ); - const ResponseGroup *group = &rs.m_Responses[ i ]; - pSave->WriteAll( group ); - - short groupCount = group->group.Count(); - pSave->WriteShort( &groupCount ); - for ( int j = 0; j < groupCount; ++j ) - { - const Response *response = &group->group[ j ]; - pSave->StartBlock( "Response" ); - pSave->WriteString( response->value ); - pSave->WriteAll( response ); - pSave->EndBlock(); - } - - pSave->EndBlock(); - } - } - - void Restore( IRestore *pRestore, bool createPlayers ) - { - if ( !m_fDoLoad ) - return; - - CDefaultResponseSystem& rs = defaultresponsesytem; - - int count = pRestore->ReadInt(); - for ( int i = 0; i < count; ++i ) - { - char szResponseGroupBlockName[SIZE_BLOCK_NAME_BUF]; - pRestore->StartBlock( szResponseGroupBlockName ); - if ( !Q_stricmp( szResponseGroupBlockName, "ResponseGroup" ) ) - { - - char groupname[ 256 ]; - pRestore->ReadString( groupname, sizeof( groupname ), 0 ); - - // Try and find it - int idx = rs.m_Responses.Find( groupname ); - if ( idx != rs.m_Responses.InvalidIndex() ) - { - ResponseGroup *group = &rs.m_Responses[ idx ]; - pRestore->ReadAll( group ); - - short groupCount = pRestore->ReadShort(); - for ( int j = 0; j < groupCount; ++j ) - { - char szResponseBlockName[SIZE_BLOCK_NAME_BUF]; - - char responsename[ 256 ]; - pRestore->StartBlock( szResponseBlockName ); - if ( !Q_stricmp( szResponseBlockName, "Response" ) ) - { - pRestore->ReadString( responsename, sizeof( responsename ), 0 ); - - // Find it by name - int ri; - for ( ri = 0; ri < group->group.Count(); ++ri ) - { - Response *response = &group->group[ ri ]; - if ( !Q_stricmp( response->value, responsename ) ) - { - break; - } - } - - if ( ri < group->group.Count() ) - { - Response *response = &group->group[ ri ]; - pRestore->ReadAll( response ); - } - } - - pRestore->EndBlock(); - } - } - } - - pRestore->EndBlock(); - } - } -private: - - bool m_fDoLoad; - -} g_DefaultResponseSystemSaveRestoreBlockHandler; - -ISaveRestoreBlockHandler *GetDefaultResponseSystemSaveRestoreBlockHandler() -{ - return &g_DefaultResponseSystemSaveRestoreBlockHandler; -} - -//----------------------------------------------------------------------------- -// CResponseSystemSaveRestoreOps -// -// Purpose: Handles save and load for instanced response systems... -// -// BUGBUG: This will save the same response system to file multiple times for "shared" response systems and -// therefore it'll restore the same data onto the same pointer N times on reload (probably benign for now, but we could -// write code to save/restore the instanced ones by filename in the block handler above maybe? -//----------------------------------------------------------------------------- - -class CResponseSystemSaveRestoreOps : public CDefSaveRestoreOps -{ -public: - - virtual void Save( const SaveRestoreFieldInfo_t &fieldInfo, ISave *pSave ) - { - CResponseSystem *pRS = *(CResponseSystem **)fieldInfo.pField; - if ( !pRS || pRS == &defaultresponsesytem ) - return; - - int count = pRS->m_Responses.Count(); - pSave->WriteInt( &count ); - for ( int i = 0; i < count; ++i ) - { - pSave->StartBlock( "ResponseGroup" ); - - pSave->WriteString( pRS->m_Responses.GetElementName( i ) ); - const ResponseGroup *group = &pRS->m_Responses[ i ]; - pSave->WriteAll( group ); - - short groupCount = group->group.Count(); - pSave->WriteShort( &groupCount ); - for ( int j = 0; j < groupCount; ++j ) - { - const Response *response = &group->group[ j ]; - pSave->StartBlock( "Response" ); - pSave->WriteString( response->value ); - pSave->WriteAll( response ); - pSave->EndBlock(); - } - - pSave->EndBlock(); - } - } - - virtual void Restore( const SaveRestoreFieldInfo_t &fieldInfo, IRestore *pRestore ) - { - CResponseSystem *pRS = *(CResponseSystem **)fieldInfo.pField; - if ( !pRS || pRS == &defaultresponsesytem ) - return; - - int count = pRestore->ReadInt(); - for ( int i = 0; i < count; ++i ) - { - char szResponseGroupBlockName[SIZE_BLOCK_NAME_BUF]; - pRestore->StartBlock( szResponseGroupBlockName ); - if ( !Q_stricmp( szResponseGroupBlockName, "ResponseGroup" ) ) - { - - char groupname[ 256 ]; - pRestore->ReadString( groupname, sizeof( groupname ), 0 ); - - // Try and find it - int idx = pRS->m_Responses.Find( groupname ); - if ( idx != pRS->m_Responses.InvalidIndex() ) - { - ResponseGroup *group = &pRS->m_Responses[ idx ]; - pRestore->ReadAll( group ); - - short groupCount = pRestore->ReadShort(); - for ( int j = 0; j < groupCount; ++j ) - { - char szResponseBlockName[SIZE_BLOCK_NAME_BUF]; - - char responsename[ 256 ]; - pRestore->StartBlock( szResponseBlockName ); - if ( !Q_stricmp( szResponseBlockName, "Response" ) ) - { - pRestore->ReadString( responsename, sizeof( responsename ), 0 ); - - // Find it by name - int ri; - for ( ri = 0; ri < group->group.Count(); ++ri ) - { - Response *response = &group->group[ ri ]; - if ( !Q_stricmp( response->value, responsename ) ) - { - break; - } - } - - if ( ri < group->group.Count() ) - { - Response *response = &group->group[ ri ]; - pRestore->ReadAll( response ); - } - } - - pRestore->EndBlock(); - } - } - } - - pRestore->EndBlock(); - } - } - -} g_ResponseSystemSaveRestoreOps; - -ISaveRestoreOps *responseSystemSaveRestoreOps = &g_ResponseSystemSaveRestoreOps; - -//----------------------------------------------------------------------------- -// Purpose: -// Output : Returns true on success, false on failure. -//----------------------------------------------------------------------------- -bool CDefaultResponseSystem::Init() -{ -/* - Warning( "sizeof( Response ) == %d\n", sizeof( Response ) ); - Warning( "sizeof( ResponseGroup ) == %d\n", sizeof( ResponseGroup ) ); - Warning( "sizeof( Criteria ) == %d\n", sizeof( Criteria ) ); - Warning( "sizeof( AI_ResponseParams ) == %d\n", sizeof( AI_ResponseParams ) ); -*/ - const char *basescript = GetScriptFile(); - - LoadRuleSet( basescript ); - - return true; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CDefaultResponseSystem::Shutdown() -{ - // Wipe instanced versions - ClearInstanced(); - - // Clear outselves - Clear(); - // IServerSystem chain - BaseClass::Shutdown(); -} - -//----------------------------------------------------------------------------- -// Purpose: Instance a custom response system -// Input : *scriptfile - -// Output : IResponseSystem -//----------------------------------------------------------------------------- -IResponseSystem *PrecacheCustomResponseSystem( const char *scriptfile ) -{ - return defaultresponsesytem.PrecacheCustomResponseSystem( scriptfile ); -} - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -void CResponseSystem::DumpRules() -{ - int c = m_Rules.Count(); - int i; - - for ( i = 0; i < c; i++ ) - { - Msg("%s\n", m_Rules.GetElementName( i ) ); - } -} \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + + +#include "cbase.h" +#include "SoundEmitterSystem/isoundemittersystembase.h" +#include "AI_ResponseSystem.h" +#include "igamesystem.h" +#include "AI_Criteria.h" +#include +#include "filesystem.h" +#include "utldict.h" +#include "ai_speech.h" +#include "vstdlib/ICommandLine.h" +#include +#include "sceneentity.h" +#include "isaverestore.h" +#include "utlbuffer.h" +#include "stringpool.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +ConVar rr_debugresponses( "rr_debugresponses", "0", FCVAR_NONE, "Show verbose matching output (1 for simple, 2 for rule scoring). If set to 3, it will only show response success/failure for npc_selected NPCs." ); +ConVar rr_debugrule( "rr_debugrule", "", FCVAR_NONE, "If set to the name of the rule, that rule's score will be shown whenever a concept is passed into the response rules system."); +ConVar rr_dumpresponses( "rr_dumpresponses", "0", FCVAR_NONE, "Dump all response_rules.txt and rules (requires restart)" ); + +static CUtlSymbolTable g_RS; + +inline static char *CopyString( const char *in ) +{ + if ( !in ) + return NULL; + + int len = Q_strlen( in ); + char *out = new char[ len + 1 ]; + Q_memcpy( out, in, len ); + out[ len ] = 0; + return out; +} + +#pragma pack(1) +class Matcher +{ +public: + Matcher() + { + valid = false; + isnumeric = false; + notequal = false; + usemin = false; + minequals = false; + usemax = false; + maxequals = false; + maxval = 0.0f; + minval = 0.0f; + + token = UTL_INVAL_SYMBOL; + rawtoken = UTL_INVAL_SYMBOL; + } + + void Describe( void ) + { + if ( !valid ) + { + DevMsg( " invalid!\n" ); + return; + } + char sz[ 128 ]; + + sz[ 0] = 0; + int minmaxcount = 0; + if ( usemin ) + { + Q_snprintf( sz, sizeof( sz ), ">%s%.3f", minequals ? "=" : "", minval ); + minmaxcount++; + } + if ( usemax ) + { + char sz2[ 128 ]; + Q_snprintf( sz2, sizeof( sz2 ), "<%s%.3f", maxequals ? "=" : "", maxval ); + + if ( minmaxcount > 0 ) + { + Q_strncat( sz, " and ", sizeof( sz ), COPY_ALL_CHARACTERS ); + } + Q_strncat( sz, sz2, sizeof( sz ), COPY_ALL_CHARACTERS ); + minmaxcount++; + } + + if ( minmaxcount >= 1 ) + { + DevMsg( " matcher: %s\n", sz ); + return; + } + + if ( notequal ) + { + DevMsg( " matcher: !=%s\n", GetToken() ); + return; + } + + DevMsg( " matcher: ==%s\n", GetToken() ); + } + + float maxval; + float minval; + + bool valid : 1; //1 + bool isnumeric : 1; //2 + bool notequal : 1; //3 + bool usemin : 1; //4 + bool minequals : 1; //5 + bool usemax : 1; //6 + bool maxequals : 1; //7 + + void SetToken( char const *s ) + { + token = g_RS.AddString( s ); + } + + char const *GetToken() + { + if ( token.IsValid() ) + { + return g_RS.String( token ); + } + return ""; + } + void SetRaw( char const *raw ) + { + rawtoken = g_RS.AddString( raw ); + } + char const *GetRaw() + { + if ( rawtoken.IsValid() ) + { + return g_RS.String( rawtoken ); + } + return ""; + } + +private: + CUtlSymbol token; + CUtlSymbol rawtoken; +}; + +struct Response +{ + DECLARE_SIMPLE_DATADESC(); + + Response() + { + type = RESPONSE_NONE; + value = NULL; + weight.SetFloat( 1.0f ); + depletioncount = 0; + first = false; + last = false; + } + + Response( const Response& src ) + { + weight = src.weight; + type = src.type; + value = CopyString( src.value ); + depletioncount = src.depletioncount; + first = src.first; + last = src.last; + } + + Response& operator =( const Response& src ) + { + if ( this == &src ) + return *this; + weight = src.weight; + type = src.type; + value = CopyString( src.value ); + depletioncount = src.depletioncount; + first = src.first; + last = src.last; + return *this; + } + + ~Response() + { + delete[] value; + } + + ResponseType_t GetType() { return (ResponseType_t)type; } + + char *value; // fixed up value spot // 4 + float16 weight; // 6 + + byte depletioncount; // 7 + byte type : 6; // 8 + byte first : 1; // + byte last : 1; // +}; + +struct ResponseGroup +{ + DECLARE_SIMPLE_DATADESC(); + + ResponseGroup() + { + // By default visit all nodes before repeating + m_bSequential = false; + m_bNoRepeat = false; + m_bEnabled = true; + m_nCurrentIndex = 0; + m_bDepleteBeforeRepeat = true; + m_nDepletionCount = 1; + m_bHasFirst = false; + m_bHasLast = false; + } + + ResponseGroup( const ResponseGroup& src ) + { + int c = src.group.Count(); + for ( int i = 0; i < c; i++ ) + { + group.AddToTail( src.group[ i ] ); + } + + rp = src.rp; + m_bDepleteBeforeRepeat = src.m_bDepleteBeforeRepeat; + m_nDepletionCount = src.m_nDepletionCount; + m_bHasFirst = src.m_bHasFirst; + m_bHasLast = src.m_bHasLast; + m_bSequential = src.m_bSequential; + m_bNoRepeat = src.m_bNoRepeat; + m_bEnabled = src.m_bEnabled; + m_nCurrentIndex = src.m_nCurrentIndex; + } + + ResponseGroup& operator=( const ResponseGroup& src ) + { + if ( this == &src ) + return *this; + int c = src.group.Count(); + for ( int i = 0; i < c; i++ ) + { + group.AddToTail( src.group[ i ] ); + } + + rp = src.rp; + m_bDepleteBeforeRepeat = src.m_bDepleteBeforeRepeat; + m_nDepletionCount = src.m_nDepletionCount; + m_bHasFirst = src.m_bHasFirst; + m_bHasLast = src.m_bHasLast; + m_bSequential = src.m_bSequential; + m_bNoRepeat = src.m_bNoRepeat; + m_bEnabled = src.m_bEnabled; + m_nCurrentIndex = src.m_nCurrentIndex; + return *this; + } + + bool HasUndepletedChoices() const + { + if ( !m_bDepleteBeforeRepeat ) + return true; + + int c = group.Count(); + for ( int i = 0; i < c; i++ ) + { + if ( group[ i ].depletioncount != m_nDepletionCount ) + return true; + } + + return false; + } + + void MarkResponseUsed( int idx ) + { + if ( !m_bDepleteBeforeRepeat ) + return; + + if ( idx < 0 || idx >= group.Count() ) + { + Assert( 0 ); + return; + } + + group[ idx ].depletioncount = m_nDepletionCount; + } + + void ResetDepletionCount() + { + if ( !m_bDepleteBeforeRepeat ) + return; + ++m_nDepletionCount; + } + + void Reset() + { + ResetDepletionCount(); + SetEnabled( true ); + SetCurrentIndex( 0 ); + m_nDepletionCount = 1; + + for ( int i = 0; i < group.Count(); ++i ) + { + group[ i ].depletioncount = 0; + } + } + + bool HasUndepletedFirst( int& index ) + { + index = -1; + + if ( !m_bDepleteBeforeRepeat ) + return false; + + int c = group.Count(); + for ( int i = 0; i < c; i++ ) + { + Response *r = &group[ i ]; + + if ( ( r->depletioncount != m_nDepletionCount ) && r->first ) + { + index = i; + return true; + } + } + + return false; + } + + bool HasUndepletedLast( int& index ) + { + index = -1; + + if ( !m_bDepleteBeforeRepeat ) + return false; + + int c = group.Count(); + for ( int i = 0; i < c; i++ ) + { + Response *r = &group[ i ]; + + if ( ( r->depletioncount != m_nDepletionCount ) && r->last ) + { + index = i; + return true; + } + } + + return false; + } + + bool ShouldCheckRepeats() const { return m_bDepleteBeforeRepeat; } + int GetDepletionCount() const { return m_nDepletionCount; } + + bool IsSequential() const { return m_bSequential; } + void SetSequential( bool seq ) { m_bSequential = seq; } + + bool IsNoRepeat() const { return m_bNoRepeat; } + void SetNoRepeat( bool norepeat ) { m_bNoRepeat = norepeat; } + + bool IsEnabled() const { return m_bEnabled; } + void SetEnabled( bool enabled ) { m_bEnabled = enabled; } + + int GetCurrentIndex() const { return m_nCurrentIndex; } + void SetCurrentIndex( byte idx ) { m_nCurrentIndex = idx; } + + CUtlVector< Response > group; + + AI_ResponseParams rp; + + bool m_bEnabled; + + byte m_nCurrentIndex; + // Invalidation counter + byte m_nDepletionCount; + + // Use all slots before repeating any + bool m_bDepleteBeforeRepeat : 1; + bool m_bHasFirst : 1; + bool m_bHasLast : 1; + bool m_bSequential : 1; + bool m_bNoRepeat : 1; + +}; + +struct Criteria +{ + Criteria() + { + name = NULL; + value = NULL; + weight.SetFloat( 1.0f ); + required = false; + } + Criteria& operator =(const Criteria& src ) + { + if ( this == &src ) + return *this; + + name = CopyString( src.name ); + value = CopyString( src.value ); + weight = src.weight; + required = src.required; + + matcher = src.matcher; + + int c = src.subcriteria.Count(); + for ( int i = 0; i < c; i++ ) + { + subcriteria.AddToTail( src.subcriteria[ i ] ); + } + + return *this; + } + Criteria(const Criteria& src ) + { + name = CopyString( src.name ); + value = CopyString( src.value ); + weight = src.weight; + required = src.required; + + matcher = src.matcher; + + int c = src.subcriteria.Count(); + for ( int i = 0; i < c; i++ ) + { + subcriteria.AddToTail( src.subcriteria[ i ] ); + } + } + ~Criteria() + { + delete[] name; + delete[] value; + } + + bool IsSubCriteriaType() const + { + return ( subcriteria.Count() > 0 ) ? true : false; + } + + char *name; + char *value; + float16 weight; + bool required; + + Matcher matcher; + + // Indices into sub criteria + CUtlVector< unsigned short > subcriteria; +}; + +struct Rule +{ + Rule() + { + m_bMatchOnce = false; + m_bEnabled = true; + m_szContext = NULL; + } + + Rule& operator =( const Rule& src ) + { + if ( this == &src ) + return *this; + + int i; + int c; + + c = src.m_Criteria.Count(); + for ( i = 0; i < c; i++ ) + { + m_Criteria.AddToTail( src.m_Criteria[ i ] ); + } + + c = src.m_Responses.Count(); + for ( i = 0; i < c; i++ ) + { + m_Responses.AddToTail( src.m_Responses[ i ] ); + } + + SetContext( src.m_szContext ); + m_bMatchOnce = src.m_bMatchOnce; + m_bEnabled = src.m_bEnabled; + return *this; + } + + Rule( const Rule& src ) + { + int i; + int c; + + c = src.m_Criteria.Count(); + for ( i = 0; i < c; i++ ) + { + m_Criteria.AddToTail( src.m_Criteria[ i ] ); + } + + c = src.m_Responses.Count(); + for ( i = 0; i < c; i++ ) + { + m_Responses.AddToTail( src.m_Responses[ i ] ); + } + + SetContext( src.m_szContext ); + m_bMatchOnce = src.m_bMatchOnce; + m_bEnabled = src.m_bEnabled; + } + + ~Rule() + { + delete[] m_szContext; + } + + void SetContext( const char *context ) + { + delete[] m_szContext; + m_szContext = CopyString( context ); + } + + const char *GetContext( void ) const { return m_szContext; } + + bool IsEnabled() const { return m_bEnabled; } + void Disable() { m_bEnabled = false; } + bool IsMatchOnce() const { return m_bMatchOnce; } + + // Indices into underlying criteria and response dictionaries + CUtlVector< unsigned short > m_Criteria; + CUtlVector< unsigned short> m_Responses; + + char *m_szContext; + + bool m_bMatchOnce : 1; + bool m_bEnabled : 1; +}; +#pragma pack() + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +abstract_class CResponseSystem : public IResponseSystem +{ +public: + CResponseSystem(); + ~CResponseSystem(); + + // IResponseSystem + virtual bool FindBestResponse( const AI_CriteriaSet& set, AI_Response& response, IResponseFilter *pFilter = NULL ); + virtual void GetAllResponses( CUtlVector *pResponses ); + + virtual void Release() = 0; + + virtual void DumpRules(); + + virtual void Precache(); + + virtual void PrecacheResponses( bool bEnable ) + { + m_bPrecache = bEnable; + } + + bool ShouldPrecache() { return m_bPrecache; } + + void Clear(); + +protected: + + virtual const char *GetScriptFile( void ) = 0; + void LoadRuleSet( const char *setname ); + + void ResetResponseGroups(); +public: + + + + +private: + + struct Enumeration + { + float value; + }; + + struct ResponseSearchResult + { + ResponseSearchResult() + { + group = NULL; + action = NULL; + } + + ResponseGroup *group; + Response *action; + }; + + inline bool ParseToken( void ) + { + if ( m_bUnget ) + { + m_bUnget = false; + return true; + } + if ( m_ScriptStack.Count() <= 0 ) + { + Assert( 0 ); + return false; + } + + m_ScriptStack[ 0 ].currenttoken = engine->ParseFile( m_ScriptStack[ 0 ].currenttoken, token, sizeof( token ) ); + m_ScriptStack[ 0 ].tokencount++; + return m_ScriptStack[ 0 ].currenttoken != NULL ? true : false; + } + + inline void Unget() + { + m_bUnget = true; + } + + inline bool TokenWaiting( void ) + { + if ( m_ScriptStack.Count() <= 0 ) + { + Assert( 0 ); + return false; + } + + const char *p = m_ScriptStack[ 0 ].currenttoken; + + if ( !p ) + { + Error( "AI_ResponseSystem: Unxpected TokenWaiting() with NULL buffer in %s", m_ScriptStack[ 0 ].name ); + return false; + } + + + while ( *p && *p!='\n') + { + // Special handler for // comment blocks + if ( *p == '/' && *(p+1) == '/' ) + return false; + + if ( !isspace( *p ) || isalnum( *p ) ) + return true; + + p++; + } + + return false; + } + + void ParseOneResponse( const char *responseGroupName, ResponseGroup& group ); + + void ParseInclude( CStringPool &includedFiles ); + void ParseResponse( void ); + void ParseCriterion( void ); + void ParseRule( void ); + void ParseEnumeration( void ); + + int ParseOneCriterion( const char *criterionName ); + + bool Compare( const char *setValue, Criteria *c, bool verbose = false ); + bool CompareUsingMatcher( const char *setValue, Matcher& m, bool verbose = false ); + void ComputeMatcher( Criteria *c, Matcher& matcher ); + void ResolveToken( Matcher& matcher, char *token, size_t bufsize, char const *rawtoken ); + float LookupEnumeration( const char *name, bool& found ); + + int FindBestMatchingRule( const AI_CriteriaSet& set, bool verbose ); + + float ScoreCriteriaAgainstRule( const AI_CriteriaSet& set, int irule, bool verbose = false ); + float RecursiveScoreSubcriteriaAgainstRule( const AI_CriteriaSet& set, Criteria *parent, bool& exclude, bool verbose /*=false*/ ); + float ScoreCriteriaAgainstRuleCriteria( const AI_CriteriaSet& set, int icriterion, bool& exclude, bool verbose = false ); + bool GetBestResponse( ResponseSearchResult& result, Rule *rule, bool verbose = false, IResponseFilter *pFilter = NULL ); + bool ResolveResponse( ResponseSearchResult& result, int depth, const char *name, bool verbose = false, IResponseFilter *pFilter = NULL ); + int SelectWeightedResponseFromResponseGroup( ResponseGroup *g, IResponseFilter *pFilter ); + void DescribeResponseGroup( ResponseGroup *group, int selected, int depth ); + void DebugPrint( int depth, const char *fmt, ... ); + + void LoadFromBuffer( const char *scriptfile, const char *buffer, CStringPool &includedFiles ); + +// void TouchReferencedScenes(); + + void GetCurrentScript( char *buf, size_t buflen ); + int GetCurrentToken() const; + void SetCurrentScript( const char *script ); + bool IsRootCommand(); + + void PushScript( const char *scriptfile, unsigned char *buffer ); + void PopScript(void); + + void ResponseWarning( const char *fmt, ... ); + + CUtlDict< ResponseGroup, short > m_Responses; + CUtlDict< Criteria, short > m_Criteria; + CUtlDict< Rule, short > m_Rules; + CUtlDict< Enumeration, short > m_Enumerations; + + char token[ 1204 ]; + + bool m_bUnget; + bool m_bPrecache; + + struct ScriptEntry + { + unsigned char *buffer; + FileNameHandle_t name; + const char *currenttoken; + int tokencount; + }; + + CUtlVector< ScriptEntry > m_ScriptStack; + + friend class CDefaultResponseSystemSaveRestoreBlockHandler; + friend class CResponseSystemSaveRestoreOps; +}; + +BEGIN_SIMPLE_DATADESC( Response ) + // DEFINE_FIELD( type, FIELD_INTEGER ), + // DEFINE_ARRAY( value, FIELD_CHARACTER ), + // DEFINE_FIELD( weight, FIELD_FLOAT ), + DEFINE_FIELD( depletioncount, FIELD_CHARACTER ), + // DEFINE_FIELD( first, FIELD_BOOLEAN ), + // DEFINE_FIELD( last, FIELD_BOOLEAN ), +END_DATADESC() + +BEGIN_SIMPLE_DATADESC( ResponseGroup ) + // DEFINE_FIELD( group, FIELD_UTLVECTOR ), + // DEFINE_FIELD( rp, FIELD_EMBEDDED ), + // DEFINE_FIELD( m_bDepleteBeforeRepeat, FIELD_BOOLEAN ), + DEFINE_FIELD( m_nDepletionCount, FIELD_CHARACTER ), + // DEFINE_FIELD( m_bHasFirst, FIELD_BOOLEAN ), + // DEFINE_FIELD( m_bHasLast, FIELD_BOOLEAN ), + // DEFINE_FIELD( m_bSequential, FIELD_BOOLEAN ), + // DEFINE_FIELD( m_bNoRepeat, FIELD_BOOLEAN ), + DEFINE_FIELD( m_bEnabled, FIELD_BOOLEAN ), + DEFINE_FIELD( m_nCurrentIndex, FIELD_CHARACTER ), +END_DATADESC() + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CResponseSystem::CResponseSystem() +{ + token[0] = 0; + m_bUnget = false; + m_bPrecache = true; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CResponseSystem::~CResponseSystem() +{ +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : char const +//----------------------------------------------------------------------------- +void CResponseSystem::GetCurrentScript( char *buf, size_t buflen ) +{ + Assert( buf ); + buf[ 0 ] = 0; + if ( m_ScriptStack.Count() <= 0 ) + return; + + if ( filesystem->String( m_ScriptStack[ 0 ].name, buf, buflen ) ) + { + return; + } + buf[ 0 ] = 0; +} + +void CResponseSystem::PushScript( const char *scriptfile, unsigned char *buffer ) +{ + ScriptEntry e; + e.name = filesystem->FindOrAddFileName( scriptfile ); + e.buffer = buffer; + e.currenttoken = (char *)e.buffer; + e.tokencount = 0; + m_ScriptStack.AddToHead( e ); +} + +void CResponseSystem::PopScript(void) +{ + Assert( m_ScriptStack.Count() >= 1 ); + if ( m_ScriptStack.Count() <= 0 ) + return; + + m_ScriptStack.Remove( 0 ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CResponseSystem::Clear() +{ + m_Responses.RemoveAll(); + m_Criteria.RemoveAll(); + m_Rules.RemoveAll(); + m_Enumerations.RemoveAll(); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *name - +// found - +// Output : float +//----------------------------------------------------------------------------- +float CResponseSystem::LookupEnumeration( const char *name, bool& found ) +{ + int idx = m_Enumerations.Find( name ); + if ( idx == m_Enumerations.InvalidIndex() ) + { + found = false; + return 0.0f; + } + + + found = true; + return m_Enumerations[ idx ].value; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : matcher - +//----------------------------------------------------------------------------- +void CResponseSystem::ResolveToken( Matcher& matcher, char *token, size_t bufsize, char const *rawtoken ) +{ + if ( rawtoken[0] != '[' ) + { + Q_strncpy( token, rawtoken, bufsize ); + return; + } + + // Now lookup enumeration + bool found = false; + float f = LookupEnumeration( rawtoken, found ); + if ( !found ) + { + Q_strncpy( token, rawtoken, bufsize ); + ResponseWarning( "No such enumeration '%s'\n", token ); + return; + } + + Q_snprintf( token, bufsize, "%f", f ); +} + + +static bool AppearsToBeANumber( char const *token ) +{ + if ( atof( token ) != 0.0f ) + return true; + + char const *p = token; + while ( *p ) + { + if ( *p != '0' ) + return false; + + p++; + } + + return true; +} + +void CResponseSystem::ComputeMatcher( Criteria *c, Matcher& matcher ) +{ + const char *s = c->value; + if ( !s ) + { + matcher.valid = false; + return; + } + + const char *in = s; + + char token[ 128 ]; + char rawtoken[ 128 ]; + + token[ 0 ] = 0; + rawtoken[ 0 ] = 0; + + int n = 0; + + bool gt = false; + bool lt = false; + bool eq = false; + bool nt = false; + + bool done = false; + while ( !done ) + { + switch( *in ) + { + case '>': + { + gt = true; + Assert( !lt ); // Can't be both + } + break; + case '<': + { + lt = true; + Assert( !gt ); // Can't be both + } + break; + case '=': + { + eq = true; + } + break; + case ',': + case '\0': + { + rawtoken[ n ] = 0; + n = 0; + + // Convert raw token to real token in case token is an enumerated type specifier + ResolveToken( matcher, token, sizeof( token ), rawtoken ); + + // Fill in first data set + if ( gt ) + { + matcher.usemin = true; + matcher.minequals = eq; + matcher.minval = (float)atof( token ); + + matcher.isnumeric = true; + } + else if ( lt ) + { + matcher.usemax = true; + matcher.maxequals = eq; + matcher.maxval = (float)atof( token ); + + matcher.isnumeric = true; + } + else + { + if ( *in == ',' ) + { + // If there's a comma, this better have been a less than or a gt key + Assert( 0 ); + } + + matcher.notequal = nt; + + matcher.isnumeric = AppearsToBeANumber( token ); + } + + gt = lt = eq = nt = false; + + if ( !(*in) ) + { + done = true; + } + } + break; + case '!': + nt = true; + break; + default: + rawtoken[ n++ ] = *in; + break; + } + + in++; + } + + matcher.SetToken( token ); + matcher.SetRaw( rawtoken ); + matcher.valid = true; +} + +bool CResponseSystem::CompareUsingMatcher( const char *setValue, Matcher& m, bool verbose /*=false*/ ) +{ + if ( !m.valid ) + return false; + + float v = (float)atof( setValue ); + if ( setValue[0] == '[' ) + { + bool found = false; + v = LookupEnumeration( setValue, found ); + } + + int minmaxcount = 0; + + if ( m.usemin ) + { + if ( m.minequals ) + { + if ( v < m.minval ) + return false; + } + else + { + if ( v <= m.minval ) + return false; + } + + ++minmaxcount; + } + + if ( m.usemax ) + { + if ( m.maxequals ) + { + if ( v > m.maxval ) + return false; + } + else + { + if ( v >= m.maxval ) + return false; + } + + ++minmaxcount; + } + + // Had one or both criteria and met them + if ( minmaxcount >= 1 ) + { + return true; + } + + if ( m.notequal ) + { + if ( m.isnumeric ) + { + if ( v == (float)atof( m.GetToken() ) ) + return false; + } + else + { + if ( !Q_stricmp( setValue, m.GetToken() ) ) + return false; + } + + return true; + } + + if ( m.isnumeric ) + { + // If the setValue is "", the NPC doesn't have the key at all, + // in which case we shouldn't match "0". + if ( !setValue || !setValue[0] ) + return false; + + return v == (float)atof( m.GetToken() ); + } + + return !Q_stricmp( setValue, m.GetToken() ) ? true : false; +} + +bool CResponseSystem::Compare( const char *setValue, Criteria *c, bool verbose /*= false*/ ) +{ + Assert( c ); + Assert( setValue ); + + bool bret = CompareUsingMatcher( setValue, c->matcher, verbose ); + + if ( verbose ) + { + DevMsg( "'%20s' vs. '%20s' = ", setValue, c->value ); + + { + //DevMsg( "\n" ); + //m.Describe(); + } + } + return bret; +} + +float CResponseSystem::RecursiveScoreSubcriteriaAgainstRule( const AI_CriteriaSet& set, Criteria *parent, bool& exclude, bool verbose /*=false*/ ) +{ + float score = 0.0f; + int subcount = parent->subcriteria.Count(); + for ( int i = 0; i < subcount; i++ ) + { + int icriterion = parent->subcriteria[ i ]; + + bool excludesubrule = false; + if (verbose) + { + DevMsg( "\n" ); + } + score += ScoreCriteriaAgainstRuleCriteria( set, icriterion, excludesubrule, verbose ); + } + + exclude = ( parent->required && score == 0.0f ) ? true : false; + + return score * parent->weight.GetFloat(); +} + +float CResponseSystem::ScoreCriteriaAgainstRuleCriteria( const AI_CriteriaSet& set, int icriterion, bool& exclude, bool verbose /*=false*/ ) +{ + Criteria *c = &m_Criteria[ icriterion ]; + + if ( c->IsSubCriteriaType() ) + { + return RecursiveScoreSubcriteriaAgainstRule( set, c, exclude, verbose ); + } + + if ( verbose ) + { + DevMsg( " criterion '%25s':'%15s' ", m_Criteria.GetElementName( icriterion ), c->name ); + } + + exclude = false; + + float score = 0.0f; + + const char *actualValue = ""; + + int found = set.FindCriterionIndex( c->name ); + if ( found != -1 ) + { + actualValue = set.GetValue( found ); + if ( !actualValue ) + { + Assert( 0 ); + return score; + } + } + + Assert( actualValue ); + + if ( Compare( actualValue, c, verbose ) ) + { + float w = set.GetWeight( found ); + score = w * c->weight.GetFloat(); + + if ( verbose ) + { + DevMsg( "matched, weight %4.2f (s %4.2f x c %4.2f)", + score, w, c->weight.GetFloat() ); + } + } + else + { + if ( c->required ) + { + exclude = true; + if ( verbose ) + { + DevMsg( "failed (+exclude rule)" ); + } + } + else + { + if ( verbose ) + { + DevMsg( "failed" ); + } + } + } + + return score; +} + +float CResponseSystem::ScoreCriteriaAgainstRule( const AI_CriteriaSet& set, int irule, bool verbose /*=false*/ ) +{ + Rule *rule = &m_Rules[ irule ]; + float score = 0.0f; + + bool bBeingWatched = false; + + // See if we're trying to debug this rule + const char *pszText = rr_debugrule.GetString(); + if ( pszText && pszText[0] && !Q_stricmp( pszText, m_Rules.GetElementName( irule ) ) ) + { + bBeingWatched = true; + } + + if ( !rule->IsEnabled() ) + { + if ( bBeingWatched ) + { + DevMsg("Rule '%s' is disabled.\n" ); + } + return 0.0f; + } + + if ( bBeingWatched ) + { + verbose = true; + } + + if ( verbose ) + { + DevMsg( "Scoring rule '%s' (%i)\n{\n", m_Rules.GetElementName( irule ), irule+1 ); + } + + // Iterate set criteria + int count = rule->m_Criteria.Count(); + int i; + for ( i = 0; i < count; i++ ) + { + int icriterion = rule->m_Criteria[ i ]; + + bool exclude = false; + score += ScoreCriteriaAgainstRuleCriteria( set, icriterion, exclude, verbose ); + + if ( verbose ) + { + DevMsg( ", score %4.2f\n", score ); + } + + if ( exclude ) + { + score = 0.0f; + break; + } + } + + if ( verbose ) + { + DevMsg( "}\n" ); + } + + return score; +} + +void CResponseSystem::DebugPrint( int depth, const char *fmt, ... ) +{ + int indentchars = 3 * depth; + char *indent = (char *)_alloca( indentchars + 1); + indent[ indentchars ] = 0; + while ( --indentchars >= 0 ) + { + indent[ indentchars ] = ' '; + } + + // Dump text to debugging console. + va_list argptr; + char szText[1024]; + + va_start (argptr, fmt); + Q_vsnprintf (szText, sizeof( szText ), fmt, argptr); + va_end (argptr); + + DevMsg( "%s%s", indent, szText ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CResponseSystem::ResetResponseGroups() +{ + int i; + int c = m_Responses.Count(); + for ( i = 0; i < c; i++ ) + { + m_Responses[ i ].Reset(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *g - +// Output : int +//----------------------------------------------------------------------------- +int CResponseSystem::SelectWeightedResponseFromResponseGroup( ResponseGroup *g, IResponseFilter *pFilter ) +{ + int c = g->group.Count(); + if ( !c ) + { + Assert( !"Expecting response group with >= 1 elements" ); + return -1; + } + + int i; + + // Fake depletion of unavailable choices + CUtlVector fakedDepletes; + if ( pFilter && g->ShouldCheckRepeats() ) + { + for ( i = 0; i < c; i++ ) + { + Response *r = &g->group[ i ]; + if ( r->depletioncount != g->GetDepletionCount() && !pFilter->IsValidResponse( r->GetType(), r->value ) ) + { + fakedDepletes.AddToTail( i ); + g->MarkResponseUsed( i ); + } + } + } + + if ( !g->HasUndepletedChoices() ) + { + g->ResetDepletionCount(); + + if ( pFilter && g->ShouldCheckRepeats() ) + { + fakedDepletes.RemoveAll(); + for ( i = 0; i < c; i++ ) + { + Response *r = &g->group[ i ]; + if ( !pFilter->IsValidResponse( r->GetType(), r->value ) ) + { + fakedDepletes.AddToTail( i ); + g->MarkResponseUsed( i ); + } + } + } + + if ( !g->HasUndepletedChoices() ) + return -1; + + // Disable the group if we looped through all the way + if ( g->IsNoRepeat() ) + { + g->SetEnabled( false ); + return -1; + } + } + + bool checkrepeats = g->ShouldCheckRepeats(); + int depletioncount = g->GetDepletionCount(); + + float totalweight = 0.0f; + int slot = -1; + + if ( checkrepeats ) + { + int check= -1; + // Snag the first slot right away + if ( g->HasUndepletedFirst( check ) && check != -1 ) + { + slot = check; + } + + if ( slot == -1 && g->HasUndepletedLast( check ) && check != -1 ) + { + // If this is the only undepleted one, use it now + for ( i = 0; i < c; i++ ) + { + Response *r = &g->group[ i ]; + if ( checkrepeats && + ( r->depletioncount == depletioncount ) ) + { + continue; + } + + if ( r->last ) + { + Assert( i == check ); + continue; + } + + // There's still another undepleted entry + break; + } + + // No more undepleted so use the r->last slot + if ( i >= c ) + { + slot = check; + } + } + } + + if ( slot == -1 ) + { + for ( i = 0; i < c; i++ ) + { + Response *r = &g->group[ i ]; + if ( checkrepeats && + ( r->depletioncount == depletioncount ) ) + { + continue; + } + + // Always skip last entry here since we will deal with it above + if ( checkrepeats && r->last ) + continue; + + int prevSlot = slot; + + if ( !totalweight ) + { + slot = i; + } + + // Always assume very first slot will match + totalweight += r->weight.GetFloat(); + if ( !totalweight || random->RandomFloat(0,totalweight) < r->weight.GetFloat() ) + { + slot = i; + } + + if ( !checkrepeats && slot != prevSlot && pFilter && !pFilter->IsValidResponse( r->GetType(), r->value ) ) + { + slot = prevSlot; + totalweight -= r->weight.GetFloat(); + } + } + } + + if ( slot != -1 ) + g->MarkResponseUsed( slot ); + + // Revert fake depletion of unavailable choices + if ( pFilter && g->ShouldCheckRepeats() ) + { + for ( i = 0; i < fakedDepletes.Count(); i++ ) + { + g->group[ fakedDepletes[ i ] ].depletioncount = 0;; + } + } + + return slot; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : searchResult - +// depth - +// *name - +// verbose - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CResponseSystem::ResolveResponse( ResponseSearchResult& searchResult, int depth, const char *name, bool verbose /*= false*/, IResponseFilter *pFilter ) +{ + int responseIndex = m_Responses.Find( name ); + if ( responseIndex == m_Responses.InvalidIndex() ) + return false; + + ResponseGroup *g = &m_Responses[ responseIndex ]; + // Group has been disabled + if ( !g->IsEnabled() ) + return false; + + int c = g->group.Count(); + if ( !c ) + return false; + + int idx = 0; + + if ( g->IsSequential() ) + { + // See if next index is valid + int initialIndex = g->GetCurrentIndex(); + bool bFoundValid = false; + + do + { + idx = g->GetCurrentIndex(); + g->SetCurrentIndex( idx + 1 ); + if ( idx >= c ) + { + if ( g->IsNoRepeat() ) + { + g->SetEnabled( false ); + return false; + } + idx = 0; + g->SetCurrentIndex( 0 ); + } + + if ( !pFilter || pFilter->IsValidResponse( g->group[idx].GetType(), g->group[idx].value ) ) + { + bFoundValid = true; + break; + } + + } while ( g->GetCurrentIndex() != initialIndex ); + + if ( !bFoundValid ) + return false; + } + else + { + idx = SelectWeightedResponseFromResponseGroup( g, pFilter ); + if ( idx < 0 ) + return false; + } + + if ( verbose ) + { + DebugPrint( depth, "%s\n", m_Responses.GetElementName( responseIndex ) ); + DebugPrint( depth, "{\n" ); + DescribeResponseGroup( g, idx, depth ); + } + + bool bret = true; + + Response *result = &g->group[ idx ]; + if ( result->type == RESPONSE_RESPONSE ) + { + // Recurse + bret = ResolveResponse( searchResult, depth + 1, result->value, verbose, pFilter ); + } + else + { + searchResult.action = result; + searchResult.group = g; + } + + if( verbose ) + { + DebugPrint( depth, "}\n" ); + } + + return bret; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *group - +// selected - +// depth - +//----------------------------------------------------------------------------- +void CResponseSystem::DescribeResponseGroup( ResponseGroup *group, int selected, int depth ) +{ + int c = group->group.Count(); + + for ( int i = 0; i < c ; i++ ) + { + Response *r = &group->group[ i ]; + DebugPrint( depth + 1, "%s%20s : %40s %5.3f\n", + i == selected ? "-> " : " ", + AI_Response::DescribeResponse( r->GetType() ), + r->value, + r->weight.GetFloat() ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *rule - +// Output : CResponseSystem::Response +//----------------------------------------------------------------------------- +bool CResponseSystem::GetBestResponse( ResponseSearchResult& searchResult, Rule *rule, bool verbose /*=false*/, IResponseFilter *pFilter ) +{ + int c = rule->m_Responses.Count(); + if ( !c ) + return false; + + int index = random->RandomInt( 0, c - 1 ); + int groupIndex = rule->m_Responses[ index ]; + + ResponseGroup *g = &m_Responses[ groupIndex ]; + + // Group has been disabled + if ( !g->IsEnabled() ) + return false; + + int count = g->group.Count(); + if ( !count ) + return false; + + int responseIndex = 0; + + if ( g->IsSequential() ) + { + // See if next index is valid + int initialIndex = g->GetCurrentIndex(); + bool bFoundValid = false; + + do + { + responseIndex = g->GetCurrentIndex(); + g->SetCurrentIndex( responseIndex + 1 ); + if ( responseIndex >= count ) + { + if ( g->IsNoRepeat() ) + { + g->SetEnabled( false ); + return false; + } + responseIndex = 0; + g->SetCurrentIndex( 0 ); + } + + if ( !pFilter || pFilter->IsValidResponse( g->group[responseIndex].GetType(), g->group[responseIndex].value ) ) + { + bFoundValid = true; + break; + } + + } while ( g->GetCurrentIndex() != initialIndex ); + + if ( !bFoundValid ) + return false; + } + else + { + responseIndex = SelectWeightedResponseFromResponseGroup( g, pFilter ); + if ( responseIndex < 0 ) + return false; + } + + + Response *r = &g->group[ responseIndex ]; + + int depth = 0; + + if ( verbose ) + { + DebugPrint( depth, "%s\n", m_Responses.GetElementName( groupIndex ) ); + DebugPrint( depth, "{\n" ); + + DescribeResponseGroup( g, responseIndex, depth ); + } + + bool bret = true; + + if ( r->type == RESPONSE_RESPONSE ) + { + bret = ResolveResponse( searchResult, depth + 1, r->value, verbose, pFilter ); + } + else + { + searchResult.action = r; + searchResult.group = g; + } + + if ( verbose ) + { + DebugPrint( depth, "}\n" ); + } + + return bret; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : set - +// verbose - +// Output : int +//----------------------------------------------------------------------------- +int CResponseSystem::FindBestMatchingRule( const AI_CriteriaSet& set, bool verbose ) +{ + CUtlVector< int > bestrules; + float bestscore = 0.001f; + + int c = m_Rules.Count(); + int i; + for ( i = 0; i < c; i++ ) + { + float score = ScoreCriteriaAgainstRule( set, i, verbose ); + // Check equals so that we keep track of all matching rules + if ( score >= bestscore ) + { + // Reset bucket + if( score != bestscore ) + { + bestscore = score; + bestrules.RemoveAll(); + } + + // Add to bucket + bestrules.AddToTail( i ); + } + } + + int bestCount = bestrules.Count(); + if ( bestCount <= 0 ) + return -1; + + if ( bestCount == 1 ) + return bestrules[ 0 ]; + + // Randomly pick one of the tied matching rules + int idx = random->RandomInt( 0, bestCount - 1 ); + if ( verbose ) + { + DevMsg( "Found %i matching rules, selecting slot %i\n", bestCount, idx ); + } + return bestrules[ idx ]; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : set - +// Output : AI_Response +//----------------------------------------------------------------------------- +bool CResponseSystem::FindBestResponse( const AI_CriteriaSet& set, AI_Response& response, IResponseFilter *pFilter ) +{ + bool valid = false; + + int iDbgResponse = rr_debugresponses.GetInt(); + bool showRules = ( iDbgResponse == 2 ); + bool showResult = ( iDbgResponse == 1 || iDbgResponse == 2 ); + + // Look for match + int bestRule = FindBestMatchingRule( set, showRules ); + + ResponseType_t responseType = RESPONSE_NONE; + AI_ResponseParams rp; + + char ruleName[ 128 ]; + char responseName[ 128 ]; + const char *context; + ruleName[ 0 ] = 0; + responseName[ 0 ] = 0; + context = NULL; + if ( bestRule != -1 ) + { + Rule *r = &m_Rules[ bestRule ]; + + ResponseSearchResult result; + if ( GetBestResponse( result, r, showResult, pFilter ) ) + { + Q_strncpy( responseName, result.action->value, sizeof( responseName ) ); + responseType = result.action->GetType(); + rp = result.group->rp; + } + + Q_strncpy( ruleName, m_Rules.GetElementName( bestRule ), sizeof( ruleName ) ); + + // Disable the rule if it only allows for matching one time + if ( r->IsMatchOnce() ) + { + r->Disable(); + } + context = r->GetContext(); + + valid = true; + } + + response.Init( responseType, responseName, set, rp, ruleName, context ); + + if ( showResult ) + { + if ( valid ) + { + // Rescore the winner and dump to console + ScoreCriteriaAgainstRule( set, bestRule, true ); + } + + if ( valid || showRules ) + { + // Describe the response, too + response.Describe(); + } + } + + return valid; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CResponseSystem::GetAllResponses( CUtlVector *pResponses ) +{ + for ( int i = 0; i < (int)m_Responses.Count(); i++ ) + { + ResponseGroup &group = m_Responses[i]; + + for ( int j = 0; j < group.group.Count(); j++) + { + Response &response = group.group[j]; + if ( response.type != RESPONSE_RESPONSE ) + { + AI_Response *pResponse = new AI_Response; + pResponse->Init( response.GetType(), response.value, AI_CriteriaSet(), group.rp, NULL, NULL ); + pResponses->AddToTail(pResponse); + } + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CResponseSystem::Precache() +{ + // enumerate and mark all the scripts so we know they're referenced + for ( int i = 0; i < (int)m_Responses.Count(); i++ ) + { + ResponseGroup &group = m_Responses[i]; + + for ( int j = 0; j < group.group.Count(); j++) + { + Response &response = group.group[j]; + switch ( response.type ) + { + default: + break; + case RESPONSE_SCENE: + { + // fixup $gender references + char file[_MAX_PATH]; + Q_strncpy( file, response.value, sizeof(file) ); + char *gender = strstr( file, "$gender" ); + if ( gender ) + { + // replace with male & female + const char *postGender = gender + strlen("$gender"); + *gender = 0; + char genderFile[_MAX_PATH]; + // male + Q_snprintf( genderFile, sizeof(genderFile), "%smale%s", file, postGender); + + PrecacheInstancedScene( genderFile ); + + Q_snprintf( genderFile, sizeof(genderFile), "%sfemale%s", file, postGender); + + PrecacheInstancedScene( genderFile ); + } + else + { + PrecacheInstancedScene( file ); + } + } + break; + case RESPONSE_SPEAK: + { + CBaseEntity::PrecacheScriptSound( response.value ); + } + break; + } + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Does any necessary resource allocation for resources references in the script file +//----------------------------------------------------------------------------- +/* +I think this is obsolete in view of having to precache all of the scenes, etc. +void CResponseSystem::TouchReferencedScenes() +{ + if (CommandLine()->CheckParm("-makereslists")) + { + // enumerate and mark all the scripts so we know they're referenced + for ( int i = 0; i < (int)m_Responses.Count(); i++ ) + { + ResponseGroup &group = m_Responses[i]; + + for ( int j = 0; j < group.group.Count(); j++) + { + Response &response = group.group[j]; + if (response.type == RESPONSE_SCENE) + { + // fixup $gender references + char file[_MAX_PATH]; + Q_strncpy( file, response.value, sizeof(file) ); + char *gender = strstr( file, "$gender" ); + if ( gender ) + { + // replace with male & female + const char *postGender = gender + strlen("$gender"); + *gender = 0; + char genderFile[_MAX_PATH]; + // male + Q_snprintf( genderFile, sizeof(genderFile), "%smale%s", file, postGender); + FileHandle_t f = filesystem->Open(genderFile, "rb"); + if (f) + { + filesystem->Close(f); + } + // female + Q_snprintf( genderFile, sizeof(genderFile), "%sfemale%s", file, postGender); + f = filesystem->Open(genderFile, "rb"); + if (f) + { + filesystem->Close(f); + } + } + else + { + // just force the file open and closed so the filesystem can log it + FileHandle_t f = filesystem->Open(file, "rb"); + if (f) + { + filesystem->Close(f); + } + } + } + } + } + } +} +*/ + +void CResponseSystem::ParseInclude( CStringPool &includedFiles ) +{ + char includefile[ 256 ]; + ParseToken(); + Q_snprintf( includefile, sizeof( includefile ), "scripts/%s", token ); + + // check if the file is already included + if ( includedFiles.Find( includefile ) != NULL ) + { + return; + } + + // Try and load it + CUtlBuffer buf; + if ( !filesystem->ReadFile( includefile, "GAME", buf ) ) + { + DevMsg( "Unable to load #included script %s\n", includefile ); + return; + } + + LoadFromBuffer( includefile, (const char *)buf.PeekGet(), includedFiles ); +} + +void CResponseSystem::LoadFromBuffer( const char *scriptfile, const char *buffer, CStringPool &includedFiles ) +{ + includedFiles.Allocate( scriptfile ); + PushScript( scriptfile, (unsigned char * )buffer ); + + if( rr_dumpresponses.GetBool() ) + { + DevMsg("Reading: %s\n", scriptfile ); + } + + while ( 1 ) + { + ParseToken(); + if ( !token[0] ) + { + break; + } + + if ( !Q_stricmp( token, "#include" ) ) + { + ParseInclude( includedFiles ); + } + else if ( !Q_stricmp( token, "response" ) ) + { + ParseResponse(); + } + else if ( !Q_stricmp( token, "criterion" ) || + !Q_stricmp( token, "criteria" ) ) + { + ParseCriterion(); + } + else if ( !Q_stricmp( token, "rule" ) ) + { + ParseRule(); + } + else if ( !Q_stricmp( token, "enumeration" ) ) + { + ParseEnumeration(); + } + else + { + int byteoffset = m_ScriptStack[ 0 ].currenttoken - (const char *)m_ScriptStack[ 0 ].buffer; + + Error( "CResponseSystem::LoadFromBuffer: Unknown entry type '%s', expecting 'response', 'criterion', 'enumeration' or 'rules' in file %s(offset:%i)\n", + token, scriptfile, byteoffset ); + break; + } + } + + if ( m_ScriptStack.Count() == 1 ) + { + char cur[ 256 ]; + GetCurrentScript( cur, sizeof( cur ) ); + DevMsg( 1, "CResponseSystem: %s (%i rules, %i criteria, and %i responses)\n", + cur, m_Rules.Count(), m_Criteria.Count(), m_Responses.Count() ); + + if( rr_dumpresponses.GetBool() ) + { + DumpRules(); + } + } + + PopScript(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CResponseSystem::LoadRuleSet( const char *basescript ) +{ + int length = 0; + unsigned char *buffer = (unsigned char *)UTIL_LoadFileForMe( basescript, &length ); + if ( length <= 0 || !buffer ) + { + DevMsg( 1, "CResponseSystem: failed to load %s\n", basescript ); + return; + } + + CStringPool includedFiles; + + LoadFromBuffer( basescript, (const char *)buffer, includedFiles ); + + UTIL_FreeFile( buffer ); + + Assert( m_ScriptStack.Count() == 0 ); + + //TouchReferencedScenes(); +} + +static ResponseType_t ComputeResponseType( const char *s ) +{ + if ( !Q_stricmp( s, "scene" ) ) + { + return RESPONSE_SCENE; + } + else if ( !Q_stricmp( s, "sentence" ) ) + { + return RESPONSE_SENTENCE; + } + else if ( !Q_stricmp( s, "speak" ) ) + { + return RESPONSE_SPEAK; + } + else if ( !Q_stricmp( s, "response" ) ) + { + return RESPONSE_RESPONSE; + } + else if ( !Q_stricmp( s, "print" ) ) + { + return RESPONSE_PRINT; + } + + return RESPONSE_NONE; +} + +void CResponseSystem::ParseOneResponse( const char *responseGroupName, ResponseGroup& group ) +{ + Response newResponse; + newResponse.weight.SetFloat( 1.0f ); + AI_ResponseParams *rp = &group.rp; + + newResponse.type = ComputeResponseType( token ); + if ( RESPONSE_NONE == newResponse.type ) + { + ResponseWarning( "response entry '%s' with unknown response type '%s'\n", responseGroupName, token ); + return; + } + + ParseToken(); + newResponse.value = CopyString( token ); + + while ( TokenWaiting() ) + { + ParseToken(); + if ( !Q_stricmp( token, "weight" ) ) + { + ParseToken(); + newResponse.weight.SetFloat( (float)atof( token ) ); + continue; + } + + if ( !Q_stricmp( token, "nodelay" ) ) + { + ParseToken(); + rp->flags |= AI_ResponseParams::RG_DELAYAFTERSPEAK; + rp->delay.start = 0; + rp->delay.range = 0; + continue; + } + + if ( !Q_stricmp( token, "defaultdelay" ) ) + { + rp->flags |= AI_ResponseParams::RG_DELAYAFTERSPEAK; + rp->delay.start = AIS_DEF_MIN_DELAY; + rp->delay.range = ( AIS_DEF_MAX_DELAY - AIS_DEF_MIN_DELAY ); + continue; + } + + if ( !Q_stricmp( token, "delay" ) ) + { + ParseToken(); + rp->flags |= AI_ResponseParams::RG_DELAYAFTERSPEAK; + rp->delay.FromInterval( ReadInterval( token ) ); + continue; + } + + if ( !Q_stricmp( token, "speakonce" ) ) + { + rp->flags |= AI_ResponseParams::RG_SPEAKONCE; + continue; + } + + if ( !Q_stricmp( token, "noscene" ) ) + { + rp->flags |= AI_ResponseParams::RG_DONT_USE_SCENE; + continue; + } + + if ( !Q_stricmp( token, "stop_on_nonidle" ) ) + { + rp->flags |= AI_ResponseParams::RG_STOP_ON_NONIDLE; + continue; + } + + if ( !Q_stricmp( token, "odds" ) ) + { + ParseToken(); + rp->flags |= AI_ResponseParams::RG_ODDS; + rp->odds = clamp( atoi( token ), 0, 100 ); + continue; + } + + if ( !Q_stricmp( token, "respeakdelay" ) ) + { + ParseToken(); + rp->flags |= AI_ResponseParams::RG_RESPEAKDELAY; + rp->respeakdelay.FromInterval( ReadInterval( token ) ); + continue; + } + + if ( !Q_stricmp( token, "weapondelay" ) ) + { + ParseToken(); + rp->flags |= AI_ResponseParams::RG_WEAPONDELAY; + rp->weapondelay.FromInterval( ReadInterval( token ) ); + continue; + } + + if ( !Q_stricmp( token, "soundlevel" ) ) + { + ParseToken(); + rp->flags |= AI_ResponseParams::RG_SOUNDLEVEL; + rp->soundlevel = (soundlevel_t)TextToSoundLevel( token ); + continue; + } + + if ( !Q_stricmp( token, "displayfirst" ) ) + { + newResponse.first = true; + group.m_bHasFirst = true; + continue; + } + + if ( !Q_stricmp( token, "displaylast" ) ) + { + newResponse.last = true; + group.m_bHasLast= true; + continue; + } + } + + group.group.AddToTail( newResponse ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CResponseSystem::IsRootCommand() +{ + if ( !Q_stricmp( token, "#include" ) ) + return true; + if ( !Q_stricmp( token, "response" ) ) + return true; + if ( !Q_stricmp( token, "enumeration" ) ) + return true; + if ( !Q_stricmp( token, "criteria" ) ) + return true; + if ( !Q_stricmp( token, "criterion" ) ) + return true; + if ( !Q_stricmp( token, "rule" ) ) + return true; + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *kv - +//----------------------------------------------------------------------------- +void CResponseSystem::ParseResponse( void ) +{ + // Should have groupname at start + char responseGroupName[ 128 ]; + + ResponseGroup newGroup; + AI_ResponseParams *rp = &newGroup.rp; + + // Response Group Name + ParseToken(); + Q_strncpy( responseGroupName, token, sizeof( responseGroupName ) ); + + while ( 1 ) + { + ParseToken(); + + // Oops, part of next definition + if( IsRootCommand() ) + { + Unget(); + break; + } + + if ( !Q_stricmp( token, "{" ) ) + { + while ( 1 ) + { + ParseToken(); + if ( !Q_stricmp( token, "}" ) ) + break; + + if ( !Q_stricmp( token, "permitrepeats" ) ) + { + newGroup.m_bDepleteBeforeRepeat = false; + continue; + } + else if ( !Q_stricmp( token, "sequential" ) ) + { + newGroup.SetSequential( true ); + continue; + } + else if ( !Q_stricmp( token, "norepeat" ) ) + { + newGroup.SetNoRepeat( true ); + continue; + } + + ParseOneResponse( responseGroupName, newGroup ); + } + break; + } + + if ( !Q_stricmp( token, "nodelay" ) ) + { + ParseToken(); + rp->flags |= AI_ResponseParams::RG_DELAYAFTERSPEAK; + rp->delay.start = 0; + rp->delay.range = 0; + continue; + } + + if ( !Q_stricmp( token, "defaultdelay" ) ) + { + rp->flags |= AI_ResponseParams::RG_DELAYAFTERSPEAK; + rp->delay.start = AIS_DEF_MIN_DELAY; + rp->delay.range = ( AIS_DEF_MAX_DELAY - AIS_DEF_MIN_DELAY ); + continue; + } + + if ( !Q_stricmp( token, "delay" ) ) + { + ParseToken(); + rp->flags |= AI_ResponseParams::RG_DELAYAFTERSPEAK; + rp->delay.FromInterval( ReadInterval( token ) ); + continue; + } + + if ( !Q_stricmp( token, "speakonce" ) ) + { + rp->flags |= AI_ResponseParams::RG_SPEAKONCE; + continue; + } + + if ( !Q_stricmp( token, "noscene" ) ) + { + rp->flags |= AI_ResponseParams::RG_DONT_USE_SCENE; + continue; + } + + if ( !Q_stricmp( token, "stop_on_nonidle" ) ) + { + rp->flags |= AI_ResponseParams::RG_STOP_ON_NONIDLE; + continue; + } + + if ( !Q_stricmp( token, "odds" ) ) + { + ParseToken(); + rp->flags |= AI_ResponseParams::RG_ODDS; + rp->odds = clamp( atoi( token ), 0, 100 ); + continue; + } + + if ( !Q_stricmp( token, "respeakdelay" ) ) + { + ParseToken(); + rp->flags |= AI_ResponseParams::RG_RESPEAKDELAY; + rp->respeakdelay.FromInterval( ReadInterval( token ) ); + continue; + } + + if ( !Q_stricmp( token, "weapondelay" ) ) + { + ParseToken(); + rp->flags |= AI_ResponseParams::RG_WEAPONDELAY; + rp->weapondelay.FromInterval( ReadInterval( token ) ); + continue; + } + + if ( !Q_stricmp( token, "soundlevel" ) ) + { + ParseToken(); + rp->flags |= AI_ResponseParams::RG_SOUNDLEVEL; + rp->soundlevel = (soundlevel_t)TextToSoundLevel( token ); + continue; + } + + ParseOneResponse( responseGroupName, newGroup ); + } + + m_Responses.Insert( responseGroupName, newGroup ); +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *criterion - +//----------------------------------------------------------------------------- +int CResponseSystem::ParseOneCriterion( const char *criterionName ) +{ + char key[ 128 ]; + char value[ 128 ]; + + Criteria newCriterion; + + bool gotbody = false; + + while ( TokenWaiting() || !gotbody ) + { + ParseToken(); + + // Oops, part of next definition + if( IsRootCommand() ) + { + Unget(); + break; + } + + if ( !Q_stricmp( token, "{" ) ) + { + gotbody = true; + + while ( 1 ) + { + ParseToken(); + if ( !Q_stricmp( token, "}" ) ) + break; + + // Look up subcriteria index + int idx = m_Criteria.Find( token ); + if ( idx != m_Criteria.InvalidIndex() ) + { + newCriterion.subcriteria.AddToTail( idx ); + } + else + { + ResponseWarning( "Skipping unrecongized subcriterion '%s' in '%s'\n", token, criterionName ); + } + } + continue; + } + else if ( !Q_stricmp( token, "required" ) ) + { + newCriterion.required = true; + } + else if ( !Q_stricmp( token, "weight" ) ) + { + ParseToken(); + newCriterion.weight.SetFloat( (float)atof( token ) ); + } + else + { + Assert( newCriterion.subcriteria.Count() == 0 ); + + // Assume it's the math info for a non-subcriteria resposne + Q_strncpy( key, token, sizeof( key ) ); + ParseToken(); + Q_strncpy( value, token, sizeof( value ) ); + + newCriterion.name = CopyString( key ); + newCriterion.value = CopyString( value ); + + gotbody = true; + } + } + + if ( !newCriterion.IsSubCriteriaType() ) + { + ComputeMatcher( &newCriterion, newCriterion.matcher ); + } + + if ( m_Criteria.Find( criterionName ) != m_Criteria.InvalidIndex() ) + { + ResponseWarning( "Multiple definitions for criteria '%s'\n", criterionName ); + return m_Criteria.InvalidIndex(); + } + + int idx = m_Criteria.Insert( criterionName, newCriterion ); + return idx; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *kv - +//----------------------------------------------------------------------------- +void CResponseSystem::ParseCriterion( void ) +{ + // Should have groupname at start + char criterionName[ 128 ]; + ParseToken(); + Q_strncpy( criterionName, token, sizeof( criterionName ) ); + + ParseOneCriterion( criterionName ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *kv - +//----------------------------------------------------------------------------- +void CResponseSystem::ParseEnumeration( void ) +{ + char enumerationName[ 128 ]; + ParseToken(); + Q_strncpy( enumerationName, token, sizeof( enumerationName ) ); + + ParseToken(); + if ( Q_stricmp( token, "{" ) ) + { + ResponseWarning( "Expecting '{' in enumeration '%s', got '%s'\n", enumerationName, token ); + return; + } + + while ( 1 ) + { + ParseToken(); + if ( !Q_stricmp( token, "}" ) ) + break; + + if ( Q_strlen( token ) <= 0 ) + { + ResponseWarning( "Expecting more tokens in enumeration '%s'\n", enumerationName ); + break; + } + + char key[ 128 ]; + + Q_strncpy( key, token, sizeof( key ) ); + ParseToken(); + float value = (float)atof( token ); + + char sz[ 128 ]; + Q_snprintf( sz, sizeof( sz ), "[%s::%s]", enumerationName, key ); + Q_strlower( sz ); + + Enumeration newEnum; + newEnum.value = value; + + if ( m_Enumerations.Find( sz ) == m_Enumerations.InvalidIndex() ) + { + m_Enumerations.Insert( sz, newEnum ); + } + else + { + ResponseWarning( "Ignoring duplication enumeration '%s'\n", sz ); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *kv - +//----------------------------------------------------------------------------- +void CResponseSystem::ParseRule( void ) +{ + static int instancedCriteria = 0; + + char ruleName[ 128 ]; + ParseToken(); + Q_strncpy( ruleName, token, sizeof( ruleName ) ); + + ParseToken(); + if ( Q_stricmp( token, "{" ) ) + { + ResponseWarning( "Expecting '{' in rule '%s', got '%s'\n", ruleName, token ); + return; + } + + // entries are "criteria", "response" or an in-line criteria to instance + Rule newRule; + + char sz[ 128 ]; + + bool validRule = true; + while ( 1 ) + { + ParseToken(); + if ( !Q_stricmp( token, "}" ) ) + { + break; + } + + if ( Q_strlen( token ) <= 0 ) + { + ResponseWarning( "Expecting more tokens in rule '%s'\n", ruleName ); + break; + } + + if ( !Q_stricmp( token, "matchonce" ) ) + { + newRule.m_bMatchOnce = true; + continue; + } + + if ( !Q_stricmp( token, "applyContext" ) ) + { + ParseToken(); + if ( newRule.GetContext() == NULL ) + { + newRule.SetContext( token ); + } + else + { + CFmtStrN<1024> newContext( "%s,%s", newRule.GetContext(), token ); + newRule.SetContext( newContext ); + } + continue; + } + + if ( !Q_stricmp( token, "response" ) ) + { + // Read them until we run out. + while ( TokenWaiting() ) + { + ParseToken(); + int idx = m_Responses.Find( token ); + if ( idx != m_Responses.InvalidIndex() ) + { + MEM_ALLOC_CREDIT(); + newRule.m_Responses.AddToTail( idx ); + } + else + { + validRule = false; + ResponseWarning( "No such response '%s' for rule '%s'\n", token, ruleName ); + } + } + continue; + } + + if ( !Q_stricmp( token, "criteria" ) || + !Q_stricmp( token, "criterion" ) ) + { + // Read them until we run out. + while ( TokenWaiting() ) + { + ParseToken(); + + int idx = m_Criteria.Find( token ); + if ( idx != m_Criteria.InvalidIndex() ) + { + MEM_ALLOC_CREDIT(); + newRule.m_Criteria.AddToTail( idx ); + } + else + { + validRule = false; + ResponseWarning( "No such criterion '%s' for rule '%s'\n", token, ruleName ); + } + } + continue; + } + + // It's an inline criteria, generate a name and parse it in + Q_snprintf( sz, sizeof( sz ), "[%s%03i]", ruleName, ++instancedCriteria ); + Unget(); + int idx = ParseOneCriterion( sz ); + if ( idx != m_Criteria.InvalidIndex() ) + { + newRule.m_Criteria.AddToTail( idx ); + } + } + + if ( validRule ) + { + m_Rules.Insert( ruleName, newRule ); + } + else + { + DevMsg( "Discarded rule %s\n", ruleName ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +int CResponseSystem::GetCurrentToken() const +{ + if ( m_ScriptStack.Count() <= 0 ) + return -1; + + return m_ScriptStack[ 0 ].tokencount; +} + + +void CResponseSystem::ResponseWarning( const char *fmt, ... ) +{ + va_list argptr; +#ifndef _XBOX + static char string[1024]; +#else + char string[1024]; +#endif + + va_start (argptr, fmt); + Q_vsnprintf(string, sizeof(string), fmt,argptr); + va_end (argptr); + + char cur[ 256 ]; + GetCurrentScript( cur, sizeof( cur ) ); + DevMsg( 1, "%s(token %i) : %s", cur, GetCurrentToken(), string ); +} + + +//----------------------------------------------------------------------------- +// Purpose: A special purpose response system associated with a custom entity +//----------------------------------------------------------------------------- +class CInstancedResponseSystem : public CResponseSystem +{ + typedef CResponseSystem BaseClass; + +public: + CInstancedResponseSystem( const char *scriptfile ) : + m_pszScriptFile( 0 ) + { + Assert( scriptfile ); + + int len = Q_strlen( scriptfile ) + 1; + m_pszScriptFile = new char[ len ]; + Assert( m_pszScriptFile ); + Q_strncpy( m_pszScriptFile, scriptfile, len ); + } + + ~CInstancedResponseSystem() + { + delete[] m_pszScriptFile; + } + virtual const char *GetScriptFile( void ) + { + Assert( m_pszScriptFile ); + return m_pszScriptFile; + } + + // CAutoGameSystem + virtual bool Init() + { + const char *basescript = GetScriptFile(); + LoadRuleSet( basescript ); + return true; + } + + virtual void LevelInitPostEntity() + { + ResetResponseGroups(); + } + + virtual void Release() + { + Clear(); + delete this; + } +private: + + char *m_pszScriptFile; +}; + +//----------------------------------------------------------------------------- +// Purpose: The default response system for expressive AIs +//----------------------------------------------------------------------------- +class CDefaultResponseSystem : public CResponseSystem, public CAutoGameSystem +{ + typedef CAutoGameSystem BaseClass; + +public: + CDefaultResponseSystem() : CAutoGameSystem( "CDefaultResponseSystem" ) + { + } + + virtual const char *GetScriptFile( void ) + { + return "scripts/talker/response_rules.txt"; + } + + // CAutoServerSystem + virtual bool Init(); + virtual void Shutdown(); + + virtual void LevelInitPostEntity() + { + } + + virtual void Release() + { + Assert( 0 ); + } + + void AddInstancedResponseSystem( const char *scriptfile, CInstancedResponseSystem *sys ) + { + m_InstancedSystems.Insert( scriptfile, sys ); + } + + CInstancedResponseSystem *FindResponseSystem( const char *scriptfile ) + { + int idx = m_InstancedSystems.Find( scriptfile ); + if ( idx == m_InstancedSystems.InvalidIndex() ) + return NULL; + return m_InstancedSystems[ idx ]; + } + + IResponseSystem *PrecacheCustomResponseSystem( const char *scriptfile ) + { + CInstancedResponseSystem *sys = ( CInstancedResponseSystem * )FindResponseSystem( scriptfile ); + if ( !sys ) + { + sys = new CInstancedResponseSystem( scriptfile ); + if ( !sys ) + { + Error( "Failed to load response system data from %s", scriptfile ); + } + + if ( !sys->Init() ) + { + Error( "CInstancedResponseSystem: Failed to init response system from %s!", scriptfile ); + } + + AddInstancedResponseSystem( scriptfile, sys ); + } + + sys->Precache(); + + return ( IResponseSystem * )sys; + } + + virtual void LevelInitPreEntity() + { + // This will precache the default system + // All user installed systems are init'd by PrecacheCustomResponseSystem which will call sys->Precache() on the ones being used + + // FIXME: This is SLOW the first time you run the engine (can take 3 - 10 seconds!!!) + if ( ShouldPrecache() ) + { + Precache(); + } + + ResetResponseGroups(); + } + + void ReloadAllResponseSystems() + { + Clear(); + Init(); + + int c = m_InstancedSystems.Count(); + for ( int i = c - 1 ; i >= 0; i-- ) + { + CInstancedResponseSystem *sys = m_InstancedSystems[ i ]; + sys->Clear(); + sys->Init(); + } + + } + +private: + + void ClearInstanced() + { + int c = m_InstancedSystems.Count(); + for ( int i = c - 1 ; i >= 0; i-- ) + { + CInstancedResponseSystem *sys = m_InstancedSystems[ i ]; + sys->Release(); + } + m_InstancedSystems.RemoveAll(); + } + + CUtlDict< CInstancedResponseSystem *, int > m_InstancedSystems; +}; + +static CDefaultResponseSystem defaultresponsesytem; +IResponseSystem *g_pResponseSystem = &defaultresponsesytem; + +CON_COMMAND( rr_reloadresponsesystems, "Reload all response system scripts." ) +{ + defaultresponsesytem.ReloadAllResponseSystems(); +} + +static short RESPONSESYSTEM_SAVE_RESTORE_VERSION = 1; + +// note: this won't save/restore settings from instanced response systems. Could add that with a CDefSaveRestoreOps implementation if needed +// +class CDefaultResponseSystemSaveRestoreBlockHandler : public CDefSaveRestoreBlockHandler +{ +public: + const char *GetBlockName() + { + return "ResponseSystem"; + } + + void WriteSaveHeaders( ISave *pSave ) + { + pSave->WriteShort( &RESPONSESYSTEM_SAVE_RESTORE_VERSION ); + } + + void ReadRestoreHeaders( IRestore *pRestore ) + { + // No reason why any future version shouldn't try to retain backward compatability. The default here is to not do so. + short version; + pRestore->ReadShort( &version ); + m_fDoLoad = ( version == RESPONSESYSTEM_SAVE_RESTORE_VERSION ); + } + + void Save( ISave *pSave ) + { + CDefaultResponseSystem& rs = defaultresponsesytem; + + int count = rs.m_Responses.Count(); + pSave->WriteInt( &count ); + for ( int i = 0; i < count; ++i ) + { + pSave->StartBlock( "ResponseGroup" ); + + pSave->WriteString( rs.m_Responses.GetElementName( i ) ); + const ResponseGroup *group = &rs.m_Responses[ i ]; + pSave->WriteAll( group ); + + short groupCount = group->group.Count(); + pSave->WriteShort( &groupCount ); + for ( int j = 0; j < groupCount; ++j ) + { + const Response *response = &group->group[ j ]; + pSave->StartBlock( "Response" ); + pSave->WriteString( response->value ); + pSave->WriteAll( response ); + pSave->EndBlock(); + } + + pSave->EndBlock(); + } + } + + void Restore( IRestore *pRestore, bool createPlayers ) + { + if ( !m_fDoLoad ) + return; + + CDefaultResponseSystem& rs = defaultresponsesytem; + + int count = pRestore->ReadInt(); + for ( int i = 0; i < count; ++i ) + { + char szResponseGroupBlockName[SIZE_BLOCK_NAME_BUF]; + pRestore->StartBlock( szResponseGroupBlockName ); + if ( !Q_stricmp( szResponseGroupBlockName, "ResponseGroup" ) ) + { + + char groupname[ 256 ]; + pRestore->ReadString( groupname, sizeof( groupname ), 0 ); + + // Try and find it + int idx = rs.m_Responses.Find( groupname ); + if ( idx != rs.m_Responses.InvalidIndex() ) + { + ResponseGroup *group = &rs.m_Responses[ idx ]; + pRestore->ReadAll( group ); + + short groupCount = pRestore->ReadShort(); + for ( int j = 0; j < groupCount; ++j ) + { + char szResponseBlockName[SIZE_BLOCK_NAME_BUF]; + + char responsename[ 256 ]; + pRestore->StartBlock( szResponseBlockName ); + if ( !Q_stricmp( szResponseBlockName, "Response" ) ) + { + pRestore->ReadString( responsename, sizeof( responsename ), 0 ); + + // Find it by name + int ri; + for ( ri = 0; ri < group->group.Count(); ++ri ) + { + Response *response = &group->group[ ri ]; + if ( !Q_stricmp( response->value, responsename ) ) + { + break; + } + } + + if ( ri < group->group.Count() ) + { + Response *response = &group->group[ ri ]; + pRestore->ReadAll( response ); + } + } + + pRestore->EndBlock(); + } + } + } + + pRestore->EndBlock(); + } + } +private: + + bool m_fDoLoad; + +} g_DefaultResponseSystemSaveRestoreBlockHandler; + +ISaveRestoreBlockHandler *GetDefaultResponseSystemSaveRestoreBlockHandler() +{ + return &g_DefaultResponseSystemSaveRestoreBlockHandler; +} + +//----------------------------------------------------------------------------- +// CResponseSystemSaveRestoreOps +// +// Purpose: Handles save and load for instanced response systems... +// +// BUGBUG: This will save the same response system to file multiple times for "shared" response systems and +// therefore it'll restore the same data onto the same pointer N times on reload (probably benign for now, but we could +// write code to save/restore the instanced ones by filename in the block handler above maybe? +//----------------------------------------------------------------------------- + +class CResponseSystemSaveRestoreOps : public CDefSaveRestoreOps +{ +public: + + virtual void Save( const SaveRestoreFieldInfo_t &fieldInfo, ISave *pSave ) + { + CResponseSystem *pRS = *(CResponseSystem **)fieldInfo.pField; + if ( !pRS || pRS == &defaultresponsesytem ) + return; + + int count = pRS->m_Responses.Count(); + pSave->WriteInt( &count ); + for ( int i = 0; i < count; ++i ) + { + pSave->StartBlock( "ResponseGroup" ); + + pSave->WriteString( pRS->m_Responses.GetElementName( i ) ); + const ResponseGroup *group = &pRS->m_Responses[ i ]; + pSave->WriteAll( group ); + + short groupCount = group->group.Count(); + pSave->WriteShort( &groupCount ); + for ( int j = 0; j < groupCount; ++j ) + { + const Response *response = &group->group[ j ]; + pSave->StartBlock( "Response" ); + pSave->WriteString( response->value ); + pSave->WriteAll( response ); + pSave->EndBlock(); + } + + pSave->EndBlock(); + } + } + + virtual void Restore( const SaveRestoreFieldInfo_t &fieldInfo, IRestore *pRestore ) + { + CResponseSystem *pRS = *(CResponseSystem **)fieldInfo.pField; + if ( !pRS || pRS == &defaultresponsesytem ) + return; + + int count = pRestore->ReadInt(); + for ( int i = 0; i < count; ++i ) + { + char szResponseGroupBlockName[SIZE_BLOCK_NAME_BUF]; + pRestore->StartBlock( szResponseGroupBlockName ); + if ( !Q_stricmp( szResponseGroupBlockName, "ResponseGroup" ) ) + { + + char groupname[ 256 ]; + pRestore->ReadString( groupname, sizeof( groupname ), 0 ); + + // Try and find it + int idx = pRS->m_Responses.Find( groupname ); + if ( idx != pRS->m_Responses.InvalidIndex() ) + { + ResponseGroup *group = &pRS->m_Responses[ idx ]; + pRestore->ReadAll( group ); + + short groupCount = pRestore->ReadShort(); + for ( int j = 0; j < groupCount; ++j ) + { + char szResponseBlockName[SIZE_BLOCK_NAME_BUF]; + + char responsename[ 256 ]; + pRestore->StartBlock( szResponseBlockName ); + if ( !Q_stricmp( szResponseBlockName, "Response" ) ) + { + pRestore->ReadString( responsename, sizeof( responsename ), 0 ); + + // Find it by name + int ri; + for ( ri = 0; ri < group->group.Count(); ++ri ) + { + Response *response = &group->group[ ri ]; + if ( !Q_stricmp( response->value, responsename ) ) + { + break; + } + } + + if ( ri < group->group.Count() ) + { + Response *response = &group->group[ ri ]; + pRestore->ReadAll( response ); + } + } + + pRestore->EndBlock(); + } + } + } + + pRestore->EndBlock(); + } + } + +} g_ResponseSystemSaveRestoreOps; + +ISaveRestoreOps *responseSystemSaveRestoreOps = &g_ResponseSystemSaveRestoreOps; + +//----------------------------------------------------------------------------- +// Purpose: +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CDefaultResponseSystem::Init() +{ +/* + Warning( "sizeof( Response ) == %d\n", sizeof( Response ) ); + Warning( "sizeof( ResponseGroup ) == %d\n", sizeof( ResponseGroup ) ); + Warning( "sizeof( Criteria ) == %d\n", sizeof( Criteria ) ); + Warning( "sizeof( AI_ResponseParams ) == %d\n", sizeof( AI_ResponseParams ) ); +*/ + const char *basescript = GetScriptFile(); + + LoadRuleSet( basescript ); + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CDefaultResponseSystem::Shutdown() +{ + // Wipe instanced versions + ClearInstanced(); + + // Clear outselves + Clear(); + // IServerSystem chain + BaseClass::Shutdown(); +} + +//----------------------------------------------------------------------------- +// Purpose: Instance a custom response system +// Input : *scriptfile - +// Output : IResponseSystem +//----------------------------------------------------------------------------- +IResponseSystem *PrecacheCustomResponseSystem( const char *scriptfile ) +{ + return defaultresponsesytem.PrecacheCustomResponseSystem( scriptfile ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CResponseSystem::DumpRules() +{ + int c = m_Rules.Count(); + int i; + + for ( i = 0; i < c; i++ ) + { + Msg("%s\n", m_Rules.GetElementName( i ) ); + } +} diff --git a/dlls/CRagdollMagnet.h b/dlls/CRagdollMagnet.h index 87f20b0d..673a96ae 100644 --- a/dlls/CRagdollMagnet.h +++ b/dlls/CRagdollMagnet.h @@ -1,45 +1,45 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: Used to influence the initial force for a dying NPC's ragdoll. -// Passive entity. Just represents position in the world, radius, force -// -// $NoKeywords: $ -//=============================================================================// -#pragma once - -#ifndef CRAGDOLLMAGNET_H -#define CRAGDOLLMAGNET_H - -#define SF_RAGDOLLMAGNET_BAR 0x00000002 // this is a bar magnet. - -class CRagdollMagnet : public CPointEntity -{ -public: - DECLARE_CLASS( CRagdollMagnet, CPointEntity ); - DECLARE_DATADESC(); - - Vector GetForceVector( CBaseEntity *pNPC ); - float GetRadius( void ) { return m_radius; } - Vector GetAxisVector( void ) { return m_axis - GetAbsOrigin(); } - float DistToPoint( const Vector &vecPoint ); - - bool IsEnabled( void ) { return !m_bDisabled; } - - int IsBarMagnet( void ) { return (m_spawnflags & SF_RAGDOLLMAGNET_BAR); } - - static CRagdollMagnet *FindBestMagnet( CBaseEntity *pNPC ); - - void Enable( bool bEnable ) { m_bDisabled = !bEnable; } - - // Inputs - void InputEnable( inputdata_t &inputdata ); - void InputDisable( inputdata_t &inputdata ); - -private: - bool m_bDisabled; - float m_radius; - float m_force; - Vector m_axis; -}; - -#endif //CRAGDOLLMAGNET_H \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: Used to influence the initial force for a dying NPC's ragdoll. +// Passive entity. Just represents position in the world, radius, force +// +// $NoKeywords: $ +//=============================================================================// +#pragma once + +#ifndef CRAGDOLLMAGNET_H +#define CRAGDOLLMAGNET_H + +#define SF_RAGDOLLMAGNET_BAR 0x00000002 // this is a bar magnet. + +class CRagdollMagnet : public CPointEntity +{ +public: + DECLARE_CLASS( CRagdollMagnet, CPointEntity ); + DECLARE_DATADESC(); + + Vector GetForceVector( CBaseEntity *pNPC ); + float GetRadius( void ) { return m_radius; } + Vector GetAxisVector( void ) { return m_axis - GetAbsOrigin(); } + float DistToPoint( const Vector &vecPoint ); + + bool IsEnabled( void ) { return !m_bDisabled; } + + int IsBarMagnet( void ) { return (m_spawnflags & SF_RAGDOLLMAGNET_BAR); } + + static CRagdollMagnet *FindBestMagnet( CBaseEntity *pNPC ); + + void Enable( bool bEnable ) { m_bDisabled = !bEnable; } + + // Inputs + void InputEnable( inputdata_t &inputdata ); + void InputDisable( inputdata_t &inputdata ); + +private: + bool m_bDisabled; + float m_radius; + float m_force; + Vector m_axis; +}; + +#endif //CRAGDOLLMAGNET_H diff --git a/dlls/EffectsServer.cpp b/dlls/EffectsServer.cpp index 9c6b2744..1ad6f3a1 100644 --- a/dlls/EffectsServer.cpp +++ b/dlls/EffectsServer.cpp @@ -124,7 +124,7 @@ void CEffectsServer::Smoke( const Vector &origin, int mModel, float flScale, flo CPVSFilter filter( origin ); if ( !SuppressTE( filter ) ) { - te->Smoke( filter, 0.0, &origin, mModel, flScale * 0.1f, flFramerate ); + te->Smoke( filter, 0.0, &origin, mModel, flScale * 0.1f, static_cast(flFramerate) ); } } diff --git a/dlls/EntityFlame.cpp b/dlls/EntityFlame.cpp index d105ae11..9d496ce0 100644 --- a/dlls/EntityFlame.cpp +++ b/dlls/EntityFlame.cpp @@ -274,8 +274,8 @@ void CEntityFlame::FlameThink( void ) { const float ENTITYFLAME_MOVE_AWAY_DIST = 24.0f; // Make a sound near my origin, and up a little higher (in case I'm on the ground, so NPC's still hear it) - CSoundEnt::InsertSound( SOUND_MOVE_AWAY, GetAbsOrigin(), ENTITYFLAME_MOVE_AWAY_DIST, 0.1f, this, SOUNDENT_CHANNEL_REPEATED_DANGER ); - CSoundEnt::InsertSound( SOUND_MOVE_AWAY, GetAbsOrigin() + Vector( 0, 0, 48.0f ), ENTITYFLAME_MOVE_AWAY_DIST, 0.1f, this, SOUNDENT_CHANNEL_REPEATING ); + CSoundEnt::InsertSound( SOUND_MOVE_AWAY, GetAbsOrigin(), static_cast(ENTITYFLAME_MOVE_AWAY_DIST), 0.1f, this, SOUNDENT_CHANNEL_REPEATED_DANGER ); + CSoundEnt::InsertSound( SOUND_MOVE_AWAY, GetAbsOrigin() + Vector( 0, 0, 48.0f ), static_cast(ENTITYFLAME_MOVE_AWAY_DIST), 0.1f, this, SOUNDENT_CHANNEL_REPEATING ); } } else diff --git a/dlls/EnvBeam.cpp b/dlls/EnvBeam.cpp index a4b7f277..6e32fc74 100644 --- a/dlls/EnvBeam.cpp +++ b/dlls/EnvBeam.cpp @@ -381,7 +381,7 @@ void CEnvBeam::Strike( void ) if ( pStart == NULL || pEnd == NULL ) return; - m_speed = clamp( m_speed, 0, MAX_BEAM_SCROLLSPEED ); + m_speed = static_cast(clamp( m_speed, 0, MAX_BEAM_SCROLLSPEED )); int pointStart = IsStaticPointEntity( pStart ); int pointEnd = IsStaticPointEntity( pEnd ); @@ -710,7 +710,7 @@ void CEnvBeam::BeamUpdateVars( void ) m_nNumBeamEnts = 2; - m_speed = clamp( m_speed, 0, MAX_BEAM_SCROLLSPEED ); + m_speed = static_cast(clamp( m_speed, 0, MAX_BEAM_SCROLLSPEED )); // NOTE: If the end entity is the beam itself (and the start entity // isn't *also* the beam itself, we've got problems. This is a problem diff --git a/dlls/EnvShake.cpp b/dlls/EnvShake.cpp index 679fe9eb..d59e73d9 100644 --- a/dlls/EnvShake.cpp +++ b/dlls/EnvShake.cpp @@ -245,6 +245,8 @@ void CEnvShake::ApplyShake( ShakeCommand_t command ) case SHAKE_FREQUENCY: m_pShakeController->WakeObjects(); break; + default: + break; } } } diff --git a/dlls/SkyCamera.cpp b/dlls/SkyCamera.cpp index 843b7bd6..4fcc295e 100644 --- a/dlls/SkyCamera.cpp +++ b/dlls/SkyCamera.cpp @@ -61,6 +61,7 @@ END_DATADESC() //----------------------------------------------------------------------------- // List of maps in HL2 that we must apply our skybox fog fixup hack to //----------------------------------------------------------------------------- +#ifdef HL2_DLL static const char *s_pBogusFogMaps[] = { "d1_canals_01", @@ -84,6 +85,7 @@ static const char *s_pBogusFogMaps[] = "d3_citadel_01", NULL }; +#endif //----------------------------------------------------------------------------- // Constructor, destructor diff --git a/dlls/TemplateEntities.cpp b/dlls/TemplateEntities.cpp index 1c85bcea..db98532e 100644 --- a/dlls/TemplateEntities.cpp +++ b/dlls/TemplateEntities.cpp @@ -309,7 +309,7 @@ void Templates_StartUniqueInstance( void ) g_iCurrentTemplateInstance++; // Make sure there's enough room to fit it into the string - int iMax = pow(10.0f, (int)((strlen(ENTITYIO_FIXUP_STRING) - 1))); // -1 for the & + int iMax = static_cast(pow(10.0f, (int)((strlen(ENTITYIO_FIXUP_STRING) - 1)))); // -1 for the & if ( g_iCurrentTemplateInstance >= iMax ) { // We won't hit this. diff --git a/dlls/ai_baseactor.cpp b/dlls/ai_baseactor.cpp index e0a2169e..d42e25b6 100644 --- a/dlls/ai_baseactor.cpp +++ b/dlls/ai_baseactor.cpp @@ -629,29 +629,22 @@ bool CAI_BaseActor::CheckSceneEventCompletion( CSceneEventInfo *info, float curr Assert( info->m_pScene ); Assert( info->m_pEvent ); - switch ( event->GetType() ) + if (event->GetType() == CChoreoEvent::GENERIC) { - case CChoreoEvent::GENERIC: + if (info->m_nType == SCENE_AI_HOLSTER || info->m_nType == SCENE_AI_UNHOLSTER) { - switch( info->m_nType) + if (info->m_iLayer == -1) { - case SCENE_AI_HOLSTER: - case SCENE_AI_UNHOLSTER: - { - if (info->m_iLayer == -1) - { - return true; - } - float preload = event->GetEndTime() - currenttime; - if (preload < 0) - { - return true; - } - float t = (1.0 - GetLayerCycle( info->m_iLayer )) * SequenceDuration( GetLayerSequence( info->m_iLayer ) ); - - return (t <= preload); - } + return true; } + float preload = event->GetEndTime() - currenttime; + if (preload < 0) + { + return true; + } + float t = (1.0 - GetLayerCycle( info->m_iLayer )) * SequenceDuration( GetLayerSequence( info->m_iLayer ) ); + + return (t <= preload); } } @@ -1665,6 +1658,8 @@ void CAI_BaseActor::PlayExpressionForState( NPC_STATE state ) SetExpression( STRING(m_iszDeathExpression) ); } break; + default: + break; } } diff --git a/dlls/ai_baseactor.h b/dlls/ai_baseactor.h index 66720edd..a94ce0ec 100644 --- a/dlls/ai_baseactor.h +++ b/dlls/ai_baseactor.h @@ -81,11 +81,11 @@ public: m_flBlinktime( 0 ), m_hLookTarget( NULL ), m_iszExpressionScene( NULL_STRING ), + m_iszExpressionOverride( NULL_STRING ), m_iszIdleExpression( NULL_STRING ), m_iszAlertExpression( NULL_STRING ), m_iszCombatExpression( NULL_STRING ), - m_iszDeathExpression( NULL_STRING ), - m_iszExpressionOverride( NULL_STRING ) + m_iszDeathExpression( NULL_STRING ) { memset( m_flextarget, 0, 64 * sizeof( m_flextarget[0] ) ); } diff --git a/dlls/ai_basenpc.cpp b/dlls/ai_basenpc.cpp index a4015e72..f3caf305 100644 --- a/dlls/ai_basenpc.cpp +++ b/dlls/ai_basenpc.cpp @@ -1,13658 +1,13681 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -//=============================================================================// - -#include "cbase.h" - -#include "ai_basenpc.h" -#include "fmtstr.h" -#include "activitylist.h" -#include "animation.h" -#include "basecombatweapon.h" -#include "soundent.h" -#include "decals.h" -#include "entitylist.h" -#include "eventqueue.h" -#include "entityapi.h" -#include "bitstring.h" -#include "gamerules.h" // For g_pGameRules -#include "scripted.h" -#include "worldsize.h" -#include "game.h" -#include "shot_manipulator.h" - -#ifdef HL2_DLL -#include "ai_interactions.h" -#include "hl2_gamerules.h" -#endif // HL2_DLL - -#include "ai_network.h" -#include "ai_networkmanager.h" -#include "ai_pathfinder.h" -#include "ai_node.h" -#include "ai_default.h" -#include "ai_schedule.h" -#include "ai_task.h" -#include "ai_hull.h" -#include "ai_moveprobe.h" -#include "ai_hint.h" -#include "ai_navigator.h" -#include "ai_senses.h" -#include "ai_squadslot.h" -#include "ai_memory.h" -#include "ai_squad.h" -#include "ai_localnavigator.h" -#include "ai_tacticalservices.h" -#include "ai_behavior.h" -#include "ai_dynamiclink.h" -#include "AI_Criteria.h" -#include "basegrenade_shared.h" -#include "ammodef.h" -#include "player.h" -#include "sceneentity.h" -#include "ndebugoverlay.h" -#include "mathlib.h" -#include "bone_setup.h" -#include "IEffects.h" -#include "vstdlib/random.h" -#include "engine/IEngineSound.h" -#include "vstdlib/strtools.h" -#include "doors.h" -#include "BasePropDoor.h" -#include "saverestore_utlvector.h" -#include "npcevent.h" -#include "movevars_shared.h" -#include "te_effect_dispatch.h" -#include "globals.h" -#include "saverestore_bitstring.h" -#include "checksum_crc.h" -#include "iservervehicle.h" -#include "filters.h" -#ifdef HL2_DLL -#include "npc_bullseye.h" -#include "hl2_player.h" -#include "weapon_physcannon.h" -#endif -#include "waterbullet.h" -#include "in_buttons.h" -#include "eventlist.h" -#include "globalstate.h" -#include "physics_prop_ragdoll.h" -#include "vphysics/friction.h" -#include "physics_npc_solver.h" -#include "tier0/vcrmode.h" -#include "death_pose.h" -#include "datacache/imdlcache.h" -#ifdef HL2_EPISODIC -#include "npc_alyx_episodic.h" -#endif - -#include "env_debughistory.h" -#include "collisionutils.h" - -extern ConVar sk_healthkit; - -// dvs: for opening doors -- these should probably not be here -#include "ai_route.h" -#include "ai_waypoint.h" - -// memdbgon must be the last include file in a .cpp file!!! -#include "tier0/memdbgon.h" - -//#define DEBUG_LOOK - -bool RagdollManager_SaveImportant( CAI_BaseNPC *pNPC ); - -#define MIN_PHYSICS_FLINCH_DAMAGE 5.0f - -#define NPC_GRENADE_FEAR_DIST 200 -#define MAX_GLASS_PENETRATION_DEPTH 16.0f - -#define FINDNAMEDENTITY_MAX_ENTITIES 32 // max number of entities to be considered for random entity selection in FindNamedEntity - -extern bool g_fDrawLines; -extern short g_sModelIndexLaser; // holds the index for the laser beam -extern short g_sModelIndexLaserDot; // holds the index for the laser beam dot - -// Debugging tools -ConVar ai_no_select_box( "ai_no_select_box", "0" ); - -ConVar ai_show_think_tolerance( "ai_show_think_tolerance", "0" ); -ConVar ai_debug_think_ticks( "ai_debug_think_ticks", "0" ); -ConVar ai_debug_doors( "ai_debug_doors", "0" ); -ConVar ai_debug_enemies( "ai_debug_enemies", "0" ); - -ConVar ai_rebalance_thinks( "ai_rebalance_thinks", "1" ); -ConVar ai_use_efficiency( "ai_use_efficiency", "1" ); -ConVar ai_use_frame_think_limits( "ai_use_frame_think_limits", "1" ); -ConVar ai_default_efficient( "ai_default_efficient", ( IsXbox() ) ? "1" : "0" ); -ConVar ai_efficiency_override( "ai_efficiency_override", "0" ); -ConVar ai_debug_efficiency( "ai_debug_efficiency", "0" ); -ConVar ai_debug_dyninteractions( "ai_debug_dyninteractions", "0", FCVAR_NONE, "Debug the NPC dynamic interaction system." ); -ConVar ai_frametime_limit( "ai_frametime_limit", "50", FCVAR_NONE, "frametime limit for min efficiency AIE_NORMAL (in sec's)." ); - -ConVar ai_use_think_optimizations( "ai_use_think_optimizations", "1" ); - -ConVar ai_test_moveprobe_ignoresmall( "ai_test_moveprobe_ignoresmall", "0" ); - -#ifndef _RETAIL -#define ShouldUseEfficiency() ( ai_use_think_optimizations.GetBool() && ai_use_efficiency.GetBool() ) -#define ShouldUseFrameThinkLimits() ( ai_use_think_optimizations.GetBool() && ai_use_frame_think_limits.GetBool() ) -#define ShouldRebalanceThinks() ( ai_use_think_optimizations.GetBool() && ai_rebalance_thinks.GetBool() ) -#define ShouldDefaultEfficient() ( ai_use_think_optimizations.GetBool() && ai_default_efficient.GetBool() ) -#else -#define ShouldUseEfficiency() ( true ) -#define ShouldUseFrameThinkLimits() ( true ) -#define ShouldRebalanceThinks() ( true ) -#define ShouldDefaultEfficient() ( true ) -#endif - -#ifndef _RETAIL -#define DbgEnemyMsg if ( !ai_debug_enemies.GetBool() ) ; else DevMsg -#else -#define DbgEnemyMsg if ( 0 ) ; else DevMsg -#endif - -#ifdef DEBUG_AI_FRAME_THINK_LIMITS -#define DbgFrameLimitMsg DevMsg -#else -#define DbgFrameLimitMsg (void) -#endif - -// NPC damage adjusters -ConVar sk_npc_head( "sk_npc_head","2" ); -ConVar sk_npc_chest( "sk_npc_chest","1" ); -ConVar sk_npc_stomach( "sk_npc_stomach","1" ); -ConVar sk_npc_arm( "sk_npc_arm","1" ); -ConVar sk_npc_leg( "sk_npc_leg","1" ); -ConVar showhitlocation( "showhitlocation", "0" ); - -// Squad debugging -ConVar ai_debug_squads( "ai_debug_squads", "0" ); -ConVar ai_debug_loners( "ai_debug_loners", "0" ); - -// Shoot trajectory -ConVar ai_lead_time( "ai_lead_time","0.0" ); -ConVar ai_shot_stats( "ai_shot_stats", "0" ); -ConVar ai_shot_stats_term( "ai_shot_stats_term", "1000" ); -ConVar ai_shot_bias( "ai_shot_bias", "1.0" ); - -ConVar ai_spread_defocused_cone_multiplier( "ai_spread_defocused_cone_multiplier","3.0" ); -ConVar ai_spread_cone_focus_time( "ai_spread_cone_focus_time","0.6" ); -ConVar ai_spread_pattern_focus_time( "ai_spread_pattern_focus_time","0.8" ); - -ConVar ai_reaction_delay_idle( "ai_reaction_delay_idle","0.3" ); -ConVar ai_reaction_delay_alert( "ai_reaction_delay_alert", "0.1" ); - -//----------------------------------------------------------------------------- -// -// Crude frame timings -// - -CFastTimer g_AIRunTimer; -CFastTimer g_AIPostRunTimer; -CFastTimer g_AIMoveTimer; - -CFastTimer g_AIConditionsTimer; -CFastTimer g_AIPrescheduleThinkTimer; -CFastTimer g_AIMaintainScheduleTimer; - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- - -CAI_Manager g_AI_Manager; - -//------------------------------------- - -CAI_Manager::CAI_Manager() -{ - m_AIs.EnsureCapacity( MAX_AIS ); -} - -//------------------------------------- - -CAI_BaseNPC **CAI_Manager::AccessAIs() -{ - if (m_AIs.Count()) - return &m_AIs[0]; - return NULL; -} - -//------------------------------------- - -int CAI_Manager::NumAIs() -{ - return m_AIs.Count(); -} - -//------------------------------------- - -void CAI_Manager::AddAI( CAI_BaseNPC *pAI ) -{ - m_AIs.AddToTail( pAI ); -} - -//------------------------------------- - -void CAI_Manager::RemoveAI( CAI_BaseNPC *pAI ) -{ - int i = m_AIs.Find( pAI ); - - if ( i != -1 ) - m_AIs.FastRemove( i ); -} - - -//----------------------------------------------------------------------------- - -// ================================================================ -// Init static data -// ================================================================ -int CAI_BaseNPC::m_nDebugBits = 0; -CAI_BaseNPC* CAI_BaseNPC::m_pDebugNPC = NULL; -int CAI_BaseNPC::m_nDebugPauseIndex = -1; - -CAI_ClassScheduleIdSpace CAI_BaseNPC::gm_ClassScheduleIdSpace( true ); -CAI_GlobalScheduleNamespace CAI_BaseNPC::gm_SchedulingSymbols; -CAI_LocalIdSpace CAI_BaseNPC::gm_SquadSlotIdSpace( true ); - -string_t CAI_BaseNPC::gm_iszPlayerSquad; - -int CAI_BaseNPC::gm_iNextThinkRebalanceTick; -float CAI_BaseNPC::gm_flTimeLastSpawn; -int CAI_BaseNPC::gm_nSpawnedThisFrame; - -CSimpleSimTimer CAI_BaseNPC::m_AnyUpdateEnemyPosTimer; - -// ================================================================ -// Class Methods -// ================================================================ - -//----------------------------------------------------------------------------- -// Purpose: Static debug function to clear schedules for all NPCS -// Input : -// Output : -//----------------------------------------------------------------------------- -void CAI_BaseNPC::ClearAllSchedules(void) -{ - CAI_BaseNPC *npc = gEntList.NextEntByClass( (CAI_BaseNPC *)NULL ); - - while (npc) - { - npc->ClearSchedule(); - npc->GetNavigator()->ClearGoal(); - npc = gEntList.NextEntByClass(npc); - } -} - -// ============================================================================== - -//----------------------------------------------------------------------------- -// Purpose: -// Input : -// Output : -//----------------------------------------------------------------------------- -bool CAI_BaseNPC::Event_Gibbed( const CTakeDamageInfo &info ) -{ - bool gibbed = CorpseGib( info ); - - if ( gibbed ) - { - // don't remove players! - UTIL_Remove( this ); - SetThink( NULL ); //We're going away, so don't think anymore. - } - else - { - CorpseFade(); - } - - return gibbed; -} - -//========================================================= -// GetFlinchActivity - determines the best type of flinch -// anim to play. -//========================================================= -Activity CAI_BaseNPC::GetFlinchActivity( bool bHeavyDamage, bool bGesture ) -{ - Activity flinchActivity; - - switch ( LastHitGroup() ) - { - // pick a region-specific flinch - case HITGROUP_HEAD: - flinchActivity = bGesture ? ACT_GESTURE_FLINCH_HEAD : ACT_FLINCH_HEAD; - break; - case HITGROUP_STOMACH: - flinchActivity = bGesture ? ACT_GESTURE_FLINCH_STOMACH : ACT_FLINCH_STOMACH; - break; - case HITGROUP_LEFTARM: - flinchActivity = bGesture ? ACT_GESTURE_FLINCH_LEFTARM : ACT_FLINCH_LEFTARM; - break; - case HITGROUP_RIGHTARM: - flinchActivity = bGesture ? ACT_GESTURE_FLINCH_RIGHTARM : ACT_FLINCH_RIGHTARM; - break; - case HITGROUP_LEFTLEG: - flinchActivity = bGesture ? ACT_GESTURE_FLINCH_LEFTLEG : ACT_FLINCH_LEFTLEG; - break; - case HITGROUP_RIGHTLEG: - flinchActivity = bGesture ? ACT_GESTURE_FLINCH_RIGHTLEG : ACT_FLINCH_RIGHTLEG; - break; - case HITGROUP_CHEST: - flinchActivity = bGesture ? ACT_GESTURE_FLINCH_CHEST : ACT_FLINCH_CHEST; - break; - case HITGROUP_GEAR: - case HITGROUP_GENERIC: - default: - // just get a generic flinch. - if ( bHeavyDamage ) - { - flinchActivity = bGesture ? ACT_GESTURE_BIG_FLINCH : ACT_BIG_FLINCH; - } - else - { - flinchActivity = bGesture ? ACT_GESTURE_SMALL_FLINCH : ACT_SMALL_FLINCH; - } - break; - } - - // do we have a sequence for the ideal activity? - if ( SelectWeightedSequence ( flinchActivity ) == ACTIVITY_NOT_AVAILABLE ) - { - if ( bHeavyDamage ) - { - flinchActivity = bGesture ? ACT_GESTURE_BIG_FLINCH : ACT_BIG_FLINCH; - - // If we fail at finding a big flinch, resort to a small one - if ( SelectWeightedSequence ( flinchActivity ) == ACTIVITY_NOT_AVAILABLE ) - { - flinchActivity = bGesture ? ACT_GESTURE_SMALL_FLINCH : ACT_SMALL_FLINCH; - } - } - else - { - flinchActivity = bGesture ? ACT_GESTURE_SMALL_FLINCH : ACT_SMALL_FLINCH; - } - } - - return flinchActivity; -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : -//----------------------------------------------------------------------------- -void CAI_BaseNPC::CleanupOnDeath( CBaseEntity *pCulprit, bool bFireDeathOutput ) -{ - if ( !m_bDidDeathCleanup ) - { - m_bDidDeathCleanup = true; - - if ( m_NPCState == NPC_STATE_SCRIPT && m_hCine ) - { - // bail out of this script here - m_hCine->CancelScript(); - // now keep going with the death code - } - - if ( GetHintNode() ) - { - GetHintNode()->Unlock(); - SetHintNode( NULL ); - } - - if( bFireDeathOutput ) - { - m_OnDeath.FireOutput( pCulprit, this ); - } - - // Vacate any strategy slot I might have - VacateStrategySlot(); - - // Remove from squad if in one - if (m_pSquad) - { - // If I'm in idle it means that I didn't see who killed me - // and my squad is still in idle state. Tell squad we have - // an enemy to wake them up and put the enemy position at - // my death position - if ( m_NPCState == NPC_STATE_IDLE && pCulprit) - { - // If we already have some danger memory, don't do this cheat - if ( GetEnemies()->GetDangerMemory() == NULL ) - { - UpdateEnemyMemory( pCulprit, GetAbsOrigin() ); - } - } - - // Remove from squad - m_pSquad->RemoveFromSquad(this, true); - m_pSquad = NULL; - } - - RemoveActorFromScriptedScenes( this, false /*all scenes*/ ); - } - else - DevMsg( "Unexpected double-death-cleanup\n" ); -} - -void CAI_BaseNPC::SelectDeathPose( const CTakeDamageInfo &info ) -{ - if ( !GetModelPtr() || (info.GetDamageType() & DMG_PREVENT_PHYSICS_FORCE) ) - return; - - if ( ShouldPickADeathPose() == false ) - return; - - Activity aActivity = ACT_INVALID; - int iDeathFrame = 0; - - SelectDeathPoseActivityAndFrame( this, info, LastHitGroup(), aActivity, iDeathFrame ); - if ( aActivity == ACT_INVALID ) - { - SetDeathPose( ACT_INVALID ); - SetDeathPoseFrame( 0 ); - return; - } - - SetDeathPose( SelectWeightedSequence( aActivity ) ); - SetDeathPoseFrame( iDeathFrame ); -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : -//----------------------------------------------------------------------------- -void CAI_BaseNPC::Event_Killed( const CTakeDamageInfo &info ) -{ - if (IsCurSchedule(SCHED_NPC_FREEZE)) - { - // We're frozen; don't die. - return; - } - - Wake( false ); - - //Adrian: Select a death pose to extrapolate the ragdoll's velocity. - SelectDeathPose( info ); - - m_lifeState = LIFE_DYING; - - CleanupOnDeath( info.GetAttacker() ); - - StopLoopingSounds(); - DeathSound( info ); - - if ( ( GetFlags() & FL_NPC ) && ( ShouldGib( info ) == false ) ) - { - SetTouch( NULL ); - } - - BaseClass::Event_Killed( info ); - - - if ( m_bFadeCorpse ) - { - m_bImportanRagdoll = RagdollManager_SaveImportant( this ); - } - - // Make sure this condition is fired too (OnTakeDamage breaks out before this happens on death) - SetCondition( COND_LIGHT_DAMAGE ); - SetIdealState( NPC_STATE_DEAD ); - - // Some characters rely on getting a state transition, even to death. - // zombies, for instance. When a character becomes a ragdoll, their - // server entity ceases to think, so we have to set the dead state here - // because the AI code isn't going to pick up the change on the next think - // for us. - - // Adrian - Only set this if we are going to become a ragdoll. We still want to - // select SCHED_DIE or do something special when this NPC dies and we wont - // catch the change of state if we set this to whatever the ideal state is. - if ( CanBecomeRagdoll() || IsRagdoll() ) - SetState( NPC_STATE_DEAD ); - - // If the remove-no-ragdoll flag is set in the damage type, we're being - // told to remove ourselves immediately on death. This is used when something - // else has some special reason for us to vanish instead of creating a ragdoll. - // i.e. The barnacle does this because it's already got a ragdoll for us. - if ( info.GetDamageType() & DMG_REMOVENORAGDOLL ) - { - if ( !IsEFlagSet( EFL_IS_BEING_LIFTED_BY_BARNACLE ) ) - { - // Go away - RemoveDeferred(); - } - } -} - -//----------------------------------------------------------------------------- - -void CAI_BaseNPC::Ignite( float flFlameLifetime, bool bNPCOnly, float flSize, bool bCalledByLevelDesigner ) -{ - BaseClass::Ignite( flFlameLifetime, bNPCOnly, flSize, bCalledByLevelDesigner ); - -#ifdef HL2_EPISODIC - CBasePlayer *pPlayer = AI_GetSinglePlayer(); - if ( pPlayer->IRelationType( this ) != D_LI ) - { - CNPC_Alyx *alyx = CNPC_Alyx::GetAlyx(); - - if ( alyx ) - { - alyx->EnemyIgnited( this ); - } - } -#endif -} - -//----------------------------------------------------------------------------- - -ConVar ai_block_damage( "ai_block_damage","0" ); - -bool CAI_BaseNPC::PassesDamageFilter( const CTakeDamageInfo &info ) -{ - if ( ai_block_damage.GetBool() ) - return false; - // FIXME: hook a friendly damage filter to the npc instead? - if ( (CapabilitiesGet() & bits_CAP_FRIENDLY_DMG_IMMUNE) && info.GetAttacker() && info.GetAttacker() != this ) - { - // check attackers relationship with me - CBaseCombatCharacter *npcEnemy = info.GetAttacker()->MyCombatCharacterPointer(); - bool bHitByVehicle = false; - if ( !npcEnemy ) - { - if ( info.GetAttacker()->GetServerVehicle() ) - { - bHitByVehicle = true; - } - } - - if ( bHitByVehicle || (npcEnemy && npcEnemy->IRelationType( this ) == D_LI) ) - { - m_fNoDamageDecal = true; - - if ( npcEnemy && npcEnemy->IsPlayer() ) - { - m_OnDamagedByPlayer.FireOutput( info.GetAttacker(), this ); - // This also counts as being harmed by player's squad. - m_OnDamagedByPlayerSquad.FireOutput( info.GetAttacker(), this ); - } - - return false; - } - } - - if ( !BaseClass::PassesDamageFilter( info ) ) - { - m_fNoDamageDecal = true; - return false; - } - return true; -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : -// Output : -//----------------------------------------------------------------------------- -int CAI_BaseNPC::OnTakeDamage_Alive( const CTakeDamageInfo &info ) -{ - Forget( bits_MEMORY_INCOVER ); - - if ( !BaseClass::OnTakeDamage_Alive( info ) ) - return 0; - - if ( GetSleepState() == AISS_WAITING_FOR_THREAT ) - Wake(); - - // NOTE: This must happen after the base class is called; we need to reduce - // health before the pain sound, since some NPCs use the final health - // level as a modifier to determine which pain sound to use. - - // REVISIT: Combine soldiers shoot each other a lot and then talk about it - // this improves that case a bunch, but it seems kind of harsh. - if ( !m_pSquad || !m_pSquad->SquadIsMember( info.GetAttacker() ) ) - { - PainSound( info );// "Ouch!" - } - - // See if we're running a dynamic interaction that should break when I am damaged. - if ( IsActiveDynamicInteraction() ) - { - ScriptedNPCInteraction_t *pInteraction = GetRunningDynamicInteraction(); - if ( pInteraction->iLoopBreakTriggerMethod & SNPCINT_LOOPBREAK_ON_DAMAGE ) - { - // Can only break when we're in the action anim - if ( m_hCine->IsPlayingAction() ) - { - m_hCine->StopActionLoop( true ); - } - } - } - - // If we're not allowed to die, refuse to die - // Allow my interaction partner to kill me though - if ( m_iHealth <= 0 && HasInteractionCantDie() && info.GetAttacker() != m_hInteractionPartner ) - { - m_iHealth = 1; - } - -#if 0 - // HACKHACK Don't kill npcs in a script. Let them break their scripts first - // THIS is a Half-Life 1 hack that's not cutting the mustard in the scripts - // that have been authored for Half-Life 2 thus far. (sjb) - if ( m_NPCState == NPC_STATE_SCRIPT ) - { - SetCondition( COND_LIGHT_DAMAGE ); - } -#endif - - // ----------------------------------- - // Fire outputs - // ----------------------------------- - if ( m_flLastDamageTime != gpGlobals->curtime ) - { - // only fire once per frame - m_OnDamaged.FireOutput( info.GetAttacker(), this); - - if( info.GetAttacker()->IsPlayer() ) - { - 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 = dynamic_cast( info.GetAttacker() ); - CBasePlayer *pPlayer = AI_GetSinglePlayer(); - - if( pAttacker && pAttacker->IsAlive() && pPlayer ) - { - if( pAttacker->GetSquad() != NULL && pAttacker->IsInPlayerSquad() ) - { - m_OnDamagedByPlayerSquad.FireOutput( info.GetAttacker(), this ); - } - } - } - } - - if( (info.GetDamageType() & DMG_CRUSH) && !(info.GetDamageType() & DMG_PHYSGUN) && info.GetDamage() >= MIN_PHYSICS_FLINCH_DAMAGE ) - { - SetCondition( COND_PHYSICS_DAMAGE ); - } - - if ( m_iHealth <= ( m_iMaxHealth / 2 ) ) - { - m_OnHalfHealth.FireOutput( info.GetAttacker(), this ); - } - - // react to the damage (get mad) - if ( ( (GetFlags() & FL_NPC) == 0 ) || !info.GetAttacker() ) - return 1; - - // If the attacker was an NPC or client update my position memory - if ( info.GetAttacker()->GetFlags() & (FL_NPC | FL_CLIENT) ) - { - // ------------------------------------------------------------------ - // DO NOT CHANGE THIS CODE W/O CONSULTING - // Only update information about my attacker I don't see my attacker - // ------------------------------------------------------------------ - if ( !FInViewCone( info.GetAttacker() ) || !FVisible( info.GetAttacker() ) ) - { - // ------------------------------------------------------------- - // If I have an inflictor (enemy / grenade) update memory with - // position of inflictor, otherwise update with an position - // estimate for where the attack came from - // ------------------------------------------------------ - Vector vAttackPos; - if (info.GetInflictor()) - { - vAttackPos = info.GetInflictor()->GetAbsOrigin(); - } - else - { - vAttackPos = (GetAbsOrigin() + ( g_vecAttackDir * 64 )); - } - - - // ---------------------------------------------------------------- - // If I already have an enemy, assume that the attack - // came from the enemy and update my enemy's position - // unless I already know about the attacker or I can see my enemy - // ---------------------------------------------------------------- - if ( GetEnemy() != NULL && - !GetEnemies()->HasMemory( info.GetAttacker() ) && - !HasCondition(COND_SEE_ENEMY) ) - { - UpdateEnemyMemory(GetEnemy(), vAttackPos, GetEnemy()); - } - // ---------------------------------------------------------------- - // If I already know about this enemy, update his position - // ---------------------------------------------------------------- - else if (GetEnemies()->HasMemory( info.GetAttacker() )) - { - UpdateEnemyMemory(info.GetAttacker(), vAttackPos); - } - // ----------------------------------------------------------------- - // Otherwise just note the position, but don't add enemy to my list - // ----------------------------------------------------------------- - else - { - UpdateEnemyMemory(NULL, vAttackPos); - } - } - - // add pain to the conditions - if ( IsLightDamage( info ) ) - { - SetCondition( COND_LIGHT_DAMAGE ); - } - if ( IsHeavyDamage( info ) ) - { - SetCondition( COND_HEAVY_DAMAGE ); - } - - ForceGatherConditions(); - - // Keep track of how much consecutive damage I have recieved - if ((gpGlobals->curtime - m_flLastDamageTime) < 1.0) - { - m_flSumDamage += info.GetDamage(); - } - else - { - m_flSumDamage = info.GetDamage(); - } - m_flLastDamageTime = gpGlobals->curtime; - if ( info.GetAttacker() && info.GetAttacker()->IsPlayer() ) - m_flLastPlayerDamageTime = gpGlobals->curtime; - GetEnemies()->OnTookDamageFrom( info.GetAttacker() ); - - if (m_flSumDamage > m_iMaxHealth*0.3) - { - SetCondition(COND_REPEATED_DAMAGE); - } - - NotifyFriendsOfDamage( info.GetAttacker() ); - } - - // --------------------------------------------------------------- - // Insert a combat sound so that nearby NPCs know I've been hit - // --------------------------------------------------------------- - CSoundEnt::InsertSound( SOUND_COMBAT, GetAbsOrigin(), 1024, 0.5, this, SOUNDENT_CHANNEL_INJURY ); - - return 1; -} - - -//========================================================= -// OnTakeDamage_Dying - takedamage function called when a npc's -// corpse is damaged. -//========================================================= -int CAI_BaseNPC::OnTakeDamage_Dying( const CTakeDamageInfo &info ) -{ - if ( info.GetDamageType() & DMG_PLASMA ) - { - if ( m_takedamage != DAMAGE_EVENTS_ONLY ) - { - m_iHealth -= info.GetDamage(); - - if (m_iHealth < -500) - { - UTIL_Remove(this); - } - } - } - return BaseClass::OnTakeDamage_Dying( info ); -} - -//========================================================= -// OnTakeDamage_Dead - takedamage function called when a npc's -// corpse is damaged. -//========================================================= -int CAI_BaseNPC::OnTakeDamage_Dead( const CTakeDamageInfo &info ) -{ - Vector vecDir; - - // grab the vector of the incoming attack. ( pretend that the inflictor is a little lower than it really is, so the body will tend to fly upward a bit). - vecDir = vec3_origin; - if ( info.GetInflictor() ) - { - vecDir = info.GetInflictor()->WorldSpaceCenter() - Vector ( 0, 0, 10 ) - WorldSpaceCenter(); - VectorNormalize( vecDir ); - g_vecAttackDir = vecDir; - } - -#if 0// turn this back on when the bounding box issues are resolved. - - SetGroundEntity( NULL ); - GetLocalOrigin().z += 1; - - // let the damage scoot the corpse around a bit. - if ( info.GetInflictor() && (info.GetAttacker()->GetSolid() != SOLID_TRIGGER) ) - { - ApplyAbsVelocityImpulse( vecDir * -DamageForce( flDamage ) ); - } - -#endif - - // kill the corpse if enough damage was done to destroy the corpse and the damage is of a type that is allowed to destroy the corpse. - if ( info.GetDamageType() & DMG_GIB_CORPSE ) - { - // Accumulate corpse gibbing damage, so you can gib with multiple hits - if ( m_takedamage != DAMAGE_EVENTS_ONLY ) - { - m_iHealth -= info.GetDamage() * 0.1; - } - } - - if ( info.GetDamageType() & DMG_PLASMA ) - { - if ( m_takedamage != DAMAGE_EVENTS_ONLY ) - { - m_iHealth -= info.GetDamage(); - - if (m_iHealth < -500) - { - UTIL_Remove(this); - } - } - } - - return 1; -} - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -void CAI_BaseNPC::NotifyFriendsOfDamage( CBaseEntity *pAttackerEntity ) -{ - CAI_BaseNPC *pAttacker = pAttackerEntity->MyNPCPointer(); - if ( pAttacker ) - { - const Vector &origin = GetAbsOrigin(); - for ( int i = 0; i < g_AI_Manager.NumAIs(); i++ ) - { - const float NEAR_Z = 10*12; - const float NEAR_XY_SQ = Square( 50*12 ); - CAI_BaseNPC *pNpc = g_AI_Manager.AccessAIs()[i]; - if ( pNpc && pNpc != this ) - { - const Vector &originNpc = pNpc->GetAbsOrigin(); - if ( fabsf( originNpc.z - origin.z ) < NEAR_Z ) - { - if ( (originNpc.AsVector2D() - origin.AsVector2D()).LengthSqr() < NEAR_XY_SQ ) - { - if ( pNpc->GetSquad() == GetSquad() || IRelationType( pNpc ) == D_LI ) - pNpc->OnFriendDamaged( this, pAttacker ); - } - } - } - } - } -} - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -void CAI_BaseNPC::OnFriendDamaged( CBaseCombatCharacter *pSquadmate, CBaseEntity *pAttacker ) -{ - if ( GetSleepState() != AISS_WAITING_FOR_INPUT ) - { - float distSqToThreat = ( GetAbsOrigin() - pAttacker->GetAbsOrigin() ).LengthSqr(); - - if ( GetSleepState() != AISS_AWAKE && distSqToThreat < Square( 20 * 12 ) ) - Wake(); - - if ( distSqToThreat < Square( 50 * 12 ) ) - ForceGatherConditions(); - } -} - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -bool CAI_BaseNPC::IsLightDamage( const CTakeDamageInfo &info ) -{ - // ALL nonzero damage is light damage! Mask off COND_LIGHT_DAMAGE if you want to ignore light damage. - return ( info.GetDamage() > 0 ); -} - -bool CAI_BaseNPC::IsHeavyDamage( const CTakeDamageInfo &info ) -{ - return ( info.GetDamage() > 20 ); -} - -void CAI_BaseNPC::DoRadiusDamage( const CTakeDamageInfo &info, int iClassIgnore, CBaseEntity *pEntityIgnore ) -{ - RadiusDamage( info, GetAbsOrigin(), info.GetDamage() * 2.5, iClassIgnore, pEntityIgnore ); -} - - -void CAI_BaseNPC::DoRadiusDamage( const CTakeDamageInfo &info, const Vector &vecSrc, int iClassIgnore, CBaseEntity *pEntityIgnore ) -{ - RadiusDamage( info, vecSrc, info.GetDamage() * 2.5, iClassIgnore, pEntityIgnore ); -} - - -//----------------------------------------------------------------------------- -// Set the contents types that are solid by default to all NPCs -//----------------------------------------------------------------------------- -unsigned int CAI_BaseNPC::PhysicsSolidMaskForEntity( void ) const -{ - return MASK_NPCSOLID; -} - - -//========================================================= - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CAI_BaseNPC::DecalTrace( trace_t *pTrace, char const *decalName ) -{ - if ( m_fNoDamageDecal ) - { - m_fNoDamageDecal = false; - // @Note (toml 04-23-03): e3, don't decal face on damage if still alive - return; - } - BaseClass::DecalTrace( pTrace, decalName ); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CAI_BaseNPC::ImpactTrace( trace_t *pTrace, int iDamageType, char *pCustomImpactName ) -{ - if ( m_fNoDamageDecal ) - { - m_fNoDamageDecal = false; - // @Note (toml 04-23-03): e3, don't decal face on damage if still alive - return; - } - BaseClass::ImpactTrace( pTrace, iDamageType, pCustomImpactName ); -} - -//--------------------------------------------------------- -// Return the number by which to multiply incoming damage -// based on the hitgroup it hits. This exists mainly so -// that individual NPC's can have more or less resistance -// to damage done to certain hitgroups. -//--------------------------------------------------------- -float CAI_BaseNPC::GetHitgroupDamageMultiplier( int iHitGroup, const CTakeDamageInfo &info ) -{ - switch( iHitGroup ) - { - case HITGROUP_GENERIC: - return 1.0f; - - case HITGROUP_HEAD: - return sk_npc_head.GetFloat(); - - case HITGROUP_CHEST: - return sk_npc_chest.GetFloat(); - - case HITGROUP_STOMACH: - return sk_npc_stomach.GetFloat(); - - case HITGROUP_LEFTARM: - case HITGROUP_RIGHTARM: - return sk_npc_arm.GetFloat(); - - case HITGROUP_LEFTLEG: - case HITGROUP_RIGHTLEG: - return sk_npc_leg.GetFloat(); - - default: - return 1.0f; - } -} - -//========================================================= -// TraceAttack -//========================================================= -void CAI_BaseNPC::TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr ) -{ - m_fNoDamageDecal = false; - if ( m_takedamage == DAMAGE_NO ) - return; - - CTakeDamageInfo subInfo = info; - - SetLastHitGroup( ptr->hitgroup ); - m_nForceBone = ptr->physicsbone; // save this bone for physics forces - - Assert( m_nForceBone > -255 && m_nForceBone < 256 ); - - bool bDebug = showhitlocation.GetBool(); - - switch ( ptr->hitgroup ) - { - case HITGROUP_GENERIC: - if( bDebug ) DevMsg("Hit Location: Generic\n"); - break; - - // hit gear, react but don't bleed - case HITGROUP_GEAR: - subInfo.SetDamage( 0.01 ); - ptr->hitgroup = HITGROUP_GENERIC; - if( bDebug ) DevMsg("Hit Location: Gear\n"); - break; - - case HITGROUP_HEAD: - subInfo.ScaleDamage( GetHitgroupDamageMultiplier(ptr->hitgroup, info) ); - if( bDebug ) DevMsg("Hit Location: Head\n"); - break; - - case HITGROUP_CHEST: - subInfo.ScaleDamage( GetHitgroupDamageMultiplier(ptr->hitgroup, info) ); - if( bDebug ) DevMsg("Hit Location: Chest\n"); - break; - - case HITGROUP_STOMACH: - subInfo.ScaleDamage( GetHitgroupDamageMultiplier(ptr->hitgroup, info) ); - if( bDebug ) DevMsg("Hit Location: Stomach\n"); - break; - - case HITGROUP_LEFTARM: - case HITGROUP_RIGHTARM: - subInfo.ScaleDamage( GetHitgroupDamageMultiplier(ptr->hitgroup, info) ); - if( bDebug ) DevMsg("Hit Location: Left/Right Arm\n"); - break - ; - case HITGROUP_LEFTLEG: - case HITGROUP_RIGHTLEG: - subInfo.ScaleDamage( GetHitgroupDamageMultiplier(ptr->hitgroup, info) ); - if( bDebug ) DevMsg("Hit Location: Left/Right Leg\n"); - break; - - default: - if( bDebug ) DevMsg("Hit Location: UNKNOWN\n"); - break; - } - - if ( subInfo.GetDamage() >= 1.0 && !(subInfo.GetDamageType() & DMG_SHOCK ) ) - { - if( !IsPlayer() || ( IsPlayer() && g_pGameRules->IsMultiplayer() ) ) - { - // NPC's always bleed. Players only bleed in multiplayer. - SpawnBlood( ptr->endpos, vecDir, BloodColor(), subInfo.GetDamage() );// a little surface blood. - } - - TraceBleed( subInfo.GetDamage(), vecDir, ptr, subInfo.GetDamageType() ); - - if ( ptr->hitgroup == HITGROUP_HEAD && m_iHealth - subInfo.GetDamage() > 0 ) - { - m_fNoDamageDecal = true; - } - } - - // Airboat gun will impart major force if it's about to kill him.... - if ( info.GetDamageType() & DMG_AIRBOAT ) - { - if ( subInfo.GetDamage() >= GetHealth() ) - { - float flMagnitude = subInfo.GetDamageForce().Length(); - if ( (flMagnitude != 0.0f) && (flMagnitude < 400.0f * 65.0f) ) - { - subInfo.ScaleDamageForce( 400.0f * 65.0f / flMagnitude ); - } - } - } - - if( info.GetInflictor() ) - { - subInfo.SetInflictor( info.GetInflictor() ); - } - else - { - subInfo.SetInflictor( info.GetAttacker() ); - } - - AddMultiDamage( subInfo, this ); -} - -//----------------------------------------------------------------------------- -// Purpose: Checks if point is in spread angle between source and target Pos -// Used to prevent friendly fire -// Input : Source of attack, target position, spread angle -// Output : -//----------------------------------------------------------------------------- -bool CAI_BaseNPC::PointInSpread( CBaseCombatCharacter *pCheckEntity, const Vector &sourcePos, const Vector &targetPos, const Vector &testPoint, float flSpread, float maxDistOffCenter ) -{ - float distOffLine = CalcDistanceToLine2D( testPoint.AsVector2D(), sourcePos.AsVector2D(), targetPos.AsVector2D() ); - if ( distOffLine < maxDistOffCenter ) - { - Vector toTarget = targetPos - sourcePos; - float distTarget = VectorNormalize(toTarget); - - Vector toTest = testPoint - sourcePos; - float distTest = VectorNormalize(toTest); - // Only reject if target is on other side - if (distTarget > distTest) - { - toTarget.z = 0.0; - toTest.z = 0.0; - - float dotProduct = DotProduct(toTarget,toTest); - if (dotProduct > flSpread) - { - return true; - } - else if( dotProduct > 0.0f ) - { - // If this guy is in front, do the hull/line test: - if( pCheckEntity ) - { - float flBBoxDist = NAI_Hull::Width( pCheckEntity->GetHullType() ); - flBBoxDist *= 1.414f; // sqrt(2) - - // !!!BUGBUG - this 2d check will stop a citizen shooting at a gunship or strider - // if another citizen is between them, even though the strider or gunship may be - // high up in the air (sjb) - distOffLine = CalcDistanceToLine( testPoint, sourcePos, targetPos ); - if( distOffLine < flBBoxDist ) - { - return true; - } - } - } - } - } - return false; -} - -//----------------------------------------------------------------------------- -// Purpose: Checks if player is in spread angle between source and target Pos -// Used to prevent friendly fire -// Input : Source of attack, target position, spread angle -// Output : -//----------------------------------------------------------------------------- -bool CAI_BaseNPC::PlayerInSpread( const Vector &sourcePos, const Vector &targetPos, float flSpread, float maxDistOffCenter, bool ignoreHatedPlayers ) -{ - // loop through all players - for (int i = 1; i <= gpGlobals->maxClients; i++ ) - { - CBasePlayer *pPlayer = UTIL_PlayerByIndex( i ); - - if ( pPlayer && ( !ignoreHatedPlayers || IRelationType( pPlayer ) != D_HT ) ) - { - if ( PointInSpread( pPlayer, sourcePos, targetPos, pPlayer->WorldSpaceCenter(), flSpread, maxDistOffCenter ) ) - return true; - } - } - return false; -} - -//----------------------------------------------------------------------------- -// Purpose: Checks if player is in range of given location. Used by NPCs -// to prevent friendly fire -// Input : -// Output : -//----------------------------------------------------------------------------- -CBaseEntity *CAI_BaseNPC::PlayerInRange( const Vector &vecLocation, float flDist ) -{ - // loop through all players - for (int i = 1; i <= gpGlobals->maxClients; i++ ) - { - CBasePlayer *pPlayer = UTIL_PlayerByIndex( i ); - - if (pPlayer && (vecLocation - pPlayer->WorldSpaceCenter() ).Length2D() <= flDist) - { - return pPlayer; - } - } - return NULL; -} - - -#define BULLET_WIZZDIST 80.0 -#define SLOPE ( -1.0 / BULLET_WIZZDIST ) - -void BulletWizz( Vector vecSrc, Vector vecEndPos, edict_t *pShooter, bool isTracer ) -{ - CBasePlayer *pPlayer; - Vector vecBulletPath; - Vector vecPlayerPath; - Vector vecBulletDir; - Vector vecNearestPoint; - float flDist; - float flBulletDist; - - vecBulletPath = vecEndPos - vecSrc; - vecBulletDir = vecBulletPath; - VectorNormalize(vecBulletDir); - - // see how near this bullet passed by player in a single player game - // for multiplayer, we need to go through the list of clients. - for (int i = 1; i <= gpGlobals->maxClients; i++ ) - { - pPlayer = UTIL_PlayerByIndex( i ); - - if ( !pPlayer ) - continue; - - // Don't hear one's own bullets - if( pPlayer->edict() == pShooter ) - continue; - - vecPlayerPath = pPlayer->EarPosition() - vecSrc; - flDist = DotProduct( vecPlayerPath, vecBulletDir ); - vecNearestPoint = vecSrc + vecBulletDir * flDist; - // FIXME: minus m_vecViewOffset? - flBulletDist = ( vecNearestPoint - pPlayer->EarPosition() ).Length(); - } -} - -//----------------------------------------------------------------------------- -// Hits triggers with raycasts -//----------------------------------------------------------------------------- -class CTriggerTraceEnum : public IEntityEnumerator -{ -public: - CTriggerTraceEnum( Ray_t *pRay, const CTakeDamageInfo &info, const Vector& dir, int contentsMask ) : - m_info( info ), m_VecDir(dir), m_ContentsMask(contentsMask), m_pRay(pRay) - { - } - - virtual bool EnumEntity( IHandleEntity *pHandleEntity ) - { - trace_t tr; - - CBaseEntity *pEnt = gEntList.GetBaseEntity( pHandleEntity->GetRefEHandle() ); - - // Done to avoid hitting an entity that's both solid & a trigger. - if ( pEnt->IsSolid() ) - return true; - - enginetrace->ClipRayToEntity( *m_pRay, m_ContentsMask, pHandleEntity, &tr ); - if (tr.fraction < 1.0f) - { - pEnt->DispatchTraceAttack( m_info, m_VecDir, &tr ); - ApplyMultiDamage(); - } - - return true; - } - -private: - Vector m_VecDir; - int m_ContentsMask; - Ray_t *m_pRay; - CTakeDamageInfo m_info; -}; - -void CBaseEntity::TraceAttackToTriggers( const CTakeDamageInfo &info, const Vector& start, const Vector& end, const Vector& dir ) -{ - Ray_t ray; - ray.Init( start, end ); - - CTriggerTraceEnum triggerTraceEnum( &ray, info, dir, MASK_SHOT ); - enginetrace->EnumerateEntities( ray, true, &triggerTraceEnum ); -} - -//----------------------------------------------------------------------------- -// Purpose: -// Output : const char -//----------------------------------------------------------------------------- -const char *CAI_BaseNPC::GetTracerType( void ) -{ - if ( GetActiveWeapon() ) - { - return GetActiveWeapon()->GetTracerType(); - } - - return BaseClass::GetTracerType(); -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : &vecTracerSrc - -// &tr - -// iTracerType - -//----------------------------------------------------------------------------- -void CAI_BaseNPC::MakeTracer( const Vector &vecTracerSrc, const trace_t &tr, int iTracerType ) -{ - if ( GetActiveWeapon() ) - { - GetActiveWeapon()->MakeTracer( vecTracerSrc, tr, iTracerType ); - return; - } - - BaseClass::MakeTracer( vecTracerSrc, tr, iTracerType ); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CAI_BaseNPC::FireBullets( const FireBulletsInfo_t &info ) -{ -#ifdef HL2_DLL - // If we're shooting at a bullseye, become perfectly accurate if the bullseye demands it - if ( GetEnemy() && GetEnemy()->Classify() == CLASS_BULLSEYE ) - { - CNPC_Bullseye *pBullseye = dynamic_cast(GetEnemy()); - if ( pBullseye && pBullseye->UsePerfectAccuracy() ) - { - FireBulletsInfo_t accurateInfo = info; - accurateInfo.m_vecSpread = vec3_origin; - BaseClass::FireBullets( accurateInfo ); - return; - } - } -#endif - - BaseClass::FireBullets( info ); -} - - -//----------------------------------------------------------------------------- -// Shot statistics -//----------------------------------------------------------------------------- -void CBaseEntity::UpdateShotStatistics( const trace_t &tr ) -{ - if ( ai_shot_stats.GetBool() ) - { - CAI_BaseNPC *pNpc = MyNPCPointer(); - if ( pNpc ) - { - pNpc->m_TotalShots++; - if ( tr.m_pEnt == pNpc->GetEnemy() ) - { - pNpc->m_TotalHits++; - } - } - } -} - - -//----------------------------------------------------------------------------- -// Handle shot entering water -//----------------------------------------------------------------------------- -void CBaseEntity::HandleShotImpactingGlass( const FireBulletsInfo_t &info, - const trace_t &tr, const Vector &vecDir, ITraceFilter *pTraceFilter ) -{ - // Move through the glass until we're at the other side - Vector testPos = tr.endpos + ( vecDir * MAX_GLASS_PENETRATION_DEPTH ); - - CEffectData data; - - data.m_vNormal = tr.plane.normal; - data.m_vOrigin = tr.endpos; - - DispatchEffect( "GlassImpact", data ); - - trace_t penetrationTrace; - - // Re-trace as if the bullet had passed right through - UTIL_TraceLine( testPos, tr.endpos, MASK_SHOT, pTraceFilter, &penetrationTrace ); - - // See if we found the surface again - if ( penetrationTrace.startsolid || tr.fraction == 0.0f || penetrationTrace.fraction == 1.0f ) - return; - - //FIXME: This is technically frustrating MultiDamage, but multiple shots hitting multiple targets in one call - // would do exactly the same anyway... - - // Impact the other side (will look like an exit effect) - DoImpactEffect( penetrationTrace, GetAmmoDef()->DamageType(info.m_iAmmoType) ); - - data.m_vNormal = penetrationTrace.plane.normal; - data.m_vOrigin = penetrationTrace.endpos; - - DispatchEffect( "GlassImpact", data ); - - // Refire the round, as if starting from behind the glass - FireBulletsInfo_t behindGlassInfo; - behindGlassInfo.m_iShots = 1; - behindGlassInfo.m_vecSrc = penetrationTrace.endpos; - behindGlassInfo.m_vecDirShooting = vecDir; - behindGlassInfo.m_vecSpread = vec3_origin; - behindGlassInfo.m_flDistance = info.m_flDistance*( 1.0f - tr.fraction ); - behindGlassInfo.m_iAmmoType = info.m_iAmmoType; - behindGlassInfo.m_iTracerFreq = info.m_iTracerFreq; - behindGlassInfo.m_iDamage = info.m_iDamage; - behindGlassInfo.m_pAttacker = info.m_pAttacker ? info.m_pAttacker : this; - behindGlassInfo.m_nFlags = info.m_nFlags; - - FireBullets( behindGlassInfo ); -} - - -//----------------------------------------------------------------------------- -// Computes the tracer start position -//----------------------------------------------------------------------------- -#define SHOT_UNDERWATER_BUBBLE_DIST 400 - -void CBaseEntity::CreateBubbleTrailTracer( const Vector &vecShotSrc, const Vector &vecShotEnd, const Vector &vecShotDir ) -{ - int nBubbles; - Vector vecBubbleEnd; - float flLengthSqr = vecShotSrc.DistToSqr( vecShotEnd ); - if ( flLengthSqr > SHOT_UNDERWATER_BUBBLE_DIST * SHOT_UNDERWATER_BUBBLE_DIST ) - { - VectorMA( vecShotSrc, SHOT_UNDERWATER_BUBBLE_DIST, vecShotDir, vecBubbleEnd ); - nBubbles = WATER_BULLET_BUBBLES_PER_INCH * SHOT_UNDERWATER_BUBBLE_DIST; - } - else - { - float flLength = sqrt(flLengthSqr) - 0.1f; - nBubbles = WATER_BULLET_BUBBLES_PER_INCH * flLength; - VectorMA( vecShotSrc, flLength, vecShotDir, vecBubbleEnd ); - } - - Vector vecTracerSrc; - ComputeTracerStartPosition( vecShotSrc, &vecTracerSrc ); - UTIL_BubbleTrail( vecTracerSrc, vecBubbleEnd, nBubbles ); -} - - -//========================================================= -//========================================================= -void CAI_BaseNPC::MakeDamageBloodDecal ( int cCount, float flNoise, trace_t *ptr, Vector vecDir ) -{ - // make blood decal on the wall! - trace_t Bloodtr; - Vector vecTraceDir; - int i; - - if ( !IsAlive() ) - { - // dealing with a dead npc. - if ( m_iMaxHealth <= 0 ) - { - // no blood decal for a npc that has already decalled its limit. - return; - } - else - { - m_iMaxHealth -= 1; - } - } - - for ( i = 0 ; i < cCount ; i++ ) - { - vecTraceDir = vecDir; - - vecTraceDir.x += random->RandomFloat( -flNoise, flNoise ); - vecTraceDir.y += random->RandomFloat( -flNoise, flNoise ); - vecTraceDir.z += random->RandomFloat( -flNoise, flNoise ); - - AI_TraceLine( ptr->endpos, ptr->endpos + vecTraceDir * 172, MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &Bloodtr); - - if ( Bloodtr.fraction != 1.0 ) - { - UTIL_BloodDecalTrace( &Bloodtr, BloodColor() ); - } - } -} - - -//----------------------------------------------------------------------------- -// Purpose: -// Input : &tr - -// nDamageType - -//----------------------------------------------------------------------------- -void CAI_BaseNPC::DoImpactEffect( trace_t &tr, int nDamageType ) -{ - if ( GetActiveWeapon() != NULL ) - { - GetActiveWeapon()->DoImpactEffect( tr, nDamageType ); - return; - } - - BaseClass::DoImpactEffect( tr, nDamageType ); -} - -//--------------------------------------------------------- -//--------------------------------------------------------- -#define InterruptFromCondition( iCondition ) \ - AI_RemapFromGlobal( ( AI_IdIsLocal( iCondition ) ? GetClassScheduleIdSpace()->ConditionLocalToGlobal( iCondition ) : iCondition ) ) - -void CAI_BaseNPC::SetCondition( int iCondition ) -{ - int interrupt = InterruptFromCondition( iCondition ); - - if ( interrupt == -1 ) - { - Assert(0); - return; - } - - m_Conditions.SetBit( interrupt ); -} - -//--------------------------------------------------------- -//--------------------------------------------------------- -bool CAI_BaseNPC::HasCondition( int iCondition ) -{ - int interrupt = InterruptFromCondition( iCondition ); - - if ( interrupt == -1 ) - { - Assert(0); - return false; - } - - bool bReturn = m_Conditions.GetBit(interrupt); - return (bReturn); -} - -//--------------------------------------------------------- -//--------------------------------------------------------- -bool CAI_BaseNPC::HasCondition( int iCondition, bool bUseIgnoreConditions ) -{ - if ( bUseIgnoreConditions ) - return HasCondition( iCondition ); - - int interrupt = InterruptFromCondition( iCondition ); - - if ( interrupt == -1 ) - { - Assert(0); - return false; - } - - bool bReturn = m_ConditionsPreIgnore.GetBit(interrupt); - return (bReturn); -} - -//--------------------------------------------------------- -//--------------------------------------------------------- -void CAI_BaseNPC::ClearCondition( int iCondition ) -{ - int interrupt = InterruptFromCondition( iCondition ); - - if ( interrupt == -1 ) - { - Assert(0); - return; - } - - m_Conditions.ClearBit(interrupt); -} - -//--------------------------------------------------------- -//--------------------------------------------------------- -void CAI_BaseNPC::ClearConditions( int *pConditions, int nConditions ) -{ - for ( int i = 0; i < nConditions; ++i ) - { - int iCondition = pConditions[i]; - int interrupt = InterruptFromCondition( iCondition ); - - if ( interrupt == -1 ) - { - Assert(0); - continue; - } - - m_Conditions.ClearBit( interrupt ); - } -} - -//--------------------------------------------------------- -//--------------------------------------------------------- -void CAI_BaseNPC::SetIgnoreConditions( int *pConditions, int nConditions ) -{ - for ( int i = 0; i < nConditions; ++i ) - { - int iCondition = pConditions[i]; - int interrupt = InterruptFromCondition( iCondition ); - - if ( interrupt == -1 ) - { - Assert(0); - continue; - } - - m_InverseIgnoreConditions.ClearBit( interrupt ); // clear means ignore - } -} - -void CAI_BaseNPC::ClearIgnoreConditions( int *pConditions, int nConditions ) -{ - for ( int i = 0; i < nConditions; ++i ) - { - int iCondition = pConditions[i]; - int interrupt = InterruptFromCondition( iCondition ); - - if ( interrupt == -1 ) - { - Assert(0); - continue; - } - - m_InverseIgnoreConditions.SetBit( interrupt ); // set means don't ignore - } -} - -//--------------------------------------------------------- -//--------------------------------------------------------- -bool CAI_BaseNPC::HasInterruptCondition( int iCondition ) -{ - if( !GetCurSchedule() ) - { - return false; - } - - int interrupt = InterruptFromCondition( iCondition ); - - if ( interrupt == -1 ) - { - Assert(0); - return false; - } - return ( m_Conditions.GetBit( interrupt ) && GetCurSchedule()->HasInterrupt( interrupt ) ); -} - -//--------------------------------------------------------- -//--------------------------------------------------------- -bool CAI_BaseNPC::ConditionInterruptsCurSchedule( int iCondition ) -{ - if( !GetCurSchedule() ) - { - return false; - } - - int interrupt = InterruptFromCondition( iCondition ); - - if ( interrupt == -1 ) - { - Assert(0); - return false; - } - return ( GetCurSchedule()->HasInterrupt( interrupt ) ); -} - -//--------------------------------------------------------- -//--------------------------------------------------------- -bool CAI_BaseNPC::ConditionInterruptsSchedule( int localScheduleID, int iCondition ) -{ - CAI_Schedule *pSchedule = GetSchedule( localScheduleID ); - if ( !pSchedule ) - return false; - - int interrupt = InterruptFromCondition( iCondition ); - - if ( interrupt == -1 ) - { - Assert(0); - return false; - } - return ( pSchedule->HasInterrupt( interrupt ) ); -} - - -//----------------------------------------------------------------------------- -// Returns whether we currently have any interrupt conditions that would -// interrupt the given schedule. -//----------------------------------------------------------------------------- -bool CAI_BaseNPC::HasConditionsToInterruptSchedule( int nLocalScheduleID ) -{ - CAI_Schedule *pSchedule = GetSchedule( nLocalScheduleID ); - if ( !pSchedule ) - return false; - - CAI_ScheduleBits bitsMask; - pSchedule->GetInterruptMask( &bitsMask ); - - CAI_ScheduleBits bitsOut; - AccessConditionBits().And( bitsMask, &bitsOut ); - - return !bitsOut.IsAllClear(); -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -bool CAI_BaseNPC::IsCustomInterruptConditionSet( int nCondition ) -{ - int interrupt = InterruptFromCondition( nCondition ); - - if ( interrupt == -1 ) - { - Assert(0); - return false; - } - - return m_CustomInterruptConditions.GetBit( interrupt ); -} - -//----------------------------------------------------------------------------- -// Purpose: Sets a flag in the custom interrupt flags, translating the condition -// to the proper global space, if necessary -//----------------------------------------------------------------------------- -void CAI_BaseNPC::SetCustomInterruptCondition( int nCondition ) -{ - int interrupt = InterruptFromCondition( nCondition ); - - if ( interrupt == -1 ) - { - Assert(0); - return; - } - - m_CustomInterruptConditions.SetBit( interrupt ); -} - -//----------------------------------------------------------------------------- -// Purpose: Clears a flag in the custom interrupt flags, translating the condition -// to the proper global space, if necessary -//----------------------------------------------------------------------------- -void CAI_BaseNPC::ClearCustomInterruptCondition( int nCondition ) -{ - int interrupt = InterruptFromCondition( nCondition ); - - if ( interrupt == -1 ) - { - Assert(0); - return; - } - - m_CustomInterruptConditions.ClearBit( interrupt ); -} - - -//----------------------------------------------------------------------------- -// Purpose: Clears all the custom interrupt flags. -//----------------------------------------------------------------------------- -void CAI_BaseNPC::ClearCustomInterruptConditions() -{ - m_CustomInterruptConditions.ClearAllBits(); -} - - -//----------------------------------------------------------------------------- - -void CAI_BaseNPC::SetDistLook( float flDistLook ) -{ - m_pSenses->SetDistLook( flDistLook ); -} - -//----------------------------------------------------------------------------- - -bool CAI_BaseNPC::QueryHearSound( CSound *pSound ) -{ - if ( pSound->SoundContext() & SOUND_CONTEXT_COMBINE_ONLY ) - return false; - - if ( pSound->SoundContext() & SOUND_CONTEXT_ALLIES_ONLY ) - { - if ( !IsPlayerAlly() ) - return false; - } - - if ( pSound->IsSoundType( SOUND_PLAYER ) && GetState() == NPC_STATE_IDLE && !FVisible( pSound->GetSoundReactOrigin() ) ) - { - // NPC's that are IDLE should disregard player movement sounds if they can't see them. - // This does not affect them hearing the player's weapon. - // !!!BUGBUG - this probably makes NPC's not hear doors opening, because doors opening put SOUND_PLAYER - // in the world, but the door's model will block the FVisible() trace and this code will then - // deduce that the sound can not be heard - return false; - } - - // Disregard footsteps from our own class type - if ( pSound->IsSoundType( SOUND_COMBAT ) && pSound->SoundChannel() == SOUNDENT_CHANNEL_NPC_FOOTSTEP ) - { - if ( pSound->m_hOwner && pSound->m_hOwner->ClassMatches( m_iClassname ) ) - return false; - } - - if( ShouldIgnoreSound( pSound ) ) - return false; - - return true; -} - -//----------------------------------------------------------------------------- - -bool CAI_BaseNPC::QuerySeeEntity( CBaseEntity *pEntity, bool bOnlyHateOrFearIfNPC ) -{ - if ( bOnlyHateOrFearIfNPC && pEntity->IsNPC() ) - { - Disposition_t disposition = IRelationType( pEntity ); - return ( disposition == D_HT || disposition == D_FR ); - } - return true; -} - -//----------------------------------------------------------------------------- - -void CAI_BaseNPC::OnLooked( int iDistance ) -{ - // DON'T let visibility information from last frame sit around! - static int conditionsToClear[] = - { - COND_SEE_HATE, - COND_SEE_DISLIKE, - COND_SEE_ENEMY, - COND_SEE_FEAR, - COND_SEE_NEMESIS, - COND_SEE_PLAYER, - COND_LOST_PLAYER, - COND_ENEMY_WENT_NULL, - }; - - bool bHadSeePlayer = HasCondition(COND_SEE_PLAYER); - - ClearConditions( conditionsToClear, ARRAYSIZE( conditionsToClear ) ); - - AISightIter_t iter; - CBaseEntity *pSightEnt; - - pSightEnt = GetSenses()->GetFirstSeenEntity( &iter ); - - while( pSightEnt ) - { - if ( pSightEnt->IsPlayer() ) - { - // if we see a client, remember that (mostly for scripted AI) - SetCondition(COND_SEE_PLAYER); - m_flLastSawPlayerTime = gpGlobals->curtime; - } - - Disposition_t relation = IRelationType( pSightEnt ); - - // the looker will want to consider this entity - // don't check anything else about an entity that can't be seen, or an entity that you don't care about. - if ( relation != D_NU ) - { - if ( pSightEnt == GetEnemy() ) - { - // we know this ent is visible, so if it also happens to be our enemy, store that now. - SetCondition(COND_SEE_ENEMY); - } - - // don't add the Enemy's relationship to the conditions. We only want to worry about conditions when - // we see npcs other than the Enemy. - switch ( relation ) - { - case D_HT: - { - int priority = IRelationPriority( pSightEnt ); - if (priority < 0) - { - SetCondition(COND_SEE_DISLIKE); - } - else if (priority > 10) - { - SetCondition(COND_SEE_NEMESIS); - } - else - { - SetCondition(COND_SEE_HATE); - } - UpdateEnemyMemory(pSightEnt,pSightEnt->GetAbsOrigin()); - break; - - } - case D_FR: - UpdateEnemyMemory(pSightEnt,pSightEnt->GetAbsOrigin()); - SetCondition(COND_SEE_FEAR); - break; - case D_LI: - case D_NU: - break; - default: - DevWarning( 2, "%s can't assess %s\n", GetClassname(), pSightEnt->GetClassname() ); - break; - } - } - - pSightEnt = GetSenses()->GetNextSeenEntity( &iter ); - } - - // Did we lose the player? - if ( bHadSeePlayer && !HasCondition(COND_SEE_PLAYER) ) - { - SetCondition(COND_LOST_PLAYER); - } -} - -//----------------------------------------------------------------------------- - -void CAI_BaseNPC::OnListened() -{ - AISoundIter_t iter; - - CSound *pCurrentSound; - - static int conditionsToClear[] = - { - COND_HEAR_DANGER, - COND_HEAR_COMBAT, - COND_HEAR_WORLD, - COND_HEAR_PLAYER, - COND_HEAR_THUMPER, - COND_HEAR_BUGBAIT, - COND_HEAR_PHYSICS_DANGER, - COND_HEAR_BULLET_IMPACT, - COND_HEAR_MOVE_AWAY, - - COND_NO_HEAR_DANGER, - - COND_SMELL, - }; - - ClearConditions( conditionsToClear, ARRAYSIZE( conditionsToClear ) ); - - pCurrentSound = GetSenses()->GetFirstHeardSound( &iter ); - - while ( pCurrentSound ) - { - // the npc cares about this sound, and it's close enough to hear. - int condition = COND_NONE; - - if ( pCurrentSound->FIsSound() ) - { - // this is an audible sound. - switch( pCurrentSound->SoundTypeNoContext() ) - { - case SOUND_DANGER: - if( gpGlobals->curtime > m_flIgnoreDangerSoundsUntil) - condition = COND_HEAR_DANGER; - break; - - case SOUND_THUMPER: condition = COND_HEAR_THUMPER; break; - case SOUND_BUGBAIT: condition = COND_HEAR_BUGBAIT; break; - case SOUND_COMBAT: - if ( pCurrentSound->SoundChannel() == SOUNDENT_CHANNEL_SPOOKY_NOISE ) - { - condition = COND_HEAR_SPOOKY; - } - else - { - condition = COND_HEAR_COMBAT; - } - break; - - case SOUND_WORLD: condition = COND_HEAR_WORLD; break; - case SOUND_PLAYER: condition = COND_HEAR_PLAYER; break; - case SOUND_BULLET_IMPACT: condition = COND_HEAR_BULLET_IMPACT; break; - case SOUND_PHYSICS_DANGER: condition = COND_HEAR_PHYSICS_DANGER; break; - case SOUND_DANGER_SNIPERONLY:/* silence warning */ break; - case SOUND_MOVE_AWAY: condition = COND_HEAR_MOVE_AWAY; break; - case SOUND_PLAYER_VEHICLE: condition = COND_HEAR_PLAYER; break; - - default: - DevMsg( "**ERROR: NPC %s hearing sound of unknown type %d!\n", GetClassname(), pCurrentSound->SoundType() ); - break; - } - } - else - { - // if not a sound, must be a smell - determine if it's just a scent, or if it's a food scent - condition = COND_SMELL; - } - - if ( condition != COND_NONE ) - { - SetCondition( condition ); - } - - pCurrentSound = GetSenses()->GetNextHeardSound( &iter ); - } - - if( !HasCondition( COND_HEAR_DANGER ) ) - { - SetCondition( COND_NO_HEAR_DANGER ); - } - - // Sound outputs - if ( HasCondition( COND_HEAR_WORLD ) ) - { - m_OnHearWorld.FireOutput(this, this); - } - - if ( HasCondition( COND_HEAR_PLAYER ) ) - { - m_OnHearPlayer.FireOutput(this, this); - } - - if ( HasCondition( COND_HEAR_COMBAT ) || - HasCondition( COND_HEAR_BULLET_IMPACT ) || - HasCondition( COND_HEAR_DANGER ) ) - { - m_OnHearCombat.FireOutput(this, this); - } -} - -//========================================================= -// FValidateHintType - tells use whether or not the npc cares -// about the type of Hint Node given -//========================================================= -bool CAI_BaseNPC::FValidateHintType ( CAI_Hint *pHint ) -{ - return false; -} - -//----------------------------------------------------------------------------- -// Purpose: Override in subclasses to associate specific hint types -// with activities -//----------------------------------------------------------------------------- -Activity CAI_BaseNPC::GetHintActivity( short sHintType, Activity HintsActivity ) -{ - if ( HintsActivity != ACT_INVALID ) - return HintsActivity; - - return ACT_IDLE; -} - -//----------------------------------------------------------------------------- -// Purpose: Override in subclasses to give specific hint types delays -// before they can be used again -// Input : -// Output : -//----------------------------------------------------------------------------- -float CAI_BaseNPC::GetHintDelay( short sHintType ) -{ - return 0; -} - -//----------------------------------------------------------------------------- -// Purpose: Return incoming grenade if spotted -// Input : -// Output : -//----------------------------------------------------------------------------- -CBaseGrenade* CAI_BaseNPC::IncomingGrenade(void) -{ - int iDist; - - AIEnemiesIter_t iter; - for( AI_EnemyInfo_t *pEMemory = GetEnemies()->GetFirst(&iter); pEMemory != NULL; pEMemory = GetEnemies()->GetNext(&iter) ) - { - CBaseGrenade* pBG = dynamic_cast((CBaseEntity*)pEMemory->hEnemy); - - // Make sure this memory is for a grenade and grenade is not dead - if (!pBG || pBG->m_lifeState == LIFE_DEAD) - continue; - - // Make sure it's visible - if (!FVisible(pBG)) - continue; - - // Check if it's near me - iDist = ( pBG->GetAbsOrigin() - GetAbsOrigin() ).Length(); - if ( iDist <= NPC_GRENADE_FEAR_DIST ) - return pBG; - - // Check if it's headed towards me - Vector vGrenadeDir = GetAbsOrigin() - pBG->GetAbsOrigin(); - Vector vGrenadeVel; - pBG->GetVelocity( &vGrenadeVel, NULL ); - VectorNormalize(vGrenadeDir); - VectorNormalize(vGrenadeVel); - float flDotPr = DotProduct(vGrenadeDir, vGrenadeVel); - if (flDotPr > 0.85) - return pBG; - } - return NULL; -} - - -//----------------------------------------------------------------------------- -// Purpose: Check my physical state with the environment -// Input : -// Output : -//----------------------------------------------------------------------------- -void CAI_BaseNPC::TryRestoreHull(void) -{ - if ( IsUsingSmallHull() && GetCurSchedule() ) - { - trace_t tr; - Vector vUpBit = GetAbsOrigin(); - vUpBit.z += 1; - - AI_TraceHull( GetAbsOrigin(), vUpBit, GetHullMins(), - GetHullMaxs(), MASK_SOLID, this, COLLISION_GROUP_NONE, &tr ); - if ( !tr.startsolid && (tr.fraction == 1.0) ) - { - SetHullSizeNormal(); - } - } -} - -//========================================================= -//========================================================= -int CAI_BaseNPC::GetSoundInterests( void ) -{ - return SOUND_WORLD | SOUND_COMBAT | SOUND_PLAYER | SOUND_PLAYER_VEHICLE | - SOUND_BULLET_IMPACT; -} - -//--------------------------------------------------------- -//--------------------------------------------------------- -int CAI_BaseNPC::GetSoundPriority( CSound *pSound ) -{ - int iSoundTypeNoContext = pSound->SoundTypeNoContext(); - int iSoundContext = pSound->SoundContext(); - - if( iSoundTypeNoContext & SOUND_DANGER ) - { - return SOUND_PRIORITY_HIGHEST; - } - - if( iSoundTypeNoContext & SOUND_COMBAT ) - { - if( iSoundContext & SOUND_CONTEXT_EXPLOSION ) - { - return SOUND_PRIORITY_VERY_HIGH; - } - - return SOUND_PRIORITY_HIGH; - } - - return SOUND_PRIORITY_NORMAL; -} - -//--------------------------------------------------------- -// Return the loudest sound of this type in the sound list -//--------------------------------------------------------- -CSound *CAI_BaseNPC::GetLoudestSoundOfType( int iType ) -{ - return CSoundEnt::GetLoudestSoundOfType( iType, EarPosition() ); -} - -//========================================================= -// GetBestSound - returns a pointer to the sound the npc -// should react to. Right now responds only to nearest sound. -//========================================================= -CSound* CAI_BaseNPC::GetBestSound( int validTypes ) -{ - if ( m_pLockedBestSound->m_iType != SOUND_NONE ) - return m_pLockedBestSound; - CSound *pResult = GetSenses()->GetClosestSound( false, validTypes ); - if ( pResult == NULL) - DevMsg( "Warning: NULL Return from GetBestSound\n" ); // condition previously set now no longer true. Have seen this when play too many sounds... - return pResult; -} - -//========================================================= -// PBestScent - returns a pointer to the scent the npc -// should react to. Right now responds only to nearest scent -//========================================================= -CSound* CAI_BaseNPC::GetBestScent( void ) -{ - CSound *pResult = GetSenses()->GetClosestSound( true ); - if ( pResult == NULL) - DevMsg("Warning: NULL Return from GetBestScent\n" ); - return pResult; -} - -//----------------------------------------------------------------------------- -void CAI_BaseNPC::LockBestSound() -{ - UnlockBestSound(); - CSound *pBestSound = GetBestSound(); - if ( pBestSound ) - *m_pLockedBestSound = *pBestSound; -} - -//----------------------------------------------------------------------------- -void CAI_BaseNPC::UnlockBestSound() -{ - if ( m_pLockedBestSound->m_iType != SOUND_NONE ) - { - m_pLockedBestSound->m_iType = SOUND_NONE; - OnListened(); // reset hearing conditions - } -} - -//----------------------------------------------------------------------------- -// Purpose: Return true if the specified sound is visible. Handles sounds generated by entities correctly. -// Input : *pSound - -//----------------------------------------------------------------------------- -bool CAI_BaseNPC::SoundIsVisible( CSound *pSound ) -{ - CBaseEntity *pBlocker = NULL; - if ( !FVisible( pSound->GetSoundReactOrigin(), MASK_OPAQUE, &pBlocker ) ) - { - // Is the blocker the sound owner? - if ( pBlocker && pBlocker == pSound->m_hOwner ) - return true; - - return false; - } - return true; -} - -//----------------------------------------------------------------------------- -// Purpose: Returns true if target is in legal range of eye movements -// Input : -// Output : -//----------------------------------------------------------------------------- -bool CAI_BaseNPC::ValidEyeTarget(const Vector &lookTargetPos) -{ - Vector vHeadDir = HeadDirection3D( ); - Vector lookTargetDir = lookTargetPos - EyePosition(); - VectorNormalize(lookTargetDir); - - // Only look if it doesn't crank my eyeballs too far - float dotPr = DotProduct(lookTargetDir, vHeadDir); - if (dotPr > 0.7) - { - return true; - } - return false; -} - - -//----------------------------------------------------------------------------- -// Purpose: Integrate head turn over time -// Input : -// Output : -//----------------------------------------------------------------------------- -void CAI_BaseNPC::SetHeadDirection( const Vector &vTargetPos, float flInterval) -{ - if (!(CapabilitiesGet() & bits_CAP_TURN_HEAD)) - return; - -#ifdef DEBUG_LOOK - // Draw line in body, head, and eye directions - Vector vEyePos = EyePosition(); - Vector vHeadDir; - HeadDirection3D(&vHeadDir); - Vector vBodyDir; - BodyDirection2D(&vBodyDir); - - //UNDONE <> - // currently eye dir just returns head dir, so use vTargetPos for now - //Vector vEyeDir; w - //EyeDirection3D(&vEyeDir); - NDebugOverlay::Line( vEyePos, vEyePos+(50*vHeadDir), 255, 0, 0, false, 0.1 ); - NDebugOverlay::Line( vEyePos, vEyePos+(50*vBodyDir), 0, 255, 0, false, 0.1 ); - NDebugOverlay::Line( vEyePos, vTargetPos, 0, 0, 255, false, 0.1 ); -#endif - - //-------------------------------------- - // Set head yaw - //-------------------------------------- - float flDesiredYaw = VecToYaw(vTargetPos - GetLocalOrigin()) - GetLocalAngles().y; - if (flDesiredYaw > 180) - flDesiredYaw -= 360; - if (flDesiredYaw < -180) - flDesiredYaw += 360; - - float iRate = 0.8; - - // Make frame rate independent - float timeToUse = flInterval; - while (timeToUse > 0) - { - m_flHeadYaw = (iRate * m_flHeadYaw) + (1-iRate)*flDesiredYaw; - timeToUse -= 0.1; - } - if (m_flHeadYaw > 360) m_flHeadYaw = 0; - - m_flHeadYaw = SetBoneController( 0, m_flHeadYaw ); - - - //-------------------------------------- - // Set head pitch - //-------------------------------------- - Vector vEyePosition = EyePosition(); - float fTargetDist = (vTargetPos - vEyePosition).Length(); - float fVertDist = vTargetPos.z - vEyePosition.z; - float flDesiredPitch = -RAD2DEG(atan(fVertDist/fTargetDist)); - - // Make frame rate independent - timeToUse = flInterval; - while (timeToUse > 0) - { - m_flHeadPitch = (iRate * m_flHeadPitch) + (1-iRate)*flDesiredPitch; - timeToUse -= 0.1; - } - if (m_flHeadPitch > 360) m_flHeadPitch = 0; - - SetBoneController( 1, m_flHeadPitch ); - -} - -//------------------------------------------------------------------------------ -// Purpose : Calculate the NPC's eye direction in 2D world space -// Input : -// Output : -//------------------------------------------------------------------------------ -Vector CAI_BaseNPC::EyeDirection2D( void ) -{ - // UNDONE - // For now just return head direction.... - return HeadDirection2D( ); -} - -//------------------------------------------------------------------------------ -// Purpose : Calculate the NPC's eye direction in 2D world space -// Input : -// Output : -//------------------------------------------------------------------------------ -Vector CAI_BaseNPC::EyeDirection3D( void ) -{ - // UNDONE //<> - // For now just return head direction.... - return HeadDirection3D( ); -} - -//------------------------------------------------------------------------------ -// Purpose : Calculate the NPC's head direction in 2D world space -// Input : -// Output : -//------------------------------------------------------------------------------ -Vector CAI_BaseNPC::HeadDirection2D( void ) -{ - // UNDONE <> - // This does not account for head rotations in the animation, - // only those done via bone controllers - - // Head yaw is in local cooridnate so it must be added to the body's yaw - QAngle bodyAngles = BodyAngles(); - float flWorldHeadYaw = m_flHeadYaw + bodyAngles.y; - - // Convert head yaw into facing direction - return UTIL_YawToVector( flWorldHeadYaw ); -} - -//------------------------------------------------------------------------------ -// Purpose : Calculate the NPC's head direction in 3D world space -// Input : -// Output : -//------------------------------------------------------------------------------ -Vector CAI_BaseNPC::HeadDirection3D( void ) -{ - Vector vHeadDirection; - - // UNDONE <> - // This does not account for head rotations in the animation, - // only those done via bone controllers - - // Head yaw is in local cooridnate so it must be added to the body's yaw - QAngle bodyAngles = BodyAngles(); - float flWorldHeadYaw = m_flHeadYaw + bodyAngles.y; - - // Convert head yaw into facing direction - AngleVectors( QAngle( m_flHeadPitch, flWorldHeadYaw, 0), &vHeadDirection ); - return vHeadDirection; -} - - -//----------------------------------------------------------------------------- -// Purpose: Look at other NPCs and clients from time to time -// Input : -// Output : -//----------------------------------------------------------------------------- -CBaseEntity *CAI_BaseNPC::EyeLookTarget( void ) -{ - if (m_flNextEyeLookTime < gpGlobals->curtime) - { - CBaseEntity* pBestEntity = NULL; - float fBestDist = MAX_COORD_RANGE; - float fTestDist; - - CBaseEntity *pEntity = NULL; - - for ( CEntitySphereQuery sphere( GetAbsOrigin(), 1024, 0 ); (pEntity = sphere.GetCurrentEntity()) != NULL; sphere.NextEntity() ) - { - if (pEntity == this) - { - continue; - } - CAI_BaseNPC *pNPC = pEntity->MyNPCPointer(); - if (pNPC || (pEntity->GetFlags() & FL_CLIENT)) - { - fTestDist = (GetAbsOrigin() - pEntity->EyePosition()).Length(); - if (fTestDist < fBestDist) - { - if (ValidEyeTarget(pEntity->EyePosition())) - { - fBestDist = fTestDist; - pBestEntity = pEntity; - } - } - } - } - if (pBestEntity) - { - m_flNextEyeLookTime = gpGlobals->curtime + random->RandomInt(1,5); - m_hEyeLookTarget = pBestEntity; - } - } - return m_hEyeLookTarget; -} - - -//----------------------------------------------------------------------------- -// Purpose: Set direction that the NPC aiming their gun -// returns true is the passed Vector is in -// the caller's forward aim cone. The dot product is performed -// in 2d, making the view cone infinitely tall. By default, the -// callers aim cone is assumed to be very narrow -//----------------------------------------------------------------------------- - -bool CAI_BaseNPC::FInAimCone( const Vector &vecSpot ) -{ - Vector los = ( vecSpot - GetAbsOrigin() ); - - // do this in 2D - los.z = 0; - VectorNormalize( los ); - - Vector facingDir = BodyDirection2D( ); - - float flDot = DotProduct( los, facingDir ); - - if (CapabilitiesGet() & bits_CAP_AIM_GUN) - { - // FIXME: query current animation for ranges - return ( flDot > DOT_30DEGREE ); - } - - if ( flDot > 0.994 )//!!!BUGBUG - magic number same as FacingIdeal(), what is this? - return true; - - return false; -} - - -//----------------------------------------------------------------------------- -// Purpose: Set direction that the NPC aiming their gun -//----------------------------------------------------------------------------- - -void CAI_BaseNPC::SetAim( const Vector &aimDir ) -{ - QAngle angDir; - VectorAngles( aimDir, angDir ); - - float curPitch = GetPoseParameter( "aim_pitch" ); - float curYaw = GetPoseParameter( "aim_yaw" ); - - float newPitch; - float newYaw; - - if( GetEnemy() ) - { - // clamp and dampen movement - newPitch = curPitch + 0.8 * UTIL_AngleDiff( UTIL_ApproachAngle( angDir.x, curPitch, 20 ), curPitch ); - - float flRelativeYaw = UTIL_AngleDiff( angDir.y, GetAbsAngles().y ); - // float flNewTargetYaw = UTIL_ApproachAngle( flRelativeYaw, curYaw, 20 ); - // float newYaw = curYaw + 0.8 * UTIL_AngleDiff( flNewTargetYaw, curYaw ); - newYaw = curYaw + UTIL_AngleDiff( flRelativeYaw, curYaw ); - } - else - { - // Sweep your weapon more slowly if you're not fighting someone - newPitch = curPitch + 0.6 * UTIL_AngleDiff( UTIL_ApproachAngle( angDir.x, curPitch, 20 ), curPitch ); - - float flRelativeYaw = UTIL_AngleDiff( angDir.y, GetAbsAngles().y ); - newYaw = curYaw + 0.6 * UTIL_AngleDiff( flRelativeYaw, curYaw ); - } - - newPitch = AngleNormalize( newPitch ); - newYaw = AngleNormalize( newYaw ); - - SetPoseParameter( "aim_pitch", newPitch ); - SetPoseParameter( "aim_yaw", newYaw ); - - // Msg("yaw %.0f (%.0f %.0f)\n", newYaw, angDir.y, GetAbsAngles().y ); - - // Calculate our interaction yaw. - // If we've got a small adjustment off our abs yaw, use that. - // Otherwise, use our abs yaw. - if ( fabs(newYaw) < 20 ) - { - m_flInteractionYaw = angDir.y; - } - else - { - m_flInteractionYaw = GetAbsAngles().y; - } -} - - -void CAI_BaseNPC::RelaxAim( ) -{ - float curPitch = GetPoseParameter( "aim_pitch" ); - float curYaw = GetPoseParameter( "aim_yaw" ); - - // dampen existing aim - float newPitch = AngleNormalize( UTIL_ApproachAngle( 0, curPitch, 3 ) ); - float newYaw = AngleNormalize( UTIL_ApproachAngle( 0, curYaw, 2 ) ); - - SetPoseParameter( "aim_pitch", newPitch ); - SetPoseParameter( "aim_yaw", newYaw ); - // DevMsg("relax aim %.0f %0.f\n", newPitch, newYaw ); -} - -//----------------------------------------------------------------------------- -void CAI_BaseNPC::AimGun() -{ - if (GetEnemy()) - { - Vector vecShootOrigin; - - vecShootOrigin = Weapon_ShootPosition(); - Vector vecShootDir = GetShootEnemyDir( vecShootOrigin, false ); - - SetAim( vecShootDir ); - } - else - { - RelaxAim( ); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Set direction that the NPC is looking -// Input : -// Output : -//----------------------------------------------------------------------------- -void CAI_BaseNPC::MaintainLookTargets ( float flInterval ) -{ - // -------------------------------------------------------- - // Try to look at enemy if I have one - // -------------------------------------------------------- - if ((CBaseEntity*)GetEnemy()) - { - if ( ValidEyeTarget(GetEnemy()->EyePosition()) ) - { - SetHeadDirection(GetEnemy()->EyePosition(),flInterval); - SetViewtarget( GetEnemy()->EyePosition() ); - return; - } - } - -#if 0 - // -------------------------------------------------------- - // First check if I've been assigned to look at an entity - // -------------------------------------------------------- - CBaseEntity *lookTarget = EyeLookTarget(); - if (lookTarget && ValidEyeTarget(lookTarget->EyePosition())) - { - SetHeadDirection(lookTarget->EyePosition(),flInterval); - SetViewtarget( lookTarget->EyePosition() ); - return; - } -#endif - - // -------------------------------------------------------- - // If I'm moving, look at my target position - // -------------------------------------------------------- - if (GetNavigator()->IsGoalActive() && ValidEyeTarget(GetNavigator()->GetCurWaypointPos())) - { - SetHeadDirection(GetNavigator()->GetCurWaypointPos(),flInterval); - SetViewtarget( GetNavigator()->GetCurWaypointPos() ); - return; - } - - - // ------------------------------------- - // If I hear a combat sounds look there - // ------------------------------------- - if ( HasCondition(COND_HEAR_COMBAT) || HasCondition(COND_HEAR_DANGER) ) - { - CSound *pSound = GetBestSound(); - - if ( pSound && pSound->IsSoundType(SOUND_COMBAT | SOUND_DANGER) ) - { - if (ValidEyeTarget( pSound->GetSoundOrigin() )) - { - SetHeadDirection(pSound->GetSoundOrigin(),flInterval); - SetViewtarget( pSound->GetSoundOrigin() ); - return; - } - } - } - - // ------------------------------------- - // Otherwise just look around randomly - // ------------------------------------- - - // Check that look target position is still valid - if (m_flNextEyeLookTime > gpGlobals->curtime) - { - if (!ValidEyeTarget(m_vEyeLookTarget)) - { - // Force choosing of new look target - m_flNextEyeLookTime = 0; - } - } - - if (m_flNextEyeLookTime < gpGlobals->curtime) - { - Vector vBodyDir = BodyDirection2D( ); - - /* - Vector lookSpread = Vector(0.82,0.82,0.22); - float x = random->RandomFloat(-0.5,0.5) + random->RandomFloat(-0.5,0.5); - float y = random->RandomFloat(-0.5,0.5) + random->RandomFloat(-0.5,0.5); - float z = random->RandomFloat(-0.5,0.5) + random->RandomFloat(-0.5,0.5); - - QAngle angles; - VectorAngles( vBodyDir, angles ); - Vector forward, right, up; - AngleVectors( angles, &forward, &right, &up ); - - Vector vecDir = vBodyDir + (x * lookSpread.x * forward) + (y * lookSpread.y * right) + (z * lookSpread.z * up); - float lookDist = random->RandomInt(50,2000); - m_vEyeLookTarget = EyePosition() + lookDist*vecDir; - */ - m_vEyeLookTarget = EyePosition() + 500*vBodyDir; - m_flNextEyeLookTime = gpGlobals->curtime + 0.5; // random->RandomInt(1,5); - } - SetHeadDirection(m_vEyeLookTarget,flInterval); - - // ---------------------------------------------------- - // Integrate eye turn in frame rate independent manner - // ---------------------------------------------------- - float timeToUse = flInterval; - while (timeToUse > 0) - { - m_vCurEyeTarget = ((1 - m_flEyeIntegRate) * m_vCurEyeTarget + m_flEyeIntegRate * m_vEyeLookTarget); - timeToUse -= 0.1; - } - SetViewtarget( m_vCurEyeTarget ); -} - - -//----------------------------------------------------------------------------- -// Let the motor deal with turning presentation issues -//----------------------------------------------------------------------------- - -void CAI_BaseNPC::MaintainTurnActivity( ) -{ - AI_PROFILE_SCOPE( CAI_BaseNPC_MaintainTurnActivity ); - GetMotor()->MaintainTurnActivity( ); -} - - -//----------------------------------------------------------------------------- -// Here's where all motion occurs -//----------------------------------------------------------------------------- -void CAI_BaseNPC::PerformMovement() -{ - // don't bother to move if the npc isn't alive - if (!IsAlive()) - return; - - AI_PROFILE_SCOPE(CAI_BaseNPC_PerformMovement); - g_AIMoveTimer.Start(); - - float flInterval = ( m_flTimeLastMovement != FLT_MAX ) ? gpGlobals->curtime - m_flTimeLastMovement : 0.1; - - m_pNavigator->Move( ROUND_TO_TICKS( flInterval ) ); - m_flTimeLastMovement = gpGlobals->curtime; - - g_AIMoveTimer.End(); - -} - -//----------------------------------------------------------------------------- -// Updates to npc after movement is completed -//----------------------------------------------------------------------------- - -void CAI_BaseNPC::PostMovement() -{ - AI_PROFILE_SCOPE( CAI_BaseNPC_PostMovement ); - - InvalidateBoneCache(); - - if ( GetModelPtr() && GetModelPtr()->SequencesAvailable() ) - { - float flInterval = GetAnimTimeInterval(); - - if ( CapabilitiesGet() & bits_CAP_AIM_GUN ) - { - AI_PROFILE_SCOPE( CAI_BaseNPC_PM_AimGun ); - AimGun(); - } - - // set look targets for npcs with animated faces - if ( CapabilitiesGet() & bits_CAP_ANIMATEDFACE ) - { - AI_PROFILE_SCOPE( CAI_BaseNPC_PM_MaintainLookTargets ); - MaintainLookTargets(flInterval); - } - - } - - - { - AI_PROFILE_SCOPE( CAI_BaseNPC_PM_MaintainTurnActivity ); - // update turning as needed - MaintainTurnActivity( ); - } -} - - -//----------------------------------------------------------------------------- - -float g_AINextDisabledMessageTime; -extern bool IsInCommentaryMode( void ); - -bool CAI_BaseNPC::PreThink( void ) -{ - // ---------------------------------------------------------- - // Skip AI if its been disabled or networks haven't been - // loaded, and put a warning message on the screen - // - // Don't do this if the convar wants it hidden - // ---------------------------------------------------------- - if ( (CAI_BaseNPC::m_nDebugBits & bits_debugDisableAI || !g_pAINetworkManager->NetworksLoaded()) ) - { - if ( gpGlobals->curtime >= g_AINextDisabledMessageTime && !IsInCommentaryMode() ) - { - g_AINextDisabledMessageTime = gpGlobals->curtime + 0.5f; - - hudtextparms_s tTextParam; - tTextParam.x = 0.7; - tTextParam.y = 0.65; - tTextParam.effect = 0; - tTextParam.r1 = 255; - tTextParam.g1 = 255; - tTextParam.b1 = 255; - tTextParam.a1 = 255; - tTextParam.r2 = 255; - tTextParam.g2 = 255; - tTextParam.b2 = 255; - tTextParam.a2 = 255; - tTextParam.fadeinTime = 0; - tTextParam.fadeoutTime = 0; - tTextParam.holdTime = 0.6; - tTextParam.fxTime = 0; - tTextParam.channel = 1; - UTIL_HudMessageAll( tTextParam, "A.I. Disabled...\n" ); - } - SetActivity( ACT_IDLE ); - return false; - } - - // -------------------------------------------------------- - // If debug stepping - // -------------------------------------------------------- - if (CAI_BaseNPC::m_nDebugBits & bits_debugStepAI) - { - if (m_nDebugCurIndex >= CAI_BaseNPC::m_nDebugPauseIndex) - { - if (!GetNavigator()->IsGoalActive()) - { - m_flPlaybackRate = 0; - } - return false; - } - else - { - m_flPlaybackRate = 1; - } - } - - if ( m_hOpeningDoor.Get() && AIIsDebuggingDoors( this ) ) - { - NDebugOverlay::Line( EyePosition(), m_hOpeningDoor->WorldSpaceCenter(), 255, 255, 255, false, .1 ); - } - - return true; -} - -//----------------------------------------------------------------------------- - -void CAI_BaseNPC::RunAnimation( void ) -{ - VPROF_BUDGET( "CAI_BaseNPC_RunAnimation", VPROF_BUDGETGROUP_SERVER_ANIM ); - - if ( !GetModelPtr() ) - return; - - float flInterval = GetAnimTimeInterval(); - - StudioFrameAdvance( ); // animate - - if ((CAI_BaseNPC::m_nDebugBits & bits_debugStepAI) && !GetNavigator()->IsGoalActive()) - { - flInterval = 0; - } - - // start or end a fidget - // This needs a better home -- switching animations over time should be encapsulated on a per-activity basis - // perhaps MaintainActivity() or a ShiftAnimationOverTime() or something. - if ( m_NPCState != NPC_STATE_SCRIPT && m_NPCState != NPC_STATE_DEAD && m_Activity == ACT_IDLE && IsActivityFinished() ) - { - int iSequence; - - // FIXME: this doesn't reissue a translation, so if the idle activity translation changes over time, it'll never get reset - if ( SequenceLoops() ) - { - // animation does loop, which means we're playing subtle idle. Might need to fidget. - iSequence = SelectWeightedSequence ( m_translatedActivity ); - } - else - { - // animation that just ended doesn't loop! That means we just finished a fidget - // and should return to our heaviest weighted idle (the subtle one) - iSequence = SelectHeaviestSequence ( m_translatedActivity ); - } - if ( iSequence != ACTIVITY_NOT_AVAILABLE ) - { - ResetSequence( iSequence ); // Set to new anim (if it's there) - - //Adrian: Basically everywhere else in the AI code this variable gets set to whatever our sequence is. - //But here it doesn't and not setting it causes any animation set through here to be stomped by the - //ideal sequence before it has a chance of playing out (since there's code that reselects the ideal - //sequence if it doesn't match the current one). - if ( hl2_episodic.GetBool() ) - { - m_nIdealSequence = iSequence; - } - } - } - - DispatchAnimEvents( this ); -} - -//----------------------------------------------------------------------------- - -void CAI_BaseNPC::PostRun( void ) -{ - AI_PROFILE_SCOPE(CAI_BaseNPC_PostRun); - - g_AIPostRunTimer.Start(); - - if ( !IsMoving() ) - { - if ( GetIdealActivity() == ACT_WALK || - GetIdealActivity() == ACT_RUN || - GetIdealActivity() == ACT_WALK_AIM || - GetIdealActivity() == ACT_RUN_AIM ) - { - PostRunStopMoving(); - } - } - - RunAnimation(); - - // update slave items (weapons) - Weapon_FrameUpdate(); - - g_AIPostRunTimer.End(); -} - -//----------------------------------------------------------------------------- - -void CAI_BaseNPC::PostRunStopMoving() -{ - DbgNavMsg1( this, "NPC %s failed to stop properly, slamming activity\n", GetDebugName() ); - if ( !GetNavigator()->SetGoalFromStoppingPath() ) - SetIdealActivity( GetStoppedActivity() ); // This is to prevent running in place -} - -//----------------------------------------------------------------------------- - -bool CAI_BaseNPC::ShouldAlwaysThink() -{ - // @TODO (toml 07-08-03): This needs to be beefed up. - // There are simple cases already seen where an AI can briefly leave - // the PVS while navigating to the player. Perhaps should incorporate a heuristic taking into - // account mode, enemy, last time saw player, player range etc. For example, if enemy is player, - // and player is within 100 feet, and saw the player within the past 15 seconds, keep running... - return HasSpawnFlags(SF_NPC_ALWAYSTHINK); -} - - -bool CAI_BaseNPC::ShouldPlayerAvoid( void ) -{ - if ( GetState() == NPC_STATE_SCRIPT ) - return true; - - if ( IsInAScript() ) - return true; - - if ( IsInLockedScene() == true ) - return true; - - if ( HasSpawnFlags( SF_NPC_ALTCOLLISION ) ) - return true; - - return false; -} - -//----------------------------------------------------------------------------- - -void CAI_BaseNPC::UpdateEfficiency( bool bInPVS ) -{ - // Sleeping NPCs always dormant - if ( GetSleepState() != AISS_AWAKE ) - { - SetEfficiency( AIE_DORMANT ); - return; - } - - m_bInChoreo = ( GetState() == NPC_STATE_SCRIPT || IsCurSchedule( SCHED_SCENE_GENERIC, false ) ); - - if ( !ShouldUseEfficiency() ) - { - SetEfficiency( AIE_NORMAL ); - SetMoveEfficiency( AIME_NORMAL ); - return; - } - - //--------------------------------- - - CBasePlayer *pPlayer = AI_GetSinglePlayer(); - static Vector vPlayerEyePosition; - static Vector vPlayerForward; - static int iPrevFrame = -1; - if ( gpGlobals->framecount != iPrevFrame ) - { - iPrevFrame = gpGlobals->framecount; - if ( pPlayer ) - { - pPlayer->EyePositionAndVectors( &vPlayerEyePosition, &vPlayerForward, NULL, NULL ); - } - } - - Vector vToNPC = GetAbsOrigin() - vPlayerEyePosition; - float playerDist = VectorNormalize( vToNPC ); - bool bPlayerFacing; - - bool bClientPVSExpanded = UTIL_ClientPVSIsExpanded(); - - if ( pPlayer ) - { - bPlayerFacing = ( bClientPVSExpanded || ( bInPVS && vPlayerForward.Dot( vToNPC ) > 0 ) ); - } - else - { - playerDist = 0; - bPlayerFacing = true; - } - - //--------------------------------- - - bool bInVisibilityPVS = ( bClientPVSExpanded && UTIL_FindClientInVisibilityPVS( edict() ) != NULL ); - - //--------------------------------- - - if ( ( bInPVS && ( bPlayerFacing || playerDist < 25*12 ) ) || bClientPVSExpanded ) - { - SetMoveEfficiency( AIME_NORMAL ); - } - else - { - SetMoveEfficiency( AIME_EFFICIENT ); - } - - //--------------------------------- - - if ( !IsRetail() && ai_efficiency_override.GetInt() > AIE_NORMAL && ai_efficiency_override.GetInt() <= AIE_DORMANT ) - { - SetEfficiency( (AI_Efficiency_t)ai_efficiency_override.GetInt() ); - return; - } - - //--------------------------------- - - // Some conditions will always force normal - if ( gpGlobals->curtime - GetLastAttackTime() < .15 ) - { - SetEfficiency( AIE_NORMAL ); - return; - } - - bool bFramerateOk = ( gpGlobals->frametime < ai_frametime_limit.GetFloat() ); - - if ( m_bForceConditionsGather || - gpGlobals->curtime - GetLastAttackTime() < .2 || - gpGlobals->curtime - m_flLastDamageTime < .2 || - ( GetState() < NPC_STATE_IDLE || GetState() > NPC_STATE_SCRIPT ) || - ( ( bInPVS || bInVisibilityPVS ) && - ( ( GetTask() && !TaskIsRunning() ) || - GetTaskInterrupt() > 0 || - m_bInChoreo ) ) ) - { - SetEfficiency( ( bFramerateOk ) ? AIE_NORMAL : AIE_EFFICIENT ); - return; - } - - AI_Efficiency_t minEfficiency; - - if ( !ShouldDefaultEfficient() ) - { - minEfficiency = ( bFramerateOk ) ? AIE_NORMAL : AIE_EFFICIENT; - } - else - { - minEfficiency = ( bFramerateOk ) ? AIE_EFFICIENT : AIE_VERY_EFFICIENT; - } - - // Stay normal if there's any chance of a relevant danger sound - bool bPotentialDanger = false; - - if ( GetSoundInterests() & SOUND_DANGER ) - { - int iSound = CSoundEnt::ActiveList(); - - while ( iSound != SOUNDLIST_EMPTY ) - { - CSound *pCurrentSound = CSoundEnt::SoundPointerForIndex( iSound ); - - float hearingSensitivity = HearingSensitivity(); - Vector vEarPosition = EarPosition(); - - if ( pCurrentSound && (SOUND_DANGER & pCurrentSound->SoundType()) ) - { - float flHearDistanceSq = pCurrentSound->Volume() * hearingSensitivity; - flHearDistanceSq *= flHearDistanceSq; - if ( pCurrentSound->GetSoundOrigin().DistToSqr( vEarPosition ) <= flHearDistanceSq ) - { - bPotentialDanger = true; - break; - } - } - - iSound = pCurrentSound->NextSound(); - } - } - - if ( bPotentialDanger ) - { - SetEfficiency( minEfficiency ); - return; - } - - //--------------------------------- - - if ( !pPlayer ) - { - // No heuristic currently for dedicated servers - SetEfficiency( minEfficiency ); - return; - } - - enum - { - NEAR, - MID, - FAR - }; - - int range; - if ( bInPVS ) - { - if ( playerDist < 15*12 ) - { - SetEfficiency( minEfficiency ); - return; - } - - range = ( playerDist < 50*12 ) ? NEAR : - ( playerDist < 200*12 ) ? MID : FAR; - } - else - { - range = ( playerDist < 25*12 ) ? NEAR : - ( playerDist < 100*12 ) ? MID : FAR; - } - - // Efficiency mappings - int state = GetState(); - if (state == NPC_STATE_SCRIPT ) // Treat script as alert. Already confirmed not in PVS - state = NPC_STATE_ALERT; - - static AI_Efficiency_t mappings[] = - { - // Idle - // In PVS - // Facing - AIE_NORMAL, - AIE_EFFICIENT, - AIE_EFFICIENT, - // Not facing - AIE_EFFICIENT, - AIE_EFFICIENT, - AIE_VERY_EFFICIENT, - // Not in PVS - AIE_VERY_EFFICIENT, - AIE_SUPER_EFFICIENT, - AIE_SUPER_EFFICIENT, - // Alert - // In PVS - // Facing - AIE_NORMAL, - AIE_EFFICIENT, - AIE_EFFICIENT, - // Not facing - AIE_NORMAL, - AIE_EFFICIENT, - AIE_EFFICIENT, - // Not in PVS - AIE_EFFICIENT, - AIE_VERY_EFFICIENT, - AIE_SUPER_EFFICIENT, - // Combat - // In PVS - // Facing - AIE_NORMAL, - AIE_NORMAL, - AIE_EFFICIENT, - // Not facing - AIE_NORMAL, - AIE_EFFICIENT, - AIE_EFFICIENT, - // Not in PVS - AIE_NORMAL, - AIE_EFFICIENT, - AIE_VERY_EFFICIENT, - }; - - static const int stateBase[] = { 0, 9, 18 }; - const int NOT_FACING_OFFSET = 3; - const int NO_PVS_OFFSET = 6; - - int iStateOffset = stateBase[state - NPC_STATE_IDLE] ; - int iFacingOffset = (!bInPVS || bPlayerFacing) ? 0 : NOT_FACING_OFFSET; - int iPVSOffset = (bInPVS) ? 0 : NO_PVS_OFFSET; - int iMapping = iStateOffset + iPVSOffset + iFacingOffset + range; - - Assert( iMapping < ARRAYSIZE( mappings ) ); - - AI_Efficiency_t efficiency = mappings[iMapping]; - - //--------------------------------- - - AI_Efficiency_t maxEfficiency = AIE_SUPER_EFFICIENT; - if ( bInVisibilityPVS && state >= NPC_STATE_ALERT ) - { - maxEfficiency = AIE_EFFICIENT; - } - else if ( bInVisibilityPVS || HasCondition( COND_SEE_PLAYER ) ) - { - maxEfficiency = AIE_VERY_EFFICIENT; - } - - //--------------------------------- - - SetEfficiency( clamp( efficiency, minEfficiency, maxEfficiency ) ); -} - - -//----------------------------------------------------------------------------- - -void CAI_BaseNPC::UpdateSleepState( bool bInPVS ) -{ - if ( GetSleepState() > AISS_AWAKE ) - { - CBasePlayer *pLocalPlayer = AI_GetSinglePlayer(); - if ( !pLocalPlayer ) - { - if ( gpGlobals->maxClients > 1 ) - { - Wake(); - } - else - { - Warning( "CAI_BaseNPC::UpdateSleepState called with NULL pLocalPlayer\n" ); - } - return; - } - - if ( m_flWakeRadius > .1 && !(pLocalPlayer->GetFlags() & FL_NOTARGET) && ( pLocalPlayer->GetAbsOrigin() - GetAbsOrigin() ).LengthSqr() <= Square(m_flWakeRadius) ) - Wake(); - else if ( GetSleepState() == AISS_WAITING_FOR_PVS ) - { - if ( bInPVS ) - Wake(); - } - else if ( GetSleepState() == AISS_WAITING_FOR_THREAT ) - { - if ( HasCondition( COND_LIGHT_DAMAGE ) || HasCondition( COND_HEAVY_DAMAGE ) ) - Wake(); - else - { - if ( bInPVS ) - { - for (int i = 1; i <= gpGlobals->maxClients; i++ ) - { - CBasePlayer *pPlayer = UTIL_PlayerByIndex( i ); - if ( pPlayer && !(pPlayer->GetFlags() & FL_NOTARGET) && pPlayer->FVisible( this ) ) - Wake(); - } - } - - // Should check for visible danger sounds - if ( (GetSoundInterests() & SOUND_DANGER) && !(HasSpawnFlags(SF_NPC_WAIT_TILL_SEEN)) ) - { - int iSound = CSoundEnt::ActiveList(); - - while ( iSound != SOUNDLIST_EMPTY ) - { - CSound *pCurrentSound = CSoundEnt::SoundPointerForIndex( iSound ); - Assert( pCurrentSound ); - - if ( (pCurrentSound->SoundType() & SOUND_DANGER) && - GetSenses()->CanHearSound( pCurrentSound ) && - SoundIsVisible( pCurrentSound )) - { - Wake(); - break; - } - - iSound = pCurrentSound->NextSound(); - } - } - } - } - } - else - { - // NPC is awake - // Don't let an NPC sleep if they're running a script! - if( !IsInAScript() && m_NPCState != NPC_STATE_SCRIPT ) - { - if( HasSleepFlags(AI_SLEEP_FLAG_AUTO_PVS) ) - { - if( !HasCondition(COND_IN_PVS) ) - { - SetSleepState( AISS_WAITING_FOR_PVS ); - Sleep(); - } - } - if( HasSleepFlags(AI_SLEEP_FLAG_AUTO_PVS_AFTER_PVS) ) - { - if( HasCondition(COND_IN_PVS) ) - { - // OK, we're in the player's PVS. Now switch to regular old AUTO_PVS sleep rules. - AddSleepFlags(AI_SLEEP_FLAG_AUTO_PVS); - RemoveSleepFlags(AI_SLEEP_FLAG_AUTO_PVS_AFTER_PVS); - } - } - } - } -} - -//----------------------------------------------------------------------------- - -struct AIRebalanceInfo_t -{ - CAI_BaseNPC * pNPC; - int iNextThinkTick; - bool bInPVS; - float dotPlayer; - float distPlayer; -}; - -int __cdecl ThinkRebalanceCompare( const AIRebalanceInfo_t *pLeft, const AIRebalanceInfo_t *pRight ) -{ - int base = pLeft->iNextThinkTick - pRight->iNextThinkTick; - if ( base != 0 ) - return base; - - if ( !pLeft->bInPVS && !pRight->bInPVS ) - return 0; - - if ( !pLeft->bInPVS ) - return 1; - - if ( !pRight->bInPVS ) - return -1; - - if ( pLeft->dotPlayer < 0 && pRight->dotPlayer < 0 ) - return 0; - - if ( pLeft->dotPlayer < 0 ) - return 1; - - if ( pRight->dotPlayer < 0 ) - return -1; - - const float NEAR_PLAYER = 50*12; - - if ( pLeft->distPlayer < NEAR_PLAYER && pRight->distPlayer >= NEAR_PLAYER ) - return -1; - - if ( pRight->distPlayer < NEAR_PLAYER && pLeft->distPlayer >= NEAR_PLAYER ) - return 1; - - if ( pLeft->dotPlayer > pRight->dotPlayer ) - return -1; - - if ( pLeft->dotPlayer < pRight->dotPlayer ) - return 1; - - return 0; -} - -inline bool CAI_BaseNPC::CanThinkRebalance() -{ - if ( m_pfnThink != (BASEPTR)&CAI_BaseNPC::CallNPCThink ) - { - return false; - } - - if ( m_bInChoreo ) - { - return false; - } - - if ( m_NPCState == NPC_STATE_DEAD ) - { - return false; - } - - if ( GetSleepState() != AISS_AWAKE ) - { - return false; - } - - if ( !m_bUsingStandardThinkTime /*&& m_iFrameBlocked == -1 */ ) - { - return false; - } - - return true; -} - -void CAI_BaseNPC::RebalanceThinks() -{ - bool bDebugThinkTicks = ai_debug_think_ticks.GetBool(); - if ( bDebugThinkTicks ) - { - static int iPrevTick; - static int nThinksInTick; - static int nRebalanceableThinksInTick; - - if ( gpGlobals->tickcount != iPrevTick ) - { - DevMsg( "NPC per tick is %d [%d] (tick %d, frame %d)\n", nRebalanceableThinksInTick, nThinksInTick, iPrevTick, gpGlobals->framecount ); - iPrevTick = gpGlobals->tickcount; - nThinksInTick = 0; - nRebalanceableThinksInTick = 0; - } - nThinksInTick++; - if ( CanThinkRebalance() ) - nRebalanceableThinksInTick++; - } - - if ( ShouldRebalanceThinks() && gpGlobals->tickcount >= gm_iNextThinkRebalanceTick ) - { - AI_PROFILE_SCOPE(AI_Think_Rebalance ); - - static CUtlVector rebalanceCandidates( 16, 64 ); - gm_iNextThinkRebalanceTick = gpGlobals->tickcount + TIME_TO_TICKS( random->RandomFloat( 3, 5) ); - - int i; - - CBasePlayer *pPlayer = AI_GetSinglePlayer(); - Vector vPlayerForward; - Vector vPlayerEyePosition; - - vPlayerForward.Init(); - vPlayerEyePosition.Init(); - - if ( pPlayer ) - { - pPlayer->EyePositionAndVectors( &vPlayerEyePosition, &vPlayerForward, NULL, NULL ); - } - - int iTicksPer10Hz = TIME_TO_TICKS( .1 ); - int iMinTickRebalance = gpGlobals->tickcount - 1; // -1 needed for alternate ticks - int iMaxTickRebalance = gpGlobals->tickcount + iTicksPer10Hz; - - for ( i = 0; i < g_AI_Manager.NumAIs(); i++ ) - { - CAI_BaseNPC *pCandidate = g_AI_Manager.AccessAIs()[i]; - if ( pCandidate->CanThinkRebalance() && - ( pCandidate->GetNextThinkTick() >= iMinTickRebalance && - pCandidate->GetNextThinkTick() < iMaxTickRebalance ) ) - { - int iInfo = rebalanceCandidates.AddToTail(); - - rebalanceCandidates[iInfo].pNPC = pCandidate; - rebalanceCandidates[iInfo].iNextThinkTick = pCandidate->GetNextThinkTick(); - - if ( pCandidate->IsFlaggedEfficient() ) - { - rebalanceCandidates[iInfo].bInPVS = false; - } - else if ( pPlayer ) - { - Vector vToCandidate = pCandidate->EyePosition() - vPlayerEyePosition; - rebalanceCandidates[iInfo].bInPVS = ( UTIL_FindClientInPVS( pCandidate->edict() ) != NULL ); - rebalanceCandidates[iInfo].distPlayer = VectorNormalize( vToCandidate ); - rebalanceCandidates[iInfo].dotPlayer = vPlayerForward.Dot( vToCandidate ); - } - else - { - rebalanceCandidates[iInfo].bInPVS = true; - rebalanceCandidates[iInfo].dotPlayer = 1; - rebalanceCandidates[iInfo].distPlayer = 0; - } - } - else if ( bDebugThinkTicks ) - DevMsg( " Ignoring %d\n", pCandidate->GetNextThinkTick() ); - } - - if ( rebalanceCandidates.Count() ) - { - rebalanceCandidates.Sort( ThinkRebalanceCompare ); - - int iMaxThinkersPerTick = ceil( (float)( rebalanceCandidates.Count() + 1 ) / (float)iTicksPer10Hz ); // +1 to account for "this" - - int iCurTickDistributing = min( gpGlobals->tickcount, rebalanceCandidates[0].iNextThinkTick ); - int iRemainingThinksToDistribute = iMaxThinkersPerTick - 1; // Start with one fewer first time because "this" is - // always gets a slot in the current tick to avoid complications - // in the calculation of "last think" - - if ( bDebugThinkTicks ) - { - DevMsg( "Rebalance %d!\n", rebalanceCandidates.Count() + 1 ); - DevMsg( " Distributing %d\n", iCurTickDistributing ); - } - - for ( i = 0; i < rebalanceCandidates.Count(); i++ ) - { - if ( iRemainingThinksToDistribute == 0 || rebalanceCandidates[i].iNextThinkTick > iCurTickDistributing ) - { - if ( rebalanceCandidates[i].iNextThinkTick <= iCurTickDistributing ) - { - iCurTickDistributing = iCurTickDistributing + 1; - } - else - { - iCurTickDistributing = rebalanceCandidates[i].iNextThinkTick; - } - - if ( bDebugThinkTicks ) - DevMsg( " Distributing %d\n", iCurTickDistributing ); - - iRemainingThinksToDistribute = iMaxThinkersPerTick; - } - - if ( rebalanceCandidates[i].pNPC->GetNextThinkTick() != iCurTickDistributing ) - { - if ( bDebugThinkTicks ) - DevMsg( " Bumping %d to %d\n", rebalanceCandidates[i].pNPC->GetNextThinkTick(), iCurTickDistributing ); - - rebalanceCandidates[i].pNPC->SetNextThink( TICKS_TO_TIME( iCurTickDistributing ) ); - } - else if ( bDebugThinkTicks ) - { - DevMsg( " Leaving %d\n", rebalanceCandidates[i].pNPC->GetNextThinkTick() ); - } - - iRemainingThinksToDistribute--; - } - } - - rebalanceCandidates.RemoveAll(); - - if ( bDebugThinkTicks ) - { - DevMsg( "New distribution is:\n"); - for ( i = 0; i < g_AI_Manager.NumAIs(); i++ ) - { - DevMsg( " %d\n", g_AI_Manager.AccessAIs()[i]->GetNextThinkTick() ); - } - } - - Assert( GetNextThinkTick() == TICK_NEVER_THINK ); // never change this objects tick - } -} - -static float g_NpcTimeThisFrame; -static float g_StartTimeCurThink; - -bool CAI_BaseNPC::PreNPCThink() -{ - static int iPrevFrame = -1; - static float frameTimeLimit = FLT_MAX; - static const ConVar *pHostTimescale; - - if ( frameTimeLimit == FLT_MAX ) - { - pHostTimescale = cvar->FindVar( "host_timescale" ); - } - - bool bUseThinkLimits = ( !m_bInChoreo && ShouldUseFrameThinkLimits() ); - -#ifdef _DEBUG - const float NPC_THINK_LIMIT = 30.0 / 1000.0; -#else - const float NPC_THINK_LIMIT = ( !IsXbox() ) ? (10.0 / 1000.0) : (12.5 / 1000.0); -#endif - - g_StartTimeCurThink = 0; - - if ( bUseThinkLimits && VCRGetMode() == VCR_Disabled ) - { - if ( m_iFrameBlocked == gpGlobals->framecount ) - { - DbgFrameLimitMsg( "Stalled %d (%d)\n", this, gpGlobals->framecount ); - SetNextThink( gpGlobals->curtime ); - return false; - } - else if ( gpGlobals->framecount != iPrevFrame ) - { - DbgFrameLimitMsg( "--- FRAME: %d (%d)\n", this, gpGlobals->framecount ); - float timescale = pHostTimescale->GetFloat(); - if ( timescale < 1 ) - timescale = 1; - - iPrevFrame = gpGlobals->framecount; - frameTimeLimit = NPC_THINK_LIMIT * timescale; - g_NpcTimeThisFrame = 0; - } - else - { - if ( g_NpcTimeThisFrame > NPC_THINK_LIMIT ) - { - float timeSinceLastRealThink = gpGlobals->curtime - m_flLastRealThinkTime; - // Don't bump anyone more that a quarter second - if ( timeSinceLastRealThink <= .25 ) - { - DbgFrameLimitMsg( "Bumped %d (%d)\n", this, gpGlobals->framecount ); - m_iFrameBlocked = gpGlobals->framecount; - SetNextThink( gpGlobals->curtime ); - return false; - } - else - { - DbgFrameLimitMsg( "(Over %d )\n", this ); - } - } - } - - DbgFrameLimitMsg( "Running %d (%d)\n", this, gpGlobals->framecount ); - g_StartTimeCurThink = engine->Time(); - - m_iFrameBlocked = -1; - m_nLastThinkTick = TIME_TO_TICKS( m_flLastRealThinkTime ); - } - - return true; -} - -void CAI_BaseNPC::PostNPCThink( void ) -{ - if ( g_StartTimeCurThink != 0.0 && VCRGetMode() == VCR_Disabled ) - { - g_NpcTimeThisFrame += engine->Time() - g_StartTimeCurThink; - } -} - -void CAI_BaseNPC::CallNPCThink( void ) -{ - RebalanceThinks(); - - //--------------------------------- - - m_bUsingStandardThinkTime = false; - - //--------------------------------- - - if ( !PreNPCThink() ) - { - return; - } - - // reduce cache queries by locking model in memory - MDLCACHE_CRITICAL_SECTION(); - - this->NPCThink(); - - m_flLastRealThinkTime = gpGlobals->curtime; - - PostNPCThink(); -} - -bool NPC_CheckBrushExclude( CBaseEntity *pEntity, CBaseEntity *pBrush ) -{ - CAI_BaseNPC *pNPC = pEntity->MyNPCPointer(); - - if ( pNPC ) - { - return pNPC->GetMoveProbe()->ShouldBrushBeIgnored( pBrush ); - } - - return false; -} - -class CTraceFilterPlayerAvoidance : public CTraceFilterEntitiesOnly -{ -public: - CTraceFilterPlayerAvoidance( const CBaseEntity *pEntity ) { m_pIgnore = pEntity; } - - virtual bool ShouldHitEntity( IHandleEntity *pHandleEntity, int contentsMask ) - { - CBaseEntity *pEntity = EntityFromEntityHandle( pHandleEntity ); - - if ( m_pIgnore == pEntity ) - return false; - - if ( pEntity->IsPlayer() ) - return true; - - return false; - } -private: - - const CBaseEntity *m_pIgnore; -}; - -void CAI_BaseNPC::GetPlayerAvoidBounds( Vector *pMins, Vector *pMaxs ) -{ - *pMins = WorldAlignMins(); - *pMaxs = WorldAlignMaxs(); -} - -ConVar ai_debug_avoidancebounds( "ai_debug_avoidancebounds", "0" ); - -void CAI_BaseNPC::SetPlayerAvoidState( void ) -{ - bool bShouldPlayerAvoid = false; - Vector vNothing; - - GetSequenceLinearMotion( GetSequence(), &vNothing ); - bool bIsMoving = ( IsMoving() || ( vNothing != vec3_origin ) ); - - //If we are coming out of a script, check if we are stuck inside the player. - if ( m_bPerformAvoidance || ( ShouldPlayerAvoid() && bIsMoving ) ) - { - trace_t trace; - Vector vMins, vMaxs; - - GetPlayerAvoidBounds( &vMins, &vMaxs ); - - CBasePlayer *pLocalPlayer = AI_GetSinglePlayer(); - - if ( pLocalPlayer ) - { - bShouldPlayerAvoid = IsBoxIntersectingBox( GetAbsOrigin() + vMins, GetAbsOrigin() + vMaxs, - pLocalPlayer->GetAbsOrigin() + pLocalPlayer->WorldAlignMins(), pLocalPlayer->GetAbsOrigin() + pLocalPlayer->WorldAlignMaxs() ); - } - - if ( ai_debug_avoidancebounds.GetBool() ) - { - int iRed = ( bShouldPlayerAvoid == true ) ? 255 : 0; - - NDebugOverlay::Box( GetAbsOrigin(), vMins, vMaxs, iRed, 0, 255, 64, 0.1 ); - } - } - - m_bPlayerAvoidState = ShouldPlayerAvoid(); - m_bPerformAvoidance = bShouldPlayerAvoid; - - if ( GetCollisionGroup() == COLLISION_GROUP_NPC || GetCollisionGroup() == COLLISION_GROUP_NPC_ACTOR ) - { - if ( m_bPerformAvoidance == true ) - { - SetCollisionGroup( COLLISION_GROUP_NPC_ACTOR ); - } - else - { - SetCollisionGroup( COLLISION_GROUP_NPC ); - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: Enables player avoidance when the player's vphysics shadow penetrates our vphysics shadow. This can -// happen when the player is hit by a combine ball, which pushes them into an adjacent npc. Subclasses should -// override this if it causes problems, but in general this will solve cases of the player getting stuck in -// the NPC from being pushed. -//----------------------------------------------------------------------------- -void CAI_BaseNPC::PlayerPenetratingVPhysics( void ) -{ - m_bPerformAvoidance = true; -} - -//----------------------------------------------------------------------------- - -bool CAI_BaseNPC::CheckPVSCondition() -{ - bool bInPVS = ( UTIL_FindClientInPVS( edict() ) != NULL ) || (UTIL_ClientPVSIsExpanded() && UTIL_FindClientInVisibilityPVS( edict() )); - - if ( bInPVS ) - SetCondition( COND_IN_PVS ); - else - ClearCondition( COND_IN_PVS ); - - return bInPVS; -} - - -//----------------------------------------------------------------------------- -// NPC Think - calls out to core AI functions and handles this -// npc's specific animation events -// - -void CAI_BaseNPC::NPCThink( void ) -{ - if ( m_bCheckContacts ) - { - CheckPhysicsContacts(); - } - - Assert( !(m_NPCState == NPC_STATE_DEAD && m_lifeState == LIFE_ALIVE) ); - - //--------------------------------- - - SetNextThink( TICK_NEVER_THINK ); - - //--------------------------------- - - bool bInPVS = CheckPVSCondition(); - - //--------------------------------- - - UpdateSleepState( bInPVS ); - - //--------------------------------- - bool bRanDecision = false; - - if ( GetEfficiency() < AIE_DORMANT && GetSleepState() == AISS_AWAKE ) - { - static CFastTimer timer; - float thinkLimit = ai_show_think_tolerance.GetFloat(); - - if ( thinkLimit > 0 ) - timer.Start(); - - if ( g_pAINetworkManager && g_pAINetworkManager->IsInitialized() ) - { - VPROF_BUDGET( "NPCs", VPROF_BUDGETGROUP_NPCS ); - - AI_PROFILE_SCOPE_BEGIN_( GetClassScheduleIdSpace()->GetClassName() ); // need to use a string stable from map load to map load - - SetPlayerAvoidState(); - - if ( PreThink() ) - { - if ( m_flNextDecisionTime <= gpGlobals->curtime ) - { - bRanDecision = true; - m_ScheduleState.bTaskRanAutomovement = false; - m_ScheduleState.bTaskUpdatedYaw = false; - RunAI(); - } - else - { - if ( m_ScheduleState.bTaskRanAutomovement ) - AutoMovement(); - if ( m_ScheduleState.bTaskUpdatedYaw ) - GetMotor()->UpdateYaw(); - } - - PostRun(); - - PerformMovement(); - - m_bIsMoving = IsMoving(); - - PostMovement(); - - SetSimulationTime( gpGlobals->curtime ); - } - else - m_flTimeLastMovement = FLT_MAX; - - AI_PROFILE_SCOPE_END(); - } - - if ( thinkLimit > 0 ) - { - timer.End(); - - float thinkTime = g_AIRunTimer.GetDuration().GetMillisecondsF(); - - if ( thinkTime > thinkLimit ) - { - int color = (int)RemapVal( thinkTime, thinkLimit, thinkLimit * 3, 96.0, 255.0 ); - if ( color > 255 ) - color = 255; - else if ( color < 96 ) - color = 96; - - Vector right; - Vector vecPoint; - - vecPoint = EyePosition() + Vector( 0, 0, 12 ); - GetVectors( NULL, &right, NULL ); - NDebugOverlay::Line( vecPoint, vecPoint + Vector( 0, 0, 64 ), color, 0, 0, false , 1.0 ); - NDebugOverlay::Line( vecPoint, vecPoint + Vector( 0, 0, 16 ) + right * 16, color, 0, 0, false , 1.0 ); - NDebugOverlay::Line( vecPoint, vecPoint + Vector( 0, 0, 16 ) - right * 16, color, 0, 0, false , 1.0 ); - } - } - } - - m_bUsingStandardThinkTime = ( GetNextThinkTick() == TICK_NEVER_THINK ); - - UpdateEfficiency( bInPVS ); - - if ( m_bUsingStandardThinkTime ) - { - static const char *ppszEfficiencies[] = - { - "AIE_NORMAL", - "AIE_EFFICIENT", - "AIE_VERY_EFFICIENT", - "AIE_SUPER_EFFICIENT", - "AIE_DORMANT", - }; - - static const char *ppszMoveEfficiencies[] = - { - "AIME_NORMAL", - "AIME_EFFICIENT", - }; - - if ( ai_debug_efficiency.GetBool() ) - DevMsg( this, "Eff: %s, Move: %s\n", ppszEfficiencies[GetEfficiency()], ppszMoveEfficiencies[GetMoveEfficiency()] ); - - static float g_DecisionIntervals[] = - { - .1, // AIE_NORMAL - .2, // AIE_EFFICIENT - .4, // AIE_VERY_EFFICIENT - .6, // AIE_SUPER_EFFICIENT - }; - - if ( bRanDecision ) - { - m_flNextDecisionTime = gpGlobals->curtime + g_DecisionIntervals[GetEfficiency()]; - } - - if ( GetMoveEfficiency() == AIME_NORMAL || GetEfficiency() == AIE_NORMAL ) - { - SetNextThink( gpGlobals->curtime + .1 ); - } - else - { - SetNextThink( gpGlobals->curtime + .2 ); - } - } - else - { - m_flNextDecisionTime = 0; - } -} - -//========================================================= -// CAI_BaseNPC - USE - will make a npc angry at whomever -// activated it. -//========================================================= -void CAI_BaseNPC::NPCUse ( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) -{ - return; - - // Can't +USE NPCs running scripts - if ( GetState() == NPC_STATE_SCRIPT ) - return; - - if ( IsInAScript() ) - return; - - SetIdealState( NPC_STATE_ALERT ); -} - -//----------------------------------------------------------------------------- -// Purpose: Virtual function that allows us to have any npc ignore a set of -// shared conditions. -// -//----------------------------------------------------------------------------- -void CAI_BaseNPC::RemoveIgnoredConditions ( void ) -{ - m_ConditionsPreIgnore = m_Conditions; - m_Conditions.And( m_InverseIgnoreConditions, &m_Conditions ); - - if ( m_NPCState == NPC_STATE_SCRIPT && m_hCine ) - m_hCine->RemoveIgnoredConditions(); -} - -//========================================================= -// RangeAttack1Conditions -//========================================================= -int CAI_BaseNPC::RangeAttack1Conditions ( float flDot, float flDist ) -{ - if ( flDist < 64) - { - return COND_TOO_CLOSE_TO_ATTACK; - } - else if (flDist > 784) - { - return COND_TOO_FAR_TO_ATTACK; - } - else if (flDot < 0.5) - { - return COND_NOT_FACING_ATTACK; - } - - return COND_CAN_RANGE_ATTACK1; -} - -//========================================================= -// RangeAttack2Conditions -//========================================================= -int CAI_BaseNPC::RangeAttack2Conditions ( float flDot, float flDist ) -{ - if ( flDist < 64) - { - return COND_TOO_CLOSE_TO_ATTACK; - } - else if (flDist > 512) - { - return COND_TOO_FAR_TO_ATTACK; - } - else if (flDot < 0.5) - { - return COND_NOT_FACING_ATTACK; - } - - return COND_CAN_RANGE_ATTACK2; -} - -//========================================================= -// MeleeAttack1Conditions -//========================================================= -int CAI_BaseNPC::MeleeAttack1Conditions ( float flDot, float flDist ) -{ - if ( flDist > 64) - { - return COND_TOO_FAR_TO_ATTACK; - } - else if (flDot < 0.7) - { - return 0; - } - else if (GetEnemy() == NULL) - { - return 0; - } - - // Decent fix to keep folks from kicking/punching hornets and snarks is to check the onground flag(sjb) - if ( GetEnemy()->GetFlags() & FL_ONGROUND ) - { - return COND_CAN_MELEE_ATTACK1; - } - return 0; -} - -//========================================================= -// MeleeAttack2Conditions -//========================================================= -int CAI_BaseNPC::MeleeAttack2Conditions ( float flDot, float flDist ) -{ - if ( flDist > 64) - { - return COND_TOO_FAR_TO_ATTACK; - } - else if (flDot < 0.7) - { - return 0; - } - return COND_CAN_MELEE_ATTACK2; -} - -// Get capability mask -int CAI_BaseNPC::CapabilitiesGet( void ) const -{ - int capability = m_afCapability; - if ( GetActiveWeapon() ) - { - capability |= GetActiveWeapon()->CapabilitiesGet(); - } - return capability; -} - -// Set capability mask -int CAI_BaseNPC::CapabilitiesAdd( int capability ) -{ - m_afCapability |= capability; - - return m_afCapability; -} - -// Set capability mask -int CAI_BaseNPC::CapabilitiesRemove( int capability ) -{ - m_afCapability &= ~capability; - - return m_afCapability; -} - -// Clear capability mask -void CAI_BaseNPC::CapabilitiesClear( void ) -{ - m_afCapability = 0; -} - - -//========================================================= -// ClearAttacks - clear out all attack conditions -//========================================================= -void CAI_BaseNPC::ClearAttackConditions( ) -{ - // Clear all attack conditions - ClearCondition( COND_CAN_RANGE_ATTACK1 ); - ClearCondition( COND_CAN_RANGE_ATTACK2 ); - ClearCondition( COND_CAN_MELEE_ATTACK1 ); - ClearCondition( COND_CAN_MELEE_ATTACK2 ); - ClearCondition( COND_WEAPON_HAS_LOS ); - ClearCondition( COND_WEAPON_BLOCKED_BY_FRIEND ); - ClearCondition( COND_WEAPON_PLAYER_IN_SPREAD ); // Player in shooting direction - ClearCondition( COND_WEAPON_PLAYER_NEAR_TARGET ); // Player near shooting position - ClearCondition( COND_WEAPON_SIGHT_OCCLUDED ); -} - -//========================================================= -// GatherAttackConditions - sets all of the bits for attacks that the -// npc is capable of carrying out on the passed entity. -//========================================================= - -void CAI_BaseNPC::GatherAttackConditions( CBaseEntity *pTarget, float flDist ) -{ - AI_PROFILE_SCOPE(CAI_BaseNPC_GatherAttackConditions); - - Vector vecLOS = ( pTarget->GetAbsOrigin() - GetAbsOrigin() ); - vecLOS.z = 0; - VectorNormalize( vecLOS ); - - Vector vBodyDir = BodyDirection2D( ); - float flDot = DotProduct( vecLOS, vBodyDir ); - - // we know the enemy is in front now. We'll find which attacks the npc is capable of by - // checking for corresponding Activities in the model file, then do the simple checks to validate - // those attack types. - - int capability; - Vector targetPos; - bool bWeaponHasLOS; - int condition; - - capability = CapabilitiesGet(); - - // Clear all attack conditions - AI_PROFILE_SCOPE_BEGIN( CAI_BaseNPC_GatherAttackConditions_PrimaryWeaponLOS ); - - // @TODO (toml 06-15-03): There are simple cases where - // the upper torso of the enemy is visible, and the NPC is at an angle below - // them, but the above test fails because BodyTarget returns the center of - // the target. This needs some better handling/closer evaluation - - // Try the eyes first, as likely to succeed (because can see or else wouldn't be here) thus reducing - // the odds of the need for a second trace - ClearAttackConditions(); - targetPos = pTarget->EyePosition(); - bWeaponHasLOS = WeaponLOSCondition( GetAbsOrigin(), targetPos, true ); - - AI_PROFILE_SCOPE_END(); - - if ( !bWeaponHasLOS ) - { - AI_PROFILE_SCOPE( CAI_BaseNPC_GatherAttackConditions_SecondaryWeaponLOS ); - ClearAttackConditions( ); - targetPos = pTarget->BodyTarget( GetAbsOrigin() ); - bWeaponHasLOS = WeaponLOSCondition( GetAbsOrigin(), targetPos, true ); - } - else - { - SetCondition( COND_WEAPON_HAS_LOS ); - } - - bool bWeaponIsReady = (GetActiveWeapon() && !IsWeaponStateChanging()); - - // FIXME: move this out of here - if ( (capability & bits_CAP_WEAPON_RANGE_ATTACK1) && bWeaponIsReady ) - { - AI_PROFILE_SCOPE( CAI_BaseNPC_GatherAttackConditions_WeaponRangeAttack1Condition ); - - condition = GetActiveWeapon()->WeaponRangeAttack1Condition(flDot, flDist); - - if ( condition == COND_NOT_FACING_ATTACK && FInAimCone( targetPos ) ) - DevMsg( "Warning: COND_NOT_FACING_ATTACK set but FInAimCone is true\n" ); - - if (condition != COND_CAN_RANGE_ATTACK1 || bWeaponHasLOS) - { - SetCondition(condition); - } - } - else if ( capability & bits_CAP_INNATE_RANGE_ATTACK1 ) - { - AI_PROFILE_SCOPE( CAI_BaseNPC_GatherAttackConditions_RangeAttack1Condition ); - - condition = RangeAttack1Conditions( flDot, flDist ); - if (condition != COND_CAN_RANGE_ATTACK1 || bWeaponHasLOS) - { - SetCondition(condition); - } - } - - if ( (capability & bits_CAP_WEAPON_RANGE_ATTACK2) && bWeaponIsReady && ( GetActiveWeapon()->CapabilitiesGet() & bits_CAP_WEAPON_RANGE_ATTACK2 ) ) - { - AI_PROFILE_SCOPE( CAI_BaseNPC_GatherAttackConditions_WeaponRangeAttack2Condition ); - - condition = GetActiveWeapon()->WeaponRangeAttack2Condition(flDot, flDist); - if (condition != COND_CAN_RANGE_ATTACK2 || bWeaponHasLOS) - { - SetCondition(condition); - } - } - else if ( capability & bits_CAP_INNATE_RANGE_ATTACK2 ) - { - AI_PROFILE_SCOPE( CAI_BaseNPC_GatherAttackConditions_RangeAttack2Condition ); - - condition = RangeAttack2Conditions( flDot, flDist ); - if (condition != COND_CAN_RANGE_ATTACK2 || bWeaponHasLOS) - { - SetCondition(condition); - } - } - - if ( (capability & bits_CAP_WEAPON_MELEE_ATTACK1) && bWeaponIsReady) - { - AI_PROFILE_SCOPE( CAI_BaseNPC_GatherAttackConditions_WeaponMeleeAttack1Condition ); - SetCondition(GetActiveWeapon()->WeaponMeleeAttack1Condition(flDot, flDist)); - } - else if ( capability & bits_CAP_INNATE_MELEE_ATTACK1 ) - { - AI_PROFILE_SCOPE( CAI_BaseNPC_GatherAttackConditions_MeleeAttack1Condition ); - SetCondition(MeleeAttack1Conditions ( flDot, flDist )); - } - - if ( (capability & bits_CAP_WEAPON_MELEE_ATTACK2) && bWeaponIsReady) - { - AI_PROFILE_SCOPE( CAI_BaseNPC_GatherAttackConditions_WeaponMeleeAttack2Condition ); - SetCondition(GetActiveWeapon()->WeaponMeleeAttack2Condition(flDot, flDist)); - } - else if ( capability & bits_CAP_INNATE_MELEE_ATTACK2 ) - { - AI_PROFILE_SCOPE( CAI_BaseNPC_GatherAttackConditions_MeleeAttack2Condition ); - SetCondition(MeleeAttack2Conditions ( flDot, flDist )); - } - - // ----------------------------------------------------------------- - // If any attacks are possible clear attack specific bits - // ----------------------------------------------------------------- - if (HasCondition(COND_CAN_RANGE_ATTACK2) || - HasCondition(COND_CAN_RANGE_ATTACK1) || - HasCondition(COND_CAN_MELEE_ATTACK2) || - HasCondition(COND_CAN_MELEE_ATTACK1) ) - { - ClearCondition(COND_TOO_CLOSE_TO_ATTACK); - ClearCondition(COND_TOO_FAR_TO_ATTACK); - ClearCondition(COND_WEAPON_BLOCKED_BY_FRIEND); - } -} - - -//========================================================= -// SetState -//========================================================= -void CAI_BaseNPC::SetState( NPC_STATE State ) -{ - NPC_STATE OldState; - - OldState = m_NPCState; - - if ( State != m_NPCState ) - { - m_flLastStateChangeTime = gpGlobals->curtime; - } - - switch( State ) - { - // Drop enemy pointers when going to idle - case NPC_STATE_IDLE: - - if ( GetEnemy() != NULL ) - { - SetEnemy( NULL ); // not allowed to have an enemy anymore. - DevMsg( 2, "Stripped\n" ); - } - break; - } - - bool fNotifyChange = false; - - if( m_NPCState != State ) - { - // Don't notify if we're changing to a state we're already in! - fNotifyChange = true; - } - - m_NPCState = State; - SetIdealState( State ); - - // Notify the character that its state has changed. - if( fNotifyChange ) - { - OnStateChange( OldState, m_NPCState ); - } -} - -bool CAI_BaseNPC::WokeThisTick() const -{ - return m_nWakeTick == gpGlobals->tickcount ? true : false; -} - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -void CAI_BaseNPC::Wake( bool bFireOutput ) -{ - if ( GetSleepState() != AISS_AWAKE ) - { - m_nWakeTick = gpGlobals->tickcount; - SetSleepState( AISS_AWAKE ); - RemoveEffects( EF_NODRAW ); - if ( bFireOutput ) - m_OnWake.FireOutput( this, this ); - - if ( m_bWakeSquad && GetSquad() ) - { - AISquadIter_t iter; - for ( CAI_BaseNPC *pSquadMember = GetSquad()->GetFirstMember( &iter ); pSquadMember; pSquadMember = GetSquad()->GetNextMember( &iter ) ) - { - if ( pSquadMember->IsAlive() && pSquadMember != this ) - { - pSquadMember->m_bWakeSquad = false; - pSquadMember->Wake(); - } - } - - } - } -} - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -void CAI_BaseNPC::Sleep() -{ - // Don't render. - AddEffects( EF_NODRAW ); - - if( GetState() == NPC_STATE_SCRIPT ) - { - Warning( "%s put to sleep while in Scripted state!\n"); - } - - VacateStrategySlot(); - - // Slam my schedule. - SetSchedule( SCHED_SLEEP ); - - m_OnSleep.FireOutput( this, this ); -} - -//----------------------------------------------------------------------------- -// Sets all sensing-related conditions -//----------------------------------------------------------------------------- -void CAI_BaseNPC::PerformSensing( void ) -{ - GetSenses()->PerformSensing(); -} - - -//----------------------------------------------------------------------------- - -void CAI_BaseNPC::ClearSenseConditions( void ) -{ - static int conditionsToClear[] = - { - COND_SEE_HATE, - COND_SEE_DISLIKE, - COND_SEE_ENEMY, - COND_SEE_FEAR, - COND_SEE_NEMESIS, - COND_SEE_PLAYER, - COND_HEAR_DANGER, - COND_HEAR_COMBAT, - COND_HEAR_WORLD, - COND_HEAR_PLAYER, - COND_HEAR_THUMPER, - COND_HEAR_BUGBAIT, - COND_HEAR_PHYSICS_DANGER, - COND_HEAR_MOVE_AWAY, - COND_SMELL, - }; - - ClearConditions( conditionsToClear, ARRAYSIZE( conditionsToClear ) ); -} - -//----------------------------------------------------------------------------- - -void CAI_BaseNPC::CheckOnGround( void ) -{ - bool bScriptedWait = ( IsCurSchedule( SCHED_WAIT_FOR_SCRIPT ) || ( m_hCine && m_scriptState == CAI_BaseNPC::SCRIPT_WAIT ) ); - if ( !bScriptedWait && !HasCondition( COND_FLOATING_OFF_GROUND ) ) - { - // parented objects are never floating - if (GetMoveParent() != NULL) - return; - - if ( ( GetNavType() == NAV_GROUND ) && ( GetMoveType() != MOVETYPE_VPHYSICS ) && ( GetMoveType() != MOVETYPE_NONE ) ) - { - if ( m_CheckOnGroundTimer.Expired() ) - { - m_CheckOnGroundTimer.Set(0.5); - - // check a shrunk box centered around the foot - Vector maxs = WorldAlignMaxs(); - Vector mins = WorldAlignMins(); - - if ( mins != maxs ) // some NPCs have no hull, so mins == maxs == vec3_origin - { - maxs -= Vector( 0.0f, 0.0f, 0.2f ); - - Vector vecStart = GetAbsOrigin() + Vector( 0, 0, .1f ); - Vector vecDown = GetAbsOrigin(); - vecDown.z -= 4.0; - - trace_t trace; - m_pMoveProbe->TraceHull( vecStart, vecDown, mins, maxs, MASK_NPCSOLID, &trace ); - - if (trace.fraction == 1.0) - { - SetCondition( COND_FLOATING_OFF_GROUND ); - SetGroundEntity( NULL ); - } - else - { - if ( trace.startsolid && trace.m_pEnt->GetMoveType() == MOVETYPE_VPHYSICS && - trace.m_pEnt->VPhysicsGetObject() && trace.m_pEnt->VPhysicsGetObject()->GetMass() < VPHYSICS_LARGE_OBJECT_MASS ) - { - // stuck inside a small physics object? - m_CheckOnGroundTimer.Set(0.1f); - NPCPhysics_CreateSolver( this, trace.m_pEnt, true, 0.25f ); - if ( VPhysicsGetObject() ) - { - VPhysicsGetObject()->RecheckContactPoints(); - } - } - // Check to see if someone changed the ground on us... - if ( trace.m_pEnt && trace.m_pEnt != GetGroundEntity() ) - { - SetGroundEntity( trace.m_pEnt ); - } - } - } - } - } - } - else - { - // parented objects are never floating - if ( bScriptedWait || GetMoveParent() != NULL || (GetFlags() & FL_ONGROUND ) || GetNavType() != NAV_GROUND ) - { - ClearCondition( COND_FLOATING_OFF_GROUND ); - } - } - -} - -void CAI_BaseNPC::NotifyPushMove() -{ - // don't recheck ground when I'm being push-moved - m_CheckOnGroundTimer.Set( 0.5f ); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -bool CAI_BaseNPC::CanFlinch( void ) -{ - if ( IsCurSchedule( SCHED_BIG_FLINCH ) ) - return false; - - if ( m_flNextFlinchTime >= gpGlobals->curtime ) - return false; - - return true; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CAI_BaseNPC::CheckFlinches( void ) -{ - // If we're currently flinching, don't allow gesture flinches to be overlaid - if ( IsCurSchedule( SCHED_BIG_FLINCH ) ) - { - ClearCondition( COND_LIGHT_DAMAGE ); - ClearCondition( COND_HEAVY_DAMAGE ); - } - - // If we've taken heavy damage, try to do a full schedule flinch - if ( HasCondition(COND_HEAVY_DAMAGE) ) - { - // If we've already flinched recently, gesture flinch instead. - if ( HasMemory(bits_MEMORY_FLINCHED) ) - { - // Clear the heavy damage condition so we don't interrupt schedules - // when we play a gesture flinch because we recently did a full flinch. - // Prevents the player from stun-locking enemies, even though they don't full flinch. - ClearCondition( COND_HEAVY_DAMAGE ); - } - else if ( !HasInterruptCondition(COND_HEAVY_DAMAGE) ) - { - // If we have taken heavy damage, but the current schedule doesn't - // break on that, resort to just playing a gesture flinch. - PlayFlinchGesture(); - } - - // Otherwise, do nothing. The heavy damage will interrupt our schedule and we'll flinch. - } - else if ( HasCondition( COND_LIGHT_DAMAGE ) ) - { - // If we have taken light damage play gesture flinches - PlayFlinchGesture(); - } - - // If it's been a while since we did a full flinch, forget that we flinched so we'll flinch fully again - if ( HasMemory(bits_MEMORY_FLINCHED) && gpGlobals->curtime > m_flNextFlinchTime ) - { - Forget(bits_MEMORY_FLINCHED); - } -} - -//----------------------------------------------------------------------------- - -void CAI_BaseNPC::GatherConditions( void ) -{ - m_bConditionsGathered = true; - g_AIConditionsTimer.Start(); - - if ( m_NPCState != NPC_STATE_NONE && m_NPCState != NPC_STATE_DEAD ) - { - if ( FacingIdeal() ) - Forget( bits_MEMORY_TURNING ); - - bool bForcedGather = m_bForceConditionsGather; - m_bForceConditionsGather = false; - - if ( m_pfnThink != (BASEPTR)&CAI_BaseNPC::CallNPCThink ) - { - if ( UTIL_FindClientInPVS( edict() ) != NULL ) - SetCondition( COND_IN_PVS ); - else - ClearCondition( COND_IN_PVS ); - } - - // Sample the environment. Do this unconditionally if there is a player in this - // npc's PVS. NPCs in COMBAT state are allowed to simulate when there is no player in - // the same PVS. This is so that any fights in progress will continue even if the player leaves the PVS. - if ( !IsFlaggedEfficient() && - ( bForcedGather || - HasCondition( COND_IN_PVS ) || - ShouldAlwaysThink() || - m_NPCState == NPC_STATE_COMBAT ) ) - { - CheckOnGround(); - - if ( ShouldPlayIdleSound() ) - { - AI_PROFILE_SCOPE(CAI_BaseNPC_IdleSound); - IdleSound(); - } - - PerformSensing(); - - GetEnemies()->RefreshMemories(); - ChooseEnemy(); - - // Check to see if there is a better weapon available - if (Weapon_IsBetterAvailable()) - { - SetCondition(COND_BETTER_WEAPON_AVAILABLE); - } - - if ( GetCurSchedule() && - ( m_NPCState == NPC_STATE_IDLE || m_NPCState == NPC_STATE_ALERT) && - GetEnemy() && - !HasCondition( COND_NEW_ENEMY ) && - GetCurSchedule()->HasInterrupt( COND_NEW_ENEMY ) ) - { - // @Note (toml 05-05-04): There seems to be a case where an NPC can not respond - // to COND_NEW_ENEMY. Only evidence right now is save - // games after the fact, so for now, just patching it up - DevMsg( 2, "Had to force COND_NEW_ENEMY\n" ); - SetCondition(COND_NEW_ENEMY); - } - } - else - { - // if not done, can have problems if leave PVS in same frame heard/saw things, - // since only PerformSensing clears conditions - ClearSenseConditions(); - } - - // do these calculations if npc has an enemy. - if ( GetEnemy() != NULL ) - { - if ( !IsFlaggedEfficient() ) - { - GatherEnemyConditions( GetEnemy() ); - m_flLastEnemyTime = gpGlobals->curtime; - } - else - { - SetEnemy( NULL ); - } - } - - // do these calculations if npc has a target - if ( GetTarget() != NULL ) - { - CheckTarget( GetTarget() ); - } - - CheckAmmo(); - - CheckFlinches(); - - CheckSquad(); - } - else - ClearCondition( COND_IN_PVS ); - - RemoveIgnoredConditions(); - - g_AIConditionsTimer.End(); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CAI_BaseNPC::PrescheduleThink( void ) -{ -#ifdef HL2_EPISODIC - CheckForScriptedNPCInteractions(); -#endif - - // If we use weapons, and our desired weapon state is not the current, fix it - if( (CapabilitiesGet() & bits_CAP_USE_WEAPONS) && (m_iDesiredWeaponState == DESIREDWEAPONSTATE_HOLSTERED || m_iDesiredWeaponState == DESIREDWEAPONSTATE_UNHOLSTERED || m_iDesiredWeaponState == DESIREDWEAPONSTATE_HOLSTERED_DESTROYED ) ) - { - if ( IsAlive() && !IsInAScript() ) - { - if ( !IsCurSchedule( SCHED_MELEE_ATTACK1, false ) && !IsCurSchedule( SCHED_MELEE_ATTACK2, false ) && - !IsCurSchedule( SCHED_RANGE_ATTACK1, false ) && !IsCurSchedule( SCHED_RANGE_ATTACK2, false ) ) - { - if ( m_iDesiredWeaponState == DESIREDWEAPONSTATE_HOLSTERED || m_iDesiredWeaponState == DESIREDWEAPONSTATE_HOLSTERED_DESTROYED ) - { - HolsterWeapon(); - } - else if ( m_iDesiredWeaponState == DESIREDWEAPONSTATE_UNHOLSTERED ) - { - UnholsterWeapon(); - } - } - } - else - { - // Throw away the request - m_iDesiredWeaponState = DESIREDWEAPONSTATE_IGNORE; - } - } -} - -//----------------------------------------------------------------------------- -// Main entry point for processing AI -//----------------------------------------------------------------------------- - -void CAI_BaseNPC::RunAI( void ) -{ - AI_PROFILE_SCOPE(CAI_BaseNPC_RunAI); - g_AIRunTimer.Start(); - - if( ai_debug_squads.GetBool() ) - { - if( IsInSquad() && GetSquad() && !CAI_Squad::IsSilentMember(this ) && ( GetSquad()->IsLeader( this ) || GetSquad()->NumMembers() == 1 ) ) - { - AISquadIter_t iter; - CAI_Squad *pSquad = GetSquad(); - - Vector right; - Vector vecPoint; - - vecPoint = EyePosition() + Vector( 0, 0, 12 ); - GetVectors( NULL, &right, NULL ); - NDebugOverlay::Line( vecPoint, vecPoint + Vector( 0, 0, 64 ), 0, 255, 0, false , 0.1 ); - NDebugOverlay::Line( vecPoint, vecPoint + Vector( 0, 0, 32 ) + right * 32, 0, 255, 0, false , 0.1 ); - NDebugOverlay::Line( vecPoint, vecPoint + Vector( 0, 0, 32 ) - right * 32, 0, 255, 0, false , 0.1 ); - - for ( CAI_BaseNPC *pSquadMember = pSquad->GetFirstMember( &iter, false ); pSquadMember; pSquadMember = pSquad->GetNextMember( &iter, false ) ) - { - if ( pSquadMember != this ) - NDebugOverlay::Line( EyePosition(), pSquadMember->EyePosition(), 0, - CAI_Squad::IsSilentMember(pSquadMember) ? 127 : 255, 0, false , 0.1 ); - } - } - } - - if( ai_debug_loners.GetBool() && !IsInSquad() && AI_IsSinglePlayer() ) - { - Vector right; - Vector vecPoint; - - vecPoint = EyePosition() + Vector( 0, 0, 12 ); - - UTIL_GetLocalPlayer()->GetVectors( NULL, &right, NULL ); - - NDebugOverlay::Line( vecPoint, vecPoint + Vector( 0, 0, 64 ), 255, 0, 0, false , 0.1 ); - NDebugOverlay::Line( vecPoint, vecPoint + Vector( 0, 0, 32 ) + right * 32, 255, 0, 0, false , 0.1 ); - NDebugOverlay::Line( vecPoint, vecPoint + Vector( 0, 0, 32 ) - right * 32, 255, 0, 0, false , 0.1 ); - } - -#ifdef _DEBUG - m_bSelected = ( (m_debugOverlays & OVERLAY_NPC_SELECTED_BIT) != 0 ); -#endif - - m_bConditionsGathered = false; - m_bSkippedChooseEnemy = false; - - if ( g_pDeveloper->GetInt() && !GetNavigator()->IsOnNetwork() ) - { - AddTimedOverlay( "NPC w/no reachable nodes!", 5 ); - } - - AI_PROFILE_SCOPE_BEGIN(CAI_BaseNPC_RunAI_GatherConditions); - GatherConditions(); - AI_PROFILE_SCOPE_END(); - - if ( !m_bConditionsGathered ) - m_bConditionsGathered = true; // derived class didn't call to base - - TryRestoreHull(); - - g_AIPrescheduleThinkTimer.Start(); - - AI_PROFILE_SCOPE_BEGIN(CAI_RunAI_PrescheduleThink); - PrescheduleThink(); - AI_PROFILE_SCOPE_END(); - - g_AIPrescheduleThinkTimer.End(); - - MaintainSchedule(); - - PostscheduleThink(); - - ClearTransientConditions(); - - g_AIRunTimer.End(); -} - -//----------------------------------------------------------------------------- -void CAI_BaseNPC::ClearTransientConditions() -{ - // if the npc didn't use these conditions during the above call to MaintainSchedule() - // we throw them out cause we don't want them sitting around through the lifespan of a schedule - // that doesn't use them. - ClearCondition( COND_LIGHT_DAMAGE ); - ClearCondition( COND_HEAVY_DAMAGE ); - ClearCondition( COND_PHYSICS_DAMAGE ); - ClearCondition( COND_PLAYER_PUSHING ); -} - - -//----------------------------------------------------------------------------- -// Selecting the idle ideal state -//----------------------------------------------------------------------------- -NPC_STATE CAI_BaseNPC::SelectIdleIdealState() -{ - // IDLE goes to ALERT upon hearing a sound - // IDLE goes to ALERT upon being injured - // IDLE goes to ALERT upon seeing food - // IDLE goes to COMBAT upon sighting an enemy - if ( HasCondition(COND_NEW_ENEMY) || - HasCondition(COND_SEE_ENEMY) ) - { - // new enemy! This means an idle npc has seen someone it dislikes, or - // that a npc in combat has found a more suitable target to attack - return NPC_STATE_COMBAT; - } - - // Set our ideal yaw if we've taken damage - if ( HasCondition(COND_LIGHT_DAMAGE) || - HasCondition(COND_HEAVY_DAMAGE) || - (!GetEnemy() && gpGlobals->curtime - GetEnemies()->LastTimeSeen( AI_UNKNOWN_ENEMY ) < TIME_CARE_ABOUT_DAMAGE ) ) - { - Vector vecEnemyLKP; - - // Fill in where we're trying to look - if ( GetEnemy() ) - { - vecEnemyLKP = GetEnemyLKP(); - } - else - { - if ( GetEnemies()->Find( AI_UNKNOWN_ENEMY ) ) - { - vecEnemyLKP = GetEnemies()->LastKnownPosition( AI_UNKNOWN_ENEMY ); - } - else - { - // Don't have an enemy, so face the direction the last attack came from (don't face north) - vecEnemyLKP = WorldSpaceCenter() + ( g_vecAttackDir * 128 ); - } - } - - // Set the ideal - GetMotor()->SetIdealYawToTarget( vecEnemyLKP ); - - return NPC_STATE_ALERT; - } - - if ( HasCondition(COND_HEAR_DANGER) || - HasCondition(COND_HEAR_COMBAT) || - HasCondition(COND_HEAR_WORLD) || - HasCondition(COND_HEAR_PLAYER) || - HasCondition(COND_HEAR_THUMPER) || - HasCondition(COND_HEAR_BULLET_IMPACT) ) - { - // Interrupted by a sound. So make our ideal yaw the - // source of the sound! - CSound *pSound; - - pSound = GetBestSound(); - Assert( pSound != NULL ); - if ( pSound ) - { - // BRJ 1/7/04: This code used to set the ideal yaw. - // It's really side-effecty to set the yaw here. - // That is now done by the FACE_BESTSOUND schedule. - // Revert this change if it causes problems. - GetMotor()->SetIdealYawToTarget( pSound->GetSoundReactOrigin() ); - if ( pSound->IsSoundType( SOUND_COMBAT | SOUND_DANGER | SOUND_BULLET_IMPACT ) ) - { - return NPC_STATE_ALERT; - } - } - } - - if ( HasInterruptCondition(COND_SMELL) ) - { - return NPC_STATE_ALERT; - } - - return NPC_STATE_INVALID; -} - - -//----------------------------------------------------------------------------- -// Selecting the alert ideal state -//----------------------------------------------------------------------------- -NPC_STATE CAI_BaseNPC::SelectAlertIdealState() -{ - // ALERT goes to IDLE upon becoming bored - // ALERT goes to COMBAT upon sighting an enemy - if ( HasCondition(COND_NEW_ENEMY) || - HasCondition(COND_SEE_ENEMY) || - GetEnemy() != NULL ) - { - return NPC_STATE_COMBAT; - } - - // Set our ideal yaw if we've taken damage - if ( HasCondition(COND_LIGHT_DAMAGE) || - HasCondition(COND_HEAVY_DAMAGE) || - (!GetEnemy() && gpGlobals->curtime - GetEnemies()->LastTimeSeen( AI_UNKNOWN_ENEMY ) < TIME_CARE_ABOUT_DAMAGE ) ) - { - Vector vecEnemyLKP; - - // Fill in where we're trying to look - if ( GetEnemy() ) - { - vecEnemyLKP = GetEnemyLKP(); - } - else - { - if ( GetEnemies()->Find( AI_UNKNOWN_ENEMY ) ) - { - vecEnemyLKP = GetEnemies()->LastKnownPosition( AI_UNKNOWN_ENEMY ); - } - else - { - // Don't have an enemy, so face the direction the last attack came from (don't face north) - vecEnemyLKP = WorldSpaceCenter() + ( g_vecAttackDir * 128 ); - } - } - - // Set the ideal - GetMotor()->SetIdealYawToTarget( vecEnemyLKP ); - - return NPC_STATE_ALERT; - } - - if ( HasCondition(COND_HEAR_DANGER) || - HasCondition(COND_HEAR_COMBAT) ) - { - CSound *pSound = GetBestSound(); - AssertOnce( pSound != NULL ); - - if ( pSound ) - { - GetMotor()->SetIdealYawToTarget( pSound->GetSoundReactOrigin() ); - } - - return NPC_STATE_ALERT; - } - - if ( ShouldGoToIdleState() ) - { - return NPC_STATE_IDLE; - } - - return NPC_STATE_INVALID; -} - - -//----------------------------------------------------------------------------- -// Selecting the alert ideal state -//----------------------------------------------------------------------------- -NPC_STATE CAI_BaseNPC::SelectScriptIdealState() -{ - if ( HasCondition(COND_TASK_FAILED) || - HasCondition(COND_LIGHT_DAMAGE) || - HasCondition(COND_HEAVY_DAMAGE) ) - { - ExitScriptedSequence(); // This will set the ideal state - } - - if ( m_IdealNPCState == NPC_STATE_IDLE ) - { - // Exiting a script. Select the ideal state assuming we were idle now. - m_NPCState = NPC_STATE_IDLE; - NPC_STATE eIdealState = SelectIdealState(); - m_NPCState = NPC_STATE_SCRIPT; - return eIdealState; - } - - return NPC_STATE_INVALID; -} - - -//----------------------------------------------------------------------------- -// Purpose: Surveys the Conditions information available and finds the best -// new state for a npc. -// -// NOTICE the CAI_BaseNPC implementation of this function does not care about -// private conditions! -// -// Output : NPC_STATE - the suggested ideal state based on current conditions. -//----------------------------------------------------------------------------- -NPC_STATE CAI_BaseNPC::SelectIdealState( void ) -{ - // dvs: FIXME: lots of side effecty code in here!! this function should ONLY return an ideal state! - - // --------------------------- - // Do some squad stuff first - // --------------------------- - if (m_pSquad) - { - switch( m_NPCState ) - { - case NPC_STATE_IDLE: - case NPC_STATE_ALERT: - if ( HasCondition ( COND_NEW_ENEMY ) ) - { - m_pSquad->SquadNewEnemy( GetEnemy() ); - } - break; - } - } - - // --------------------------- - // Set ideal state - // --------------------------- - switch ( m_NPCState ) - { - case NPC_STATE_IDLE: - { - NPC_STATE nState = SelectIdleIdealState(); - if ( nState != NPC_STATE_INVALID ) - return nState; - } - break; - - case NPC_STATE_ALERT: - { - NPC_STATE nState = SelectAlertIdealState(); - if ( nState != NPC_STATE_INVALID ) - return nState; - } - break; - - case NPC_STATE_COMBAT: - // COMBAT goes to ALERT upon death of enemy - { - if ( GetEnemy() == NULL ) - { - DevWarning( 2, "***Combat state with no enemy!\n" ); - return NPC_STATE_ALERT; - } - break; - } - case NPC_STATE_SCRIPT: - { - NPC_STATE nState = SelectScriptIdealState(); - if ( nState != NPC_STATE_INVALID ) - return nState; - } - break; - - case NPC_STATE_DEAD: - return NPC_STATE_DEAD; - } - - // The best ideal state is the current ideal state. - return m_IdealNPCState; -} - -//------------------------------------------------------------------------------ -//------------------------------------------------------------------------------ -void CAI_BaseNPC::GiveWeapon( string_t iszWeaponName ) -{ - CBaseCombatWeapon *pWeapon = Weapon_Create( STRING(iszWeaponName) ); - if ( !pWeapon ) - { - Warning( "Couldn't create weapon %s to give NPC %s.\n", STRING(iszWeaponName), STRING(GetEntityName()) ); - return; - } - - // If I have a weapon already, drop it - if ( GetActiveWeapon() ) - { - Weapon_Drop( GetActiveWeapon() ); - } - - // If I have a name, make my weapon match it with "_weapon" appended - if ( GetEntityName() != NULL_STRING ) - { - pWeapon->SetName( AllocPooledString(UTIL_VarArgs("%s_weapon", GetEntityName())) ); - } - - Weapon_Equip( pWeapon ); - - // Handle this case - OnGivenWeapon( pWeapon ); -} - -//----------------------------------------------------------------------------- -// Rather specific function that tells us if an NPC is in the process of -// moving to a weapon with the intent to pick it up. -//----------------------------------------------------------------------------- -bool CAI_BaseNPC::IsMovingToPickupWeapon() -{ - return IsCurSchedule( SCHED_NEW_WEAPON ); -} - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -bool CAI_BaseNPC::ShouldLookForBetterWeapon() -{ - if( m_flNextWeaponSearchTime > gpGlobals->curtime ) - return false; - - if( !(CapabilitiesGet() & bits_CAP_USE_WEAPONS) ) - return false; - - // Already armed and currently fighting. Don't try to upgrade. - if( GetActiveWeapon() && m_NPCState == NPC_STATE_COMBAT ) - return false; - - if( IsMovingToPickupWeapon() ) - return false; - - if( !IsPlayerAlly() && GetActiveWeapon() ) - return false; - - if( IsInAScript() ) - return false; - - return true; -} - -//----------------------------------------------------------------------------- -// Purpose: Check if a better weapon is available. -// For now check if there is a weapon and I don't have one. In -// the future -// UNDONE: actually rate the weapons based on there strength -// Input : -// Output : -//----------------------------------------------------------------------------- -bool CAI_BaseNPC::Weapon_IsBetterAvailable() -{ - if( m_iszPendingWeapon != NULL_STRING ) - { - // Some weapon is reserved for us. - return true; - } - - if( ShouldLookForBetterWeapon() ) - { - if( GetActiveWeapon() ) - { - m_flNextWeaponSearchTime = gpGlobals->curtime + 2; - } - else - { - if( IsInPlayerSquad() ) - { - // Look for a weapon frequently. - m_flNextWeaponSearchTime = gpGlobals->curtime + 1; - } - else - { - m_flNextWeaponSearchTime = gpGlobals->curtime + 2; - } - } - - if ( Weapon_FindUsable( WEAPON_SEARCH_DELTA ) ) - { - return true; - } - } - - return false; -} - -//----------------------------------------------------------------------------- -// Purpose: Returns true is weapon has a line of sight. If bSetConditions is -// true, also sets LOC conditions -// Input : -// Output : -//----------------------------------------------------------------------------- -bool CAI_BaseNPC::WeaponLOSCondition(const Vector &ownerPos, const Vector &targetPos, bool bSetConditions ) -{ -#if 0 - // @TODO (toml 03-07-04): this code might be better (not tested) - Vector vecLocalRelativePosition; - VectorITransform( npcOwner->Weapon_ShootPosition(), npcOwner->EntityToWorldTransform(), vecLocalRelativePosition ); - - // Compute desired test transform - - // Compute desired x axis - Vector xaxis; - VectorSubtract( targetPos, ownerPos, xaxis ); - - // FIXME: Insert angle test here? - float flAngle = acos( xaxis.z / xaxis.Length() ); - - xaxis.z = 0.0f; - float flLength = VectorNormalize( xaxis ); - if ( flLength < 1e-3 ) - return false; - - Vector yaxis( -xaxis.y, xaxis.x, 0.0f ); - - matrix3x4_t losTestToWorld; - MatrixInitialize( losTestToWorld, ownerPos, xaxis, yaxis, zaxis ); - - Vector barrelPos; - VectorTransform( vecLocalRelativePosition, losTestToWorld, barrelPos ); - -#endif - - bool bHaveLOS; - - if (GetActiveWeapon()) - { - bHaveLOS = GetActiveWeapon()->WeaponLOSCondition(ownerPos, targetPos, bSetConditions); - } - else if (CapabilitiesGet() & bits_CAP_INNATE_RANGE_ATTACK1) - { - bHaveLOS = InnateWeaponLOSCondition(ownerPos, targetPos, bSetConditions); - } - else - { - if (bSetConditions) - { - SetCondition( COND_NO_WEAPON ); - } - bHaveLOS = false; - } - // ------------------------------------------- - // Check for friendly fire with the player - // ------------------------------------------- - if ( CapabilitiesGet() & ( bits_CAP_NO_HIT_PLAYER | bits_CAP_NO_HIT_SQUADMATES ) ) - { - float spread = 0.92; - if ( GetActiveWeapon() ) - { - Vector vSpread = GetAttackSpread( GetActiveWeapon() ); - if ( vSpread.x > VECTOR_CONE_15DEGREES.x ) - spread = TableCos(asin(vSpread.x)); - else // too much error because using point not box - spread = 0.99145; // "15 degrees" - } - if (CapabilitiesGet() & bits_CAP_NO_HIT_PLAYER) - { - // Check shoot direction relative to player - if (PlayerInSpread( ownerPos, targetPos, spread, 8*12 )) - { - if (bSetConditions) - { - SetCondition( COND_WEAPON_PLAYER_IN_SPREAD ); - } - bHaveLOS = false; - } - /* For grenades etc. check that player is clear? - // Check player position also - if (PlayerInRange( targetPos, 100 )) - { - SetCondition( COND_WEAPON_PLAYER_NEAR_TARGET ); - } - */ - } - - if ( bHaveLOS ) - { - if ( (CapabilitiesGet() & bits_CAP_NO_HIT_SQUADMATES) && m_pSquad && GetEnemy() ) - { - if ( IsSquadmateInSpread( ownerPos, targetPos, spread, 8*12 ) ) - bHaveLOS = false; - } - } - } - return bHaveLOS; -} - -//----------------------------------------------------------------------------- -// Purpose: Check the innate weapon LOS for an owner at an arbitrary position -// If bSetConditions is true, LOS related conditions will also be set -// Input : -// Output : -//----------------------------------------------------------------------------- -bool CAI_BaseNPC::InnateWeaponLOSCondition( const Vector &ownerPos, const Vector &targetPos, bool bSetConditions ) -{ - // -------------------- - // Check for occlusion - // -------------------- - // Base class version assumes innate weapon position is at eye level - Vector barrelPos = ownerPos + GetViewOffset(); - trace_t tr; - AI_TraceLine( barrelPos, targetPos, MASK_SHOT, this, COLLISION_GROUP_NONE, &tr); - - if ( tr.fraction == 1.0 ) - { - return true; - } - - CBaseEntity *pHitEntity = tr.m_pEnt; - - // Translate a hit vehicle into its passenger if found - if ( GetEnemy() != NULL ) - { - CBaseCombatCharacter *pCCEnemy = GetEnemy()->MyCombatCharacterPointer(); - if ( pCCEnemy != NULL && pCCEnemy->IsInAVehicle() ) - { - // Ok, player in vehicle, check if vehicle is target we're looking at, fire if it is - // Also, check to see if the owner of the entity is the vehicle, in which case it's valid too. - // This catches vehicles that use bone followers. - CBaseEntity *pVehicleEnt = pCCEnemy->GetVehicleEntity(); - if ( pHitEntity == pVehicleEnt || pHitEntity->GetOwnerEntity() == pVehicleEnt ) - return true; - } - } - - if ( pHitEntity == GetEnemy() ) - { - return true; - } - else if ( pHitEntity && pHitEntity->MyCombatCharacterPointer() ) - { - if (IRelationType( pHitEntity ) == D_HT) - { - return true; - } - else if (bSetConditions) - { - SetCondition(COND_WEAPON_BLOCKED_BY_FRIEND); - } - } - else if (bSetConditions) - { - SetCondition(COND_WEAPON_SIGHT_OCCLUDED); - SetEnemyOccluder(tr.m_pEnt); - } - - return false; -} - -//========================================================= -// CanCheckAttacks - prequalifies a npc to do more fine -// checking of potential attacks. -//========================================================= -bool CAI_BaseNPC::FCanCheckAttacks( void ) -{ - // Not allowed to check attacks while climbing or jumping - // Otherwise schedule is interrupted while on ladder/etc - // which is NOT a legal place to attack from - if ( GetNavType() == NAV_CLIMB || GetNavType() == NAV_JUMP ) - return false; - - if ( HasCondition(COND_SEE_ENEMY) && !HasCondition( COND_ENEMY_TOO_FAR)) - { - return true; - } - - return false; -} - -//----------------------------------------------------------------------------- -// Purpose: Return dist. to enemy (closest of origin/head/feet) -// Input : -// Output : -//----------------------------------------------------------------------------- -float CAI_BaseNPC::EnemyDistance( CBaseEntity *pEnemy ) -{ - Vector enemyDelta = pEnemy->WorldSpaceCenter() - WorldSpaceCenter(); - - // NOTE: We ignore rotation for computing height. Assume it isn't an effect - // we care about, so we simply use OBBSize().z for height. - // Otherwise you'd do this: - // pEnemy->CollisionProp()->WorldSpaceSurroundingBounds( &enemyMins, &enemyMaxs ); - // float enemyHeight = enemyMaxs.z - enemyMins.z; - - float enemyHeight = pEnemy->CollisionProp()->OBBSize().z; - float myHeight = CollisionProp()->OBBSize().z; - - // max distance our centers can be apart with the boxes still overlapping - float flMaxZDist = ( enemyHeight + myHeight ) * 0.5f; - - // see if the enemy is closer to my head, feet or in between - if ( enemyDelta.z > flMaxZDist ) - { - // enemy feet above my head, compute distance from my head to his feet - enemyDelta.z -= flMaxZDist; - } - else if ( enemyDelta.z < -flMaxZDist ) - { - // enemy head below my feet, return distance between my feet and his head - enemyDelta.z += flMaxZDist; - } - else - { - // boxes overlap in Z, no delta - enemyDelta.z = 0; - } - - return enemyDelta.Length(); -} - -//----------------------------------------------------------------------------- - -float CAI_BaseNPC::GetReactionDelay( CBaseEntity *pEnemy ) -{ - return ( m_NPCState == NPC_STATE_ALERT || m_NPCState == NPC_STATE_COMBAT ) ? - ai_reaction_delay_alert.GetFloat() : - ai_reaction_delay_idle.GetFloat(); -} - -//----------------------------------------------------------------------------- -// Purpose: Update information on my enemy -// Input : -// Output : Returns true is new enemy, false is known enemy -//----------------------------------------------------------------------------- -bool CAI_BaseNPC::UpdateEnemyMemory( CBaseEntity *pEnemy, const Vector &position, CBaseEntity *pInformer ) -{ - bool firstHand = ( pInformer == NULL || pInformer == this ); - - AI_PROFILE_SCOPE(CAI_BaseNPC_UpdateEnemyMemory); - - if ( GetEnemies() ) - { - // If the was eluding me and allow the NPC to play a sound - if (GetEnemies()->HasEludedMe(pEnemy)) - { - FoundEnemySound(); - } - float reactionDelay = ( !pInformer || pInformer == this ) ? GetReactionDelay( pEnemy ) : 0.0; - bool result = GetEnemies()->UpdateMemory(GetNavigator()->GetNetwork(), pEnemy, position, reactionDelay, firstHand); - - if ( !firstHand && pEnemy && result && GetState() == NPC_STATE_IDLE ) // if it's a new potential enemy - ForceDecisionThink(); - - if ( firstHand && pEnemy && m_pSquad ) - { - m_pSquad->UpdateEnemyMemory( this, pEnemy, position ); - } - return result; - } - return true; -} - - -//----------------------------------------------------------------------------- -// Purpose: Remembers the thing my enemy is hiding behind. Called when either -// COND_ENEMY_OCCLUDED or COND_WEAPON_SIGHT_OCCLUDED is set. -//----------------------------------------------------------------------------- -void CAI_BaseNPC::SetEnemyOccluder(CBaseEntity *pBlocker) -{ - m_hEnemyOccluder = pBlocker; -} - - -//----------------------------------------------------------------------------- -// Purpose: Gets the thing my enemy is hiding behind (assuming they are hiding). -//----------------------------------------------------------------------------- -CBaseEntity *CAI_BaseNPC::GetEnemyOccluder(void) -{ - return m_hEnemyOccluder.Get(); -} - - -//----------------------------------------------------------------------------- -// Purpose: part of the Condition collection process -// gets and stores data and conditions pertaining to a npc's -// enemy. -// @TODO (toml 07-27-03): this should become subservient to the senses. right -// now, it yields different result -// Input : -// Output : -//----------------------------------------------------------------------------- -void CAI_BaseNPC::GatherEnemyConditions( CBaseEntity *pEnemy ) -{ - AI_PROFILE_SCOPE(CAI_BaseNPC_GatherEnemyConditions); - - ClearCondition( COND_ENEMY_FACING_ME ); - ClearCondition( COND_BEHIND_ENEMY ); - - // --------------------------- - // Set visibility conditions - // --------------------------- - if ( HasCondition( COND_NEW_ENEMY ) || GetSenses()->GetTimeLastUpdate( GetEnemy() ) == gpGlobals->curtime ) - { - AI_PROFILE_SCOPE_BEGIN(CAI_BaseNPC_GatherEnemyConditions_Visibility); - - ClearCondition( COND_HAVE_ENEMY_LOS ); - ClearCondition( COND_ENEMY_OCCLUDED ); - - CBaseEntity *pBlocker = NULL; - SetEnemyOccluder(NULL); - - bool bSensesDidSee = GetSenses()->DidSeeEntity( pEnemy ); - - if ( !bSensesDidSee && ( ( EnemyDistance( pEnemy ) >= GetSenses()->GetDistLook() ) || !FVisible( pEnemy, MASK_OPAQUE, &pBlocker ) ) ) - { - // No LOS to enemy - SetEnemyOccluder(pBlocker); - SetCondition( COND_ENEMY_OCCLUDED ); - ClearCondition( COND_SEE_ENEMY ); - - if (HasMemory( bits_MEMORY_HAD_LOS )) - { - AI_PROFILE_SCOPE(CAI_BaseNPC_GatherEnemyConditions_Outputs); - // Send output event - if (GetEnemy()->IsPlayer()) - { - m_OnLostPlayerLOS.FireOutput( GetEnemy(), this ); - } - m_OnLostEnemyLOS.FireOutput( GetEnemy(), this ); - } - Forget( bits_MEMORY_HAD_LOS ); - } - else - { - // Have LOS but may not be in view cone - SetCondition( COND_HAVE_ENEMY_LOS ); - - if ( bSensesDidSee ) - { - // Have LOS and in view cone - SetCondition( COND_SEE_ENEMY ); - } - else - { - ClearCondition( COND_SEE_ENEMY ); - } - - if (!HasMemory( bits_MEMORY_HAD_LOS )) - { - AI_PROFILE_SCOPE(CAI_BaseNPC_GatherEnemyConditions_Outputs); - // Send output event - EHANDLE hEnemy; - hEnemy.Set( GetEnemy() ); - - if (GetEnemy()->IsPlayer()) - { - m_OnFoundPlayer.Set(hEnemy, this, this); - m_OnFoundEnemy.Set(hEnemy, this, this); - } - else - { - m_OnFoundEnemy.Set(hEnemy, this, this); - } - } - Remember( bits_MEMORY_HAD_LOS ); - } - - AI_PROFILE_SCOPE_END(); - } - - // ------------------- - // If enemy is dead - // ------------------- - if ( !pEnemy->IsAlive() ) - { - SetCondition( COND_ENEMY_DEAD ); - ClearCondition( COND_SEE_ENEMY ); - ClearCondition( COND_ENEMY_OCCLUDED ); - return; - } - - float flDistToEnemy = EnemyDistance(pEnemy); - - AI_PROFILE_SCOPE_BEGIN(CAI_BaseNPC_GatherEnemyConditions_SeeEnemy); - - if ( HasCondition( COND_SEE_ENEMY ) ) - { - // Trail the enemy a bit if he's moving - if (pEnemy->GetSmoothedVelocity() != vec3_origin) - { - Vector vTrailPos = pEnemy->GetAbsOrigin() - pEnemy->GetSmoothedVelocity() * random->RandomFloat( -0.05, 0 ); - UpdateEnemyMemory(pEnemy,vTrailPos); - } - else - { - UpdateEnemyMemory(pEnemy,pEnemy->GetAbsOrigin()); - } - - // If it's not an NPC, assume it can't see me - if ( pEnemy->MyCombatCharacterPointer() && pEnemy->MyCombatCharacterPointer()->FInViewCone ( this ) ) - { - SetCondition ( COND_ENEMY_FACING_ME ); - ClearCondition ( COND_BEHIND_ENEMY ); - } - else - { - ClearCondition( COND_ENEMY_FACING_ME ); - SetCondition ( COND_BEHIND_ENEMY ); - } - } - else if ( (!HasCondition(COND_ENEMY_OCCLUDED) && !HasCondition(COND_SEE_ENEMY)) && ( flDistToEnemy <= 256 ) ) - { - // if the enemy is not occluded, and unseen, that means it is behind or beside the npc. - // if the enemy is near enough the npc, we go ahead and let the npc know where the - // enemy is. Send the enemy in as the informer so this knowledge will be regarded as - // secondhand so that the NPC doesn't - UpdateEnemyMemory( pEnemy, pEnemy->GetAbsOrigin(), pEnemy ); - } - - AI_PROFILE_SCOPE_END(); - - float tooFar = m_flDistTooFar; - if ( GetActiveWeapon() && HasCondition(COND_SEE_ENEMY) ) - { - tooFar = max( m_flDistTooFar, GetActiveWeapon()->m_fMaxRange1 ); - } - - if ( flDistToEnemy >= tooFar ) - { - // enemy is very far away from npc - SetCondition( COND_ENEMY_TOO_FAR ); - } - else - { - ClearCondition( COND_ENEMY_TOO_FAR ); - } - - if ( FCanCheckAttacks() ) - { - // This may also call SetEnemyOccluder! - GatherAttackConditions( GetEnemy(), flDistToEnemy ); - } - else - { - ClearAttackConditions(); - } - - // If my enemy has moved significantly, or if the enemy has changed update my path - UpdateEnemyPos(); - - // If my target entity has moved significantly, update my path - // This is an odd place to put this, but where else should it go? - UpdateTargetPos(); - - // ---------------------------------------------------------------------------- - // Check if enemy is reachable via the node graph unless I'm not on a network - // ---------------------------------------------------------------------------- - if (GetNavigator()->IsOnNetwork()) - { - // Note that unreachablity times out - if (IsUnreachable(GetEnemy())) - { - SetCondition(COND_ENEMY_UNREACHABLE); - } - } - - //----------------------------------------------------------------------- - // If I haven't seen the enemy in a while he may have eluded me - //----------------------------------------------------------------------- - if (gpGlobals->curtime - GetEnemyLastTimeSeen() > 8) - { - //----------------------------------------------------------------------- - // I'm at last known position at enemy isn't in sight then has eluded me - // ---------------------------------------------------------------------- - Vector flEnemyLKP = GetEnemyLKP(); - if (((flEnemyLKP - GetAbsOrigin()).Length2D() < 48) && - !HasCondition(COND_SEE_ENEMY)) - { - MarkEnemyAsEluded(); - } - //------------------------------------------------------------------- - // If enemy isn't reachable, I can see last known position and enemy - // isn't there, then he has eluded me - // ------------------------------------------------------------------ - if (!HasCondition(COND_SEE_ENEMY) && HasCondition(COND_ENEMY_UNREACHABLE)) - { - if ( !FVisible( flEnemyLKP ) ) - { - MarkEnemyAsEluded(); - } - } - } -} - - -//----------------------------------------------------------------------------- -// In the case of goaltype enemy, update the goal position -//----------------------------------------------------------------------------- -float CAI_BaseNPC::GetGoalRepathTolerance( CBaseEntity *pGoalEnt, GoalType_t type, const Vector &curGoal, const Vector &curTargetPos ) -{ - float distToGoal = ( GetAbsOrigin() - curTargetPos ).Length() - GetNavigator()->GetArrivalDistance(); - float distMoved1Sec = GetSmoothedVelocity().Length(); - float result = 120; // FIXME: why 120? - - if (distMoved1Sec > 0.0) - { - float t = distToGoal / distMoved1Sec; - - result = clamp( 120 * t, 0, 120 ); - // Msg("t %.2f : d %.0f (%.0f)\n", t, result, distMoved1Sec ); - } - - if ( !pGoalEnt->IsPlayer() ) - result *= 1.20; - - return result; -} - -//----------------------------------------------------------------------------- -// In the case of goaltype enemy, update the goal position -//----------------------------------------------------------------------------- -void CAI_BaseNPC::UpdateEnemyPos() -{ - // Don't perform path recomputations during a climb or a jump - if ( !GetNavigator()->IsInterruptable() ) - return; - - if ( m_AnyUpdateEnemyPosTimer.Expired() && m_UpdateEnemyPosTimer.Expired() ) - { - // FIXME: does GetGoalRepathTolerance() limit re-routing enough to remove this? - // m_UpdateEnemyPosTimer.Set( 0.5, 1.0 ); - - // If my enemy has moved significantly, or if the enemy has changed update my path - if ( GetNavigator()->GetGoalType() == GOALTYPE_ENEMY ) - { - if (m_hEnemy != GetNavigator()->GetGoalTarget()) - { - GetNavigator()->SetGoalTarget( m_hEnemy, vec3_origin ); - } - else - { - Vector vEnemyLKP = GetEnemyLKP(); - TranslateNavGoal( GetEnemy(), vEnemyLKP ); - float tolerance = GetGoalRepathTolerance( GetEnemy(), GOALTYPE_ENEMY, GetNavigator()->GetGoalPos(), vEnemyLKP); - if ( (GetNavigator()->GetGoalPos() - vEnemyLKP).Length() > tolerance ) - { - // FIXME: when fleeing crowds, won't this severely limit the effectiveness of each individual? Shouldn't this be a mutex that's held for some period so that at least one attacker is effective? - m_AnyUpdateEnemyPosTimer.Set( 0.1 ); // FIXME: what's a reasonable interval? - if ( !GetNavigator()->RefindPathToGoal( false ) ) - { - TaskFail( FAIL_NO_ROUTE ); - } - } - } - } - } -} - - -//----------------------------------------------------------------------------- -// In the case of goaltype targetent, update the goal position -//----------------------------------------------------------------------------- -void CAI_BaseNPC::UpdateTargetPos() -{ - // BRJ 10/7/02 - // FIXME: make this check time based instead of distance based! - - // Don't perform path recomputations during a climb or a jump - if ( !GetNavigator()->IsInterruptable() ) - return; - - // If my target entity has moved significantly, or has changed, update my path - // This is an odd place to put this, but where else should it go? - if ( GetNavigator()->GetGoalType() == GOALTYPE_TARGETENT ) - { - if (m_hTargetEnt != GetNavigator()->GetGoalTarget()) - { - GetNavigator()->SetGoalTarget( m_hTargetEnt, vec3_origin ); - } - else if ( GetNavigator()->GetGoalFlags() & AIN_UPDATE_TARGET_POS ) - { - if ( GetTarget() == NULL || (GetNavigator()->GetGoalPos() - GetTarget()->GetAbsOrigin()).Length() > GetGoalRepathTolerance( GetTarget(), GOALTYPE_TARGETENT, GetNavigator()->GetGoalPos(), GetTarget()->GetAbsOrigin()) ) - { - if ( !GetNavigator()->RefindPathToGoal( false ) ) - { - TaskFail( FAIL_NO_ROUTE ); - } - } - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: part of the Condition collection process -// gets and stores data and conditions pertaining to a npc's -// enemy. -// Input : -// Output : -//----------------------------------------------------------------------------- -void CAI_BaseNPC::CheckTarget( CBaseEntity *pTarget ) -{ - AI_PROFILE_SCOPE(CAI_Enemies_CheckTarget); - - ClearCondition ( COND_HAVE_TARGET_LOS ); - ClearCondition ( COND_TARGET_OCCLUDED ); - - // --------------------------- - // Set visibility conditions - // --------------------------- - if ( ( EnemyDistance( pTarget ) >= GetSenses()->GetDistLook() ) || !FVisible( pTarget ) ) - { - // No LOS to target - SetCondition( COND_TARGET_OCCLUDED ); - } - else - { - // Have LOS (may not be in view cone) - SetCondition( COND_HAVE_TARGET_LOS ); - } - - UpdateTargetPos(); -} - -//----------------------------------------------------------------------------- -// Purpose: Creates a bullseye of limited lifespan at the provided position -// Input : vecOrigin - Where to create the bullseye -// duration - The lifespan of the bullseye -// Output : A BaseNPC pointer to the bullseye -// -// NOTES : It is the caller's responsibility to set up relationships with -// this bullseye! -//----------------------------------------------------------------------------- -CAI_BaseNPC *CAI_BaseNPC::CreateCustomTarget( const Vector &vecOrigin, float duration ) -{ -#ifdef HL2_DLL - CNPC_Bullseye *pTarget = (CNPC_Bullseye*)CreateEntityByName( "npc_bullseye" ); - - ASSERT( pTarget != NULL ); - - // Build a nonsolid bullseye and place it in the desired location - // The bullseye must take damage or the SetHealth 0 call will not be able - pTarget->AddSpawnFlags( SF_BULLSEYE_NONSOLID ); - pTarget->SetAbsOrigin( vecOrigin ); - pTarget->Spawn(); - - // Set it up to remove itself - variant_t value; - value.SetFloat(0); - g_EventQueue.AddEvent( pTarget, "SetHealth", value, 2.0, this, this ); - - return pTarget; -#else - return NULL; -#endif// HL2_DLL -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : eNewActivity - -// Output : Activity -//----------------------------------------------------------------------------- -Activity CAI_BaseNPC::NPC_TranslateActivity( Activity eNewActivity ) -{ - Assert( eNewActivity != ACT_INVALID ); - - if (eNewActivity == ACT_RANGE_ATTACK1) - { - if ( IsCrouching() ) - { - eNewActivity = ACT_RANGE_ATTACK1_LOW; - } - } - else if (eNewActivity == ACT_RELOAD) - { - if (IsCrouching()) - { - eNewActivity = ACT_RELOAD_LOW; - } - } - else if ( eNewActivity == ACT_IDLE ) - { - if ( IsCrouching() ) - { - eNewActivity = ACT_CROUCHIDLE; - } - } - - if (CapabilitiesGet() & bits_CAP_DUCK) - { - if (eNewActivity == ACT_RELOAD) - { - return GetReloadActivity(GetHintNode()); - } - else if ((eNewActivity == ACT_COVER ) || - (eNewActivity == ACT_IDLE && HasMemory(bits_MEMORY_INCOVER))) - { - Activity nCoverActivity = GetCoverActivity(GetHintNode()); - // --------------------------------------------------------------- - // Some NPCs don't have a cover activity defined so just use idle - // --------------------------------------------------------------- - if (SelectWeightedSequence( nCoverActivity ) == ACTIVITY_NOT_AVAILABLE) - { - nCoverActivity = ACT_IDLE; - } - - return nCoverActivity; - } - } - return eNewActivity; -} - - -//----------------------------------------------------------------------------- - -Activity CAI_BaseNPC::TranslateActivity( Activity idealActivity, Activity *pIdealWeaponActivity ) -{ - const int MAX_TRIES = 5; - int count = 0; - - bool bIdealWeaponRequired = false; - Activity idealWeaponActivity; - Activity baseTranslation; - bool bWeaponRequired = false; - Activity weaponTranslation; - Activity last; - Activity current; - - idealWeaponActivity = Weapon_TranslateActivity( idealActivity, &bIdealWeaponRequired ); - if ( pIdealWeaponActivity ) - *pIdealWeaponActivity = idealWeaponActivity; - - baseTranslation = idealActivity; - weaponTranslation = idealActivity; - last = idealActivity; - while ( count++ < MAX_TRIES ) - { - current = NPC_TranslateActivity( last ); - if ( current != last ) - baseTranslation = current; - - weaponTranslation = Weapon_TranslateActivity( current, &bWeaponRequired ); - - if ( weaponTranslation == last ) - break; - - last = weaponTranslation; - } - AssertMsg( count < MAX_TRIES, "Circular activity translation!" ); - - if ( last == ACT_SCRIPT_CUSTOM_MOVE ) - return ACT_SCRIPT_CUSTOM_MOVE; - - if ( HaveSequenceForActivity( weaponTranslation ) ) - return weaponTranslation; - - if ( bWeaponRequired ) - { - // only complain about an activity once - static CUtlVector< Activity > sUniqueActivities; - - if (!sUniqueActivities.Find( weaponTranslation)) - { - // FIXME: warning - DevWarning( "%s missing activity \"%s\" needed by weapon\"%s\"\n", - GetClassname(), GetActivityName( weaponTranslation ), GetActiveWeapon()->GetClassname() ); - - sUniqueActivities.AddToTail( weaponTranslation ); - } - } - - if ( baseTranslation != weaponTranslation && HaveSequenceForActivity( baseTranslation ) ) - return baseTranslation; - - if ( idealWeaponActivity != baseTranslation && HaveSequenceForActivity( idealWeaponActivity ) ) - return idealActivity; - - if ( idealActivity != idealWeaponActivity && HaveSequenceForActivity( idealActivity ) ) - return idealActivity; - - Assert( !HaveSequenceForActivity( idealActivity ) ); - if ( idealActivity == ACT_RUN ) - { - idealActivity = ACT_WALK; - } - else if ( idealActivity == ACT_WALK ) - { - idealActivity = ACT_RUN; - } - - return idealActivity; -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : NewActivity - -// iSequence - -// translatedActivity - -// weaponActivity - -//----------------------------------------------------------------------------- -void CAI_BaseNPC::ResolveActivityToSequence(Activity NewActivity, int &iSequence, Activity &translatedActivity, Activity &weaponActivity) -{ - AI_PROFILE_SCOPE( CAI_BaseNPC_ResolveActivityToSequence ); - - iSequence = ACTIVITY_NOT_AVAILABLE; - - translatedActivity = TranslateActivity( NewActivity, &weaponActivity ); - - if ( ( NewActivity == ACT_SCRIPT_CUSTOM_MOVE ) ) - { - iSequence = GetScriptCustomMoveSequence(); - } - else - { - iSequence = SelectWeightedSequence( translatedActivity ); - - if ( iSequence == ACTIVITY_NOT_AVAILABLE ) - { - static CAI_BaseNPC *pLastWarn; - static Activity lastWarnActivity; - static float timeLastWarn; - - if ( ( pLastWarn != this && lastWarnActivity != translatedActivity ) || gpGlobals->curtime - timeLastWarn > 5.0 ) - { - DevWarning( "%s has no sequence for act:%s\n", GetClassname(), ActivityList_NameForIndex(translatedActivity) ); - pLastWarn = this; - lastWarnActivity = translatedActivity; - timeLastWarn = gpGlobals->curtime; - } - - if ( translatedActivity == ACT_RUN ) - { - translatedActivity = ACT_WALK; - iSequence = SelectWeightedSequence( translatedActivity ); - } - } - } - - if ( iSequence == ACT_INVALID ) - { - // Abject failure. Use sequence zero. - iSequence = 0; - } -} - - -//----------------------------------------------------------------------------- -// Purpose: -// Input : NewActivity - -// iSequence - -// translatedActivity - -// weaponActivity - -//----------------------------------------------------------------------------- -extern ConVar ai_sequence_debug; - -void CAI_BaseNPC::SetActivityAndSequence(Activity NewActivity, int iSequence, Activity translatedActivity, Activity weaponActivity) -{ - m_translatedActivity = translatedActivity; - - if (ai_sequence_debug.GetBool() == true && (m_debugOverlays & OVERLAY_NPC_SELECTED_BIT)) - { - DevMsg("SetActivityAndSequence : %s: %s:%s -> %s:%s / %s:%s\n", GetClassname(), - GetActivityName(GetActivity()), GetSequenceName(GetSequence()), - GetActivityName(NewActivity), GetSequenceName(iSequence), - GetActivityName(translatedActivity), GetActivityName(weaponActivity) ); - - } - - // Set to the desired anim, or default anim if the desired is not present - if ( iSequence > ACTIVITY_NOT_AVAILABLE ) - { - if ( GetSequence() != iSequence || !SequenceLoops() ) - { - // - // Don't reset frame between movement phased animations - if (!IsActivityMovementPhased( m_Activity ) || - !IsActivityMovementPhased( NewActivity )) - { - SetCycle( 0 ); - } - } - - ResetSequence( iSequence ); - Weapon_SetActivity( weaponActivity, SequenceDuration( iSequence ) ); - } - else - { - // Not available try to get default anim - ResetSequence( 0 ); - } - - // Set the view position based on the current activity - SetViewOffset( EyeOffset(m_translatedActivity) ); - - if (m_Activity != NewActivity) - { - OnChangeActivity(NewActivity); - } - - // NOTE: We DO NOT write the translated activity here. - // This is to abstract the activity translation from the AI code. - // As far as the code is concerned, a translation is merely a new set of sequences - // that should be regarded as the activity in question. - - // Go ahead and set this so it doesn't keep trying when the anim is not present - m_Activity = NewActivity; - - // this cannot be called until m_Activity stores NewActivity! - GetMotor()->RecalculateYawSpeed(); -} - - -//----------------------------------------------------------------------------- -// Purpose: Sets the activity to the desired activity immediately, skipping any -// transition sequences. -// Input : NewActivity - -//----------------------------------------------------------------------------- -void CAI_BaseNPC::SetActivity( Activity NewActivity ) -{ - // If I'm already doing the NewActivity I can bail. - // FIXME: Should this be based on the current translated activity and ideal translated activity (calculated below)? - // The old code only cared about the logical activity, not translated. - - if (m_Activity == NewActivity) - { - return; - } - - // Don't do this if I'm playing a transition, unless it's ACT_RESET. - if ( NewActivity != ACT_RESET && m_Activity == ACT_TRANSITION && m_IdealActivity != ACT_DO_NOT_DISTURB ) - { - return; - } - - if (ai_sequence_debug.GetBool() == true && (m_debugOverlays & OVERLAY_NPC_SELECTED_BIT)) - { - DevMsg("SetActivity : %s: %s -> %s\n", GetClassname(), GetActivityName(GetActivity()), GetActivityName(NewActivity)); - } - - if ( !GetModelPtr() ) - return; - - // In case someone calls this with something other than the ideal activity. - m_IdealActivity = NewActivity; - - // Resolve to ideals and apply directly, skipping transitions. - ResolveActivityToSequence(m_IdealActivity, m_nIdealSequence, m_IdealTranslatedActivity, m_IdealWeaponActivity); - - //DevMsg("%s: SLAM %s -> %s\n", GetClassname(), GetSequenceName(GetSequence()), GetSequenceName(m_nIdealSequence)); - - SetActivityAndSequence(m_IdealActivity, m_nIdealSequence, m_IdealTranslatedActivity, m_IdealWeaponActivity); -} - - -//----------------------------------------------------------------------------- -// Purpose: Sets the activity that we would like to transition toward. -// Input : NewActivity - -//----------------------------------------------------------------------------- -void CAI_BaseNPC::SetIdealActivity( Activity NewActivity ) -{ - // ignore if it's an ACT_TRANSITION, it means somewhere we're setting IdealActivity with a bogus intermediate value - if (NewActivity == ACT_TRANSITION) - { - Assert( 0 ); - return; - } - - if (ai_sequence_debug.GetBool() == true && (m_debugOverlays & OVERLAY_NPC_SELECTED_BIT)) - { - DevMsg("SetIdealActivity : %s: %s -> %s\n", GetClassname(), GetActivityName(GetActivity()), GetActivityName(NewActivity)); - } - - - if (NewActivity == ACT_RESET) - { - // They probably meant to call SetActivity(ACT_RESET)... we'll fix it for them. - SetActivity(ACT_RESET); - return; - } - - m_IdealActivity = NewActivity; - - if( NewActivity == ACT_DO_NOT_DISTURB ) - { - // Don't resolve anything! Leave it the way the user has it right now. - return; - } - - if ( !GetModelPtr() ) - return; - - // 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); -} - - -//----------------------------------------------------------------------------- -// Purpose: Moves toward the ideal activity through any transition sequences. -//----------------------------------------------------------------------------- -void CAI_BaseNPC::AdvanceToIdealActivity(void) -{ - // If there is a transition sequence between the current sequence and the ideal sequence... - int nNextSequence = FindTransitionSequence(GetSequence(), m_nIdealSequence, NULL); - if (nNextSequence != -1) - { - // We found a transition sequence or possibly went straight to - // the ideal sequence. - if (nNextSequence != m_nIdealSequence) - { -// DevMsg("%s: TRANSITION %s -> %s -> %s\n", GetClassname(), GetSequenceName(GetSequence()), GetSequenceName(nNextSequence), GetSequenceName(m_nIdealSequence)); - - Activity eWeaponActivity = ACT_TRANSITION; - Activity eTranslatedActivity = ACT_TRANSITION; - - // Figure out if the transition sequence has an associated activity that - // we can use for our weapon. Do activity translation also. - Activity eTransitionActivity = GetSequenceActivity(nNextSequence); - if (eTransitionActivity != ACT_INVALID) - { - int nDiscard; - ResolveActivityToSequence(eTransitionActivity, nDiscard, eTranslatedActivity, eWeaponActivity); - } - - // Set activity and sequence to the transition stuff. Set the activity to ACT_TRANSITION - // so we know we're in a transition. - SetActivityAndSequence(ACT_TRANSITION, nNextSequence, eTranslatedActivity, eWeaponActivity); - } - else - { - //DevMsg("%s: IDEAL %s -> %s\n", GetClassname(), GetSequenceName(GetSequence()), GetSequenceName(m_nIdealSequence)); - - // Set activity and sequence to the ideal stuff that was set up in MaintainActivity. - SetActivityAndSequence(m_IdealActivity, m_nIdealSequence, m_IdealTranslatedActivity, m_IdealWeaponActivity); - } - } - // Else go straight there to the ideal activity. - else - { - //DevMsg("%s: Unable to get from sequence %s to %s!\n", GetClassname(), GetSequenceName(GetSequence()), GetSequenceName(m_nIdealSequence)); - SetActivity(m_IdealActivity); - } -} - - -//----------------------------------------------------------------------------- -// Purpose: Tries to achieve our ideal animation state, playing any transition -// sequences that we need to play to get there. -//----------------------------------------------------------------------------- -void CAI_BaseNPC::MaintainActivity(void) -{ - AI_PROFILE_SCOPE( CAI_BaseNPC_MaintainActivity ); - - if ( m_lifeState == LIFE_DEAD ) - { - // Don't maintain activities if we're daid. - // Blame Speyrer - return; - } - - if ((GetState() == NPC_STATE_SCRIPT)) - { - // HACK: finish any transitions we might be playing before we yield control to the script - if (GetActivity() != ACT_TRANSITION) - { - // Our animation state is being controlled by a script. - return; - } - } - - if( m_IdealActivity == ACT_DO_NOT_DISTURB || !GetModelPtr() ) - { - return; - } - - // We may have work to do if we aren't playing our ideal activity OR if we - // aren't playing our ideal sequence. - if ((GetActivity() != m_IdealActivity) || (GetSequence() != m_nIdealSequence)) - { - if (ai_sequence_debug.GetBool() == true && (m_debugOverlays & OVERLAY_NPC_SELECTED_BIT)) - { - DevMsg("MaintainActivity %s : %s:%s -> %s:%s\n", GetClassname(), - GetActivityName(GetActivity()), GetSequenceName(GetSequence()), - GetActivityName(m_IdealActivity), GetSequenceName(m_nIdealSequence)); - } - - bool bAdvance = false; - - // If we're in a transition activity, see if we are done with the transition. - if (GetActivity() == ACT_TRANSITION) - { - // If the current sequence is finished, try to go to the next one - // closer to our ideal sequence. - if (IsSequenceFinished()) - { - bAdvance = true; - } - // Else a transition sequence is in progress, do nothing. - } - // Else get a specific sequence for the activity and try to transition to that. - else - { - // Save off a target sequence and translated activities to apply when we finish - // playing all the transitions and finally arrive at our ideal activity. - ResolveActivityToSequence(m_IdealActivity, m_nIdealSequence, m_IdealTranslatedActivity, m_IdealWeaponActivity); - bAdvance = true; - } - - if (bAdvance) - { - // Try to go to the next sequence closer to our ideal sequence. - AdvanceToIdealActivity(); - } - } -} - - -//----------------------------------------------------------------------------- -// Purpose: Returns true if our ideal activity has finished playing. -//----------------------------------------------------------------------------- -bool CAI_BaseNPC::IsActivityFinished( void ) -{ - return (IsSequenceFinished() && (GetSequence() == m_nIdealSequence)); -} - -//----------------------------------------------------------------------------- -// Purpose: Checks to see if the activity is one of the standard phase-matched movement activities -// Input : activity -//----------------------------------------------------------------------------- -bool CAI_BaseNPC::IsActivityMovementPhased( Activity activity ) -{ - switch( activity ) - { - case ACT_WALK: - case ACT_WALK_AIM: - case ACT_WALK_CROUCH: - case ACT_WALK_CROUCH_AIM: - case ACT_RUN: - case ACT_RUN_AIM: - case ACT_RUN_CROUCH: - case ACT_RUN_CROUCH_AIM: - case ACT_RUN_PROTECTED: - return true; - } - return false; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CAI_BaseNPC::OnChangeActivity( Activity eNewActivity ) -{ - if ( eNewActivity == ACT_RUN || - eNewActivity == ACT_RUN_AIM || - eNewActivity == ACT_WALK ) - { - Stand(); - } -} - -//========================================================= -// SetSequenceByName -//========================================================= -void CAI_BaseNPC::SetSequenceByName( char *szSequence ) -{ - int iSequence = LookupSequence( szSequence ); - - if ( iSequence > ACTIVITY_NOT_AVAILABLE ) - SetSequenceById( iSequence ); - else - { - DevWarning( 2, "%s has no sequence to match request\n", GetClassname(), szSequence ); - SetSequence( 0 ); // Set to the reset anim (if it's there) - } -} - -//----------------------------------------------------------------------------- - -void CAI_BaseNPC::SetSequenceById( int iSequence ) -{ - // Set to the desired anim, or default anim if the desired is not present - if ( iSequence > ACTIVITY_NOT_AVAILABLE ) - { - if ( GetSequence() != iSequence || !SequenceLoops() ) - { - SetCycle( 0 ); - } - - ResetSequence( iSequence ); // Set to the reset anim (if it's there) - GetMotor()->RecalculateYawSpeed(); - } - else - { - // Not available try to get default anim - DevWarning( 2, "%s invalid sequence requested\n", GetClassname() ); - SetSequence( 0 ); // Set to the reset anim (if it's there) - } -} - -//----------------------------------------------------------------------------- -// Purpose: Returns the target entity -// Input : -// Output : -//----------------------------------------------------------------------------- -CBaseEntity *CAI_BaseNPC::GetNavTargetEntity(void) -{ - if ( GetNavigator()->GetGoalType() == GOALTYPE_ENEMY ) - return m_hEnemy; - else if ( GetNavigator()->GetGoalType() == GOALTYPE_TARGETENT ) - return m_hTargetEnt; - return NULL; -} - - -//----------------------------------------------------------------------------- -// Purpose: returns zero if the caller can jump from -// vecStart to vecEnd ignoring collisions with pTarget -// -// if the throw fails, returns the distance -// that can be travelled before an obstacle is hit -//----------------------------------------------------------------------------- -#include "ai_initutils.h" -//#define _THROWDEBUG -float CAI_BaseNPC::ThrowLimit( const Vector &vecStart, - const Vector &vecEnd, - float fGravity, - float fArcSize, - const Vector &mins, - const Vector &maxs, - CBaseEntity *pTarget, - Vector *jumpVel, - CBaseEntity **pBlocker) -{ - // Get my jump velocity - Vector rawJumpVel = CalcThrowVelocity(vecStart, vecEnd, fGravity, fArcSize); - *jumpVel = rawJumpVel; - Vector vecFrom = vecStart; - - // Calculate the total time of the jump minus a tiny fraction - float jumpTime = (vecStart - vecEnd).Length2D()/rawJumpVel.Length2D(); - float timeStep = jumpTime / 10.0; - - Vector gravity = Vector(0,0,fGravity); - - // this loop takes single steps to the goal. - for (float flTime = 0 ; flTime < jumpTime-0.1 ; flTime += timeStep ) - { - // Calculate my position after the time step (average velocity over this time step) - Vector nextPos = vecFrom + (rawJumpVel - 0.5 * gravity * timeStep) * timeStep; - - // If last time step make next position the target position - if ((flTime + timeStep) > jumpTime) - { - nextPos = vecEnd; - } - - trace_t tr; - AI_TraceHull( vecFrom, nextPos, mins, maxs, MASK_SOLID, this, COLLISION_GROUP_NONE, &tr ); - - if (tr.startsolid || tr.fraction < 1.0) - { - CBaseEntity *pEntity = tr.m_pEnt; - - // If we hit the target we are good to go! - if (pEntity == pTarget) - { - return 0; - } - -#ifdef _THROWDEBUG - NDebugOverlay::Line( vecFrom, nextPos, 255, 0, 0, true, 1.0 ); -#endif - // ---------------------------------------------------------- - // If blocked by an npc remember - // ---------------------------------------------------------- - *pBlocker = pEntity; - - // Return distance sucessfully traveled before block encountered - return ((tr.endpos - vecStart).Length()); - } -#ifdef _THROWDEBUG - else - { - NDebugOverlay::Line( vecFrom, nextPos, 255, 255, 255, true, 1.0 ); - } -#endif - - - rawJumpVel = rawJumpVel - gravity * timeStep; - vecFrom = nextPos; - } - return 0; -} - - - -//----------------------------------------------------------------------------- -// Purpose: Called to initialize or re-initialize the vphysics hull when the size -// of the NPC changes -//----------------------------------------------------------------------------- -void CAI_BaseNPC::SetupVPhysicsHull() -{ - if ( GetMoveType() == MOVETYPE_VPHYSICS || GetMoveType() == MOVETYPE_NONE ) - return; - - if ( VPhysicsGetObject() ) - { - // Disable collisions to get - VPhysicsGetObject()->EnableCollisions(false); - VPhysicsDestroyObject(); - } - VPhysicsInitShadow( true, false ); - IPhysicsObject *pPhysObj = VPhysicsGetObject(); - if ( pPhysObj ) - { - float mass = Studio_GetMass(GetModelPtr()); - if ( mass > 0 ) - { - pPhysObj->SetMass( mass ); - } -#if _DEBUG - else - { - DevMsg("Warning: %s has no physical mass\n", STRING(GetModelName())); - } -#endif - IPhysicsShadowController *pController = pPhysObj->GetShadowController(); - float avgsize = (WorldAlignSize().x + WorldAlignSize().y) * 0.5; - pController->SetTeleportDistance( avgsize * 0.5 ); - m_bCheckContacts = true; - } -} - - -// Check for problematic physics objects resting on this NPC. -// They can screw up his navigation, so attach a controller to -// help separate the NPC & physics when you encounter these. -ConVar ai_auto_contact_solver( "ai_auto_contact_solver", "1" ); -void CAI_BaseNPC::CheckPhysicsContacts() -{ - if ( gpGlobals->frametime <= 0.0f || !ai_auto_contact_solver.GetBool() ) - return; - - m_bCheckContacts = false; - if ( GetMoveType() == MOVETYPE_STEP && VPhysicsGetObject()) - { - IPhysicsObject *pPhysics = VPhysicsGetObject(); - IPhysicsFrictionSnapshot *pSnapshot = pPhysics->CreateFrictionSnapshot(); - CBaseEntity *pGroundEntity = GetGroundEntity(); - float heightCheck = GetAbsOrigin().z + GetHullMaxs().z; - Vector npcVel; - pPhysics->GetVelocity( &npcVel, NULL ); - CBaseEntity *pOtherEntity = NULL; - bool createSolver = false; - float solverTime = 0.0f; - while ( pSnapshot->IsValid() ) - { - IPhysicsObject *pOther = pSnapshot->GetObject(1); - pOtherEntity = static_cast(pOther->GetGameData()); - - if ( pOtherEntity && pGroundEntity != pOtherEntity ) - { - float otherMass = PhysGetEntityMass(pOtherEntity); - - if ( pOtherEntity->GetMoveType() == MOVETYPE_VPHYSICS && pOther->IsMoveable() && - otherMass < VPHYSICS_LARGE_OBJECT_MASS ) - { - Assert( !NPCPhysics_SolverExists(this, pOtherEntity) ); - m_bCheckContacts = true; - Vector vel, point; - pOther->GetVelocity( &vel, NULL ); - pSnapshot->GetContactPoint( point ); - - // compare the relative velocity - vel -= npcVel; - - // slow moving object probably won't clear itself. - // Either set ignore, or disable collisions entirely - if ( vel.LengthSqr() < 5.0f*5.0f ) - { - float topdist = fabs(point.z-heightCheck); - // 4 seconds to ignore this for nav - solverTime = 4.0f; - if ( topdist < 2.0f ) - { - // Resting on my head so disable collisions for a bit - solverTime = 0.5f; // UNDONE: Tune - if ( pOther->GetGameFlags() & FVPHYSICS_PLAYER_HELD ) - { - // player is being a monkey - solverTime = 0.25f; - } - - //Msg("Dropping %s from %s\n", pOtherEntity->GetClassname(), GetClassname() ); - createSolver = true; - break; - } - } - } - } - pSnapshot->NextFrictionData(); - } - pPhysics->DestroyFrictionSnapshot( pSnapshot ); - if ( createSolver ) - { - // turn collisions back on once we've been separated for enough time - NPCPhysics_CreateSolver( this, pOtherEntity, true, solverTime ); - pPhysics->RecheckContactPoints(); - } - } -} - -void CAI_BaseNPC::StartTouch( CBaseEntity *pOther ) -{ - BaseClass::StartTouch(pOther); - - if ( pOther->GetMoveType() == MOVETYPE_VPHYSICS ) - { - m_bCheckContacts = true; - } -} - -//----------------------------------------------------------------------------- -// Purpose: To be called instead of UTIL_SetSize, so pathfinding hull -// and actual hull agree -// Input : -// Output : -//----------------------------------------------------------------------------- -void CAI_BaseNPC::SetHullSizeNormal( bool force ) -{ - if ( m_fIsUsingSmallHull || force ) - { - UTIL_SetSize(this, GetHullMins(),GetHullMaxs()); - m_fIsUsingSmallHull = false; - if ( VPhysicsGetObject() ) - { - SetupVPhysicsHull(); - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: To be called instead of UTIL_SetSize, so pathfinding hull -// and actual hull agree -// Input : -// Output : -//----------------------------------------------------------------------------- -bool CAI_BaseNPC::SetHullSizeSmall( bool force ) -{ - if ( !m_fIsUsingSmallHull || force ) - { - UTIL_SetSize(this, NAI_Hull::SmallMins(GetHullType()),NAI_Hull::SmallMaxs(GetHullType())); - m_fIsUsingSmallHull = true; - if ( VPhysicsGetObject() ) - { - SetupVPhysicsHull(); - } - } - return true; -} - -//----------------------------------------------------------------------------- -// Checks to see that the nav hull is valid for the NPC -//----------------------------------------------------------------------------- -bool CAI_BaseNPC::IsNavHullValid() const -{ - Assert( GetSolid() != SOLID_BSP ); - - Vector hullMin = GetHullMins(); - Vector hullMax = GetHullMaxs(); - Vector vecMins, vecMaxs; - if ( GetSolid() == SOLID_BBOX ) - { - vecMins = WorldAlignMins(); - vecMaxs = WorldAlignMaxs(); - } - else if ( GetSolid() == SOLID_VPHYSICS ) - { - Assert( VPhysicsGetObject() ); - const CPhysCollide *pPhysCollide = VPhysicsGetObject()->GetCollide(); - physcollision->CollideGetAABB( vecMins, vecMaxs, pPhysCollide, GetAbsOrigin(), GetAbsAngles() ); - vecMins -= GetAbsOrigin(); - vecMaxs -= GetAbsOrigin(); - } - else - { - vecMins = hullMin; - vecMaxs = hullMax; - } - - if ( (hullMin.x > vecMins.x) || (hullMax.x < vecMaxs.x) || - (hullMin.y > vecMins.y) || (hullMax.y < vecMaxs.y) || - (hullMin.z > vecMins.z) || (hullMax.z < vecMaxs.z) ) - { - return false; - } - - return true; -} - - -//========================================================= -// NPCInit - after a npc is spawned, it needs to -// be dropped into the world, checked for mobility problems, -// and put on the proper path, if any. This function does -// all of those things after the npc spawns. Any -// initialization that should take place for all npcs -// goes here. -//========================================================= -void CAI_BaseNPC::NPCInit ( void ) -{ - if (!g_pGameRules->FAllowNPCs()) - { - UTIL_Remove( this ); - return; - } - - if( IsWaitingToRappel() ) - { - // If this guy's supposed to rappel, keep him from - // falling to the ground when he spawns. - AddFlag( FL_FLY ); - } - -#ifdef _DEBUG - // Make sure that the bounding box is appropriate for the hull size... - // FIXME: We can't test vphysics objects because NPCInit occurs before VPhysics is set up - if ( GetSolid() != SOLID_VPHYSICS && !IsSolidFlagSet(FSOLID_NOT_SOLID) ) - { - if ( !IsNavHullValid() ) - { - Warning("NPC Entity %s (%d) has a bounding box which extends outside its nav box!\n", - STRING(m_iClassname), entindex() ); - } - } -#endif - - // Set fields common to all npcs - AddFlag( FL_AIMTARGET | FL_NPC ); - AddSolidFlags( FSOLID_NOT_STANDABLE ); - - m_flOriginalYaw = GetAbsAngles().y; - - SetBlocksLOS( false ); - - SetGravity(1.0); // Don't change - m_takedamage = DAMAGE_YES; - GetMotor()->SetIdealYaw( GetLocalAngles().y ); - m_iMaxHealth = m_iHealth; - m_lifeState = LIFE_ALIVE; - SetIdealState( NPC_STATE_IDLE );// Assume npc will be idle, until proven otherwise - SetIdealActivity( ACT_IDLE ); - SetActivity( ACT_IDLE ); - -#ifdef HL1_DLL - SetDeathPose( ACT_INVALID ); -#endif - - ClearCommandGoal(); - - ClearSchedule(); - GetNavigator()->ClearGoal(); - InitBoneControllers( ); // FIX: should be done in Spawn - if ( GetModelPtr() ) - { - ResetActivityIndexes(); - ResetEventIndexes(); - } - - SetHintNode( NULL ); - - m_afMemory = MEMORY_CLEAR; - - SetEnemy( NULL ); - - m_flDistTooFar = 1024.0; - SetDistLook( 2048.0 ); - - if ( HasSpawnFlags( SF_NPC_LONG_RANGE ) ) - { - m_flDistTooFar = 1e9f; - SetDistLook( 6000.0 ); - } - - // Clear conditions - m_Conditions.ClearAllBits(); - - // set eye position - SetDefaultEyeOffset(); - - // Only give weapon of allowed to have one - if (CapabilitiesGet() & bits_CAP_USE_WEAPONS) - { // Does this npc spawn with a weapon - if ( m_spawnEquipment != NULL_STRING && strcmp(STRING(m_spawnEquipment), "0")) - { - CBaseCombatWeapon *pWeapon = Weapon_Create( STRING(m_spawnEquipment) ); - if ( pWeapon ) - { - // If I have a name, make my weapon match it with "_weapon" appended - if ( GetEntityName() != NULL_STRING ) - { - pWeapon->SetName( AllocPooledString(UTIL_VarArgs("%s_weapon", GetEntityName())) ); - } - Weapon_Equip( pWeapon ); - } - } - } - - // Robin: Removed this, since it stomps the weapon's settings, and it's stomped - // by OnUpdateShotRegulator() as soon as they finish firing the first time. - //GetShotRegulator()->SetParameters( 2, 6, 0.3f, 0.8f ); - - SetUse ( &CAI_BaseNPC::NPCUse ); - - // NOTE: Can't call NPC Init Think directly... logic changed about - // what time it is when worldspawn happens.. - - // We must put off the rest of our initialization - // until we're sure everything else has had a chance to spawn. Otherwise - // we may try to reference entities that haven't spawned yet.(sjb) - SetThink( &CAI_BaseNPC::NPCInitThink ); - SetNextThink( gpGlobals->curtime + 0.01f ); - - ForceGatherConditions(); - - // HACKHACK: set up a pre idle animation - // NOTE: Must do this before CreateVPhysics() so bone followers have the correct initial positions. - if ( HasSpawnFlags( SF_NPC_WAIT_FOR_SCRIPT ) ) - { - const char *pStartSequence = CAI_ScriptedSequence::GetSpawnPreIdleSequenceForScript( this ); - if ( pStartSequence ) - { - SetSequence( LookupSequence( pStartSequence ) ); - } - } - - CreateVPhysics(); - - if ( HasSpawnFlags( SF_NPC_START_EFFICIENT ) ) - { - SetEfficiency( AIE_EFFICIENT ); - } - - m_bFadeCorpse = ShouldFadeOnDeath(); - - m_GiveUpOnDeadEnemyTimer.Set( 0.75, 2.0 ); - - m_flTimeLastMovement = FLT_MAX; - - m_flIgnoreDangerSoundsUntil = 0; - - SetDeathPose( ACT_INVALID ); - SetDeathPoseFrame( 0 ); - - m_EnemiesSerialNumber = -1; -} - -//----------------------------------------------------------------------------- - -bool CAI_BaseNPC::CreateVPhysics() -{ - if ( IsAlive() && !VPhysicsGetObject() ) - { - SetupVPhysicsHull(); - } - return true; -} - - -//----------------------------------------------------------------------------- -// Set up the shot regulator based on the equipped weapon -//----------------------------------------------------------------------------- -void CAI_BaseNPC::OnUpdateShotRegulator( ) -{ - CBaseCombatWeapon *pWeapon = GetActiveWeapon(); - if ( !pWeapon ) - return; - - // Default values - m_ShotRegulator.SetBurstInterval( pWeapon->GetFireRate(), pWeapon->GetFireRate() ); - m_ShotRegulator.SetBurstShotCountRange( pWeapon->GetMinBurst(), pWeapon->GetMaxBurst() ); - m_ShotRegulator.SetRestInterval( pWeapon->GetMinRestTime(), pWeapon->GetMaxRestTime() ); - - // Let the behavior have a whack at it. - if ( GetRunningBehavior() ) - { - GetRunningBehavior()->OnUpdateShotRegulator(); - } -} - - -//----------------------------------------------------------------------------- -// Set up the shot regulator based on the equipped weapon -//----------------------------------------------------------------------------- -void CAI_BaseNPC::OnChangeActiveWeapon( CBaseCombatWeapon *pOldWeapon, CBaseCombatWeapon *pNewWeapon ) -{ - BaseClass::OnChangeActiveWeapon( pOldWeapon, pNewWeapon ); - - // Shot regulator code - if ( pNewWeapon ) - { - OnUpdateShotRegulator(); - m_ShotRegulator.Reset( true ); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Tests to see if NPC can holster their weapon (if animation exists to holster weapon) -// Output : true if holster weapon animation exists -//----------------------------------------------------------------------------- -bool CAI_BaseNPC::CanHolsterWeapon( void ) -{ - int seq = SelectWeightedSequence( ACT_DISARM ); - return (seq >= 0); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -int CAI_BaseNPC::HolsterWeapon( void ) -{ - if ( IsWeaponHolstered() ) - return -1; - - int iHolsterGesture = FindGestureLayer( ACT_DISARM ); - if ( iHolsterGesture != -1 ) - return iHolsterGesture; - - int iLayer = AddGesture( ACT_DISARM, true ); - //iLayer = AddGesture( ACT_GESTURE_DISARM, true ); - - if (iLayer != -1) - { - // Prevent firing during the holster / unholster - float flDuration = GetLayerDuration( iLayer ); - m_ShotRegulator.FireNoEarlierThan( gpGlobals->curtime + flDuration + 0.5 ); - - if( m_iDesiredWeaponState == DESIREDWEAPONSTATE_HOLSTERED_DESTROYED ) - { - m_iDesiredWeaponState = DESIREDWEAPONSTATE_CHANGING_DESTROY; - } - else - { - m_iDesiredWeaponState = DESIREDWEAPONSTATE_CHANGING; - } - - // Make sure we don't try to reload while we're holstering - ClearCondition(COND_LOW_PRIMARY_AMMO); - ClearCondition(COND_NO_PRIMARY_AMMO); - ClearCondition(COND_NO_SECONDARY_AMMO); - } - - return iLayer; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -int CAI_BaseNPC::UnholsterWeapon( void ) -{ - if ( !IsWeaponHolstered() ) - return -1; - - int iHolsterGesture = FindGestureLayer( ACT_ARM ); - if ( iHolsterGesture != -1 ) - return iHolsterGesture; - - // Deploy the first weapon you can find - for (int i = 0; i < WeaponCount(); i++) - { - if ( GetWeapon( i )) - { - SetActiveWeapon( GetWeapon(i) ); - - int iLayer = AddGesture( ACT_ARM, true ); - //iLayer = AddGesture( ACT_GESTURE_ARM, true ); - - if (iLayer != -1) - { - // Prevent firing during the holster / unholster - float flDuration = GetLayerDuration( iLayer ); - m_ShotRegulator.FireNoEarlierThan( gpGlobals->curtime + flDuration + 0.5 ); - - m_iDesiredWeaponState = DESIREDWEAPONSTATE_CHANGING; - } - - // Refill the clip - if ( GetActiveWeapon()->UsesClipsForAmmo1() ) - { - GetActiveWeapon()->m_iClip1 = GetActiveWeapon()->GetMaxClip1(); - } - - // Make sure we don't try to reload while we're unholstering - ClearCondition(COND_LOW_PRIMARY_AMMO); - ClearCondition(COND_NO_PRIMARY_AMMO); - ClearCondition(COND_NO_SECONDARY_AMMO); - - return iLayer; - } - } - - return -1; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CAI_BaseNPC::InputHolsterWeapon( inputdata_t &inputdata ) -{ - m_iDesiredWeaponState = DESIREDWEAPONSTATE_HOLSTERED; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CAI_BaseNPC::InputHolsterAndDestroyWeapon( inputdata_t &inputdata ) -{ - m_iDesiredWeaponState = DESIREDWEAPONSTATE_HOLSTERED_DESTROYED; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CAI_BaseNPC::InputUnholsterWeapon( inputdata_t &inputdata ) -{ - m_iDesiredWeaponState = DESIREDWEAPONSTATE_UNHOLSTERED; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -bool CAI_BaseNPC::IsWeaponHolstered( void ) -{ - if( !GetActiveWeapon() ) - return true; - - if( GetActiveWeapon()->IsEffectActive(EF_NODRAW) ) - return true; - - return false; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -bool CAI_BaseNPC::IsWeaponStateChanging( void ) -{ - return ( m_iDesiredWeaponState == DESIREDWEAPONSTATE_CHANGING || m_iDesiredWeaponState == DESIREDWEAPONSTATE_CHANGING_DESTROY ); -} - -//----------------------------------------------------------------------------- -// Set up the shot regulator based on the equipped weapon -//----------------------------------------------------------------------------- -void CAI_BaseNPC::OnRangeAttack1() -{ - SetLastAttackTime( gpGlobals->curtime ); - - // Houston, there is a problem! - AssertOnce( GetShotRegulator()->ShouldShoot() ); - - m_ShotRegulator.OnFiredWeapon(); - if ( m_ShotRegulator.IsInRestInterval() ) - { - OnUpdateShotRegulator(); - } - - SetNextAttack( m_ShotRegulator.NextShotTime() ); -} - - -//----------------------------------------------------------------------------- -// Purpose: Initialze the relationship table from the keyvalues -// Input : -// Output : -//----------------------------------------------------------------------------- -void CAI_BaseNPC::InitRelationshipTable(void) -{ - AddRelationship( STRING( m_RelationshipString ), NULL ); -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CAI_BaseNPC::AddRelationship( const char *pszRelationship, CBaseEntity *pActivator ) -{ - // Parse the keyvalue data - char parseString[1000]; - Q_strncpy(parseString, pszRelationship, sizeof(parseString)); - - // Look for an entity string - char *entityString = strtok(parseString," "); - while (entityString) - { - // Get the disposition - char *dispositionString = strtok(NULL," "); - Disposition_t disposition = D_NU; - if ( dispositionString ) - { - if (!stricmp(dispositionString,"D_HT")) - { - disposition = D_HT; - } - else if (!stricmp(dispositionString,"D_FR")) - { - disposition = D_FR; - } - else if (!stricmp(dispositionString,"D_LI")) - { - disposition = D_LI; - } - else if (!stricmp(dispositionString,"D_NU")) - { - disposition = D_NU; - } - else - { - disposition = D_NU; - Warning( "***ERROR***\nBad relationship type (%s) to unknown entity (%s)!\n", dispositionString,entityString ); - Assert( 0 ); - return; - } - } - else - { - Warning("Can't parse relationship info (%s)\n", pszRelationship ); - Assert(0); - return; - } - - // Get the priority - char *priorityString = strtok(NULL," "); - int priority = ( priorityString ) ? atoi(priorityString) : DEF_RELATIONSHIP_PRIORITY; - - bool bFoundEntity = false; - - // Try to get pointer to an entity of this name - CBaseEntity *entity = gEntList.FindEntityByName( NULL, entityString ); - while( entity ) - { - // make sure you catch all entities of this name. - bFoundEntity = true; - AddEntityRelationship(entity, disposition, priority ); - entity = gEntList.FindEntityByName( entity, entityString ); - } - - if( !bFoundEntity ) - { - // Need special condition for player as we can only have one - if (!stricmp("player", entityString) || !stricmp("!player", entityString)) - { - AddClassRelationship( CLASS_PLAYER, disposition, priority ); - } - // Otherwise try to create one too see if a valid classname and get class type - else - { - // HACKHACK: - CBaseEntity *pEntity = CanCreateEntityClass( entityString ) ? CreateEntityByName( entityString ) : NULL; - if (pEntity) - { - AddClassRelationship( pEntity->Classify(), disposition, priority ); - UTIL_RemoveImmediate(pEntity); - } - else - { - DevWarning( "Couldn't set relationship to unknown entity or class (%s)!\n", entityString ); - } - } - } - // Check for another entity in the list - entityString = strtok(NULL," "); - } -} - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -void CAI_BaseNPC::AddEntityRelationship( CBaseEntity *pEntity, Disposition_t nDisposition, int nPriority ) -{ -#if 0 - ForceGatherConditions(); -#endif - BaseClass::AddEntityRelationship( pEntity, nDisposition, nPriority ); -} - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -void CAI_BaseNPC::AddClassRelationship( Class_T nClass, Disposition_t nDisposition, int nPriority ) -{ -#if 0 - ForceGatherConditions(); -#endif - BaseClass::AddClassRelationship( nClass, nDisposition, nPriority ); -} - -//========================================================= -// NPCInitThink - Calls StartNPC. Startnpc is -// virtual, but this function cannot be -//========================================================= -void CAI_BaseNPC::NPCInitThink ( void ) -{ - // Initialize the relationship table - InitRelationshipTable(); - - StartNPC(); - - PostNPCInit(); - - if( GetSleepState() == AISS_AUTO_PVS ) - { - // This code is a bit wonky, but it makes it easier for level designers to - // select this option in Hammer. So we set a sleep flag to indicate the choice, - // and then set the sleep state to awake (normal) - AddSleepFlags( AI_SLEEP_FLAG_AUTO_PVS ); - SetSleepState( AISS_AWAKE ); - } - - if( GetSleepState() == AISS_AUTO_PVS_AFTER_PVS ) - { - AddSleepFlags( AI_SLEEP_FLAG_AUTO_PVS_AFTER_PVS ); - SetSleepState( AISS_AWAKE ); - } - - if ( GetSleepState() > AISS_AWAKE ) - { - Sleep(); - } - - m_flLastRealThinkTime = gpGlobals->curtime; -} - -//========================================================= -// StartNPC - final bit of initization before a npc -// is turned over to the AI. -//========================================================= -void CAI_BaseNPC::StartNPC( void ) -{ - // Raise npc off the floor one unit, then drop to floor - if ( (GetMoveType() != MOVETYPE_FLY) && (GetMoveType() != MOVETYPE_FLYGRAVITY) && - !(CapabilitiesGet() & bits_CAP_MOVE_FLY) && - !HasSpawnFlags( SF_NPC_FALL_TO_GROUND ) && !IsWaitingToRappel() && !GetMoveParent() ) - { - Vector origin = GetLocalOrigin(); - - if (!GetMoveProbe()->FloorPoint( origin + Vector(0, 0, 0.1), MASK_NPCSOLID, 0, -2048, &origin )) - { - Warning( "NPC %s stuck in wall--level design error at (%.2f %.2f %.2f)\n", GetClassname(), GetAbsOrigin().x, GetAbsOrigin().y, GetAbsOrigin().z ); - if ( g_pDeveloper->GetInt() > 1 ) - { - m_debugOverlays |= OVERLAY_BBOX_BIT; - } - } - - SetLocalOrigin( origin ); - } - else - { - SetGroundEntity( NULL ); - } - - if ( m_target != NULL_STRING )// this npc has a target - { - // Find the npc's initial target entity, stash it - SetGoalEnt( gEntList.FindEntityByName( NULL, m_target ) ); - - if ( !GetGoalEnt() ) - { - Warning( "ReadyNPC()--%s couldn't find target %s\n", GetClassname(), STRING(m_target)); - } - else - { - StartTargetHandling( GetGoalEnt() ); - } - } - - //SetState ( m_IdealNPCState ); - //SetActivity ( m_IdealActivity ); - - InitSquad(); - - //--------------------------------- - // - // Spread think times of simultaneously spawned NPCs so that they don't all happen at the same time - // - // Think distribution based on spawn order is: - // - // Tick offset Think time Spawn order - // 0 0 1 - // 1 0.015 13 - // 2 0.03 5 - // 3 0.045 9 - // 4 0.06 18 - // 5 0.075 3 - // 6 0.09 15 - // 7 0.105 11 - // 8 0.12 7 - // 9 0.135 17 - // 10 0.15 2 - // 11 0.165 14 - // 12 0.18 6 - // 13 0.195 19 - // 14 0.21 10 - // 15 0.225 4 - // 16 0.24 16 - // 17 0.255 12 - // 18 0.27 8 - // 19 0.285 20 - - - // If this NPC is spawning late in the game, just push through the rest of the initialization - // start thinking right now. Some spread is added to handle triggered spawns that bring - // a bunch of NPCs into the level - SetThink ( &CAI_BaseNPC::CallNPCThink ); - - if ( gm_flTimeLastSpawn != gpGlobals->curtime ) - { - gm_nSpawnedThisFrame = 0; - gm_flTimeLastSpawn = gpGlobals->curtime; - } - - static const float nextThinkTimes[20] = - { - .0, .150, .075, .225, .030, .180, .120, .270, .045, .210, .105, .255, .015, .165, .090, .240, .135, .060, .195, .285 - }; - - SetNextThink( gpGlobals->curtime + nextThinkTimes[gm_nSpawnedThisFrame % 20] ); - - gm_nSpawnedThisFrame++; - - //--------------------------------- - - m_ScriptArrivalActivity = AIN_DEF_ACTIVITY; - m_strScriptArrivalSequence = NULL_STRING; - - if ( HasSpawnFlags(SF_NPC_WAIT_FOR_SCRIPT) ) - { - SetState( NPC_STATE_IDLE ); - m_Activity = m_IdealActivity; - m_nIdealSequence = GetSequence(); - SetSchedule( SCHED_WAIT_FOR_SCRIPT ); - } -} - -//----------------------------------------------------------------------------- - -void CAI_BaseNPC::StartTargetHandling( CBaseEntity *pTargetEnt ) -{ - // set the npc up to walk a path corner path. - // !!!BUGBUG - this is a minor bit of a hack. - // JAYJAY - - // NPC will start turning towards his destination - bool bIsFlying = (GetMoveType() == MOVETYPE_FLY) || (GetMoveType() == MOVETYPE_FLYGRAVITY); - AI_NavGoal_t goal( GOALTYPE_PATHCORNER, pTargetEnt->GetAbsOrigin(), - bIsFlying ? ACT_FLY : ACT_WALK, - AIN_DEF_TOLERANCE, AIN_YAW_TO_DEST); - - SetState( NPC_STATE_IDLE ); - SetSchedule( SCHED_IDLE_WALK ); - - if ( !GetNavigator()->SetGoal( goal ) ) - { - DevWarning( 2, "Can't Create Route!\n" ); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Connect my memory to the squad's -//----------------------------------------------------------------------------- -bool CAI_BaseNPC::InitSquad( void ) -{ - // ------------------------------------------------------- - // If I form squads add me to a squad - // ------------------------------------------------------- - if (!m_pSquad && ( CapabilitiesGet() & bits_CAP_SQUAD )) - { - if ( !m_SquadName ) - { - DevMsg(2, "Found %s that isn't in a squad\n",GetClassname()); - } - else - { - m_pSquad = g_AI_SquadManager.FindCreateSquad(this, m_SquadName); - } - } - - return ( m_pSquad != NULL ); -} - -//----------------------------------------------------------------------------- -// Purpose: Get the memory for this NPC -//----------------------------------------------------------------------------- -CAI_Enemies *CAI_BaseNPC::GetEnemies( void ) -{ - return m_pEnemies; -} - -//----------------------------------------------------------------------------- -// Purpose: Remove this NPC's memory -//----------------------------------------------------------------------------- -void CAI_BaseNPC::RemoveMemory( void ) -{ - delete m_pEnemies; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CAI_BaseNPC::TaskComplete( bool fIgnoreSetFailedCondition ) -{ - EndTaskOverlay(); - - // Handy thing to use for debugging - //if (IsCurSchedule(SCHED_PUT_HERE) && - // GetTask()->iTask == TASK_PUT_HERE) - //{ - // int put_breakpoint_here = 5; - //} - - if ( fIgnoreSetFailedCondition || !HasCondition(COND_TASK_FAILED) ) - { - SetTaskStatus( TASKSTATUS_COMPLETE ); - } -} - -void CAI_BaseNPC::TaskMovementComplete( void ) -{ - switch( GetTaskStatus() ) - { - case TASKSTATUS_NEW: - case TASKSTATUS_RUN_MOVE_AND_TASK: - SetTaskStatus( TASKSTATUS_RUN_TASK ); - break; - - case TASKSTATUS_RUN_MOVE: - TaskComplete(); - break; - - case TASKSTATUS_RUN_TASK: - // FIXME: find out how to safely restart movement - //Warning( "Movement completed twice!\n" ); - //Assert( 0 ); - break; - - case TASKSTATUS_COMPLETE: - break; - } - - // JAY: Put this back in. - // UNDONE: Figure out how much of the timestep was consumed by movement - // this frame and restart the movement/schedule engine if necessary - if ( m_scriptState != SCRIPT_CUSTOM_MOVE_TO_MARK ) - { - SetIdealActivity( GetStoppedActivity() ); - } - - // Advance past the last node (in case there is some event at this node) - if ( GetNavigator()->IsGoalActive() ) - { - GetNavigator()->AdvancePath(); - } - - // Now clear the path, it's done. - GetNavigator()->ClearGoal(); - - OnMovementComplete(); -} - - -int CAI_BaseNPC::TaskIsRunning( void ) -{ - if ( GetTaskStatus() != TASKSTATUS_COMPLETE && - GetTaskStatus() != TASKSTATUS_RUN_MOVE ) - return 1; - - return 0; -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : -// Output : -//----------------------------------------------------------------------------- -void CAI_BaseNPC::TaskFail( AI_TaskFailureCode_t code ) -{ - EndTaskOverlay(); - - // Handy tool for debugging - //if (IsCurSchedule(SCHED_PUT_NAME_HERE)) - //{ - // int put_breakpoint_here = 5; - //} - - // If in developer mode save the fail text for debug output - if (g_pDeveloper->GetInt()) - { - m_failText = TaskFailureToString( code ); - - m_interuptSchedule = NULL; - m_failedSchedule = GetCurSchedule(); - - if (m_debugOverlays & OVERLAY_TASK_TEXT_BIT) - { - DevMsg(this, AIMF_IGNORE_SELECTED, " TaskFail -> %s\n", m_failText ); - } - - ADD_DEBUG_HISTORY( HISTORY_AI_DECISIONS, UTIL_VarArgs("%s(%d): TaskFail -> %s\n", GetDebugName(), entindex(), m_failText ) ); - - //AddTimedOverlay( fail_text, 5); - } - - m_ScheduleState.taskFailureCode = code; - SetCondition(COND_TASK_FAILED); - Forget( bits_MEMORY_TURNING ); -} - -//------------------------------------------------------------------------------ -// Purpose : Remember that this entity wasn't reachable -// Input : -// Output : -//------------------------------------------------------------------------------ -void CAI_BaseNPC::RememberUnreachable(CBaseEntity *pEntity) -{ - if ( pEntity == GetEnemy() ) - { - ForceChooseNewEnemy(); - } - - const float NPC_UNREACHABLE_TIMEOUT = 3; - // Only add to list if not already on it - for (int i=m_UnreachableEnts.Size()-1;i>=0;i--) - { - // If record already exists just update mark time - if (pEntity == m_UnreachableEnts[i].hUnreachableEnt) - { - m_UnreachableEnts[i].fExpireTime = gpGlobals->curtime + NPC_UNREACHABLE_TIMEOUT; - m_UnreachableEnts[i].vLocationWhenUnreachable = pEntity->GetAbsOrigin(); - return; - } - } - - // Add new unreachabe entity to list - int nNewIndex = m_UnreachableEnts.AddToTail(); - m_UnreachableEnts[nNewIndex].hUnreachableEnt = pEntity; - m_UnreachableEnts[nNewIndex].fExpireTime = gpGlobals->curtime + NPC_UNREACHABLE_TIMEOUT; - m_UnreachableEnts[nNewIndex].vLocationWhenUnreachable = pEntity->GetAbsOrigin(); -} - -//------------------------------------------------------------------------------ -// Purpose : Returns true is entity was remembered as unreachable. -// After a time delay reachability is checked -// Input : -// Output : -//------------------------------------------------------------------------------ -bool CAI_BaseNPC::IsUnreachable(CBaseEntity *pEntity) -{ - float UNREACHABLE_DIST_TOLERANCE_SQ = (120*120); - - // Note that it's ok to remove elements while I'm iterating - // as long as I iterate backwards and remove them using FastRemove - for (int i=m_UnreachableEnts.Size()-1;i>=0;i--) - { - // Remove any dead elements - if (m_UnreachableEnts[i].hUnreachableEnt == NULL) - { - m_UnreachableEnts.FastRemove(i); - } - else if (pEntity == m_UnreachableEnts[i].hUnreachableEnt) - { - // Test for reachablility on any elements that have timed out - if ( gpGlobals->curtime > m_UnreachableEnts[i].fExpireTime || - pEntity->GetAbsOrigin().DistToSqr(m_UnreachableEnts[i].vLocationWhenUnreachable) > UNREACHABLE_DIST_TOLERANCE_SQ) - { - m_UnreachableEnts.FastRemove(i); - return false; - } - return true; - } - } - return false; -} - -bool CAI_BaseNPC::IsValidEnemy( CBaseEntity *pEnemy ) -{ - CAI_BaseNPC *pEnemyNPC = pEnemy->MyNPCPointer(); - if ( pEnemyNPC && pEnemyNPC->CanBeAnEnemyOf( this ) == false ) - return false; - - // Test our enemy filter - if ( m_hEnemyFilter.Get()!= NULL && m_hEnemyFilter->PassesFilter( this, pEnemy ) == false ) - return false; - - return true; -} - - -bool CAI_BaseNPC::CanBeAnEnemyOf( CBaseEntity *pEnemy ) -{ - if ( GetSleepState() > AISS_WAITING_FOR_THREAT ) - return false; - - return true; -} - - -//----------------------------------------------------------------------------- -// Purpose: Picks best enemy from my list of enemies -// Prefers reachable enemies over enemies that are unreachable, -// regardless of priority. For enemies that are both reachable or -// unreachable picks by priority. If priority is the same, picks -// by distance. -// Input : -// Output : -//----------------------------------------------------------------------------- - -CBaseEntity *CAI_BaseNPC::BestEnemy( void ) -{ - AI_PROFILE_SCOPE( CAI_BaseNPC_BestEnemy ); - // TODO - may want to consider distance, attack types, back turned, etc. - - CBaseEntity* pBestEnemy = NULL; - int iBestDistSq = MAX_COORD_RANGE * MAX_COORD_RANGE;// so first visible entity will become the closest. - int iBestPriority = -1000; - bool bBestUnreachable = true; // Forces initial check - ThreeState_t fBestSeen = TRS_NONE; - ThreeState_t fBestVisible = TRS_NONE; - int iDistSq; - bool bUnreachable = false; - - AIEnemiesIter_t iter; - - DbgEnemyMsg( this, "BestEnemy() {\n" ); - - for( AI_EnemyInfo_t *pEMemory = GetEnemies()->GetFirst(&iter); pEMemory != NULL; pEMemory = GetEnemies()->GetNext(&iter) ) - { - CBaseEntity *pEnemy = pEMemory->hEnemy; - - if (!pEnemy || !pEnemy->IsAlive()) - { - if ( pEnemy ) - DbgEnemyMsg( this, " %s rejected: dead\n", pEnemy->GetDebugName() ); - continue; - } - - if ( (pEnemy->GetFlags() & FL_NOTARGET) ) - { - DbgEnemyMsg( this, " %s rejected: no target\n", pEnemy->GetDebugName() ); - continue; - } - - // UNDONE: Move relationship checks into IsValidEnemy? - Disposition_t relation = IRelationType( pEnemy ); - if ( (relation != D_HT && relation != D_FR) ) - { - DbgEnemyMsg( this, " %s rejected: no hate/fear\n", pEnemy->GetDebugName() ); - continue; - } - - if ( m_flAcceptableTimeSeenEnemy > 0.0 && pEMemory->timeLastSeen < m_flAcceptableTimeSeenEnemy ) - { - DbgEnemyMsg( this, " %s rejected: old\n", pEnemy->GetDebugName() ); - continue; - } - - if ( pEMemory->timeValidEnemy > gpGlobals->curtime ) - { - DbgEnemyMsg( this, " %s rejected: not yet valid\n", pEnemy->GetDebugName() ); - continue; - } - - // Skip enemies that have eluded me to prevent infinite loops - if ( pEMemory->bEludedMe ) - { - DbgEnemyMsg( this, " %s rejected: eluded\n", pEnemy->GetDebugName() ); - continue; - } - - // Skip enemies I fear that I've never seen. (usually seen through an enemy finder) - if ( relation == D_FR && !pEMemory->bUnforgettable && pEMemory->timeFirstSeen == AI_INVALID_TIME ) - { - DbgEnemyMsg( this, " %s rejected: feared, but never seen\n", pEnemy->GetDebugName() ); - continue; - } - - if ( !IsValidEnemy( pEnemy ) ) - { - DbgEnemyMsg( this, " %s rejected: not valid\n", pEnemy->GetDebugName() ); - continue; - } - - // establish the reachability of this enemy - bUnreachable = IsUnreachable(pEnemy); - - // If best is reachable and current is unreachable, skip the unreachable enemy regardless of priority - if (!bBestUnreachable && bUnreachable) - { - DbgEnemyMsg( this, " %s rejected: unreachable\n", pEnemy->GetDebugName() ); - continue; - } - - // If best is unreachable and current is reachable, always pick the current regardless of priority - if (bBestUnreachable && !bUnreachable) - { - DbgEnemyMsg( this, " %s accepted (1)\n", pEnemy->GetDebugName() ); - if ( pBestEnemy ) - DbgEnemyMsg( this, " (%s displaced)\n", pBestEnemy->GetDebugName() ); - - iBestPriority = IRelationPriority ( pEnemy ); - iBestDistSq = (pEnemy->GetAbsOrigin() - GetAbsOrigin() ).LengthSqr(); - pBestEnemy = pEnemy; - bBestUnreachable = bUnreachable; - fBestSeen = TRS_NONE; - fBestVisible = TRS_NONE; - } - // If both are unreachable or both are reachable, chose enemy based on priority and distance - else if ( IRelationPriority( pEnemy ) > iBestPriority ) - { - DbgEnemyMsg( this, " %s accepted\n", pEnemy->GetDebugName() ); - if ( pBestEnemy ) - DbgEnemyMsg( this, " (%s displaced due to priority, %d > %d )\n", pBestEnemy->GetDebugName(), IRelationPriority( pEnemy ), iBestPriority ); - // this entity is disliked MORE than the entity that we - // currently think is the best visible enemy. No need to do - // a distance check, just get mad at this one for now. - iBestPriority = IRelationPriority ( pEnemy ); - iBestDistSq = ( pEnemy->GetAbsOrigin() - GetAbsOrigin() ).LengthSqr(); - pBestEnemy = pEnemy; - bBestUnreachable = bUnreachable; - fBestSeen = TRS_NONE; - fBestVisible = TRS_NONE; - } - else if ( IRelationPriority( pEnemy ) == iBestPriority ) - { - // this entity is disliked just as much as the entity that - // we currently think is the best visible enemy, so we only - // get mad at it if it is closer. - iDistSq = ( pEnemy->GetAbsOrigin() - GetAbsOrigin() ).LengthSqr(); - - bool bAcceptCurrent = false; - bool bCloser = ( iDistSq < iBestDistSq ); - ThreeState_t fCurSeen = TRS_NONE; - ThreeState_t fCurVisible = TRS_NONE; - - // The following code is constructed in such a verbose manner to - // ensure the expensive calls only occur if absolutely needed - - // If current is farther, and best has previously been confirmed as seen or visible, move on - if ( !bCloser) - { - if ( fBestSeen == TRS_TRUE || fBestVisible == TRS_TRUE ) - { - DbgEnemyMsg( this, " %s rejected: current is closer and seen\n", pEnemy->GetDebugName() ); - continue; - } - } - - // If current is closer, and best has previously been confirmed as not seen and not visible, take it - if ( bCloser) - { - if ( fBestSeen == TRS_FALSE && fBestVisible == TRS_FALSE ) - { - bAcceptCurrent = true; - } - } - - if ( !bAcceptCurrent ) - { - // If current is closer, and seen, take it - if ( bCloser ) - { - fCurSeen = ( GetSenses()->DidSeeEntity( pEnemy ) ) ? TRS_TRUE : TRS_FALSE; - - bAcceptCurrent = ( fCurSeen == TRS_TRUE ); - } - } - - if ( !bAcceptCurrent ) - { - // If current is farther, and best is seen, move on - if ( !bCloser ) - { - if ( fBestSeen == TRS_NONE ) - { - fBestSeen = ( GetSenses()->DidSeeEntity( pBestEnemy ) ) ? TRS_TRUE : TRS_FALSE; - } - - if ( fBestSeen == TRS_TRUE ) - { - DbgEnemyMsg( this, " %s rejected: current is closer and seen\n", pEnemy->GetDebugName() ); - continue; - } - } - - // At this point, need to start performing expensive tests - if ( bCloser && fBestVisible == TRS_NONE ) - { - // Perform shortest FVisible - fCurVisible = ( ( EnemyDistance( pEnemy ) < GetSenses()->GetDistLook() ) && FVisible( pEnemy ) ) ? TRS_TRUE : TRS_FALSE; - - bAcceptCurrent = ( fCurVisible == TRS_TRUE ); - } - - // Alas, must do the most expensive comparison - if ( !bAcceptCurrent ) - { - if ( fBestSeen == TRS_NONE ) - { - fBestSeen = ( GetSenses()->DidSeeEntity( pBestEnemy ) ) ? TRS_TRUE : TRS_FALSE; - } - - if ( fBestVisible == TRS_NONE ) - { - fBestVisible = ( ( EnemyDistance( pBestEnemy ) < GetSenses()->GetDistLook() ) && FVisible( pBestEnemy ) ) ? TRS_TRUE : TRS_FALSE; - } - - if ( fCurSeen == TRS_NONE ) - { - fCurSeen = ( GetSenses()->DidSeeEntity( pEnemy ) ) ? TRS_TRUE : TRS_FALSE; - } - - if ( fCurVisible == TRS_NONE ) - { - fCurVisible = ( ( EnemyDistance( pEnemy ) < GetSenses()->GetDistLook() ) && FVisible( pEnemy ) ) ? TRS_TRUE : TRS_FALSE; - } - - bool bBestSeenOrVisible = ( fBestSeen == TRS_TRUE || fBestVisible == TRS_TRUE ); - bool bCurSeenOrVisible = ( fCurSeen == TRS_TRUE || fCurVisible == TRS_TRUE ); - - if ( !bCloser) - { - if ( bBestSeenOrVisible ) - { - DbgEnemyMsg( this, " %s rejected: current is closer and seen\n", pEnemy->GetDebugName() ); - continue; - } - else if ( !bCurSeenOrVisible ) - { - DbgEnemyMsg( this, " %s rejected: current is closer and neither is seen\n", pEnemy->GetDebugName() ); - continue; - } - } - else // Closer - { - if ( !bCurSeenOrVisible && bBestSeenOrVisible ) - { - DbgEnemyMsg( this, " %s rejected: current is father but seen\n", pEnemy->GetDebugName() ); - continue; - } - } - } - } - - DbgEnemyMsg( this, " %s accepted\n", pEnemy->GetDebugName() ); - if ( pBestEnemy ) - DbgEnemyMsg( this, " (%s displaced due to distance/visibility)\n", pBestEnemy->GetDebugName() ); - fBestSeen = fCurSeen; - fBestVisible = fCurVisible; - iBestDistSq = iDistSq; - iBestPriority = IRelationPriority ( pEnemy ); - pBestEnemy = pEnemy; - bBestUnreachable = bUnreachable; - } - else - DbgEnemyMsg( this, " %s rejected: lower priority\n", pEnemy->GetDebugName() ); - } - - DbgEnemyMsg( this, "} == %s\n", pBestEnemy->GetDebugName() ); - - return pBestEnemy; -} - -//----------------------------------------------------------------------------- -// Purpose: Given a node returns the appropriate reload activity -// Input : -// Output : -//----------------------------------------------------------------------------- -Activity CAI_BaseNPC::GetReloadActivity( CAI_Hint* pHint ) -{ - Activity nReloadActivity = ACT_RELOAD; - - if (pHint && GetEnemy()!=NULL) - { - switch (pHint->HintType()) - { - case HINT_TACTICAL_COVER_LOW: - case HINT_TACTICAL_COVER_MED: - { - if (SelectWeightedSequence( ACT_RELOAD_LOW ) != ACTIVITY_NOT_AVAILABLE) - { - Vector vEyePos = GetAbsOrigin() + EyeOffset(ACT_RELOAD_LOW); - // Check if this location will block the threat's line of sight to me - trace_t tr; - AI_TraceLOS( vEyePos, GetEnemy()->EyePosition(), this, &tr ); - if (tr.fraction != 1.0) - { - nReloadActivity = ACT_RELOAD_LOW; - } - } - break; - } - } - } - return nReloadActivity; -} - -//----------------------------------------------------------------------------- -// Purpose: Given a node returns the appropriate cover activity -// Input : -// Output : -//----------------------------------------------------------------------------- -Activity CAI_BaseNPC::GetCoverActivity( CAI_Hint *pHint ) -{ - Activity nCoverActivity = ACT_INVALID; - - // --------------------------------------------------------------- - // Check if hint node specifies different cover type - // --------------------------------------------------------------- - if (pHint) - { - switch (pHint->HintType()) - { - case HINT_TACTICAL_COVER_MED: - { - nCoverActivity = ACT_COVER_MED; - break; - } - case HINT_TACTICAL_COVER_LOW: - { - nCoverActivity = ACT_COVER_LOW; - break; - } - } - } - - if ( nCoverActivity == ACT_INVALID ) - nCoverActivity = ACT_COVER; - - return nCoverActivity; -} - -//========================================================= -// CalcIdealYaw - gets a yaw value for the caller that would -// face the supplied vector. Value is stuffed into the npc's -// ideal_yaw -//========================================================= -float CAI_BaseNPC::CalcIdealYaw( const Vector &vecTarget ) -{ - Vector vecProjection; - - // strafing npc needs to face 90 degrees away from its goal - if ( GetNavigator()->GetMovementActivity() == ACT_STRAFE_LEFT ) - { - vecProjection.x = -vecTarget.y; - vecProjection.y = vecTarget.x; - - return UTIL_VecToYaw( vecProjection - GetLocalOrigin() ); - } - else if ( GetNavigator()->GetMovementActivity() == ACT_STRAFE_RIGHT ) - { - vecProjection.x = vecTarget.y; - vecProjection.y = vecTarget.x; - - return UTIL_VecToYaw( vecProjection - GetLocalOrigin() ); - } - else - { - return UTIL_VecToYaw ( vecTarget - GetLocalOrigin() ); - } -} - -//========================================================= -// SetEyePosition -// -// queries the npc's model for $eyeposition and copies -// that vector to the npc's m_vDefaultEyeOffset and m_vecViewOffset -// -//========================================================= -void CAI_BaseNPC::SetDefaultEyeOffset ( void ) -{ - if ( GetModelPtr() ) - { - GetEyePosition( GetModelPtr(), m_vDefaultEyeOffset ); - - if ( m_vDefaultEyeOffset == vec3_origin ) - { - if ( Classify() != CLASS_NONE ) - { - DevMsg( "WARNING: %s(%s) has no eye offset in .qc!\n", GetClassname(), STRING(GetModelName()) ); - } - VectorAdd( WorldAlignMins(), WorldAlignMaxs(), m_vDefaultEyeOffset ); - m_vDefaultEyeOffset *= 0.75; - } - } - else - m_vDefaultEyeOffset = vec3_origin; - - SetViewOffset( m_vDefaultEyeOffset ); - -} - -//------------------------------------------------------------------------------ -// Purpose : Returns eye offset for an NPC for the given activity -// Input : -// Output : -//------------------------------------------------------------------------------ -Vector CAI_BaseNPC::EyeOffset( Activity nActivity ) -{ - if ( CapabilitiesGet() & bits_CAP_DUCK ) - { - if ( IsCrouchedActivity( nActivity ) ) - return GetCrouchEyeOffset(); - } - - // if the hint doesn't tell anything, assume current state - if ( IsCrouching() ) - return GetCrouchEyeOffset(); - - return m_vDefaultEyeOffset; -} - -//----------------------------------------------------------------------------- -// Purpose: -// Output : Vector -//----------------------------------------------------------------------------- -Vector CAI_BaseNPC::EyePosition( void ) -{ - if ( IsCrouching() ) - return GetAbsOrigin() + GetCrouchEyeOffset(); - - return BaseClass::EyePosition(); -} - -//------------------------------------------------------------------------------ -// Purpose : -// Input : -// Output : -//------------------------------------------------------------------------------ -void CAI_BaseNPC::HandleAnimEvent( animevent_t *pEvent ) -{ - // UNDONE: Share this code into CBaseAnimating as appropriate? - switch( pEvent->event ) - { - case SCRIPT_EVENT_DEAD: - if ( m_NPCState == NPC_STATE_SCRIPT ) - { - m_lifeState = LIFE_DYING; - // Kill me now! (and fade out when CineCleanup() is called) -#if _DEBUG - DevMsg( 2, "Death event: %s\n", GetClassname() ); -#endif - m_iHealth = 0; - } -#if _DEBUG - else - DevWarning( 2, "INVALID death event:%s\n", GetClassname() ); -#endif - break; - case SCRIPT_EVENT_NOT_DEAD: - if ( m_NPCState == NPC_STATE_SCRIPT ) - { - m_lifeState = LIFE_ALIVE; - // This is for life/death sequences where the player can determine whether a character is dead or alive after the script - m_iHealth = m_iMaxHealth; - } - break; - - case SCRIPT_EVENT_SOUND: // Play a named wave file - { - EmitSound( pEvent->options ); - } - break; - - case SCRIPT_EVENT_SOUND_VOICE: - { - EmitSound( pEvent->options ); - } - break; - - case SCRIPT_EVENT_SENTENCE_RND1: // Play a named sentence group 33% of the time - if (random->RandomInt(0,2) == 0) - break; - // fall through... - case SCRIPT_EVENT_SENTENCE: // Play a named sentence group - SENTENCEG_PlayRndSz( edict(), pEvent->options, 1.0, SNDLVL_TALKING, 0, 100 ); - break; - - case SCRIPT_EVENT_FIREEVENT: - { - // - // Fire a script event. The number of the script event to fire is in the options string. - // - if ( m_hCine != NULL ) - { - m_hCine->FireScriptEvent( atoi( pEvent->options ) ); - } - else - { - // FIXME: look so see if it's playing a vcd and fire those instead - // AssertOnce( 0 ); - } - break; - } - case SCRIPT_EVENT_FIRE_INPUT: - { - variant_t emptyVariant; - this->AcceptInput( pEvent->options, this, this, emptyVariant, 0 ); - break; - } - - case SCRIPT_EVENT_NOINTERRUPT: // Can't be interrupted from now on - if ( m_hCine ) - m_hCine->AllowInterrupt( false ); - break; - - case SCRIPT_EVENT_CANINTERRUPT: // OK to interrupt now - if ( m_hCine ) - m_hCine->AllowInterrupt( true ); - break; - -#if 0 - case SCRIPT_EVENT_INAIR: // Don't engine->DropToFloor() - case SCRIPT_EVENT_ENDANIMATION: // Set ending animation sequence to - break; -#endif - case SCRIPT_EVENT_BODYGROUPON: - case SCRIPT_EVENT_BODYGROUPOFF: - case SCRIPT_EVENT_BODYGROUPTEMP: - DevMsg( "Bodygroup!\n" ); - break; - - case AE_NPC_ATTACK_BROADCAST: - break; - - case NPC_EVENT_BODYDROP_HEAVY: - if ( GetFlags() & FL_ONGROUND ) - { - EmitSound( "AI_BaseNPC.BodyDrop_Heavy" ); - } - break; - - case NPC_EVENT_BODYDROP_LIGHT: - if ( GetFlags() & FL_ONGROUND ) - { - EmitSound( "AI_BaseNPC.BodyDrop_Light" ); - } - break; - - case NPC_EVENT_SWISHSOUND: - { - // NO NPC may use this anim event unless that npc's precache precaches this sound!!! - EmitSound( "AI_BaseNPC.SwishSound" ); - break; - } - - - case NPC_EVENT_180TURN: - { - //DevMsg( "Turned!\n" ); - SetIdealActivity( ACT_IDLE ); - Forget( bits_MEMORY_TURNING ); - SetBoneController( 0, GetLocalAngles().y ); - AddEffects( EF_NOINTERP ); - break; - } - - case NPC_EVENT_ITEM_PICKUP: - { - CBaseEntity *pPickup = NULL; - - // - // Figure out what we're supposed to pick up. - // - if ( pEvent->options && strlen( pEvent->options ) > 0 ) - { - // Pick up the weapon or item that was specified in the anim event. - pPickup = gEntList.FindEntityGenericNearest( pEvent->options, GetAbsOrigin(), 256, this ); - } - else - { - // Pick up the weapon or item that was found earlier and cached in our target pointer. - pPickup = GetTarget(); - } - - // Make sure we found something to pick up. - if ( !pPickup ) - { - TaskFail("Item no longer available!\n"); - break; - } - - // Make sure the item hasn't moved. - float flDist = ( pPickup->WorldSpaceCenter() - GetAbsOrigin() ).Length2D(); - if ( flDist > ITEM_PICKUP_TOLERANCE ) - { - TaskFail("Item has moved!\n"); - break; - } - - CBaseCombatWeapon *pWeapon = dynamic_cast( pPickup ); - if ( pWeapon ) - { - // Picking up a weapon. - CBaseCombatCharacter *pOwner = pWeapon->GetOwner(); - if ( pOwner ) - { - TaskFail( "Weapon in use by someone else" ); - } - else if ( !pWeapon ) - { - TaskFail( "Weapon doesn't exist" ); - } - else if (!Weapon_CanUse( pWeapon )) - { - TaskFail( "Can't use this weapon type" ); - } - else - { - PickupWeapon( pWeapon ); - TaskComplete(); - break; - } - } - else - { - // Picking up an item. - PickupItem( pPickup ); - TaskComplete(); - } - - break; - } - - case NPC_EVENT_WEAPON_SET_SEQUENCE_NUMBER: - { - CBaseCombatWeapon *pWeapon = GetActiveWeapon(); - if ((pWeapon) && (pEvent->options)) - { - int nSequence = atoi(pEvent->options); - if (nSequence != -1) - { - pWeapon->ResetSequence(nSequence); - } - } - break; - } - - case NPC_EVENT_WEAPON_SET_SEQUENCE_NAME: - { - CBaseCombatWeapon *pWeapon = GetActiveWeapon(); - if ((pWeapon) && (pEvent->options)) - { - int nSequence = pWeapon->LookupSequence(pEvent->options); - if (nSequence != -1) - { - pWeapon->ResetSequence(nSequence); - } - } - break; - } - - case NPC_EVENT_WEAPON_SET_ACTIVITY: - { - CBaseCombatWeapon *pWeapon = GetActiveWeapon(); - if ((pWeapon) && (pEvent->options)) - { - Activity act = (Activity)pWeapon->LookupActivity(pEvent->options); - if (act != ACT_INVALID) - { - // FIXME: where should the duration come from? normally it would come from the current sequence - Weapon_SetActivity(act, 0); - } - } - break; - } - - case NPC_EVENT_WEAPON_DROP: - { - // - // Drop our active weapon (or throw it at the specified target entity). - // - CBaseEntity *pTarget = NULL; - if (pEvent->options) - { - pTarget = gEntList.FindEntityGeneric(NULL, pEvent->options, this); - } - - if (pTarget) - { - Vector vecTargetPos = pTarget->WorldSpaceCenter(); - Weapon_Drop(GetActiveWeapon(), &vecTargetPos); - } - else - { - Weapon_Drop(GetActiveWeapon()); - } - - break; - } - - case EVENT_WEAPON_RELOAD: - { - if ( GetActiveWeapon() ) - { - GetActiveWeapon()->WeaponSound( RELOAD_NPC ); - GetActiveWeapon()->m_iClip1 = GetActiveWeapon()->GetMaxClip1(); - ClearCondition(COND_LOW_PRIMARY_AMMO); - ClearCondition(COND_NO_PRIMARY_AMMO); - ClearCondition(COND_NO_SECONDARY_AMMO); - } - break; - } - - case EVENT_WEAPON_RELOAD_SOUND: - { - if ( GetActiveWeapon() ) - { - GetActiveWeapon()->WeaponSound( RELOAD_NPC ); - } - break; - } - - case EVENT_WEAPON_RELOAD_FILL_CLIP: - { - if ( GetActiveWeapon() ) - { - GetActiveWeapon()->m_iClip1 = GetActiveWeapon()->GetMaxClip1(); - ClearCondition(COND_LOW_PRIMARY_AMMO); - ClearCondition(COND_NO_PRIMARY_AMMO); - ClearCondition(COND_NO_SECONDARY_AMMO); - } - break; - } - - case NPC_EVENT_LEFTFOOT: - case NPC_EVENT_RIGHTFOOT: - // For right now, do nothing. All functionality for this lives in individual npcs. - break; - - case NPC_EVENT_OPEN_DOOR: - { - CBasePropDoor *pDoor = (CBasePropDoor *)(CBaseEntity *)GetNavigator()->GetPath()->GetCurWaypoint()->GetEHandleData(); - if (pDoor != NULL) - { - OpenPropDoorNow( pDoor ); - } - - break; - } - - default: - if ((pEvent->type & AE_TYPE_NEWEVENTSYSTEM) && (pEvent->type & AE_TYPE_SERVER)) - { - if (pEvent->event == AE_NPC_HOLSTER) - { - // Cache off the weapon. - CBaseCombatWeapon *pWeapon = GetActiveWeapon(); - - Assert( pWeapon != NULL ); - - GetActiveWeapon()->Holster(); - SetActiveWeapon( NULL ); - - //Force the NPC to recalculate it's arrival activity since it'll most likely be wrong now that we don't have a weapon out. - GetNavigator()->SetArrivalSequence( ACT_INVALID ); - - if ( m_iDesiredWeaponState == DESIREDWEAPONSTATE_CHANGING_DESTROY ) - { - // Get rid of it! - UTIL_Remove( pWeapon ); - } - - if ( m_iDesiredWeaponState != DESIREDWEAPONSTATE_IGNORE ) - { - m_iDesiredWeaponState = DESIREDWEAPONSTATE_IGNORE; - m_Activity = ACT_RESET; - } - - return; - } - else if (pEvent->event == AE_NPC_DRAW) - { - if (GetActiveWeapon()) - { - GetActiveWeapon()->Deploy(); - - //Force the NPC to recalculate it's arrival activity since it'll most likely be wrong now. - GetNavigator()->SetArrivalSequence( ACT_INVALID ); - - if ( m_iDesiredWeaponState != DESIREDWEAPONSTATE_IGNORE ) - { - m_iDesiredWeaponState = DESIREDWEAPONSTATE_IGNORE; - m_Activity = ACT_RESET; - } - } - return; - } - else if ( pEvent->event == AE_NPC_BODYDROP_HEAVY ) - { - if ( GetFlags() & FL_ONGROUND ) - { - EmitSound( "AI_BaseNPC.BodyDrop_Heavy" ); - } - return; - } - else if ( pEvent->event == AE_NPC_LEFTFOOT || pEvent->event == AE_NPC_RIGHTFOOT ) - { - return; - } - else if ( pEvent->event == AE_NPC_RAGDOLL ) - { - // Convert to ragdoll immediately - BecomeRagdollOnClient( vec3_origin ); - return; - } - else if ( pEvent->event == AE_NPC_ADDGESTURE ) - { - Activity act = ( Activity )LookupActivity( pEvent->options ); - if (act != ACT_INVALID) - { - act = TranslateActivity( act ); - if (act != ACT_INVALID) - { - AddGesture( act ); - } - } - return; - } - else if ( pEvent->event == AE_NPC_RESTARTGESTURE ) - { - Activity act = ( Activity )LookupActivity( pEvent->options ); - if (act != ACT_INVALID) - { - act = TranslateActivity( act ); - if (act != ACT_INVALID) - { - RestartGesture( act ); - } - } - return; - } - else if ( pEvent->event == AE_NPC_WEAPON_DROP ) - { - // Drop our active weapon (or throw it at the specified target entity). - CBaseEntity *pTarget = NULL; - if (pEvent->options) - { - pTarget = gEntList.FindEntityGeneric(NULL, pEvent->options, this); - } - - if (pTarget) - { - Vector vecTargetPos = pTarget->WorldSpaceCenter(); - Weapon_Drop(GetActiveWeapon(), &vecTargetPos); - } - else - { - Weapon_Drop(GetActiveWeapon()); - } - return; - } - else if ( pEvent->event == AE_NPC_WEAPON_SET_ACTIVITY ) - { - CBaseCombatWeapon *pWeapon = GetActiveWeapon(); - if ((pWeapon) && (pEvent->options)) - { - Activity act = (Activity)pWeapon->LookupActivity(pEvent->options); - if (act == ACT_INVALID) - { - // Try and translate it - act = Weapon_TranslateActivity( (Activity)CAI_BaseNPC::GetActivityID(pEvent->options), false ); - } - - if (act != ACT_INVALID) - { - // FIXME: where should the duration come from? normally it would come from the current sequence - Weapon_SetActivity(act, 0); - } - } - return; - } - else if ( pEvent->event == AE_NPC_SET_INTERACTION_CANTDIE ) - { - SetInteractionCantDie( (atoi(pEvent->options) != 0) ); - return; - } - else if ( pEvent->event == AE_NPC_HURT_INTERACTION_PARTNER ) - { - // If we're currently interacting with an enemy, hurt them/me - if ( m_hInteractionPartner ) - { - CAI_BaseNPC *pTarget = NULL; - CAI_BaseNPC *pAttacker = NULL; - if ( pEvent->options ) - { - char szEventOptions[128]; - Q_strncpy( szEventOptions, pEvent->options, sizeof(szEventOptions) ); - char *pszParam = strtok( szEventOptions, " " ); - if ( pszParam ) - { - if ( !Q_strncmp( pszParam, "ME", 2 ) ) - { - pTarget = this; - pAttacker = m_hInteractionPartner; - } - else if ( !Q_strncmp( pszParam, "THEM", 4 ) ) - { - pAttacker = this; - pTarget = m_hInteractionPartner; - } - - pszParam = strtok(NULL," "); - if ( pAttacker && pTarget && pszParam ) - { - int iDamage = atoi( pszParam ); - if ( iDamage ) - { - // We've got a target, and damage. Now hurt them. - CTakeDamageInfo info; - info.SetDamage( iDamage ); - info.SetAttacker( pAttacker ); - info.SetInflictor( pAttacker ); - info.SetDamageType( DMG_GENERIC | DMG_PREVENT_PHYSICS_FORCE ); - pTarget->TakeDamage( info ); - return; - } - } - } - } - - // Bad data. Explain how to use this anim event. - const char *pName = EventList_NameForIndex( pEvent->event ); - DevWarning( 1, "Bad %s format. Should be: { AE_NPC_HURT_INTERACTION_PARTNER \" \" }\n", pName ); - return; - } - - DevWarning( "%s received AE_NPC_HURT_INTERACTION_PARTNER anim event, but it's not interacting with anything.\n", GetDebugName() ); - return; - } - } - - // FIXME: why doesn't this code pass unhandled events down to its parent? - // Came from my weapon? - //Adrian I'll clean this up once the old event system is phased out. - if ( pEvent->pSource != this || ( pEvent->type & AE_TYPE_NEWEVENTSYSTEM && pEvent->type & AE_TYPE_WEAPON ) || (pEvent->event >= EVENT_WEAPON && pEvent->event <= EVENT_WEAPON_LAST) ) - { - Weapon_HandleAnimEvent( pEvent ); - } - else - { - BaseClass::HandleAnimEvent( pEvent ); - } - break; - } -} - - -//----------------------------------------------------------------------------- -// Purpose: Override base class to add display of routes -// Input : -// Output : Current text offset from the top -//----------------------------------------------------------------------------- -void CAI_BaseNPC::DrawDebugGeometryOverlays(void) -{ - // Handy for debug - //NDebugOverlay::Cross3D(EyePosition(),Vector(-2,-2,-2),Vector(2,2,2),0,255,0,true); - - // ------------------------------ - // Remove me if requested - // ------------------------------ - if (m_debugOverlays & OVERLAY_NPC_ZAP_BIT) - { - VacateStrategySlot(); - Weapon_Drop( GetActiveWeapon() ); - m_iHealth = 0; - SetThink( &CAI_BaseNPC::SUB_Remove ); - } - - // ------------------------------ - // properly kill an NPC. - // ------------------------------ - if (m_debugOverlays & OVERLAY_NPC_KILL_BIT) - { - CTakeDamageInfo info; - - info.SetDamage( m_iHealth ); - info.SetAttacker( this ); - info.SetInflictor( ( AI_IsSinglePlayer() ) ? (CBaseEntity *)AI_GetSinglePlayer() : (CBaseEntity *)this ); - info.SetDamageType( DMG_GENERIC ); - - m_debugOverlays &= ~OVERLAY_NPC_KILL_BIT; - TakeDamage( info ); - return; - } - - - // ------------------------------ - // Draw route if requested - // ------------------------------ - if ((m_debugOverlays & OVERLAY_NPC_ROUTE_BIT)) - { - GetNavigator()->DrawDebugRouteOverlay(); - if ( IsMoving() ) - { - float yaw = GetMotor()->GetIdealYaw(); - Vector vecYaw = UTIL_YawToVector(yaw); - NDebugOverlay::Line(WorldSpaceCenter(),WorldSpaceCenter() + vecYaw * GetHullWidth() * .5,255,255,255,true,0.0); - } - } - - if (!(CAI_BaseNPC::m_nDebugBits & bits_debugDisableAI) && (IsCurSchedule(SCHED_FORCED_GO) || IsCurSchedule(SCHED_FORCED_GO_RUN))) - { - NDebugOverlay::Box(m_vecLastPosition, Vector(-5,-5,-5),Vector(5,5,5), 255, 0, 255, 0, 0); - NDebugOverlay::HorzArrow( GetAbsOrigin(), m_vecLastPosition, 16, 255, 0, 255, 64, true, 0 ); - } - - // ------------------------------ - // Draw red box around if selected - // ------------------------------ - if ((m_debugOverlays & OVERLAY_NPC_SELECTED_BIT) && !ai_no_select_box.GetBool()) - { - NDebugOverlay::EntityBounds(this, 255, 0, 0, 20, 0); - } - - // ------------------------------ - // Draw nearest node if selected - // ------------------------------ - if ((m_debugOverlays & OVERLAY_NPC_NEAREST_BIT)) - { - int iNodeID = GetPathfinder()->NearestNodeToNPC(); - if (iNodeID != NO_NODE) - { - NDebugOverlay::Box(GetNavigator()->GetNetwork()->AccessNodes()[iNodeID]->GetPosition(GetHullType()), Vector(-10,-10,-10),Vector(10,10,10), 255, 255, 255, 0, 0); - } - } - - // ------------------------------ - // Draw viewcone if selected - // ------------------------------ - if ((m_debugOverlays & OVERLAY_NPC_VIEWCONE_BIT)) - { - float flViewRange = acos(m_flFieldOfView); - Vector vEyeDir = EyeDirection2D( ); - Vector vLeftDir, vRightDir; - float fSin, fCos; - SinCos( flViewRange, &fSin, &fCos ); - - vLeftDir.x = vEyeDir.x * fCos - vEyeDir.y * fSin; - vLeftDir.y = vEyeDir.x * fSin + vEyeDir.y * fCos; - vLeftDir.z = vEyeDir.z; - fSin = sin(-flViewRange); - fCos = cos(-flViewRange); - vRightDir.x = vEyeDir.x * fCos - vEyeDir.y * fSin; - vRightDir.y = vEyeDir.x * fSin + vEyeDir.y * fCos; - vRightDir.z = vEyeDir.z; - - // Visualize it - NDebugOverlay::VertArrow( EyePosition(), EyePosition() + ( vLeftDir * 200 ), 64, 255, 0, 0, 50, false, 0 ); - NDebugOverlay::VertArrow( EyePosition(), EyePosition() + ( vRightDir * 200 ), 64, 255, 0, 0, 50, false, 0 ); - NDebugOverlay::VertArrow( EyePosition(), EyePosition() + ( vEyeDir * 100 ), 8, 0, 255, 0, 50, false, 0 ); - NDebugOverlay::Box(EyePosition(), -Vector(2,2,2), Vector(2,2,2), 0, 255, 0, 128, 0 ); - } - - // ---------------------------------------------- - // Draw the relationships for this NPC to others - // ---------------------------------------------- - if ( m_debugOverlays & OVERLAY_NPC_RELATION_BIT ) - { - // Show the relationships to entities around us - int r = 0; - int g = 0; - int b = 0; - - int nRelationship; - CAI_BaseNPC **ppAIs = g_AI_Manager.AccessAIs(); - - // Rate all NPCs - for ( int i = 0; i < g_AI_Manager.NumAIs(); i++ ) - { - if ( ppAIs[i] == NULL || ppAIs[i] == this ) - continue; - - // Get our relation to the target - nRelationship = IRelationType( ppAIs[i] ); - - // Get the color for the arrow - UTIL_GetDebugColorForRelationship( nRelationship, r, g, b ); - - // Draw an arrow - NDebugOverlay::HorzArrow( GetAbsOrigin(), ppAIs[i]->GetAbsOrigin(), 16, r, g, b, 64, true, 0.0f ); - } - - // Also include all players - for ( int i = 1; i <= gpGlobals->maxClients; i++ ) - { - CBasePlayer *pPlayer = UTIL_PlayerByIndex( i ); - if ( pPlayer == NULL ) - continue; - - // Get our relation to the target - nRelationship = IRelationType( pPlayer ); - - // Get the color for the arrow - UTIL_GetDebugColorForRelationship( nRelationship, r, g, b ); - - // Draw an arrow - NDebugOverlay::HorzArrow( GetAbsOrigin(), pPlayer->GetAbsOrigin(), 16, r, g, b, 64, true, 0.0f ); - } - } - - // ------------------------------ - // Draw enemies if selected - // ------------------------------ - if ((m_debugOverlays & OVERLAY_NPC_ENEMIES_BIT)) - { - AIEnemiesIter_t iter; - for( AI_EnemyInfo_t *eMemory = GetEnemies()->GetFirst(&iter); eMemory != NULL; eMemory = GetEnemies()->GetNext(&iter) ) - { - if (eMemory->hEnemy) - { - CBaseCombatCharacter *npcEnemy = (eMemory->hEnemy)->MyCombatCharacterPointer(); - if (npcEnemy) - { - float r,g,b; - char debugText[255]; - debugText[0] = NULL; - - if (npcEnemy == GetEnemy()) - { - Q_strncat(debugText,"Current Enemy", sizeof( debugText ), COPY_ALL_CHARACTERS ); - } - else if (npcEnemy == GetTarget()) - { - Q_strncat(debugText,"Current Target", sizeof( debugText ), COPY_ALL_CHARACTERS ); - } - else - { - Q_strncat(debugText,"Other Memory", sizeof( debugText ), COPY_ALL_CHARACTERS ); - } - if (IsUnreachable(npcEnemy)) - { - Q_strncat(debugText," (Unreachable)", sizeof( debugText ), COPY_ALL_CHARACTERS ); - } - if (eMemory->bEludedMe) - { - Q_strncat(debugText," (Eluded)", sizeof( debugText ), COPY_ALL_CHARACTERS ); - } - // Unreachable enemy drawn in green - if (IsUnreachable(npcEnemy)) - { - r = 0; - g = 255; - b = 0; - } - // Eluded enemy drawn in blue - else if (eMemory->bEludedMe) - { - r = 0; - g = 0; - b = 255; - } - // Current enemy drawn in red - else if (npcEnemy == GetEnemy()) - { - r = 255; - g = 0; - b = 0; - } - // Current traget drawn in magenta - else if (npcEnemy == GetTarget()) - { - r = 255; - g = 0; - b = 255; - } - // All other enemies drawn in pink - else - { - r = 255; - g = 100; - b = 100; - } - - - Vector drawPos = eMemory->vLastKnownLocation; - NDebugOverlay::Text( drawPos, debugText, false, 0.0 ); - - // If has a line on the player draw cross slightly in front so player can see - if (npcEnemy->IsPlayer() && - (eMemory->vLastKnownLocation - npcEnemy->GetAbsOrigin()).Length()<10 ) - { - Vector vEnemyFacing = npcEnemy->BodyDirection2D( ); - Vector eyePos = npcEnemy->EyePosition() + vEnemyFacing*10.0; - Vector upVec = Vector(0,0,2); - Vector sideVec; - CrossProduct( vEnemyFacing, upVec, sideVec); - NDebugOverlay::Line(eyePos+sideVec+upVec, eyePos-sideVec-upVec, r,g,b, false,0); - NDebugOverlay::Line(eyePos+sideVec-upVec, eyePos-sideVec+upVec, r,g,b, false,0); - - NDebugOverlay::Text( eyePos, debugText, false, 0.0 ); - } - else - { - NDebugOverlay::Cross3D(drawPos,NAI_Hull::Mins(npcEnemy->GetHullType()),NAI_Hull::Maxs(npcEnemy->GetHullType()),r,g,b,false,0); - } - } - } - } - } - - // ---------------------------------------------- - // Draw line to target and enemy entity if exist - // ---------------------------------------------- - if ((m_debugOverlays & OVERLAY_NPC_FOCUS_BIT)) - { - if (GetEnemy() != NULL) - { - NDebugOverlay::Line(EyePosition(),GetEnemy()->EyePosition(),255,0,0,true,0.0); - } - if (GetTarget() != NULL) - { - NDebugOverlay::Line(EyePosition(),GetTarget()->EyePosition(),0,0,255,true,0.0); - } - } - - - GetPathfinder()->DrawDebugGeometryOverlays(m_debugOverlays); - - CBaseEntity::DrawDebugGeometryOverlays(); -} - -//----------------------------------------------------------------------------- -// Purpose: Draw any debug text overlays -// Input : -// Output : Current text offset from the top -//----------------------------------------------------------------------------- -int CAI_BaseNPC::DrawDebugTextOverlays(void) -{ - int text_offset = 0; - - // --------------------- - // Print Baseclass text - // --------------------- - text_offset = BaseClass::DrawDebugTextOverlays(); - - if (m_debugOverlays & OVERLAY_NPC_SQUAD_BIT) - { - // Print health - char tempstr[512]; - Q_snprintf(tempstr,sizeof(tempstr),"Health: %i",m_iHealth); - EntityText(text_offset,tempstr,0); - text_offset++; - - // Print squad name - Q_strncpy(tempstr,"Squad: ",sizeof(tempstr)); - if (m_pSquad) - { - Q_strncat(tempstr,m_pSquad->GetName(),sizeof(tempstr), COPY_ALL_CHARACTERS); - - if( m_pSquad->GetLeader() == this ) - { - Q_strncat(tempstr," (LEADER)",sizeof(tempstr), COPY_ALL_CHARACTERS); - } - - Q_strncat(tempstr,"\n",sizeof(tempstr), COPY_ALL_CHARACTERS); - } - else - { - Q_strncat(tempstr," - \n",sizeof(tempstr), COPY_ALL_CHARACTERS); - } - EntityText(text_offset,tempstr,0); - text_offset++; - - // Print enemy name - Q_strncpy(tempstr,"Enemy: ",sizeof(tempstr)); - if (GetEnemy()) - { - if (GetEnemy()->GetEntityName() != NULL_STRING) - { - Q_strncat(tempstr,STRING(GetEnemy()->GetEntityName()),sizeof(tempstr), COPY_ALL_CHARACTERS); - Q_strncat(tempstr,"\n",sizeof(tempstr), COPY_ALL_CHARACTERS); - } - else - { - Q_strncat(tempstr,STRING(GetEnemy()->m_iClassname),sizeof(tempstr), COPY_ALL_CHARACTERS); - Q_strncat(tempstr,"\n",sizeof(tempstr), COPY_ALL_CHARACTERS); - } - } - else - { - Q_strncat(tempstr," - \n",sizeof(tempstr), COPY_ALL_CHARACTERS); - } - EntityText(text_offset,tempstr,0); - text_offset++; - - // Print slot - Q_snprintf(tempstr,sizeof(tempstr),"Slot: %s \n", - SquadSlotName(m_iMySquadSlot)); - EntityText(text_offset,tempstr,0); - text_offset++; - - } - - if (m_debugOverlays & OVERLAY_TEXT_BIT) - { - char tempstr[512]; - // -------------- - // Print Health - // -------------- - Q_snprintf(tempstr,sizeof(tempstr),"Health: %i (DACC:%1.2f)",m_iHealth, GetDamageAccumulator() ); - EntityText(text_offset,tempstr,0); - text_offset++; - - // -------------- - // Print State - // -------------- - static const char *pStateNames[] = { "None", "Idle", "Alert", "Combat", "Scripted", "PlayDead", "Dead" }; - if ( (int)m_NPCState < ARRAYSIZE(pStateNames) ) - { - Q_snprintf(tempstr,sizeof(tempstr),"Stat: %s, ", pStateNames[m_NPCState] ); - EntityText(text_offset,tempstr,0); - text_offset++; - } - - // ----------------- - // Start Scripting? - // ----------------- - if( IsInAScript() ) - { - Q_snprintf(tempstr,sizeof(tempstr),"STARTSCRIPTING" ); - EntityText(text_offset,tempstr,0); - text_offset++; - } - - // ----------------- - // Print MotionType - // ----------------- - int navTypeIndex = (int)GetNavType() + 1; - static const char *pMoveNames[] = { "None", "Ground", "Jump", "Fly", "Climb" }; - Assert( navTypeIndex >= 0 && navTypeIndex < ARRAYSIZE(pMoveNames) ); - if ( navTypeIndex < ARRAYSIZE(pMoveNames) ) - { - Q_snprintf(tempstr,sizeof(tempstr),"Move: %s, ", pMoveNames[navTypeIndex] ); - EntityText(text_offset,tempstr,0); - text_offset++; - } - - // -------------- - // Print Schedule - // -------------- - if ( GetCurSchedule() ) - { - CAI_BehaviorBase *pBehavior = GetRunningBehavior(); - if ( pBehavior ) - { - Q_snprintf(tempstr,sizeof(tempstr),"Behv: %s, ", pBehavior->GetName() ); - EntityText(text_offset,tempstr,0); - text_offset++; - } - - const char *pName = NULL; - pName = GetCurSchedule()->GetName(); - if ( !pName ) - { - pName = "Unknown"; - } - Q_snprintf(tempstr,sizeof(tempstr),"Schd: %s, ", pName ); - EntityText(text_offset,tempstr,0); - text_offset++; - - if (m_debugOverlays & OVERLAY_NPC_TASK_BIT) - { - for (int i = 0 ; i < GetCurSchedule()->NumTasks(); i++) - { - Q_snprintf(tempstr,sizeof(tempstr),"%s%s%s%s", - ((i==0) ? "Task:":" "), - ((i==GetScheduleCurTaskIndex()) ? "->" :" "), - TaskName(GetCurSchedule()->GetTaskList()[i].iTask), - ((i==GetScheduleCurTaskIndex()) ? "<-" :"")); - - EntityText(text_offset,tempstr,0); - text_offset++; - } - } - else - { - const Task_t *pTask = GetTask(); - if ( pTask ) - { - Q_snprintf(tempstr,sizeof(tempstr),"Task: %s (#%d), ", TaskName(pTask->iTask), GetScheduleCurTaskIndex() ); - } - else - { - Q_strncpy(tempstr,"Task: None",sizeof(tempstr)); - } - EntityText(text_offset,tempstr,0); - text_offset++; - } - } - - // -------------- - // Print Acitivity - // -------------- - if( m_Activity != ACT_INVALID && m_IdealActivity != ACT_INVALID && m_Activity != ACT_RESET) - { - Activity iActivity = TranslateActivity( m_Activity ); - - Activity iIdealActivity = Weapon_TranslateActivity( m_IdealActivity ); - iIdealActivity = NPC_TranslateActivity( iIdealActivity ); - - const char *pszActivity = GetActivityName( iActivity ); - const char *pszIdealActivity = GetActivityName( iIdealActivity ); - const char *pszRootActivity = GetActivityName( m_Activity ); - - Q_snprintf(tempstr,sizeof(tempstr),"Actv: %s (%s) [%s]\n", pszActivity, pszIdealActivity, pszRootActivity ); - } - else if (m_Activity == ACT_RESET) - { - Q_strncpy(tempstr,"Actv: RESET",sizeof(tempstr) ); - } - else - { - Q_strncpy(tempstr,"Actv: INVALID", sizeof(tempstr) ); - } - EntityText(text_offset,tempstr,0); - text_offset++; - - // - // Print all the current conditions. - // - if (m_debugOverlays & OVERLAY_NPC_CONDITIONS_BIT) - { - bool bHasConditions = false; - for (int i = 0; i < MAX_CONDITIONS; i++) - { - if (m_Conditions.GetBit(i)) - { - Q_snprintf(tempstr, sizeof(tempstr), "Cond: %s\n", ConditionName(AI_RemapToGlobal(i))); - EntityText(text_offset, tempstr, 0); - text_offset++; - bHasConditions = true; - } - } - if (!bHasConditions) - { - Q_snprintf(tempstr,sizeof(tempstr),"(no conditions)",m_iHealth); - EntityText(text_offset,tempstr,0); - text_offset++; - } - } - - if ( GetFlags() & FL_FLY ) - { - EntityText(text_offset,"HAS FL_FLY",0); - text_offset++; - } - - // -------------- - // Print Interrupte - // -------------- - if (m_interuptSchedule) - { - const char *pName = NULL; - pName = m_interuptSchedule->GetName(); - if ( !pName ) - { - pName = "Unknown"; - } - - Q_snprintf(tempstr,sizeof(tempstr),"Intr: %s (%s)\n", pName, m_interruptText ); - EntityText(text_offset,tempstr,0); - text_offset++; - } - - // -------------- - // Print Failure - // -------------- - if (m_failedSchedule) - { - const char *pName = NULL; - pName = m_failedSchedule->GetName(); - if ( !pName ) - { - pName = "Unknown"; - } - Q_snprintf(tempstr,sizeof(tempstr),"Fail: %s (%s)\n", pName,m_failText ); - EntityText(text_offset,tempstr,0); - text_offset++; - } - - - // ------------------------------- - // Print any important condtions - // ------------------------------- - if (HasCondition(COND_ENEMY_TOO_FAR)) - { - EntityText(text_offset,"Enemy too far to attack",0); - text_offset++; - } - if ( GetAbsVelocity() != vec3_origin || GetLocalAngularVelocity() != vec3_angle ) - { - char tmp[512]; - Q_snprintf( tmp, sizeof(tmp), "Vel %.1f %.1f %.1f Ang: %.1f %.1f %.1f\n", - GetAbsVelocity().x, GetAbsVelocity().y, GetAbsVelocity().z, - GetLocalAngularVelocity().x, GetLocalAngularVelocity().y, GetLocalAngularVelocity().z ); - EntityText(text_offset,tmp,0); - text_offset++; - } - - // ------------------------------- - // Print shot accuracy - // ------------------------------- - if ( m_LastShootAccuracy != -1 && ai_shot_stats.GetBool() ) - { - CFmtStr msg; - EntityText(text_offset,msg.sprintf("Cur Accuracy: %.1f", m_LastShootAccuracy),0); - text_offset++; - if ( m_TotalShots ) - { - EntityText(text_offset,msg.sprintf("Act Accuracy: %.1f", ((float)m_TotalHits/(float)m_TotalShots)*100.0),0); - text_offset++; - } - - if ( GetActiveWeapon() && GetEnemy() ) - { - Vector curSpread = GetAttackSpread(GetActiveWeapon(), GetEnemy()); - float curCone = RAD2DEG(asin(curSpread.x)) * 2; - float bias = GetSpreadBias( GetActiveWeapon(), GetEnemy()); - EntityText(text_offset,msg.sprintf("Cone %.1f, Bias %.2f", curCone, bias),0); - text_offset++; - } - } - - if ( GetGoalEnt() && GetNavigator()->GetGoalType() == GOALTYPE_PATHCORNER ) - { - Q_strncpy(tempstr,"Pathcorner/goal ent: ",sizeof(tempstr)); - if (GetGoalEnt()->GetEntityName() != NULL_STRING) - { - Q_strncat(tempstr,STRING(GetGoalEnt()->GetEntityName()),sizeof(tempstr), COPY_ALL_CHARACTERS); - } - else - { - Q_strncat(tempstr,STRING(GetGoalEnt()->m_iClassname),sizeof(tempstr), COPY_ALL_CHARACTERS); - } - EntityText(text_offset, tempstr, 0); - text_offset++; - } - - if ( VPhysicsGetObject() ) - { - vphysics_objectstress_t stressOut; - CalculateObjectStress( VPhysicsGetObject(), this, &stressOut ); - Q_snprintf(tempstr, sizeof(tempstr),"Stress: %.2f", stressOut.receivedStress ); - EntityText(text_offset, tempstr, 0); - text_offset++; - } - if ( m_pSquad ) - { - if( m_pSquad->IsLeader(this) ) - { - Q_snprintf(tempstr, sizeof(tempstr),"**Squad Leader**" ); - EntityText(text_offset, tempstr, 0); - text_offset++; - } - - Q_snprintf(tempstr, sizeof(tempstr), "SquadSlot:%s", GetSquadSlotDebugName( GetMyStrategySlot() ) ); - EntityText(text_offset, tempstr, 0); - text_offset++; - } - } - return text_offset; -} - - -//----------------------------------------------------------------------------- -// Purpose: Displays information in the console about the state of this npc. -//----------------------------------------------------------------------------- -void CAI_BaseNPC::ReportAIState( void ) -{ - static const char *pStateNames[] = { "None", "Idle", "Alert", "Combat", "Scripted", "PlayDead", "Dead" }; - - DevMsg( "%s: ", GetClassname() ); - if ( (int)m_NPCState < ARRAYSIZE(pStateNames) ) - DevMsg( "State: %s, ", pStateNames[m_NPCState] ); - - if( m_Activity != ACT_INVALID && m_IdealActivity != ACT_INVALID ) - { - const char *pszActivity = GetActivityName(m_Activity); - const char *pszIdealActivity = GetActivityName(m_IdealActivity); - - DevMsg( "Activity: %s - Ideal Activity: %s\n", pszActivity, pszIdealActivity ); - } - - if ( GetCurSchedule() ) - { - const char *pName = NULL; - pName = GetCurSchedule()->GetName(); - if ( !pName ) - pName = "Unknown"; - DevMsg( "Schedule %s, ", pName ); - const Task_t *pTask = GetTask(); - if ( pTask ) - DevMsg( "Task %d (#%d), ", pTask->iTask, GetScheduleCurTaskIndex() ); - } - else - DevMsg( "No Schedule, " ); - - if ( GetEnemy() != NULL ) - { - g_pEffects->Sparks( GetEnemy()->GetAbsOrigin() + Vector( 0, 0, 64 ) ); - DevMsg( "\nEnemy is %s", GetEnemy()->GetClassname() ); - } - else - DevMsg( "No enemy " ); - - if ( IsMoving() ) - { - DevMsg( " Moving " ); - if ( m_flMoveWaitFinished > gpGlobals->curtime ) - DevMsg( ": Stopped for %.2f. ", m_flMoveWaitFinished - gpGlobals->curtime ); - else if ( m_IdealActivity == GetStoppedActivity() ) - DevMsg( ": In stopped anim. " ); - } - - DevMsg( "Leader." ); - - DevMsg( "\n" ); - DevMsg( "Yaw speed:%3.1f,Health: %3d\n", GetMotor()->GetYawSpeed(), m_iHealth ); - - if ( GetGroundEntity() ) - { - DevMsg( "Groundent:%s\n\n", GetGroundEntity()->GetClassname() ); - } - else - { - DevMsg( "Groundent: NULL\n\n" ); - } -} - -//----------------------------------------------------------------------------- - -ConVar ai_report_task_timings_on_limit( "ai_report_task_timings_on_limit", "0", FCVAR_ARCHIVE ); -ConVar ai_think_limit_label( "ai_think_limit_label", "0", FCVAR_ARCHIVE ); - -void CAI_BaseNPC::ReportOverThinkLimit( float time ) -{ - DevMsg( "%s thinking for %.02fms!!! (%s); r%.2f (c%.2f, pst%.2f, ms%.2f), p-r%.2f, m%.2f\n", - GetDebugName(), time, GetCurSchedule()->GetName(), - g_AIRunTimer.GetDuration().GetMillisecondsF(), - g_AIConditionsTimer.GetDuration().GetMillisecondsF(), - g_AIPrescheduleThinkTimer.GetDuration().GetMillisecondsF(), - g_AIMaintainScheduleTimer.GetDuration().GetMillisecondsF(), - g_AIPostRunTimer.GetDuration().GetMillisecondsF(), - g_AIMoveTimer.GetDuration().GetMillisecondsF() ); - - if (ai_think_limit_label.GetBool()) - { - Vector tmp; - CollisionProp()->NormalizedToWorldSpace( Vector( 0.5f, 0.5f, 1.0f ), &tmp ); - tmp.z += 16; - - float max = -1; - const char *pszMax = "unknown"; - - if ( g_AIConditionsTimer.GetDuration().GetMillisecondsF() > max ) - { - max = g_AIConditionsTimer.GetDuration().GetMillisecondsF(); - pszMax = "Conditions"; - } - if ( g_AIPrescheduleThinkTimer.GetDuration().GetMillisecondsF() > max ) - { - max = g_AIPrescheduleThinkTimer.GetDuration().GetMillisecondsF(); - pszMax = "Pre-think"; - } - if ( g_AIMaintainScheduleTimer.GetDuration().GetMillisecondsF() > max ) - { - max = g_AIMaintainScheduleTimer.GetDuration().GetMillisecondsF(); - pszMax = "Schedule"; - } - if ( g_AIPostRunTimer.GetDuration().GetMillisecondsF() > max ) - { - max = g_AIPostRunTimer.GetDuration().GetMillisecondsF(); - pszMax = "Post-run"; - } - if ( g_AIMoveTimer.GetDuration().GetMillisecondsF() > max ) - { - max = g_AIMoveTimer.GetDuration().GetMillisecondsF(); - pszMax = "Move"; - } - NDebugOverlay::Text( tmp, (char*)(const char *)CFmtStr( "Slow %.1f, %s %.1f ", time, pszMax, max ), false, 1 ); - } - - if ( ai_report_task_timings_on_limit.GetBool() ) - DumpTaskTimings(); -} - -//----------------------------------------------------------------------------- -// Purpose: Returns whether or not this npc can play the scripted sequence or AI -// sequence that is trying to possess it. If DisregardState is set, the npc -// will be sucked into the script no matter what state it is in. ONLY -// Scripted AI ents should allow this. -// Input : fDisregardNPCState - -// interruptLevel - -// eMode - If the function returns true, eMode will be one of the following values: -// CAN_PLAY_NOW -// CAN_PLAY_ENQUEUED -// Output : -//----------------------------------------------------------------------------- -CanPlaySequence_t CAI_BaseNPC::CanPlaySequence( bool fDisregardNPCState, int interruptLevel ) -{ - CanPlaySequence_t eReturn = CAN_PLAY_NOW; - - if ( m_hCine ) - { - if ( !m_hCine->CanEnqueueAfter() ) - { - // npc is already running one scripted sequence and has an important script to play next - return CANNOT_PLAY; - } - - eReturn = CAN_PLAY_ENQUEUED; - } - - if ( !IsAlive() ) - { - // npc is dead! - return CANNOT_PLAY; - } - - if ( fDisregardNPCState ) - { - // ok to go, no matter what the npc state. (scripted AI) - - // No clue as to how to proced if they're climbing or jumping - // Assert( GetNavType() != NAV_JUMP && GetNavType() != NAV_CLIMB ); - - return eReturn; - } - - if ( m_NPCState == NPC_STATE_NONE || m_NPCState == NPC_STATE_IDLE || m_IdealNPCState == NPC_STATE_IDLE ) - { - // ok to go, but only in these states - return eReturn; - } - - if ( m_NPCState == NPC_STATE_ALERT && interruptLevel >= SS_INTERRUPT_BY_NAME ) - { - return eReturn; - } - - // unknown situation - return CANNOT_PLAY; -} - - -//----------------------------------------------------------------------------- - -void CAI_BaseNPC::SetHintGroup( string_t newGroup, bool bHintGroupNavLimiting ) -{ - string_t oldGroup = m_strHintGroup; - m_strHintGroup = newGroup; - m_bHintGroupNavLimiting = bHintGroupNavLimiting; - - if ( oldGroup != newGroup ) - OnChangeHintGroup( oldGroup, newGroup ); - -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : -// Output : -//----------------------------------------------------------------------------- -Vector CAI_BaseNPC::GetShootEnemyDir( const Vector &shootOrigin, bool bNoisy ) -{ - CBaseEntity *pEnemy = GetEnemy(); - - if ( pEnemy ) - { - Vector vecEnemyLKP = GetEnemyLKP(); - Vector retval = (pEnemy->BodyTarget( shootOrigin, bNoisy ) - pEnemy->GetAbsOrigin()) + vecEnemyLKP - shootOrigin; - VectorNormalize( retval ); - return retval; - } - else - { - Vector forward; - AngleVectors( GetLocalAngles(), &forward ); - return forward; - } -} - -//----------------------------------------------------------------------------- -// Simulates many times and reports statistical accuracy. -//----------------------------------------------------------------------------- -void CAI_BaseNPC::CollectShotStats( const Vector &vecShootOrigin, const Vector &vecShootDir ) -{ -#ifdef HL2_DLL - if( ai_shot_stats.GetBool() != 0 && GetEnemy()->IsPlayer() ) - { - int iterations = ai_shot_stats_term.GetInt(); - int iHits = 0; - Vector testDir = vecShootDir; - - CShotManipulator manipulator( testDir ); - - for( int i = 0 ; i < iterations ; i++ ) - { - // Apply appropriate accuracy. - manipulator.ApplySpread( GetAttackSpread( GetActiveWeapon(), GetEnemy() ), GetSpreadBias( GetActiveWeapon(), GetEnemy() ) ); - Vector shotDir = manipulator.GetResult(); - - Vector vecEnd = vecShootOrigin + shotDir * 8192; - - trace_t tr; - AI_TraceLine( vecShootOrigin, vecEnd, MASK_SHOT, this, COLLISION_GROUP_NONE, &tr); - - if( tr.m_pEnt && tr.m_pEnt == GetEnemy() ) - { - iHits++; - } - Vector vecProjectedPosition = GetActualShootPosition( vecShootOrigin ); - Vector testDir = vecProjectedPosition - vecShootOrigin; - VectorNormalize( testDir ); - manipulator.SetShootDir( testDir ); - } - - float flHitPercent = ((float)iHits / (float)iterations) * 100.0; - m_LastShootAccuracy = flHitPercent; - //DevMsg("Shots:%d Hits:%d Percentage:%.1f\n", iterations, iHits, flHitPercent); - } - else - { - m_LastShootAccuracy = -1; - } -#endif -} - -#ifdef HL2_DLL -//----------------------------------------------------------------------------- -// Purpose: Return the actual position the NPC wants to fire at when it's trying -// to hit it's current enemy. -//----------------------------------------------------------------------------- -Vector CAI_BaseNPC::GetActualShootPosition( const Vector &shootOrigin ) -{ - // Project the target's location into the future. - Vector vecEnemyLKP = GetEnemyLKP(); - Vector vecTargetPosition = (GetEnemy()->BodyTarget( shootOrigin ) - GetEnemy()->GetAbsOrigin()) + vecEnemyLKP; - - // lead for some fraction of a second. - return (vecTargetPosition + ( GetEnemy()->GetSmoothedVelocity() * ai_lead_time.GetFloat() )); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -float CAI_BaseNPC::GetSpreadBias( CBaseCombatWeapon *pWeapon, CBaseEntity *pTarget ) -{ - float bias = BaseClass::GetSpreadBias( pWeapon, pTarget ); - AI_EnemyInfo_t *pEnemyInfo = m_pEnemies->Find( pTarget ); - if ( ai_shot_bias.GetFloat() != 1.0 ) - bias = ai_shot_bias.GetFloat(); - if ( pEnemyInfo ) - { - float timeToFocus = ai_spread_pattern_focus_time.GetFloat(); - if ( timeToFocus > 0.0 ) - { - float timeSinceValidEnemy = gpGlobals->curtime - pEnemyInfo->timeValidEnemy; - if ( timeSinceValidEnemy < 0.0f ) - { - timeSinceValidEnemy = 0.0f; - } - float timeSinceReacquire = gpGlobals->curtime - pEnemyInfo->timeLastReacquired; - if ( timeSinceValidEnemy < timeToFocus ) - { - float scale = timeSinceValidEnemy / timeToFocus; - Assert( scale >= 0.0 && scale <= 1.0 ); - bias *= scale; - } - else if ( timeSinceReacquire < timeToFocus ) // handled seperately as might be tuning seperately - { - float scale = timeSinceReacquire / timeToFocus; - Assert( scale >= 0.0 && scale <= 1.0 ); - bias *= scale; - } - - } - } - return bias; -} - -//----------------------------------------------------------------------------- -Vector CAI_BaseNPC::GetAttackSpread( CBaseCombatWeapon *pWeapon, CBaseEntity *pTarget ) -{ - Vector baseResult = BaseClass::GetAttackSpread( pWeapon, pTarget ); - - AI_EnemyInfo_t *pEnemyInfo = m_pEnemies->Find( pTarget ); - if ( pEnemyInfo ) - { - float timeToFocus = ai_spread_cone_focus_time.GetFloat(); - if ( timeToFocus > 0.0 ) - { - float timeSinceValidEnemy = gpGlobals->curtime - pEnemyInfo->timeValidEnemy; - if ( timeSinceValidEnemy < 0 ) - timeSinceValidEnemy = 0; - if ( timeSinceValidEnemy < timeToFocus ) - { - float coneMultiplier = ai_spread_defocused_cone_multiplier.GetFloat(); - if ( coneMultiplier > 1.0 ) - { - float scale = 1.0 - timeSinceValidEnemy / timeToFocus; - Assert( scale >= 0.0 && scale <= 1.0 ); - float multiplier = ( (coneMultiplier - 1.0) * scale ) + 1.0; - baseResult *= multiplier; - } - } - } - } - return baseResult; -} - -//----------------------------------------------------------------------------- -// Similar to calling GetShootEnemyDir, but returns the exact trajectory to -// fire the bullet along, after calculating for target speed, location, -// concealment, etc. -// -// Ultimately, this code results in the shooter aiming his weapon somewhere ahead of -// a moving target. Since bullet traces are instant, aiming ahead of a target -// will result in more misses than hits. This is designed to provide targets with -// a bonus when moving perpendicular to the shooter's line of sight. -// -// Do not confuse this with leading a target in an effort to more easily hit it. -// -// This code PENALIZES a target for moving directly along the shooter's line of sight. -// -// This code REWARDS a target for moving perpendicular to the shooter's line of sight. -//----------------------------------------------------------------------------- -Vector CAI_BaseNPC::GetActualShootTrajectory( const Vector &shootOrigin ) -{ - if( !GetEnemy() ) - return GetShootEnemyDir( shootOrigin ); - - // If we're above water shooting at a player underwater, bias some of the shots forward of - // the player so that they see the cool bubble trails in the water ahead of them. - if (GetEnemy()->IsPlayer() && (GetWaterLevel() != 3) && (GetEnemy()->GetWaterLevel() == 3)) - { -#if 1 - if (random->RandomInt(0, 4) < 3) - { - Vector vecEnemyForward; - GetEnemy()->GetVectors( &vecEnemyForward, NULL, NULL ); - vecEnemyForward.z = 0; - - // Lead up to a second ahead of them unless they are moving backwards. - Vector vecEnemyVelocity = GetEnemy()->GetSmoothedVelocity(); - VectorNormalize( vecEnemyVelocity ); - float flVelocityScale = vecEnemyForward.Dot( vecEnemyVelocity ); - if ( flVelocityScale < 0.0f ) - { - flVelocityScale = 0.0f; - } - - Vector vecAimPos = GetEnemy()->EyePosition() + ( 48.0f * vecEnemyForward ) + (flVelocityScale * GetEnemy()->GetSmoothedVelocity() ); - //NDebugOverlay::Cross3D(vecAimPos, Vector(-16,-16,-16), Vector(16,16,16), 255, 255, 0, true, 1.0f ); - - //vecAimPos.z = UTIL_WaterLevel( vecAimPos, vecAimPos.z, vecAimPos.z + 400.0f ); - //NDebugOverlay::Cross3D(vecAimPos, Vector(-32,-32,-32), Vector(32,32,32), 255, 0, 0, true, 1.0f ); - - Vector vecShotDir = vecAimPos - shootOrigin; - VectorNormalize( vecShotDir ); - return vecShotDir; - } -#else - if (random->RandomInt(0, 4) < 3) - { - // Aim at a point a few feet in front of the player's eyes - Vector vecEnemyForward; - GetEnemy()->GetVectors( &vecEnemyForward, NULL, NULL ); - - Vector vecAimPos = GetEnemy()->EyePosition() + (120.0f * vecEnemyForward ); - - Vector vecShotDir = vecAimPos - shootOrigin; - VectorNormalize( vecShotDir ); - - CShotManipulator manipulator( vecShotDir ); - manipulator.ApplySpread( VECTOR_CONE_10DEGREES, 1 ); - vecShotDir = manipulator.GetResult(); - - return vecShotDir; - } -#endif - } - - Vector vecProjectedPosition = GetActualShootPosition( shootOrigin ); - - Vector shotDir = vecProjectedPosition - shootOrigin; - VectorNormalize( shotDir ); - - CollectShotStats( shootOrigin, shotDir ); - - // NOW we have a shoot direction. Where a 100% accurate bullet should go. - // Modify it by weapon proficiency. - // construct a manipulator - CShotManipulator manipulator( shotDir ); - - // Apply appropriate accuracy. - bool bUsePerfectAccuracy = false; - if ( GetEnemy() && GetEnemy()->Classify() == CLASS_BULLSEYE ) - { - CNPC_Bullseye *pBullseye = dynamic_cast(GetEnemy()); - if ( pBullseye && pBullseye->UsePerfectAccuracy() ) - { - bUsePerfectAccuracy = true; - } - } - - if ( !bUsePerfectAccuracy ) - { - manipulator.ApplySpread( GetAttackSpread( GetActiveWeapon(), GetEnemy() ), GetSpreadBias( GetActiveWeapon(), GetEnemy() ) ); - shotDir = manipulator.GetResult(); - } - - // Look for an opportunity to make misses hit interesting things. - CBaseCombatCharacter *pEnemy; - - pEnemy = GetEnemy()->MyCombatCharacterPointer(); - - if( pEnemy && pEnemy->ShouldShootMissTarget( this ) ) - { - Vector vecEnd = shootOrigin + shotDir * 8192; - trace_t tr; - - AI_TraceLine(shootOrigin, vecEnd, MASK_SHOT, this, COLLISION_GROUP_NONE, &tr); - - if( tr.fraction != 1.0 && tr.m_pEnt && tr.m_pEnt->m_takedamage != DAMAGE_NO ) - { - // Hit something we can harm. Just shoot it. - return manipulator.GetResult(); - } - - // Find something interesting around the enemy to shoot instead of just missing. - CBaseEntity *pMissTarget = pEnemy->FindMissTarget(); - - if( pMissTarget ) - { - shotDir = pMissTarget->WorldSpaceCenter() - shootOrigin; - VectorNormalize( shotDir ); - } - } - - return shotDir; -} -#endif HL2_DLL - -//----------------------------------------------------------------------------- - -Vector CAI_BaseNPC::BodyTarget( const Vector &posSrc, bool bNoisy ) -{ - Vector low = WorldSpaceCenter() - ( WorldSpaceCenter() - GetAbsOrigin() ) * .25; - Vector high = EyePosition(); - Vector delta = high - low; - Vector result; - if ( bNoisy ) - { - // bell curve - float rand1 = random->RandomFloat( 0.0, 0.5 ); - float rand2 = random->RandomFloat( 0.0, 0.5 ); - result = low + delta * rand1 + delta * rand2; - } - else - result = low + delta * 0.5; - - return result; -} - -//----------------------------------------------------------------------------- - -bool CAI_BaseNPC::ShouldMoveAndShoot() -{ - return ( ( CapabilitiesGet() & bits_CAP_MOVE_SHOOT ) != 0 ); -} - - -//========================================================= -// FacingIdeal - tells us if a npc is facing its ideal -// yaw. Created this function because many spots in the -// code were checking the yawdiff against this magic -// number. Nicer to have it in one place if we're gonna -// be stuck with it. -//========================================================= -bool CAI_BaseNPC::FacingIdeal( void ) -{ - if ( fabs( GetMotor()->DeltaIdealYaw() ) <= 0.006 )//!!!BUGBUG - no magic numbers!!! - { - return true; - } - - return false; -} - -//--------------------------------- - -void CAI_BaseNPC::AddFacingTarget( CBaseEntity *pTarget, float flImportance, float flDuration, float flRamp ) -{ - GetMotor()->AddFacingTarget( pTarget, flImportance, flDuration, flRamp ); -} - -void CAI_BaseNPC::AddFacingTarget( const Vector &vecPosition, float flImportance, float flDuration, float flRamp ) -{ - GetMotor()->AddFacingTarget( vecPosition, flImportance, flDuration, flRamp ); -} - -void CAI_BaseNPC::AddFacingTarget( CBaseEntity *pTarget, const Vector &vecPosition, float flImportance, float flDuration, float flRamp ) -{ - GetMotor()->AddFacingTarget( pTarget, vecPosition, flImportance, flDuration, flRamp ); -} - -float CAI_BaseNPC::GetFacingDirection( Vector &vecDir ) -{ - return (GetMotor()->GetFacingDirection( vecDir )); -} - - -//--------------------------------- - - -int CAI_BaseNPC::PlaySentence( const char *pszSentence, float delay, float volume, soundlevel_t soundlevel, CBaseEntity *pListener ) -{ - int sentenceIndex = -1; - - if ( pszSentence && IsAlive() ) - { - - if ( pszSentence[0] == '!' ) - { - sentenceIndex = SENTENCEG_Lookup( pszSentence ); - CPASAttenuationFilter filter( this, soundlevel ); - CBaseEntity::EmitSentenceByIndex( filter, entindex(), CHAN_VOICE, sentenceIndex, volume, soundlevel, 0, PITCH_NORM ); - } - else - { - sentenceIndex = SENTENCEG_PlayRndSz( edict(), pszSentence, volume, soundlevel, 0, PITCH_NORM ); - } - } - - return sentenceIndex; -} - - -int CAI_BaseNPC::PlayScriptedSentence( const char *pszSentence, float delay, float volume, soundlevel_t soundlevel, bool bConcurrent, CBaseEntity *pListener ) -{ - return PlaySentence( pszSentence, delay, volume, soundlevel, pListener ); -} - - -void CAI_BaseNPC::SentenceStop( void ) -{ - EmitSound( "AI_BaseNPC.SentenceStop" ); -} - - - - -//----------------------------------------------------------------------------- -// Purpose: Play a one-shot scene -// Input : -// Output : -//----------------------------------------------------------------------------- -float CAI_BaseNPC::PlayScene( const char *pszScene, float flDelay, AI_Response *response ) -{ - return InstancedScriptedScene( this, pszScene, NULL, flDelay, false, response ); -} - -//----------------------------------------------------------------------------- -// Purpose: Generate a one-shot scene in memory with one track which is to play the named sound on the actor -// Input : *soundname - -// Output : float -//----------------------------------------------------------------------------- -float CAI_BaseNPC::PlayAutoGeneratedSoundScene( const char *soundname ) -{ - return InstancedAutoGeneratedSoundScene( this, soundname ); -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : -// Output : -//----------------------------------------------------------------------------- -CBaseEntity *CAI_BaseNPC::FindNamedEntity( const char *name, IEntityFindFilter *pFilter ) -{ - if ( !stricmp( name, "!player" )) - { - return ( CBaseEntity * )AI_GetSinglePlayer(); - } - else if ( !stricmp( name, "!enemy" ) ) - { - if (GetEnemy() != NULL) - return GetEnemy(); - } - else if ( !stricmp( name, "!self" ) || !stricmp( name, "!target1" ) ) - { - return this; - } - else if ( !stricmp( name, "!nearestfriend" ) || !stricmp( name, "!friend" ) ) - { - // FIXME: look at CBaseEntity *CNPCSimpleTalker::FindNearestFriend(bool fPlayer) - // punt for now - return ( CBaseEntity * )AI_GetSinglePlayer(); - } - else if (!stricmp( name, "self" )) - { - static int selfwarningcount = 0; - - // fix the vcd, the reserved names have changed - if ( ++selfwarningcount < 5 ) - { - DevMsg( "ERROR: \"self\" is no longer used, use \"!self\" in vcd instead!\n" ); - } - return this; - } - else if ( !stricmp( name, "Player" )) - { - static int playerwarningcount = 0; - if ( ++playerwarningcount < 5 ) - { - DevMsg( "ERROR: \"player\" is no longer used, use \"!player\" in vcd instead!\n" ); - } - return ( CBaseEntity * )AI_GetSinglePlayer(); - } - else - { - // search for up to 32 entities with the same name and choose one randomly - CBaseEntity *entityList[ FINDNAMEDENTITY_MAX_ENTITIES ]; - CBaseEntity *entity; - int iCount; - - entity = NULL; - for( iCount = 0; iCount < FINDNAMEDENTITY_MAX_ENTITIES; iCount++ ) - { - entity = gEntList.FindEntityByName( entity, name, NULL, NULL, NULL, pFilter ); - if ( !entity ) - { - break; - } - entityList[ iCount ] = entity; - } - - if ( iCount > 0 ) - { - int index = RandomInt( 0, iCount - 1 ); - entity = entityList[ index ]; - return entity; - } - } - - return NULL; -} - - -void CAI_BaseNPC::CorpseFallThink( void ) -{ - if ( GetFlags() & FL_ONGROUND ) - { - SetThink ( NULL ); - - SetSequenceBox( ); - } - else - { - SetNextThink( gpGlobals->curtime + 0.1f ); - } -} - -// Call after animation/pose is set up -void CAI_BaseNPC::NPCInitDead( void ) -{ - InitBoneControllers(); - - RemoveSolidFlags( FSOLID_NOT_SOLID ); - - // so he'll fall to ground - SetMoveType( MOVETYPE_FLYGRAVITY, MOVECOLLIDE_FLY_BOUNCE ); - - SetCycle( 0 ); - ResetSequenceInfo( ); - m_flPlaybackRate = 0; - - // Copy health - m_iMaxHealth = m_iHealth; - m_lifeState = LIFE_DEAD; - - UTIL_SetSize(this, vec3_origin, vec3_origin ); - - // Setup health counters, etc. - SetThink( &CAI_BaseNPC::CorpseFallThink ); - - SetNextThink( gpGlobals->curtime + 0.5f ); -} - -//========================================================= -// BBoxIsFlat - check to see if the npc's bounding box -// is lying flat on a surface (traces from all four corners -// are same length.) -//========================================================= -bool CAI_BaseNPC::BBoxFlat ( void ) -{ - trace_t tr; - Vector vecPoint; - float flXSize, flYSize; - float flLength; - float flLength2; - - flXSize = WorldAlignSize().x / 2; - flYSize = WorldAlignSize().y / 2; - - vecPoint.x = GetAbsOrigin().x + flXSize; - vecPoint.y = GetAbsOrigin().y + flYSize; - vecPoint.z = GetAbsOrigin().z; - - AI_TraceLine ( vecPoint, vecPoint - Vector ( 0, 0, 100 ), MASK_NPCSOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr ); - flLength = (vecPoint - tr.endpos).Length(); - - vecPoint.x = GetAbsOrigin().x - flXSize; - vecPoint.y = GetAbsOrigin().y - flYSize; - - AI_TraceLine ( vecPoint, vecPoint - Vector ( 0, 0, 100 ), MASK_NPCSOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr ); - flLength2 = (vecPoint - tr.endpos).Length(); - if ( flLength2 > flLength ) - { - return false; - } - flLength = flLength2; - - vecPoint.x = GetAbsOrigin().x - flXSize; - vecPoint.y = GetAbsOrigin().y + flYSize; - AI_TraceLine ( vecPoint, vecPoint - Vector ( 0, 0, 100 ), MASK_NPCSOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr ); - flLength2 = (vecPoint - tr.endpos).Length(); - if ( flLength2 > flLength ) - { - return false; - } - flLength = flLength2; - - vecPoint.x = GetAbsOrigin().x + flXSize; - vecPoint.y = GetAbsOrigin().y - flYSize; - AI_TraceLine ( vecPoint, vecPoint - Vector ( 0, 0, 100 ), MASK_NPCSOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr ); - flLength2 = (vecPoint - tr.endpos).Length(); - if ( flLength2 > flLength ) - { - return false; - } - flLength = flLength2; - - return true; -} - - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *pEnemy - -// bSetCondNewEnemy - -//----------------------------------------------------------------------------- -void CAI_BaseNPC::SetEnemy( CBaseEntity *pEnemy, bool bSetCondNewEnemy ) -{ - if (m_hEnemy != pEnemy) - { - ClearAttackConditions( ); - VacateStrategySlot(); - m_GiveUpOnDeadEnemyTimer.Stop(); - - // If we've just found a new enemy, set the condition - if ( pEnemy && bSetCondNewEnemy ) - { - SetCondition( COND_NEW_ENEMY ); - } - } - - // Assert( (pEnemy == NULL) || (m_NPCState == NPC_STATE_COMBAT) ); - - m_hEnemy = pEnemy; - m_flTimeEnemyAcquired = gpGlobals->curtime; - - m_LastShootAccuracy = -1; - m_TotalShots = 0; - m_TotalHits = 0; - - if ( !pEnemy ) - ClearCondition( COND_NEW_ENEMY ); -} - -const Vector &CAI_BaseNPC::GetEnemyLKP() const -{ - return (const_cast(this))->GetEnemies()->LastKnownPosition( GetEnemy() ); -} - -float CAI_BaseNPC::GetEnemyLastTimeSeen() const -{ - return (const_cast(this))->GetEnemies()->LastTimeSeen( GetEnemy() ); -} - -void CAI_BaseNPC::MarkEnemyAsEluded() -{ - GetEnemies()->MarkAsEluded( GetEnemy() ); -} - -void CAI_BaseNPC::ClearEnemyMemory() -{ - GetEnemies()->ClearMemory( GetEnemy() ); -} - -bool CAI_BaseNPC::EnemyHasEludedMe() const -{ - return (const_cast(this))->GetEnemies()->HasEludedMe( GetEnemy() ); -} - -void CAI_BaseNPC::SetTarget( CBaseEntity *pTarget ) -{ - m_hTargetEnt = pTarget; -} - - -//========================================================= -// Choose Enemy - tries to find the best suitable enemy for the npc. -//========================================================= - -bool CAI_BaseNPC::ShouldChooseNewEnemy() -{ - CBaseEntity *pEnemy = GetEnemy(); - if ( pEnemy ) - { - if ( GetEnemies()->GetSerialNumber() != m_EnemiesSerialNumber ) - { - return true; - } - - m_EnemiesSerialNumber = GetEnemies()->GetSerialNumber(); - - if ( EnemyHasEludedMe() || (IRelationType( pEnemy ) != D_HT && IRelationType( pEnemy ) != D_FR) || !IsValidEnemy( pEnemy ) ) - { - DbgEnemyMsg( this, "ShouldChooseNewEnemy() --> true (1)\n" ); - return true; - } - if ( HasCondition(COND_SEE_HATE) || HasCondition(COND_SEE_DISLIKE) || HasCondition(COND_SEE_NEMESIS) || HasCondition(COND_SEE_FEAR) ) - { - DbgEnemyMsg( this, "ShouldChooseNewEnemy() --> true (2)\n" ); - return true; - } - if ( !pEnemy->IsAlive() ) - { - if ( m_GiveUpOnDeadEnemyTimer.IsRunning() ) - { - if ( m_GiveUpOnDeadEnemyTimer.Expired() ) - { - DbgEnemyMsg( this, "ShouldChooseNewEnemy() --> true (3)\n" ); - return true; - } - } - else - m_GiveUpOnDeadEnemyTimer.Start(); - } - - AI_EnemyInfo_t *pInfo = GetEnemies()->Find( pEnemy ); - - if ( m_FailChooseEnemyTimer.Expired() ) - { - m_FailChooseEnemyTimer.Set( 1.5 ); - if ( HasCondition( COND_TASK_FAILED ) || - ( pInfo && ( pInfo->timeAtFirstHand == AI_INVALID_TIME || gpGlobals->curtime - pInfo->timeLastSeen > 10 ) ) ) - { - return true; - } - } - - if ( pInfo && pInfo->timeValidEnemy < gpGlobals->curtime ) - { - DbgEnemyMsg( this, "ShouldChooseNewEnemy() --> false\n" ); - return false; - } - } - - DbgEnemyMsg( this, "ShouldChooseNewEnemy() --> true (4)\n" ); - m_EnemiesSerialNumber = GetEnemies()->GetSerialNumber(); - - return true; -} - -//------------------------------------- - -bool CAI_BaseNPC::ChooseEnemy( void ) -{ - AI_PROFILE_SCOPE(CAI_Enemies_ChooseEnemy); - - DbgEnemyMsg( this, "ChooseEnemy() {\n" ); - - //--------------------------------- - // - // Gather initial conditions - // - - CBaseEntity *pInitialEnemy = GetEnemy(); - CBaseEntity *pChosenEnemy = pInitialEnemy; - - // Use memory bits in case enemy pointer altered outside this function, (e.g., ehandle goes NULL) - bool fHadEnemy = ( HasMemory( bits_MEMORY_HAD_ENEMY | bits_MEMORY_HAD_PLAYER ) ); - bool fEnemyWasPlayer = HasMemory( bits_MEMORY_HAD_PLAYER ); - bool fEnemyWentNull = ( fHadEnemy && !pInitialEnemy ); - bool fEnemyEluded = ( fEnemyWentNull || ( pInitialEnemy && GetEnemies()->HasEludedMe( pInitialEnemy ) ) ); - - //--------------------------------- - // - // Establish suitability of choosing a new enemy - // - - bool fHaveCondNewEnemy; - bool fHaveCondLostEnemy; - - if ( GetCurSchedule() && !FScheduleDone() ) - { - Assert( InterruptFromCondition( COND_NEW_ENEMY ) == COND_NEW_ENEMY && InterruptFromCondition( COND_LOST_ENEMY ) == COND_LOST_ENEMY ); - fHaveCondNewEnemy = GetCurSchedule()->HasInterrupt( COND_NEW_ENEMY ); - fHaveCondLostEnemy = GetCurSchedule()->HasInterrupt( COND_LOST_ENEMY ); - - // See if they've been added as a custom interrupt - if ( !fHaveCondNewEnemy ) - { - fHaveCondNewEnemy = IsCustomInterruptConditionSet( COND_NEW_ENEMY ); - } - if ( !fHaveCondLostEnemy ) - { - fHaveCondLostEnemy = IsCustomInterruptConditionSet( COND_LOST_ENEMY ); - } - } - else - { - fHaveCondNewEnemy = true; // not having a schedule is the same as being interruptable by any condition - fHaveCondLostEnemy = true; - } - - if ( !fEnemyWentNull ) - { - if ( !fHaveCondNewEnemy && !( fHaveCondLostEnemy && fEnemyEluded ) ) - { - // DO NOT mess with the npc's enemy pointer unless the schedule the npc is currently - // running will be interrupted by COND_NEW_ENEMY or COND_LOST_ENEMY. This will - // eliminate the problem of npcs getting a new enemy while they are in a schedule - // that doesn't care, and then not realizing it by the time they get to a schedule - // that does. I don't feel this is a good permanent fix. - m_bSkippedChooseEnemy = true; - - DbgEnemyMsg( this, "Skipped enemy selection due to schedule restriction\n" ); - DbgEnemyMsg( this, "}\n" ); - return ( pChosenEnemy != NULL ); - } - } - else if ( !fHaveCondNewEnemy && !fHaveCondLostEnemy && GetCurSchedule() ) - { - DevMsg( 2, "WARNING: AI enemy went NULL but schedule (%s) is not interested\n", GetCurSchedule()->GetName() ); - } - - m_bSkippedChooseEnemy = false; - - //--------------------------------- - // - // Select a target - // - - if ( ShouldChooseNewEnemy() ) - { - pChosenEnemy = BestEnemy(); - } - - //--------------------------------- - // - // React to result of selection - // - - bool fChangingEnemy = ( pChosenEnemy != pInitialEnemy ); - - if ( fChangingEnemy || fEnemyWentNull ) - { - DbgEnemyMsg( this, "Enemy changed from %s to %s\n", pInitialEnemy->GetDebugName(), pChosenEnemy->GetDebugName() ); - Forget( bits_MEMORY_HAD_ENEMY | bits_MEMORY_HAD_PLAYER ); - - // Did our old enemy snuff it? - if ( pInitialEnemy && !pInitialEnemy->IsAlive() ) - { - SetCondition( COND_ENEMY_DEAD ); - } - - SetEnemy( pChosenEnemy ); - - if ( fHadEnemy ) - { - // Vacate any strategy slot on old enemy - VacateStrategySlot(); - - // Force output event for establishing LOS - Forget( bits_MEMORY_HAD_LOS ); - // m_flLastAttackTime = 0; - } - - if ( !pChosenEnemy ) - { - // Don't break on enemies going null if they've been killed - if ( !HasCondition(COND_ENEMY_DEAD) ) - { - SetCondition( COND_ENEMY_WENT_NULL ); - } - - if ( fEnemyEluded ) - { - SetCondition( COND_LOST_ENEMY ); - LostEnemySound(); - } - - if ( fEnemyWasPlayer ) - { - m_OnLostPlayer.FireOutput( pInitialEnemy, this ); - } - m_OnLostEnemy.FireOutput( pInitialEnemy, this); - } - else - { - Remember( ( pChosenEnemy->IsPlayer() ) ? bits_MEMORY_HAD_PLAYER : bits_MEMORY_HAD_ENEMY ); - } - } - - //--------------------------------- - - return ( pChosenEnemy != NULL ); -} - - -//========================================================= -void CAI_BaseNPC::PickupWeapon( CBaseCombatWeapon *pWeapon ) -{ - pWeapon->OnPickedUp( this ); - Weapon_Equip( pWeapon ); - m_iszPendingWeapon = NULL_STRING; -} - -//========================================================= -// DropItem - dead npc drops named item -//========================================================= -CBaseEntity *CAI_BaseNPC::DropItem ( char *pszItemName, Vector vecPos, QAngle vecAng ) -{ - if ( !pszItemName ) - { - DevMsg( "DropItem() - No item name!\n" ); - return NULL; - } - - CBaseEntity *pItem = CBaseEntity::Create( pszItemName, vecPos, vecAng, this ); - - if ( pItem ) - { - if ( g_pGameRules->IsAllowedToSpawn( pItem ) == false ) - { - UTIL_Remove( pItem ); - return NULL; - } - - IPhysicsObject *pPhys = pItem->VPhysicsGetObject(); - - if ( pPhys ) - { - // Add an extra push in a random direction - Vector vel = RandomVector( -64.0f, 64.0f ); - AngularImpulse angImp = RandomAngularImpulse( -300.0f, 300.0f ); - - vel[2] = 0.0f; - pPhys->AddVelocity( &vel, &angImp ); - } - else - { - // do we want this behavior to be default?! (sjb) - pItem->ApplyAbsVelocityImpulse( GetAbsVelocity() ); - pItem->ApplyLocalAngularVelocityImpulse( AngularImpulse( 0, random->RandomFloat( 0, 100 ), 0 ) ); - } - - return pItem; - } - else - { - DevMsg( "DropItem() - Didn't create!\n" ); - return NULL; - } - -} - -bool CAI_BaseNPC::ShouldFadeOnDeath( void ) -{ - if ( g_RagdollLVManager.IsLowViolence() ) - { - return true; - } - else - { - // if flagged to fade out - return HasSpawnFlags(SF_NPC_FADE_CORPSE); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Indicates whether or not this npc should play an idle sound now. -// -// -// Output : Returns true if yes, false if no. -//----------------------------------------------------------------------------- -bool CAI_BaseNPC::ShouldPlayIdleSound( void ) -{ - if ( ( m_NPCState == NPC_STATE_IDLE || m_NPCState == NPC_STATE_ALERT ) && - random->RandomInt(0,99) == 0 && !HasSpawnFlags(SF_NPC_GAG) ) - { - return true; - } - - return false; -} - -//----------------------------------------------------------------------------- -// Purpose: Make a sound that other AI's can hear, to broadcast our presence -// Input : volume (radius) of the sound. -// Output : -//----------------------------------------------------------------------------- -void CAI_BaseNPC::MakeAIFootstepSound( float volume, float duration ) -{ - CSoundEnt::InsertSound( SOUND_COMBAT, EyePosition(), volume, duration, this, SOUNDENT_CHANNEL_NPC_FOOTSTEP ); -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : -// Output : -//----------------------------------------------------------------------------- -bool CAI_BaseNPC::FOkToMakeSound( int soundPriority ) -{ - // ask the squad to filter sounds if I'm in one - if ( m_pSquad ) - { - if ( !m_pSquad->FOkToMakeSound( soundPriority ) ) - return false; - } - else - { - // otherwise, check my own sound timer - // Am I making uninterruptable sound? - if (gpGlobals->curtime <= m_flSoundWaitTime) - { - if ( soundPriority <= m_nSoundPriority ) - return false; - } - } - - // no talking outside of combat if gagged. - if ( HasSpawnFlags(SF_NPC_GAG) && ( m_NPCState != NPC_STATE_COMBAT ) ) - return false; - - return true; -} - - -//----------------------------------------------------------------------------- -// Purpose: -// Input : -// Output : -//----------------------------------------------------------------------------- -void CAI_BaseNPC::JustMadeSound( int soundPriority, float flSoundLength ) -{ - m_flSoundWaitTime = gpGlobals->curtime + flSoundLength + random->RandomFloat(1.5, 2.0); - m_nSoundPriority = soundPriority; - - if (m_pSquad) - { - m_pSquad->JustMadeSound( soundPriority, gpGlobals->curtime + flSoundLength + random->RandomFloat(1.5, 2.0) ); - } -} - -Activity CAI_BaseNPC::GetStoppedActivity( void ) -{ - if (GetNavigator()->IsGoalActive()) - { - Activity activity = GetNavigator()->GetArrivalActivity(); - - if (activity > ACT_RESET) - { - return activity; - } - } - - return ACT_IDLE; -} - - -//========================================================= -//========================================================= -void CAI_BaseNPC::OnScheduleChange ( void ) -{ - EndTaskOverlay(); - - m_pNavigator->OnScheduleChange(); - - m_flMoveWaitFinished = 0; - - VacateStrategySlot(); - - // If I still have have a route, clear it - // FIXME: Routes should only be cleared inside of tasks (kenb) - GetNavigator()->ClearGoal(); - - UnlockBestSound(); - - // If I locked a hint node clear it - if ( HasMemory(bits_MEMORY_LOCKED_HINT) && GetHintNode() != NULL) - { - float hintDelay = GetHintDelay(GetHintNode()->HintType()); - GetHintNode()->Unlock(hintDelay); - SetHintNode( NULL ); - } -} - - - -CBaseCombatCharacter* CAI_BaseNPC::GetEnemyCombatCharacterPointer() -{ - if ( GetEnemy() == NULL ) - return NULL; - - return GetEnemy()->MyCombatCharacterPointer(); -} - - -// Global Savedata for npc -// -// This should be an exact copy of the var's in the header. Fields -// that aren't save/restored are commented out - -BEGIN_DATADESC( CAI_BaseNPC ) - - // m_pSchedule (reacquired on restore) - DEFINE_EMBEDDED( m_ScheduleState ), - DEFINE_FIELD( m_IdealSchedule, FIELD_INTEGER ), // handled specially but left in for "virtual" schedules - DEFINE_FIELD( m_failSchedule, FIELD_INTEGER ), // handled specially but left in for "virtual" schedules - DEFINE_FIELD( m_bUsingStandardThinkTime, FIELD_BOOLEAN ), - DEFINE_FIELD( m_flLastRealThinkTime, FIELD_TIME ), - // m_iFrameBlocked (not saved) - // m_bInChoreo (not saved) - // m_bDoPostRestoreRefindPath (not saved) - // gm_flTimeLastSpawn (static) - // gm_nSpawnedThisFrame (static) - // m_Conditions (custom save) - // m_CustomInterruptConditions (custom save) - // m_ConditionsPreIgnore (custom save) - // m_InverseIgnoreConditions (custom save) - DEFINE_FIELD( m_bForceConditionsGather, FIELD_BOOLEAN ), - DEFINE_FIELD( m_bConditionsGathered, FIELD_BOOLEAN ), - DEFINE_FIELD( m_bSkippedChooseEnemy, FIELD_BOOLEAN ), - DEFINE_FIELD( m_NPCState, FIELD_INTEGER ), - DEFINE_FIELD( m_IdealNPCState, FIELD_INTEGER ), - DEFINE_FIELD( m_flLastStateChangeTime, FIELD_TIME ), - DEFINE_FIELD( m_Efficiency, FIELD_INTEGER ), - DEFINE_FIELD( m_MoveEfficiency, FIELD_INTEGER ), - DEFINE_FIELD( m_flNextDecisionTime, FIELD_TIME ), - DEFINE_KEYFIELD( m_SleepState, FIELD_INTEGER, "sleepstate" ), - DEFINE_FIELD( m_SleepFlags, FIELD_INTEGER ), - DEFINE_KEYFIELD( m_flWakeRadius, FIELD_FLOAT, "wakeradius" ), - DEFINE_KEYFIELD( m_bWakeSquad, FIELD_BOOLEAN, "wakesquad" ), - DEFINE_FIELD( m_nWakeTick, FIELD_TICK ), - - DEFINE_CUSTOM_FIELD( m_Activity, ActivityDataOps() ), - DEFINE_CUSTOM_FIELD( m_translatedActivity, ActivityDataOps() ), - DEFINE_CUSTOM_FIELD( m_IdealActivity, ActivityDataOps() ), - DEFINE_CUSTOM_FIELD( m_IdealTranslatedActivity, ActivityDataOps() ), - DEFINE_CUSTOM_FIELD( m_IdealWeaponActivity, ActivityDataOps() ), - - DEFINE_FIELD( m_nIdealSequence, FIELD_INTEGER ), - DEFINE_EMBEDDEDBYREF( m_pSenses ), - DEFINE_EMBEDDEDBYREF( m_pLockedBestSound ), - DEFINE_FIELD( m_hEnemy, FIELD_EHANDLE ), - DEFINE_FIELD( m_flTimeEnemyAcquired, FIELD_TIME ), - DEFINE_FIELD( m_hTargetEnt, FIELD_EHANDLE ), - DEFINE_EMBEDDED( m_GiveUpOnDeadEnemyTimer ), - DEFINE_EMBEDDED( m_FailChooseEnemyTimer ), - DEFINE_FIELD( m_EnemiesSerialNumber, FIELD_INTEGER ), - DEFINE_FIELD( m_flAcceptableTimeSeenEnemy, FIELD_TIME ), - DEFINE_EMBEDDED( m_UpdateEnemyPosTimer ), - // m_flTimeAnyUpdateEnemyPos (static) - DEFINE_FIELD( m_vecCommandGoal, FIELD_VECTOR ), - DEFINE_EMBEDDED( m_CommandMoveMonitor ), - DEFINE_FIELD( m_flSoundWaitTime, FIELD_TIME ), - DEFINE_FIELD( m_nSoundPriority, FIELD_INTEGER ), - DEFINE_FIELD( m_flIgnoreDangerSoundsUntil, FIELD_TIME ), - DEFINE_FIELD( m_afCapability, FIELD_INTEGER ), - DEFINE_FIELD( m_flMoveWaitFinished, FIELD_TIME ), - DEFINE_FIELD( m_hOpeningDoor, FIELD_EHANDLE ), - DEFINE_EMBEDDEDBYREF( m_pNavigator ), - DEFINE_EMBEDDEDBYREF( m_pLocalNavigator ), - DEFINE_EMBEDDEDBYREF( m_pPathfinder ), - DEFINE_EMBEDDEDBYREF( m_pMoveProbe ), - DEFINE_EMBEDDEDBYREF( m_pMotor ), - DEFINE_UTLVECTOR(m_UnreachableEnts, FIELD_EMBEDDED), - DEFINE_FIELD( m_hInteractionPartner, FIELD_EHANDLE ), - DEFINE_FIELD( m_hLastInteractionTestTarget, FIELD_EHANDLE ), - DEFINE_FIELD( m_hForcedInteractionPartner, FIELD_EHANDLE ), - DEFINE_FIELD( m_vecForcedWorldPosition, FIELD_POSITION_VECTOR ), - DEFINE_FIELD( m_bCannotDieDuringInteraction, FIELD_BOOLEAN ), - DEFINE_FIELD( m_iInteractionState, FIELD_INTEGER ), - DEFINE_FIELD( m_iInteractionPlaying, FIELD_INTEGER ), - DEFINE_UTLVECTOR(m_ScriptedInteractions,FIELD_EMBEDDED), - DEFINE_FIELD( m_flInteractionYaw, FIELD_FLOAT ), - DEFINE_EMBEDDED( m_CheckOnGroundTimer ), - DEFINE_FIELD( m_vDefaultEyeOffset, FIELD_VECTOR ), - DEFINE_FIELD( m_flNextEyeLookTime, FIELD_TIME ), - DEFINE_FIELD( m_flEyeIntegRate, FIELD_FLOAT ), - DEFINE_FIELD( m_vEyeLookTarget, FIELD_POSITION_VECTOR ), - DEFINE_FIELD( m_vCurEyeTarget, FIELD_POSITION_VECTOR ), - DEFINE_FIELD( m_hEyeLookTarget, FIELD_EHANDLE ), - DEFINE_FIELD( m_flHeadYaw, FIELD_FLOAT ), - DEFINE_FIELD( m_flHeadPitch, FIELD_FLOAT ), - DEFINE_FIELD( m_flOriginalYaw, FIELD_FLOAT ), - DEFINE_FIELD( m_bInAScript, FIELD_BOOLEAN ), - DEFINE_FIELD( m_scriptState, FIELD_INTEGER ), - DEFINE_FIELD( m_hCine, FIELD_EHANDLE ), - DEFINE_CUSTOM_FIELD( m_ScriptArrivalActivity, ActivityDataOps() ), - DEFINE_FIELD( m_strScriptArrivalSequence, FIELD_STRING ), - DEFINE_FIELD( m_flSceneTime, FIELD_TIME ), - DEFINE_FIELD( m_iszSceneCustomMoveSeq, FIELD_STRING ), - // m_pEnemies Saved specially in ai_saverestore.cpp - DEFINE_FIELD( m_afMemory, FIELD_INTEGER ), - DEFINE_FIELD( m_hEnemyOccluder, FIELD_EHANDLE ), - DEFINE_FIELD( m_flSumDamage, FIELD_FLOAT ), - DEFINE_FIELD( m_flLastDamageTime, FIELD_TIME ), - DEFINE_FIELD( m_flLastPlayerDamageTime, FIELD_TIME ), - DEFINE_FIELD( m_flLastSawPlayerTime, FIELD_TIME ), - DEFINE_FIELD( m_flLastAttackTime, FIELD_TIME ), - DEFINE_FIELD( m_flLastEnemyTime, FIELD_TIME ), - DEFINE_FIELD( m_flNextWeaponSearchTime, FIELD_TIME ), - DEFINE_FIELD( m_iszPendingWeapon, FIELD_STRING ), - DEFINE_EMBEDDED( m_ShotRegulator ), - DEFINE_FIELD( m_iDesiredWeaponState, FIELD_INTEGER ), - // m_pSquad Saved specially in ai_saverestore.cpp - DEFINE_KEYFIELD(m_SquadName, FIELD_STRING, "squadname" ), - DEFINE_FIELD( m_iMySquadSlot, FIELD_INTEGER ), - DEFINE_KEYFIELD( m_strHintGroup, FIELD_STRING, "hintgroup" ), - DEFINE_KEYFIELD( m_bHintGroupNavLimiting, FIELD_BOOLEAN, "hintlimiting" ), - DEFINE_EMBEDDEDBYREF( m_pTacticalServices ), - DEFINE_FIELD( m_flWaitFinished, FIELD_TIME ), - DEFINE_FIELD( m_flNextFlinchTime, FIELD_TIME ), - DEFINE_FIELD( m_flNextDodgeTime, FIELD_TIME ), - DEFINE_EMBEDDED( m_MoveAndShootOverlay ), - DEFINE_FIELD( m_vecLastPosition, FIELD_POSITION_VECTOR ), - DEFINE_FIELD( m_vSavePosition, FIELD_POSITION_VECTOR ), - DEFINE_FIELD( m_vInterruptSavePosition, FIELD_POSITION_VECTOR ), - DEFINE_FIELD( m_pHintNode, FIELD_EHANDLE), - DEFINE_FIELD( m_cAmmoLoaded, FIELD_INTEGER ), - DEFINE_FIELD( m_flDistTooFar, FIELD_FLOAT ), - DEFINE_FIELD( m_hGoalEnt, FIELD_EHANDLE ), - DEFINE_FIELD( m_flTimeLastMovement, FIELD_TIME ), - DEFINE_KEYFIELD(m_spawnEquipment, FIELD_STRING, "additionalequipment" ), - DEFINE_FIELD( m_fNoDamageDecal, FIELD_BOOLEAN ), - DEFINE_FIELD( m_hStoredPathTarget, FIELD_EHANDLE ), - DEFINE_FIELD( m_vecStoredPathGoal, FIELD_POSITION_VECTOR ), - DEFINE_FIELD( m_nStoredPathType, FIELD_INTEGER ), - DEFINE_FIELD( m_fStoredPathFlags, FIELD_INTEGER ), - DEFINE_FIELD( m_bDidDeathCleanup, FIELD_BOOLEAN ), - DEFINE_FIELD( m_bCrouchDesired, FIELD_BOOLEAN ), - DEFINE_FIELD( m_bForceCrouch, FIELD_BOOLEAN ), - DEFINE_FIELD( m_bIsCrouching, FIELD_BOOLEAN ), - DEFINE_FIELD( m_bPerformAvoidance, FIELD_BOOLEAN ), - DEFINE_FIELD( m_bIsMoving, FIELD_BOOLEAN ), - DEFINE_FIELD( m_bFadeCorpse, FIELD_BOOLEAN ), - DEFINE_FIELD( m_iDeathPose, FIELD_INTEGER ), - DEFINE_FIELD( m_iDeathFrame, FIELD_INTEGER ), - DEFINE_FIELD( m_bCheckContacts, FIELD_BOOLEAN ), - DEFINE_FIELD( m_bSpeedModActive, FIELD_BOOLEAN ), - DEFINE_FIELD( m_iSpeedModRadius, FIELD_INTEGER ), - DEFINE_FIELD( m_iSpeedModSpeed, FIELD_INTEGER ), - DEFINE_FIELD( m_hEnemyFilter, FIELD_EHANDLE ), - DEFINE_KEYFIELD( m_iszEnemyFilterName, FIELD_STRING, "enemyfilter" ), - DEFINE_FIELD( m_bImportanRagdoll, FIELD_BOOLEAN ), - - // m_fIsUsingSmallHull TODO -- This needs more consideration than simple save/load - // m_failText DEBUG - // m_interruptText DEBUG - // m_failedSchedule DEBUG - // m_interuptSchedule DEBUG - // m_nDebugCurIndex DEBUG - - // m_LastShootAccuracy DEBUG - // m_RecentShotAccuracy DEBUG - // m_TotalShots DEBUG - // m_TotalHits DEBUG - // m_bSelected DEBUG - // m_TimeLastShotMark DEBUG - - - // Outputs - DEFINE_OUTPUT( m_OnDamaged, "OnDamaged" ), - DEFINE_OUTPUT( m_OnDeath, "OnDeath" ), - DEFINE_OUTPUT( m_OnHalfHealth, "OnHalfHealth" ), - DEFINE_OUTPUT( m_OnFoundEnemy, "OnFoundEnemy" ), - DEFINE_OUTPUT( m_OnLostEnemyLOS, "OnLostEnemyLOS" ), - DEFINE_OUTPUT( m_OnLostEnemy, "OnLostEnemy" ), - DEFINE_OUTPUT( m_OnFoundPlayer, "OnFoundPlayer" ), - DEFINE_OUTPUT( m_OnLostPlayerLOS, "OnLostPlayerLOS" ), - DEFINE_OUTPUT( m_OnLostPlayer, "OnLostPlayer" ), - DEFINE_OUTPUT( m_OnHearWorld, "OnHearWorld" ), - DEFINE_OUTPUT( m_OnHearPlayer, "OnHearPlayer" ), - DEFINE_OUTPUT( m_OnHearCombat, "OnHearCombat" ), - DEFINE_OUTPUT( m_OnDamagedByPlayer, "OnDamagedByPlayer" ), - DEFINE_OUTPUT( m_OnDamagedByPlayerSquad, "OnDamagedByPlayerSquad" ), - DEFINE_OUTPUT( m_OnDenyCommanderUse, "OnDenyCommanderUse" ), - DEFINE_OUTPUT( m_OnRappelTouchdown, "OnRappelTouchdown" ), - DEFINE_OUTPUT( m_OnWake, "OnWake" ), - DEFINE_OUTPUT( m_OnSleep, "OnSleep" ), - DEFINE_OUTPUT( m_OnForcedInteractionAborted, "OnForcedInteractionAborted" ), - DEFINE_OUTPUT( m_OnForcedInteractionFinished, "OnForcedInteractionFinished" ), - - // Inputs - DEFINE_INPUTFUNC( FIELD_STRING, "SetRelationship", InputSetRelationship ), - DEFINE_INPUTFUNC( FIELD_INTEGER, "SetHealth", InputSetHealth ), - DEFINE_INPUTFUNC( FIELD_VOID, "BeginRappel", InputBeginRappel ), - DEFINE_INPUTFUNC( FIELD_STRING, "SetSquad", InputSetSquad ), - DEFINE_INPUTFUNC( FIELD_VOID, "Wake", InputWake ), - DEFINE_INPUTFUNC( FIELD_STRING, "ForgetEntity", InputForgetEntity ), - DEFINE_INPUTFUNC( FIELD_FLOAT, "IgnoreDangerSounds", InputIgnoreDangerSounds ), - DEFINE_INPUTFUNC( FIELD_VOID, "Break", InputBreak ), - DEFINE_INPUTFUNC( FIELD_VOID, "StartScripting", InputStartScripting ), - DEFINE_INPUTFUNC( FIELD_VOID, "StopScripting", InputStopScripting ), - DEFINE_INPUTFUNC( FIELD_VOID, "GagEnable", InputGagEnable ), - DEFINE_INPUTFUNC( FIELD_VOID, "GagDisable", InputGagDisable ), - DEFINE_INPUTFUNC( FIELD_VOID, "InsideTransition", InputInsideTransition ), - DEFINE_INPUTFUNC( FIELD_VOID, "OutsideTransition", InputOutsideTransition ), - DEFINE_INPUTFUNC( FIELD_VOID, "ActivateSpeedModifier", InputActivateSpeedModifier ), - DEFINE_INPUTFUNC( FIELD_VOID, "DisableSpeedModifier", InputDisableSpeedModifier ), - DEFINE_INPUTFUNC( FIELD_INTEGER, "SetSpeedModRadius", InputSetSpeedModifierRadius ), - DEFINE_INPUTFUNC( FIELD_INTEGER, "SetSpeedModSpeed", InputSetSpeedModifierSpeed ), - DEFINE_INPUTFUNC( FIELD_VOID, "HolsterWeapon", InputHolsterWeapon ), - DEFINE_INPUTFUNC( FIELD_VOID, "HolsterAndDestroyWeapon", InputHolsterAndDestroyWeapon ), - DEFINE_INPUTFUNC( FIELD_VOID, "UnholsterWeapon", InputUnholsterWeapon ), - DEFINE_INPUTFUNC( FIELD_STRING, "ForceInteractionWithNPC", InputForceInteractionWithNPC ), - DEFINE_INPUTFUNC( FIELD_STRING, "UpdateEnemyMemory", InputUpdateEnemyMemory ), - - // Function pointers - DEFINE_USEFUNC( NPCUse ), - DEFINE_THINKFUNC( CallNPCThink ), - DEFINE_THINKFUNC( CorpseFallThink ), - DEFINE_THINKFUNC( NPCInitThink ), - -END_DATADESC() - -BEGIN_SIMPLE_DATADESC( AIScheduleState_t ) - DEFINE_FIELD( iCurTask, FIELD_INTEGER ), - DEFINE_FIELD( fTaskStatus, FIELD_INTEGER ), - DEFINE_FIELD( timeStarted, FIELD_TIME ), - DEFINE_FIELD( timeCurTaskStarted, FIELD_TIME ), - DEFINE_FIELD( taskFailureCode, FIELD_INTEGER ), - DEFINE_FIELD( iTaskInterrupt, FIELD_INTEGER ), - DEFINE_FIELD( bTaskRanAutomovement, FIELD_BOOLEAN ), - DEFINE_FIELD( bTaskUpdatedYaw, FIELD_BOOLEAN ), -END_DATADESC() - - -IMPLEMENT_SERVERCLASS_ST( CAI_BaseNPC, DT_AI_BaseNPC ) - SendPropInt( SENDINFO( m_lifeState ), 3, SPROP_UNSIGNED ), - SendPropBool( SENDINFO( m_bPerformAvoidance ) ), - SendPropBool( SENDINFO( m_bIsMoving ) ), - SendPropBool( SENDINFO( m_bFadeCorpse ) ), - SendPropInt( SENDINFO( m_iDeathPose ), ANIMATION_SEQUENCE_BITS ), - SendPropInt( SENDINFO( m_iDeathFrame ), 5 ), - SendPropBool( SENDINFO( m_bSpeedModActive ) ), - SendPropInt( SENDINFO( m_iSpeedModRadius ) ), - SendPropInt( SENDINFO( m_iSpeedModSpeed ) ), - SendPropBool( SENDINFO( m_bImportanRagdoll ) ), -END_SEND_TABLE() - -//------------------------------------- - -BEGIN_SIMPLE_DATADESC( UnreachableEnt_t ) - - DEFINE_FIELD( hUnreachableEnt, FIELD_EHANDLE ), - DEFINE_FIELD( fExpireTime, FIELD_TIME ), - DEFINE_FIELD( vLocationWhenUnreachable, FIELD_POSITION_VECTOR ), - -END_DATADESC() - -//------------------------------------- - -BEGIN_SIMPLE_DATADESC( ScriptedNPCInteraction_Phases_t ) -DEFINE_FIELD( iszSequence, FIELD_STRING ), -DEFINE_FIELD( iActivity, FIELD_INTEGER ), -END_DATADESC() - -//------------------------------------- - -BEGIN_SIMPLE_DATADESC( ScriptedNPCInteraction_t ) - DEFINE_FIELD( iszInteractionName, FIELD_STRING ), - DEFINE_FIELD( iFlags, FIELD_INTEGER ), - DEFINE_FIELD( iTriggerMethod, FIELD_INTEGER ), - DEFINE_FIELD( iLoopBreakTriggerMethod, FIELD_INTEGER ), - DEFINE_FIELD( vecRelativeOrigin, FIELD_VECTOR ), - DEFINE_FIELD( angRelativeAngles, FIELD_VECTOR ), - DEFINE_FIELD( vecRelativeVelocity, FIELD_VECTOR ), - DEFINE_FIELD( flDelay, FIELD_FLOAT ), - DEFINE_FIELD( flDistSqr, FIELD_FLOAT ), - DEFINE_FIELD( iszMyWeapon, FIELD_STRING ), - DEFINE_FIELD( iszTheirWeapon, FIELD_STRING ), - DEFINE_EMBEDDED_ARRAY( sPhases, SNPCINT_NUM_PHASES ), - DEFINE_FIELD( matDesiredLocalToWorld, FIELD_VMATRIX ), - DEFINE_FIELD( bValidOnCurrentEnemy, FIELD_BOOLEAN ), - DEFINE_FIELD( flNextAttemptTime, FIELD_TIME ), -END_DATADESC() - -//------------------------------------- - -void CAI_BaseNPC::PostConstructor( const char *szClassname ) -{ - BaseClass::PostConstructor( szClassname ); - CreateComponents(); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CAI_BaseNPC::Activate( void ) -{ - BaseClass::Activate(); - - if ( GetModelPtr() ) - { - ParseScriptedNPCInteractions(); - } - - // Get a handle to my enemy filter entity if there is one. - if ( m_iszEnemyFilterName != NULL_STRING ) - { - CBaseEntity *pFilter = gEntList.FindEntityByName( NULL, m_iszEnemyFilterName ); - if ( pFilter != NULL ) - { - m_hEnemyFilter = dynamic_cast(pFilter); - } - } -} - -void CAI_BaseNPC::Precache( void ) -{ - gm_iszPlayerSquad = AllocPooledString( PLAYER_SQUADNAME ); // cache for fast IsPlayerSquad calls - - if ( m_spawnEquipment != NULL_STRING && strcmp(STRING(m_spawnEquipment), "0") ) - { - UTIL_PrecacheOther( STRING(m_spawnEquipment) ); - } - - // Make sure schedules are loaded for this NPC type - if (!LoadedSchedules()) - { - DevMsg("ERROR: Rejecting spawn of %s as error in NPC's schedules.\n",GetDebugName()); - UTIL_Remove(this); - return; - } - - PrecacheScriptSound( "AI_BaseNPC.SwishSound" ); - PrecacheScriptSound( "AI_BaseNPC.BodyDrop_Heavy" ); - PrecacheScriptSound( "AI_BaseNPC.BodyDrop_Light" ); - PrecacheScriptSound( "AI_BaseNPC.SentenceStop" ); - - BaseClass::Precache(); -} - - -//----------------------------------------------------------------------------- - -const short AI_EXTENDED_SAVE_HEADER_VERSION = 5; -const short AI_EXTENDED_SAVE_HEADER_RESET_VERSION = 3; - -const short AI_EXTENDED_SAVE_HEADER_FIRST_VERSION_WITH_CONDITIONS = 2; -const short AI_EXTENDED_SAVE_HEADER_FIRST_VERSION_WITH_SCHEDULE_ID_FIXUP = 3; -const short AI_EXTENDED_SAVE_HEADER_FIRST_VERSION_WITH_SEQUENCE = 4; -const short AI_EXTENDED_SAVE_HEADER_FIRST_VERSION_WITH_NAVIGATOR_SAVE = 5; - -struct AIExtendedSaveHeader_t -{ - AIExtendedSaveHeader_t() - : version(AI_EXTENDED_SAVE_HEADER_VERSION), - flags(0), - scheduleCrc(0) - { - szSchedule[0] = 0; - szIdealSchedule[0] = 0; - szFailSchedule[0] = 0; - szSequence[0] = 0; - } - - short version; - unsigned flags; - char szSchedule[128]; - CRC32_t scheduleCrc; - char szIdealSchedule[128]; - char szFailSchedule[128]; - char szSequence[128]; - - DECLARE_SIMPLE_DATADESC(); -}; - -enum AIExtendedSaveHeaderFlags_t -{ - AIESH_HAD_ENEMY = 0x01, - AIESH_HAD_TARGET = 0x02, - AIESH_HAD_NAVGOAL = 0x04, -}; - -//------------------------------------- - -BEGIN_SIMPLE_DATADESC( AIExtendedSaveHeader_t ) - DEFINE_FIELD( version, FIELD_SHORT ), - DEFINE_FIELD( flags, FIELD_INTEGER ), - DEFINE_AUTO_ARRAY( szSchedule, FIELD_CHARACTER ), - DEFINE_FIELD( scheduleCrc, FIELD_INTEGER ), - DEFINE_AUTO_ARRAY( szIdealSchedule, FIELD_CHARACTER ), - DEFINE_AUTO_ARRAY( szFailSchedule, FIELD_CHARACTER ), - DEFINE_AUTO_ARRAY( szSequence, FIELD_CHARACTER ), -END_DATADESC() - -//------------------------------------- - -int CAI_BaseNPC::Save( ISave &save ) -{ - AIExtendedSaveHeader_t saveHeader; - - if ( GetEnemy() ) - saveHeader.flags |= AIESH_HAD_ENEMY; - if ( GetTarget() ) - saveHeader.flags |= AIESH_HAD_TARGET; - if ( GetNavigator()->IsGoalActive() ) - saveHeader.flags |= AIESH_HAD_NAVGOAL; - - if ( m_pSchedule ) - { - const char *pszSchedule = m_pSchedule->GetName(); - - Assert( Q_strlen( pszSchedule ) < sizeof( saveHeader.szSchedule ) - 1 ); - Q_strncpy( saveHeader.szSchedule, pszSchedule, sizeof( saveHeader.szSchedule ) ); - - CRC32_Init( &saveHeader.scheduleCrc ); - CRC32_ProcessBuffer( &saveHeader.scheduleCrc, (void *)m_pSchedule->GetTaskList(), m_pSchedule->NumTasks() * sizeof(Task_t) ); - CRC32_Final( &saveHeader.scheduleCrc ); - } - else - { - saveHeader.szSchedule[0] = 0; - saveHeader.scheduleCrc = 0; - } - - int idealSchedule = GetGlobalScheduleId( m_IdealSchedule ); - - if ( idealSchedule != -1 && idealSchedule != AI_RemapToGlobal( SCHED_NONE ) && idealSchedule != AI_RemapToGlobal( SCHED_AISCRIPT ) ) - { - CAI_Schedule *pIdealSchedule = GetSchedule( m_IdealSchedule ); - if ( pIdealSchedule ) - { - const char *pszIdealSchedule = pIdealSchedule->GetName(); - Assert( Q_strlen( pszIdealSchedule ) < sizeof( saveHeader.szIdealSchedule ) - 1 ); - Q_strncpy( saveHeader.szIdealSchedule, pszIdealSchedule, sizeof( saveHeader.szIdealSchedule ) ); - } - } - - int failSchedule = GetGlobalScheduleId( m_failSchedule ); - if ( failSchedule != -1 && failSchedule != AI_RemapToGlobal( SCHED_NONE ) && failSchedule != AI_RemapToGlobal( SCHED_AISCRIPT ) ) - { - CAI_Schedule *pFailSchedule = GetSchedule( m_failSchedule ); - if ( pFailSchedule ) - { - const char *pszFailSchedule = pFailSchedule->GetName(); - Assert( Q_strlen( pszFailSchedule ) < sizeof( saveHeader.szFailSchedule ) - 1 ); - Q_strncpy( saveHeader.szFailSchedule, pszFailSchedule, sizeof( saveHeader.szFailSchedule ) ); - } - } - - if ( GetSequence() != ACT_INVALID && GetModelPtr() ) - { - const char *pszSequenceName = GetSequenceName( GetSequence() ); - if ( pszSequenceName && *pszSequenceName ) - { - Assert( Q_strlen( pszSequenceName ) < sizeof( saveHeader.szSequence ) - 1 ); - Q_strncpy( saveHeader.szSequence, pszSequenceName, sizeof(saveHeader.szSequence) ); - } - } - - save.WriteAll( &saveHeader ); - - save.StartBlock(); - SaveConditions( save, m_Conditions ); - SaveConditions( save, m_CustomInterruptConditions ); - SaveConditions( save, m_ConditionsPreIgnore ); - CAI_ScheduleBits ignoreConditions; - m_InverseIgnoreConditions.Not( &ignoreConditions ); - SaveConditions( save, ignoreConditions ); - save.EndBlock(); - - save.StartBlock(); - GetNavigator()->Save( save ); - save.EndBlock(); - - return BaseClass::Save(save); -} - -//------------------------------------- - -void CAI_BaseNPC::DiscardScheduleState() -{ - // We don't save/restore routes yet - GetNavigator()->ClearGoal(); - - // We don't save/restore schedules yet - ClearSchedule(); - - // Reset animation - m_Activity = ACT_RESET; - - // If we don't have an enemy, clear conditions like see enemy, etc. - if ( GetEnemy() == NULL ) - { - m_Conditions.ClearAllBits(); - } - - // went across a transition and lost my m_hCine - bool bLostScript = ( m_NPCState == NPC_STATE_SCRIPT && m_hCine == NULL ); - if ( bLostScript ) - { - // UNDONE: Do something better here? - // for now, just go back to idle and let the AI figure out what to do. - SetState( NPC_STATE_IDLE ); - SetIdealState( NPC_STATE_IDLE ); - DevMsg(1, "Scripted Sequence stripped on level transition for %s\n", GetDebugName() ); - } -} - -//------------------------------------- - -void CAI_BaseNPC::OnRestore() -{ - gm_iszPlayerSquad = AllocPooledString( PLAYER_SQUADNAME ); // cache for fast IsPlayerSquad calls - - if ( m_bDoPostRestoreRefindPath && CAI_NetworkManager::NetworksLoaded() ) - { - CAI_DynamicLink::InitDynamicLinks(); - if ( !GetNavigator()->RefindPathToGoal( false ) ) - DiscardScheduleState(); - } - else - { - GetNavigator()->ClearGoal(); - } - BaseClass::OnRestore(); - m_bCheckContacts = true; -} - - -//------------------------------------- - -int CAI_BaseNPC::Restore( IRestore &restore ) -{ - AIExtendedSaveHeader_t saveHeader; - restore.ReadAll( &saveHeader ); - - if ( saveHeader.version >= AI_EXTENDED_SAVE_HEADER_FIRST_VERSION_WITH_SCHEDULE_ID_FIXUP ) - { - if ( saveHeader.szIdealSchedule[0] ) - { - CAI_Schedule *pIdealSchedule = g_AI_SchedulesManager.GetScheduleByName( saveHeader.szIdealSchedule ); - m_IdealSchedule = ( pIdealSchedule ) ? pIdealSchedule->GetId() : SCHED_NONE; - } - - if ( saveHeader.szFailSchedule[0] ) - { - CAI_Schedule *pFailSchedule = g_AI_SchedulesManager.GetScheduleByName( saveHeader.szFailSchedule ); - m_failSchedule = ( pFailSchedule ) ? pFailSchedule->GetId() : SCHED_NONE; - } - } - - if ( saveHeader.version >= AI_EXTENDED_SAVE_HEADER_FIRST_VERSION_WITH_CONDITIONS ) - { - restore.StartBlock(); - RestoreConditions( restore, &m_Conditions ); - RestoreConditions( restore, &m_CustomInterruptConditions ); - RestoreConditions( restore, &m_ConditionsPreIgnore ); - CAI_ScheduleBits ignoreConditions; - RestoreConditions( restore, &ignoreConditions ); - ignoreConditions.Not( &m_InverseIgnoreConditions ); - restore.EndBlock(); - } - - if ( saveHeader.version >= AI_EXTENDED_SAVE_HEADER_FIRST_VERSION_WITH_NAVIGATOR_SAVE ) - { - restore.StartBlock(); - GetNavigator()->Restore( restore ); - restore.EndBlock(); - } - - // do a normal restore - int status = BaseClass::Restore(restore); - if ( !status ) - return 0; - - bool bLostSequence = false; - if ( saveHeader.version >= AI_EXTENDED_SAVE_HEADER_FIRST_VERSION_WITH_SEQUENCE && saveHeader.szSequence[0] && GetModelPtr() ) - { - SetSequence( LookupSequence( saveHeader.szSequence ) ); - if ( GetSequence() == ACT_INVALID ) - { - DevMsg( this, AIMF_IGNORE_SELECTED, "Discarding missing sequence %s on load.\n", saveHeader.szSequence ); - SetSequence( 0 ); - bLostSequence = true; - } - - Assert( IsValidSequence( GetSequence() ) ); - } - - bool bLostScript = ( m_NPCState == NPC_STATE_SCRIPT && m_hCine == NULL ); - bool bDiscardScheduleState = ( bLostScript || - bLostSequence || - saveHeader.szSchedule[0] == 0 || - saveHeader.version < AI_EXTENDED_SAVE_HEADER_RESET_VERSION || - ( (saveHeader.flags & AIESH_HAD_ENEMY) && !GetEnemy() ) || - ( (saveHeader.flags & AIESH_HAD_TARGET) && !GetTarget() ) ); - - if ( m_ScheduleState.taskFailureCode >= NUM_FAIL_CODES ) - m_ScheduleState.taskFailureCode = FAIL_NO_TARGET; // must have been a string, gotta punt - - if ( !bDiscardScheduleState ) - { - m_pSchedule = g_AI_SchedulesManager.GetScheduleByName( saveHeader.szSchedule ); - if ( m_pSchedule ) - { - CRC32_t scheduleCrc; - CRC32_Init( &scheduleCrc ); - CRC32_ProcessBuffer( &scheduleCrc, (void *)m_pSchedule->GetTaskList(), m_pSchedule->NumTasks() * sizeof(Task_t) ); - CRC32_Final( &scheduleCrc ); - - if ( scheduleCrc != saveHeader.scheduleCrc ) - { - m_pSchedule = NULL; - } - } - } - - if ( !m_pSchedule ) - bDiscardScheduleState = true; - - if ( !bDiscardScheduleState ) - m_bDoPostRestoreRefindPath = ( ( saveHeader.flags & AIESH_HAD_NAVGOAL) != 0 ); - else - { - m_bDoPostRestoreRefindPath = false; - DiscardScheduleState(); - } - - return status; -} - -//------------------------------------- - -void CAI_BaseNPC::SaveConditions( ISave &save, const CAI_ScheduleBits &conditions ) -{ - for (int i = 0; i < MAX_CONDITIONS; i++) - { - if (conditions.GetBit(i)) - { - const char *pszConditionName = ConditionName(AI_RemapToGlobal(i)); - if ( !pszConditionName ) - break; - save.WriteString( pszConditionName ); - } - } - save.WriteString( "" ); -} - -//------------------------------------- - -void CAI_BaseNPC::RestoreConditions( IRestore &restore, CAI_ScheduleBits *pConditions ) -{ - pConditions->ClearAllBits(); - char szCondition[256]; - for (;;) - { - restore.ReadString( szCondition, sizeof(szCondition), 0 ); - if ( !szCondition[0] ) - break; - int iCondition = GetSchedulingSymbols()->ConditionSymbolToId( szCondition ); - if ( iCondition != -1 ) - pConditions->SetBit( AI_RemapFromGlobal( iCondition ) ); - } -} - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -bool CAI_BaseNPC::KeyValue( const char *szKeyName, const char *szValue ) -{ - bool bResult = BaseClass::KeyValue( szKeyName, szValue ); - - if( !bResult ) - { - // Defer unhandled Keys to behaviors - CAI_BehaviorBase **ppBehaviors = AccessBehaviors(); - - for ( int i = 0; i < NumBehaviors(); i++ ) - { - if( ppBehaviors[ i ]->KeyValue( szKeyName, szValue ) ) - { - return true; - } - } - } - - return bResult; -} - -//----------------------------------------------------------------------------- -// Purpose: Debug function to make this NPC freeze in place (or unfreeze). -//----------------------------------------------------------------------------- -void CAI_BaseNPC::ToggleFreeze(void) -{ - if (!IsCurSchedule(SCHED_NPC_FREEZE)) - { - // Freeze them. - SetCondition(COND_NPC_FREEZE); - SetMoveType(MOVETYPE_NONE); - SetGravity(0); - SetLocalAngularVelocity(vec3_angle); - SetAbsVelocity( vec3_origin ); - } - else - { - // Unfreeze them. - SetCondition(COND_NPC_UNFREEZE); - m_Activity = ACT_RESET; - - // BUGBUG: this might not be the correct movetype! - SetMoveType( MOVETYPE_STEP ); - - // Doesn't restore gravity to the original value, but who cares? - SetGravity(1); - } -} - - -//----------------------------------------------------------------------------- -// Purpose: Written by subclasses macro to load schedules -// Input : -// Output : -//----------------------------------------------------------------------------- -bool CAI_BaseNPC::LoadSchedules(void) -{ - return true; -} - -//----------------------------------------------------------------------------- - -bool CAI_BaseNPC::LoadedSchedules(void) -{ - return true; -} - -//----------------------------------------------------------------------------- -// Purpose: Constructor -// Input : -// Output : -//----------------------------------------------------------------------------- -CAI_BaseNPC::CAI_BaseNPC(void) - : m_UnreachableEnts( 0, 4 ) -{ - m_pMotor = NULL; - m_pMoveProbe = NULL; - m_pNavigator = NULL; - m_pSenses = NULL; - m_pPathfinder = NULL; - m_pLocalNavigator = NULL; - - m_pSchedule = NULL; - m_IdealSchedule = SCHED_NONE; - -#ifdef _DEBUG - // necessary since in debug, we initialize vectors to NAN for debugging - m_vecLastPosition.Init(); - m_vSavePosition.Init(); - m_vEyeLookTarget.Init(); - m_vCurEyeTarget.Init(); - m_vDefaultEyeOffset.Init(); - -#endif - m_bDidDeathCleanup = false; - - m_afCapability = 0; // Make sure this is cleared in the base class - - SetHullType(HULL_HUMAN); // Give human hull by default, subclasses should override - - m_iMySquadSlot = SQUAD_SLOT_NONE; - m_flSumDamage = 0; - m_flLastDamageTime = 0; - m_flLastAttackTime = 0; - m_flSoundWaitTime = 0; - m_flNextEyeLookTime = 0; - m_flHeadYaw = 0; - m_flHeadPitch = 0; - m_spawnEquipment = NULL_STRING; - m_pEnemies = new CAI_Enemies; - m_flEyeIntegRate = 0.95; - SetTarget( NULL ); - - m_pSquad = NULL; - - m_flMoveWaitFinished = 0; - - m_fIsUsingSmallHull = true; - - m_bHintGroupNavLimiting = false; - - m_fNoDamageDecal = false; - - SetInAScript( false ); - - m_pLockedBestSound = new CSound; - m_pLockedBestSound->m_iType = SOUND_NONE; - - // ---------------------------- - // Debugging fields - // ---------------------------- - m_interruptText = NULL; - m_failText = NULL; - m_failedSchedule = NULL; - m_interuptSchedule = NULL; - m_nDebugPauseIndex = 0; - - g_AI_Manager.AddAI( this ); - - if ( g_AI_Manager.NumAIs() == 1 ) - { - m_AnyUpdateEnemyPosTimer.Force(); - gm_flTimeLastSpawn = -1; - gm_nSpawnedThisFrame = 0; - gm_iNextThinkRebalanceTick = 0; - } - - m_iFrameBlocked = -1; - m_bInChoreo = true; // assume so until call to UpdateEfficiency() - - SetCollisionGroup( COLLISION_GROUP_NPC ); -} - -//----------------------------------------------------------------------------- -// Purpose: Destructor -// Input : -// Output : -//----------------------------------------------------------------------------- -CAI_BaseNPC::~CAI_BaseNPC(void) -{ - g_AI_Manager.RemoveAI( this ); - - delete m_pLockedBestSound; - - RemoveMemory(); - - delete m_pPathfinder; - delete m_pNavigator; - delete m_pMotor; - delete m_pLocalNavigator; - delete m_pMoveProbe; - delete m_pSenses; - delete m_pTacticalServices; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CAI_BaseNPC::UpdateOnRemove(void) -{ - if ( !m_bDidDeathCleanup ) - { - if ( m_NPCState == NPC_STATE_DEAD ) - DevMsg( "May not have cleaned up on NPC death\n"); - - CleanupOnDeath( NULL, false ); - } - - // Chain at end to mimic destructor unwind order - BaseClass::UpdateOnRemove(); -} - -//----------------------------------------------------------------------------- - -bool CAI_BaseNPC::CreateComponents() -{ - m_pSenses = CreateSenses(); - if ( !m_pSenses ) - return false; - - m_pMotor = CreateMotor(); - if ( !m_pMotor ) - return false; - - m_pLocalNavigator = CreateLocalNavigator(); - if ( !m_pLocalNavigator ) - return false; - - m_pMoveProbe = CreateMoveProbe(); - if ( !m_pMoveProbe ) - return false; - - m_pNavigator = CreateNavigator(); - if ( !m_pNavigator ) - return false; - - m_pPathfinder = CreatePathfinder(); - if ( !m_pPathfinder ) - return false; - - m_pTacticalServices = CreateTacticalServices(); - if ( !m_pTacticalServices ) - return false; - - m_MoveAndShootOverlay.SetOuter( this ); - - m_pMotor->Init( m_pLocalNavigator ); - m_pLocalNavigator->Init( m_pNavigator ); - m_pNavigator->Init( g_pBigAINet ); - m_pPathfinder->Init( g_pBigAINet ); - m_pTacticalServices->Init( g_pBigAINet ); - - return true; -} - -//----------------------------------------------------------------------------- - -CAI_Senses *CAI_BaseNPC::CreateSenses() -{ - CAI_Senses *pSenses = new CAI_Senses; - pSenses->SetOuter( this ); - return pSenses; -} - -//----------------------------------------------------------------------------- - -CAI_Motor *CAI_BaseNPC::CreateMotor() -{ - return new CAI_Motor( this ); -} - -//----------------------------------------------------------------------------- - -CAI_MoveProbe *CAI_BaseNPC::CreateMoveProbe() -{ - return new CAI_MoveProbe( this ); -} - -//----------------------------------------------------------------------------- - -CAI_LocalNavigator *CAI_BaseNPC::CreateLocalNavigator() -{ - return new CAI_LocalNavigator( this ); -} - -//----------------------------------------------------------------------------- - -CAI_TacticalServices *CAI_BaseNPC::CreateTacticalServices() -{ - return new CAI_TacticalServices( this ); -} - -//----------------------------------------------------------------------------- - -CAI_Navigator *CAI_BaseNPC::CreateNavigator() -{ - return new CAI_Navigator( this ); -} - -//----------------------------------------------------------------------------- - -CAI_Pathfinder *CAI_BaseNPC::CreatePathfinder() -{ - return new CAI_Pathfinder( this ); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CAI_BaseNPC::InputSetRelationship( inputdata_t &inputdata ) -{ - AddRelationship( inputdata.value.String(), inputdata.pActivator ); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CAI_BaseNPC::InputSetHealth( inputdata_t &inputdata ) -{ - int iNewHealth = inputdata.value.Int(); - int iDelta = abs(GetHealth() - iNewHealth); - if ( iNewHealth > GetHealth() ) - { - TakeHealth( iDelta, DMG_GENERIC ); - } - else if ( iNewHealth < GetHealth() ) - { - TakeDamage( CTakeDamageInfo( this, this, iDelta, DMG_GENERIC ) ); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CAI_BaseNPC::InputBeginRappel( inputdata_t &inputdata ) -{ - BeginRappel(); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CAI_BaseNPC::InputSetSquad( inputdata_t &inputdata ) -{ - if ( !( CapabilitiesGet() & bits_CAP_SQUAD ) ) - { - Warning("SetSquad Input received for NPC %s, but that NPC can't use squads.\n", GetDebugName() ); - return; - } - - m_SquadName = inputdata.value.StringID(); - - // Removing from squad? - if ( m_SquadName == NULL_STRING ) - { - if ( m_pSquad ) - { - m_pSquad->RemoveFromSquad(this, true); - m_pSquad = NULL; - } - } - else - { - m_pSquad = g_AI_SquadManager.FindCreateSquad(this, m_SquadName); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CAI_BaseNPC::InputWake( inputdata_t &inputdata ) -{ - Wake(); - - // Check if we have a path to follow. This is normally done in StartNPC, - // but putting the NPC to sleep will cancel it, so we have to do it again. - if ( m_target != NULL_STRING )// this npc has a target - { - // Find the npc's initial target entity, stash it - SetGoalEnt( gEntList.FindEntityByName( NULL, m_target ) ); - - if ( !GetGoalEnt() ) - { - Warning( "ReadyNPC()--%s couldn't find target %s\n", GetClassname(), STRING(m_target)); - } - else - { - StartTargetHandling( GetGoalEnt() ); - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CAI_BaseNPC::InputForgetEntity( inputdata_t &inputdata ) -{ - const char *pszEntityToForget = inputdata.value.String(); - - if ( g_pDeveloper->GetInt() && pszEntityToForget[strlen( pszEntityToForget ) - 1] == '*' ) - DevMsg( "InputForgetEntity does not support wildcards\n" ); - - CBaseEntity *pEntity = gEntList.FindEntityByName( NULL, pszEntityToForget ); - if ( pEntity ) - { - if ( GetEnemy() == pEntity ) - { - SetEnemy( NULL ); - SetIdealState( NPC_STATE_ALERT ); - } - GetEnemies()->ClearMemory( pEntity ); - } -} - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -void CAI_BaseNPC::InputIgnoreDangerSounds( inputdata_t &inputdata ) -{ - // Default is 10 seconds. - float flDelay = 10.0f; - - if( inputdata.value.Float() > 0.0f ) - { - flDelay = inputdata.value.Float(); - } - - m_flIgnoreDangerSoundsUntil = gpGlobals->curtime + flDelay; -} - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -void CAI_BaseNPC::InputUpdateEnemyMemory( inputdata_t &inputdata ) -{ - const char *pszEnemy = inputdata.value.String(); - CBaseEntity *pEnemy = gEntList.FindEntityByName( NULL, pszEnemy ); - - if( pEnemy ) - { - UpdateEnemyMemory( pEnemy, pEnemy->GetAbsOrigin(), this ); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : &inputdata - -//----------------------------------------------------------------------------- -void CAI_BaseNPC::InputOutsideTransition( inputdata_t &inputdata ) -{ -} - -//----------------------------------------------------------------------------- -// Purpose: Called when this NPC transitions to another level with the player -// Input : &inputdata - -//----------------------------------------------------------------------------- -void CAI_BaseNPC::InputInsideTransition( inputdata_t &inputdata ) -{ - CleanupScriptsOnTeleport( true ); - - // If we're inside a vcd, tell it to stop - if ( IsCurSchedule( SCHED_SCENE_GENERIC, false ) ) - { - RemoveActorFromScriptedScenes( this, false ); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CAI_BaseNPC::CleanupScriptsOnTeleport( bool bEnrouteAsWell ) -{ - // If I'm running a scripted sequence, I need to clean up - if ( m_NPCState == NPC_STATE_SCRIPT && m_hCine ) - { - if ( !bEnrouteAsWell ) - { - // Don't cancel scripts when they're teleporting an NPC - // to the script due to CINE_MOVETO_TELEPORT. - if ( m_hCine->IsTeleportingDueToMoveTo() ) - return; - } - - m_hCine->ScriptEntityCancel( m_hCine, true ); - } -} - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -bool CAI_BaseNPC::HandleInteraction(int interactionType, void *data, CBaseCombatCharacter* sourceEnt) -{ -#ifdef HL2_DLL - if ( interactionType == g_interactionBarnacleVictimGrab ) - { - // Make the victim stop thinking so they're as good as dead without - // shocking the system by destroying the entity. - StopLoopingSounds(); - BarnacleDeathSound(); - SetThink( NULL ); - - // Gag the NPC so they won't talk anymore - AddSpawnFlags( SF_NPC_GAG ); - - // Drop any weapon they're holding - if ( GetActiveWeapon() ) - { - Weapon_Drop( GetActiveWeapon() ); - } - - return true; - } -#endif // HL2_DLL - - return BaseClass::HandleInteraction( interactionType, data, sourceEnt ); -} - -CAI_BaseNPC *CAI_BaseNPC::GetInteractionPartner( void ) -{ - if ( m_hInteractionPartner == NULL ) - return NULL; - - return m_hInteractionPartner->MyNPCPointer(); -} - -//----------------------------------------------------------------------------- -// Purpose: Called when exiting a scripted sequence. -// Output : Returns true if alive, false if dead. -//----------------------------------------------------------------------------- -bool CAI_BaseNPC::ExitScriptedSequence( ) -{ - if ( m_lifeState == LIFE_DYING ) - { - // is this legal? - // BUGBUG -- This doesn't call Killed() - SetIdealState( NPC_STATE_DEAD ); - return false; - } - - if (m_hCine) - { - m_hCine->CancelScript( ); - } - - return true; -} - - -bool CAI_BaseNPC::CineCleanup() -{ - CAI_ScriptedSequence *pOldCine = m_hCine; - - bool bDestroyCine = false; - if ( IsRunningDynamicInteraction() ) - { - bDestroyCine = true; - - // Re-enable physics collisions between me & the other NPC - if ( m_hInteractionPartner ) - { - PhysEnableEntityCollisions( this, m_hInteractionPartner ); - //Msg("%s(%s) enabled collisions with %s(%s) at %0.2f\n", GetClassName(), GetDebugName(), m_hInteractionPartner->GetClassName(), m_hInteractionPartner->GetDebugName(), gpGlobals->curtime ); - } - - if ( m_hForcedInteractionPartner ) - { - // We've finished a forced interaction. Let the mapmaker know. - m_OnForcedInteractionFinished.FireOutput( this, this ); - } - - // Clear interaction partner, because we're not running a scripted sequence anymore - m_hInteractionPartner = NULL; - CleanupForcedInteraction(); - } - - // am I linked to a cinematic? - if (m_hCine) - { - // okay, reset me to what it thought I was before - m_hCine->SetTarget( NULL ); - // NOTE that this will have had EF_NODRAW removed in script.dll when it's cached off - SetEffects( m_hCine->m_saved_effects ); - } - else - { - // arg, punt - AddSolidFlags( FSOLID_NOT_STANDABLE ); - } - m_hCine = NULL; - SetTarget( NULL ); - SetGoalEnt( NULL ); - if (m_lifeState == LIFE_DYING) - { - // last frame of death animation? - m_iHealth = 0; - AddSolidFlags( FSOLID_NOT_SOLID ); - SetState( NPC_STATE_DEAD ); - m_lifeState = LIFE_DEAD; - UTIL_SetSize( this, WorldAlignMins(), Vector(WorldAlignMaxs().x, WorldAlignMaxs().y, WorldAlignMins().z + 2) ); - - if ( pOldCine && pOldCine->HasSpawnFlags( SF_SCRIPT_LEAVECORPSE ) ) - { - SetUse( NULL ); // BUGBUG -- This doesn't call Killed() - SetThink( NULL ); // This will probably break some stuff - SetTouch( NULL ); - } - else - SUB_StartFadeOut(); // SetThink( SUB_DoNothing ); - - - //Not becoming a ragdoll, so set the NOINTERP flag on. - if ( CanBecomeRagdoll() == false ) - { - StopAnimation(); - AddEffects( EF_NOINTERP ); // Don't interpolate either, assume the corpse is positioned in its final resting place - } - - SetMoveType( MOVETYPE_NONE ); - return false; - } - - // If we actually played a sequence - if ( pOldCine && pOldCine->m_iszPlay != NULL_STRING && pOldCine->PlayedSequence() ) - { - if ( !pOldCine->HasSpawnFlags(SF_SCRIPT_NOSCRIPTMOVEMENT) ) - { - // reset position - Vector new_origin; - QAngle new_angle; - GetBonePosition( 0, new_origin, new_angle ); - - // Figure out how far they have moved - // We can't really solve this problem because we can't query the movement of the origin relative - // to the sequence. We can get the root bone's position as we do here, but there are - // cases where the root bone is in a different relative position to the entity's origin - // before/after the sequence plays. So we are stuck doing this: - - // !!!HACKHACK: Float the origin up and drop to floor because some sequences have - // irregular motion that can't be properly accounted for. - - // UNDONE: THIS SHOULD ONLY HAPPEN IF WE ACTUALLY PLAYED THE SEQUENCE. - Vector oldOrigin = GetLocalOrigin(); - - // UNDONE: ugly hack. Don't move NPC if they don't "seem" to move - // this really needs to be done with the AX,AY,etc. flags, but that aren't consistantly - // being set, so animations that really do move won't be caught. - if ((oldOrigin - new_origin).Length2D() < 8.0) - new_origin = oldOrigin; - - Vector origin = GetLocalOrigin(); - - origin.x = new_origin.x; - origin.y = new_origin.y; - origin.z += 1; - - if ( GetFlags() & FL_FLY ) - { - origin.z = new_origin.z; - SetLocalOrigin( origin ); - } - else - { - SetLocalOrigin( origin ); - - int drop = UTIL_DropToFloor( this, MASK_NPCSOLID ); - - // Origin in solid? Set to org at the end of the sequence - if ( drop < 0 ) - { - SetLocalOrigin( oldOrigin ); - } - else if ( drop == 0 ) // Hanging in air? - { - Vector origin = GetLocalOrigin(); - origin.z = new_origin.z; - SetLocalOrigin( origin ); - SetGroundEntity( NULL ); - } - } - - origin = GetLocalOrigin(); - - // teleport if it's a non-trivial distance - if ((oldOrigin - origin).Length() > 8.0) - { - // Call teleport to notify - Teleport( &origin, NULL, NULL ); - SetLocalOrigin( origin ); - AddEffects( EF_NOINTERP ); - } - - if ( m_iHealth <= 0 ) - { - // Dropping out because he got killed - SetIdealState( NPC_STATE_DEAD ); - SetCondition( COND_LIGHT_DAMAGE ); - m_lifeState = LIFE_DYING; - } - } - - // We should have some animation to put these guys in, but for now it's idle. - // Due to NOINTERP above, there won't be any blending between this anim & the sequence - m_Activity = ACT_RESET; - } - - // set them back into a normal state - if ( m_iHealth > 0 ) - { - SetIdealState( NPC_STATE_IDLE ); - } - else - { - // Dropping out because he got killed - SetIdealState( NPC_STATE_DEAD ); - SetCondition( COND_LIGHT_DAMAGE ); - } - - // SetAnimation( m_NPCState ); - CLEARBITS(m_spawnflags, SF_NPC_WAIT_FOR_SCRIPT ); - - if ( bDestroyCine ) - { - UTIL_Remove( pOldCine ); - } - - return true; -} - -//----------------------------------------------------------------------------- - -void CAI_BaseNPC::Teleport( const Vector *newPosition, const QAngle *newAngles, const Vector *newVelocity ) -{ - CleanupScriptsOnTeleport( false ); - - BaseClass::Teleport( newPosition, newAngles, newVelocity ); -} - -//----------------------------------------------------------------------------- - -bool CAI_BaseNPC::FindSpotForNPCInRadius( Vector *pResult, const Vector &vStartPos, CAI_BaseNPC *pNPC, float radius, bool bOutOfPlayerViewcone ) -{ - CBasePlayer *pPlayer = AI_GetSinglePlayer(); - QAngle fan; - - fan.x = 0; - fan.z = 0; - - for( fan.y = 0 ; fan.y < 360 ; fan.y += 18.0 ) - { - Vector vecTest; - Vector vecDir; - - AngleVectors( fan, &vecDir ); - - vecTest = vStartPos + vecDir * radius; - - if ( bOutOfPlayerViewcone && pPlayer && !pPlayer->FInViewCone( vecTest ) ) - continue; - - trace_t tr; - - UTIL_TraceLine( vecTest, vecTest - Vector( 0, 0, 8192 ), MASK_SHOT, pNPC, COLLISION_GROUP_NONE, &tr ); - if( tr.fraction == 1.0 ) - { - continue; - } - - UTIL_TraceHull( tr.endpos, - tr.endpos + Vector( 0, 0, 10 ), - pNPC->GetHullMins(), - pNPC->GetHullMaxs(), - MASK_NPCSOLID, - pNPC, - COLLISION_GROUP_NONE, - &tr ); - - if( tr.fraction == 1.0 && pNPC->GetMoveProbe()->CheckStandPosition( tr.endpos, MASK_NPCSOLID ) ) - { - *pResult = tr.endpos; - return true; - } - } - return false; -} - -//----------------------------------------------------------------------------- - -bool CAI_BaseNPC::IsNavigationUrgent() -{ - // return true if the navigation is for something that can't react well to failure - if ( IsCurSchedule( SCHED_SCRIPTED_WALK, false ) || - IsCurSchedule( SCHED_SCRIPTED_RUN, false ) || - IsCurSchedule( SCHED_SCRIPTED_CUSTOM_MOVE, false ) || - ( IsCurSchedule( SCHED_SCENE_GENERIC, false ) && IsInLockedScene() ) ) - { - return true; - } - return false; -} - -//----------------------------------------------------------------------------- - -bool CAI_BaseNPC::ShouldFailNav( bool bMovementFailed ) -{ - // Robin: Only fail movement. Fail route building so that we'll keep trying to build routes. - // Otherwise, we can can get stuck inside a movement task without a route. - if ( bMovementFailed && IsNavigationUrgent()) - { - return false; - } - return true; -} - -Navigation_t CAI_BaseNPC::GetNavType() const -{ - return m_pNavigator->GetNavType(); -} - -void CAI_BaseNPC::SetNavType( Navigation_t navType ) -{ - m_pNavigator->SetNavType( navType ); -} - -//----------------------------------------------------------------------------- -// NPCs can override this to tweak with how costly particular movements are -//----------------------------------------------------------------------------- -bool CAI_BaseNPC::MovementCost( int moveType, const Vector &vecStart, const Vector &vecEnd, float *pCost ) -{ - // We have nothing to say on the matter, but derived classes might - return false; -} - -bool CAI_BaseNPC::OverrideMoveFacing( const AILocalMoveGoal_t &move, float flInterval ) -{ - return false; -} - -bool CAI_BaseNPC::OverrideMove( float flInterval ) -{ - return false; -} - - -//========================================================= -// VecToYaw - turns a directional vector into a yaw value -// that points down that vector. -//========================================================= -float CAI_BaseNPC::VecToYaw( const Vector &vecDir ) -{ - if (vecDir.x == 0 && vecDir.y == 0 && vecDir.z == 0) - return GetLocalAngles().y; - - return UTIL_VecToYaw( vecDir ); -} - -//----------------------------------------------------------------------------- -// Inherited from IAI_MotorMovementServices -//----------------------------------------------------------------------------- -float CAI_BaseNPC::CalcYawSpeed( void ) -{ - // Negative values are invalud - return -1.0f; -} - -bool CAI_BaseNPC::OnCalcBaseMove( AILocalMoveGoal_t *pMoveGoal, - float distClear, - AIMoveResult_t *pResult ) -{ - if ( pMoveGoal->directTrace.pObstruction ) - { - CBasePropDoor *pPropDoor = dynamic_cast( pMoveGoal->directTrace.pObstruction ); - if ( pPropDoor && OnUpcomingPropDoor( pMoveGoal, pPropDoor, distClear, pResult ) ) - { - return true; - } - } - - return false; -} - - -bool CAI_BaseNPC::OnObstructionPreSteer( AILocalMoveGoal_t *pMoveGoal, - float distClear, - AIMoveResult_t *pResult ) -{ - if ( pMoveGoal->directTrace.pObstruction ) - { - CBaseDoor *pDoor = dynamic_cast( pMoveGoal->directTrace.pObstruction ); - if ( pDoor && OnObstructingDoor( pMoveGoal, pDoor, distClear, pResult ) ) - { - return true; - } - } - - return false; -} - - -bool CAI_BaseNPC::OnObstructingDoor( AILocalMoveGoal_t *pMoveGoal, - CBaseDoor *pDoor, - float distClear, - AIMoveResult_t *pResult ) -{ - if ( pMoveGoal->maxDist < distClear ) - return false; - - // By default, NPCs don't know how to open doors - if ( pDoor->m_toggle_state == TS_AT_BOTTOM || pDoor->m_toggle_state == TS_GOING_DOWN ) - { - if ( distClear < 0.1 ) - { - *pResult = AIMR_BLOCKED_ENTITY; - } - else - { - pMoveGoal->maxDist = distClear; - *pResult = AIMR_OK; - } - - return true; - } - - return false; -} - - -//----------------------------------------------------------------------------- -// Purpose: -// Input : pMoveGoal - -// pDoor - -// distClear - -// default - -// spawn - -// oldorg - -// pfPosition - -// neworg - -// Output : Returns true if movement is solved, false otherwise. -//----------------------------------------------------------------------------- - -bool CAI_BaseNPC::OnUpcomingPropDoor( AILocalMoveGoal_t *pMoveGoal, - CBasePropDoor *pDoor, - float distClear, - AIMoveResult_t *pResult ) -{ - if ( (pMoveGoal->flags & AILMG_TARGET_IS_GOAL) && pMoveGoal->maxDist < distClear ) - return false; - - if ( pMoveGoal->maxDist + GetHullWidth() * .25 < distClear ) - return false; - - if (pDoor == m_hOpeningDoor) - { - if ( pDoor->IsNPCOpening( this ) ) - { - // We're in the process of opening the door, don't be blocked by it. - pMoveGoal->maxDist = distClear; - *pResult = AIMR_OK; - return true; - } - m_hOpeningDoor = NULL; - } - - if ((CapabilitiesGet() & bits_CAP_DOORS_GROUP) && !pDoor->IsDoorLocked() && (pDoor->IsDoorClosed() || pDoor->IsDoorClosing())) - { - AI_Waypoint_t *pOpenDoorRoute = NULL; - - opendata_t opendata; - pDoor->GetNPCOpenData(this, opendata); - - // dvs: FIXME: local route might not be sufficient - pOpenDoorRoute = GetPathfinder()->BuildLocalRoute( - GetLocalOrigin(), - opendata.vecStandPos, - NULL, - bits_WP_TO_DOOR | bits_WP_DONT_SIMPLIFY, - NO_NODE, - bits_BUILD_GROUND | bits_BUILD_IGNORE_NPCS, - 0.0); - - if ( pOpenDoorRoute ) - { - if ( AIIsDebuggingDoors(this) ) - { - NDebugOverlay::Cross3D(opendata.vecStandPos + Vector(0,0,1), 32, 255, 255, 255, false, 1.0 ); - Msg( "Opening door!\n" ); - } - - // Attach the door to the waypoint so we open it when we get there. - // dvs: FIXME: this is kind of bullshit, I need to find the exact waypoint to open the door - // should I just walk the path until I find it? - pOpenDoorRoute->m_hData = pDoor; - - GetNavigator()->GetPath()->PrependWaypoints( pOpenDoorRoute ); - - m_hOpeningDoor = pDoor; - pMoveGoal->maxDist = distClear; - *pResult = AIMR_CHANGE_TYPE; - - return true; - } - else - AIDoorDebugMsg( this, "Failed create door route!\n" ); - } - - return false; -} - - -//----------------------------------------------------------------------------- -// Purpose: Called by the navigator to initiate the opening of a prop_door -// that is in our way. -//----------------------------------------------------------------------------- -void CAI_BaseNPC::OpenPropDoorBegin( CBasePropDoor *pDoor ) -{ - // dvs: not quite working, disabled for now. - //opendata_t opendata; - //pDoor->GetNPCOpenData(this, opendata); - // - //if (HaveSequenceForActivity(opendata.eActivity)) - //{ - // SetIdealActivity(opendata.eActivity); - //} - //else - { - // We don't have an appropriate sequence, just open the door magically. - OpenPropDoorNow( pDoor ); - } -} - - -//----------------------------------------------------------------------------- -// Purpose: Called when we are trying to open a prop_door and it's time to start -// the door moving. This is called either in response to an anim event -// or as a fallback when we don't have an appropriate open activity. -//----------------------------------------------------------------------------- -void CAI_BaseNPC::OpenPropDoorNow( CBasePropDoor *pDoor ) -{ - // Start the door moving. - pDoor->NPCOpenDoor(this); - - // Wait for the door to finish opening before trying to move through the doorway. - m_flMoveWaitFinished = gpGlobals->curtime + pDoor->GetOpenInterval(); -} - - -//----------------------------------------------------------------------------- -// Purpose: Called when the door we were trying to open becomes fully open. -// Input : pDoor - -//----------------------------------------------------------------------------- -void CAI_BaseNPC::OnDoorFullyOpen(CBasePropDoor *pDoor) -{ - // We're done with the door. - m_hOpeningDoor = NULL; -} - - -//----------------------------------------------------------------------------- -// Purpose: Called when the door we were trying to open becomes blocked before opening. -// Input : pDoor - -//----------------------------------------------------------------------------- -void CAI_BaseNPC::OnDoorBlocked(CBasePropDoor *pDoor) -{ - // dvs: FIXME: do something so that we don't loop forever trying to open this door - // not clearing out the door handle will cause the NPC to invalidate the connection - // We're done with the door. - //m_hOpeningDoor = NULL; -} - - -//----------------------------------------------------------------------------- -// Purpose: Template NPCs are marked as templates by the level designer. They -// do not spawn, but their keyvalues are saved for use by a template -// spawner. -//----------------------------------------------------------------------------- -bool CAI_BaseNPC::IsTemplate( void ) -{ - return HasSpawnFlags( SF_NPC_TEMPLATE ); -} - - - -//----------------------------------------------------------------------------- -// -// Movement code for walking + flying -// -//----------------------------------------------------------------------------- -int CAI_BaseNPC::FlyMove( const Vector& pfPosition, unsigned int mask ) -{ - Vector oldorg, neworg; - trace_t trace; - - // try the move - VectorCopy( GetAbsOrigin(), oldorg ); - VectorAdd( oldorg, pfPosition, neworg ); - UTIL_TraceEntity( this, oldorg, neworg, mask, &trace ); - if (trace.fraction == 1) - { - if ( (GetFlags() & FL_SWIM) && enginetrace->GetPointContents(trace.endpos) == CONTENTS_EMPTY ) - return false; // swim monster left water - - SetAbsOrigin( trace.endpos ); - PhysicsTouchTriggers(); - return true; - } - - return false; -} - - -//----------------------------------------------------------------------------- -// Purpose: -// Input : ent - -// Dir - Normalized direction vector for movement. -// dist - Distance along 'Dir' to move. -// iMode - -// Output : Returns nonzero on success, zero on failure. -//----------------------------------------------------------------------------- -int CAI_BaseNPC::WalkMove( const Vector& vecPosition, unsigned int mask ) -{ - if ( GetFlags() & (FL_FLY | FL_SWIM) ) - { - return FlyMove( vecPosition, mask ); - } - - if ( (GetFlags() & FL_ONGROUND) == 0 ) - { - return 0; - } - - trace_t trace; - Vector oldorg, neworg, end; - Vector move( vecPosition[0], vecPosition[1], 0.0f ); - VectorCopy( GetAbsOrigin(), oldorg ); - VectorAdd( oldorg, move, neworg ); - - // push down from a step height above the wished position - float flStepSize = sv_stepsize.GetFloat(); - neworg[2] += flStepSize; - VectorCopy(neworg, end); - end[2] -= flStepSize*2; - - UTIL_TraceEntity( this, neworg, end, mask, &trace ); - if ( trace.allsolid ) - return false; - - if (trace.startsolid) - { - neworg[2] -= flStepSize; - UTIL_TraceEntity( this, neworg, end, mask, &trace ); - if ( trace.allsolid || trace.startsolid ) - return false; - } - - if (trace.fraction == 1) - { - // if monster had the ground pulled out, go ahead and fall - if ( GetFlags() & FL_PARTIALGROUND ) - { - SetAbsOrigin( oldorg + move ); - PhysicsTouchTriggers(); - SetGroundEntity( NULL ); - return true; - } - - return false; // walked off an edge - } - - // check point traces down for dangling corners - SetAbsOrigin( trace.endpos ); - - if (UTIL_CheckBottom( this, NULL, flStepSize ) == 0) - { - if ( GetFlags() & FL_PARTIALGROUND ) - { - // entity had floor mostly pulled out from underneath it - // and is trying to correct - PhysicsTouchTriggers(); - return true; - } - - // Reset to original position - SetAbsOrigin( oldorg ); - return false; - } - - if ( GetFlags() & FL_PARTIALGROUND ) - { - // Con_Printf ("back on ground\n"); - RemoveFlag( FL_PARTIALGROUND ); - } - - // the move is ok - SetGroundEntity( trace.m_pEnt ); - PhysicsTouchTriggers(); - return true; -} - -//----------------------------------------------------------------------------- - -static void AIMsgGuts( CAI_BaseNPC *pAI, unsigned flags, const char *pszMsg ) -{ - int len = strlen( pszMsg ); - const char *pszFmt2 = NULL; - - if ( len && pszMsg[len-1] == '\n' ) - { - (const_cast(pszMsg))[len-1] = 0; - pszFmt2 = "%s (%s: %d/%s) [%d]\n"; - } - else - pszFmt2 = "%s (%s: %d/%s) [%d]"; - - DevMsg( pszFmt2, - pszMsg, - pAI->GetClassname(), - pAI->entindex(), - ( pAI->GetEntityName() == NULL_STRING ) ? "" : STRING(pAI->GetEntityName()), - gpGlobals->tickcount ); -} - -void DevMsg( CAI_BaseNPC *pAI, unsigned flags, const char *pszFormat, ... ) -{ - if ( (flags & AIMF_IGNORE_SELECTED) || (pAI->m_debugOverlays & OVERLAY_NPC_SELECTED_BIT) ) - { - AIMsgGuts( pAI, flags, CFmtStr( &pszFormat ) ); - } -} - -//----------------------------------------------------------------------------- - -void DevMsg( CAI_BaseNPC *pAI, const char *pszFormat, ... ) -{ - if ( (pAI->m_debugOverlays & OVERLAY_NPC_SELECTED_BIT) ) - { - AIMsgGuts( pAI, 0, CFmtStr( &pszFormat ) ); - } -} - -//----------------------------------------------------------------------------- - -bool CAI_BaseNPC::IsPlayerAlly( CBasePlayer *pPlayer ) -{ - if ( pPlayer == NULL ) - { - // in multiplayer mode we need a valid pPlayer - // or override this virtual function - if ( !AI_IsSinglePlayer() ) - return false; - - // NULL means single player mode - pPlayer = UTIL_GetLocalPlayer(); - } - - return ( !pPlayer || IRelationType( pPlayer ) == D_LI ); -} - -//----------------------------------------------------------------------------- - -void CAI_BaseNPC::SetCommandGoal( const Vector &vecGoal ) -{ - m_vecCommandGoal = vecGoal; - m_CommandMoveMonitor.ClearMark(); -} - -//----------------------------------------------------------------------------- - -void CAI_BaseNPC::ClearCommandGoal() -{ - m_vecCommandGoal = vec3_invalid; - m_CommandMoveMonitor.ClearMark(); -} - -//----------------------------------------------------------------------------- - -bool CAI_BaseNPC::IsInPlayerSquad() const -{ - return ( m_pSquad && MAKE_STRING(m_pSquad->GetName()) == GetPlayerSquadName() && !CAI_Squad::IsSilentMember(this) ); -} - - -//----------------------------------------------------------------------------- - -bool CAI_BaseNPC::CanBeUsedAsAFriend( void ) -{ - if ( IsCurSchedule(SCHED_FORCED_GO) || IsCurSchedule(SCHED_FORCED_GO_RUN) ) - return false; - return true; -} - -//----------------------------------------------------------------------------- - -Vector CAI_BaseNPC::GetSmoothedVelocity( void ) -{ - if( GetNavType() == NAV_GROUND || GetNavType() == NAV_FLY ) - { - return m_pMotor->GetCurVel(); - } - - return BaseClass::GetSmoothedVelocity(); -} - - -//----------------------------------------------------------------------------- - -bool CAI_BaseNPC::IsCoverPosition( const Vector &vecThreat, const Vector &vecPosition ) -{ - trace_t tr; - - // By default, we ignore the viewer (me) when determining cover positions - CTraceFilterLOS filter( NULL, COLLISION_GROUP_NONE, this ); - - // If I'm trying to find cover from the player, and the player is in a vehicle, - // ignore the vehicle for the purpose of determining line of sight. - CBaseEntity *pEnemy = GetEnemy(); - if ( pEnemy ) - { - // Hack to see if our threat position is our enemy - bool bThreatPosIsEnemy = ( (vecThreat - GetEnemy()->EyePosition()).LengthSqr() < 0.1f ); - if ( bThreatPosIsEnemy ) - { - CBaseCombatCharacter *pCCEnemy = GetEnemy()->MyCombatCharacterPointer(); - if ( pCCEnemy != NULL && pCCEnemy->IsInAVehicle() ) - { - // Ignore the vehicle - filter.SetPassEntity( pCCEnemy->GetVehicleEntity() ); - } - - if ( !filter.GetPassEntity() ) - { - filter.SetPassEntity( pEnemy ); - } - } - } - - AI_TraceLOS( vecThreat, vecPosition, this, &tr, &filter ); - - if( tr.fraction != 1.0 && hl2_episodic.GetBool() ) - { - if( tr.m_pEnt->m_iClassname == m_iClassname ) - { - // Don't hide behind buddies! - return false; - } - } - - return (tr.fraction != 1.0); -} - -//----------------------------------------------------------------------------- - -float CAI_BaseNPC::SetWait( float minWait, float maxWait ) -{ - int minThinks = Ceil2Int( minWait * 10 ); - - if ( maxWait == 0.0 ) - { - m_flWaitFinished = gpGlobals->curtime + ( 0.1 * minThinks ); - } - else - { - if ( minThinks == 0 ) // random 0..n is almost certain to not return 0 - minThinks = 1; - int maxThinks = Ceil2Int( maxWait * 10 ); - - m_flWaitFinished = gpGlobals->curtime + ( 0.1 * random->RandomInt( minThinks, maxThinks ) ); - } - return m_flWaitFinished; -} - -//----------------------------------------------------------------------------- - -void CAI_BaseNPC::ClearWait() -{ - m_flWaitFinished = FLT_MAX; -} - -//----------------------------------------------------------------------------- - -bool CAI_BaseNPC::IsWaitFinished() -{ - return ( gpGlobals->curtime >= m_flWaitFinished ); -} - -//----------------------------------------------------------------------------- - -bool CAI_BaseNPC::IsWaitSet() -{ - return ( m_flWaitFinished != FLT_MAX ); -} - -void CAI_BaseNPC::TestPlayerPushing( CBaseEntity *pEntity ) -{ - if ( HasSpawnFlags( SF_NPC_NO_PLAYER_PUSHAWAY ) ) - return; - - // Heuristic for determining if the player is pushing me away - CBasePlayer *pPlayer = ToBasePlayer( pEntity ); - if ( pPlayer && !( pPlayer->GetFlags() & FL_NOTARGET ) ) - { - if ( (pPlayer->m_nButtons & (IN_FORWARD|IN_BACK|IN_MOVELEFT|IN_MOVERIGHT)) || - pPlayer->GetAbsVelocity().AsVector2D().LengthSqr() > 50*50 ) - { - SetCondition( COND_PLAYER_PUSHING ); - Vector vecPush = GetAbsOrigin() - pPlayer->GetAbsOrigin(); - VectorNormalize( vecPush ); - CascadePlayerPush( vecPush, pPlayer->WorldSpaceCenter() ); - } - } -} - -void CAI_BaseNPC::CascadePlayerPush( const Vector &push, const Vector &pushOrigin ) -{ - // - // Try to push any friends that are in the way. - // - float hullWidth = GetHullWidth(); - const Vector & origin = GetAbsOrigin(); - const Vector2D &origin2D = origin.AsVector2D(); - - const float MIN_Z_TO_TRANSMIT = GetHullHeight() * 0.5 + 0.1; - const float DIST_REQD_TO_TRANSMIT_PUSH_SQ = Square( hullWidth * 5 + 0.1 ); - const float DIST_FROM_PUSH_VECTOR_REQD_SQ = Square( hullWidth + 0.1 ); - - Vector2D pushTestPoint = vec2_invalid; - - for ( int i = 0; i < g_AI_Manager.NumAIs(); i++ ) - { - CAI_BaseNPC *pOther = g_AI_Manager.AccessAIs()[i]; - if ( pOther != this && pOther->IRelationType(this) == D_LI && !pOther->HasCondition( COND_PLAYER_PUSHING ) ) - { - const Vector &friendOrigin = pOther->GetAbsOrigin(); - if ( fabsf( friendOrigin.z - origin.z ) < MIN_Z_TO_TRANSMIT && - ( friendOrigin.AsVector2D() - origin.AsVector2D() ).LengthSqr() < DIST_REQD_TO_TRANSMIT_PUSH_SQ ) - { - if ( pushTestPoint == vec2_invalid ) - { - pushTestPoint = origin2D - pushOrigin.AsVector2D(); - // No normalize, since it wants to just be a big number and we can't be less that a hull away - pushTestPoint *= 2000; - pushTestPoint += origin2D; - - } - float t; - float distSq = CalcDistanceSqrToLine2D( friendOrigin.AsVector2D(), origin2D, pushTestPoint, &t ); - if ( t > 0 && distSq < DIST_FROM_PUSH_VECTOR_REQD_SQ ) - { - pOther->SetCondition( COND_PLAYER_PUSHING ); - } - } - } - } -} - - -//----------------------------------------------------------------------------- -// Break into pieces! -//----------------------------------------------------------------------------- -void CAI_BaseNPC::Break( CBaseEntity *pBreaker ) -{ - m_takedamage = DAMAGE_NO; - - Vector velocity; - AngularImpulse angVelocity; - IPhysicsObject *pPhysics = VPhysicsGetObject(); - Vector origin; - QAngle angles; - AddSolidFlags( FSOLID_NOT_SOLID ); - if ( pPhysics ) - { - pPhysics->GetVelocity( &velocity, &angVelocity ); - pPhysics->GetPosition( &origin, &angles ); - pPhysics->RecheckCollisionFilter(); - } - else - { - velocity = GetAbsVelocity(); - QAngleToAngularImpulse( GetLocalAngularVelocity(), angVelocity ); - origin = GetAbsOrigin(); - angles = GetAbsAngles(); - } - - breakablepropparams_t params( GetAbsOrigin(), GetAbsAngles(), velocity, angVelocity ); - params.impactEnergyScale = m_impactEnergyScale; - params.defCollisionGroup = GetCollisionGroup(); - if ( params.defCollisionGroup == COLLISION_GROUP_NONE ) - { - // don't automatically make anything COLLISION_GROUP_NONE or it will - // collide with debris being ejected by breaking - params.defCollisionGroup = COLLISION_GROUP_INTERACTIVE; - } - - // no damage/damage force? set a burst of 100 for some movement - params.defBurstScale = 100;//pDamageInfo ? 0 : 100; - PropBreakableCreateAll( GetModelIndex(), pPhysics, params, this, -1, false ); - - UTIL_Remove(this); -} - - -//----------------------------------------------------------------------------- -// Purpose: Input handler for breaking the breakable immediately. -//----------------------------------------------------------------------------- -void CAI_BaseNPC::InputBreak( inputdata_t &inputdata ) -{ - Break( inputdata.pActivator ); -} - - -//----------------------------------------------------------------------------- - -bool CAI_BaseNPC::FindNearestValidGoalPos( const Vector &vTestPoint, Vector *pResult ) -{ - AIMoveTrace_t moveTrace; - Vector vCandidate = vec3_invalid; - if ( GetNavigator()->CanFitAtPosition( vTestPoint, MASK_SOLID_BRUSHONLY ) ) - { - if ( GetMoveProbe()->CheckStandPosition( vTestPoint, MASK_SOLID_BRUSHONLY ) ) - { - vCandidate = vTestPoint; - } - } - - if ( vCandidate == vec3_invalid ) - { - int iNearestNode = GetPathfinder()->NearestNodeToPoint( vTestPoint ); - if ( iNearestNode != NO_NODE ) - { - GetMoveProbe()->MoveLimit( NAV_GROUND, - g_pBigAINet->GetNodePosition(GetHullType(), iNearestNode ), - vTestPoint, - MASK_SOLID_BRUSHONLY, - NULL, - 0, - &moveTrace ); - if ( ( moveTrace.vEndPosition - vTestPoint ).Length2DSqr() < Square( GetHullWidth() * 3.0 ) && - GetMoveProbe()->CheckStandPosition( moveTrace.vEndPosition, MASK_SOLID_BRUSHONLY ) ) - { - vCandidate = moveTrace.vEndPosition; - } - } - } - - if ( vCandidate != vec3_invalid ) - { - AI_Waypoint_t *pPathToPoint = GetPathfinder()->BuildRoute( GetAbsOrigin(), vCandidate, AI_GetSinglePlayer(), 5*12, NAV_NONE, true ); - if ( pPathToPoint ) - { - GetPathfinder()->UnlockRouteNodes( pPathToPoint ); - CAI_Path tempPath; - tempPath.SetWaypoints( pPathToPoint ); // path object will delete waypoints - } - else - vCandidate = vec3_invalid; - } - - if ( vCandidate == vec3_invalid ) - { - GetMoveProbe()->MoveLimit( NAV_GROUND, - GetAbsOrigin(), - vTestPoint, - MASK_SOLID_BRUSHONLY, - NULL, - 0, - &moveTrace ); - vCandidate = moveTrace.vEndPosition; - } - - if ( vCandidate == vec3_invalid ) - return false; - - *pResult = vCandidate; - return true; -} - -//--------------------------------------------------------- -// Pass a direction to get how far an NPC would see if facing -// that direction. Pass nothing to get the length of the NPC's -// current line of sight. -//--------------------------------------------------------- -float CAI_BaseNPC::LineOfSightDist( const Vector &vecDir, float zEye ) -{ - Vector testDir; - if( vecDir == vec3_invalid ) - { - testDir = EyeDirection3D(); - } - else - { - testDir = vecDir; - } - - if ( zEye == FLT_MAX ) - zEye = EyePosition().z; - - trace_t tr; - // Need to center trace so don't get erratic results based on orientation - Vector testPos( GetAbsOrigin().x, GetAbsOrigin().y, zEye ); - AI_TraceLOS( testPos, testPos + testDir * MAX_COORD_RANGE, this, &tr ); - return (tr.startpos - tr.endpos ).Length(); -} - -ConVar ai_LOS_mode( "ai_LOS_mode", "0", FCVAR_REPLICATED ); - -//----------------------------------------------------------------------------- -// Purpose: Use this to perform AI tracelines that are trying to determine LOS between points. -// LOS checks between entities should use FVisible. -//----------------------------------------------------------------------------- -void AI_TraceLOS( const Vector& vecAbsStart, const Vector& vecAbsEnd, CBaseEntity *pLooker, trace_t *ptr, ITraceFilter *pFilter ) -{ - AI_PROFILE_SCOPE( AI_TraceLOS ); - - if ( ai_LOS_mode.GetBool() ) - { - // Don't use LOS tracefilter - UTIL_TraceLine( vecAbsStart, vecAbsEnd, MASK_OPAQUE, pLooker, COLLISION_GROUP_NONE, ptr ); - return; - } - - // Use the custom LOS trace filter - CTraceFilterLOS traceFilter( pLooker, COLLISION_GROUP_NONE ); - if ( !pFilter ) - pFilter = &traceFilter; - AI_TraceLine( vecAbsStart, vecAbsEnd, MASK_OPAQUE_AND_NPCS, pFilter, ptr ); -} - -void CAI_BaseNPC::InputSetSpeedModifierRadius( inputdata_t &inputdata ) -{ - m_iSpeedModRadius = inputdata.value.Int(); - m_iSpeedModRadius *= m_iSpeedModRadius; -} -void CAI_BaseNPC::InputSetSpeedModifierSpeed( inputdata_t &inputdata ) -{ - m_iSpeedModSpeed = inputdata.value.Int(); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -bool CAI_BaseNPC::IsAllowedToDodge( void ) -{ - // Can't do it if I'm not available - if ( m_NPCState != NPC_STATE_IDLE && m_NPCState != NPC_STATE_ALERT && m_NPCState != NPC_STATE_COMBAT ) - return false; - - return ( m_flNextDodgeTime <= gpGlobals->curtime ); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CAI_BaseNPC::ParseScriptedNPCInteractions( void ) -{ - // Already parsed them? - if ( m_ScriptedInteractions.Count() ) - return; - - // Parse the model's key values and find any dynamic interactions - KeyValues *modelKeyValues = new KeyValues(""); - if ( modelKeyValues->LoadFromBuffer( modelinfo->GetModelName( GetModel() ), modelinfo->GetModelKeyValueText( GetModel() ) ) ) - { - // Do we have a dynamic interactions section? - KeyValues *pkvInteractions = modelKeyValues->FindKey("dynamic_interactions"); - if ( pkvInteractions ) - { - KeyValues *pkvNode = pkvInteractions->GetFirstSubKey(); - while ( pkvNode ) - { - ScriptedNPCInteraction_t sInteraction; - sInteraction.iszInteractionName = AllocPooledString( pkvNode->GetName() ); - - // Trigger method - const char *pszTrigger = pkvNode->GetString( "trigger", NULL ); - if ( pszTrigger ) - { - if ( !Q_strncmp( pszTrigger, "auto_in_combat", 14) ) - { - sInteraction.iTriggerMethod = SNPCINT_AUTOMATIC_IN_COMBAT; - } - } - - // Loop Break trigger method - pszTrigger = pkvNode->GetString( "loop_break_trigger", NULL ); - if ( pszTrigger ) - { - char szTrigger[256]; - Q_strncpy( szTrigger, pszTrigger, sizeof(szTrigger) ); - char *pszParam = strtok( szTrigger, " " ); - while (pszParam) - { - if ( !Q_strncmp( pszParam, "on_damage", 9) ) - { - sInteraction.iLoopBreakTriggerMethod |= SNPCINT_LOOPBREAK_ON_DAMAGE; - } - if ( !Q_strncmp( pszParam, "on_flashlight_illum", 19) ) - { - sInteraction.iLoopBreakTriggerMethod |= SNPCINT_LOOPBREAK_ON_FLASHLIGHT_ILLUM; - } - - pszParam = strtok(NULL," "); - } - } - - // Origin - const char *pszOrigin = pkvNode->GetString( "origin_relative", "0 0 0" ); - UTIL_StringToVector( sInteraction.vecRelativeOrigin.Base(), pszOrigin ); - - // Angles - const char *pszAngles = pkvNode->GetString( "angles_relative", NULL ); - if ( pszAngles ) - { - sInteraction.iFlags |= SCNPC_FLAG_TEST_OTHER_ANGLES; - UTIL_StringToVector( sInteraction.angRelativeAngles.Base(), pszAngles ); - } - - // Velocity - const char *pszVelocity = pkvNode->GetString( "velocity_relative", NULL ); - if ( pszVelocity ) - { - sInteraction.iFlags |= SCNPC_FLAG_TEST_OTHER_VELOCITY; - UTIL_StringToVector( sInteraction.vecRelativeVelocity.Base(), pszVelocity ); - } - - // Entry Sequence - const char *pszSequence = pkvNode->GetString( "entry_sequence", NULL ); - if ( pszSequence ) - { - sInteraction.sPhases[SNPCINT_ENTRY].iszSequence = AllocPooledString( pszSequence ); - } - // Entry Activity - const char *pszActivity = pkvNode->GetString( "entry_activity", NULL ); - if ( pszActivity ) - { - sInteraction.sPhases[SNPCINT_ENTRY].iActivity = GetActivityID( pszActivity ); - } - - // Sequence - pszSequence = pkvNode->GetString( "sequence", NULL ); - if ( pszSequence ) - { - sInteraction.sPhases[SNPCINT_SEQUENCE].iszSequence = AllocPooledString( pszSequence ); - } - // Activity - pszActivity = pkvNode->GetString( "activity", NULL ); - if ( pszActivity ) - { - sInteraction.sPhases[SNPCINT_SEQUENCE].iActivity = GetActivityID( pszActivity ); - } - - // Exit Sequence - pszSequence = pkvNode->GetString( "exit_sequence", NULL ); - if ( pszSequence ) - { - sInteraction.sPhases[SNPCINT_EXIT].iszSequence = AllocPooledString( pszSequence ); - } - // Exit Activity - pszActivity = pkvNode->GetString( "exit_activity", NULL ); - if ( pszActivity ) - { - sInteraction.sPhases[SNPCINT_EXIT].iActivity = GetActivityID( pszActivity ); - } - - // Delay - sInteraction.flDelay = pkvNode->GetFloat( "delay", 10.0 ); - - // Delta - sInteraction.flDistSqr = pkvNode->GetFloat( "origin_max_delta", (DSS_MAX_DIST * DSS_MAX_DIST) ); - - // Loop? - if ( pkvNode->GetFloat( "loop_in_action", 0 ) ) - { - sInteraction.iFlags |= SCNPC_FLAG_LOOP_IN_ACTION; - } - - // Needs a weapon? - const char *pszNeedsWeapon = pkvNode->GetString( "needs_weapon", NULL ); - if ( pszNeedsWeapon ) - { - if ( !Q_strncmp( pszNeedsWeapon, "ME", 2 ) ) - { - sInteraction.iFlags |= SCNPC_FLAG_NEEDS_WEAPON_ME; - } - else if ( !Q_strncmp( pszNeedsWeapon, "THEM", 4 ) ) - { - sInteraction.iFlags |= SCNPC_FLAG_NEEDS_WEAPON_THEM; - } - else if ( !Q_strncmp( pszNeedsWeapon, "BOTH", 4 ) ) - { - sInteraction.iFlags |= SCNPC_FLAG_NEEDS_WEAPON_ME; - sInteraction.iFlags |= SCNPC_FLAG_NEEDS_WEAPON_THEM; - } - } - - // Specific weapon types - const char *pszWeaponName = pkvNode->GetString( "weapon_mine", NULL ); - if ( pszWeaponName ) - { - sInteraction.iFlags |= SCNPC_FLAG_NEEDS_WEAPON_ME; - sInteraction.iszMyWeapon = AllocPooledString( pszWeaponName ); - } - pszWeaponName = pkvNode->GetString( "weapon_theirs", NULL ); - if ( pszWeaponName ) - { - sInteraction.iFlags |= SCNPC_FLAG_NEEDS_WEAPON_THEM; - sInteraction.iszTheirWeapon = AllocPooledString( pszWeaponName ); - } - - // Add it to the list - AddScriptedNPCInteraction( &sInteraction ); - - // Move to next interaction - pkvNode = pkvNode->GetNextKey(); - } - } - } - - modelKeyValues->deleteThis(); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CAI_BaseNPC::AddScriptedNPCInteraction( ScriptedNPCInteraction_t *pInteraction ) -{ - int nNewIndex = m_ScriptedInteractions.AddToTail(); - - if ( ai_debug_dyninteractions.GetBool() ) - { - Msg("%s(%s): Added dynamic interaction: %s\n", GetClassName(), GetDebugName(), STRING(pInteraction->iszInteractionName) ); - } - - // Copy the interaction over - ScriptedNPCInteraction_t *pNewInt = &(m_ScriptedInteractions[nNewIndex]); - memcpy( pNewInt, pInteraction, sizeof(ScriptedNPCInteraction_t) ); - - // Calculate the local to world matrix - m_ScriptedInteractions[nNewIndex].matDesiredLocalToWorld.SetupMatrixOrgAngles( pInteraction->vecRelativeOrigin, pInteraction->angRelativeAngles ); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -const char *CAI_BaseNPC::GetScriptedNPCInteractionSequence( ScriptedNPCInteraction_t *pInteraction, int iPhase ) -{ - if ( pInteraction->sPhases[iPhase].iActivity != ACT_INVALID ) - { - int iSequence = SelectWeightedSequence( (Activity)pInteraction->sPhases[iPhase].iActivity ); - return GetSequenceName( iSequence ); - } - - if ( pInteraction->sPhases[iPhase].iszSequence != NULL_STRING ) - return STRING(pInteraction->sPhases[iPhase].iszSequence); - - return NULL; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CAI_BaseNPC::StartRunningInteraction( CAI_BaseNPC *pOtherNPC, bool bActive ) -{ - m_hInteractionPartner = pOtherNPC; - if ( bActive ) - { - m_iInteractionState = NPCINT_RUNNING_ACTIVE; - } - else - { - m_iInteractionState = NPCINT_RUNNING_PARTNER; - } - m_bCannotDieDuringInteraction = true; - - // Force the NPC into an idle schedule so they don't move. - // NOTE: We must set SCHED_IDLE_STAND directly, to prevent derived NPC - // classes from translating the idle stand schedule away to do something bad. - SetSchedule( GetSchedule(SCHED_IDLE_STAND) ); - - // Prepare the NPC for the script. Setting this allows the scripted sequences - // that we're about to create to immediately grab & use this NPC right away. - // This prevents the NPC from being able to make any schedule decisions - // before the DSS gets underway. - m_scriptState = SCRIPT_PLAYING; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CAI_BaseNPC::StartScriptedNPCInteraction( CAI_BaseNPC *pOtherNPC, ScriptedNPCInteraction_t *pInteraction, Vector vecOtherOrigin, QAngle angOtherAngles ) -{ - variant_t emptyVariant; - - StartRunningInteraction( pOtherNPC, true ); - if ( pOtherNPC ) - { - pOtherNPC->StartRunningInteraction( this, false ); - - //Msg("%s(%s) disabled collisions with %s(%s) at %0.2f\n", GetClassName(), GetDebugName(), pOtherNPC->GetClassName(), pOtherNPC->GetDebugName(), gpGlobals->curtime ); - PhysDisableEntityCollisions( this, pOtherNPC ); - } - - // Determine which sequences we're going to use - const char *pszEntrySequence = GetScriptedNPCInteractionSequence( pInteraction, SNPCINT_ENTRY ); - const char *pszSequence = GetScriptedNPCInteractionSequence( pInteraction, SNPCINT_SEQUENCE ); - const char *pszExitSequence = GetScriptedNPCInteractionSequence( pInteraction, SNPCINT_EXIT ); - - // Debug - if ( ai_debug_dyninteractions.GetBool() ) - { - if ( pOtherNPC ) - { - Msg("%s(%s) starting dynamic interaction \"%s\" with %s(%s).\n", GetClassName(), GetDebugName(), STRING(pInteraction->iszInteractionName), pOtherNPC->GetClassName(), pOtherNPC->GetDebugName() ); - if ( pszEntrySequence ) - { - Msg( " - Entry sequence: %s\n", pszEntrySequence ); - } - Msg( " - Core sequence: %s\n", pszSequence ); - if ( pszExitSequence ) - { - Msg( " - Exit sequence: %s\n", pszExitSequence ); - } - } - } - - // Create a scripted sequence name that's guaranteed to be unique - char szSSName[256]; - if ( pOtherNPC ) - { - Q_snprintf( szSSName, sizeof(szSSName), "dss_%s%d%s%d", GetDebugName(), entindex(), pOtherNPC->GetDebugName(), pOtherNPC->entindex() ); - } - else - { - Q_snprintf( szSSName, sizeof(szSSName), "dss_%s%d", GetDebugName(), entindex() ); - } - string_t iszSSName = AllocPooledString(szSSName); - - // Setup next attempt - pInteraction->flNextAttemptTime = gpGlobals->curtime + pInteraction->flDelay + RandomFloat(-2,2); - - // Spawn a scripted sequence for this NPC to play the interaction anim - CAI_ScriptedSequence *pMySequence = (CAI_ScriptedSequence*)CreateEntityByName( "scripted_sequence" ); - pMySequence->KeyValue( "m_iszEntry", pszEntrySequence ); - pMySequence->KeyValue( "m_iszPlay", pszSequence ); - pMySequence->KeyValue( "m_iszPostIdle", pszExitSequence ); - pMySequence->KeyValue( "m_fMoveTo", "5" ); - pMySequence->SetAbsOrigin( GetAbsOrigin() ); - - QAngle angDesired = GetAbsAngles(); - angDesired[YAW] = m_flInteractionYaw; - - pMySequence->SetAbsAngles( angDesired ); - pMySequence->ForceSetTargetEntity( this, true ); - pMySequence->SetName( iszSSName ); - pMySequence->AddSpawnFlags( SF_SCRIPT_NOINTERRUPT | SF_SCRIPT_HIGH_PRIORITY | SF_SCRIPT_OVERRIDESTATE ); - pMySequence->SetLoopActionSequence( (pInteraction->iFlags & SCNPC_FLAG_LOOP_IN_ACTION) != 0 ); - pMySequence->SetSynchPostIdles( true ); - if ( ai_debug_dyninteractions.GetBool() ) - { - pMySequence->m_debugOverlays |= OVERLAY_TEXT_BIT | OVERLAY_PIVOT_BIT; - } - - // Spawn the matching scripted sequence for the other NPC - CAI_ScriptedSequence *pTheirSequence = NULL; - if ( pOtherNPC ) - { - pTheirSequence = (CAI_ScriptedSequence*)CreateEntityByName( "scripted_sequence" ); - pTheirSequence->KeyValue( "m_iszEntry", pszEntrySequence ); - pTheirSequence->KeyValue( "m_iszPlay", pszSequence ); - pTheirSequence->KeyValue( "m_iszPostIdle", pszExitSequence ); - pTheirSequence->KeyValue( "m_fMoveTo", "5" ); - pTheirSequence->SetAbsOrigin( vecOtherOrigin ); - pTheirSequence->SetAbsAngles( angOtherAngles ); - pTheirSequence->ForceSetTargetEntity( pOtherNPC, true ); - pTheirSequence->SetName( iszSSName ); - pTheirSequence->AddSpawnFlags( SF_SCRIPT_NOINTERRUPT | SF_SCRIPT_HIGH_PRIORITY | SF_SCRIPT_OVERRIDESTATE ); - pTheirSequence->SetLoopActionSequence( (pInteraction->iFlags & SCNPC_FLAG_LOOP_IN_ACTION) != 0 ); - pTheirSequence->SetSynchPostIdles( true ); - if ( ai_debug_dyninteractions.GetBool() ) - { - pTheirSequence->m_debugOverlays |= OVERLAY_TEXT_BIT | OVERLAY_PIVOT_BIT; - } - - // Tell their sequence to keep their position relative to me - pTheirSequence->SetupInteractionPosition( this, pInteraction->matDesiredLocalToWorld ); - } - - // Spawn both sequences at once - pMySequence->Spawn(); - if ( pTheirSequence ) - { - pTheirSequence->Spawn(); - } - - // Call activate on both sequences at once - pMySequence->Activate(); - if ( pTheirSequence ) - { - pTheirSequence->Activate(); - } - - // Setup the outputs for both sequences. The first kills them both when it finishes - pMySequence->KeyValue( "OnCancelFailedSequence", UTIL_VarArgs("%s,Kill,,0,-1", szSSName ) ); - if ( pszExitSequence ) - { - pMySequence->KeyValue( "OnPostIdleEndSequence", UTIL_VarArgs("%s,Kill,,0,-1", szSSName ) ); - if ( pTheirSequence ) - { - pTheirSequence->KeyValue( "OnPostIdleEndSequence", UTIL_VarArgs("%s,Kill,,0,-1", szSSName ) ); - } - } - else - { - pMySequence->KeyValue( "OnEndSequence", UTIL_VarArgs("%s,Kill,,0,-1", szSSName ) ); - if ( pTheirSequence ) - { - pTheirSequence->KeyValue( "OnEndSequence", UTIL_VarArgs("%s,Kill,,0,-1", szSSName ) ); - } - } - if ( pTheirSequence ) - { - pTheirSequence->KeyValue( "OnCancelFailedSequence", UTIL_VarArgs("%s,Kill,,0,-1", szSSName ) ); - } - - // Tell both sequences to start - pMySequence->AcceptInput( "BeginSequence", this, this, emptyVariant, 0 ); - if ( pTheirSequence ) - { - pTheirSequence->AcceptInput( "BeginSequence", this, this, emptyVariant, 0 ); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -bool CAI_BaseNPC::CanRunAScriptedNPCInteraction( bool bForced ) -{ - if ( m_NPCState != NPC_STATE_IDLE && m_NPCState != NPC_STATE_ALERT && m_NPCState != NPC_STATE_COMBAT ) - return false; - - if ( !IsAlive() ) - return false; - - if ( IsOnFire() ) - return false; - - if ( IsCrouching() ) - return false; - - // Not while running scripted sequences - if ( m_hCine ) - return false; - - if ( bForced ) - { - if ( !m_hForcedInteractionPartner ) - return false; - } - else - { - if ( m_hForcedInteractionPartner || m_hInteractionPartner ) - return false; - if ( IsInAScript() || !HasCondition(COND_IN_PVS) ) - return false; - if ( HasCondition(COND_HEAR_DANGER) || HasCondition(COND_HEAR_MOVE_AWAY) ) - return false; - - // Default AI prevents interactions while melee attacking, but not ranged attacking - if ( IsCurSchedule( SCHED_MELEE_ATTACK1 ) || IsCurSchedule( SCHED_MELEE_ATTACK2 ) ) - return false; - } - - return true; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CAI_BaseNPC::CheckForScriptedNPCInteractions( void ) -{ - // Are we being forced to interact with another NPC? If so, do that - if ( m_hForcedInteractionPartner ) - { - CheckForcedNPCInteractions(); - return; - } - - // Otherwise, see if we can interaction with our enemy - if ( !m_ScriptedInteractions.Count() || !GetEnemy() ) - return; - - CAI_BaseNPC *pNPC = GetEnemy()->MyNPCPointer(); - - if( !pNPC ) - return; - - // Recalculate interaction capability whenever we switch enemies - if ( m_hLastInteractionTestTarget != GetEnemy() ) - { - m_hLastInteractionTestTarget = GetEnemy(); - - CalculateValidEnemyInteractions(); - } - - // First, make sure I'm in a state where I can do this - if ( !CanRunAScriptedNPCInteraction() ) - return; - if ( pNPC && !pNPC->CanRunAScriptedNPCInteraction() ) - return; - - for ( int i = 0; i < m_ScriptedInteractions.Count(); i++ ) - { - ScriptedNPCInteraction_t *pInteraction = &m_ScriptedInteractions[i]; - - if ( !pInteraction->bValidOnCurrentEnemy ) - continue; - if ( pInteraction->flNextAttemptTime > gpGlobals->curtime ) - continue; - - Vector vecOrigin; - QAngle angAngles; - if ( !InteractionCouldStart( pNPC, pInteraction, vecOrigin, angAngles ) ) - continue; - - m_iInteractionPlaying = i; - StartScriptedNPCInteraction( pNPC, pInteraction, vecOrigin, angAngles ); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Calculate all the valid dynamic interactions we can perform with our current enemy -//----------------------------------------------------------------------------- -void CAI_BaseNPC::CalculateValidEnemyInteractions( void ) -{ - CAI_BaseNPC *pNPC = GetEnemy()->MyNPCPointer(); - if ( !pNPC ) - return; - - bool bDebug = (m_debugOverlays & OVERLAY_NPC_SELECTED_BIT && ai_debug_dyninteractions.GetBool()); - if ( bDebug ) - { - Msg("%s(%s): Computing valid interactions with %s(%s)\n", GetClassName(), GetDebugName(), pNPC->GetClassName(), pNPC->GetDebugName() ); - } - - bool bFound = false; - for ( int i = 0; i < m_ScriptedInteractions.Count(); i++ ) - { - ScriptedNPCInteraction_t *pInteraction = &m_ScriptedInteractions[i]; - pInteraction->bValidOnCurrentEnemy = false; - - // If the trigger method of the interaction isn't the one we're after, we're done - if ( pInteraction->iTriggerMethod != SNPCINT_AUTOMATIC_IN_COMBAT ) - continue; - - if ( !pNPC->GetModelPtr() ) - continue; - - // If we have a damage filter that prevents us hurting the enemy, - // don't interact with him, since most interactions kill the enemy. - // Create a fake damage info to test it with. - CTakeDamageInfo tempinfo( this, this, vec3_origin, vec3_origin, 1.0, DMG_BULLET ); - if ( !pNPC->PassesDamageFilter( tempinfo ) ) - continue; - - // Check the weapon requirements for the interaction - if ( pInteraction->iFlags & SCNPC_FLAG_NEEDS_WEAPON_ME ) - { - if ( !GetActiveWeapon()) - continue; - - // Check the specific weapon type - if ( pInteraction->iszMyWeapon != NULL_STRING && GetActiveWeapon()->m_iClassname != pInteraction->iszMyWeapon ) - continue; - } - if ( pInteraction->iFlags & SCNPC_FLAG_NEEDS_WEAPON_THEM ) - { - if ( !pNPC->GetActiveWeapon() ) - continue; - - // Check the specific weapon type - if ( pInteraction->iszTheirWeapon != NULL_STRING && pNPC->GetActiveWeapon()->m_iClassname != pInteraction->iszTheirWeapon ) - continue; - } - - // Script needs the other NPC, so make sure they're not dead - if ( !pNPC->IsAlive() ) - continue; - - // Use sequence? or activity? - if ( pInteraction->sPhases[SNPCINT_SEQUENCE].iActivity != ACT_INVALID ) - { - // Resolve the activity to a sequence, and make sure our enemy has it - const char *pszSequence = GetScriptedNPCInteractionSequence( pInteraction, SNPCINT_SEQUENCE ); - if ( !pszSequence ) - continue; - if ( pNPC->LookupSequence( pszSequence ) == -1 ) - continue; - } - else - { - if ( pNPC->LookupSequence( STRING(pInteraction->sPhases[SNPCINT_SEQUENCE].iszSequence) ) == -1 ) - continue; - } - - pInteraction->bValidOnCurrentEnemy = true; - bFound = true; - - if ( bDebug ) - { - Msg(" Found: %s\n", STRING(pInteraction->iszInteractionName) ); - } - } - - if ( bDebug && !bFound ) - { - Msg(" No valid interactions found.\n"); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CAI_BaseNPC::CheckForcedNPCInteractions( void ) -{ - // If we don't have an interaction, we're waiting for our partner to start it. Do nothing. - if ( m_iInteractionPlaying == NPCINT_NONE ) - return; - - CAI_BaseNPC *pNPC = m_hForcedInteractionPartner->MyNPCPointer(); - - // First, make sure both NPCs are able to do this - if ( !CanRunAScriptedNPCInteraction( true ) || !pNPC->CanRunAScriptedNPCInteraction( true ) ) - { - // If we were still moving to our target, abort. - if ( m_iInteractionState == NPCINT_MOVING_TO_MARK ) - { - if ( m_hForcedInteractionPartner ) - { - // We've aborted a forced interaction. Let the mapmaker know. - m_OnForcedInteractionAborted.FireOutput( this, this ); - } - - CleanupForcedInteraction(); - pNPC->CleanupForcedInteraction(); - } - return; - } - - // Check to see if we can start our interaction. If we can, dance. - ScriptedNPCInteraction_t *pInteraction = &m_ScriptedInteractions[m_iInteractionPlaying]; - Vector vecOrigin; - QAngle angAngles; - if ( !InteractionCouldStart( pNPC, pInteraction, vecOrigin, angAngles ) ) - return;; - - StartScriptedNPCInteraction( pNPC, pInteraction, vecOrigin, angAngles ); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -bool CAI_BaseNPC::InteractionCouldStart( CAI_BaseNPC *pOtherNPC, ScriptedNPCInteraction_t *pInteraction, Vector &vecOrigin, QAngle &angAngles ) -{ - // Get a matrix that'll convert from my local interaction space to world space - VMatrix matMeToWorld, matLocalToWorld; - QAngle angMyCurrent = GetAbsAngles(); - angMyCurrent[YAW] = m_flInteractionYaw; - matMeToWorld.SetupMatrixOrgAngles( GetAbsOrigin(), angMyCurrent ); - MatrixMultiply( matMeToWorld, pInteraction->matDesiredLocalToWorld, matLocalToWorld ); - - // Get the desired NPC position in worldspace - vecOrigin = matLocalToWorld.GetTranslation(); - MatrixToAngles( matLocalToWorld, angAngles ); - - bool bDebug = ai_debug_dyninteractions.GetBool(); - if ( bDebug ) - { - NDebugOverlay::Axis( vecOrigin, angAngles, 20, true, 0.1 ); - } - - // Determine whether or not the enemy is on the target - float flDistSqr = (vecOrigin - pOtherNPC->GetAbsOrigin()).LengthSqr(); - if ( flDistSqr > pInteraction->flDistSqr ) - { - if ( bDebug ) - { - if ( m_debugOverlays & OVERLAY_NPC_SELECTED_BIT || pOtherNPC->m_debugOverlays & OVERLAY_NPC_SELECTED_BIT ) - { - if ( ai_debug_dyninteractions.GetFloat() == 2 ) - { - Msg(" %s distsqr: %0.2f (%0.2f %0.2f %0.2f), desired: (%0.2f %0.2f %0.2f)\n", GetDebugName(), flDistSqr, - pOtherNPC->GetAbsOrigin().x, pOtherNPC->GetAbsOrigin().y, pOtherNPC->GetAbsOrigin().z, vecOrigin.x, vecOrigin.y, vecOrigin.z ); - } - } - } - return false; - } - - if ( bDebug ) - { - Msg("DYNINT: (%s) testing interaction \"%s\"\n", GetDebugName(), STRING(pInteraction->iszInteractionName) ); - Msg(" %s is at: %0.2f %0.2f %0.2f\n", GetDebugName(), GetAbsOrigin().x, GetAbsOrigin().y, GetAbsOrigin().z ); - Msg(" %s distsqr: %0.2f (%0.2f %0.2f %0.2f), desired: (%0.2f %0.2f %0.2f)\n", GetDebugName(), flDistSqr, - pOtherNPC->GetAbsOrigin().x, pOtherNPC->GetAbsOrigin().y, pOtherNPC->GetAbsOrigin().z, vecOrigin.x, vecOrigin.y, vecOrigin.z ); - - if ( pOtherNPC ) - { - float flOtherSpeed = pOtherNPC->GetSequenceGroundSpeed( pOtherNPC->GetSequence() ); - Msg(" %s Speed: %.2f\n", pOtherNPC->GetSequenceName( pOtherNPC->GetSequence() ), flOtherSpeed); - } - } - - // Angle check, if we're supposed to - if ( pInteraction->iFlags & SCNPC_FLAG_TEST_OTHER_ANGLES ) - { - QAngle angEnemyAngles = pOtherNPC->GetAbsAngles(); - bool bMatches = true; - for ( int ang = 0; ang < 3; ang++ ) - { - float flAngDiff = AngleDiff( angEnemyAngles[ang], angAngles[ang] ); - if ( fabs(flAngDiff) > DSS_MAX_ANGLE_DIFF ) - { - bMatches = false; - break; - } - } - if ( !bMatches ) - return false; - - if ( bDebug ) - { - Msg(" %s angle matched: (%0.2f %0.2f %0.2f), desired (%0.2f, %0.2f, %0.2f)\n", GetDebugName(), - anglemod(angEnemyAngles.x), anglemod(angEnemyAngles.y), anglemod(angEnemyAngles.z), anglemod(angAngles.x), anglemod(angAngles.y), anglemod(angAngles.z) ); - } - } - - // TODO: Velocity check, if we're supposed to - if ( pInteraction->iFlags & SCNPC_FLAG_TEST_OTHER_VELOCITY ) - { - - } - - // Valid so far. Now check to make sure there's nothing in the way. - // This isn't a very good method of checking, but it's cheap and rules out the problems we're seeing so far. - // If we start getting interactions that start a fair distance apart, we're going to need to do more work here. - trace_t tr; - AI_TraceLine( EyePosition(), pOtherNPC->EyePosition(), MASK_NPCSOLID, this, COLLISION_GROUP_NONE, &tr); - if ( tr.fraction != 1.0 && tr.m_pEnt != pOtherNPC ) - { - if ( bDebug ) - { - Msg( " %s Interaction was blocked.\n", GetDebugName() ); - NDebugOverlay::Line( tr.startpos, tr.endpos, 0,255,0, true, 1.0 ); - NDebugOverlay::Line( pOtherNPC->EyePosition(), tr.endpos, 255,0,0, true, 1.0 ); - } - return false; - } - - if ( bDebug ) - { - NDebugOverlay::Line( tr.startpos, tr.endpos, 0,255,0, true, 1.0 ); - } - - // Do a knee-level trace to find low physics objects - Vector vecMyKnee, vecOtherKnee; - CollisionProp()->NormalizedToWorldSpace( Vector(0,0,0.25f), &vecMyKnee ); - pOtherNPC->CollisionProp()->NormalizedToWorldSpace( Vector(0,0,0.25f), &vecOtherKnee ); - AI_TraceLine( vecMyKnee, vecOtherKnee, MASK_NPCSOLID, this, COLLISION_GROUP_NONE, &tr); - if ( tr.fraction != 1.0 && tr.m_pEnt != pOtherNPC ) - { - if ( bDebug ) - { - Msg( " %s Interaction was blocked.\n", GetDebugName() ); - NDebugOverlay::Line( tr.startpos, tr.endpos, 0,255,0, true, 1.0 ); - NDebugOverlay::Line( vecOtherKnee, tr.endpos, 255,0,0, true, 1.0 ); - } - return false; - } - - if ( bDebug ) - { - NDebugOverlay::Line( tr.startpos, tr.endpos, 0,255,0, true, 1.0 ); - } - - // Finally, make sure the NPC can actually fit at the interaction position - // This solves problems with NPCs who are a few units or so above the - // interaction point, and would sink into the ground when playing the anim. - CTraceFilterSkipTwoEntities traceFilter( pOtherNPC, this, COLLISION_GROUP_NONE ); - AI_TraceHull( vecOrigin, vecOrigin, pOtherNPC->GetHullMins(), pOtherNPC->GetHullMaxs(), MASK_SOLID, &traceFilter, &tr ); - if ( tr.startsolid ) - { - if ( bDebug ) - { - NDebugOverlay::Box( vecOrigin, pOtherNPC->GetHullMins(), pOtherNPC->GetHullMaxs(), 255,0,0, true, 1.0 ); - } - return false; - } - - return true; -} - -//----------------------------------------------------------------------------- -// Purpose: Return true if this NPC cannot die because it's in an interaction -// and the flag has been set by the animation. -//----------------------------------------------------------------------------- -bool CAI_BaseNPC::HasInteractionCantDie( void ) -{ - return ( m_bCannotDieDuringInteraction && IsRunningDynamicInteraction() ); -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : &inputdata - -//----------------------------------------------------------------------------- -void CAI_BaseNPC::InputForceInteractionWithNPC( inputdata_t &inputdata ) -{ - // Get the interaction name & target - char parseString[255]; - Q_strncpy(parseString, inputdata.value.String(), sizeof(parseString)); - - // First, the target's name - char *pszParam = strtok(parseString," "); - if ( !pszParam || !pszParam[0] ) - { - Warning("%s(%s) received ForceInteractionWithNPC input with bad parameters: %s\nFormat should be: ForceInteractionWithNPC \n", GetClassname(), GetDebugName(), inputdata.value.String() ); - return; - } - // Find the target - CBaseEntity *pTarget = FindNamedEntity( pszParam ); - if ( !pTarget ) - { - Warning("%s(%s) received ForceInteractionWithNPC input, but couldn't find entity named: %s\n", GetClassname(), GetDebugName(), pszParam ); - return; - } - CAI_BaseNPC *pNPC = pTarget->MyNPCPointer(); - if ( !pNPC || !pNPC->GetModelPtr() ) - { - Warning("%s(%s) received ForceInteractionWithNPC input, but entity named %s cannot run dynamic interactions.\n", GetClassname(), GetDebugName(), pszParam ); - return; - } - - // Second, the interaction name - pszParam = strtok(NULL," "); - if ( !pszParam || !pszParam[0] ) - { - Warning("%s(%s) received ForceInteractionWithNPC input with bad parameters: %s\nFormat should be: ForceInteractionWithNPC \n", GetClassname(), GetDebugName(), inputdata.value.String() ); - return; - } - - // Find the interaction from the name, and ensure it's one that the target NPC can play - int iInteraction = -1; - for ( int i = 0; i < m_ScriptedInteractions.Count(); i++ ) - { - if ( Q_strncmp( pszParam, STRING(m_ScriptedInteractions[i].iszInteractionName), strlen(pszParam) ) ) - continue; - - // Use sequence? or activity? - if ( m_ScriptedInteractions[i].sPhases[SNPCINT_SEQUENCE].iActivity != ACT_INVALID ) - { - if ( !pNPC->HaveSequenceForActivity( (Activity)m_ScriptedInteractions[i].sPhases[SNPCINT_SEQUENCE].iActivity ) ) - { - // Other NPC may have all the matching sequences, but just without the activity specified. - // Lets find a single sequence for us, and ensure they have a matching one. - int iMySeq = SelectWeightedSequence( (Activity)m_ScriptedInteractions[i].sPhases[SNPCINT_SEQUENCE].iActivity ); - if ( pNPC->LookupSequence( GetSequenceName(iMySeq) ) == -1 ) - continue; - } - } - else - { - if ( pNPC->LookupSequence( STRING(m_ScriptedInteractions[i].sPhases[SNPCINT_SEQUENCE].iszSequence) ) == -1 ) - continue; - } - - iInteraction = i; - break; - } - - if ( iInteraction == -1 ) - { - Warning("%s(%s) received ForceInteractionWithNPC input, but couldn't find an interaction named %s that entity named %s could run.\n", GetClassname(), GetDebugName(), pszParam, pNPC->GetDebugName() ); - return; - } - - // Found both pieces of data, lets dance. - StartForcedInteraction( pNPC, iInteraction ); - pNPC->StartForcedInteraction( this, NPCINT_NONE ); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CAI_BaseNPC::StartForcedInteraction( CAI_BaseNPC *pNPC, int iInteraction ) -{ - m_hForcedInteractionPartner = pNPC; - ClearSchedule(); - - m_iInteractionPlaying = iInteraction; - m_iInteractionState = NPCINT_MOVING_TO_MARK; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CAI_BaseNPC::CleanupForcedInteraction( void ) -{ - m_hForcedInteractionPartner = NULL; - m_iInteractionPlaying = NPCINT_NONE; - m_iInteractionState = NPCINT_NOT_RUNNING; -} - -//----------------------------------------------------------------------------- -// Purpose: Calculate a position to move to so that I can interact with my -// target NPC. -// -// FIXME: THIS ONLY WORKS FOR INTERACTIONS THAT REQUIRE THE TARGET -// NPC TO BE SOME DISTANCE DIRECTLY IN FRONT OF ME. -//----------------------------------------------------------------------------- -void CAI_BaseNPC::CalculateForcedInteractionPosition( void ) -{ - if ( m_iInteractionPlaying == NPCINT_NONE ) - return; - - ScriptedNPCInteraction_t *pInteraction = GetRunningDynamicInteraction(); - - // Pretend I was facing the target, and extrapolate from that the position I should be at - Vector vecToTarget = m_hForcedInteractionPartner->GetAbsOrigin() - GetAbsOrigin(); - VectorNormalize( vecToTarget ); - QAngle angToTarget; - VectorAngles( vecToTarget, angToTarget ); - - // Get the desired position in worldspace, relative to the target - VMatrix matMeToWorld, matLocalToWorld; - matMeToWorld.SetupMatrixOrgAngles( GetAbsOrigin(), angToTarget ); - MatrixMultiply( matMeToWorld, pInteraction->matDesiredLocalToWorld, matLocalToWorld ); - - Vector vecOrigin = GetAbsOrigin() - matLocalToWorld.GetTranslation(); - m_vecForcedWorldPosition = m_hForcedInteractionPartner->GetAbsOrigin() + vecOrigin; - - //NDebugOverlay::Axis( m_vecForcedWorldPosition, angToTarget, 20, true, 3.0 ); -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *pPlayer - -//----------------------------------------------------------------------------- -void CAI_BaseNPC::PlayerHasIlluminatedNPC( CBasePlayer *pPlayer, float flDot ) -{ -#ifdef HL2_DLL - if ( IsActiveDynamicInteraction() ) - { - ScriptedNPCInteraction_t *pInteraction = GetRunningDynamicInteraction(); - if ( pInteraction->iLoopBreakTriggerMethod & SNPCINT_LOOPBREAK_ON_FLASHLIGHT_ILLUM ) - { - // Only do this in alyx darkness mode - if ( HL2GameRules()->IsAlyxInDarknessMode() ) - { - // Can only break when we're in the action anim - if ( m_hCine->IsPlayingAction() ) - { - m_hCine->StopActionLoop( true ); - } - } - } - } -#endif -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CAI_BaseNPC::ModifyOrAppendCriteria( AI_CriteriaSet& set ) -{ - BaseClass::ModifyOrAppendCriteria( set ); - - // Append time since seen player - if ( m_flLastSawPlayerTime ) - { - set.AppendCriteria( "timesinceseenplayer", UTIL_VarArgs( "%f", gpGlobals->curtime - m_flLastSawPlayerTime ) ); - } - else - { - set.AppendCriteria( "timesinceseenplayer", "-1" ); - } - - // Append distance to my enemy - if ( GetEnemy() ) - { - set.AppendCriteria( "distancetoenemy", UTIL_VarArgs( "%f", EnemyDistance(GetEnemy()) ) ); - } - else - { - set.AppendCriteria( "distancetoenemy", "-1" ); - } -} - -//----------------------------------------------------------------------------- -// If I were crouching at my current location, could I shoot this target? -//----------------------------------------------------------------------------- -bool CAI_BaseNPC::CouldShootIfCrouching( CBaseEntity *pTarget ) -{ - bool bWasStanding = !IsCrouching(); - Crouch(); - - Vector vecTarget; - if (GetActiveWeapon()) - { - vecTarget = pTarget->BodyTarget( GetActiveWeapon()->GetLocalOrigin() ); - } - else - { - vecTarget = pTarget->BodyTarget( GetLocalOrigin() ); - } - - bool bResult = WeaponLOSCondition( GetLocalOrigin(), vecTarget, false ); - - if ( bWasStanding ) - { - Stand(); - } - - return bResult; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -bool CAI_BaseNPC::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: - return true; - } - - return false; -} - -//----------------------------------------------------------------------------- -// Purpose: Get shoot position of BCC at an arbitrary position -//----------------------------------------------------------------------------- -Vector CAI_BaseNPC::Weapon_ShootPosition( void ) -{ - Vector right; - GetVectors( NULL, &right, NULL ); - - bool bStanding = !IsCrouching(); - if ( bStanding && (CapabilitiesGet() & bits_CAP_DUCK) ) - { - if ( IsCrouchedActivity( GetActivity() ) ) - { - bStanding = false; - } - } - - if ( !bStanding ) - return (GetAbsOrigin() + GetCrouchGunOffset() + right * 8); - - return BaseClass::Weapon_ShootPosition(); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -bool CAI_BaseNPC::ShouldProbeCollideAgainstEntity( CBaseEntity *pEntity ) -{ - if ( pEntity->GetMoveType() == MOVETYPE_VPHYSICS ) - { - if ( ai_test_moveprobe_ignoresmall.GetBool() && IsNavigationUrgent() ) - { - IPhysicsObject *pPhysics = pEntity->VPhysicsGetObject(); - - if ( pPhysics->IsMoveable() && pPhysics->GetMass() < 40.0 ) - return false; - } - } - - return true; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -bool CAI_BaseNPC::Crouch( void ) -{ - m_bIsCrouching = true; - return true; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -bool CAI_BaseNPC::IsCrouching( void ) -{ - return ( (CapabilitiesGet() & bits_CAP_DUCK) && m_bIsCrouching ); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -bool CAI_BaseNPC::Stand( void ) -{ - if ( m_bForceCrouch ) - return false; - - m_bIsCrouching = false; - DesireStand(); - return true; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CAI_BaseNPC::DesireCrouch( void ) -{ - m_bCrouchDesired = true; -} - -bool CAI_BaseNPC::IsInChoreo() const -{ - return m_bInChoreo; -} +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "cbase.h" + +#include "ai_basenpc.h" +#include "fmtstr.h" +#include "activitylist.h" +#include "animation.h" +#include "basecombatweapon.h" +#include "soundent.h" +#include "decals.h" +#include "entitylist.h" +#include "eventqueue.h" +#include "entityapi.h" +#include "bitstring.h" +#include "gamerules.h" // For g_pGameRules +#include "scripted.h" +#include "worldsize.h" +#include "game.h" +#include "shot_manipulator.h" + +#ifdef HL2_DLL +#include "ai_interactions.h" +#include "hl2_gamerules.h" +#endif // HL2_DLL + +#include "ai_network.h" +#include "ai_networkmanager.h" +#include "ai_pathfinder.h" +#include "ai_node.h" +#include "ai_default.h" +#include "ai_schedule.h" +#include "ai_task.h" +#include "ai_hull.h" +#include "ai_moveprobe.h" +#include "ai_hint.h" +#include "ai_navigator.h" +#include "ai_senses.h" +#include "ai_squadslot.h" +#include "ai_memory.h" +#include "ai_squad.h" +#include "ai_localnavigator.h" +#include "ai_tacticalservices.h" +#include "ai_behavior.h" +#include "ai_dynamiclink.h" +#include "AI_Criteria.h" +#include "basegrenade_shared.h" +#include "ammodef.h" +#include "player.h" +#include "sceneentity.h" +#include "ndebugoverlay.h" +#include "mathlib.h" +#include "bone_setup.h" +#include "IEffects.h" +#include "vstdlib/random.h" +#include "engine/IEngineSound.h" +#include "vstdlib/strtools.h" +#include "doors.h" +#include "BasePropDoor.h" +#include "saverestore_utlvector.h" +#include "npcevent.h" +#include "movevars_shared.h" +#include "te_effect_dispatch.h" +#include "globals.h" +#include "saverestore_bitstring.h" +#include "checksum_crc.h" +#include "iservervehicle.h" +#include "filters.h" +#ifdef HL2_DLL +#include "npc_bullseye.h" +#include "hl2_player.h" +#include "weapon_physcannon.h" +#endif +#include "waterbullet.h" +#include "in_buttons.h" +#include "eventlist.h" +#include "globalstate.h" +#include "physics_prop_ragdoll.h" +#include "vphysics/friction.h" +#include "physics_npc_solver.h" +#include "tier0/vcrmode.h" +#include "death_pose.h" +#include "datacache/imdlcache.h" +#ifdef HL2_EPISODIC +#include "npc_alyx_episodic.h" +#endif + +#include "env_debughistory.h" +#include "collisionutils.h" + +extern ConVar sk_healthkit; + +// dvs: for opening doors -- these should probably not be here +#include "ai_route.h" +#include "ai_waypoint.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +//#define DEBUG_LOOK + +bool RagdollManager_SaveImportant( CAI_BaseNPC *pNPC ); + +#define MIN_PHYSICS_FLINCH_DAMAGE 5.0f + +#define NPC_GRENADE_FEAR_DIST 200 +#define MAX_GLASS_PENETRATION_DEPTH 16.0f + +#define FINDNAMEDENTITY_MAX_ENTITIES 32 // max number of entities to be considered for random entity selection in FindNamedEntity + +extern bool g_fDrawLines; +extern short g_sModelIndexLaser; // holds the index for the laser beam +extern short g_sModelIndexLaserDot; // holds the index for the laser beam dot + +// Debugging tools +ConVar ai_no_select_box( "ai_no_select_box", "0" ); + +ConVar ai_show_think_tolerance( "ai_show_think_tolerance", "0" ); +ConVar ai_debug_think_ticks( "ai_debug_think_ticks", "0" ); +ConVar ai_debug_doors( "ai_debug_doors", "0" ); +ConVar ai_debug_enemies( "ai_debug_enemies", "0" ); + +ConVar ai_rebalance_thinks( "ai_rebalance_thinks", "1" ); +ConVar ai_use_efficiency( "ai_use_efficiency", "1" ); +ConVar ai_use_frame_think_limits( "ai_use_frame_think_limits", "1" ); +ConVar ai_default_efficient( "ai_default_efficient", ( IsXbox() ) ? "1" : "0" ); +ConVar ai_efficiency_override( "ai_efficiency_override", "0" ); +ConVar ai_debug_efficiency( "ai_debug_efficiency", "0" ); +ConVar ai_debug_dyninteractions( "ai_debug_dyninteractions", "0", FCVAR_NONE, "Debug the NPC dynamic interaction system." ); +ConVar ai_frametime_limit( "ai_frametime_limit", "50", FCVAR_NONE, "frametime limit for min efficiency AIE_NORMAL (in sec's)." ); + +ConVar ai_use_think_optimizations( "ai_use_think_optimizations", "1" ); + +ConVar ai_test_moveprobe_ignoresmall( "ai_test_moveprobe_ignoresmall", "0" ); + +#ifndef _RETAIL +#define ShouldUseEfficiency() ( ai_use_think_optimizations.GetBool() && ai_use_efficiency.GetBool() ) +#define ShouldUseFrameThinkLimits() ( ai_use_think_optimizations.GetBool() && ai_use_frame_think_limits.GetBool() ) +#define ShouldRebalanceThinks() ( ai_use_think_optimizations.GetBool() && ai_rebalance_thinks.GetBool() ) +#define ShouldDefaultEfficient() ( ai_use_think_optimizations.GetBool() && ai_default_efficient.GetBool() ) +#else +#define ShouldUseEfficiency() ( true ) +#define ShouldUseFrameThinkLimits() ( true ) +#define ShouldRebalanceThinks() ( true ) +#define ShouldDefaultEfficient() ( true ) +#endif + +#ifndef _RETAIL +#define DbgEnemyMsg if ( !ai_debug_enemies.GetBool() ) ; else DevMsg +#else +#define DbgEnemyMsg DevMsg +#endif + +#ifdef DEBUG_AI_FRAME_THINK_LIMITS +#define DbgFrameLimitMsg DevMsg +#else +#if defined __GNUC__ || (defined _MSC_VER && _MSC_VER >= 1400) +#define DbgFrameLimitMsg(...) +#else +#define DbgFrameLimitMsg (void) +#endif +#endif + +// NPC damage adjusters +ConVar sk_npc_head( "sk_npc_head","2" ); +ConVar sk_npc_chest( "sk_npc_chest","1" ); +ConVar sk_npc_stomach( "sk_npc_stomach","1" ); +ConVar sk_npc_arm( "sk_npc_arm","1" ); +ConVar sk_npc_leg( "sk_npc_leg","1" ); +ConVar showhitlocation( "showhitlocation", "0" ); + +// Squad debugging +ConVar ai_debug_squads( "ai_debug_squads", "0" ); +ConVar ai_debug_loners( "ai_debug_loners", "0" ); + +// Shoot trajectory +ConVar ai_lead_time( "ai_lead_time","0.0" ); +ConVar ai_shot_stats( "ai_shot_stats", "0" ); +ConVar ai_shot_stats_term( "ai_shot_stats_term", "1000" ); +ConVar ai_shot_bias( "ai_shot_bias", "1.0" ); + +ConVar ai_spread_defocused_cone_multiplier( "ai_spread_defocused_cone_multiplier","3.0" ); +ConVar ai_spread_cone_focus_time( "ai_spread_cone_focus_time","0.6" ); +ConVar ai_spread_pattern_focus_time( "ai_spread_pattern_focus_time","0.8" ); + +ConVar ai_reaction_delay_idle( "ai_reaction_delay_idle","0.3" ); +ConVar ai_reaction_delay_alert( "ai_reaction_delay_alert", "0.1" ); + +//----------------------------------------------------------------------------- +// +// Crude frame timings +// + +CFastTimer g_AIRunTimer; +CFastTimer g_AIPostRunTimer; +CFastTimer g_AIMoveTimer; + +CFastTimer g_AIConditionsTimer; +CFastTimer g_AIPrescheduleThinkTimer; +CFastTimer g_AIMaintainScheduleTimer; + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +CAI_Manager g_AI_Manager; + +//------------------------------------- + +CAI_Manager::CAI_Manager() +{ + m_AIs.EnsureCapacity( MAX_AIS ); +} + +//------------------------------------- + +CAI_BaseNPC **CAI_Manager::AccessAIs() +{ + if (m_AIs.Count()) + return &m_AIs[0]; + return NULL; +} + +//------------------------------------- + +int CAI_Manager::NumAIs() +{ + return m_AIs.Count(); +} + +//------------------------------------- + +void CAI_Manager::AddAI( CAI_BaseNPC *pAI ) +{ + m_AIs.AddToTail( pAI ); +} + +//------------------------------------- + +void CAI_Manager::RemoveAI( CAI_BaseNPC *pAI ) +{ + int i = m_AIs.Find( pAI ); + + if ( i != -1 ) + m_AIs.FastRemove( i ); +} + + +//----------------------------------------------------------------------------- + +// ================================================================ +// Init static data +// ================================================================ +int CAI_BaseNPC::m_nDebugBits = 0; +CAI_BaseNPC* CAI_BaseNPC::m_pDebugNPC = NULL; +int CAI_BaseNPC::m_nDebugPauseIndex = -1; + +CAI_ClassScheduleIdSpace CAI_BaseNPC::gm_ClassScheduleIdSpace( true ); +CAI_GlobalScheduleNamespace CAI_BaseNPC::gm_SchedulingSymbols; +CAI_LocalIdSpace CAI_BaseNPC::gm_SquadSlotIdSpace( true ); + +string_t CAI_BaseNPC::gm_iszPlayerSquad; + +int CAI_BaseNPC::gm_iNextThinkRebalanceTick; +float CAI_BaseNPC::gm_flTimeLastSpawn; +int CAI_BaseNPC::gm_nSpawnedThisFrame; + +CSimpleSimTimer CAI_BaseNPC::m_AnyUpdateEnemyPosTimer; + +// ================================================================ +// Class Methods +// ================================================================ + +//----------------------------------------------------------------------------- +// Purpose: Static debug function to clear schedules for all NPCS +// Input : +// Output : +//----------------------------------------------------------------------------- +void CAI_BaseNPC::ClearAllSchedules(void) +{ + CAI_BaseNPC *npc = gEntList.NextEntByClass( (CAI_BaseNPC *)NULL ); + + while (npc) + { + npc->ClearSchedule(); + npc->GetNavigator()->ClearGoal(); + npc = gEntList.NextEntByClass(npc); + } +} + +// ============================================================================== + +//----------------------------------------------------------------------------- +// Purpose: +// Input : +// Output : +//----------------------------------------------------------------------------- +bool CAI_BaseNPC::Event_Gibbed( const CTakeDamageInfo &info ) +{ + bool gibbed = CorpseGib( info ); + + if ( gibbed ) + { + // don't remove players! + UTIL_Remove( this ); + SetThink( NULL ); //We're going away, so don't think anymore. + } + else + { + CorpseFade(); + } + + return gibbed; +} + +//========================================================= +// GetFlinchActivity - determines the best type of flinch +// anim to play. +//========================================================= +Activity CAI_BaseNPC::GetFlinchActivity( bool bHeavyDamage, bool bGesture ) +{ + Activity flinchActivity; + + switch ( LastHitGroup() ) + { + // pick a region-specific flinch + case HITGROUP_HEAD: + flinchActivity = bGesture ? ACT_GESTURE_FLINCH_HEAD : ACT_FLINCH_HEAD; + break; + case HITGROUP_STOMACH: + flinchActivity = bGesture ? ACT_GESTURE_FLINCH_STOMACH : ACT_FLINCH_STOMACH; + break; + case HITGROUP_LEFTARM: + flinchActivity = bGesture ? ACT_GESTURE_FLINCH_LEFTARM : ACT_FLINCH_LEFTARM; + break; + case HITGROUP_RIGHTARM: + flinchActivity = bGesture ? ACT_GESTURE_FLINCH_RIGHTARM : ACT_FLINCH_RIGHTARM; + break; + case HITGROUP_LEFTLEG: + flinchActivity = bGesture ? ACT_GESTURE_FLINCH_LEFTLEG : ACT_FLINCH_LEFTLEG; + break; + case HITGROUP_RIGHTLEG: + flinchActivity = bGesture ? ACT_GESTURE_FLINCH_RIGHTLEG : ACT_FLINCH_RIGHTLEG; + break; + case HITGROUP_CHEST: + flinchActivity = bGesture ? ACT_GESTURE_FLINCH_CHEST : ACT_FLINCH_CHEST; + break; + case HITGROUP_GEAR: + case HITGROUP_GENERIC: + default: + // just get a generic flinch. + if ( bHeavyDamage ) + { + flinchActivity = bGesture ? ACT_GESTURE_BIG_FLINCH : ACT_BIG_FLINCH; + } + else + { + flinchActivity = bGesture ? ACT_GESTURE_SMALL_FLINCH : ACT_SMALL_FLINCH; + } + break; + } + + // do we have a sequence for the ideal activity? + if ( SelectWeightedSequence ( flinchActivity ) == ACTIVITY_NOT_AVAILABLE ) + { + if ( bHeavyDamage ) + { + flinchActivity = bGesture ? ACT_GESTURE_BIG_FLINCH : ACT_BIG_FLINCH; + + // If we fail at finding a big flinch, resort to a small one + if ( SelectWeightedSequence ( flinchActivity ) == ACTIVITY_NOT_AVAILABLE ) + { + flinchActivity = bGesture ? ACT_GESTURE_SMALL_FLINCH : ACT_SMALL_FLINCH; + } + } + else + { + flinchActivity = bGesture ? ACT_GESTURE_SMALL_FLINCH : ACT_SMALL_FLINCH; + } + } + + return flinchActivity; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : +//----------------------------------------------------------------------------- +void CAI_BaseNPC::CleanupOnDeath( CBaseEntity *pCulprit, bool bFireDeathOutput ) +{ + if ( !m_bDidDeathCleanup ) + { + m_bDidDeathCleanup = true; + + if ( m_NPCState == NPC_STATE_SCRIPT && m_hCine ) + { + // bail out of this script here + m_hCine->CancelScript(); + // now keep going with the death code + } + + if ( GetHintNode() ) + { + GetHintNode()->Unlock(); + SetHintNode( NULL ); + } + + if( bFireDeathOutput ) + { + m_OnDeath.FireOutput( pCulprit, this ); + } + + // Vacate any strategy slot I might have + VacateStrategySlot(); + + // Remove from squad if in one + if (m_pSquad) + { + // If I'm in idle it means that I didn't see who killed me + // and my squad is still in idle state. Tell squad we have + // an enemy to wake them up and put the enemy position at + // my death position + if ( m_NPCState == NPC_STATE_IDLE && pCulprit) + { + // If we already have some danger memory, don't do this cheat + if ( GetEnemies()->GetDangerMemory() == NULL ) + { + UpdateEnemyMemory( pCulprit, GetAbsOrigin() ); + } + } + + // Remove from squad + m_pSquad->RemoveFromSquad(this, true); + m_pSquad = NULL; + } + + RemoveActorFromScriptedScenes( this, false /*all scenes*/ ); + } + else + DevMsg( "Unexpected double-death-cleanup\n" ); +} + +void CAI_BaseNPC::SelectDeathPose( const CTakeDamageInfo &info ) +{ + if ( !GetModelPtr() || (info.GetDamageType() & DMG_PREVENT_PHYSICS_FORCE) ) + return; + + if ( ShouldPickADeathPose() == false ) + return; + + Activity aActivity = ACT_INVALID; + int iDeathFrame = 0; + + SelectDeathPoseActivityAndFrame( this, info, LastHitGroup(), aActivity, iDeathFrame ); + if ( aActivity == ACT_INVALID ) + { + SetDeathPose( ACT_INVALID ); + SetDeathPoseFrame( 0 ); + return; + } + + SetDeathPose( SelectWeightedSequence( aActivity ) ); + SetDeathPoseFrame( iDeathFrame ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : +//----------------------------------------------------------------------------- +void CAI_BaseNPC::Event_Killed( const CTakeDamageInfo &info ) +{ + if (IsCurSchedule(SCHED_NPC_FREEZE)) + { + // We're frozen; don't die. + return; + } + + Wake( false ); + + //Adrian: Select a death pose to extrapolate the ragdoll's velocity. + SelectDeathPose( info ); + + m_lifeState = LIFE_DYING; + + CleanupOnDeath( info.GetAttacker() ); + + StopLoopingSounds(); + DeathSound( info ); + + if ( ( GetFlags() & FL_NPC ) && ( ShouldGib( info ) == false ) ) + { + SetTouch( NULL ); + } + + BaseClass::Event_Killed( info ); + + + if ( m_bFadeCorpse ) + { + m_bImportanRagdoll = RagdollManager_SaveImportant( this ); + } + + // Make sure this condition is fired too (OnTakeDamage breaks out before this happens on death) + SetCondition( COND_LIGHT_DAMAGE ); + SetIdealState( NPC_STATE_DEAD ); + + // Some characters rely on getting a state transition, even to death. + // zombies, for instance. When a character becomes a ragdoll, their + // server entity ceases to think, so we have to set the dead state here + // because the AI code isn't going to pick up the change on the next think + // for us. + + // Adrian - Only set this if we are going to become a ragdoll. We still want to + // select SCHED_DIE or do something special when this NPC dies and we wont + // catch the change of state if we set this to whatever the ideal state is. + if ( CanBecomeRagdoll() || IsRagdoll() ) + SetState( NPC_STATE_DEAD ); + + // If the remove-no-ragdoll flag is set in the damage type, we're being + // told to remove ourselves immediately on death. This is used when something + // else has some special reason for us to vanish instead of creating a ragdoll. + // i.e. The barnacle does this because it's already got a ragdoll for us. + if ( info.GetDamageType() & DMG_REMOVENORAGDOLL ) + { + if ( !IsEFlagSet( EFL_IS_BEING_LIFTED_BY_BARNACLE ) ) + { + // Go away + RemoveDeferred(); + } + } +} + +//----------------------------------------------------------------------------- + +void CAI_BaseNPC::Ignite( float flFlameLifetime, bool bNPCOnly, float flSize, bool bCalledByLevelDesigner ) +{ + BaseClass::Ignite( flFlameLifetime, bNPCOnly, flSize, bCalledByLevelDesigner ); + +#ifdef HL2_EPISODIC + CBasePlayer *pPlayer = AI_GetSinglePlayer(); + if ( pPlayer->IRelationType( this ) != D_LI ) + { + CNPC_Alyx *alyx = CNPC_Alyx::GetAlyx(); + + if ( alyx ) + { + alyx->EnemyIgnited( this ); + } + } +#endif +} + +//----------------------------------------------------------------------------- + +ConVar ai_block_damage( "ai_block_damage","0" ); + +bool CAI_BaseNPC::PassesDamageFilter( const CTakeDamageInfo &info ) +{ + if ( ai_block_damage.GetBool() ) + return false; + // FIXME: hook a friendly damage filter to the npc instead? + if ( (CapabilitiesGet() & bits_CAP_FRIENDLY_DMG_IMMUNE) && info.GetAttacker() && info.GetAttacker() != this ) + { + // check attackers relationship with me + CBaseCombatCharacter *npcEnemy = info.GetAttacker()->MyCombatCharacterPointer(); + bool bHitByVehicle = false; + if ( !npcEnemy ) + { + if ( info.GetAttacker()->GetServerVehicle() ) + { + bHitByVehicle = true; + } + } + + if ( bHitByVehicle || (npcEnemy && npcEnemy->IRelationType( this ) == D_LI) ) + { + m_fNoDamageDecal = true; + + if ( npcEnemy && npcEnemy->IsPlayer() ) + { + m_OnDamagedByPlayer.FireOutput( info.GetAttacker(), this ); + // This also counts as being harmed by player's squad. + m_OnDamagedByPlayerSquad.FireOutput( info.GetAttacker(), this ); + } + + return false; + } + } + + if ( !BaseClass::PassesDamageFilter( info ) ) + { + m_fNoDamageDecal = true; + return false; + } + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : +// Output : +//----------------------------------------------------------------------------- +int CAI_BaseNPC::OnTakeDamage_Alive( const CTakeDamageInfo &info ) +{ + Forget( bits_MEMORY_INCOVER ); + + if ( !BaseClass::OnTakeDamage_Alive( info ) ) + return 0; + + if ( GetSleepState() == AISS_WAITING_FOR_THREAT ) + Wake(); + + // NOTE: This must happen after the base class is called; we need to reduce + // health before the pain sound, since some NPCs use the final health + // level as a modifier to determine which pain sound to use. + + // REVISIT: Combine soldiers shoot each other a lot and then talk about it + // this improves that case a bunch, but it seems kind of harsh. + if ( !m_pSquad || !m_pSquad->SquadIsMember( info.GetAttacker() ) ) + { + PainSound( info );// "Ouch!" + } + + // See if we're running a dynamic interaction that should break when I am damaged. + if ( IsActiveDynamicInteraction() ) + { + ScriptedNPCInteraction_t *pInteraction = GetRunningDynamicInteraction(); + if ( pInteraction->iLoopBreakTriggerMethod & SNPCINT_LOOPBREAK_ON_DAMAGE ) + { + // Can only break when we're in the action anim + if ( m_hCine->IsPlayingAction() ) + { + m_hCine->StopActionLoop( true ); + } + } + } + + // If we're not allowed to die, refuse to die + // Allow my interaction partner to kill me though + if ( m_iHealth <= 0 && HasInteractionCantDie() && info.GetAttacker() != m_hInteractionPartner ) + { + m_iHealth = 1; + } + +#if 0 + // HACKHACK Don't kill npcs in a script. Let them break their scripts first + // THIS is a Half-Life 1 hack that's not cutting the mustard in the scripts + // that have been authored for Half-Life 2 thus far. (sjb) + if ( m_NPCState == NPC_STATE_SCRIPT ) + { + SetCondition( COND_LIGHT_DAMAGE ); + } +#endif + + // ----------------------------------- + // Fire outputs + // ----------------------------------- + if ( m_flLastDamageTime != gpGlobals->curtime ) + { + // only fire once per frame + m_OnDamaged.FireOutput( info.GetAttacker(), this); + + if( info.GetAttacker()->IsPlayer() ) + { + 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 = dynamic_cast( info.GetAttacker() ); + CBasePlayer *pPlayer = AI_GetSinglePlayer(); + + if( pAttacker && pAttacker->IsAlive() && pPlayer ) + { + if( pAttacker->GetSquad() != NULL && pAttacker->IsInPlayerSquad() ) + { + m_OnDamagedByPlayerSquad.FireOutput( info.GetAttacker(), this ); + } + } + } + } + + if( (info.GetDamageType() & DMG_CRUSH) && !(info.GetDamageType() & DMG_PHYSGUN) && info.GetDamage() >= MIN_PHYSICS_FLINCH_DAMAGE ) + { + SetCondition( COND_PHYSICS_DAMAGE ); + } + + if ( m_iHealth <= ( m_iMaxHealth / 2 ) ) + { + m_OnHalfHealth.FireOutput( info.GetAttacker(), this ); + } + + // react to the damage (get mad) + if ( ( (GetFlags() & FL_NPC) == 0 ) || !info.GetAttacker() ) + return 1; + + // If the attacker was an NPC or client update my position memory + if ( info.GetAttacker()->GetFlags() & (FL_NPC | FL_CLIENT) ) + { + // ------------------------------------------------------------------ + // DO NOT CHANGE THIS CODE W/O CONSULTING + // Only update information about my attacker I don't see my attacker + // ------------------------------------------------------------------ + if ( !FInViewCone( info.GetAttacker() ) || !FVisible( info.GetAttacker() ) ) + { + // ------------------------------------------------------------- + // If I have an inflictor (enemy / grenade) update memory with + // position of inflictor, otherwise update with an position + // estimate for where the attack came from + // ------------------------------------------------------ + Vector vAttackPos; + if (info.GetInflictor()) + { + vAttackPos = info.GetInflictor()->GetAbsOrigin(); + } + else + { + vAttackPos = (GetAbsOrigin() + ( g_vecAttackDir * 64 )); + } + + + // ---------------------------------------------------------------- + // If I already have an enemy, assume that the attack + // came from the enemy and update my enemy's position + // unless I already know about the attacker or I can see my enemy + // ---------------------------------------------------------------- + if ( GetEnemy() != NULL && + !GetEnemies()->HasMemory( info.GetAttacker() ) && + !HasCondition(COND_SEE_ENEMY) ) + { + UpdateEnemyMemory(GetEnemy(), vAttackPos, GetEnemy()); + } + // ---------------------------------------------------------------- + // If I already know about this enemy, update his position + // ---------------------------------------------------------------- + else if (GetEnemies()->HasMemory( info.GetAttacker() )) + { + UpdateEnemyMemory(info.GetAttacker(), vAttackPos); + } + // ----------------------------------------------------------------- + // Otherwise just note the position, but don't add enemy to my list + // ----------------------------------------------------------------- + else + { + UpdateEnemyMemory(NULL, vAttackPos); + } + } + + // add pain to the conditions + if ( IsLightDamage( info ) ) + { + SetCondition( COND_LIGHT_DAMAGE ); + } + if ( IsHeavyDamage( info ) ) + { + SetCondition( COND_HEAVY_DAMAGE ); + } + + ForceGatherConditions(); + + // Keep track of how much consecutive damage I have recieved + if ((gpGlobals->curtime - m_flLastDamageTime) < 1.0) + { + m_flSumDamage += info.GetDamage(); + } + else + { + m_flSumDamage = info.GetDamage(); + } + m_flLastDamageTime = gpGlobals->curtime; + if ( info.GetAttacker() && info.GetAttacker()->IsPlayer() ) + m_flLastPlayerDamageTime = gpGlobals->curtime; + GetEnemies()->OnTookDamageFrom( info.GetAttacker() ); + + if (m_flSumDamage > m_iMaxHealth*0.3) + { + SetCondition(COND_REPEATED_DAMAGE); + } + + NotifyFriendsOfDamage( info.GetAttacker() ); + } + + // --------------------------------------------------------------- + // Insert a combat sound so that nearby NPCs know I've been hit + // --------------------------------------------------------------- + CSoundEnt::InsertSound( SOUND_COMBAT, GetAbsOrigin(), 1024, 0.5, this, SOUNDENT_CHANNEL_INJURY ); + + return 1; +} + + +//========================================================= +// OnTakeDamage_Dying - takedamage function called when a npc's +// corpse is damaged. +//========================================================= +int CAI_BaseNPC::OnTakeDamage_Dying( const CTakeDamageInfo &info ) +{ + if ( info.GetDamageType() & DMG_PLASMA ) + { + if ( m_takedamage != DAMAGE_EVENTS_ONLY ) + { + m_iHealth -= info.GetDamage(); + + if (m_iHealth < -500) + { + UTIL_Remove(this); + } + } + } + return BaseClass::OnTakeDamage_Dying( info ); +} + +//========================================================= +// OnTakeDamage_Dead - takedamage function called when a npc's +// corpse is damaged. +//========================================================= +int CAI_BaseNPC::OnTakeDamage_Dead( const CTakeDamageInfo &info ) +{ + Vector vecDir; + + // grab the vector of the incoming attack. ( pretend that the inflictor is a little lower than it really is, so the body will tend to fly upward a bit). + vecDir = vec3_origin; + if ( info.GetInflictor() ) + { + vecDir = info.GetInflictor()->WorldSpaceCenter() - Vector ( 0, 0, 10 ) - WorldSpaceCenter(); + VectorNormalize( vecDir ); + g_vecAttackDir = vecDir; + } + +#if 0// turn this back on when the bounding box issues are resolved. + + SetGroundEntity( NULL ); + GetLocalOrigin().z += 1; + + // let the damage scoot the corpse around a bit. + if ( info.GetInflictor() && (info.GetAttacker()->GetSolid() != SOLID_TRIGGER) ) + { + ApplyAbsVelocityImpulse( vecDir * -DamageForce( flDamage ) ); + } + +#endif + + // kill the corpse if enough damage was done to destroy the corpse and the damage is of a type that is allowed to destroy the corpse. + if ( info.GetDamageType() & DMG_GIB_CORPSE ) + { + // Accumulate corpse gibbing damage, so you can gib with multiple hits + if ( m_takedamage != DAMAGE_EVENTS_ONLY ) + { + m_iHealth -= info.GetDamage() * 0.1; + } + } + + if ( info.GetDamageType() & DMG_PLASMA ) + { + if ( m_takedamage != DAMAGE_EVENTS_ONLY ) + { + m_iHealth -= info.GetDamage(); + + if (m_iHealth < -500) + { + UTIL_Remove(this); + } + } + } + + return 1; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CAI_BaseNPC::NotifyFriendsOfDamage( CBaseEntity *pAttackerEntity ) +{ + CAI_BaseNPC *pAttacker = pAttackerEntity->MyNPCPointer(); + if ( pAttacker ) + { + const Vector &origin = GetAbsOrigin(); + for ( int i = 0; i < g_AI_Manager.NumAIs(); i++ ) + { + const float NEAR_Z = 10*12; + const float NEAR_XY_SQ = Square( 50*12 ); + CAI_BaseNPC *pNpc = g_AI_Manager.AccessAIs()[i]; + if ( pNpc && pNpc != this ) + { + const Vector &originNpc = pNpc->GetAbsOrigin(); + if ( fabsf( originNpc.z - origin.z ) < NEAR_Z ) + { + if ( (originNpc.AsVector2D() - origin.AsVector2D()).LengthSqr() < NEAR_XY_SQ ) + { + if ( pNpc->GetSquad() == GetSquad() || IRelationType( pNpc ) == D_LI ) + pNpc->OnFriendDamaged( this, pAttacker ); + } + } + } + } + } +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CAI_BaseNPC::OnFriendDamaged( CBaseCombatCharacter *pSquadmate, CBaseEntity *pAttacker ) +{ + if ( GetSleepState() != AISS_WAITING_FOR_INPUT ) + { + float distSqToThreat = ( GetAbsOrigin() - pAttacker->GetAbsOrigin() ).LengthSqr(); + + if ( GetSleepState() != AISS_AWAKE && distSqToThreat < Square( 20 * 12 ) ) + Wake(); + + if ( distSqToThreat < Square( 50 * 12 ) ) + ForceGatherConditions(); + } +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +bool CAI_BaseNPC::IsLightDamage( const CTakeDamageInfo &info ) +{ + // ALL nonzero damage is light damage! Mask off COND_LIGHT_DAMAGE if you want to ignore light damage. + return ( info.GetDamage() > 0 ); +} + +bool CAI_BaseNPC::IsHeavyDamage( const CTakeDamageInfo &info ) +{ + return ( info.GetDamage() > 20 ); +} + +void CAI_BaseNPC::DoRadiusDamage( const CTakeDamageInfo &info, int iClassIgnore, CBaseEntity *pEntityIgnore ) +{ + RadiusDamage( info, GetAbsOrigin(), info.GetDamage() * 2.5, iClassIgnore, pEntityIgnore ); +} + + +void CAI_BaseNPC::DoRadiusDamage( const CTakeDamageInfo &info, const Vector &vecSrc, int iClassIgnore, CBaseEntity *pEntityIgnore ) +{ + RadiusDamage( info, vecSrc, info.GetDamage() * 2.5, iClassIgnore, pEntityIgnore ); +} + + +//----------------------------------------------------------------------------- +// Set the contents types that are solid by default to all NPCs +//----------------------------------------------------------------------------- +unsigned int CAI_BaseNPC::PhysicsSolidMaskForEntity( void ) const +{ + return MASK_NPCSOLID; +} + + +//========================================================= + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CAI_BaseNPC::DecalTrace( trace_t *pTrace, char const *decalName ) +{ + if ( m_fNoDamageDecal ) + { + m_fNoDamageDecal = false; + // @Note (toml 04-23-03): e3, don't decal face on damage if still alive + return; + } + BaseClass::DecalTrace( pTrace, decalName ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CAI_BaseNPC::ImpactTrace( trace_t *pTrace, int iDamageType, char *pCustomImpactName ) +{ + if ( m_fNoDamageDecal ) + { + m_fNoDamageDecal = false; + // @Note (toml 04-23-03): e3, don't decal face on damage if still alive + return; + } + BaseClass::ImpactTrace( pTrace, iDamageType, pCustomImpactName ); +} + +//--------------------------------------------------------- +// Return the number by which to multiply incoming damage +// based on the hitgroup it hits. This exists mainly so +// that individual NPC's can have more or less resistance +// to damage done to certain hitgroups. +//--------------------------------------------------------- +float CAI_BaseNPC::GetHitgroupDamageMultiplier( int iHitGroup, const CTakeDamageInfo &info ) +{ + switch( iHitGroup ) + { + case HITGROUP_GENERIC: + return 1.0f; + + case HITGROUP_HEAD: + return sk_npc_head.GetFloat(); + + case HITGROUP_CHEST: + return sk_npc_chest.GetFloat(); + + case HITGROUP_STOMACH: + return sk_npc_stomach.GetFloat(); + + case HITGROUP_LEFTARM: + case HITGROUP_RIGHTARM: + return sk_npc_arm.GetFloat(); + + case HITGROUP_LEFTLEG: + case HITGROUP_RIGHTLEG: + return sk_npc_leg.GetFloat(); + + default: + return 1.0f; + } +} + +//========================================================= +// TraceAttack +//========================================================= +void CAI_BaseNPC::TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr ) +{ + m_fNoDamageDecal = false; + if ( m_takedamage == DAMAGE_NO ) + return; + + CTakeDamageInfo subInfo = info; + + SetLastHitGroup( ptr->hitgroup ); + m_nForceBone = ptr->physicsbone; // save this bone for physics forces + + Assert( m_nForceBone > -255 && m_nForceBone < 256 ); + + bool bDebug = showhitlocation.GetBool(); + + switch ( ptr->hitgroup ) + { + case HITGROUP_GENERIC: + if( bDebug ) DevMsg("Hit Location: Generic\n"); + break; + + // hit gear, react but don't bleed + case HITGROUP_GEAR: + subInfo.SetDamage( 0.01 ); + ptr->hitgroup = HITGROUP_GENERIC; + if( bDebug ) DevMsg("Hit Location: Gear\n"); + break; + + case HITGROUP_HEAD: + subInfo.ScaleDamage( GetHitgroupDamageMultiplier(ptr->hitgroup, info) ); + if( bDebug ) DevMsg("Hit Location: Head\n"); + break; + + case HITGROUP_CHEST: + subInfo.ScaleDamage( GetHitgroupDamageMultiplier(ptr->hitgroup, info) ); + if( bDebug ) DevMsg("Hit Location: Chest\n"); + break; + + case HITGROUP_STOMACH: + subInfo.ScaleDamage( GetHitgroupDamageMultiplier(ptr->hitgroup, info) ); + if( bDebug ) DevMsg("Hit Location: Stomach\n"); + break; + + case HITGROUP_LEFTARM: + case HITGROUP_RIGHTARM: + subInfo.ScaleDamage( GetHitgroupDamageMultiplier(ptr->hitgroup, info) ); + if( bDebug ) DevMsg("Hit Location: Left/Right Arm\n"); + break + ; + case HITGROUP_LEFTLEG: + case HITGROUP_RIGHTLEG: + subInfo.ScaleDamage( GetHitgroupDamageMultiplier(ptr->hitgroup, info) ); + if( bDebug ) DevMsg("Hit Location: Left/Right Leg\n"); + break; + + default: + if( bDebug ) DevMsg("Hit Location: UNKNOWN\n"); + break; + } + + if ( subInfo.GetDamage() >= 1.0 && !(subInfo.GetDamageType() & DMG_SHOCK ) ) + { + if( !IsPlayer() || ( IsPlayer() && g_pGameRules->IsMultiplayer() ) ) + { + // NPC's always bleed. Players only bleed in multiplayer. + SpawnBlood( ptr->endpos, vecDir, BloodColor(), subInfo.GetDamage() );// a little surface blood. + } + + TraceBleed( subInfo.GetDamage(), vecDir, ptr, subInfo.GetDamageType() ); + + if ( ptr->hitgroup == HITGROUP_HEAD && m_iHealth - subInfo.GetDamage() > 0 ) + { + m_fNoDamageDecal = true; + } + } + + // Airboat gun will impart major force if it's about to kill him.... + if ( info.GetDamageType() & DMG_AIRBOAT ) + { + if ( subInfo.GetDamage() >= GetHealth() ) + { + float flMagnitude = subInfo.GetDamageForce().Length(); + if ( (flMagnitude != 0.0f) && (flMagnitude < 400.0f * 65.0f) ) + { + subInfo.ScaleDamageForce( 400.0f * 65.0f / flMagnitude ); + } + } + } + + if( info.GetInflictor() ) + { + subInfo.SetInflictor( info.GetInflictor() ); + } + else + { + subInfo.SetInflictor( info.GetAttacker() ); + } + + AddMultiDamage( subInfo, this ); +} + +//----------------------------------------------------------------------------- +// Purpose: Checks if point is in spread angle between source and target Pos +// Used to prevent friendly fire +// Input : Source of attack, target position, spread angle +// Output : +//----------------------------------------------------------------------------- +bool CAI_BaseNPC::PointInSpread( CBaseCombatCharacter *pCheckEntity, const Vector &sourcePos, const Vector &targetPos, const Vector &testPoint, float flSpread, float maxDistOffCenter ) +{ + float distOffLine = CalcDistanceToLine2D( testPoint.AsVector2D(), sourcePos.AsVector2D(), targetPos.AsVector2D() ); + if ( distOffLine < maxDistOffCenter ) + { + Vector toTarget = targetPos - sourcePos; + float distTarget = VectorNormalize(toTarget); + + Vector toTest = testPoint - sourcePos; + float distTest = VectorNormalize(toTest); + // Only reject if target is on other side + if (distTarget > distTest) + { + toTarget.z = 0.0; + toTest.z = 0.0; + + float dotProduct = DotProduct(toTarget,toTest); + if (dotProduct > flSpread) + { + return true; + } + else if( dotProduct > 0.0f ) + { + // If this guy is in front, do the hull/line test: + if( pCheckEntity ) + { + float flBBoxDist = NAI_Hull::Width( pCheckEntity->GetHullType() ); + flBBoxDist *= 1.414f; // sqrt(2) + + // !!!BUGBUG - this 2d check will stop a citizen shooting at a gunship or strider + // if another citizen is between them, even though the strider or gunship may be + // high up in the air (sjb) + distOffLine = CalcDistanceToLine( testPoint, sourcePos, targetPos ); + if( distOffLine < flBBoxDist ) + { + return true; + } + } + } + } + } + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: Checks if player is in spread angle between source and target Pos +// Used to prevent friendly fire +// Input : Source of attack, target position, spread angle +// Output : +//----------------------------------------------------------------------------- +bool CAI_BaseNPC::PlayerInSpread( const Vector &sourcePos, const Vector &targetPos, float flSpread, float maxDistOffCenter, bool ignoreHatedPlayers ) +{ + // loop through all players + for (int i = 1; i <= gpGlobals->maxClients; i++ ) + { + CBasePlayer *pPlayer = UTIL_PlayerByIndex( i ); + + if ( pPlayer && ( !ignoreHatedPlayers || IRelationType( pPlayer ) != D_HT ) ) + { + if ( PointInSpread( pPlayer, sourcePos, targetPos, pPlayer->WorldSpaceCenter(), flSpread, maxDistOffCenter ) ) + return true; + } + } + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: Checks if player is in range of given location. Used by NPCs +// to prevent friendly fire +// Input : +// Output : +//----------------------------------------------------------------------------- +CBaseEntity *CAI_BaseNPC::PlayerInRange( const Vector &vecLocation, float flDist ) +{ + // loop through all players + for (int i = 1; i <= gpGlobals->maxClients; i++ ) + { + CBasePlayer *pPlayer = UTIL_PlayerByIndex( i ); + + if (pPlayer && (vecLocation - pPlayer->WorldSpaceCenter() ).Length2D() <= flDist) + { + return pPlayer; + } + } + return NULL; +} + + +#define BULLET_WIZZDIST 80.0 +#define SLOPE ( -1.0 / BULLET_WIZZDIST ) + +void BulletWizz( Vector vecSrc, Vector vecEndPos, edict_t *pShooter, bool isTracer ) +{ + CBasePlayer *pPlayer; + Vector vecBulletPath; + Vector vecPlayerPath; + Vector vecBulletDir; + Vector vecNearestPoint; + float flDist; + float flBulletDist; + + vecBulletPath = vecEndPos - vecSrc; + vecBulletDir = vecBulletPath; + VectorNormalize(vecBulletDir); + + // see how near this bullet passed by player in a single player game + // for multiplayer, we need to go through the list of clients. + for (int i = 1; i <= gpGlobals->maxClients; i++ ) + { + pPlayer = UTIL_PlayerByIndex( i ); + + if ( !pPlayer ) + continue; + + // Don't hear one's own bullets + if( pPlayer->edict() == pShooter ) + continue; + + vecPlayerPath = pPlayer->EarPosition() - vecSrc; + flDist = DotProduct( vecPlayerPath, vecBulletDir ); + vecNearestPoint = vecSrc + vecBulletDir * flDist; + // FIXME: minus m_vecViewOffset? + flBulletDist = ( vecNearestPoint - pPlayer->EarPosition() ).Length(); + } +} + +//----------------------------------------------------------------------------- +// Hits triggers with raycasts +//----------------------------------------------------------------------------- +class CTriggerTraceEnum : public IEntityEnumerator +{ +public: + CTriggerTraceEnum( Ray_t *pRay, const CTakeDamageInfo &info, const Vector& dir, int contentsMask ) : + m_info( info ), m_VecDir(dir), m_ContentsMask(contentsMask), m_pRay(pRay) + { + } + + virtual bool EnumEntity( IHandleEntity *pHandleEntity ) + { + trace_t tr; + + CBaseEntity *pEnt = gEntList.GetBaseEntity( pHandleEntity->GetRefEHandle() ); + + // Done to avoid hitting an entity that's both solid & a trigger. + if ( pEnt->IsSolid() ) + return true; + + enginetrace->ClipRayToEntity( *m_pRay, m_ContentsMask, pHandleEntity, &tr ); + if (tr.fraction < 1.0f) + { + pEnt->DispatchTraceAttack( m_info, m_VecDir, &tr ); + ApplyMultiDamage(); + } + + return true; + } + +private: + CTakeDamageInfo m_info; + Vector m_VecDir; + int m_ContentsMask; + Ray_t *m_pRay; +}; + +void CBaseEntity::TraceAttackToTriggers( const CTakeDamageInfo &info, const Vector& start, const Vector& end, const Vector& dir ) +{ + Ray_t ray; + ray.Init( start, end ); + + CTriggerTraceEnum triggerTraceEnum( &ray, info, dir, MASK_SHOT ); + enginetrace->EnumerateEntities( ray, true, &triggerTraceEnum ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : const char +//----------------------------------------------------------------------------- +const char *CAI_BaseNPC::GetTracerType( void ) +{ + if ( GetActiveWeapon() ) + { + return GetActiveWeapon()->GetTracerType(); + } + + return BaseClass::GetTracerType(); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : &vecTracerSrc - +// &tr - +// iTracerType - +//----------------------------------------------------------------------------- +void CAI_BaseNPC::MakeTracer( const Vector &vecTracerSrc, const trace_t &tr, int iTracerType ) +{ + if ( GetActiveWeapon() ) + { + GetActiveWeapon()->MakeTracer( vecTracerSrc, tr, iTracerType ); + return; + } + + BaseClass::MakeTracer( vecTracerSrc, tr, iTracerType ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CAI_BaseNPC::FireBullets( const FireBulletsInfo_t &info ) +{ +#ifdef HL2_DLL + // If we're shooting at a bullseye, become perfectly accurate if the bullseye demands it + if ( GetEnemy() && GetEnemy()->Classify() == CLASS_BULLSEYE ) + { + CNPC_Bullseye *pBullseye = dynamic_cast(GetEnemy()); + if ( pBullseye && pBullseye->UsePerfectAccuracy() ) + { + FireBulletsInfo_t accurateInfo = info; + accurateInfo.m_vecSpread = vec3_origin; + BaseClass::FireBullets( accurateInfo ); + return; + } + } +#endif + + BaseClass::FireBullets( info ); +} + + +//----------------------------------------------------------------------------- +// Shot statistics +//----------------------------------------------------------------------------- +void CBaseEntity::UpdateShotStatistics( const trace_t &tr ) +{ + if ( ai_shot_stats.GetBool() ) + { + CAI_BaseNPC *pNpc = MyNPCPointer(); + if ( pNpc ) + { + pNpc->m_TotalShots++; + if ( tr.m_pEnt == pNpc->GetEnemy() ) + { + pNpc->m_TotalHits++; + } + } + } +} + + +//----------------------------------------------------------------------------- +// Handle shot entering water +//----------------------------------------------------------------------------- +void CBaseEntity::HandleShotImpactingGlass( const FireBulletsInfo_t &info, + const trace_t &tr, const Vector &vecDir, ITraceFilter *pTraceFilter ) +{ + // Move through the glass until we're at the other side + Vector testPos = tr.endpos + ( vecDir * MAX_GLASS_PENETRATION_DEPTH ); + + CEffectData data; + + data.m_vNormal = tr.plane.normal; + data.m_vOrigin = tr.endpos; + + DispatchEffect( "GlassImpact", data ); + + trace_t penetrationTrace; + + // Re-trace as if the bullet had passed right through + UTIL_TraceLine( testPos, tr.endpos, MASK_SHOT, pTraceFilter, &penetrationTrace ); + + // See if we found the surface again + if ( penetrationTrace.startsolid || tr.fraction == 0.0f || penetrationTrace.fraction == 1.0f ) + return; + + //FIXME: This is technically frustrating MultiDamage, but multiple shots hitting multiple targets in one call + // would do exactly the same anyway... + + // Impact the other side (will look like an exit effect) + DoImpactEffect( penetrationTrace, GetAmmoDef()->DamageType(info.m_iAmmoType) ); + + data.m_vNormal = penetrationTrace.plane.normal; + data.m_vOrigin = penetrationTrace.endpos; + + DispatchEffect( "GlassImpact", data ); + + // Refire the round, as if starting from behind the glass + FireBulletsInfo_t behindGlassInfo; + behindGlassInfo.m_iShots = 1; + behindGlassInfo.m_vecSrc = penetrationTrace.endpos; + behindGlassInfo.m_vecDirShooting = vecDir; + behindGlassInfo.m_vecSpread = vec3_origin; + behindGlassInfo.m_flDistance = info.m_flDistance*( 1.0f - tr.fraction ); + behindGlassInfo.m_iAmmoType = info.m_iAmmoType; + behindGlassInfo.m_iTracerFreq = info.m_iTracerFreq; + behindGlassInfo.m_iDamage = info.m_iDamage; + behindGlassInfo.m_pAttacker = info.m_pAttacker ? info.m_pAttacker : this; + behindGlassInfo.m_nFlags = info.m_nFlags; + + FireBullets( behindGlassInfo ); +} + + +//----------------------------------------------------------------------------- +// Computes the tracer start position +//----------------------------------------------------------------------------- +#define SHOT_UNDERWATER_BUBBLE_DIST 400 + +void CBaseEntity::CreateBubbleTrailTracer( const Vector &vecShotSrc, const Vector &vecShotEnd, const Vector &vecShotDir ) +{ + int nBubbles; + Vector vecBubbleEnd; + float flLengthSqr = vecShotSrc.DistToSqr( vecShotEnd ); + if ( flLengthSqr > SHOT_UNDERWATER_BUBBLE_DIST * SHOT_UNDERWATER_BUBBLE_DIST ) + { + VectorMA( vecShotSrc, SHOT_UNDERWATER_BUBBLE_DIST, vecShotDir, vecBubbleEnd ); + nBubbles = static_cast(WATER_BULLET_BUBBLES_PER_INCH * SHOT_UNDERWATER_BUBBLE_DIST); + } + else + { + float flLength = sqrt(flLengthSqr) - 0.1f; + nBubbles = static_cast(WATER_BULLET_BUBBLES_PER_INCH * flLength); + VectorMA( vecShotSrc, flLength, vecShotDir, vecBubbleEnd ); + } + + Vector vecTracerSrc; + ComputeTracerStartPosition( vecShotSrc, &vecTracerSrc ); + UTIL_BubbleTrail( vecTracerSrc, vecBubbleEnd, nBubbles ); +} + + +//========================================================= +//========================================================= +void CAI_BaseNPC::MakeDamageBloodDecal ( int cCount, float flNoise, trace_t *ptr, Vector vecDir ) +{ + // make blood decal on the wall! + trace_t Bloodtr; + Vector vecTraceDir; + int i; + + if ( !IsAlive() ) + { + // dealing with a dead npc. + if ( m_iMaxHealth <= 0 ) + { + // no blood decal for a npc that has already decalled its limit. + return; + } + else + { + m_iMaxHealth -= 1; + } + } + + for ( i = 0 ; i < cCount ; i++ ) + { + vecTraceDir = vecDir; + + vecTraceDir.x += random->RandomFloat( -flNoise, flNoise ); + vecTraceDir.y += random->RandomFloat( -flNoise, flNoise ); + vecTraceDir.z += random->RandomFloat( -flNoise, flNoise ); + + AI_TraceLine( ptr->endpos, ptr->endpos + vecTraceDir * 172, MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &Bloodtr); + + if ( Bloodtr.fraction != 1.0 ) + { + UTIL_BloodDecalTrace( &Bloodtr, BloodColor() ); + } + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : &tr - +// nDamageType - +//----------------------------------------------------------------------------- +void CAI_BaseNPC::DoImpactEffect( trace_t &tr, int nDamageType ) +{ + if ( GetActiveWeapon() != NULL ) + { + GetActiveWeapon()->DoImpactEffect( tr, nDamageType ); + return; + } + + BaseClass::DoImpactEffect( tr, nDamageType ); +} + +//--------------------------------------------------------- +//--------------------------------------------------------- +#define InterruptFromCondition( iCondition ) \ + AI_RemapFromGlobal( ( AI_IdIsLocal( iCondition ) ? GetClassScheduleIdSpace()->ConditionLocalToGlobal( iCondition ) : iCondition ) ) + +void CAI_BaseNPC::SetCondition( int iCondition ) +{ + int interrupt = InterruptFromCondition( iCondition ); + + if ( interrupt == -1 ) + { + Assert(0); + return; + } + + m_Conditions.SetBit( interrupt ); +} + +//--------------------------------------------------------- +//--------------------------------------------------------- +bool CAI_BaseNPC::HasCondition( int iCondition ) +{ + int interrupt = InterruptFromCondition( iCondition ); + + if ( interrupt == -1 ) + { + Assert(0); + return false; + } + + bool bReturn = m_Conditions.GetBit(interrupt); + return (bReturn); +} + +//--------------------------------------------------------- +//--------------------------------------------------------- +bool CAI_BaseNPC::HasCondition( int iCondition, bool bUseIgnoreConditions ) +{ + if ( bUseIgnoreConditions ) + return HasCondition( iCondition ); + + int interrupt = InterruptFromCondition( iCondition ); + + if ( interrupt == -1 ) + { + Assert(0); + return false; + } + + bool bReturn = m_ConditionsPreIgnore.GetBit(interrupt); + return (bReturn); +} + +//--------------------------------------------------------- +//--------------------------------------------------------- +void CAI_BaseNPC::ClearCondition( int iCondition ) +{ + int interrupt = InterruptFromCondition( iCondition ); + + if ( interrupt == -1 ) + { + Assert(0); + return; + } + + m_Conditions.ClearBit(interrupt); +} + +//--------------------------------------------------------- +//--------------------------------------------------------- +void CAI_BaseNPC::ClearConditions( int *pConditions, int nConditions ) +{ + for ( int i = 0; i < nConditions; ++i ) + { + int iCondition = pConditions[i]; + int interrupt = InterruptFromCondition( iCondition ); + + if ( interrupt == -1 ) + { + Assert(0); + continue; + } + + m_Conditions.ClearBit( interrupt ); + } +} + +//--------------------------------------------------------- +//--------------------------------------------------------- +void CAI_BaseNPC::SetIgnoreConditions( int *pConditions, int nConditions ) +{ + for ( int i = 0; i < nConditions; ++i ) + { + int iCondition = pConditions[i]; + int interrupt = InterruptFromCondition( iCondition ); + + if ( interrupt == -1 ) + { + Assert(0); + continue; + } + + m_InverseIgnoreConditions.ClearBit( interrupt ); // clear means ignore + } +} + +void CAI_BaseNPC::ClearIgnoreConditions( int *pConditions, int nConditions ) +{ + for ( int i = 0; i < nConditions; ++i ) + { + int iCondition = pConditions[i]; + int interrupt = InterruptFromCondition( iCondition ); + + if ( interrupt == -1 ) + { + Assert(0); + continue; + } + + m_InverseIgnoreConditions.SetBit( interrupt ); // set means don't ignore + } +} + +//--------------------------------------------------------- +//--------------------------------------------------------- +bool CAI_BaseNPC::HasInterruptCondition( int iCondition ) +{ + if( !GetCurSchedule() ) + { + return false; + } + + int interrupt = InterruptFromCondition( iCondition ); + + if ( interrupt == -1 ) + { + Assert(0); + return false; + } + return ( m_Conditions.GetBit( interrupt ) && GetCurSchedule()->HasInterrupt( interrupt ) ); +} + +//--------------------------------------------------------- +//--------------------------------------------------------- +bool CAI_BaseNPC::ConditionInterruptsCurSchedule( int iCondition ) +{ + if( !GetCurSchedule() ) + { + return false; + } + + int interrupt = InterruptFromCondition( iCondition ); + + if ( interrupt == -1 ) + { + Assert(0); + return false; + } + return ( GetCurSchedule()->HasInterrupt( interrupt ) ); +} + +//--------------------------------------------------------- +//--------------------------------------------------------- +bool CAI_BaseNPC::ConditionInterruptsSchedule( int localScheduleID, int iCondition ) +{ + CAI_Schedule *pSchedule = GetSchedule( localScheduleID ); + if ( !pSchedule ) + return false; + + int interrupt = InterruptFromCondition( iCondition ); + + if ( interrupt == -1 ) + { + Assert(0); + return false; + } + return ( pSchedule->HasInterrupt( interrupt ) ); +} + + +//----------------------------------------------------------------------------- +// Returns whether we currently have any interrupt conditions that would +// interrupt the given schedule. +//----------------------------------------------------------------------------- +bool CAI_BaseNPC::HasConditionsToInterruptSchedule( int nLocalScheduleID ) +{ + CAI_Schedule *pSchedule = GetSchedule( nLocalScheduleID ); + if ( !pSchedule ) + return false; + + CAI_ScheduleBits bitsMask; + pSchedule->GetInterruptMask( &bitsMask ); + + CAI_ScheduleBits bitsOut; + AccessConditionBits().And( bitsMask, &bitsOut ); + + return !bitsOut.IsAllClear(); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +bool CAI_BaseNPC::IsCustomInterruptConditionSet( int nCondition ) +{ + int interrupt = InterruptFromCondition( nCondition ); + + if ( interrupt == -1 ) + { + Assert(0); + return false; + } + + return m_CustomInterruptConditions.GetBit( interrupt ); +} + +//----------------------------------------------------------------------------- +// Purpose: Sets a flag in the custom interrupt flags, translating the condition +// to the proper global space, if necessary +//----------------------------------------------------------------------------- +void CAI_BaseNPC::SetCustomInterruptCondition( int nCondition ) +{ + int interrupt = InterruptFromCondition( nCondition ); + + if ( interrupt == -1 ) + { + Assert(0); + return; + } + + m_CustomInterruptConditions.SetBit( interrupt ); +} + +//----------------------------------------------------------------------------- +// Purpose: Clears a flag in the custom interrupt flags, translating the condition +// to the proper global space, if necessary +//----------------------------------------------------------------------------- +void CAI_BaseNPC::ClearCustomInterruptCondition( int nCondition ) +{ + int interrupt = InterruptFromCondition( nCondition ); + + if ( interrupt == -1 ) + { + Assert(0); + return; + } + + m_CustomInterruptConditions.ClearBit( interrupt ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Clears all the custom interrupt flags. +//----------------------------------------------------------------------------- +void CAI_BaseNPC::ClearCustomInterruptConditions() +{ + m_CustomInterruptConditions.ClearAllBits(); +} + + +//----------------------------------------------------------------------------- + +void CAI_BaseNPC::SetDistLook( float flDistLook ) +{ + m_pSenses->SetDistLook( flDistLook ); +} + +//----------------------------------------------------------------------------- + +bool CAI_BaseNPC::QueryHearSound( CSound *pSound ) +{ + if ( pSound->SoundContext() & SOUND_CONTEXT_COMBINE_ONLY ) + return false; + + if ( pSound->SoundContext() & SOUND_CONTEXT_ALLIES_ONLY ) + { + if ( !IsPlayerAlly() ) + return false; + } + + if ( pSound->IsSoundType( SOUND_PLAYER ) && GetState() == NPC_STATE_IDLE && !FVisible( pSound->GetSoundReactOrigin() ) ) + { + // NPC's that are IDLE should disregard player movement sounds if they can't see them. + // This does not affect them hearing the player's weapon. + // !!!BUGBUG - this probably makes NPC's not hear doors opening, because doors opening put SOUND_PLAYER + // in the world, but the door's model will block the FVisible() trace and this code will then + // deduce that the sound can not be heard + return false; + } + + // Disregard footsteps from our own class type + if ( pSound->IsSoundType( SOUND_COMBAT ) && pSound->SoundChannel() == SOUNDENT_CHANNEL_NPC_FOOTSTEP ) + { + if ( pSound->m_hOwner && pSound->m_hOwner->ClassMatches( m_iClassname ) ) + return false; + } + + if( ShouldIgnoreSound( pSound ) ) + return false; + + return true; +} + +//----------------------------------------------------------------------------- + +bool CAI_BaseNPC::QuerySeeEntity( CBaseEntity *pEntity, bool bOnlyHateOrFearIfNPC ) +{ + if ( bOnlyHateOrFearIfNPC && pEntity->IsNPC() ) + { + Disposition_t disposition = IRelationType( pEntity ); + return ( disposition == D_HT || disposition == D_FR ); + } + return true; +} + +//----------------------------------------------------------------------------- + +void CAI_BaseNPC::OnLooked( int iDistance ) +{ + // DON'T let visibility information from last frame sit around! + static int conditionsToClear[] = + { + COND_SEE_HATE, + COND_SEE_DISLIKE, + COND_SEE_ENEMY, + COND_SEE_FEAR, + COND_SEE_NEMESIS, + COND_SEE_PLAYER, + COND_LOST_PLAYER, + COND_ENEMY_WENT_NULL, + }; + + bool bHadSeePlayer = HasCondition(COND_SEE_PLAYER); + + ClearConditions( conditionsToClear, ARRAYSIZE( conditionsToClear ) ); + + AISightIter_t iter; + CBaseEntity *pSightEnt; + + pSightEnt = GetSenses()->GetFirstSeenEntity( &iter ); + + while( pSightEnt ) + { + if ( pSightEnt->IsPlayer() ) + { + // if we see a client, remember that (mostly for scripted AI) + SetCondition(COND_SEE_PLAYER); + m_flLastSawPlayerTime = gpGlobals->curtime; + } + + Disposition_t relation = IRelationType( pSightEnt ); + + // the looker will want to consider this entity + // don't check anything else about an entity that can't be seen, or an entity that you don't care about. + if ( relation != D_NU ) + { + if ( pSightEnt == GetEnemy() ) + { + // we know this ent is visible, so if it also happens to be our enemy, store that now. + SetCondition(COND_SEE_ENEMY); + } + + // don't add the Enemy's relationship to the conditions. We only want to worry about conditions when + // we see npcs other than the Enemy. + switch ( relation ) + { + case D_HT: + { + int priority = IRelationPriority( pSightEnt ); + if (priority < 0) + { + SetCondition(COND_SEE_DISLIKE); + } + else if (priority > 10) + { + SetCondition(COND_SEE_NEMESIS); + } + else + { + SetCondition(COND_SEE_HATE); + } + UpdateEnemyMemory(pSightEnt,pSightEnt->GetAbsOrigin()); + break; + + } + case D_FR: + UpdateEnemyMemory(pSightEnt,pSightEnt->GetAbsOrigin()); + SetCondition(COND_SEE_FEAR); + break; + case D_LI: + case D_NU: + break; + default: + DevWarning( 2, "%s can't assess %s\n", GetClassname(), pSightEnt->GetClassname() ); + break; + } + } + + pSightEnt = GetSenses()->GetNextSeenEntity( &iter ); + } + + // Did we lose the player? + if ( bHadSeePlayer && !HasCondition(COND_SEE_PLAYER) ) + { + SetCondition(COND_LOST_PLAYER); + } +} + +//----------------------------------------------------------------------------- + +void CAI_BaseNPC::OnListened() +{ + AISoundIter_t iter; + + CSound *pCurrentSound; + + static int conditionsToClear[] = + { + COND_HEAR_DANGER, + COND_HEAR_COMBAT, + COND_HEAR_WORLD, + COND_HEAR_PLAYER, + COND_HEAR_THUMPER, + COND_HEAR_BUGBAIT, + COND_HEAR_PHYSICS_DANGER, + COND_HEAR_BULLET_IMPACT, + COND_HEAR_MOVE_AWAY, + + COND_NO_HEAR_DANGER, + + COND_SMELL, + }; + + ClearConditions( conditionsToClear, ARRAYSIZE( conditionsToClear ) ); + + pCurrentSound = GetSenses()->GetFirstHeardSound( &iter ); + + while ( pCurrentSound ) + { + // the npc cares about this sound, and it's close enough to hear. + int condition = COND_NONE; + + if ( pCurrentSound->FIsSound() ) + { + // this is an audible sound. + switch( pCurrentSound->SoundTypeNoContext() ) + { + case SOUND_DANGER: + if( gpGlobals->curtime > m_flIgnoreDangerSoundsUntil) + condition = COND_HEAR_DANGER; + break; + + case SOUND_THUMPER: condition = COND_HEAR_THUMPER; break; + case SOUND_BUGBAIT: condition = COND_HEAR_BUGBAIT; break; + case SOUND_COMBAT: + if ( pCurrentSound->SoundChannel() == SOUNDENT_CHANNEL_SPOOKY_NOISE ) + { + condition = COND_HEAR_SPOOKY; + } + else + { + condition = COND_HEAR_COMBAT; + } + break; + + case SOUND_WORLD: condition = COND_HEAR_WORLD; break; + case SOUND_PLAYER: condition = COND_HEAR_PLAYER; break; + case SOUND_BULLET_IMPACT: condition = COND_HEAR_BULLET_IMPACT; break; + case SOUND_PHYSICS_DANGER: condition = COND_HEAR_PHYSICS_DANGER; break; + case SOUND_DANGER_SNIPERONLY:/* silence warning */ break; + case SOUND_MOVE_AWAY: condition = COND_HEAR_MOVE_AWAY; break; + case SOUND_PLAYER_VEHICLE: condition = COND_HEAR_PLAYER; break; + + default: + DevMsg( "**ERROR: NPC %s hearing sound of unknown type %d!\n", GetClassname(), pCurrentSound->SoundType() ); + break; + } + } + else + { + // if not a sound, must be a smell - determine if it's just a scent, or if it's a food scent + condition = COND_SMELL; + } + + if ( condition != COND_NONE ) + { + SetCondition( condition ); + } + + pCurrentSound = GetSenses()->GetNextHeardSound( &iter ); + } + + if( !HasCondition( COND_HEAR_DANGER ) ) + { + SetCondition( COND_NO_HEAR_DANGER ); + } + + // Sound outputs + if ( HasCondition( COND_HEAR_WORLD ) ) + { + m_OnHearWorld.FireOutput(this, this); + } + + if ( HasCondition( COND_HEAR_PLAYER ) ) + { + m_OnHearPlayer.FireOutput(this, this); + } + + if ( HasCondition( COND_HEAR_COMBAT ) || + HasCondition( COND_HEAR_BULLET_IMPACT ) || + HasCondition( COND_HEAR_DANGER ) ) + { + m_OnHearCombat.FireOutput(this, this); + } +} + +//========================================================= +// FValidateHintType - tells use whether or not the npc cares +// about the type of Hint Node given +//========================================================= +bool CAI_BaseNPC::FValidateHintType ( CAI_Hint *pHint ) +{ + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: Override in subclasses to associate specific hint types +// with activities +//----------------------------------------------------------------------------- +Activity CAI_BaseNPC::GetHintActivity( short sHintType, Activity HintsActivity ) +{ + if ( HintsActivity != ACT_INVALID ) + return HintsActivity; + + return ACT_IDLE; +} + +//----------------------------------------------------------------------------- +// Purpose: Override in subclasses to give specific hint types delays +// before they can be used again +// Input : +// Output : +//----------------------------------------------------------------------------- +float CAI_BaseNPC::GetHintDelay( short sHintType ) +{ + return 0; +} + +//----------------------------------------------------------------------------- +// Purpose: Return incoming grenade if spotted +// Input : +// Output : +//----------------------------------------------------------------------------- +CBaseGrenade* CAI_BaseNPC::IncomingGrenade(void) +{ + int iDist; + + AIEnemiesIter_t iter; + for( AI_EnemyInfo_t *pEMemory = GetEnemies()->GetFirst(&iter); pEMemory != NULL; pEMemory = GetEnemies()->GetNext(&iter) ) + { + CBaseGrenade* pBG = dynamic_cast((CBaseEntity*)pEMemory->hEnemy); + + // Make sure this memory is for a grenade and grenade is not dead + if (!pBG || pBG->m_lifeState == LIFE_DEAD) + continue; + + // Make sure it's visible + if (!FVisible(pBG)) + continue; + + // Check if it's near me + iDist = static_cast(( pBG->GetAbsOrigin() - GetAbsOrigin() ).Length()); + if ( iDist <= NPC_GRENADE_FEAR_DIST ) + return pBG; + + // Check if it's headed towards me + Vector vGrenadeDir = GetAbsOrigin() - pBG->GetAbsOrigin(); + Vector vGrenadeVel; + pBG->GetVelocity( &vGrenadeVel, NULL ); + VectorNormalize(vGrenadeDir); + VectorNormalize(vGrenadeVel); + float flDotPr = DotProduct(vGrenadeDir, vGrenadeVel); + if (flDotPr > 0.85) + return pBG; + } + return NULL; +} + + +//----------------------------------------------------------------------------- +// Purpose: Check my physical state with the environment +// Input : +// Output : +//----------------------------------------------------------------------------- +void CAI_BaseNPC::TryRestoreHull(void) +{ + if ( IsUsingSmallHull() && GetCurSchedule() ) + { + trace_t tr; + Vector vUpBit = GetAbsOrigin(); + vUpBit.z += 1; + + AI_TraceHull( GetAbsOrigin(), vUpBit, GetHullMins(), + GetHullMaxs(), MASK_SOLID, this, COLLISION_GROUP_NONE, &tr ); + if ( !tr.startsolid && (tr.fraction == 1.0) ) + { + SetHullSizeNormal(); + } + } +} + +//========================================================= +//========================================================= +int CAI_BaseNPC::GetSoundInterests( void ) +{ + return SOUND_WORLD | SOUND_COMBAT | SOUND_PLAYER | SOUND_PLAYER_VEHICLE | + SOUND_BULLET_IMPACT; +} + +//--------------------------------------------------------- +//--------------------------------------------------------- +int CAI_BaseNPC::GetSoundPriority( CSound *pSound ) +{ + int iSoundTypeNoContext = pSound->SoundTypeNoContext(); + int iSoundContext = pSound->SoundContext(); + + if( iSoundTypeNoContext & SOUND_DANGER ) + { + return SOUND_PRIORITY_HIGHEST; + } + + if( iSoundTypeNoContext & SOUND_COMBAT ) + { + if( iSoundContext & SOUND_CONTEXT_EXPLOSION ) + { + return SOUND_PRIORITY_VERY_HIGH; + } + + return SOUND_PRIORITY_HIGH; + } + + return SOUND_PRIORITY_NORMAL; +} + +//--------------------------------------------------------- +// Return the loudest sound of this type in the sound list +//--------------------------------------------------------- +CSound *CAI_BaseNPC::GetLoudestSoundOfType( int iType ) +{ + return CSoundEnt::GetLoudestSoundOfType( iType, EarPosition() ); +} + +//========================================================= +// GetBestSound - returns a pointer to the sound the npc +// should react to. Right now responds only to nearest sound. +//========================================================= +CSound* CAI_BaseNPC::GetBestSound( int validTypes ) +{ + if ( m_pLockedBestSound->m_iType != SOUND_NONE ) + return m_pLockedBestSound; + CSound *pResult = GetSenses()->GetClosestSound( false, validTypes ); + if ( pResult == NULL) + DevMsg( "Warning: NULL Return from GetBestSound\n" ); // condition previously set now no longer true. Have seen this when play too many sounds... + return pResult; +} + +//========================================================= +// PBestScent - returns a pointer to the scent the npc +// should react to. Right now responds only to nearest scent +//========================================================= +CSound* CAI_BaseNPC::GetBestScent( void ) +{ + CSound *pResult = GetSenses()->GetClosestSound( true ); + if ( pResult == NULL) + DevMsg("Warning: NULL Return from GetBestScent\n" ); + return pResult; +} + +//----------------------------------------------------------------------------- +void CAI_BaseNPC::LockBestSound() +{ + UnlockBestSound(); + CSound *pBestSound = GetBestSound(); + if ( pBestSound ) + *m_pLockedBestSound = *pBestSound; +} + +//----------------------------------------------------------------------------- +void CAI_BaseNPC::UnlockBestSound() +{ + if ( m_pLockedBestSound->m_iType != SOUND_NONE ) + { + m_pLockedBestSound->m_iType = SOUND_NONE; + OnListened(); // reset hearing conditions + } +} + +//----------------------------------------------------------------------------- +// Purpose: Return true if the specified sound is visible. Handles sounds generated by entities correctly. +// Input : *pSound - +//----------------------------------------------------------------------------- +bool CAI_BaseNPC::SoundIsVisible( CSound *pSound ) +{ + CBaseEntity *pBlocker = NULL; + if ( !FVisible( pSound->GetSoundReactOrigin(), MASK_OPAQUE, &pBlocker ) ) + { + // Is the blocker the sound owner? + if ( pBlocker && pBlocker == pSound->m_hOwner ) + return true; + + return false; + } + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: Returns true if target is in legal range of eye movements +// Input : +// Output : +//----------------------------------------------------------------------------- +bool CAI_BaseNPC::ValidEyeTarget(const Vector &lookTargetPos) +{ + Vector vHeadDir = HeadDirection3D( ); + Vector lookTargetDir = lookTargetPos - EyePosition(); + VectorNormalize(lookTargetDir); + + // Only look if it doesn't crank my eyeballs too far + float dotPr = DotProduct(lookTargetDir, vHeadDir); + if (dotPr > 0.7) + { + return true; + } + return false; +} + + +//----------------------------------------------------------------------------- +// Purpose: Integrate head turn over time +// Input : +// Output : +//----------------------------------------------------------------------------- +void CAI_BaseNPC::SetHeadDirection( const Vector &vTargetPos, float flInterval) +{ + if (!(CapabilitiesGet() & bits_CAP_TURN_HEAD)) + return; + +#ifdef DEBUG_LOOK + // Draw line in body, head, and eye directions + Vector vEyePos = EyePosition(); + Vector vHeadDir; + HeadDirection3D(&vHeadDir); + Vector vBodyDir; + BodyDirection2D(&vBodyDir); + + //UNDONE <> + // currently eye dir just returns head dir, so use vTargetPos for now + //Vector vEyeDir; w + //EyeDirection3D(&vEyeDir); + NDebugOverlay::Line( vEyePos, vEyePos+(50*vHeadDir), 255, 0, 0, false, 0.1 ); + NDebugOverlay::Line( vEyePos, vEyePos+(50*vBodyDir), 0, 255, 0, false, 0.1 ); + NDebugOverlay::Line( vEyePos, vTargetPos, 0, 0, 255, false, 0.1 ); +#endif + + //-------------------------------------- + // Set head yaw + //-------------------------------------- + float flDesiredYaw = VecToYaw(vTargetPos - GetLocalOrigin()) - GetLocalAngles().y; + if (flDesiredYaw > 180) + flDesiredYaw -= 360; + if (flDesiredYaw < -180) + flDesiredYaw += 360; + + float iRate = 0.8; + + // Make frame rate independent + float timeToUse = flInterval; + while (timeToUse > 0) + { + m_flHeadYaw = (iRate * m_flHeadYaw) + (1-iRate)*flDesiredYaw; + timeToUse -= 0.1; + } + if (m_flHeadYaw > 360) m_flHeadYaw = 0; + + m_flHeadYaw = SetBoneController( 0, m_flHeadYaw ); + + + //-------------------------------------- + // Set head pitch + //-------------------------------------- + Vector vEyePosition = EyePosition(); + float fTargetDist = (vTargetPos - vEyePosition).Length(); + float fVertDist = vTargetPos.z - vEyePosition.z; + float flDesiredPitch = -RAD2DEG(atan(fVertDist/fTargetDist)); + + // Make frame rate independent + timeToUse = flInterval; + while (timeToUse > 0) + { + m_flHeadPitch = (iRate * m_flHeadPitch) + (1-iRate)*flDesiredPitch; + timeToUse -= 0.1; + } + if (m_flHeadPitch > 360) m_flHeadPitch = 0; + + SetBoneController( 1, m_flHeadPitch ); + +} + +//------------------------------------------------------------------------------ +// Purpose : Calculate the NPC's eye direction in 2D world space +// Input : +// Output : +//------------------------------------------------------------------------------ +Vector CAI_BaseNPC::EyeDirection2D( void ) +{ + // UNDONE + // For now just return head direction.... + return HeadDirection2D( ); +} + +//------------------------------------------------------------------------------ +// Purpose : Calculate the NPC's eye direction in 2D world space +// Input : +// Output : +//------------------------------------------------------------------------------ +Vector CAI_BaseNPC::EyeDirection3D( void ) +{ + // UNDONE //<> + // For now just return head direction.... + return HeadDirection3D( ); +} + +//------------------------------------------------------------------------------ +// Purpose : Calculate the NPC's head direction in 2D world space +// Input : +// Output : +//------------------------------------------------------------------------------ +Vector CAI_BaseNPC::HeadDirection2D( void ) +{ + // UNDONE <> + // This does not account for head rotations in the animation, + // only those done via bone controllers + + // Head yaw is in local cooridnate so it must be added to the body's yaw + QAngle bodyAngles = BodyAngles(); + float flWorldHeadYaw = m_flHeadYaw + bodyAngles.y; + + // Convert head yaw into facing direction + return UTIL_YawToVector( flWorldHeadYaw ); +} + +//------------------------------------------------------------------------------ +// Purpose : Calculate the NPC's head direction in 3D world space +// Input : +// Output : +//------------------------------------------------------------------------------ +Vector CAI_BaseNPC::HeadDirection3D( void ) +{ + Vector vHeadDirection; + + // UNDONE <> + // This does not account for head rotations in the animation, + // only those done via bone controllers + + // Head yaw is in local cooridnate so it must be added to the body's yaw + QAngle bodyAngles = BodyAngles(); + float flWorldHeadYaw = m_flHeadYaw + bodyAngles.y; + + // Convert head yaw into facing direction + AngleVectors( QAngle( m_flHeadPitch, flWorldHeadYaw, 0), &vHeadDirection ); + return vHeadDirection; +} + + +//----------------------------------------------------------------------------- +// Purpose: Look at other NPCs and clients from time to time +// Input : +// Output : +//----------------------------------------------------------------------------- +CBaseEntity *CAI_BaseNPC::EyeLookTarget( void ) +{ + if (m_flNextEyeLookTime < gpGlobals->curtime) + { + CBaseEntity* pBestEntity = NULL; + float fBestDist = MAX_COORD_RANGE; + float fTestDist; + + CBaseEntity *pEntity = NULL; + + for ( CEntitySphereQuery sphere( GetAbsOrigin(), 1024, 0 ); (pEntity = sphere.GetCurrentEntity()) != NULL; sphere.NextEntity() ) + { + if (pEntity == this) + { + continue; + } + CAI_BaseNPC *pNPC = pEntity->MyNPCPointer(); + if (pNPC || (pEntity->GetFlags() & FL_CLIENT)) + { + fTestDist = (GetAbsOrigin() - pEntity->EyePosition()).Length(); + if (fTestDist < fBestDist) + { + if (ValidEyeTarget(pEntity->EyePosition())) + { + fBestDist = fTestDist; + pBestEntity = pEntity; + } + } + } + } + if (pBestEntity) + { + m_flNextEyeLookTime = gpGlobals->curtime + random->RandomInt(1,5); + m_hEyeLookTarget = pBestEntity; + } + } + return m_hEyeLookTarget; +} + + +//----------------------------------------------------------------------------- +// Purpose: Set direction that the NPC aiming their gun +// returns true is the passed Vector is in +// the caller's forward aim cone. The dot product is performed +// in 2d, making the view cone infinitely tall. By default, the +// callers aim cone is assumed to be very narrow +//----------------------------------------------------------------------------- + +bool CAI_BaseNPC::FInAimCone( const Vector &vecSpot ) +{ + Vector los = ( vecSpot - GetAbsOrigin() ); + + // do this in 2D + los.z = 0; + VectorNormalize( los ); + + Vector facingDir = BodyDirection2D( ); + + float flDot = DotProduct( los, facingDir ); + + if (CapabilitiesGet() & bits_CAP_AIM_GUN) + { + // FIXME: query current animation for ranges + return ( flDot > DOT_30DEGREE ); + } + + if ( flDot > 0.994 )//!!!BUGBUG - magic number same as FacingIdeal(), what is this? + return true; + + return false; +} + + +//----------------------------------------------------------------------------- +// Purpose: Set direction that the NPC aiming their gun +//----------------------------------------------------------------------------- + +void CAI_BaseNPC::SetAim( const Vector &aimDir ) +{ + QAngle angDir; + VectorAngles( aimDir, angDir ); + + float curPitch = GetPoseParameter( "aim_pitch" ); + float curYaw = GetPoseParameter( "aim_yaw" ); + + float newPitch; + float newYaw; + + if( GetEnemy() ) + { + // clamp and dampen movement + newPitch = curPitch + 0.8 * UTIL_AngleDiff( UTIL_ApproachAngle( angDir.x, curPitch, 20 ), curPitch ); + + float flRelativeYaw = UTIL_AngleDiff( angDir.y, GetAbsAngles().y ); + // float flNewTargetYaw = UTIL_ApproachAngle( flRelativeYaw, curYaw, 20 ); + // float newYaw = curYaw + 0.8 * UTIL_AngleDiff( flNewTargetYaw, curYaw ); + newYaw = curYaw + UTIL_AngleDiff( flRelativeYaw, curYaw ); + } + else + { + // Sweep your weapon more slowly if you're not fighting someone + newPitch = curPitch + 0.6 * UTIL_AngleDiff( UTIL_ApproachAngle( angDir.x, curPitch, 20 ), curPitch ); + + float flRelativeYaw = UTIL_AngleDiff( angDir.y, GetAbsAngles().y ); + newYaw = curYaw + 0.6 * UTIL_AngleDiff( flRelativeYaw, curYaw ); + } + + newPitch = AngleNormalize( newPitch ); + newYaw = AngleNormalize( newYaw ); + + SetPoseParameter( "aim_pitch", newPitch ); + SetPoseParameter( "aim_yaw", newYaw ); + + // Msg("yaw %.0f (%.0f %.0f)\n", newYaw, angDir.y, GetAbsAngles().y ); + + // Calculate our interaction yaw. + // If we've got a small adjustment off our abs yaw, use that. + // Otherwise, use our abs yaw. + if ( fabs(newYaw) < 20 ) + { + m_flInteractionYaw = angDir.y; + } + else + { + m_flInteractionYaw = GetAbsAngles().y; + } +} + + +void CAI_BaseNPC::RelaxAim( ) +{ + float curPitch = GetPoseParameter( "aim_pitch" ); + float curYaw = GetPoseParameter( "aim_yaw" ); + + // dampen existing aim + float newPitch = AngleNormalize( UTIL_ApproachAngle( 0, curPitch, 3 ) ); + float newYaw = AngleNormalize( UTIL_ApproachAngle( 0, curYaw, 2 ) ); + + SetPoseParameter( "aim_pitch", newPitch ); + SetPoseParameter( "aim_yaw", newYaw ); + // DevMsg("relax aim %.0f %0.f\n", newPitch, newYaw ); +} + +//----------------------------------------------------------------------------- +void CAI_BaseNPC::AimGun() +{ + if (GetEnemy()) + { + Vector vecShootOrigin; + + vecShootOrigin = Weapon_ShootPosition(); + Vector vecShootDir = GetShootEnemyDir( vecShootOrigin, false ); + + SetAim( vecShootDir ); + } + else + { + RelaxAim( ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Set direction that the NPC is looking +// Input : +// Output : +//----------------------------------------------------------------------------- +void CAI_BaseNPC::MaintainLookTargets ( float flInterval ) +{ + // -------------------------------------------------------- + // Try to look at enemy if I have one + // -------------------------------------------------------- + if ((CBaseEntity*)GetEnemy()) + { + if ( ValidEyeTarget(GetEnemy()->EyePosition()) ) + { + SetHeadDirection(GetEnemy()->EyePosition(),flInterval); + SetViewtarget( GetEnemy()->EyePosition() ); + return; + } + } + +#if 0 + // -------------------------------------------------------- + // First check if I've been assigned to look at an entity + // -------------------------------------------------------- + CBaseEntity *lookTarget = EyeLookTarget(); + if (lookTarget && ValidEyeTarget(lookTarget->EyePosition())) + { + SetHeadDirection(lookTarget->EyePosition(),flInterval); + SetViewtarget( lookTarget->EyePosition() ); + return; + } +#endif + + // -------------------------------------------------------- + // If I'm moving, look at my target position + // -------------------------------------------------------- + if (GetNavigator()->IsGoalActive() && ValidEyeTarget(GetNavigator()->GetCurWaypointPos())) + { + SetHeadDirection(GetNavigator()->GetCurWaypointPos(),flInterval); + SetViewtarget( GetNavigator()->GetCurWaypointPos() ); + return; + } + + + // ------------------------------------- + // If I hear a combat sounds look there + // ------------------------------------- + if ( HasCondition(COND_HEAR_COMBAT) || HasCondition(COND_HEAR_DANGER) ) + { + CSound *pSound = GetBestSound(); + + if ( pSound && pSound->IsSoundType(SOUND_COMBAT | SOUND_DANGER) ) + { + if (ValidEyeTarget( pSound->GetSoundOrigin() )) + { + SetHeadDirection(pSound->GetSoundOrigin(),flInterval); + SetViewtarget( pSound->GetSoundOrigin() ); + return; + } + } + } + + // ------------------------------------- + // Otherwise just look around randomly + // ------------------------------------- + + // Check that look target position is still valid + if (m_flNextEyeLookTime > gpGlobals->curtime) + { + if (!ValidEyeTarget(m_vEyeLookTarget)) + { + // Force choosing of new look target + m_flNextEyeLookTime = 0; + } + } + + if (m_flNextEyeLookTime < gpGlobals->curtime) + { + Vector vBodyDir = BodyDirection2D( ); + + /* + Vector lookSpread = Vector(0.82,0.82,0.22); + float x = random->RandomFloat(-0.5,0.5) + random->RandomFloat(-0.5,0.5); + float y = random->RandomFloat(-0.5,0.5) + random->RandomFloat(-0.5,0.5); + float z = random->RandomFloat(-0.5,0.5) + random->RandomFloat(-0.5,0.5); + + QAngle angles; + VectorAngles( vBodyDir, angles ); + Vector forward, right, up; + AngleVectors( angles, &forward, &right, &up ); + + Vector vecDir = vBodyDir + (x * lookSpread.x * forward) + (y * lookSpread.y * right) + (z * lookSpread.z * up); + float lookDist = random->RandomInt(50,2000); + m_vEyeLookTarget = EyePosition() + lookDist*vecDir; + */ + m_vEyeLookTarget = EyePosition() + 500*vBodyDir; + m_flNextEyeLookTime = gpGlobals->curtime + 0.5; // random->RandomInt(1,5); + } + SetHeadDirection(m_vEyeLookTarget,flInterval); + + // ---------------------------------------------------- + // Integrate eye turn in frame rate independent manner + // ---------------------------------------------------- + float timeToUse = flInterval; + while (timeToUse > 0) + { + m_vCurEyeTarget = ((1 - m_flEyeIntegRate) * m_vCurEyeTarget + m_flEyeIntegRate * m_vEyeLookTarget); + timeToUse -= 0.1; + } + SetViewtarget( m_vCurEyeTarget ); +} + + +//----------------------------------------------------------------------------- +// Let the motor deal with turning presentation issues +//----------------------------------------------------------------------------- + +void CAI_BaseNPC::MaintainTurnActivity( ) +{ + AI_PROFILE_SCOPE( CAI_BaseNPC_MaintainTurnActivity ); + GetMotor()->MaintainTurnActivity( ); +} + + +//----------------------------------------------------------------------------- +// Here's where all motion occurs +//----------------------------------------------------------------------------- +void CAI_BaseNPC::PerformMovement() +{ + // don't bother to move if the npc isn't alive + if (!IsAlive()) + return; + + AI_PROFILE_SCOPE(CAI_BaseNPC_PerformMovement); + g_AIMoveTimer.Start(); + + float flInterval = ( m_flTimeLastMovement != FLT_MAX ) ? gpGlobals->curtime - m_flTimeLastMovement : 0.1; + + m_pNavigator->Move( ROUND_TO_TICKS( flInterval ) ); + m_flTimeLastMovement = gpGlobals->curtime; + + g_AIMoveTimer.End(); + +} + +//----------------------------------------------------------------------------- +// Updates to npc after movement is completed +//----------------------------------------------------------------------------- + +void CAI_BaseNPC::PostMovement() +{ + AI_PROFILE_SCOPE( CAI_BaseNPC_PostMovement ); + + InvalidateBoneCache(); + + if ( GetModelPtr() && GetModelPtr()->SequencesAvailable() ) + { + float flInterval = GetAnimTimeInterval(); + + if ( CapabilitiesGet() & bits_CAP_AIM_GUN ) + { + AI_PROFILE_SCOPE( CAI_BaseNPC_PM_AimGun ); + AimGun(); + } + + // set look targets for npcs with animated faces + if ( CapabilitiesGet() & bits_CAP_ANIMATEDFACE ) + { + AI_PROFILE_SCOPE( CAI_BaseNPC_PM_MaintainLookTargets ); + MaintainLookTargets(flInterval); + } + + } + + + { + AI_PROFILE_SCOPE( CAI_BaseNPC_PM_MaintainTurnActivity ); + // update turning as needed + MaintainTurnActivity( ); + } +} + + +//----------------------------------------------------------------------------- + +float g_AINextDisabledMessageTime; +extern bool IsInCommentaryMode( void ); + +bool CAI_BaseNPC::PreThink( void ) +{ + // ---------------------------------------------------------- + // Skip AI if its been disabled or networks haven't been + // loaded, and put a warning message on the screen + // + // Don't do this if the convar wants it hidden + // ---------------------------------------------------------- + if ( (CAI_BaseNPC::m_nDebugBits & bits_debugDisableAI || !g_pAINetworkManager->NetworksLoaded()) ) + { + if ( gpGlobals->curtime >= g_AINextDisabledMessageTime && !IsInCommentaryMode() ) + { + g_AINextDisabledMessageTime = gpGlobals->curtime + 0.5f; + + hudtextparms_s tTextParam; + tTextParam.x = 0.7; + tTextParam.y = 0.65; + tTextParam.effect = 0; + tTextParam.r1 = 255; + tTextParam.g1 = 255; + tTextParam.b1 = 255; + tTextParam.a1 = 255; + tTextParam.r2 = 255; + tTextParam.g2 = 255; + tTextParam.b2 = 255; + tTextParam.a2 = 255; + tTextParam.fadeinTime = 0; + tTextParam.fadeoutTime = 0; + tTextParam.holdTime = 0.6; + tTextParam.fxTime = 0; + tTextParam.channel = 1; + UTIL_HudMessageAll( tTextParam, "A.I. Disabled...\n" ); + } + SetActivity( ACT_IDLE ); + return false; + } + + // -------------------------------------------------------- + // If debug stepping + // -------------------------------------------------------- + if (CAI_BaseNPC::m_nDebugBits & bits_debugStepAI) + { + if (m_nDebugCurIndex >= CAI_BaseNPC::m_nDebugPauseIndex) + { + if (!GetNavigator()->IsGoalActive()) + { + m_flPlaybackRate = 0; + } + return false; + } + else + { + m_flPlaybackRate = 1; + } + } + + if ( m_hOpeningDoor.Get() && AIIsDebuggingDoors( this ) ) + { + NDebugOverlay::Line( EyePosition(), m_hOpeningDoor->WorldSpaceCenter(), 255, 255, 255, false, .1 ); + } + + return true; +} + +//----------------------------------------------------------------------------- + +void CAI_BaseNPC::RunAnimation( void ) +{ + VPROF_BUDGET( "CAI_BaseNPC_RunAnimation", VPROF_BUDGETGROUP_SERVER_ANIM ); + + if ( !GetModelPtr() ) + return; + + float flInterval = GetAnimTimeInterval(); + + StudioFrameAdvance( ); // animate + + if ((CAI_BaseNPC::m_nDebugBits & bits_debugStepAI) && !GetNavigator()->IsGoalActive()) + { + flInterval = 0; + } + + // start or end a fidget + // This needs a better home -- switching animations over time should be encapsulated on a per-activity basis + // perhaps MaintainActivity() or a ShiftAnimationOverTime() or something. + if ( m_NPCState != NPC_STATE_SCRIPT && m_NPCState != NPC_STATE_DEAD && m_Activity == ACT_IDLE && IsActivityFinished() ) + { + int iSequence; + + // FIXME: this doesn't reissue a translation, so if the idle activity translation changes over time, it'll never get reset + if ( SequenceLoops() ) + { + // animation does loop, which means we're playing subtle idle. Might need to fidget. + iSequence = SelectWeightedSequence ( m_translatedActivity ); + } + else + { + // animation that just ended doesn't loop! That means we just finished a fidget + // and should return to our heaviest weighted idle (the subtle one) + iSequence = SelectHeaviestSequence ( m_translatedActivity ); + } + if ( iSequence != ACTIVITY_NOT_AVAILABLE ) + { + ResetSequence( iSequence ); // Set to new anim (if it's there) + + //Adrian: Basically everywhere else in the AI code this variable gets set to whatever our sequence is. + //But here it doesn't and not setting it causes any animation set through here to be stomped by the + //ideal sequence before it has a chance of playing out (since there's code that reselects the ideal + //sequence if it doesn't match the current one). + if ( hl2_episodic.GetBool() ) + { + m_nIdealSequence = iSequence; + } + } + } + + DispatchAnimEvents( this ); +} + +//----------------------------------------------------------------------------- + +void CAI_BaseNPC::PostRun( void ) +{ + AI_PROFILE_SCOPE(CAI_BaseNPC_PostRun); + + g_AIPostRunTimer.Start(); + + if ( !IsMoving() ) + { + if ( GetIdealActivity() == ACT_WALK || + GetIdealActivity() == ACT_RUN || + GetIdealActivity() == ACT_WALK_AIM || + GetIdealActivity() == ACT_RUN_AIM ) + { + PostRunStopMoving(); + } + } + + RunAnimation(); + + // update slave items (weapons) + Weapon_FrameUpdate(); + + g_AIPostRunTimer.End(); +} + +//----------------------------------------------------------------------------- + +void CAI_BaseNPC::PostRunStopMoving() +{ + DbgNavMsg1( this, "NPC %s failed to stop properly, slamming activity\n", GetDebugName() ); + if ( !GetNavigator()->SetGoalFromStoppingPath() ) + SetIdealActivity( GetStoppedActivity() ); // This is to prevent running in place +} + +//----------------------------------------------------------------------------- + +bool CAI_BaseNPC::ShouldAlwaysThink() +{ + // @TODO (toml 07-08-03): This needs to be beefed up. + // There are simple cases already seen where an AI can briefly leave + // the PVS while navigating to the player. Perhaps should incorporate a heuristic taking into + // account mode, enemy, last time saw player, player range etc. For example, if enemy is player, + // and player is within 100 feet, and saw the player within the past 15 seconds, keep running... + return HasSpawnFlags(SF_NPC_ALWAYSTHINK); +} + + +bool CAI_BaseNPC::ShouldPlayerAvoid( void ) +{ + if ( GetState() == NPC_STATE_SCRIPT ) + return true; + + if ( IsInAScript() ) + return true; + + if ( IsInLockedScene() == true ) + return true; + + if ( HasSpawnFlags( SF_NPC_ALTCOLLISION ) ) + return true; + + return false; +} + +//----------------------------------------------------------------------------- + +void CAI_BaseNPC::UpdateEfficiency( bool bInPVS ) +{ + // Sleeping NPCs always dormant + if ( GetSleepState() != AISS_AWAKE ) + { + SetEfficiency( AIE_DORMANT ); + return; + } + + m_bInChoreo = ( GetState() == NPC_STATE_SCRIPT || IsCurSchedule( SCHED_SCENE_GENERIC, false ) ); + + if ( !ShouldUseEfficiency() ) + { + SetEfficiency( AIE_NORMAL ); + SetMoveEfficiency( AIME_NORMAL ); + return; + } + + //--------------------------------- + + CBasePlayer *pPlayer = AI_GetSinglePlayer(); + static Vector vPlayerEyePosition; + static Vector vPlayerForward; + static int iPrevFrame = -1; + if ( gpGlobals->framecount != iPrevFrame ) + { + iPrevFrame = gpGlobals->framecount; + if ( pPlayer ) + { + pPlayer->EyePositionAndVectors( &vPlayerEyePosition, &vPlayerForward, NULL, NULL ); + } + } + + Vector vToNPC = GetAbsOrigin() - vPlayerEyePosition; + float playerDist = VectorNormalize( vToNPC ); + bool bPlayerFacing; + + bool bClientPVSExpanded = UTIL_ClientPVSIsExpanded(); + + if ( pPlayer ) + { + bPlayerFacing = ( bClientPVSExpanded || ( bInPVS && vPlayerForward.Dot( vToNPC ) > 0 ) ); + } + else + { + playerDist = 0; + bPlayerFacing = true; + } + + //--------------------------------- + + bool bInVisibilityPVS = ( bClientPVSExpanded && UTIL_FindClientInVisibilityPVS( edict() ) != NULL ); + + //--------------------------------- + + if ( ( bInPVS && ( bPlayerFacing || playerDist < 25*12 ) ) || bClientPVSExpanded ) + { + SetMoveEfficiency( AIME_NORMAL ); + } + else + { + SetMoveEfficiency( AIME_EFFICIENT ); + } + + //--------------------------------- + + if ( !IsRetail() && ai_efficiency_override.GetInt() > AIE_NORMAL && ai_efficiency_override.GetInt() <= AIE_DORMANT ) + { + SetEfficiency( (AI_Efficiency_t)ai_efficiency_override.GetInt() ); + return; + } + + //--------------------------------- + + // Some conditions will always force normal + if ( gpGlobals->curtime - GetLastAttackTime() < .15 ) + { + SetEfficiency( AIE_NORMAL ); + return; + } + + bool bFramerateOk = ( gpGlobals->frametime < ai_frametime_limit.GetFloat() ); + + if ( m_bForceConditionsGather || + gpGlobals->curtime - GetLastAttackTime() < .2 || + gpGlobals->curtime - m_flLastDamageTime < .2 || + ( GetState() < NPC_STATE_IDLE || GetState() > NPC_STATE_SCRIPT ) || + ( ( bInPVS || bInVisibilityPVS ) && + ( ( GetTask() && !TaskIsRunning() ) || + GetTaskInterrupt() > 0 || + m_bInChoreo ) ) ) + { + SetEfficiency( ( bFramerateOk ) ? AIE_NORMAL : AIE_EFFICIENT ); + return; + } + + AI_Efficiency_t minEfficiency; + + if ( !ShouldDefaultEfficient() ) + { + minEfficiency = ( bFramerateOk ) ? AIE_NORMAL : AIE_EFFICIENT; + } + else + { + minEfficiency = ( bFramerateOk ) ? AIE_EFFICIENT : AIE_VERY_EFFICIENT; + } + + // Stay normal if there's any chance of a relevant danger sound + bool bPotentialDanger = false; + + if ( GetSoundInterests() & SOUND_DANGER ) + { + int iSound = CSoundEnt::ActiveList(); + + while ( iSound != SOUNDLIST_EMPTY ) + { + CSound *pCurrentSound = CSoundEnt::SoundPointerForIndex( iSound ); + + float hearingSensitivity = HearingSensitivity(); + Vector vEarPosition = EarPosition(); + + if ( pCurrentSound && (SOUND_DANGER & pCurrentSound->SoundType()) ) + { + float flHearDistanceSq = pCurrentSound->Volume() * hearingSensitivity; + flHearDistanceSq *= flHearDistanceSq; + if ( pCurrentSound->GetSoundOrigin().DistToSqr( vEarPosition ) <= flHearDistanceSq ) + { + bPotentialDanger = true; + break; + } + } + + iSound = pCurrentSound->NextSound(); + } + } + + if ( bPotentialDanger ) + { + SetEfficiency( minEfficiency ); + return; + } + + //--------------------------------- + + if ( !pPlayer ) + { + // No heuristic currently for dedicated servers + SetEfficiency( minEfficiency ); + return; + } + + enum + { + NEAR, + MID, + FAR + }; + + int range; + if ( bInPVS ) + { + if ( playerDist < 15*12 ) + { + SetEfficiency( minEfficiency ); + return; + } + + range = ( playerDist < 50*12 ) ? NEAR : + ( playerDist < 200*12 ) ? MID : FAR; + } + else + { + range = ( playerDist < 25*12 ) ? NEAR : + ( playerDist < 100*12 ) ? MID : FAR; + } + + // Efficiency mappings + int state = GetState(); + if (state == NPC_STATE_SCRIPT ) // Treat script as alert. Already confirmed not in PVS + state = NPC_STATE_ALERT; + + static AI_Efficiency_t mappings[] = + { + // Idle + // In PVS + // Facing + AIE_NORMAL, + AIE_EFFICIENT, + AIE_EFFICIENT, + // Not facing + AIE_EFFICIENT, + AIE_EFFICIENT, + AIE_VERY_EFFICIENT, + // Not in PVS + AIE_VERY_EFFICIENT, + AIE_SUPER_EFFICIENT, + AIE_SUPER_EFFICIENT, + // Alert + // In PVS + // Facing + AIE_NORMAL, + AIE_EFFICIENT, + AIE_EFFICIENT, + // Not facing + AIE_NORMAL, + AIE_EFFICIENT, + AIE_EFFICIENT, + // Not in PVS + AIE_EFFICIENT, + AIE_VERY_EFFICIENT, + AIE_SUPER_EFFICIENT, + // Combat + // In PVS + // Facing + AIE_NORMAL, + AIE_NORMAL, + AIE_EFFICIENT, + // Not facing + AIE_NORMAL, + AIE_EFFICIENT, + AIE_EFFICIENT, + // Not in PVS + AIE_NORMAL, + AIE_EFFICIENT, + AIE_VERY_EFFICIENT, + }; + + static const int stateBase[] = { 0, 9, 18 }; + const int NOT_FACING_OFFSET = 3; + const int NO_PVS_OFFSET = 6; + + int iStateOffset = stateBase[state - NPC_STATE_IDLE] ; + int iFacingOffset = (!bInPVS || bPlayerFacing) ? 0 : NOT_FACING_OFFSET; + int iPVSOffset = (bInPVS) ? 0 : NO_PVS_OFFSET; + int iMapping = iStateOffset + iPVSOffset + iFacingOffset + range; + + Assert( iMapping < static_cast(ARRAYSIZE( mappings )) ); + + AI_Efficiency_t efficiency = mappings[iMapping]; + + //--------------------------------- + + AI_Efficiency_t maxEfficiency = AIE_SUPER_EFFICIENT; + if ( bInVisibilityPVS && state >= NPC_STATE_ALERT ) + { + maxEfficiency = AIE_EFFICIENT; + } + else if ( bInVisibilityPVS || HasCondition( COND_SEE_PLAYER ) ) + { + maxEfficiency = AIE_VERY_EFFICIENT; + } + + //--------------------------------- + + SetEfficiency( clamp( efficiency, minEfficiency, maxEfficiency ) ); +} + + +//----------------------------------------------------------------------------- + +void CAI_BaseNPC::UpdateSleepState( bool bInPVS ) +{ + if ( GetSleepState() > AISS_AWAKE ) + { + CBasePlayer *pLocalPlayer = AI_GetSinglePlayer(); + if ( !pLocalPlayer ) + { + if ( gpGlobals->maxClients > 1 ) + { + Wake(); + } + else + { + Warning( "CAI_BaseNPC::UpdateSleepState called with NULL pLocalPlayer\n" ); + } + return; + } + + if ( m_flWakeRadius > .1 && !(pLocalPlayer->GetFlags() & FL_NOTARGET) && ( pLocalPlayer->GetAbsOrigin() - GetAbsOrigin() ).LengthSqr() <= Square(m_flWakeRadius) ) + Wake(); + else if ( GetSleepState() == AISS_WAITING_FOR_PVS ) + { + if ( bInPVS ) + Wake(); + } + else if ( GetSleepState() == AISS_WAITING_FOR_THREAT ) + { + if ( HasCondition( COND_LIGHT_DAMAGE ) || HasCondition( COND_HEAVY_DAMAGE ) ) + Wake(); + else + { + if ( bInPVS ) + { + for (int i = 1; i <= gpGlobals->maxClients; i++ ) + { + CBasePlayer *pPlayer = UTIL_PlayerByIndex( i ); + if ( pPlayer && !(pPlayer->GetFlags() & FL_NOTARGET) && pPlayer->FVisible( this ) ) + Wake(); + } + } + + // Should check for visible danger sounds + if ( (GetSoundInterests() & SOUND_DANGER) && !(HasSpawnFlags(SF_NPC_WAIT_TILL_SEEN)) ) + { + int iSound = CSoundEnt::ActiveList(); + + while ( iSound != SOUNDLIST_EMPTY ) + { + CSound *pCurrentSound = CSoundEnt::SoundPointerForIndex( iSound ); + Assert( pCurrentSound ); + + if ( (pCurrentSound->SoundType() & SOUND_DANGER) && + GetSenses()->CanHearSound( pCurrentSound ) && + SoundIsVisible( pCurrentSound )) + { + Wake(); + break; + } + + iSound = pCurrentSound->NextSound(); + } + } + } + } + } + else + { + // NPC is awake + // Don't let an NPC sleep if they're running a script! + if( !IsInAScript() && m_NPCState != NPC_STATE_SCRIPT ) + { + if( HasSleepFlags(AI_SLEEP_FLAG_AUTO_PVS) ) + { + if( !HasCondition(COND_IN_PVS) ) + { + SetSleepState( AISS_WAITING_FOR_PVS ); + Sleep(); + } + } + if( HasSleepFlags(AI_SLEEP_FLAG_AUTO_PVS_AFTER_PVS) ) + { + if( HasCondition(COND_IN_PVS) ) + { + // OK, we're in the player's PVS. Now switch to regular old AUTO_PVS sleep rules. + AddSleepFlags(AI_SLEEP_FLAG_AUTO_PVS); + RemoveSleepFlags(AI_SLEEP_FLAG_AUTO_PVS_AFTER_PVS); + } + } + } + } +} + +//----------------------------------------------------------------------------- + +struct AIRebalanceInfo_t +{ + CAI_BaseNPC * pNPC; + int iNextThinkTick; + bool bInPVS; + float dotPlayer; + float distPlayer; +}; + +int __cdecl ThinkRebalanceCompare( const AIRebalanceInfo_t *pLeft, const AIRebalanceInfo_t *pRight ) +{ + int base = pLeft->iNextThinkTick - pRight->iNextThinkTick; + if ( base != 0 ) + return base; + + if ( !pLeft->bInPVS && !pRight->bInPVS ) + return 0; + + if ( !pLeft->bInPVS ) + return 1; + + if ( !pRight->bInPVS ) + return -1; + + if ( pLeft->dotPlayer < 0 && pRight->dotPlayer < 0 ) + return 0; + + if ( pLeft->dotPlayer < 0 ) + return 1; + + if ( pRight->dotPlayer < 0 ) + return -1; + + const float NEAR_PLAYER = 50*12; + + if ( pLeft->distPlayer < NEAR_PLAYER && pRight->distPlayer >= NEAR_PLAYER ) + return -1; + + if ( pRight->distPlayer < NEAR_PLAYER && pLeft->distPlayer >= NEAR_PLAYER ) + return 1; + + if ( pLeft->dotPlayer > pRight->dotPlayer ) + return -1; + + if ( pLeft->dotPlayer < pRight->dotPlayer ) + return 1; + + return 0; +} + +inline bool CAI_BaseNPC::CanThinkRebalance() +{ + if ( m_pfnThink != (BASEPTR)&CAI_BaseNPC::CallNPCThink ) + { + return false; + } + + if ( m_bInChoreo ) + { + return false; + } + + if ( m_NPCState == NPC_STATE_DEAD ) + { + return false; + } + + if ( GetSleepState() != AISS_AWAKE ) + { + return false; + } + + if ( !m_bUsingStandardThinkTime /*&& m_iFrameBlocked == -1 */ ) + { + return false; + } + + return true; +} + +void CAI_BaseNPC::RebalanceThinks() +{ + bool bDebugThinkTicks = ai_debug_think_ticks.GetBool(); + if ( bDebugThinkTicks ) + { + static int iPrevTick; + static int nThinksInTick; + static int nRebalanceableThinksInTick; + + if ( gpGlobals->tickcount != iPrevTick ) + { + DevMsg( "NPC per tick is %d [%d] (tick %d, frame %d)\n", nRebalanceableThinksInTick, nThinksInTick, iPrevTick, gpGlobals->framecount ); + iPrevTick = gpGlobals->tickcount; + nThinksInTick = 0; + nRebalanceableThinksInTick = 0; + } + nThinksInTick++; + if ( CanThinkRebalance() ) + nRebalanceableThinksInTick++; + } + + if ( ShouldRebalanceThinks() && gpGlobals->tickcount >= gm_iNextThinkRebalanceTick ) + { + AI_PROFILE_SCOPE(AI_Think_Rebalance ); + + static CUtlVector rebalanceCandidates( 16, 64 ); + gm_iNextThinkRebalanceTick = gpGlobals->tickcount + TIME_TO_TICKS( random->RandomFloat( 3, 5) ); + + int i; + + CBasePlayer *pPlayer = AI_GetSinglePlayer(); + Vector vPlayerForward; + Vector vPlayerEyePosition; + + vPlayerForward.Init(); + vPlayerEyePosition.Init(); + + if ( pPlayer ) + { + pPlayer->EyePositionAndVectors( &vPlayerEyePosition, &vPlayerForward, NULL, NULL ); + } + + int iTicksPer10Hz = TIME_TO_TICKS( .1 ); + int iMinTickRebalance = gpGlobals->tickcount - 1; // -1 needed for alternate ticks + int iMaxTickRebalance = gpGlobals->tickcount + iTicksPer10Hz; + + for ( i = 0; i < g_AI_Manager.NumAIs(); i++ ) + { + CAI_BaseNPC *pCandidate = g_AI_Manager.AccessAIs()[i]; + if ( pCandidate->CanThinkRebalance() && + ( pCandidate->GetNextThinkTick() >= iMinTickRebalance && + pCandidate->GetNextThinkTick() < iMaxTickRebalance ) ) + { + int iInfo = rebalanceCandidates.AddToTail(); + + rebalanceCandidates[iInfo].pNPC = pCandidate; + rebalanceCandidates[iInfo].iNextThinkTick = pCandidate->GetNextThinkTick(); + + if ( pCandidate->IsFlaggedEfficient() ) + { + rebalanceCandidates[iInfo].bInPVS = false; + } + else if ( pPlayer ) + { + Vector vToCandidate = pCandidate->EyePosition() - vPlayerEyePosition; + rebalanceCandidates[iInfo].bInPVS = ( UTIL_FindClientInPVS( pCandidate->edict() ) != NULL ); + rebalanceCandidates[iInfo].distPlayer = VectorNormalize( vToCandidate ); + rebalanceCandidates[iInfo].dotPlayer = vPlayerForward.Dot( vToCandidate ); + } + else + { + rebalanceCandidates[iInfo].bInPVS = true; + rebalanceCandidates[iInfo].dotPlayer = 1; + rebalanceCandidates[iInfo].distPlayer = 0; + } + } + else if ( bDebugThinkTicks ) + DevMsg( " Ignoring %d\n", pCandidate->GetNextThinkTick() ); + } + + if ( rebalanceCandidates.Count() ) + { + rebalanceCandidates.Sort( ThinkRebalanceCompare ); + + int iMaxThinkersPerTick = static_cast(ceil( (float)( rebalanceCandidates.Count() + 1 ) / (float)iTicksPer10Hz )); // +1 to account for "this" + + int iCurTickDistributing = min( gpGlobals->tickcount, rebalanceCandidates[0].iNextThinkTick ); + int iRemainingThinksToDistribute = iMaxThinkersPerTick - 1; // Start with one fewer first time because "this" is + // always gets a slot in the current tick to avoid complications + // in the calculation of "last think" + + if ( bDebugThinkTicks ) + { + DevMsg( "Rebalance %d!\n", rebalanceCandidates.Count() + 1 ); + DevMsg( " Distributing %d\n", iCurTickDistributing ); + } + + for ( i = 0; i < rebalanceCandidates.Count(); i++ ) + { + if ( iRemainingThinksToDistribute == 0 || rebalanceCandidates[i].iNextThinkTick > iCurTickDistributing ) + { + if ( rebalanceCandidates[i].iNextThinkTick <= iCurTickDistributing ) + { + iCurTickDistributing = iCurTickDistributing + 1; + } + else + { + iCurTickDistributing = rebalanceCandidates[i].iNextThinkTick; + } + + if ( bDebugThinkTicks ) + DevMsg( " Distributing %d\n", iCurTickDistributing ); + + iRemainingThinksToDistribute = iMaxThinkersPerTick; + } + + if ( rebalanceCandidates[i].pNPC->GetNextThinkTick() != iCurTickDistributing ) + { + if ( bDebugThinkTicks ) + DevMsg( " Bumping %d to %d\n", rebalanceCandidates[i].pNPC->GetNextThinkTick(), iCurTickDistributing ); + + rebalanceCandidates[i].pNPC->SetNextThink( TICKS_TO_TIME( iCurTickDistributing ) ); + } + else if ( bDebugThinkTicks ) + { + DevMsg( " Leaving %d\n", rebalanceCandidates[i].pNPC->GetNextThinkTick() ); + } + + iRemainingThinksToDistribute--; + } + } + + rebalanceCandidates.RemoveAll(); + + if ( bDebugThinkTicks ) + { + DevMsg( "New distribution is:\n"); + for ( i = 0; i < g_AI_Manager.NumAIs(); i++ ) + { + DevMsg( " %d\n", g_AI_Manager.AccessAIs()[i]->GetNextThinkTick() ); + } + } + + Assert( GetNextThinkTick() == TICK_NEVER_THINK ); // never change this objects tick + } +} + +static float g_NpcTimeThisFrame; +static float g_StartTimeCurThink; + +bool CAI_BaseNPC::PreNPCThink() +{ + static int iPrevFrame = -1; + static float frameTimeLimit = FLT_MAX; + static const ConVar *pHostTimescale; + + if ( frameTimeLimit == FLT_MAX ) + { + pHostTimescale = cvar->FindVar( "host_timescale" ); + } + + bool bUseThinkLimits = ( !m_bInChoreo && ShouldUseFrameThinkLimits() ); + +#ifdef _DEBUG + const float NPC_THINK_LIMIT = 30.0 / 1000.0; +#else + const float NPC_THINK_LIMIT = ( !IsXbox() ) ? (10.0 / 1000.0) : (12.5 / 1000.0); +#endif + + g_StartTimeCurThink = 0; + + if ( bUseThinkLimits && VCRGetMode() == VCR_Disabled ) + { + if ( m_iFrameBlocked == gpGlobals->framecount ) + { + DbgFrameLimitMsg( "Stalled %d (%d)\n", this, gpGlobals->framecount ); + SetNextThink( gpGlobals->curtime ); + return false; + } + else if ( gpGlobals->framecount != iPrevFrame ) + { + DbgFrameLimitMsg( "--- FRAME: %d (%d)\n", this, gpGlobals->framecount ); + float timescale = pHostTimescale->GetFloat(); + if ( timescale < 1 ) + timescale = 1; + + iPrevFrame = gpGlobals->framecount; + frameTimeLimit = NPC_THINK_LIMIT * timescale; + g_NpcTimeThisFrame = 0; + } + else + { + if ( g_NpcTimeThisFrame > NPC_THINK_LIMIT ) + { + float timeSinceLastRealThink = gpGlobals->curtime - m_flLastRealThinkTime; + // Don't bump anyone more that a quarter second + if ( timeSinceLastRealThink <= .25 ) + { + DbgFrameLimitMsg( "Bumped %d (%d)\n", this, gpGlobals->framecount ); + m_iFrameBlocked = gpGlobals->framecount; + SetNextThink( gpGlobals->curtime ); + return false; + } + else + { + DbgFrameLimitMsg( "(Over %d )\n", this ); + } + } + } + + DbgFrameLimitMsg( "Running %d (%d)\n", this, gpGlobals->framecount ); + g_StartTimeCurThink = engine->Time(); + + m_iFrameBlocked = -1; + m_nLastThinkTick = TIME_TO_TICKS( m_flLastRealThinkTime ); + } + + return true; +} + +void CAI_BaseNPC::PostNPCThink( void ) +{ + if ( g_StartTimeCurThink != 0.0 && VCRGetMode() == VCR_Disabled ) + { + g_NpcTimeThisFrame += engine->Time() - g_StartTimeCurThink; + } +} + +void CAI_BaseNPC::CallNPCThink( void ) +{ + RebalanceThinks(); + + //--------------------------------- + + m_bUsingStandardThinkTime = false; + + //--------------------------------- + + if ( !PreNPCThink() ) + { + return; + } + + // reduce cache queries by locking model in memory + MDLCACHE_CRITICAL_SECTION(); + + this->NPCThink(); + + m_flLastRealThinkTime = gpGlobals->curtime; + + PostNPCThink(); +} + +bool NPC_CheckBrushExclude( CBaseEntity *pEntity, CBaseEntity *pBrush ) +{ + CAI_BaseNPC *pNPC = pEntity->MyNPCPointer(); + + if ( pNPC ) + { + return pNPC->GetMoveProbe()->ShouldBrushBeIgnored( pBrush ); + } + + return false; +} + +class CTraceFilterPlayerAvoidance : public CTraceFilterEntitiesOnly +{ +public: + CTraceFilterPlayerAvoidance( const CBaseEntity *pEntity ) { m_pIgnore = pEntity; } + + virtual bool ShouldHitEntity( IHandleEntity *pHandleEntity, int contentsMask ) + { + CBaseEntity *pEntity = EntityFromEntityHandle( pHandleEntity ); + + if ( m_pIgnore == pEntity ) + return false; + + if ( pEntity->IsPlayer() ) + return true; + + return false; + } +private: + + const CBaseEntity *m_pIgnore; +}; + +void CAI_BaseNPC::GetPlayerAvoidBounds( Vector *pMins, Vector *pMaxs ) +{ + *pMins = WorldAlignMins(); + *pMaxs = WorldAlignMaxs(); +} + +ConVar ai_debug_avoidancebounds( "ai_debug_avoidancebounds", "0" ); + +void CAI_BaseNPC::SetPlayerAvoidState( void ) +{ + bool bShouldPlayerAvoid = false; + Vector vNothing; + + GetSequenceLinearMotion( GetSequence(), &vNothing ); + bool bIsMoving = ( IsMoving() || ( vNothing != vec3_origin ) ); + + //If we are coming out of a script, check if we are stuck inside the player. + if ( m_bPerformAvoidance || ( ShouldPlayerAvoid() && bIsMoving ) ) + { + trace_t trace; + Vector vMins, vMaxs; + + GetPlayerAvoidBounds( &vMins, &vMaxs ); + + CBasePlayer *pLocalPlayer = AI_GetSinglePlayer(); + + if ( pLocalPlayer ) + { + bShouldPlayerAvoid = IsBoxIntersectingBox( GetAbsOrigin() + vMins, GetAbsOrigin() + vMaxs, + pLocalPlayer->GetAbsOrigin() + pLocalPlayer->WorldAlignMins(), pLocalPlayer->GetAbsOrigin() + pLocalPlayer->WorldAlignMaxs() ); + } + + if ( ai_debug_avoidancebounds.GetBool() ) + { + int iRed = ( bShouldPlayerAvoid == true ) ? 255 : 0; + + NDebugOverlay::Box( GetAbsOrigin(), vMins, vMaxs, iRed, 0, 255, 64, 0.1 ); + } + } + + m_bPlayerAvoidState = ShouldPlayerAvoid(); + m_bPerformAvoidance = bShouldPlayerAvoid; + + if ( GetCollisionGroup() == COLLISION_GROUP_NPC || GetCollisionGroup() == COLLISION_GROUP_NPC_ACTOR ) + { + if ( m_bPerformAvoidance == true ) + { + SetCollisionGroup( COLLISION_GROUP_NPC_ACTOR ); + } + else + { + SetCollisionGroup( COLLISION_GROUP_NPC ); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Enables player avoidance when the player's vphysics shadow penetrates our vphysics shadow. This can +// happen when the player is hit by a combine ball, which pushes them into an adjacent npc. Subclasses should +// override this if it causes problems, but in general this will solve cases of the player getting stuck in +// the NPC from being pushed. +//----------------------------------------------------------------------------- +void CAI_BaseNPC::PlayerPenetratingVPhysics( void ) +{ + m_bPerformAvoidance = true; +} + +//----------------------------------------------------------------------------- + +bool CAI_BaseNPC::CheckPVSCondition() +{ + bool bInPVS = ( UTIL_FindClientInPVS( edict() ) != NULL ) || (UTIL_ClientPVSIsExpanded() && UTIL_FindClientInVisibilityPVS( edict() )); + + if ( bInPVS ) + SetCondition( COND_IN_PVS ); + else + ClearCondition( COND_IN_PVS ); + + return bInPVS; +} + + +//----------------------------------------------------------------------------- +// NPC Think - calls out to core AI functions and handles this +// npc's specific animation events +// + +void CAI_BaseNPC::NPCThink( void ) +{ + if ( m_bCheckContacts ) + { + CheckPhysicsContacts(); + } + + Assert( !(m_NPCState == NPC_STATE_DEAD && m_lifeState == LIFE_ALIVE) ); + + //--------------------------------- + + SetNextThink( TICK_NEVER_THINK ); + + //--------------------------------- + + bool bInPVS = CheckPVSCondition(); + + //--------------------------------- + + UpdateSleepState( bInPVS ); + + //--------------------------------- + bool bRanDecision = false; + + if ( GetEfficiency() < AIE_DORMANT && GetSleepState() == AISS_AWAKE ) + { + static CFastTimer timer; + float thinkLimit = ai_show_think_tolerance.GetFloat(); + + if ( thinkLimit > 0 ) + timer.Start(); + + if ( g_pAINetworkManager && g_pAINetworkManager->IsInitialized() ) + { + VPROF_BUDGET( "NPCs", VPROF_BUDGETGROUP_NPCS ); + + AI_PROFILE_SCOPE_BEGIN_( GetClassScheduleIdSpace()->GetClassName() ); // need to use a string stable from map load to map load + + SetPlayerAvoidState(); + + if ( PreThink() ) + { + if ( m_flNextDecisionTime <= gpGlobals->curtime ) + { + bRanDecision = true; + m_ScheduleState.bTaskRanAutomovement = false; + m_ScheduleState.bTaskUpdatedYaw = false; + RunAI(); + } + else + { + if ( m_ScheduleState.bTaskRanAutomovement ) + AutoMovement(); + if ( m_ScheduleState.bTaskUpdatedYaw ) + GetMotor()->UpdateYaw(); + } + + PostRun(); + + PerformMovement(); + + m_bIsMoving = IsMoving(); + + PostMovement(); + + SetSimulationTime( gpGlobals->curtime ); + } + else + m_flTimeLastMovement = FLT_MAX; + + AI_PROFILE_SCOPE_END(); + } + + if ( thinkLimit > 0 ) + { + timer.End(); + + float thinkTime = g_AIRunTimer.GetDuration().GetMillisecondsF(); + + if ( thinkTime > thinkLimit ) + { + int color = (int)RemapVal( thinkTime, thinkLimit, thinkLimit * 3, 96.0, 255.0 ); + if ( color > 255 ) + color = 255; + else if ( color < 96 ) + color = 96; + + Vector right; + Vector vecPoint; + + vecPoint = EyePosition() + Vector( 0, 0, 12 ); + GetVectors( NULL, &right, NULL ); + NDebugOverlay::Line( vecPoint, vecPoint + Vector( 0, 0, 64 ), color, 0, 0, false , 1.0 ); + NDebugOverlay::Line( vecPoint, vecPoint + Vector( 0, 0, 16 ) + right * 16, color, 0, 0, false , 1.0 ); + NDebugOverlay::Line( vecPoint, vecPoint + Vector( 0, 0, 16 ) - right * 16, color, 0, 0, false , 1.0 ); + } + } + } + + m_bUsingStandardThinkTime = ( GetNextThinkTick() == TICK_NEVER_THINK ); + + UpdateEfficiency( bInPVS ); + + if ( m_bUsingStandardThinkTime ) + { + static const char *ppszEfficiencies[] = + { + "AIE_NORMAL", + "AIE_EFFICIENT", + "AIE_VERY_EFFICIENT", + "AIE_SUPER_EFFICIENT", + "AIE_DORMANT", + }; + + static const char *ppszMoveEfficiencies[] = + { + "AIME_NORMAL", + "AIME_EFFICIENT", + }; + + if ( ai_debug_efficiency.GetBool() ) + DevMsg( this, "Eff: %s, Move: %s\n", ppszEfficiencies[GetEfficiency()], ppszMoveEfficiencies[GetMoveEfficiency()] ); + + static float g_DecisionIntervals[] = + { + .1, // AIE_NORMAL + .2, // AIE_EFFICIENT + .4, // AIE_VERY_EFFICIENT + .6, // AIE_SUPER_EFFICIENT + }; + + if ( bRanDecision ) + { + m_flNextDecisionTime = gpGlobals->curtime + g_DecisionIntervals[GetEfficiency()]; + } + + if ( GetMoveEfficiency() == AIME_NORMAL || GetEfficiency() == AIE_NORMAL ) + { + SetNextThink( gpGlobals->curtime + .1 ); + } + else + { + SetNextThink( gpGlobals->curtime + .2 ); + } + } + else + { + m_flNextDecisionTime = 0; + } +} + +//========================================================= +// CAI_BaseNPC - USE - will make a npc angry at whomever +// activated it. +//========================================================= +void CAI_BaseNPC::NPCUse ( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) +{ + return; + + // Can't +USE NPCs running scripts + if ( GetState() == NPC_STATE_SCRIPT ) + return; + + if ( IsInAScript() ) + return; + + SetIdealState( NPC_STATE_ALERT ); +} + +//----------------------------------------------------------------------------- +// Purpose: Virtual function that allows us to have any npc ignore a set of +// shared conditions. +// +//----------------------------------------------------------------------------- +void CAI_BaseNPC::RemoveIgnoredConditions ( void ) +{ + m_ConditionsPreIgnore = m_Conditions; + m_Conditions.And( m_InverseIgnoreConditions, &m_Conditions ); + + if ( m_NPCState == NPC_STATE_SCRIPT && m_hCine ) + m_hCine->RemoveIgnoredConditions(); +} + +//========================================================= +// RangeAttack1Conditions +//========================================================= +int CAI_BaseNPC::RangeAttack1Conditions ( float flDot, float flDist ) +{ + if ( flDist < 64) + { + return COND_TOO_CLOSE_TO_ATTACK; + } + else if (flDist > 784) + { + return COND_TOO_FAR_TO_ATTACK; + } + else if (flDot < 0.5) + { + return COND_NOT_FACING_ATTACK; + } + + return COND_CAN_RANGE_ATTACK1; +} + +//========================================================= +// RangeAttack2Conditions +//========================================================= +int CAI_BaseNPC::RangeAttack2Conditions ( float flDot, float flDist ) +{ + if ( flDist < 64) + { + return COND_TOO_CLOSE_TO_ATTACK; + } + else if (flDist > 512) + { + return COND_TOO_FAR_TO_ATTACK; + } + else if (flDot < 0.5) + { + return COND_NOT_FACING_ATTACK; + } + + return COND_CAN_RANGE_ATTACK2; +} + +//========================================================= +// MeleeAttack1Conditions +//========================================================= +int CAI_BaseNPC::MeleeAttack1Conditions ( float flDot, float flDist ) +{ + if ( flDist > 64) + { + return COND_TOO_FAR_TO_ATTACK; + } + else if (flDot < 0.7) + { + return 0; + } + else if (GetEnemy() == NULL) + { + return 0; + } + + // Decent fix to keep folks from kicking/punching hornets and snarks is to check the onground flag(sjb) + if ( GetEnemy()->GetFlags() & FL_ONGROUND ) + { + return COND_CAN_MELEE_ATTACK1; + } + return 0; +} + +//========================================================= +// MeleeAttack2Conditions +//========================================================= +int CAI_BaseNPC::MeleeAttack2Conditions ( float flDot, float flDist ) +{ + if ( flDist > 64) + { + return COND_TOO_FAR_TO_ATTACK; + } + else if (flDot < 0.7) + { + return 0; + } + return COND_CAN_MELEE_ATTACK2; +} + +// Get capability mask +int CAI_BaseNPC::CapabilitiesGet( void ) const +{ + int capability = m_afCapability; + if ( GetActiveWeapon() ) + { + capability |= GetActiveWeapon()->CapabilitiesGet(); + } + return capability; +} + +// Set capability mask +int CAI_BaseNPC::CapabilitiesAdd( int capability ) +{ + m_afCapability |= capability; + + return m_afCapability; +} + +// Set capability mask +int CAI_BaseNPC::CapabilitiesRemove( int capability ) +{ + m_afCapability &= ~capability; + + return m_afCapability; +} + +// Clear capability mask +void CAI_BaseNPC::CapabilitiesClear( void ) +{ + m_afCapability = 0; +} + + +//========================================================= +// ClearAttacks - clear out all attack conditions +//========================================================= +void CAI_BaseNPC::ClearAttackConditions( ) +{ + // Clear all attack conditions + ClearCondition( COND_CAN_RANGE_ATTACK1 ); + ClearCondition( COND_CAN_RANGE_ATTACK2 ); + ClearCondition( COND_CAN_MELEE_ATTACK1 ); + ClearCondition( COND_CAN_MELEE_ATTACK2 ); + ClearCondition( COND_WEAPON_HAS_LOS ); + ClearCondition( COND_WEAPON_BLOCKED_BY_FRIEND ); + ClearCondition( COND_WEAPON_PLAYER_IN_SPREAD ); // Player in shooting direction + ClearCondition( COND_WEAPON_PLAYER_NEAR_TARGET ); // Player near shooting position + ClearCondition( COND_WEAPON_SIGHT_OCCLUDED ); +} + +//========================================================= +// GatherAttackConditions - sets all of the bits for attacks that the +// npc is capable of carrying out on the passed entity. +//========================================================= + +void CAI_BaseNPC::GatherAttackConditions( CBaseEntity *pTarget, float flDist ) +{ + AI_PROFILE_SCOPE(CAI_BaseNPC_GatherAttackConditions); + + Vector vecLOS = ( pTarget->GetAbsOrigin() - GetAbsOrigin() ); + vecLOS.z = 0; + VectorNormalize( vecLOS ); + + Vector vBodyDir = BodyDirection2D( ); + float flDot = DotProduct( vecLOS, vBodyDir ); + + // we know the enemy is in front now. We'll find which attacks the npc is capable of by + // checking for corresponding Activities in the model file, then do the simple checks to validate + // those attack types. + + int capability; + Vector targetPos; + bool bWeaponHasLOS; + int condition; + + capability = CapabilitiesGet(); + + // Clear all attack conditions + AI_PROFILE_SCOPE_BEGIN( CAI_BaseNPC_GatherAttackConditions_PrimaryWeaponLOS ); + + // @TODO (toml 06-15-03): There are simple cases where + // the upper torso of the enemy is visible, and the NPC is at an angle below + // them, but the above test fails because BodyTarget returns the center of + // the target. This needs some better handling/closer evaluation + + // Try the eyes first, as likely to succeed (because can see or else wouldn't be here) thus reducing + // the odds of the need for a second trace + ClearAttackConditions(); + targetPos = pTarget->EyePosition(); + bWeaponHasLOS = WeaponLOSCondition( GetAbsOrigin(), targetPos, true ); + + AI_PROFILE_SCOPE_END(); + + if ( !bWeaponHasLOS ) + { + AI_PROFILE_SCOPE( CAI_BaseNPC_GatherAttackConditions_SecondaryWeaponLOS ); + ClearAttackConditions( ); + targetPos = pTarget->BodyTarget( GetAbsOrigin() ); + bWeaponHasLOS = WeaponLOSCondition( GetAbsOrigin(), targetPos, true ); + } + else + { + SetCondition( COND_WEAPON_HAS_LOS ); + } + + bool bWeaponIsReady = (GetActiveWeapon() && !IsWeaponStateChanging()); + + // FIXME: move this out of here + if ( (capability & bits_CAP_WEAPON_RANGE_ATTACK1) && bWeaponIsReady ) + { + AI_PROFILE_SCOPE( CAI_BaseNPC_GatherAttackConditions_WeaponRangeAttack1Condition ); + + condition = GetActiveWeapon()->WeaponRangeAttack1Condition(flDot, flDist); + + if ( condition == COND_NOT_FACING_ATTACK && FInAimCone( targetPos ) ) + DevMsg( "Warning: COND_NOT_FACING_ATTACK set but FInAimCone is true\n" ); + + if (condition != COND_CAN_RANGE_ATTACK1 || bWeaponHasLOS) + { + SetCondition(condition); + } + } + else if ( capability & bits_CAP_INNATE_RANGE_ATTACK1 ) + { + AI_PROFILE_SCOPE( CAI_BaseNPC_GatherAttackConditions_RangeAttack1Condition ); + + condition = RangeAttack1Conditions( flDot, flDist ); + if (condition != COND_CAN_RANGE_ATTACK1 || bWeaponHasLOS) + { + SetCondition(condition); + } + } + + if ( (capability & bits_CAP_WEAPON_RANGE_ATTACK2) && bWeaponIsReady && ( GetActiveWeapon()->CapabilitiesGet() & bits_CAP_WEAPON_RANGE_ATTACK2 ) ) + { + AI_PROFILE_SCOPE( CAI_BaseNPC_GatherAttackConditions_WeaponRangeAttack2Condition ); + + condition = GetActiveWeapon()->WeaponRangeAttack2Condition(flDot, flDist); + if (condition != COND_CAN_RANGE_ATTACK2 || bWeaponHasLOS) + { + SetCondition(condition); + } + } + else if ( capability & bits_CAP_INNATE_RANGE_ATTACK2 ) + { + AI_PROFILE_SCOPE( CAI_BaseNPC_GatherAttackConditions_RangeAttack2Condition ); + + condition = RangeAttack2Conditions( flDot, flDist ); + if (condition != COND_CAN_RANGE_ATTACK2 || bWeaponHasLOS) + { + SetCondition(condition); + } + } + + if ( (capability & bits_CAP_WEAPON_MELEE_ATTACK1) && bWeaponIsReady) + { + AI_PROFILE_SCOPE( CAI_BaseNPC_GatherAttackConditions_WeaponMeleeAttack1Condition ); + SetCondition(GetActiveWeapon()->WeaponMeleeAttack1Condition(flDot, flDist)); + } + else if ( capability & bits_CAP_INNATE_MELEE_ATTACK1 ) + { + AI_PROFILE_SCOPE( CAI_BaseNPC_GatherAttackConditions_MeleeAttack1Condition ); + SetCondition(MeleeAttack1Conditions ( flDot, flDist )); + } + + if ( (capability & bits_CAP_WEAPON_MELEE_ATTACK2) && bWeaponIsReady) + { + AI_PROFILE_SCOPE( CAI_BaseNPC_GatherAttackConditions_WeaponMeleeAttack2Condition ); + SetCondition(GetActiveWeapon()->WeaponMeleeAttack2Condition(flDot, flDist)); + } + else if ( capability & bits_CAP_INNATE_MELEE_ATTACK2 ) + { + AI_PROFILE_SCOPE( CAI_BaseNPC_GatherAttackConditions_MeleeAttack2Condition ); + SetCondition(MeleeAttack2Conditions ( flDot, flDist )); + } + + // ----------------------------------------------------------------- + // If any attacks are possible clear attack specific bits + // ----------------------------------------------------------------- + if (HasCondition(COND_CAN_RANGE_ATTACK2) || + HasCondition(COND_CAN_RANGE_ATTACK1) || + HasCondition(COND_CAN_MELEE_ATTACK2) || + HasCondition(COND_CAN_MELEE_ATTACK1) ) + { + ClearCondition(COND_TOO_CLOSE_TO_ATTACK); + ClearCondition(COND_TOO_FAR_TO_ATTACK); + ClearCondition(COND_WEAPON_BLOCKED_BY_FRIEND); + } +} + + +//========================================================= +// SetState +//========================================================= +void CAI_BaseNPC::SetState( NPC_STATE State ) +{ + NPC_STATE OldState; + + OldState = m_NPCState; + + if ( State != m_NPCState ) + { + m_flLastStateChangeTime = gpGlobals->curtime; + } + + switch( State ) + { + // Drop enemy pointers when going to idle + case NPC_STATE_IDLE: + + if ( GetEnemy() != NULL ) + { + SetEnemy( NULL ); // not allowed to have an enemy anymore. + DevMsg( 2, "Stripped\n" ); + } + break; + + default: + break; + } + + bool fNotifyChange = false; + + if( m_NPCState != State ) + { + // Don't notify if we're changing to a state we're already in! + fNotifyChange = true; + } + + m_NPCState = State; + SetIdealState( State ); + + // Notify the character that its state has changed. + if( fNotifyChange ) + { + OnStateChange( OldState, m_NPCState ); + } +} + +bool CAI_BaseNPC::WokeThisTick() const +{ + return m_nWakeTick == gpGlobals->tickcount ? true : false; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CAI_BaseNPC::Wake( bool bFireOutput ) +{ + if ( GetSleepState() != AISS_AWAKE ) + { + m_nWakeTick = gpGlobals->tickcount; + SetSleepState( AISS_AWAKE ); + RemoveEffects( EF_NODRAW ); + if ( bFireOutput ) + m_OnWake.FireOutput( this, this ); + + if ( m_bWakeSquad && GetSquad() ) + { + AISquadIter_t iter; + for ( CAI_BaseNPC *pSquadMember = GetSquad()->GetFirstMember( &iter ); pSquadMember; pSquadMember = GetSquad()->GetNextMember( &iter ) ) + { + if ( pSquadMember->IsAlive() && pSquadMember != this ) + { + pSquadMember->m_bWakeSquad = false; + pSquadMember->Wake(); + } + } + + } + } +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CAI_BaseNPC::Sleep() +{ + // Don't render. + AddEffects( EF_NODRAW ); + + if( GetState() == NPC_STATE_SCRIPT ) + { + Warning( "%s put to sleep while in Scripted state!\n"); + } + + VacateStrategySlot(); + + // Slam my schedule. + SetSchedule( SCHED_SLEEP ); + + m_OnSleep.FireOutput( this, this ); +} + +//----------------------------------------------------------------------------- +// Sets all sensing-related conditions +//----------------------------------------------------------------------------- +void CAI_BaseNPC::PerformSensing( void ) +{ + GetSenses()->PerformSensing(); +} + + +//----------------------------------------------------------------------------- + +void CAI_BaseNPC::ClearSenseConditions( void ) +{ + static int conditionsToClear[] = + { + COND_SEE_HATE, + COND_SEE_DISLIKE, + COND_SEE_ENEMY, + COND_SEE_FEAR, + COND_SEE_NEMESIS, + COND_SEE_PLAYER, + COND_HEAR_DANGER, + COND_HEAR_COMBAT, + COND_HEAR_WORLD, + COND_HEAR_PLAYER, + COND_HEAR_THUMPER, + COND_HEAR_BUGBAIT, + COND_HEAR_PHYSICS_DANGER, + COND_HEAR_MOVE_AWAY, + COND_SMELL, + }; + + ClearConditions( conditionsToClear, ARRAYSIZE( conditionsToClear ) ); +} + +//----------------------------------------------------------------------------- + +void CAI_BaseNPC::CheckOnGround( void ) +{ + bool bScriptedWait = ( IsCurSchedule( SCHED_WAIT_FOR_SCRIPT ) || ( m_hCine && m_scriptState == CAI_BaseNPC::SCRIPT_WAIT ) ); + if ( !bScriptedWait && !HasCondition( COND_FLOATING_OFF_GROUND ) ) + { + // parented objects are never floating + if (GetMoveParent() != NULL) + return; + + if ( ( GetNavType() == NAV_GROUND ) && ( GetMoveType() != MOVETYPE_VPHYSICS ) && ( GetMoveType() != MOVETYPE_NONE ) ) + { + if ( m_CheckOnGroundTimer.Expired() ) + { + m_CheckOnGroundTimer.Set(0.5); + + // check a shrunk box centered around the foot + Vector maxs = WorldAlignMaxs(); + Vector mins = WorldAlignMins(); + + if ( mins != maxs ) // some NPCs have no hull, so mins == maxs == vec3_origin + { + maxs -= Vector( 0.0f, 0.0f, 0.2f ); + + Vector vecStart = GetAbsOrigin() + Vector( 0, 0, .1f ); + Vector vecDown = GetAbsOrigin(); + vecDown.z -= 4.0; + + trace_t trace; + m_pMoveProbe->TraceHull( vecStart, vecDown, mins, maxs, MASK_NPCSOLID, &trace ); + + if (trace.fraction == 1.0) + { + SetCondition( COND_FLOATING_OFF_GROUND ); + SetGroundEntity( NULL ); + } + else + { + if ( trace.startsolid && trace.m_pEnt->GetMoveType() == MOVETYPE_VPHYSICS && + trace.m_pEnt->VPhysicsGetObject() && trace.m_pEnt->VPhysicsGetObject()->GetMass() < VPHYSICS_LARGE_OBJECT_MASS ) + { + // stuck inside a small physics object? + m_CheckOnGroundTimer.Set(0.1f); + NPCPhysics_CreateSolver( this, trace.m_pEnt, true, 0.25f ); + if ( VPhysicsGetObject() ) + { + VPhysicsGetObject()->RecheckContactPoints(); + } + } + // Check to see if someone changed the ground on us... + if ( trace.m_pEnt && trace.m_pEnt != GetGroundEntity() ) + { + SetGroundEntity( trace.m_pEnt ); + } + } + } + } + } + } + else + { + // parented objects are never floating + if ( bScriptedWait || GetMoveParent() != NULL || (GetFlags() & FL_ONGROUND ) || GetNavType() != NAV_GROUND ) + { + ClearCondition( COND_FLOATING_OFF_GROUND ); + } + } + +} + +void CAI_BaseNPC::NotifyPushMove() +{ + // don't recheck ground when I'm being push-moved + m_CheckOnGroundTimer.Set( 0.5f ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CAI_BaseNPC::CanFlinch( void ) +{ + if ( IsCurSchedule( SCHED_BIG_FLINCH ) ) + return false; + + if ( m_flNextFlinchTime >= gpGlobals->curtime ) + return false; + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CAI_BaseNPC::CheckFlinches( void ) +{ + // If we're currently flinching, don't allow gesture flinches to be overlaid + if ( IsCurSchedule( SCHED_BIG_FLINCH ) ) + { + ClearCondition( COND_LIGHT_DAMAGE ); + ClearCondition( COND_HEAVY_DAMAGE ); + } + + // If we've taken heavy damage, try to do a full schedule flinch + if ( HasCondition(COND_HEAVY_DAMAGE) ) + { + // If we've already flinched recently, gesture flinch instead. + if ( HasMemory(bits_MEMORY_FLINCHED) ) + { + // Clear the heavy damage condition so we don't interrupt schedules + // when we play a gesture flinch because we recently did a full flinch. + // Prevents the player from stun-locking enemies, even though they don't full flinch. + ClearCondition( COND_HEAVY_DAMAGE ); + } + else if ( !HasInterruptCondition(COND_HEAVY_DAMAGE) ) + { + // If we have taken heavy damage, but the current schedule doesn't + // break on that, resort to just playing a gesture flinch. + PlayFlinchGesture(); + } + + // Otherwise, do nothing. The heavy damage will interrupt our schedule and we'll flinch. + } + else if ( HasCondition( COND_LIGHT_DAMAGE ) ) + { + // If we have taken light damage play gesture flinches + PlayFlinchGesture(); + } + + // If it's been a while since we did a full flinch, forget that we flinched so we'll flinch fully again + if ( HasMemory(bits_MEMORY_FLINCHED) && gpGlobals->curtime > m_flNextFlinchTime ) + { + Forget(bits_MEMORY_FLINCHED); + } +} + +//----------------------------------------------------------------------------- + +void CAI_BaseNPC::GatherConditions( void ) +{ + m_bConditionsGathered = true; + g_AIConditionsTimer.Start(); + + if ( m_NPCState != NPC_STATE_NONE && m_NPCState != NPC_STATE_DEAD ) + { + if ( FacingIdeal() ) + Forget( bits_MEMORY_TURNING ); + + bool bForcedGather = m_bForceConditionsGather; + m_bForceConditionsGather = false; + + if ( m_pfnThink != (BASEPTR)&CAI_BaseNPC::CallNPCThink ) + { + if ( UTIL_FindClientInPVS( edict() ) != NULL ) + SetCondition( COND_IN_PVS ); + else + ClearCondition( COND_IN_PVS ); + } + + // Sample the environment. Do this unconditionally if there is a player in this + // npc's PVS. NPCs in COMBAT state are allowed to simulate when there is no player in + // the same PVS. This is so that any fights in progress will continue even if the player leaves the PVS. + if ( !IsFlaggedEfficient() && + ( bForcedGather || + HasCondition( COND_IN_PVS ) || + ShouldAlwaysThink() || + m_NPCState == NPC_STATE_COMBAT ) ) + { + CheckOnGround(); + + if ( ShouldPlayIdleSound() ) + { + AI_PROFILE_SCOPE(CAI_BaseNPC_IdleSound); + IdleSound(); + } + + PerformSensing(); + + GetEnemies()->RefreshMemories(); + ChooseEnemy(); + + // Check to see if there is a better weapon available + if (Weapon_IsBetterAvailable()) + { + SetCondition(COND_BETTER_WEAPON_AVAILABLE); + } + + if ( GetCurSchedule() && + ( m_NPCState == NPC_STATE_IDLE || m_NPCState == NPC_STATE_ALERT) && + GetEnemy() && + !HasCondition( COND_NEW_ENEMY ) && + GetCurSchedule()->HasInterrupt( COND_NEW_ENEMY ) ) + { + // @Note (toml 05-05-04): There seems to be a case where an NPC can not respond + // to COND_NEW_ENEMY. Only evidence right now is save + // games after the fact, so for now, just patching it up + DevMsg( 2, "Had to force COND_NEW_ENEMY\n" ); + SetCondition(COND_NEW_ENEMY); + } + } + else + { + // if not done, can have problems if leave PVS in same frame heard/saw things, + // since only PerformSensing clears conditions + ClearSenseConditions(); + } + + // do these calculations if npc has an enemy. + if ( GetEnemy() != NULL ) + { + if ( !IsFlaggedEfficient() ) + { + GatherEnemyConditions( GetEnemy() ); + m_flLastEnemyTime = gpGlobals->curtime; + } + else + { + SetEnemy( NULL ); + } + } + + // do these calculations if npc has a target + if ( GetTarget() != NULL ) + { + CheckTarget( GetTarget() ); + } + + CheckAmmo(); + + CheckFlinches(); + + CheckSquad(); + } + else + ClearCondition( COND_IN_PVS ); + + RemoveIgnoredConditions(); + + g_AIConditionsTimer.End(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CAI_BaseNPC::PrescheduleThink( void ) +{ +#ifdef HL2_EPISODIC + CheckForScriptedNPCInteractions(); +#endif + + // If we use weapons, and our desired weapon state is not the current, fix it + if( (CapabilitiesGet() & bits_CAP_USE_WEAPONS) && (m_iDesiredWeaponState == DESIREDWEAPONSTATE_HOLSTERED || m_iDesiredWeaponState == DESIREDWEAPONSTATE_UNHOLSTERED || m_iDesiredWeaponState == DESIREDWEAPONSTATE_HOLSTERED_DESTROYED ) ) + { + if ( IsAlive() && !IsInAScript() ) + { + if ( !IsCurSchedule( SCHED_MELEE_ATTACK1, false ) && !IsCurSchedule( SCHED_MELEE_ATTACK2, false ) && + !IsCurSchedule( SCHED_RANGE_ATTACK1, false ) && !IsCurSchedule( SCHED_RANGE_ATTACK2, false ) ) + { + if ( m_iDesiredWeaponState == DESIREDWEAPONSTATE_HOLSTERED || m_iDesiredWeaponState == DESIREDWEAPONSTATE_HOLSTERED_DESTROYED ) + { + HolsterWeapon(); + } + else if ( m_iDesiredWeaponState == DESIREDWEAPONSTATE_UNHOLSTERED ) + { + UnholsterWeapon(); + } + } + } + else + { + // Throw away the request + m_iDesiredWeaponState = DESIREDWEAPONSTATE_IGNORE; + } + } +} + +//----------------------------------------------------------------------------- +// Main entry point for processing AI +//----------------------------------------------------------------------------- + +void CAI_BaseNPC::RunAI( void ) +{ + AI_PROFILE_SCOPE(CAI_BaseNPC_RunAI); + g_AIRunTimer.Start(); + + if( ai_debug_squads.GetBool() ) + { + if( IsInSquad() && GetSquad() && !CAI_Squad::IsSilentMember(this ) && ( GetSquad()->IsLeader( this ) || GetSquad()->NumMembers() == 1 ) ) + { + AISquadIter_t iter; + CAI_Squad *pSquad = GetSquad(); + + Vector right; + Vector vecPoint; + + vecPoint = EyePosition() + Vector( 0, 0, 12 ); + GetVectors( NULL, &right, NULL ); + NDebugOverlay::Line( vecPoint, vecPoint + Vector( 0, 0, 64 ), 0, 255, 0, false , 0.1 ); + NDebugOverlay::Line( vecPoint, vecPoint + Vector( 0, 0, 32 ) + right * 32, 0, 255, 0, false , 0.1 ); + NDebugOverlay::Line( vecPoint, vecPoint + Vector( 0, 0, 32 ) - right * 32, 0, 255, 0, false , 0.1 ); + + for ( CAI_BaseNPC *pSquadMember = pSquad->GetFirstMember( &iter, false ); pSquadMember; pSquadMember = pSquad->GetNextMember( &iter, false ) ) + { + if ( pSquadMember != this ) + NDebugOverlay::Line( EyePosition(), pSquadMember->EyePosition(), 0, + CAI_Squad::IsSilentMember(pSquadMember) ? 127 : 255, 0, false , 0.1 ); + } + } + } + + if( ai_debug_loners.GetBool() && !IsInSquad() && AI_IsSinglePlayer() ) + { + Vector right; + Vector vecPoint; + + vecPoint = EyePosition() + Vector( 0, 0, 12 ); + + UTIL_GetLocalPlayer()->GetVectors( NULL, &right, NULL ); + + NDebugOverlay::Line( vecPoint, vecPoint + Vector( 0, 0, 64 ), 255, 0, 0, false , 0.1 ); + NDebugOverlay::Line( vecPoint, vecPoint + Vector( 0, 0, 32 ) + right * 32, 255, 0, 0, false , 0.1 ); + NDebugOverlay::Line( vecPoint, vecPoint + Vector( 0, 0, 32 ) - right * 32, 255, 0, 0, false , 0.1 ); + } + +#ifdef _DEBUG + m_bSelected = ( (m_debugOverlays & OVERLAY_NPC_SELECTED_BIT) != 0 ); +#endif + + m_bConditionsGathered = false; + m_bSkippedChooseEnemy = false; + + if ( g_pDeveloper->GetInt() && !GetNavigator()->IsOnNetwork() ) + { + AddTimedOverlay( "NPC w/no reachable nodes!", 5 ); + } + + AI_PROFILE_SCOPE_BEGIN(CAI_BaseNPC_RunAI_GatherConditions); + GatherConditions(); + AI_PROFILE_SCOPE_END(); + + if ( !m_bConditionsGathered ) + m_bConditionsGathered = true; // derived class didn't call to base + + TryRestoreHull(); + + g_AIPrescheduleThinkTimer.Start(); + + AI_PROFILE_SCOPE_BEGIN(CAI_RunAI_PrescheduleThink); + PrescheduleThink(); + AI_PROFILE_SCOPE_END(); + + g_AIPrescheduleThinkTimer.End(); + + MaintainSchedule(); + + PostscheduleThink(); + + ClearTransientConditions(); + + g_AIRunTimer.End(); +} + +//----------------------------------------------------------------------------- +void CAI_BaseNPC::ClearTransientConditions() +{ + // if the npc didn't use these conditions during the above call to MaintainSchedule() + // we throw them out cause we don't want them sitting around through the lifespan of a schedule + // that doesn't use them. + ClearCondition( COND_LIGHT_DAMAGE ); + ClearCondition( COND_HEAVY_DAMAGE ); + ClearCondition( COND_PHYSICS_DAMAGE ); + ClearCondition( COND_PLAYER_PUSHING ); +} + + +//----------------------------------------------------------------------------- +// Selecting the idle ideal state +//----------------------------------------------------------------------------- +NPC_STATE CAI_BaseNPC::SelectIdleIdealState() +{ + // IDLE goes to ALERT upon hearing a sound + // IDLE goes to ALERT upon being injured + // IDLE goes to ALERT upon seeing food + // IDLE goes to COMBAT upon sighting an enemy + if ( HasCondition(COND_NEW_ENEMY) || + HasCondition(COND_SEE_ENEMY) ) + { + // new enemy! This means an idle npc has seen someone it dislikes, or + // that a npc in combat has found a more suitable target to attack + return NPC_STATE_COMBAT; + } + + // Set our ideal yaw if we've taken damage + if ( HasCondition(COND_LIGHT_DAMAGE) || + HasCondition(COND_HEAVY_DAMAGE) || + (!GetEnemy() && gpGlobals->curtime - GetEnemies()->LastTimeSeen( AI_UNKNOWN_ENEMY ) < TIME_CARE_ABOUT_DAMAGE ) ) + { + Vector vecEnemyLKP; + + // Fill in where we're trying to look + if ( GetEnemy() ) + { + vecEnemyLKP = GetEnemyLKP(); + } + else + { + if ( GetEnemies()->Find( AI_UNKNOWN_ENEMY ) ) + { + vecEnemyLKP = GetEnemies()->LastKnownPosition( AI_UNKNOWN_ENEMY ); + } + else + { + // Don't have an enemy, so face the direction the last attack came from (don't face north) + vecEnemyLKP = WorldSpaceCenter() + ( g_vecAttackDir * 128 ); + } + } + + // Set the ideal + GetMotor()->SetIdealYawToTarget( vecEnemyLKP ); + + return NPC_STATE_ALERT; + } + + if ( HasCondition(COND_HEAR_DANGER) || + HasCondition(COND_HEAR_COMBAT) || + HasCondition(COND_HEAR_WORLD) || + HasCondition(COND_HEAR_PLAYER) || + HasCondition(COND_HEAR_THUMPER) || + HasCondition(COND_HEAR_BULLET_IMPACT) ) + { + // Interrupted by a sound. So make our ideal yaw the + // source of the sound! + CSound *pSound; + + pSound = GetBestSound(); + Assert( pSound != NULL ); + if ( pSound ) + { + // BRJ 1/7/04: This code used to set the ideal yaw. + // It's really side-effecty to set the yaw here. + // That is now done by the FACE_BESTSOUND schedule. + // Revert this change if it causes problems. + GetMotor()->SetIdealYawToTarget( pSound->GetSoundReactOrigin() ); + if ( pSound->IsSoundType( SOUND_COMBAT | SOUND_DANGER | SOUND_BULLET_IMPACT ) ) + { + return NPC_STATE_ALERT; + } + } + } + + if ( HasInterruptCondition(COND_SMELL) ) + { + return NPC_STATE_ALERT; + } + + return NPC_STATE_INVALID; +} + + +//----------------------------------------------------------------------------- +// Selecting the alert ideal state +//----------------------------------------------------------------------------- +NPC_STATE CAI_BaseNPC::SelectAlertIdealState() +{ + // ALERT goes to IDLE upon becoming bored + // ALERT goes to COMBAT upon sighting an enemy + if ( HasCondition(COND_NEW_ENEMY) || + HasCondition(COND_SEE_ENEMY) || + GetEnemy() != NULL ) + { + return NPC_STATE_COMBAT; + } + + // Set our ideal yaw if we've taken damage + if ( HasCondition(COND_LIGHT_DAMAGE) || + HasCondition(COND_HEAVY_DAMAGE) || + (!GetEnemy() && gpGlobals->curtime - GetEnemies()->LastTimeSeen( AI_UNKNOWN_ENEMY ) < TIME_CARE_ABOUT_DAMAGE ) ) + { + Vector vecEnemyLKP; + + // Fill in where we're trying to look + if ( GetEnemy() ) + { + vecEnemyLKP = GetEnemyLKP(); + } + else + { + if ( GetEnemies()->Find( AI_UNKNOWN_ENEMY ) ) + { + vecEnemyLKP = GetEnemies()->LastKnownPosition( AI_UNKNOWN_ENEMY ); + } + else + { + // Don't have an enemy, so face the direction the last attack came from (don't face north) + vecEnemyLKP = WorldSpaceCenter() + ( g_vecAttackDir * 128 ); + } + } + + // Set the ideal + GetMotor()->SetIdealYawToTarget( vecEnemyLKP ); + + return NPC_STATE_ALERT; + } + + if ( HasCondition(COND_HEAR_DANGER) || + HasCondition(COND_HEAR_COMBAT) ) + { + CSound *pSound = GetBestSound(); + AssertOnce( pSound != NULL ); + + if ( pSound ) + { + GetMotor()->SetIdealYawToTarget( pSound->GetSoundReactOrigin() ); + } + + return NPC_STATE_ALERT; + } + + if ( ShouldGoToIdleState() ) + { + return NPC_STATE_IDLE; + } + + return NPC_STATE_INVALID; +} + + +//----------------------------------------------------------------------------- +// Selecting the alert ideal state +//----------------------------------------------------------------------------- +NPC_STATE CAI_BaseNPC::SelectScriptIdealState() +{ + if ( HasCondition(COND_TASK_FAILED) || + HasCondition(COND_LIGHT_DAMAGE) || + HasCondition(COND_HEAVY_DAMAGE) ) + { + ExitScriptedSequence(); // This will set the ideal state + } + + if ( m_IdealNPCState == NPC_STATE_IDLE ) + { + // Exiting a script. Select the ideal state assuming we were idle now. + m_NPCState = NPC_STATE_IDLE; + NPC_STATE eIdealState = SelectIdealState(); + m_NPCState = NPC_STATE_SCRIPT; + return eIdealState; + } + + return NPC_STATE_INVALID; +} + + +//----------------------------------------------------------------------------- +// Purpose: Surveys the Conditions information available and finds the best +// new state for a npc. +// +// NOTICE the CAI_BaseNPC implementation of this function does not care about +// private conditions! +// +// Output : NPC_STATE - the suggested ideal state based on current conditions. +//----------------------------------------------------------------------------- +NPC_STATE CAI_BaseNPC::SelectIdealState( void ) +{ + // dvs: FIXME: lots of side effecty code in here!! this function should ONLY return an ideal state! + + // --------------------------- + // Do some squad stuff first + // --------------------------- + if (m_pSquad) + { + switch( m_NPCState ) + { + case NPC_STATE_IDLE: + case NPC_STATE_ALERT: + if ( HasCondition ( COND_NEW_ENEMY ) ) + { + m_pSquad->SquadNewEnemy( GetEnemy() ); + } + break; + default: + break; + } + } + + // --------------------------- + // Set ideal state + // --------------------------- + switch ( m_NPCState ) + { + case NPC_STATE_IDLE: + { + NPC_STATE nState = SelectIdleIdealState(); + if ( nState != NPC_STATE_INVALID ) + return nState; + } + break; + + case NPC_STATE_ALERT: + { + NPC_STATE nState = SelectAlertIdealState(); + if ( nState != NPC_STATE_INVALID ) + return nState; + } + break; + + case NPC_STATE_COMBAT: + // COMBAT goes to ALERT upon death of enemy + { + if ( GetEnemy() == NULL ) + { + DevWarning( 2, "***Combat state with no enemy!\n" ); + return NPC_STATE_ALERT; + } + break; + } + case NPC_STATE_SCRIPT: + { + NPC_STATE nState = SelectScriptIdealState(); + if ( nState != NPC_STATE_INVALID ) + return nState; + } + break; + + case NPC_STATE_DEAD: + return NPC_STATE_DEAD; + break; + + default: + break; + } + + // The best ideal state is the current ideal state. + return m_IdealNPCState; +} + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +void CAI_BaseNPC::GiveWeapon( string_t iszWeaponName ) +{ + CBaseCombatWeapon *pWeapon = Weapon_Create( STRING(iszWeaponName) ); + if ( !pWeapon ) + { + Warning( "Couldn't create weapon %s to give NPC %s.\n", STRING(iszWeaponName), STRING(GetEntityName()) ); + return; + } + + // If I have a weapon already, drop it + if ( GetActiveWeapon() ) + { + Weapon_Drop( GetActiveWeapon() ); + } + + // If I have a name, make my weapon match it with "_weapon" appended + if ( GetEntityName() != NULL_STRING ) + { + pWeapon->SetName( AllocPooledString(UTIL_VarArgs("%s_weapon", STRING(GetEntityName()))) ); + } + + Weapon_Equip( pWeapon ); + + // Handle this case + OnGivenWeapon( pWeapon ); +} + +//----------------------------------------------------------------------------- +// Rather specific function that tells us if an NPC is in the process of +// moving to a weapon with the intent to pick it up. +//----------------------------------------------------------------------------- +bool CAI_BaseNPC::IsMovingToPickupWeapon() +{ + return IsCurSchedule( SCHED_NEW_WEAPON ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +bool CAI_BaseNPC::ShouldLookForBetterWeapon() +{ + if( m_flNextWeaponSearchTime > gpGlobals->curtime ) + return false; + + if( !(CapabilitiesGet() & bits_CAP_USE_WEAPONS) ) + return false; + + // Already armed and currently fighting. Don't try to upgrade. + if( GetActiveWeapon() && m_NPCState == NPC_STATE_COMBAT ) + return false; + + if( IsMovingToPickupWeapon() ) + return false; + + if( !IsPlayerAlly() && GetActiveWeapon() ) + return false; + + if( IsInAScript() ) + return false; + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: Check if a better weapon is available. +// For now check if there is a weapon and I don't have one. In +// the future +// UNDONE: actually rate the weapons based on there strength +// Input : +// Output : +//----------------------------------------------------------------------------- +bool CAI_BaseNPC::Weapon_IsBetterAvailable() +{ + if( m_iszPendingWeapon != NULL_STRING ) + { + // Some weapon is reserved for us. + return true; + } + + if( ShouldLookForBetterWeapon() ) + { + if( GetActiveWeapon() ) + { + m_flNextWeaponSearchTime = gpGlobals->curtime + 2; + } + else + { + if( IsInPlayerSquad() ) + { + // Look for a weapon frequently. + m_flNextWeaponSearchTime = gpGlobals->curtime + 1; + } + else + { + m_flNextWeaponSearchTime = gpGlobals->curtime + 2; + } + } + + if ( Weapon_FindUsable( WEAPON_SEARCH_DELTA ) ) + { + return true; + } + } + + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: Returns true is weapon has a line of sight. If bSetConditions is +// true, also sets LOC conditions +// Input : +// Output : +//----------------------------------------------------------------------------- +bool CAI_BaseNPC::WeaponLOSCondition(const Vector &ownerPos, const Vector &targetPos, bool bSetConditions ) +{ +#if 0 + // @TODO (toml 03-07-04): this code might be better (not tested) + Vector vecLocalRelativePosition; + VectorITransform( npcOwner->Weapon_ShootPosition(), npcOwner->EntityToWorldTransform(), vecLocalRelativePosition ); + + // Compute desired test transform + + // Compute desired x axis + Vector xaxis; + VectorSubtract( targetPos, ownerPos, xaxis ); + + // FIXME: Insert angle test here? + float flAngle = acos( xaxis.z / xaxis.Length() ); + + xaxis.z = 0.0f; + float flLength = VectorNormalize( xaxis ); + if ( flLength < 1e-3 ) + return false; + + Vector yaxis( -xaxis.y, xaxis.x, 0.0f ); + + matrix3x4_t losTestToWorld; + MatrixInitialize( losTestToWorld, ownerPos, xaxis, yaxis, zaxis ); + + Vector barrelPos; + VectorTransform( vecLocalRelativePosition, losTestToWorld, barrelPos ); + +#endif + + bool bHaveLOS; + + if (GetActiveWeapon()) + { + bHaveLOS = GetActiveWeapon()->WeaponLOSCondition(ownerPos, targetPos, bSetConditions); + } + else if (CapabilitiesGet() & bits_CAP_INNATE_RANGE_ATTACK1) + { + bHaveLOS = InnateWeaponLOSCondition(ownerPos, targetPos, bSetConditions); + } + else + { + if (bSetConditions) + { + SetCondition( COND_NO_WEAPON ); + } + bHaveLOS = false; + } + // ------------------------------------------- + // Check for friendly fire with the player + // ------------------------------------------- + if ( CapabilitiesGet() & ( bits_CAP_NO_HIT_PLAYER | bits_CAP_NO_HIT_SQUADMATES ) ) + { + float spread = 0.92; + if ( GetActiveWeapon() ) + { + Vector vSpread = GetAttackSpread( GetActiveWeapon() ); + if ( vSpread.x > VECTOR_CONE_15DEGREES.x ) + spread = TableCos(asin(vSpread.x)); + else // too much error because using point not box + spread = 0.99145; // "15 degrees" + } + if (CapabilitiesGet() & bits_CAP_NO_HIT_PLAYER) + { + // Check shoot direction relative to player + if (PlayerInSpread( ownerPos, targetPos, spread, 8*12 )) + { + if (bSetConditions) + { + SetCondition( COND_WEAPON_PLAYER_IN_SPREAD ); + } + bHaveLOS = false; + } + /* For grenades etc. check that player is clear? + // Check player position also + if (PlayerInRange( targetPos, 100 )) + { + SetCondition( COND_WEAPON_PLAYER_NEAR_TARGET ); + } + */ + } + + if ( bHaveLOS ) + { + if ( (CapabilitiesGet() & bits_CAP_NO_HIT_SQUADMATES) && m_pSquad && GetEnemy() ) + { + if ( IsSquadmateInSpread( ownerPos, targetPos, spread, 8*12 ) ) + bHaveLOS = false; + } + } + } + return bHaveLOS; +} + +//----------------------------------------------------------------------------- +// Purpose: Check the innate weapon LOS for an owner at an arbitrary position +// If bSetConditions is true, LOS related conditions will also be set +// Input : +// Output : +//----------------------------------------------------------------------------- +bool CAI_BaseNPC::InnateWeaponLOSCondition( const Vector &ownerPos, const Vector &targetPos, bool bSetConditions ) +{ + // -------------------- + // Check for occlusion + // -------------------- + // Base class version assumes innate weapon position is at eye level + Vector barrelPos = ownerPos + GetViewOffset(); + trace_t tr; + AI_TraceLine( barrelPos, targetPos, MASK_SHOT, this, COLLISION_GROUP_NONE, &tr); + + if ( tr.fraction == 1.0 ) + { + return true; + } + + CBaseEntity *pHitEntity = tr.m_pEnt; + + // Translate a hit vehicle into its passenger if found + if ( GetEnemy() != NULL ) + { + CBaseCombatCharacter *pCCEnemy = GetEnemy()->MyCombatCharacterPointer(); + if ( pCCEnemy != NULL && pCCEnemy->IsInAVehicle() ) + { + // Ok, player in vehicle, check if vehicle is target we're looking at, fire if it is + // Also, check to see if the owner of the entity is the vehicle, in which case it's valid too. + // This catches vehicles that use bone followers. + CBaseEntity *pVehicleEnt = pCCEnemy->GetVehicleEntity(); + if ( pHitEntity == pVehicleEnt || pHitEntity->GetOwnerEntity() == pVehicleEnt ) + return true; + } + } + + if ( pHitEntity == GetEnemy() ) + { + return true; + } + else if ( pHitEntity && pHitEntity->MyCombatCharacterPointer() ) + { + if (IRelationType( pHitEntity ) == D_HT) + { + return true; + } + else if (bSetConditions) + { + SetCondition(COND_WEAPON_BLOCKED_BY_FRIEND); + } + } + else if (bSetConditions) + { + SetCondition(COND_WEAPON_SIGHT_OCCLUDED); + SetEnemyOccluder(tr.m_pEnt); + } + + return false; +} + +//========================================================= +// CanCheckAttacks - prequalifies a npc to do more fine +// checking of potential attacks. +//========================================================= +bool CAI_BaseNPC::FCanCheckAttacks( void ) +{ + // Not allowed to check attacks while climbing or jumping + // Otherwise schedule is interrupted while on ladder/etc + // which is NOT a legal place to attack from + if ( GetNavType() == NAV_CLIMB || GetNavType() == NAV_JUMP ) + return false; + + if ( HasCondition(COND_SEE_ENEMY) && !HasCondition( COND_ENEMY_TOO_FAR)) + { + return true; + } + + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: Return dist. to enemy (closest of origin/head/feet) +// Input : +// Output : +//----------------------------------------------------------------------------- +float CAI_BaseNPC::EnemyDistance( CBaseEntity *pEnemy ) +{ + Vector enemyDelta = pEnemy->WorldSpaceCenter() - WorldSpaceCenter(); + + // NOTE: We ignore rotation for computing height. Assume it isn't an effect + // we care about, so we simply use OBBSize().z for height. + // Otherwise you'd do this: + // pEnemy->CollisionProp()->WorldSpaceSurroundingBounds( &enemyMins, &enemyMaxs ); + // float enemyHeight = enemyMaxs.z - enemyMins.z; + + float enemyHeight = pEnemy->CollisionProp()->OBBSize().z; + float myHeight = CollisionProp()->OBBSize().z; + + // max distance our centers can be apart with the boxes still overlapping + float flMaxZDist = ( enemyHeight + myHeight ) * 0.5f; + + // see if the enemy is closer to my head, feet or in between + if ( enemyDelta.z > flMaxZDist ) + { + // enemy feet above my head, compute distance from my head to his feet + enemyDelta.z -= flMaxZDist; + } + else if ( enemyDelta.z < -flMaxZDist ) + { + // enemy head below my feet, return distance between my feet and his head + enemyDelta.z += flMaxZDist; + } + else + { + // boxes overlap in Z, no delta + enemyDelta.z = 0; + } + + return enemyDelta.Length(); +} + +//----------------------------------------------------------------------------- + +float CAI_BaseNPC::GetReactionDelay( CBaseEntity *pEnemy ) +{ + return ( m_NPCState == NPC_STATE_ALERT || m_NPCState == NPC_STATE_COMBAT ) ? + ai_reaction_delay_alert.GetFloat() : + ai_reaction_delay_idle.GetFloat(); +} + +//----------------------------------------------------------------------------- +// Purpose: Update information on my enemy +// Input : +// Output : Returns true is new enemy, false is known enemy +//----------------------------------------------------------------------------- +bool CAI_BaseNPC::UpdateEnemyMemory( CBaseEntity *pEnemy, const Vector &position, CBaseEntity *pInformer ) +{ + bool firstHand = ( pInformer == NULL || pInformer == this ); + + AI_PROFILE_SCOPE(CAI_BaseNPC_UpdateEnemyMemory); + + if ( GetEnemies() ) + { + // If the was eluding me and allow the NPC to play a sound + if (GetEnemies()->HasEludedMe(pEnemy)) + { + FoundEnemySound(); + } + float reactionDelay = ( !pInformer || pInformer == this ) ? GetReactionDelay( pEnemy ) : 0.0; + bool result = GetEnemies()->UpdateMemory(GetNavigator()->GetNetwork(), pEnemy, position, reactionDelay, firstHand); + + if ( !firstHand && pEnemy && result && GetState() == NPC_STATE_IDLE ) // if it's a new potential enemy + ForceDecisionThink(); + + if ( firstHand && pEnemy && m_pSquad ) + { + m_pSquad->UpdateEnemyMemory( this, pEnemy, position ); + } + return result; + } + return true; +} + + +//----------------------------------------------------------------------------- +// Purpose: Remembers the thing my enemy is hiding behind. Called when either +// COND_ENEMY_OCCLUDED or COND_WEAPON_SIGHT_OCCLUDED is set. +//----------------------------------------------------------------------------- +void CAI_BaseNPC::SetEnemyOccluder(CBaseEntity *pBlocker) +{ + m_hEnemyOccluder = pBlocker; +} + + +//----------------------------------------------------------------------------- +// Purpose: Gets the thing my enemy is hiding behind (assuming they are hiding). +//----------------------------------------------------------------------------- +CBaseEntity *CAI_BaseNPC::GetEnemyOccluder(void) +{ + return m_hEnemyOccluder.Get(); +} + + +//----------------------------------------------------------------------------- +// Purpose: part of the Condition collection process +// gets and stores data and conditions pertaining to a npc's +// enemy. +// @TODO (toml 07-27-03): this should become subservient to the senses. right +// now, it yields different result +// Input : +// Output : +//----------------------------------------------------------------------------- +void CAI_BaseNPC::GatherEnemyConditions( CBaseEntity *pEnemy ) +{ + AI_PROFILE_SCOPE(CAI_BaseNPC_GatherEnemyConditions); + + ClearCondition( COND_ENEMY_FACING_ME ); + ClearCondition( COND_BEHIND_ENEMY ); + + // --------------------------- + // Set visibility conditions + // --------------------------- + if ( HasCondition( COND_NEW_ENEMY ) || GetSenses()->GetTimeLastUpdate( GetEnemy() ) == gpGlobals->curtime ) + { + AI_PROFILE_SCOPE_BEGIN(CAI_BaseNPC_GatherEnemyConditions_Visibility); + + ClearCondition( COND_HAVE_ENEMY_LOS ); + ClearCondition( COND_ENEMY_OCCLUDED ); + + CBaseEntity *pBlocker = NULL; + SetEnemyOccluder(NULL); + + bool bSensesDidSee = GetSenses()->DidSeeEntity( pEnemy ); + + if ( !bSensesDidSee && ( ( EnemyDistance( pEnemy ) >= GetSenses()->GetDistLook() ) || !FVisible( pEnemy, MASK_OPAQUE, &pBlocker ) ) ) + { + // No LOS to enemy + SetEnemyOccluder(pBlocker); + SetCondition( COND_ENEMY_OCCLUDED ); + ClearCondition( COND_SEE_ENEMY ); + + if (HasMemory( bits_MEMORY_HAD_LOS )) + { + AI_PROFILE_SCOPE(CAI_BaseNPC_GatherEnemyConditions_Outputs); + // Send output event + if (GetEnemy()->IsPlayer()) + { + m_OnLostPlayerLOS.FireOutput( GetEnemy(), this ); + } + m_OnLostEnemyLOS.FireOutput( GetEnemy(), this ); + } + Forget( bits_MEMORY_HAD_LOS ); + } + else + { + // Have LOS but may not be in view cone + SetCondition( COND_HAVE_ENEMY_LOS ); + + if ( bSensesDidSee ) + { + // Have LOS and in view cone + SetCondition( COND_SEE_ENEMY ); + } + else + { + ClearCondition( COND_SEE_ENEMY ); + } + + if (!HasMemory( bits_MEMORY_HAD_LOS )) + { + AI_PROFILE_SCOPE(CAI_BaseNPC_GatherEnemyConditions_Outputs); + // Send output event + EHANDLE hEnemy; + hEnemy.Set( GetEnemy() ); + + if (GetEnemy()->IsPlayer()) + { + m_OnFoundPlayer.Set(hEnemy, this, this); + m_OnFoundEnemy.Set(hEnemy, this, this); + } + else + { + m_OnFoundEnemy.Set(hEnemy, this, this); + } + } + Remember( bits_MEMORY_HAD_LOS ); + } + + AI_PROFILE_SCOPE_END(); + } + + // ------------------- + // If enemy is dead + // ------------------- + if ( !pEnemy->IsAlive() ) + { + SetCondition( COND_ENEMY_DEAD ); + ClearCondition( COND_SEE_ENEMY ); + ClearCondition( COND_ENEMY_OCCLUDED ); + return; + } + + float flDistToEnemy = EnemyDistance(pEnemy); + + AI_PROFILE_SCOPE_BEGIN(CAI_BaseNPC_GatherEnemyConditions_SeeEnemy); + + if ( HasCondition( COND_SEE_ENEMY ) ) + { + // Trail the enemy a bit if he's moving + if (pEnemy->GetSmoothedVelocity() != vec3_origin) + { + Vector vTrailPos = pEnemy->GetAbsOrigin() - pEnemy->GetSmoothedVelocity() * random->RandomFloat( -0.05, 0 ); + UpdateEnemyMemory(pEnemy,vTrailPos); + } + else + { + UpdateEnemyMemory(pEnemy,pEnemy->GetAbsOrigin()); + } + + // If it's not an NPC, assume it can't see me + if ( pEnemy->MyCombatCharacterPointer() && pEnemy->MyCombatCharacterPointer()->FInViewCone ( this ) ) + { + SetCondition ( COND_ENEMY_FACING_ME ); + ClearCondition ( COND_BEHIND_ENEMY ); + } + else + { + ClearCondition( COND_ENEMY_FACING_ME ); + SetCondition ( COND_BEHIND_ENEMY ); + } + } + else if ( (!HasCondition(COND_ENEMY_OCCLUDED) && !HasCondition(COND_SEE_ENEMY)) && ( flDistToEnemy <= 256 ) ) + { + // if the enemy is not occluded, and unseen, that means it is behind or beside the npc. + // if the enemy is near enough the npc, we go ahead and let the npc know where the + // enemy is. Send the enemy in as the informer so this knowledge will be regarded as + // secondhand so that the NPC doesn't + UpdateEnemyMemory( pEnemy, pEnemy->GetAbsOrigin(), pEnemy ); + } + + AI_PROFILE_SCOPE_END(); + + float tooFar = m_flDistTooFar; + if ( GetActiveWeapon() && HasCondition(COND_SEE_ENEMY) ) + { + tooFar = max( m_flDistTooFar, GetActiveWeapon()->m_fMaxRange1 ); + } + + if ( flDistToEnemy >= tooFar ) + { + // enemy is very far away from npc + SetCondition( COND_ENEMY_TOO_FAR ); + } + else + { + ClearCondition( COND_ENEMY_TOO_FAR ); + } + + if ( FCanCheckAttacks() ) + { + // This may also call SetEnemyOccluder! + GatherAttackConditions( GetEnemy(), flDistToEnemy ); + } + else + { + ClearAttackConditions(); + } + + // If my enemy has moved significantly, or if the enemy has changed update my path + UpdateEnemyPos(); + + // If my target entity has moved significantly, update my path + // This is an odd place to put this, but where else should it go? + UpdateTargetPos(); + + // ---------------------------------------------------------------------------- + // Check if enemy is reachable via the node graph unless I'm not on a network + // ---------------------------------------------------------------------------- + if (GetNavigator()->IsOnNetwork()) + { + // Note that unreachablity times out + if (IsUnreachable(GetEnemy())) + { + SetCondition(COND_ENEMY_UNREACHABLE); + } + } + + //----------------------------------------------------------------------- + // If I haven't seen the enemy in a while he may have eluded me + //----------------------------------------------------------------------- + if (gpGlobals->curtime - GetEnemyLastTimeSeen() > 8) + { + //----------------------------------------------------------------------- + // I'm at last known position at enemy isn't in sight then has eluded me + // ---------------------------------------------------------------------- + Vector flEnemyLKP = GetEnemyLKP(); + if (((flEnemyLKP - GetAbsOrigin()).Length2D() < 48) && + !HasCondition(COND_SEE_ENEMY)) + { + MarkEnemyAsEluded(); + } + //------------------------------------------------------------------- + // If enemy isn't reachable, I can see last known position and enemy + // isn't there, then he has eluded me + // ------------------------------------------------------------------ + if (!HasCondition(COND_SEE_ENEMY) && HasCondition(COND_ENEMY_UNREACHABLE)) + { + if ( !FVisible( flEnemyLKP ) ) + { + MarkEnemyAsEluded(); + } + } + } +} + + +//----------------------------------------------------------------------------- +// In the case of goaltype enemy, update the goal position +//----------------------------------------------------------------------------- +float CAI_BaseNPC::GetGoalRepathTolerance( CBaseEntity *pGoalEnt, GoalType_t type, const Vector &curGoal, const Vector &curTargetPos ) +{ + float distToGoal = ( GetAbsOrigin() - curTargetPos ).Length() - GetNavigator()->GetArrivalDistance(); + float distMoved1Sec = GetSmoothedVelocity().Length(); + float result = 120; // FIXME: why 120? + + if (distMoved1Sec > 0.0) + { + float t = distToGoal / distMoved1Sec; + + result = clamp( 120 * t, 0, 120 ); + // Msg("t %.2f : d %.0f (%.0f)\n", t, result, distMoved1Sec ); + } + + if ( !pGoalEnt->IsPlayer() ) + result *= 1.20; + + return result; +} + +//----------------------------------------------------------------------------- +// In the case of goaltype enemy, update the goal position +//----------------------------------------------------------------------------- +void CAI_BaseNPC::UpdateEnemyPos() +{ + // Don't perform path recomputations during a climb or a jump + if ( !GetNavigator()->IsInterruptable() ) + return; + + if ( m_AnyUpdateEnemyPosTimer.Expired() && m_UpdateEnemyPosTimer.Expired() ) + { + // FIXME: does GetGoalRepathTolerance() limit re-routing enough to remove this? + // m_UpdateEnemyPosTimer.Set( 0.5, 1.0 ); + + // If my enemy has moved significantly, or if the enemy has changed update my path + if ( GetNavigator()->GetGoalType() == GOALTYPE_ENEMY ) + { + if (m_hEnemy != GetNavigator()->GetGoalTarget()) + { + GetNavigator()->SetGoalTarget( m_hEnemy, vec3_origin ); + } + else + { + Vector vEnemyLKP = GetEnemyLKP(); + TranslateNavGoal( GetEnemy(), vEnemyLKP ); + float tolerance = GetGoalRepathTolerance( GetEnemy(), GOALTYPE_ENEMY, GetNavigator()->GetGoalPos(), vEnemyLKP); + if ( (GetNavigator()->GetGoalPos() - vEnemyLKP).Length() > tolerance ) + { + // FIXME: when fleeing crowds, won't this severely limit the effectiveness of each individual? Shouldn't this be a mutex that's held for some period so that at least one attacker is effective? + m_AnyUpdateEnemyPosTimer.Set( 0.1 ); // FIXME: what's a reasonable interval? + if ( !GetNavigator()->RefindPathToGoal( false ) ) + { + TaskFail( FAIL_NO_ROUTE ); + } + } + } + } + } +} + + +//----------------------------------------------------------------------------- +// In the case of goaltype targetent, update the goal position +//----------------------------------------------------------------------------- +void CAI_BaseNPC::UpdateTargetPos() +{ + // BRJ 10/7/02 + // FIXME: make this check time based instead of distance based! + + // Don't perform path recomputations during a climb or a jump + if ( !GetNavigator()->IsInterruptable() ) + return; + + // If my target entity has moved significantly, or has changed, update my path + // This is an odd place to put this, but where else should it go? + if ( GetNavigator()->GetGoalType() == GOALTYPE_TARGETENT ) + { + if (m_hTargetEnt != GetNavigator()->GetGoalTarget()) + { + GetNavigator()->SetGoalTarget( m_hTargetEnt, vec3_origin ); + } + else if ( GetNavigator()->GetGoalFlags() & AIN_UPDATE_TARGET_POS ) + { + if ( GetTarget() == NULL || (GetNavigator()->GetGoalPos() - GetTarget()->GetAbsOrigin()).Length() > GetGoalRepathTolerance( GetTarget(), GOALTYPE_TARGETENT, GetNavigator()->GetGoalPos(), GetTarget()->GetAbsOrigin()) ) + { + if ( !GetNavigator()->RefindPathToGoal( false ) ) + { + TaskFail( FAIL_NO_ROUTE ); + } + } + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: part of the Condition collection process +// gets and stores data and conditions pertaining to a npc's +// enemy. +// Input : +// Output : +//----------------------------------------------------------------------------- +void CAI_BaseNPC::CheckTarget( CBaseEntity *pTarget ) +{ + AI_PROFILE_SCOPE(CAI_Enemies_CheckTarget); + + ClearCondition ( COND_HAVE_TARGET_LOS ); + ClearCondition ( COND_TARGET_OCCLUDED ); + + // --------------------------- + // Set visibility conditions + // --------------------------- + if ( ( EnemyDistance( pTarget ) >= GetSenses()->GetDistLook() ) || !FVisible( pTarget ) ) + { + // No LOS to target + SetCondition( COND_TARGET_OCCLUDED ); + } + else + { + // Have LOS (may not be in view cone) + SetCondition( COND_HAVE_TARGET_LOS ); + } + + UpdateTargetPos(); +} + +//----------------------------------------------------------------------------- +// Purpose: Creates a bullseye of limited lifespan at the provided position +// Input : vecOrigin - Where to create the bullseye +// duration - The lifespan of the bullseye +// Output : A BaseNPC pointer to the bullseye +// +// NOTES : It is the caller's responsibility to set up relationships with +// this bullseye! +//----------------------------------------------------------------------------- +CAI_BaseNPC *CAI_BaseNPC::CreateCustomTarget( const Vector &vecOrigin, float duration ) +{ +#ifdef HL2_DLL + CNPC_Bullseye *pTarget = (CNPC_Bullseye*)CreateEntityByName( "npc_bullseye" ); + + ASSERT( pTarget != NULL ); + + // Build a nonsolid bullseye and place it in the desired location + // The bullseye must take damage or the SetHealth 0 call will not be able + pTarget->AddSpawnFlags( SF_BULLSEYE_NONSOLID ); + pTarget->SetAbsOrigin( vecOrigin ); + pTarget->Spawn(); + + // Set it up to remove itself + variant_t value; + value.SetFloat(0); + g_EventQueue.AddEvent( pTarget, "SetHealth", value, 2.0, this, this ); + + return pTarget; +#else + return NULL; +#endif// HL2_DLL +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : eNewActivity - +// Output : Activity +//----------------------------------------------------------------------------- +Activity CAI_BaseNPC::NPC_TranslateActivity( Activity eNewActivity ) +{ + Assert( eNewActivity != ACT_INVALID ); + + if (eNewActivity == ACT_RANGE_ATTACK1) + { + if ( IsCrouching() ) + { + eNewActivity = ACT_RANGE_ATTACK1_LOW; + } + } + else if (eNewActivity == ACT_RELOAD) + { + if (IsCrouching()) + { + eNewActivity = ACT_RELOAD_LOW; + } + } + else if ( eNewActivity == ACT_IDLE ) + { + if ( IsCrouching() ) + { + eNewActivity = ACT_CROUCHIDLE; + } + } + + if (CapabilitiesGet() & bits_CAP_DUCK) + { + if (eNewActivity == ACT_RELOAD) + { + return GetReloadActivity(GetHintNode()); + } + else if ((eNewActivity == ACT_COVER ) || + (eNewActivity == ACT_IDLE && HasMemory(bits_MEMORY_INCOVER))) + { + Activity nCoverActivity = GetCoverActivity(GetHintNode()); + // --------------------------------------------------------------- + // Some NPCs don't have a cover activity defined so just use idle + // --------------------------------------------------------------- + if (SelectWeightedSequence( nCoverActivity ) == ACTIVITY_NOT_AVAILABLE) + { + nCoverActivity = ACT_IDLE; + } + + return nCoverActivity; + } + } + return eNewActivity; +} + + +//----------------------------------------------------------------------------- + +Activity CAI_BaseNPC::TranslateActivity( Activity idealActivity, Activity *pIdealWeaponActivity ) +{ + const int MAX_TRIES = 5; + int count = 0; + + bool bIdealWeaponRequired = false; + Activity idealWeaponActivity; + Activity baseTranslation; + bool bWeaponRequired = false; + Activity weaponTranslation; + Activity last; + Activity current; + + idealWeaponActivity = Weapon_TranslateActivity( idealActivity, &bIdealWeaponRequired ); + if ( pIdealWeaponActivity ) + *pIdealWeaponActivity = idealWeaponActivity; + + baseTranslation = idealActivity; + weaponTranslation = idealActivity; + last = idealActivity; + while ( count++ < MAX_TRIES ) + { + current = NPC_TranslateActivity( last ); + if ( current != last ) + baseTranslation = current; + + weaponTranslation = Weapon_TranslateActivity( current, &bWeaponRequired ); + + if ( weaponTranslation == last ) + break; + + last = weaponTranslation; + } + AssertMsg( count < MAX_TRIES, "Circular activity translation!" ); + + if ( last == ACT_SCRIPT_CUSTOM_MOVE ) + return ACT_SCRIPT_CUSTOM_MOVE; + + if ( HaveSequenceForActivity( weaponTranslation ) ) + return weaponTranslation; + + if ( bWeaponRequired ) + { + // only complain about an activity once + static CUtlVector< Activity > sUniqueActivities; + + if (!sUniqueActivities.Find( weaponTranslation)) + { + // FIXME: warning + DevWarning( "%s missing activity \"%s\" needed by weapon\"%s\"\n", + GetClassname(), GetActivityName( weaponTranslation ), GetActiveWeapon()->GetClassname() ); + + sUniqueActivities.AddToTail( weaponTranslation ); + } + } + + if ( baseTranslation != weaponTranslation && HaveSequenceForActivity( baseTranslation ) ) + return baseTranslation; + + if ( idealWeaponActivity != baseTranslation && HaveSequenceForActivity( idealWeaponActivity ) ) + return idealActivity; + + if ( idealActivity != idealWeaponActivity && HaveSequenceForActivity( idealActivity ) ) + return idealActivity; + + Assert( !HaveSequenceForActivity( idealActivity ) ); + if ( idealActivity == ACT_RUN ) + { + idealActivity = ACT_WALK; + } + else if ( idealActivity == ACT_WALK ) + { + idealActivity = ACT_RUN; + } + + return idealActivity; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : NewActivity - +// iSequence - +// translatedActivity - +// weaponActivity - +//----------------------------------------------------------------------------- +void CAI_BaseNPC::ResolveActivityToSequence(Activity NewActivity, int &iSequence, Activity &translatedActivity, Activity &weaponActivity) +{ + AI_PROFILE_SCOPE( CAI_BaseNPC_ResolveActivityToSequence ); + + iSequence = ACTIVITY_NOT_AVAILABLE; + + translatedActivity = TranslateActivity( NewActivity, &weaponActivity ); + + if ( ( NewActivity == ACT_SCRIPT_CUSTOM_MOVE ) ) + { + iSequence = GetScriptCustomMoveSequence(); + } + else + { + iSequence = SelectWeightedSequence( translatedActivity ); + + if ( iSequence == ACTIVITY_NOT_AVAILABLE ) + { + static CAI_BaseNPC *pLastWarn; + static Activity lastWarnActivity; + static float timeLastWarn; + + if ( ( pLastWarn != this && lastWarnActivity != translatedActivity ) || gpGlobals->curtime - timeLastWarn > 5.0 ) + { + DevWarning( "%s has no sequence for act:%s\n", GetClassname(), ActivityList_NameForIndex(translatedActivity) ); + pLastWarn = this; + lastWarnActivity = translatedActivity; + timeLastWarn = gpGlobals->curtime; + } + + if ( translatedActivity == ACT_RUN ) + { + translatedActivity = ACT_WALK; + iSequence = SelectWeightedSequence( translatedActivity ); + } + } + } + + if ( iSequence == ACT_INVALID ) + { + // Abject failure. Use sequence zero. + iSequence = 0; + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : NewActivity - +// iSequence - +// translatedActivity - +// weaponActivity - +//----------------------------------------------------------------------------- +extern ConVar ai_sequence_debug; + +void CAI_BaseNPC::SetActivityAndSequence(Activity NewActivity, int iSequence, Activity translatedActivity, Activity weaponActivity) +{ + m_translatedActivity = translatedActivity; + + if (ai_sequence_debug.GetBool() == true && (m_debugOverlays & OVERLAY_NPC_SELECTED_BIT)) + { + DevMsg("SetActivityAndSequence : %s: %s:%s -> %s:%s / %s:%s\n", GetClassname(), + GetActivityName(GetActivity()), GetSequenceName(GetSequence()), + GetActivityName(NewActivity), GetSequenceName(iSequence), + GetActivityName(translatedActivity), GetActivityName(weaponActivity) ); + + } + + // Set to the desired anim, or default anim if the desired is not present + if ( iSequence > ACTIVITY_NOT_AVAILABLE ) + { + if ( GetSequence() != iSequence || !SequenceLoops() ) + { + // + // Don't reset frame between movement phased animations + if (!IsActivityMovementPhased( m_Activity ) || + !IsActivityMovementPhased( NewActivity )) + { + SetCycle( 0 ); + } + } + + ResetSequence( iSequence ); + Weapon_SetActivity( weaponActivity, SequenceDuration( iSequence ) ); + } + else + { + // Not available try to get default anim + ResetSequence( 0 ); + } + + // Set the view position based on the current activity + SetViewOffset( EyeOffset(m_translatedActivity) ); + + if (m_Activity != NewActivity) + { + OnChangeActivity(NewActivity); + } + + // NOTE: We DO NOT write the translated activity here. + // This is to abstract the activity translation from the AI code. + // As far as the code is concerned, a translation is merely a new set of sequences + // that should be regarded as the activity in question. + + // Go ahead and set this so it doesn't keep trying when the anim is not present + m_Activity = NewActivity; + + // this cannot be called until m_Activity stores NewActivity! + GetMotor()->RecalculateYawSpeed(); +} + + +//----------------------------------------------------------------------------- +// Purpose: Sets the activity to the desired activity immediately, skipping any +// transition sequences. +// Input : NewActivity - +//----------------------------------------------------------------------------- +void CAI_BaseNPC::SetActivity( Activity NewActivity ) +{ + // If I'm already doing the NewActivity I can bail. + // FIXME: Should this be based on the current translated activity and ideal translated activity (calculated below)? + // The old code only cared about the logical activity, not translated. + + if (m_Activity == NewActivity) + { + return; + } + + // Don't do this if I'm playing a transition, unless it's ACT_RESET. + if ( NewActivity != ACT_RESET && m_Activity == ACT_TRANSITION && m_IdealActivity != ACT_DO_NOT_DISTURB ) + { + return; + } + + if (ai_sequence_debug.GetBool() == true && (m_debugOverlays & OVERLAY_NPC_SELECTED_BIT)) + { + DevMsg("SetActivity : %s: %s -> %s\n", GetClassname(), GetActivityName(GetActivity()), GetActivityName(NewActivity)); + } + + if ( !GetModelPtr() ) + return; + + // In case someone calls this with something other than the ideal activity. + m_IdealActivity = NewActivity; + + // Resolve to ideals and apply directly, skipping transitions. + ResolveActivityToSequence(m_IdealActivity, m_nIdealSequence, m_IdealTranslatedActivity, m_IdealWeaponActivity); + + //DevMsg("%s: SLAM %s -> %s\n", GetClassname(), GetSequenceName(GetSequence()), GetSequenceName(m_nIdealSequence)); + + SetActivityAndSequence(m_IdealActivity, m_nIdealSequence, m_IdealTranslatedActivity, m_IdealWeaponActivity); +} + + +//----------------------------------------------------------------------------- +// Purpose: Sets the activity that we would like to transition toward. +// Input : NewActivity - +//----------------------------------------------------------------------------- +void CAI_BaseNPC::SetIdealActivity( Activity NewActivity ) +{ + // ignore if it's an ACT_TRANSITION, it means somewhere we're setting IdealActivity with a bogus intermediate value + if (NewActivity == ACT_TRANSITION) + { + Assert( 0 ); + return; + } + + if (ai_sequence_debug.GetBool() == true && (m_debugOverlays & OVERLAY_NPC_SELECTED_BIT)) + { + DevMsg("SetIdealActivity : %s: %s -> %s\n", GetClassname(), GetActivityName(GetActivity()), GetActivityName(NewActivity)); + } + + + if (NewActivity == ACT_RESET) + { + // They probably meant to call SetActivity(ACT_RESET)... we'll fix it for them. + SetActivity(ACT_RESET); + return; + } + + m_IdealActivity = NewActivity; + + if( NewActivity == ACT_DO_NOT_DISTURB ) + { + // Don't resolve anything! Leave it the way the user has it right now. + return; + } + + if ( !GetModelPtr() ) + return; + + // 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); +} + + +//----------------------------------------------------------------------------- +// Purpose: Moves toward the ideal activity through any transition sequences. +//----------------------------------------------------------------------------- +void CAI_BaseNPC::AdvanceToIdealActivity(void) +{ + // If there is a transition sequence between the current sequence and the ideal sequence... + int nNextSequence = FindTransitionSequence(GetSequence(), m_nIdealSequence, NULL); + if (nNextSequence != -1) + { + // We found a transition sequence or possibly went straight to + // the ideal sequence. + if (nNextSequence != m_nIdealSequence) + { +// DevMsg("%s: TRANSITION %s -> %s -> %s\n", GetClassname(), GetSequenceName(GetSequence()), GetSequenceName(nNextSequence), GetSequenceName(m_nIdealSequence)); + + Activity eWeaponActivity = ACT_TRANSITION; + Activity eTranslatedActivity = ACT_TRANSITION; + + // Figure out if the transition sequence has an associated activity that + // we can use for our weapon. Do activity translation also. + Activity eTransitionActivity = GetSequenceActivity(nNextSequence); + if (eTransitionActivity != ACT_INVALID) + { + int nDiscard; + ResolveActivityToSequence(eTransitionActivity, nDiscard, eTranslatedActivity, eWeaponActivity); + } + + // Set activity and sequence to the transition stuff. Set the activity to ACT_TRANSITION + // so we know we're in a transition. + SetActivityAndSequence(ACT_TRANSITION, nNextSequence, eTranslatedActivity, eWeaponActivity); + } + else + { + //DevMsg("%s: IDEAL %s -> %s\n", GetClassname(), GetSequenceName(GetSequence()), GetSequenceName(m_nIdealSequence)); + + // Set activity and sequence to the ideal stuff that was set up in MaintainActivity. + SetActivityAndSequence(m_IdealActivity, m_nIdealSequence, m_IdealTranslatedActivity, m_IdealWeaponActivity); + } + } + // Else go straight there to the ideal activity. + else + { + //DevMsg("%s: Unable to get from sequence %s to %s!\n", GetClassname(), GetSequenceName(GetSequence()), GetSequenceName(m_nIdealSequence)); + SetActivity(m_IdealActivity); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Tries to achieve our ideal animation state, playing any transition +// sequences that we need to play to get there. +//----------------------------------------------------------------------------- +void CAI_BaseNPC::MaintainActivity(void) +{ + AI_PROFILE_SCOPE( CAI_BaseNPC_MaintainActivity ); + + if ( m_lifeState == LIFE_DEAD ) + { + // Don't maintain activities if we're daid. + // Blame Speyrer + return; + } + + if ((GetState() == NPC_STATE_SCRIPT)) + { + // HACK: finish any transitions we might be playing before we yield control to the script + if (GetActivity() != ACT_TRANSITION) + { + // Our animation state is being controlled by a script. + return; + } + } + + if( m_IdealActivity == ACT_DO_NOT_DISTURB || !GetModelPtr() ) + { + return; + } + + // We may have work to do if we aren't playing our ideal activity OR if we + // aren't playing our ideal sequence. + if ((GetActivity() != m_IdealActivity) || (GetSequence() != m_nIdealSequence)) + { + if (ai_sequence_debug.GetBool() == true && (m_debugOverlays & OVERLAY_NPC_SELECTED_BIT)) + { + DevMsg("MaintainActivity %s : %s:%s -> %s:%s\n", GetClassname(), + GetActivityName(GetActivity()), GetSequenceName(GetSequence()), + GetActivityName(m_IdealActivity), GetSequenceName(m_nIdealSequence)); + } + + bool bAdvance = false; + + // If we're in a transition activity, see if we are done with the transition. + if (GetActivity() == ACT_TRANSITION) + { + // If the current sequence is finished, try to go to the next one + // closer to our ideal sequence. + if (IsSequenceFinished()) + { + bAdvance = true; + } + // Else a transition sequence is in progress, do nothing. + } + // Else get a specific sequence for the activity and try to transition to that. + else + { + // Save off a target sequence and translated activities to apply when we finish + // playing all the transitions and finally arrive at our ideal activity. + ResolveActivityToSequence(m_IdealActivity, m_nIdealSequence, m_IdealTranslatedActivity, m_IdealWeaponActivity); + bAdvance = true; + } + + if (bAdvance) + { + // Try to go to the next sequence closer to our ideal sequence. + AdvanceToIdealActivity(); + } + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Returns true if our ideal activity has finished playing. +//----------------------------------------------------------------------------- +bool CAI_BaseNPC::IsActivityFinished( void ) +{ + return (IsSequenceFinished() && (GetSequence() == m_nIdealSequence)); +} + +//----------------------------------------------------------------------------- +// Purpose: Checks to see if the activity is one of the standard phase-matched movement activities +// Input : activity +//----------------------------------------------------------------------------- +bool CAI_BaseNPC::IsActivityMovementPhased( Activity activity ) +{ + switch( activity ) + { + case ACT_WALK: + case ACT_WALK_AIM: + case ACT_WALK_CROUCH: + case ACT_WALK_CROUCH_AIM: + case ACT_RUN: + case ACT_RUN_AIM: + case ACT_RUN_CROUCH: + case ACT_RUN_CROUCH_AIM: + case ACT_RUN_PROTECTED: + return true; + break; + default: + break; + } + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CAI_BaseNPC::OnChangeActivity( Activity eNewActivity ) +{ + if ( eNewActivity == ACT_RUN || + eNewActivity == ACT_RUN_AIM || + eNewActivity == ACT_WALK ) + { + Stand(); + } +} + +//========================================================= +// SetSequenceByName +//========================================================= +void CAI_BaseNPC::SetSequenceByName( char *szSequence ) +{ + int iSequence = LookupSequence( szSequence ); + + if ( iSequence > ACTIVITY_NOT_AVAILABLE ) + SetSequenceById( iSequence ); + else + { + DevWarning( 2, "%s has no sequence to match request\n", GetClassname(), szSequence ); + SetSequence( 0 ); // Set to the reset anim (if it's there) + } +} + +//----------------------------------------------------------------------------- + +void CAI_BaseNPC::SetSequenceById( int iSequence ) +{ + // Set to the desired anim, or default anim if the desired is not present + if ( iSequence > ACTIVITY_NOT_AVAILABLE ) + { + if ( GetSequence() != iSequence || !SequenceLoops() ) + { + SetCycle( 0 ); + } + + ResetSequence( iSequence ); // Set to the reset anim (if it's there) + GetMotor()->RecalculateYawSpeed(); + } + else + { + // Not available try to get default anim + DevWarning( 2, "%s invalid sequence requested\n", GetClassname() ); + SetSequence( 0 ); // Set to the reset anim (if it's there) + } +} + +//----------------------------------------------------------------------------- +// Purpose: Returns the target entity +// Input : +// Output : +//----------------------------------------------------------------------------- +CBaseEntity *CAI_BaseNPC::GetNavTargetEntity(void) +{ + if ( GetNavigator()->GetGoalType() == GOALTYPE_ENEMY ) + return m_hEnemy; + else if ( GetNavigator()->GetGoalType() == GOALTYPE_TARGETENT ) + return m_hTargetEnt; + return NULL; +} + + +//----------------------------------------------------------------------------- +// Purpose: returns zero if the caller can jump from +// vecStart to vecEnd ignoring collisions with pTarget +// +// if the throw fails, returns the distance +// that can be travelled before an obstacle is hit +//----------------------------------------------------------------------------- +#include "ai_initutils.h" +//#define _THROWDEBUG +float CAI_BaseNPC::ThrowLimit( const Vector &vecStart, + const Vector &vecEnd, + float fGravity, + float fArcSize, + const Vector &mins, + const Vector &maxs, + CBaseEntity *pTarget, + Vector *jumpVel, + CBaseEntity **pBlocker) +{ + // Get my jump velocity + Vector rawJumpVel = CalcThrowVelocity(vecStart, vecEnd, fGravity, fArcSize); + *jumpVel = rawJumpVel; + Vector vecFrom = vecStart; + + // Calculate the total time of the jump minus a tiny fraction + float jumpTime = (vecStart - vecEnd).Length2D()/rawJumpVel.Length2D(); + float timeStep = jumpTime / 10.0; + + Vector gravity = Vector(0,0,fGravity); + + // this loop takes single steps to the goal. + for (float flTime = 0 ; flTime < jumpTime-0.1 ; flTime += timeStep ) + { + // Calculate my position after the time step (average velocity over this time step) + Vector nextPos = vecFrom + (rawJumpVel - 0.5 * gravity * timeStep) * timeStep; + + // If last time step make next position the target position + if ((flTime + timeStep) > jumpTime) + { + nextPos = vecEnd; + } + + trace_t tr; + AI_TraceHull( vecFrom, nextPos, mins, maxs, MASK_SOLID, this, COLLISION_GROUP_NONE, &tr ); + + if (tr.startsolid || tr.fraction < 1.0) + { + CBaseEntity *pEntity = tr.m_pEnt; + + // If we hit the target we are good to go! + if (pEntity == pTarget) + { + return 0; + } + +#ifdef _THROWDEBUG + NDebugOverlay::Line( vecFrom, nextPos, 255, 0, 0, true, 1.0 ); +#endif + // ---------------------------------------------------------- + // If blocked by an npc remember + // ---------------------------------------------------------- + *pBlocker = pEntity; + + // Return distance sucessfully traveled before block encountered + return ((tr.endpos - vecStart).Length()); + } +#ifdef _THROWDEBUG + else + { + NDebugOverlay::Line( vecFrom, nextPos, 255, 255, 255, true, 1.0 ); + } +#endif + + + rawJumpVel = rawJumpVel - gravity * timeStep; + vecFrom = nextPos; + } + return 0; +} + + + +//----------------------------------------------------------------------------- +// Purpose: Called to initialize or re-initialize the vphysics hull when the size +// of the NPC changes +//----------------------------------------------------------------------------- +void CAI_BaseNPC::SetupVPhysicsHull() +{ + if ( GetMoveType() == MOVETYPE_VPHYSICS || GetMoveType() == MOVETYPE_NONE ) + return; + + if ( VPhysicsGetObject() ) + { + // Disable collisions to get + VPhysicsGetObject()->EnableCollisions(false); + VPhysicsDestroyObject(); + } + VPhysicsInitShadow( true, false ); + IPhysicsObject *pPhysObj = VPhysicsGetObject(); + if ( pPhysObj ) + { + float mass = Studio_GetMass(GetModelPtr()); + if ( mass > 0 ) + { + pPhysObj->SetMass( mass ); + } +#if _DEBUG + else + { + DevMsg("Warning: %s has no physical mass\n", STRING(GetModelName())); + } +#endif + IPhysicsShadowController *pController = pPhysObj->GetShadowController(); + float avgsize = (WorldAlignSize().x + WorldAlignSize().y) * 0.5; + pController->SetTeleportDistance( avgsize * 0.5 ); + m_bCheckContacts = true; + } +} + + +// Check for problematic physics objects resting on this NPC. +// They can screw up his navigation, so attach a controller to +// help separate the NPC & physics when you encounter these. +ConVar ai_auto_contact_solver( "ai_auto_contact_solver", "1" ); +void CAI_BaseNPC::CheckPhysicsContacts() +{ + if ( gpGlobals->frametime <= 0.0f || !ai_auto_contact_solver.GetBool() ) + return; + + m_bCheckContacts = false; + if ( GetMoveType() == MOVETYPE_STEP && VPhysicsGetObject()) + { + IPhysicsObject *pPhysics = VPhysicsGetObject(); + IPhysicsFrictionSnapshot *pSnapshot = pPhysics->CreateFrictionSnapshot(); + CBaseEntity *pGroundEntity = GetGroundEntity(); + float heightCheck = GetAbsOrigin().z + GetHullMaxs().z; + Vector npcVel; + pPhysics->GetVelocity( &npcVel, NULL ); + CBaseEntity *pOtherEntity = NULL; + bool createSolver = false; + float solverTime = 0.0f; + while ( pSnapshot->IsValid() ) + { + IPhysicsObject *pOther = pSnapshot->GetObject(1); + pOtherEntity = static_cast(pOther->GetGameData()); + + if ( pOtherEntity && pGroundEntity != pOtherEntity ) + { + float otherMass = PhysGetEntityMass(pOtherEntity); + + if ( pOtherEntity->GetMoveType() == MOVETYPE_VPHYSICS && pOther->IsMoveable() && + otherMass < VPHYSICS_LARGE_OBJECT_MASS ) + { + Assert( !NPCPhysics_SolverExists(this, pOtherEntity) ); + m_bCheckContacts = true; + Vector vel, point; + pOther->GetVelocity( &vel, NULL ); + pSnapshot->GetContactPoint( point ); + + // compare the relative velocity + vel -= npcVel; + + // slow moving object probably won't clear itself. + // Either set ignore, or disable collisions entirely + if ( vel.LengthSqr() < 5.0f*5.0f ) + { + float topdist = fabs(point.z-heightCheck); + // 4 seconds to ignore this for nav + solverTime = 4.0f; + if ( topdist < 2.0f ) + { + // Resting on my head so disable collisions for a bit + solverTime = 0.5f; // UNDONE: Tune + if ( pOther->GetGameFlags() & FVPHYSICS_PLAYER_HELD ) + { + // player is being a monkey + solverTime = 0.25f; + } + + //Msg("Dropping %s from %s\n", pOtherEntity->GetClassname(), GetClassname() ); + createSolver = true; + break; + } + } + } + } + pSnapshot->NextFrictionData(); + } + pPhysics->DestroyFrictionSnapshot( pSnapshot ); + if ( createSolver ) + { + // turn collisions back on once we've been separated for enough time + NPCPhysics_CreateSolver( this, pOtherEntity, true, solverTime ); + pPhysics->RecheckContactPoints(); + } + } +} + +void CAI_BaseNPC::StartTouch( CBaseEntity *pOther ) +{ + BaseClass::StartTouch(pOther); + + if ( pOther->GetMoveType() == MOVETYPE_VPHYSICS ) + { + m_bCheckContacts = true; + } +} + +//----------------------------------------------------------------------------- +// Purpose: To be called instead of UTIL_SetSize, so pathfinding hull +// and actual hull agree +// Input : +// Output : +//----------------------------------------------------------------------------- +void CAI_BaseNPC::SetHullSizeNormal( bool force ) +{ + if ( m_fIsUsingSmallHull || force ) + { + UTIL_SetSize(this, GetHullMins(),GetHullMaxs()); + m_fIsUsingSmallHull = false; + if ( VPhysicsGetObject() ) + { + SetupVPhysicsHull(); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: To be called instead of UTIL_SetSize, so pathfinding hull +// and actual hull agree +// Input : +// Output : +//----------------------------------------------------------------------------- +bool CAI_BaseNPC::SetHullSizeSmall( bool force ) +{ + if ( !m_fIsUsingSmallHull || force ) + { + UTIL_SetSize(this, NAI_Hull::SmallMins(GetHullType()),NAI_Hull::SmallMaxs(GetHullType())); + m_fIsUsingSmallHull = true; + if ( VPhysicsGetObject() ) + { + SetupVPhysicsHull(); + } + } + return true; +} + +//----------------------------------------------------------------------------- +// Checks to see that the nav hull is valid for the NPC +//----------------------------------------------------------------------------- +bool CAI_BaseNPC::IsNavHullValid() const +{ + Assert( GetSolid() != SOLID_BSP ); + + Vector hullMin = GetHullMins(); + Vector hullMax = GetHullMaxs(); + Vector vecMins, vecMaxs; + if ( GetSolid() == SOLID_BBOX ) + { + vecMins = WorldAlignMins(); + vecMaxs = WorldAlignMaxs(); + } + else if ( GetSolid() == SOLID_VPHYSICS ) + { + Assert( VPhysicsGetObject() ); + const CPhysCollide *pPhysCollide = VPhysicsGetObject()->GetCollide(); + physcollision->CollideGetAABB( vecMins, vecMaxs, pPhysCollide, GetAbsOrigin(), GetAbsAngles() ); + vecMins -= GetAbsOrigin(); + vecMaxs -= GetAbsOrigin(); + } + else + { + vecMins = hullMin; + vecMaxs = hullMax; + } + + if ( (hullMin.x > vecMins.x) || (hullMax.x < vecMaxs.x) || + (hullMin.y > vecMins.y) || (hullMax.y < vecMaxs.y) || + (hullMin.z > vecMins.z) || (hullMax.z < vecMaxs.z) ) + { + return false; + } + + return true; +} + + +//========================================================= +// NPCInit - after a npc is spawned, it needs to +// be dropped into the world, checked for mobility problems, +// and put on the proper path, if any. This function does +// all of those things after the npc spawns. Any +// initialization that should take place for all npcs +// goes here. +//========================================================= +void CAI_BaseNPC::NPCInit ( void ) +{ + if (!g_pGameRules->FAllowNPCs()) + { + UTIL_Remove( this ); + return; + } + + if( IsWaitingToRappel() ) + { + // If this guy's supposed to rappel, keep him from + // falling to the ground when he spawns. + AddFlag( FL_FLY ); + } + +#ifdef _DEBUG + // Make sure that the bounding box is appropriate for the hull size... + // FIXME: We can't test vphysics objects because NPCInit occurs before VPhysics is set up + if ( GetSolid() != SOLID_VPHYSICS && !IsSolidFlagSet(FSOLID_NOT_SOLID) ) + { + if ( !IsNavHullValid() ) + { + Warning("NPC Entity %s (%d) has a bounding box which extends outside its nav box!\n", + STRING(m_iClassname), entindex() ); + } + } +#endif + + // Set fields common to all npcs + AddFlag( FL_AIMTARGET | FL_NPC ); + AddSolidFlags( FSOLID_NOT_STANDABLE ); + + m_flOriginalYaw = GetAbsAngles().y; + + SetBlocksLOS( false ); + + SetGravity(1.0); // Don't change + m_takedamage = DAMAGE_YES; + GetMotor()->SetIdealYaw( GetLocalAngles().y ); + m_iMaxHealth = m_iHealth; + m_lifeState = LIFE_ALIVE; + SetIdealState( NPC_STATE_IDLE );// Assume npc will be idle, until proven otherwise + SetIdealActivity( ACT_IDLE ); + SetActivity( ACT_IDLE ); + +#ifdef HL1_DLL + SetDeathPose( ACT_INVALID ); +#endif + + ClearCommandGoal(); + + ClearSchedule(); + GetNavigator()->ClearGoal(); + InitBoneControllers( ); // FIX: should be done in Spawn + if ( GetModelPtr() ) + { + ResetActivityIndexes(); + ResetEventIndexes(); + } + + SetHintNode( NULL ); + + m_afMemory = MEMORY_CLEAR; + + SetEnemy( NULL ); + + m_flDistTooFar = 1024.0; + SetDistLook( 2048.0 ); + + if ( HasSpawnFlags( SF_NPC_LONG_RANGE ) ) + { + m_flDistTooFar = 1e9f; + SetDistLook( 6000.0 ); + } + + // Clear conditions + m_Conditions.ClearAllBits(); + + // set eye position + SetDefaultEyeOffset(); + + // Only give weapon of allowed to have one + if (CapabilitiesGet() & bits_CAP_USE_WEAPONS) + { // Does this npc spawn with a weapon + if ( m_spawnEquipment != NULL_STRING && strcmp(STRING(m_spawnEquipment), "0")) + { + CBaseCombatWeapon *pWeapon = Weapon_Create( STRING(m_spawnEquipment) ); + if ( pWeapon ) + { + // If I have a name, make my weapon match it with "_weapon" appended + if ( GetEntityName() != NULL_STRING ) + { + pWeapon->SetName( AllocPooledString(UTIL_VarArgs("%s_weapon", STRING(GetEntityName()))) ); + } + Weapon_Equip( pWeapon ); + } + } + } + + // Robin: Removed this, since it stomps the weapon's settings, and it's stomped + // by OnUpdateShotRegulator() as soon as they finish firing the first time. + //GetShotRegulator()->SetParameters( 2, 6, 0.3f, 0.8f ); + + SetUse ( &CAI_BaseNPC::NPCUse ); + + // NOTE: Can't call NPC Init Think directly... logic changed about + // what time it is when worldspawn happens.. + + // We must put off the rest of our initialization + // until we're sure everything else has had a chance to spawn. Otherwise + // we may try to reference entities that haven't spawned yet.(sjb) + SetThink( &CAI_BaseNPC::NPCInitThink ); + SetNextThink( gpGlobals->curtime + 0.01f ); + + ForceGatherConditions(); + + // HACKHACK: set up a pre idle animation + // NOTE: Must do this before CreateVPhysics() so bone followers have the correct initial positions. + if ( HasSpawnFlags( SF_NPC_WAIT_FOR_SCRIPT ) ) + { + const char *pStartSequence = CAI_ScriptedSequence::GetSpawnPreIdleSequenceForScript( this ); + if ( pStartSequence ) + { + SetSequence( LookupSequence( pStartSequence ) ); + } + } + + CreateVPhysics(); + + if ( HasSpawnFlags( SF_NPC_START_EFFICIENT ) ) + { + SetEfficiency( AIE_EFFICIENT ); + } + + m_bFadeCorpse = ShouldFadeOnDeath(); + + m_GiveUpOnDeadEnemyTimer.Set( 0.75, 2.0 ); + + m_flTimeLastMovement = FLT_MAX; + + m_flIgnoreDangerSoundsUntil = 0; + + SetDeathPose( ACT_INVALID ); + SetDeathPoseFrame( 0 ); + + m_EnemiesSerialNumber = -1; +} + +//----------------------------------------------------------------------------- + +bool CAI_BaseNPC::CreateVPhysics() +{ + if ( IsAlive() && !VPhysicsGetObject() ) + { + SetupVPhysicsHull(); + } + return true; +} + + +//----------------------------------------------------------------------------- +// Set up the shot regulator based on the equipped weapon +//----------------------------------------------------------------------------- +void CAI_BaseNPC::OnUpdateShotRegulator( ) +{ + CBaseCombatWeapon *pWeapon = GetActiveWeapon(); + if ( !pWeapon ) + return; + + // Default values + m_ShotRegulator.SetBurstInterval( pWeapon->GetFireRate(), pWeapon->GetFireRate() ); + m_ShotRegulator.SetBurstShotCountRange( pWeapon->GetMinBurst(), pWeapon->GetMaxBurst() ); + m_ShotRegulator.SetRestInterval( pWeapon->GetMinRestTime(), pWeapon->GetMaxRestTime() ); + + // Let the behavior have a whack at it. + if ( GetRunningBehavior() ) + { + GetRunningBehavior()->OnUpdateShotRegulator(); + } +} + + +//----------------------------------------------------------------------------- +// Set up the shot regulator based on the equipped weapon +//----------------------------------------------------------------------------- +void CAI_BaseNPC::OnChangeActiveWeapon( CBaseCombatWeapon *pOldWeapon, CBaseCombatWeapon *pNewWeapon ) +{ + BaseClass::OnChangeActiveWeapon( pOldWeapon, pNewWeapon ); + + // Shot regulator code + if ( pNewWeapon ) + { + OnUpdateShotRegulator(); + m_ShotRegulator.Reset( true ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Tests to see if NPC can holster their weapon (if animation exists to holster weapon) +// Output : true if holster weapon animation exists +//----------------------------------------------------------------------------- +bool CAI_BaseNPC::CanHolsterWeapon( void ) +{ + int seq = SelectWeightedSequence( ACT_DISARM ); + return (seq >= 0); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +int CAI_BaseNPC::HolsterWeapon( void ) +{ + if ( IsWeaponHolstered() ) + return -1; + + int iHolsterGesture = FindGestureLayer( ACT_DISARM ); + if ( iHolsterGesture != -1 ) + return iHolsterGesture; + + int iLayer = AddGesture( ACT_DISARM, true ); + //iLayer = AddGesture( ACT_GESTURE_DISARM, true ); + + if (iLayer != -1) + { + // Prevent firing during the holster / unholster + float flDuration = GetLayerDuration( iLayer ); + m_ShotRegulator.FireNoEarlierThan( gpGlobals->curtime + flDuration + 0.5 ); + + if( m_iDesiredWeaponState == DESIREDWEAPONSTATE_HOLSTERED_DESTROYED ) + { + m_iDesiredWeaponState = DESIREDWEAPONSTATE_CHANGING_DESTROY; + } + else + { + m_iDesiredWeaponState = DESIREDWEAPONSTATE_CHANGING; + } + + // Make sure we don't try to reload while we're holstering + ClearCondition(COND_LOW_PRIMARY_AMMO); + ClearCondition(COND_NO_PRIMARY_AMMO); + ClearCondition(COND_NO_SECONDARY_AMMO); + } + + return iLayer; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +int CAI_BaseNPC::UnholsterWeapon( void ) +{ + if ( !IsWeaponHolstered() ) + return -1; + + int iHolsterGesture = FindGestureLayer( ACT_ARM ); + if ( iHolsterGesture != -1 ) + return iHolsterGesture; + + // Deploy the first weapon you can find + for (int i = 0; i < WeaponCount(); i++) + { + if ( GetWeapon( i )) + { + SetActiveWeapon( GetWeapon(i) ); + + int iLayer = AddGesture( ACT_ARM, true ); + //iLayer = AddGesture( ACT_GESTURE_ARM, true ); + + if (iLayer != -1) + { + // Prevent firing during the holster / unholster + float flDuration = GetLayerDuration( iLayer ); + m_ShotRegulator.FireNoEarlierThan( gpGlobals->curtime + flDuration + 0.5 ); + + m_iDesiredWeaponState = DESIREDWEAPONSTATE_CHANGING; + } + + // Refill the clip + if ( GetActiveWeapon()->UsesClipsForAmmo1() ) + { + GetActiveWeapon()->m_iClip1 = GetActiveWeapon()->GetMaxClip1(); + } + + // Make sure we don't try to reload while we're unholstering + ClearCondition(COND_LOW_PRIMARY_AMMO); + ClearCondition(COND_NO_PRIMARY_AMMO); + ClearCondition(COND_NO_SECONDARY_AMMO); + + return iLayer; + } + } + + return -1; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CAI_BaseNPC::InputHolsterWeapon( inputdata_t &inputdata ) +{ + m_iDesiredWeaponState = DESIREDWEAPONSTATE_HOLSTERED; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CAI_BaseNPC::InputHolsterAndDestroyWeapon( inputdata_t &inputdata ) +{ + m_iDesiredWeaponState = DESIREDWEAPONSTATE_HOLSTERED_DESTROYED; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CAI_BaseNPC::InputUnholsterWeapon( inputdata_t &inputdata ) +{ + m_iDesiredWeaponState = DESIREDWEAPONSTATE_UNHOLSTERED; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CAI_BaseNPC::IsWeaponHolstered( void ) +{ + if( !GetActiveWeapon() ) + return true; + + if( GetActiveWeapon()->IsEffectActive(EF_NODRAW) ) + return true; + + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CAI_BaseNPC::IsWeaponStateChanging( void ) +{ + return ( m_iDesiredWeaponState == DESIREDWEAPONSTATE_CHANGING || m_iDesiredWeaponState == DESIREDWEAPONSTATE_CHANGING_DESTROY ); +} + +//----------------------------------------------------------------------------- +// Set up the shot regulator based on the equipped weapon +//----------------------------------------------------------------------------- +void CAI_BaseNPC::OnRangeAttack1() +{ + SetLastAttackTime( gpGlobals->curtime ); + + // Houston, there is a problem! + AssertOnce( GetShotRegulator()->ShouldShoot() ); + + m_ShotRegulator.OnFiredWeapon(); + if ( m_ShotRegulator.IsInRestInterval() ) + { + OnUpdateShotRegulator(); + } + + SetNextAttack( m_ShotRegulator.NextShotTime() ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Initialze the relationship table from the keyvalues +// Input : +// Output : +//----------------------------------------------------------------------------- +void CAI_BaseNPC::InitRelationshipTable(void) +{ + AddRelationship( STRING( m_RelationshipString ), NULL ); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CAI_BaseNPC::AddRelationship( const char *pszRelationship, CBaseEntity *pActivator ) +{ + // Parse the keyvalue data + char parseString[1000]; + Q_strncpy(parseString, pszRelationship, sizeof(parseString)); + + // Look for an entity string + char *entityString = strtok(parseString," "); + while (entityString) + { + // Get the disposition + char *dispositionString = strtok(NULL," "); + Disposition_t disposition = D_NU; + if ( dispositionString ) + { + if (!stricmp(dispositionString,"D_HT")) + { + disposition = D_HT; + } + else if (!stricmp(dispositionString,"D_FR")) + { + disposition = D_FR; + } + else if (!stricmp(dispositionString,"D_LI")) + { + disposition = D_LI; + } + else if (!stricmp(dispositionString,"D_NU")) + { + disposition = D_NU; + } + else + { + disposition = D_NU; + Warning( "***ERROR***\nBad relationship type (%s) to unknown entity (%s)!\n", dispositionString,entityString ); + Assert( 0 ); + return; + } + } + else + { + Warning("Can't parse relationship info (%s)\n", pszRelationship ); + Assert(0); + return; + } + + // Get the priority + char *priorityString = strtok(NULL," "); + int priority = ( priorityString ) ? atoi(priorityString) : DEF_RELATIONSHIP_PRIORITY; + + bool bFoundEntity = false; + + // Try to get pointer to an entity of this name + CBaseEntity *entity = gEntList.FindEntityByName( NULL, entityString ); + while( entity ) + { + // make sure you catch all entities of this name. + bFoundEntity = true; + AddEntityRelationship(entity, disposition, priority ); + entity = gEntList.FindEntityByName( entity, entityString ); + } + + if( !bFoundEntity ) + { + // Need special condition for player as we can only have one + if (!stricmp("player", entityString) || !stricmp("!player", entityString)) + { + AddClassRelationship( CLASS_PLAYER, disposition, priority ); + } + // Otherwise try to create one too see if a valid classname and get class type + else + { + // HACKHACK: + CBaseEntity *pEntity = CanCreateEntityClass( entityString ) ? CreateEntityByName( entityString ) : NULL; + if (pEntity) + { + AddClassRelationship( pEntity->Classify(), disposition, priority ); + UTIL_RemoveImmediate(pEntity); + } + else + { + DevWarning( "Couldn't set relationship to unknown entity or class (%s)!\n", entityString ); + } + } + } + // Check for another entity in the list + entityString = strtok(NULL," "); + } +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CAI_BaseNPC::AddEntityRelationship( CBaseEntity *pEntity, Disposition_t nDisposition, int nPriority ) +{ +#if 0 + ForceGatherConditions(); +#endif + BaseClass::AddEntityRelationship( pEntity, nDisposition, nPriority ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CAI_BaseNPC::AddClassRelationship( Class_T nClass, Disposition_t nDisposition, int nPriority ) +{ +#if 0 + ForceGatherConditions(); +#endif + BaseClass::AddClassRelationship( nClass, nDisposition, nPriority ); +} + +//========================================================= +// NPCInitThink - Calls StartNPC. Startnpc is +// virtual, but this function cannot be +//========================================================= +void CAI_BaseNPC::NPCInitThink ( void ) +{ + // Initialize the relationship table + InitRelationshipTable(); + + StartNPC(); + + PostNPCInit(); + + if( GetSleepState() == AISS_AUTO_PVS ) + { + // This code is a bit wonky, but it makes it easier for level designers to + // select this option in Hammer. So we set a sleep flag to indicate the choice, + // and then set the sleep state to awake (normal) + AddSleepFlags( AI_SLEEP_FLAG_AUTO_PVS ); + SetSleepState( AISS_AWAKE ); + } + + if( GetSleepState() == AISS_AUTO_PVS_AFTER_PVS ) + { + AddSleepFlags( AI_SLEEP_FLAG_AUTO_PVS_AFTER_PVS ); + SetSleepState( AISS_AWAKE ); + } + + if ( GetSleepState() > AISS_AWAKE ) + { + Sleep(); + } + + m_flLastRealThinkTime = gpGlobals->curtime; +} + +//========================================================= +// StartNPC - final bit of initization before a npc +// is turned over to the AI. +//========================================================= +void CAI_BaseNPC::StartNPC( void ) +{ + // Raise npc off the floor one unit, then drop to floor + if ( (GetMoveType() != MOVETYPE_FLY) && (GetMoveType() != MOVETYPE_FLYGRAVITY) && + !(CapabilitiesGet() & bits_CAP_MOVE_FLY) && + !HasSpawnFlags( SF_NPC_FALL_TO_GROUND ) && !IsWaitingToRappel() && !GetMoveParent() ) + { + Vector origin = GetLocalOrigin(); + + if (!GetMoveProbe()->FloorPoint( origin + Vector(0, 0, 0.1), MASK_NPCSOLID, 0, -2048, &origin )) + { + Warning( "NPC %s stuck in wall--level design error at (%.2f %.2f %.2f)\n", GetClassname(), GetAbsOrigin().x, GetAbsOrigin().y, GetAbsOrigin().z ); + if ( g_pDeveloper->GetInt() > 1 ) + { + m_debugOverlays |= OVERLAY_BBOX_BIT; + } + } + + SetLocalOrigin( origin ); + } + else + { + SetGroundEntity( NULL ); + } + + if ( m_target != NULL_STRING )// this npc has a target + { + // Find the npc's initial target entity, stash it + SetGoalEnt( gEntList.FindEntityByName( NULL, m_target ) ); + + if ( !GetGoalEnt() ) + { + Warning( "ReadyNPC()--%s couldn't find target %s\n", GetClassname(), STRING(m_target)); + } + else + { + StartTargetHandling( GetGoalEnt() ); + } + } + + //SetState ( m_IdealNPCState ); + //SetActivity ( m_IdealActivity ); + + InitSquad(); + + //--------------------------------- + // + // Spread think times of simultaneously spawned NPCs so that they don't all happen at the same time + // + // Think distribution based on spawn order is: + // + // Tick offset Think time Spawn order + // 0 0 1 + // 1 0.015 13 + // 2 0.03 5 + // 3 0.045 9 + // 4 0.06 18 + // 5 0.075 3 + // 6 0.09 15 + // 7 0.105 11 + // 8 0.12 7 + // 9 0.135 17 + // 10 0.15 2 + // 11 0.165 14 + // 12 0.18 6 + // 13 0.195 19 + // 14 0.21 10 + // 15 0.225 4 + // 16 0.24 16 + // 17 0.255 12 + // 18 0.27 8 + // 19 0.285 20 + + + // If this NPC is spawning late in the game, just push through the rest of the initialization + // start thinking right now. Some spread is added to handle triggered spawns that bring + // a bunch of NPCs into the level + SetThink ( &CAI_BaseNPC::CallNPCThink ); + + if ( gm_flTimeLastSpawn != gpGlobals->curtime ) + { + gm_nSpawnedThisFrame = 0; + gm_flTimeLastSpawn = gpGlobals->curtime; + } + + static const float nextThinkTimes[20] = + { + .0, .150, .075, .225, .030, .180, .120, .270, .045, .210, .105, .255, .015, .165, .090, .240, .135, .060, .195, .285 + }; + + SetNextThink( gpGlobals->curtime + nextThinkTimes[gm_nSpawnedThisFrame % 20] ); + + gm_nSpawnedThisFrame++; + + //--------------------------------- + + m_ScriptArrivalActivity = AIN_DEF_ACTIVITY; + m_strScriptArrivalSequence = NULL_STRING; + + if ( HasSpawnFlags(SF_NPC_WAIT_FOR_SCRIPT) ) + { + SetState( NPC_STATE_IDLE ); + m_Activity = m_IdealActivity; + m_nIdealSequence = GetSequence(); + SetSchedule( SCHED_WAIT_FOR_SCRIPT ); + } +} + +//----------------------------------------------------------------------------- + +void CAI_BaseNPC::StartTargetHandling( CBaseEntity *pTargetEnt ) +{ + // set the npc up to walk a path corner path. + // !!!BUGBUG - this is a minor bit of a hack. + // JAYJAY + + // NPC will start turning towards his destination + bool bIsFlying = (GetMoveType() == MOVETYPE_FLY) || (GetMoveType() == MOVETYPE_FLYGRAVITY); + AI_NavGoal_t goal( GOALTYPE_PATHCORNER, pTargetEnt->GetAbsOrigin(), + bIsFlying ? ACT_FLY : ACT_WALK, + AIN_DEF_TOLERANCE, AIN_YAW_TO_DEST); + + SetState( NPC_STATE_IDLE ); + SetSchedule( SCHED_IDLE_WALK ); + + if ( !GetNavigator()->SetGoal( goal ) ) + { + DevWarning( 2, "Can't Create Route!\n" ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Connect my memory to the squad's +//----------------------------------------------------------------------------- +bool CAI_BaseNPC::InitSquad( void ) +{ + // ------------------------------------------------------- + // If I form squads add me to a squad + // ------------------------------------------------------- + if (!m_pSquad && ( CapabilitiesGet() & bits_CAP_SQUAD )) + { + if ( !m_SquadName ) + { + DevMsg(2, "Found %s that isn't in a squad\n",GetClassname()); + } + else + { + m_pSquad = g_AI_SquadManager.FindCreateSquad(this, m_SquadName); + } + } + + return ( m_pSquad != NULL ); +} + +//----------------------------------------------------------------------------- +// Purpose: Get the memory for this NPC +//----------------------------------------------------------------------------- +CAI_Enemies *CAI_BaseNPC::GetEnemies( void ) +{ + return m_pEnemies; +} + +//----------------------------------------------------------------------------- +// Purpose: Remove this NPC's memory +//----------------------------------------------------------------------------- +void CAI_BaseNPC::RemoveMemory( void ) +{ + delete m_pEnemies; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CAI_BaseNPC::TaskComplete( bool fIgnoreSetFailedCondition ) +{ + EndTaskOverlay(); + + // Handy thing to use for debugging + //if (IsCurSchedule(SCHED_PUT_HERE) && + // GetTask()->iTask == TASK_PUT_HERE) + //{ + // int put_breakpoint_here = 5; + //} + + if ( fIgnoreSetFailedCondition || !HasCondition(COND_TASK_FAILED) ) + { + SetTaskStatus( TASKSTATUS_COMPLETE ); + } +} + +void CAI_BaseNPC::TaskMovementComplete( void ) +{ + switch( GetTaskStatus() ) + { + case TASKSTATUS_NEW: + case TASKSTATUS_RUN_MOVE_AND_TASK: + SetTaskStatus( TASKSTATUS_RUN_TASK ); + break; + + case TASKSTATUS_RUN_MOVE: + TaskComplete(); + break; + + case TASKSTATUS_RUN_TASK: + // FIXME: find out how to safely restart movement + //Warning( "Movement completed twice!\n" ); + //Assert( 0 ); + break; + + case TASKSTATUS_COMPLETE: + break; + } + + // JAY: Put this back in. + // UNDONE: Figure out how much of the timestep was consumed by movement + // this frame and restart the movement/schedule engine if necessary + if ( m_scriptState != SCRIPT_CUSTOM_MOVE_TO_MARK ) + { + SetIdealActivity( GetStoppedActivity() ); + } + + // Advance past the last node (in case there is some event at this node) + if ( GetNavigator()->IsGoalActive() ) + { + GetNavigator()->AdvancePath(); + } + + // Now clear the path, it's done. + GetNavigator()->ClearGoal(); + + OnMovementComplete(); +} + + +int CAI_BaseNPC::TaskIsRunning( void ) +{ + if ( GetTaskStatus() != TASKSTATUS_COMPLETE && + GetTaskStatus() != TASKSTATUS_RUN_MOVE ) + return 1; + + return 0; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : +// Output : +//----------------------------------------------------------------------------- +void CAI_BaseNPC::TaskFail( AI_TaskFailureCode_t code ) +{ + EndTaskOverlay(); + + // Handy tool for debugging + //if (IsCurSchedule(SCHED_PUT_NAME_HERE)) + //{ + // int put_breakpoint_here = 5; + //} + + // If in developer mode save the fail text for debug output + if (g_pDeveloper->GetInt()) + { + m_failText = TaskFailureToString( code ); + + m_interuptSchedule = NULL; + m_failedSchedule = GetCurSchedule(); + + if (m_debugOverlays & OVERLAY_TASK_TEXT_BIT) + { + DevMsg(this, AIMF_IGNORE_SELECTED, " TaskFail -> %s\n", m_failText ); + } + + ADD_DEBUG_HISTORY( HISTORY_AI_DECISIONS, UTIL_VarArgs("%s(%d): TaskFail -> %s\n", GetDebugName(), entindex(), m_failText ) ); + + //AddTimedOverlay( fail_text, 5); + } + + m_ScheduleState.taskFailureCode = code; + SetCondition(COND_TASK_FAILED); + Forget( bits_MEMORY_TURNING ); +} + +//------------------------------------------------------------------------------ +// Purpose : Remember that this entity wasn't reachable +// Input : +// Output : +//------------------------------------------------------------------------------ +void CAI_BaseNPC::RememberUnreachable(CBaseEntity *pEntity) +{ + if ( pEntity == GetEnemy() ) + { + ForceChooseNewEnemy(); + } + + const float NPC_UNREACHABLE_TIMEOUT = 3; + // Only add to list if not already on it + for (int i=m_UnreachableEnts.Size()-1;i>=0;i--) + { + // If record already exists just update mark time + if (pEntity == m_UnreachableEnts[i].hUnreachableEnt) + { + m_UnreachableEnts[i].fExpireTime = gpGlobals->curtime + NPC_UNREACHABLE_TIMEOUT; + m_UnreachableEnts[i].vLocationWhenUnreachable = pEntity->GetAbsOrigin(); + return; + } + } + + // Add new unreachabe entity to list + int nNewIndex = m_UnreachableEnts.AddToTail(); + m_UnreachableEnts[nNewIndex].hUnreachableEnt = pEntity; + m_UnreachableEnts[nNewIndex].fExpireTime = gpGlobals->curtime + NPC_UNREACHABLE_TIMEOUT; + m_UnreachableEnts[nNewIndex].vLocationWhenUnreachable = pEntity->GetAbsOrigin(); +} + +//------------------------------------------------------------------------------ +// Purpose : Returns true is entity was remembered as unreachable. +// After a time delay reachability is checked +// Input : +// Output : +//------------------------------------------------------------------------------ +bool CAI_BaseNPC::IsUnreachable(CBaseEntity *pEntity) +{ + float UNREACHABLE_DIST_TOLERANCE_SQ = (120*120); + + // Note that it's ok to remove elements while I'm iterating + // as long as I iterate backwards and remove them using FastRemove + for (int i=m_UnreachableEnts.Size()-1;i>=0;i--) + { + // Remove any dead elements + if (m_UnreachableEnts[i].hUnreachableEnt == NULL) + { + m_UnreachableEnts.FastRemove(i); + } + else if (pEntity == m_UnreachableEnts[i].hUnreachableEnt) + { + // Test for reachablility on any elements that have timed out + if ( gpGlobals->curtime > m_UnreachableEnts[i].fExpireTime || + pEntity->GetAbsOrigin().DistToSqr(m_UnreachableEnts[i].vLocationWhenUnreachable) > UNREACHABLE_DIST_TOLERANCE_SQ) + { + m_UnreachableEnts.FastRemove(i); + return false; + } + return true; + } + } + return false; +} + +bool CAI_BaseNPC::IsValidEnemy( CBaseEntity *pEnemy ) +{ + CAI_BaseNPC *pEnemyNPC = pEnemy->MyNPCPointer(); + if ( pEnemyNPC && pEnemyNPC->CanBeAnEnemyOf( this ) == false ) + return false; + + // Test our enemy filter + if ( m_hEnemyFilter.Get()!= NULL && m_hEnemyFilter->PassesFilter( this, pEnemy ) == false ) + return false; + + return true; +} + + +bool CAI_BaseNPC::CanBeAnEnemyOf( CBaseEntity *pEnemy ) +{ + if ( GetSleepState() > AISS_WAITING_FOR_THREAT ) + return false; + + return true; +} + + +//----------------------------------------------------------------------------- +// Purpose: Picks best enemy from my list of enemies +// Prefers reachable enemies over enemies that are unreachable, +// regardless of priority. For enemies that are both reachable or +// unreachable picks by priority. If priority is the same, picks +// by distance. +// Input : +// Output : +//----------------------------------------------------------------------------- + +CBaseEntity *CAI_BaseNPC::BestEnemy( void ) +{ + AI_PROFILE_SCOPE( CAI_BaseNPC_BestEnemy ); + // TODO - may want to consider distance, attack types, back turned, etc. + + CBaseEntity* pBestEnemy = NULL; + int iBestDistSq = MAX_COORD_RANGE * MAX_COORD_RANGE;// so first visible entity will become the closest. + int iBestPriority = -1000; + bool bBestUnreachable = true; // Forces initial check + ThreeState_t fBestSeen = TRS_NONE; + ThreeState_t fBestVisible = TRS_NONE; + int iDistSq; + bool bUnreachable = false; + + AIEnemiesIter_t iter; + + DbgEnemyMsg( this, "BestEnemy() {\n" ); + + for( AI_EnemyInfo_t *pEMemory = GetEnemies()->GetFirst(&iter); pEMemory != NULL; pEMemory = GetEnemies()->GetNext(&iter) ) + { + CBaseEntity *pEnemy = pEMemory->hEnemy; + + if (!pEnemy || !pEnemy->IsAlive()) + { + if ( pEnemy ) + DbgEnemyMsg( this, " %s rejected: dead\n", pEnemy->GetDebugName() ); + continue; + } + + if ( (pEnemy->GetFlags() & FL_NOTARGET) ) + { + DbgEnemyMsg( this, " %s rejected: no target\n", pEnemy->GetDebugName() ); + continue; + } + + // UNDONE: Move relationship checks into IsValidEnemy? + Disposition_t relation = IRelationType( pEnemy ); + if ( (relation != D_HT && relation != D_FR) ) + { + DbgEnemyMsg( this, " %s rejected: no hate/fear\n", pEnemy->GetDebugName() ); + continue; + } + + if ( m_flAcceptableTimeSeenEnemy > 0.0 && pEMemory->timeLastSeen < m_flAcceptableTimeSeenEnemy ) + { + DbgEnemyMsg( this, " %s rejected: old\n", pEnemy->GetDebugName() ); + continue; + } + + if ( pEMemory->timeValidEnemy > gpGlobals->curtime ) + { + DbgEnemyMsg( this, " %s rejected: not yet valid\n", pEnemy->GetDebugName() ); + continue; + } + + // Skip enemies that have eluded me to prevent infinite loops + if ( pEMemory->bEludedMe ) + { + DbgEnemyMsg( this, " %s rejected: eluded\n", pEnemy->GetDebugName() ); + continue; + } + + // Skip enemies I fear that I've never seen. (usually seen through an enemy finder) + if ( relation == D_FR && !pEMemory->bUnforgettable && pEMemory->timeFirstSeen == AI_INVALID_TIME ) + { + DbgEnemyMsg( this, " %s rejected: feared, but never seen\n", pEnemy->GetDebugName() ); + continue; + } + + if ( !IsValidEnemy( pEnemy ) ) + { + DbgEnemyMsg( this, " %s rejected: not valid\n", pEnemy->GetDebugName() ); + continue; + } + + // establish the reachability of this enemy + bUnreachable = IsUnreachable(pEnemy); + + // If best is reachable and current is unreachable, skip the unreachable enemy regardless of priority + if (!bBestUnreachable && bUnreachable) + { + DbgEnemyMsg( this, " %s rejected: unreachable\n", pEnemy->GetDebugName() ); + continue; + } + + // If best is unreachable and current is reachable, always pick the current regardless of priority + if (bBestUnreachable && !bUnreachable) + { + DbgEnemyMsg( this, " %s accepted (1)\n", pEnemy->GetDebugName() ); + if ( pBestEnemy ) + DbgEnemyMsg( this, " (%s displaced)\n", pBestEnemy->GetDebugName() ); + + iBestPriority = IRelationPriority ( pEnemy ); + iBestDistSq = static_cast((pEnemy->GetAbsOrigin() - GetAbsOrigin() ).LengthSqr()); + pBestEnemy = pEnemy; + bBestUnreachable = bUnreachable; + fBestSeen = TRS_NONE; + fBestVisible = TRS_NONE; + } + // If both are unreachable or both are reachable, chose enemy based on priority and distance + else if ( IRelationPriority( pEnemy ) > iBestPriority ) + { + DbgEnemyMsg( this, " %s accepted\n", pEnemy->GetDebugName() ); + if ( pBestEnemy ) + DbgEnemyMsg( this, " (%s displaced due to priority, %d > %d )\n", pBestEnemy->GetDebugName(), IRelationPriority( pEnemy ), iBestPriority ); + // this entity is disliked MORE than the entity that we + // currently think is the best visible enemy. No need to do + // a distance check, just get mad at this one for now. + iBestPriority = IRelationPriority ( pEnemy ); + iBestDistSq = static_cast(( pEnemy->GetAbsOrigin() - GetAbsOrigin() ).LengthSqr()); + pBestEnemy = pEnemy; + bBestUnreachable = bUnreachable; + fBestSeen = TRS_NONE; + fBestVisible = TRS_NONE; + } + else if ( IRelationPriority( pEnemy ) == iBestPriority ) + { + // this entity is disliked just as much as the entity that + // we currently think is the best visible enemy, so we only + // get mad at it if it is closer. + iDistSq = static_cast(( pEnemy->GetAbsOrigin() - GetAbsOrigin() ).LengthSqr()); + + bool bAcceptCurrent = false; + bool bCloser = ( iDistSq < iBestDistSq ); + ThreeState_t fCurSeen = TRS_NONE; + ThreeState_t fCurVisible = TRS_NONE; + + // The following code is constructed in such a verbose manner to + // ensure the expensive calls only occur if absolutely needed + + // If current is farther, and best has previously been confirmed as seen or visible, move on + if ( !bCloser) + { + if ( fBestSeen == TRS_TRUE || fBestVisible == TRS_TRUE ) + { + DbgEnemyMsg( this, " %s rejected: current is closer and seen\n", pEnemy->GetDebugName() ); + continue; + } + } + + // If current is closer, and best has previously been confirmed as not seen and not visible, take it + if ( bCloser) + { + if ( fBestSeen == TRS_FALSE && fBestVisible == TRS_FALSE ) + { + bAcceptCurrent = true; + } + } + + if ( !bAcceptCurrent ) + { + // If current is closer, and seen, take it + if ( bCloser ) + { + fCurSeen = ( GetSenses()->DidSeeEntity( pEnemy ) ) ? TRS_TRUE : TRS_FALSE; + + bAcceptCurrent = ( fCurSeen == TRS_TRUE ); + } + } + + if ( !bAcceptCurrent ) + { + // If current is farther, and best is seen, move on + if ( !bCloser ) + { + if ( fBestSeen == TRS_NONE ) + { + fBestSeen = ( GetSenses()->DidSeeEntity( pBestEnemy ) ) ? TRS_TRUE : TRS_FALSE; + } + + if ( fBestSeen == TRS_TRUE ) + { + DbgEnemyMsg( this, " %s rejected: current is closer and seen\n", pEnemy->GetDebugName() ); + continue; + } + } + + // At this point, need to start performing expensive tests + if ( bCloser && fBestVisible == TRS_NONE ) + { + // Perform shortest FVisible + fCurVisible = ( ( EnemyDistance( pEnemy ) < GetSenses()->GetDistLook() ) && FVisible( pEnemy ) ) ? TRS_TRUE : TRS_FALSE; + + bAcceptCurrent = ( fCurVisible == TRS_TRUE ); + } + + // Alas, must do the most expensive comparison + if ( !bAcceptCurrent ) + { + if ( fBestSeen == TRS_NONE ) + { + fBestSeen = ( GetSenses()->DidSeeEntity( pBestEnemy ) ) ? TRS_TRUE : TRS_FALSE; + } + + if ( fBestVisible == TRS_NONE ) + { + fBestVisible = ( ( EnemyDistance( pBestEnemy ) < GetSenses()->GetDistLook() ) && FVisible( pBestEnemy ) ) ? TRS_TRUE : TRS_FALSE; + } + + if ( fCurSeen == TRS_NONE ) + { + fCurSeen = ( GetSenses()->DidSeeEntity( pEnemy ) ) ? TRS_TRUE : TRS_FALSE; + } + + if ( fCurVisible == TRS_NONE ) + { + fCurVisible = ( ( EnemyDistance( pEnemy ) < GetSenses()->GetDistLook() ) && FVisible( pEnemy ) ) ? TRS_TRUE : TRS_FALSE; + } + + bool bBestSeenOrVisible = ( fBestSeen == TRS_TRUE || fBestVisible == TRS_TRUE ); + bool bCurSeenOrVisible = ( fCurSeen == TRS_TRUE || fCurVisible == TRS_TRUE ); + + if ( !bCloser) + { + if ( bBestSeenOrVisible ) + { + DbgEnemyMsg( this, " %s rejected: current is closer and seen\n", pEnemy->GetDebugName() ); + continue; + } + else if ( !bCurSeenOrVisible ) + { + DbgEnemyMsg( this, " %s rejected: current is closer and neither is seen\n", pEnemy->GetDebugName() ); + continue; + } + } + else // Closer + { + if ( !bCurSeenOrVisible && bBestSeenOrVisible ) + { + DbgEnemyMsg( this, " %s rejected: current is father but seen\n", pEnemy->GetDebugName() ); + continue; + } + } + } + } + + DbgEnemyMsg( this, " %s accepted\n", pEnemy->GetDebugName() ); + if ( pBestEnemy ) + DbgEnemyMsg( this, " (%s displaced due to distance/visibility)\n", pBestEnemy->GetDebugName() ); + fBestSeen = fCurSeen; + fBestVisible = fCurVisible; + iBestDistSq = iDistSq; + iBestPriority = IRelationPriority ( pEnemy ); + pBestEnemy = pEnemy; + bBestUnreachable = bUnreachable; + } + else + DbgEnemyMsg( this, " %s rejected: lower priority\n", pEnemy->GetDebugName() ); + } + + DbgEnemyMsg( this, "} == %s\n", pBestEnemy->GetDebugName() ); + + return pBestEnemy; +} + +//----------------------------------------------------------------------------- +// Purpose: Given a node returns the appropriate reload activity +// Input : +// Output : +//----------------------------------------------------------------------------- +Activity CAI_BaseNPC::GetReloadActivity( CAI_Hint* pHint ) +{ + Activity nReloadActivity = ACT_RELOAD; + + if (pHint && GetEnemy()!=NULL) + { + switch (pHint->HintType()) + { + case HINT_TACTICAL_COVER_LOW: + case HINT_TACTICAL_COVER_MED: + { + if (SelectWeightedSequence( ACT_RELOAD_LOW ) != ACTIVITY_NOT_AVAILABLE) + { + Vector vEyePos = GetAbsOrigin() + EyeOffset(ACT_RELOAD_LOW); + // Check if this location will block the threat's line of sight to me + trace_t tr; + AI_TraceLOS( vEyePos, GetEnemy()->EyePosition(), this, &tr ); + if (tr.fraction != 1.0) + { + nReloadActivity = ACT_RELOAD_LOW; + } + } + break; + } + default: + break; + } + } + return nReloadActivity; +} + +//----------------------------------------------------------------------------- +// Purpose: Given a node returns the appropriate cover activity +// Input : +// Output : +//----------------------------------------------------------------------------- +Activity CAI_BaseNPC::GetCoverActivity( CAI_Hint *pHint ) +{ + Activity nCoverActivity = ACT_INVALID; + + // --------------------------------------------------------------- + // Check if hint node specifies different cover type + // --------------------------------------------------------------- + if (pHint) + { + switch (pHint->HintType()) + { + case HINT_TACTICAL_COVER_MED: + { + nCoverActivity = ACT_COVER_MED; + break; + } + case HINT_TACTICAL_COVER_LOW: + { + nCoverActivity = ACT_COVER_LOW; + break; + } + default: + break; + } + } + + if ( nCoverActivity == ACT_INVALID ) + nCoverActivity = ACT_COVER; + + return nCoverActivity; +} + +//========================================================= +// CalcIdealYaw - gets a yaw value for the caller that would +// face the supplied vector. Value is stuffed into the npc's +// ideal_yaw +//========================================================= +float CAI_BaseNPC::CalcIdealYaw( const Vector &vecTarget ) +{ + Vector vecProjection; + + // strafing npc needs to face 90 degrees away from its goal + if ( GetNavigator()->GetMovementActivity() == ACT_STRAFE_LEFT ) + { + vecProjection.x = -vecTarget.y; + vecProjection.y = vecTarget.x; + + return UTIL_VecToYaw( vecProjection - GetLocalOrigin() ); + } + else if ( GetNavigator()->GetMovementActivity() == ACT_STRAFE_RIGHT ) + { + vecProjection.x = vecTarget.y; + vecProjection.y = vecTarget.x; + + return UTIL_VecToYaw( vecProjection - GetLocalOrigin() ); + } + else + { + return UTIL_VecToYaw ( vecTarget - GetLocalOrigin() ); + } +} + +//========================================================= +// SetEyePosition +// +// queries the npc's model for $eyeposition and copies +// that vector to the npc's m_vDefaultEyeOffset and m_vecViewOffset +// +//========================================================= +void CAI_BaseNPC::SetDefaultEyeOffset ( void ) +{ + if ( GetModelPtr() ) + { + GetEyePosition( GetModelPtr(), m_vDefaultEyeOffset ); + + if ( m_vDefaultEyeOffset == vec3_origin ) + { + if ( Classify() != CLASS_NONE ) + { + DevMsg( "WARNING: %s(%s) has no eye offset in .qc!\n", GetClassname(), STRING(GetModelName()) ); + } + VectorAdd( WorldAlignMins(), WorldAlignMaxs(), m_vDefaultEyeOffset ); + m_vDefaultEyeOffset *= 0.75; + } + } + else + m_vDefaultEyeOffset = vec3_origin; + + SetViewOffset( m_vDefaultEyeOffset ); + +} + +//------------------------------------------------------------------------------ +// Purpose : Returns eye offset for an NPC for the given activity +// Input : +// Output : +//------------------------------------------------------------------------------ +Vector CAI_BaseNPC::EyeOffset( Activity nActivity ) +{ + if ( CapabilitiesGet() & bits_CAP_DUCK ) + { + if ( IsCrouchedActivity( nActivity ) ) + return GetCrouchEyeOffset(); + } + + // if the hint doesn't tell anything, assume current state + if ( IsCrouching() ) + return GetCrouchEyeOffset(); + + return m_vDefaultEyeOffset; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : Vector +//----------------------------------------------------------------------------- +Vector CAI_BaseNPC::EyePosition( void ) +{ + if ( IsCrouching() ) + return GetAbsOrigin() + GetCrouchEyeOffset(); + + return BaseClass::EyePosition(); +} + +//------------------------------------------------------------------------------ +// Purpose : +// Input : +// Output : +//------------------------------------------------------------------------------ +void CAI_BaseNPC::HandleAnimEvent( animevent_t *pEvent ) +{ + // UNDONE: Share this code into CBaseAnimating as appropriate? + switch( pEvent->event ) + { + case SCRIPT_EVENT_DEAD: + if ( m_NPCState == NPC_STATE_SCRIPT ) + { + m_lifeState = LIFE_DYING; + // Kill me now! (and fade out when CineCleanup() is called) +#if _DEBUG + DevMsg( 2, "Death event: %s\n", GetClassname() ); +#endif + m_iHealth = 0; + } +#if _DEBUG + else + DevWarning( 2, "INVALID death event:%s\n", GetClassname() ); +#endif + break; + case SCRIPT_EVENT_NOT_DEAD: + if ( m_NPCState == NPC_STATE_SCRIPT ) + { + m_lifeState = LIFE_ALIVE; + // This is for life/death sequences where the player can determine whether a character is dead or alive after the script + m_iHealth = m_iMaxHealth; + } + break; + + case SCRIPT_EVENT_SOUND: // Play a named wave file + { + EmitSound( pEvent->options ); + } + break; + + case SCRIPT_EVENT_SOUND_VOICE: + { + EmitSound( pEvent->options ); + } + break; + + case SCRIPT_EVENT_SENTENCE_RND1: // Play a named sentence group 33% of the time + if (random->RandomInt(0,2) == 0) + break; + // fall through... + case SCRIPT_EVENT_SENTENCE: // Play a named sentence group + SENTENCEG_PlayRndSz( edict(), pEvent->options, 1.0, SNDLVL_TALKING, 0, 100 ); + break; + + case SCRIPT_EVENT_FIREEVENT: + { + // + // Fire a script event. The number of the script event to fire is in the options string. + // + if ( m_hCine != NULL ) + { + m_hCine->FireScriptEvent( atoi( pEvent->options ) ); + } + else + { + // FIXME: look so see if it's playing a vcd and fire those instead + // AssertOnce( 0 ); + } + break; + } + case SCRIPT_EVENT_FIRE_INPUT: + { + variant_t emptyVariant; + this->AcceptInput( pEvent->options, this, this, emptyVariant, 0 ); + break; + } + + case SCRIPT_EVENT_NOINTERRUPT: // Can't be interrupted from now on + if ( m_hCine ) + m_hCine->AllowInterrupt( false ); + break; + + case SCRIPT_EVENT_CANINTERRUPT: // OK to interrupt now + if ( m_hCine ) + m_hCine->AllowInterrupt( true ); + break; + +#if 0 + case SCRIPT_EVENT_INAIR: // Don't engine->DropToFloor() + case SCRIPT_EVENT_ENDANIMATION: // Set ending animation sequence to + break; +#endif + case SCRIPT_EVENT_BODYGROUPON: + case SCRIPT_EVENT_BODYGROUPOFF: + case SCRIPT_EVENT_BODYGROUPTEMP: + DevMsg( "Bodygroup!\n" ); + break; + + case AE_NPC_ATTACK_BROADCAST: + break; + + case NPC_EVENT_BODYDROP_HEAVY: + if ( GetFlags() & FL_ONGROUND ) + { + EmitSound( "AI_BaseNPC.BodyDrop_Heavy" ); + } + break; + + case NPC_EVENT_BODYDROP_LIGHT: + if ( GetFlags() & FL_ONGROUND ) + { + EmitSound( "AI_BaseNPC.BodyDrop_Light" ); + } + break; + + case NPC_EVENT_SWISHSOUND: + { + // NO NPC may use this anim event unless that npc's precache precaches this sound!!! + EmitSound( "AI_BaseNPC.SwishSound" ); + break; + } + + + case NPC_EVENT_180TURN: + { + //DevMsg( "Turned!\n" ); + SetIdealActivity( ACT_IDLE ); + Forget( bits_MEMORY_TURNING ); + SetBoneController( 0, GetLocalAngles().y ); + AddEffects( EF_NOINTERP ); + break; + } + + case NPC_EVENT_ITEM_PICKUP: + { + CBaseEntity *pPickup = NULL; + + // + // Figure out what we're supposed to pick up. + // + if ( pEvent->options && strlen( pEvent->options ) > 0 ) + { + // Pick up the weapon or item that was specified in the anim event. + pPickup = gEntList.FindEntityGenericNearest( pEvent->options, GetAbsOrigin(), 256, this ); + } + else + { + // Pick up the weapon or item that was found earlier and cached in our target pointer. + pPickup = GetTarget(); + } + + // Make sure we found something to pick up. + if ( !pPickup ) + { + TaskFail("Item no longer available!\n"); + break; + } + + // Make sure the item hasn't moved. + float flDist = ( pPickup->WorldSpaceCenter() - GetAbsOrigin() ).Length2D(); + if ( flDist > ITEM_PICKUP_TOLERANCE ) + { + TaskFail("Item has moved!\n"); + break; + } + + CBaseCombatWeapon *pWeapon = dynamic_cast( pPickup ); + if ( pWeapon ) + { + // Picking up a weapon. + CBaseCombatCharacter *pOwner = pWeapon->GetOwner(); + if ( pOwner ) + { + TaskFail( "Weapon in use by someone else" ); + } + else if ( !pWeapon ) + { + TaskFail( "Weapon doesn't exist" ); + } + else if (!Weapon_CanUse( pWeapon )) + { + TaskFail( "Can't use this weapon type" ); + } + else + { + PickupWeapon( pWeapon ); + TaskComplete(); + break; + } + } + else + { + // Picking up an item. + PickupItem( pPickup ); + TaskComplete(); + } + + break; + } + + case NPC_EVENT_WEAPON_SET_SEQUENCE_NUMBER: + { + CBaseCombatWeapon *pWeapon = GetActiveWeapon(); + if ((pWeapon) && (pEvent->options)) + { + int nSequence = atoi(pEvent->options); + if (nSequence != -1) + { + pWeapon->ResetSequence(nSequence); + } + } + break; + } + + case NPC_EVENT_WEAPON_SET_SEQUENCE_NAME: + { + CBaseCombatWeapon *pWeapon = GetActiveWeapon(); + if ((pWeapon) && (pEvent->options)) + { + int nSequence = pWeapon->LookupSequence(pEvent->options); + if (nSequence != -1) + { + pWeapon->ResetSequence(nSequence); + } + } + break; + } + + case NPC_EVENT_WEAPON_SET_ACTIVITY: + { + CBaseCombatWeapon *pWeapon = GetActiveWeapon(); + if ((pWeapon) && (pEvent->options)) + { + Activity act = (Activity)pWeapon->LookupActivity(pEvent->options); + if (act != ACT_INVALID) + { + // FIXME: where should the duration come from? normally it would come from the current sequence + Weapon_SetActivity(act, 0); + } + } + break; + } + + case NPC_EVENT_WEAPON_DROP: + { + // + // Drop our active weapon (or throw it at the specified target entity). + // + CBaseEntity *pTarget = NULL; + if (pEvent->options) + { + pTarget = gEntList.FindEntityGeneric(NULL, pEvent->options, this); + } + + if (pTarget) + { + Vector vecTargetPos = pTarget->WorldSpaceCenter(); + Weapon_Drop(GetActiveWeapon(), &vecTargetPos); + } + else + { + Weapon_Drop(GetActiveWeapon()); + } + + break; + } + + case EVENT_WEAPON_RELOAD: + { + if ( GetActiveWeapon() ) + { + GetActiveWeapon()->WeaponSound( RELOAD_NPC ); + GetActiveWeapon()->m_iClip1 = GetActiveWeapon()->GetMaxClip1(); + ClearCondition(COND_LOW_PRIMARY_AMMO); + ClearCondition(COND_NO_PRIMARY_AMMO); + ClearCondition(COND_NO_SECONDARY_AMMO); + } + break; + } + + case EVENT_WEAPON_RELOAD_SOUND: + { + if ( GetActiveWeapon() ) + { + GetActiveWeapon()->WeaponSound( RELOAD_NPC ); + } + break; + } + + case EVENT_WEAPON_RELOAD_FILL_CLIP: + { + if ( GetActiveWeapon() ) + { + GetActiveWeapon()->m_iClip1 = GetActiveWeapon()->GetMaxClip1(); + ClearCondition(COND_LOW_PRIMARY_AMMO); + ClearCondition(COND_NO_PRIMARY_AMMO); + ClearCondition(COND_NO_SECONDARY_AMMO); + } + break; + } + + case NPC_EVENT_LEFTFOOT: + case NPC_EVENT_RIGHTFOOT: + // For right now, do nothing. All functionality for this lives in individual npcs. + break; + + case NPC_EVENT_OPEN_DOOR: + { + CBasePropDoor *pDoor = (CBasePropDoor *)(CBaseEntity *)GetNavigator()->GetPath()->GetCurWaypoint()->GetEHandleData(); + if (pDoor != NULL) + { + OpenPropDoorNow( pDoor ); + } + + break; + } + + default: + if ((pEvent->type & AE_TYPE_NEWEVENTSYSTEM) && (pEvent->type & AE_TYPE_SERVER)) + { + if (pEvent->event == AE_NPC_HOLSTER) + { + // Cache off the weapon. + CBaseCombatWeapon *pWeapon = GetActiveWeapon(); + + Assert( pWeapon != NULL ); + + GetActiveWeapon()->Holster(); + SetActiveWeapon( NULL ); + + //Force the NPC to recalculate it's arrival activity since it'll most likely be wrong now that we don't have a weapon out. + GetNavigator()->SetArrivalSequence( ACT_INVALID ); + + if ( m_iDesiredWeaponState == DESIREDWEAPONSTATE_CHANGING_DESTROY ) + { + // Get rid of it! + UTIL_Remove( pWeapon ); + } + + if ( m_iDesiredWeaponState != DESIREDWEAPONSTATE_IGNORE ) + { + m_iDesiredWeaponState = DESIREDWEAPONSTATE_IGNORE; + m_Activity = ACT_RESET; + } + + return; + } + else if (pEvent->event == AE_NPC_DRAW) + { + if (GetActiveWeapon()) + { + GetActiveWeapon()->Deploy(); + + //Force the NPC to recalculate it's arrival activity since it'll most likely be wrong now. + GetNavigator()->SetArrivalSequence( ACT_INVALID ); + + if ( m_iDesiredWeaponState != DESIREDWEAPONSTATE_IGNORE ) + { + m_iDesiredWeaponState = DESIREDWEAPONSTATE_IGNORE; + m_Activity = ACT_RESET; + } + } + return; + } + else if ( pEvent->event == AE_NPC_BODYDROP_HEAVY ) + { + if ( GetFlags() & FL_ONGROUND ) + { + EmitSound( "AI_BaseNPC.BodyDrop_Heavy" ); + } + return; + } + else if ( pEvent->event == AE_NPC_LEFTFOOT || pEvent->event == AE_NPC_RIGHTFOOT ) + { + return; + } + else if ( pEvent->event == AE_NPC_RAGDOLL ) + { + // Convert to ragdoll immediately + BecomeRagdollOnClient( vec3_origin ); + return; + } + else if ( pEvent->event == AE_NPC_ADDGESTURE ) + { + Activity act = ( Activity )LookupActivity( pEvent->options ); + if (act != ACT_INVALID) + { + act = TranslateActivity( act ); + if (act != ACT_INVALID) + { + AddGesture( act ); + } + } + return; + } + else if ( pEvent->event == AE_NPC_RESTARTGESTURE ) + { + Activity act = ( Activity )LookupActivity( pEvent->options ); + if (act != ACT_INVALID) + { + act = TranslateActivity( act ); + if (act != ACT_INVALID) + { + RestartGesture( act ); + } + } + return; + } + else if ( pEvent->event == AE_NPC_WEAPON_DROP ) + { + // Drop our active weapon (or throw it at the specified target entity). + CBaseEntity *pTarget = NULL; + if (pEvent->options) + { + pTarget = gEntList.FindEntityGeneric(NULL, pEvent->options, this); + } + + if (pTarget) + { + Vector vecTargetPos = pTarget->WorldSpaceCenter(); + Weapon_Drop(GetActiveWeapon(), &vecTargetPos); + } + else + { + Weapon_Drop(GetActiveWeapon()); + } + return; + } + else if ( pEvent->event == AE_NPC_WEAPON_SET_ACTIVITY ) + { + CBaseCombatWeapon *pWeapon = GetActiveWeapon(); + if ((pWeapon) && (pEvent->options)) + { + Activity act = (Activity)pWeapon->LookupActivity(pEvent->options); + if (act == ACT_INVALID) + { + // Try and translate it + act = Weapon_TranslateActivity( (Activity)CAI_BaseNPC::GetActivityID(pEvent->options), false ); + } + + if (act != ACT_INVALID) + { + // FIXME: where should the duration come from? normally it would come from the current sequence + Weapon_SetActivity(act, 0); + } + } + return; + } + else if ( pEvent->event == AE_NPC_SET_INTERACTION_CANTDIE ) + { + SetInteractionCantDie( (atoi(pEvent->options) != 0) ); + return; + } + else if ( pEvent->event == AE_NPC_HURT_INTERACTION_PARTNER ) + { + // If we're currently interacting with an enemy, hurt them/me + if ( m_hInteractionPartner ) + { + CAI_BaseNPC *pTarget = NULL; + CAI_BaseNPC *pAttacker = NULL; + if ( pEvent->options ) + { + char szEventOptions[128]; + Q_strncpy( szEventOptions, pEvent->options, sizeof(szEventOptions) ); + char *pszParam = strtok( szEventOptions, " " ); + if ( pszParam ) + { + if ( !Q_strncmp( pszParam, "ME", 2 ) ) + { + pTarget = this; + pAttacker = m_hInteractionPartner; + } + else if ( !Q_strncmp( pszParam, "THEM", 4 ) ) + { + pAttacker = this; + pTarget = m_hInteractionPartner; + } + + pszParam = strtok(NULL," "); + if ( pAttacker && pTarget && pszParam ) + { + int iDamage = atoi( pszParam ); + if ( iDamage ) + { + // We've got a target, and damage. Now hurt them. + CTakeDamageInfo info; + info.SetDamage( iDamage ); + info.SetAttacker( pAttacker ); + info.SetInflictor( pAttacker ); + info.SetDamageType( DMG_GENERIC | DMG_PREVENT_PHYSICS_FORCE ); + pTarget->TakeDamage( info ); + return; + } + } + } + } + + // Bad data. Explain how to use this anim event. + const char *pName = EventList_NameForIndex( pEvent->event ); + DevWarning( 1, "Bad %s format. Should be: { AE_NPC_HURT_INTERACTION_PARTNER \" \" }\n", pName ); + return; + } + + DevWarning( "%s received AE_NPC_HURT_INTERACTION_PARTNER anim event, but it's not interacting with anything.\n", GetDebugName() ); + return; + } + } + + // FIXME: why doesn't this code pass unhandled events down to its parent? + // Came from my weapon? + //Adrian I'll clean this up once the old event system is phased out. + if ( pEvent->pSource != this || ( pEvent->type & AE_TYPE_NEWEVENTSYSTEM && pEvent->type & AE_TYPE_WEAPON ) || (pEvent->event >= EVENT_WEAPON && pEvent->event <= EVENT_WEAPON_LAST) ) + { + Weapon_HandleAnimEvent( pEvent ); + } + else + { + BaseClass::HandleAnimEvent( pEvent ); + } + break; + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Override base class to add display of routes +// Input : +// Output : Current text offset from the top +//----------------------------------------------------------------------------- +void CAI_BaseNPC::DrawDebugGeometryOverlays(void) +{ + // Handy for debug + //NDebugOverlay::Cross3D(EyePosition(),Vector(-2,-2,-2),Vector(2,2,2),0,255,0,true); + + // ------------------------------ + // Remove me if requested + // ------------------------------ + if (m_debugOverlays & OVERLAY_NPC_ZAP_BIT) + { + VacateStrategySlot(); + Weapon_Drop( GetActiveWeapon() ); + m_iHealth = 0; + SetThink( &CAI_BaseNPC::SUB_Remove ); + } + + // ------------------------------ + // properly kill an NPC. + // ------------------------------ + if (m_debugOverlays & OVERLAY_NPC_KILL_BIT) + { + CTakeDamageInfo info; + + info.SetDamage( m_iHealth ); + info.SetAttacker( this ); + info.SetInflictor( ( AI_IsSinglePlayer() ) ? (CBaseEntity *)AI_GetSinglePlayer() : (CBaseEntity *)this ); + info.SetDamageType( DMG_GENERIC ); + + m_debugOverlays &= ~OVERLAY_NPC_KILL_BIT; + TakeDamage( info ); + return; + } + + + // ------------------------------ + // Draw route if requested + // ------------------------------ + if ((m_debugOverlays & OVERLAY_NPC_ROUTE_BIT)) + { + GetNavigator()->DrawDebugRouteOverlay(); + if ( IsMoving() ) + { + float yaw = GetMotor()->GetIdealYaw(); + Vector vecYaw = UTIL_YawToVector(yaw); + NDebugOverlay::Line(WorldSpaceCenter(),WorldSpaceCenter() + vecYaw * GetHullWidth() * .5,255,255,255,true,0.0); + } + } + + if (!(CAI_BaseNPC::m_nDebugBits & bits_debugDisableAI) && (IsCurSchedule(SCHED_FORCED_GO) || IsCurSchedule(SCHED_FORCED_GO_RUN))) + { + NDebugOverlay::Box(m_vecLastPosition, Vector(-5,-5,-5),Vector(5,5,5), 255, 0, 255, 0, 0); + NDebugOverlay::HorzArrow( GetAbsOrigin(), m_vecLastPosition, 16, 255, 0, 255, 64, true, 0 ); + } + + // ------------------------------ + // Draw red box around if selected + // ------------------------------ + if ((m_debugOverlays & OVERLAY_NPC_SELECTED_BIT) && !ai_no_select_box.GetBool()) + { + NDebugOverlay::EntityBounds(this, 255, 0, 0, 20, 0); + } + + // ------------------------------ + // Draw nearest node if selected + // ------------------------------ + if ((m_debugOverlays & OVERLAY_NPC_NEAREST_BIT)) + { + int iNodeID = GetPathfinder()->NearestNodeToNPC(); + if (iNodeID != NO_NODE) + { + NDebugOverlay::Box(GetNavigator()->GetNetwork()->AccessNodes()[iNodeID]->GetPosition(GetHullType()), Vector(-10,-10,-10),Vector(10,10,10), 255, 255, 255, 0, 0); + } + } + + // ------------------------------ + // Draw viewcone if selected + // ------------------------------ + if ((m_debugOverlays & OVERLAY_NPC_VIEWCONE_BIT)) + { + float flViewRange = acos(m_flFieldOfView); + Vector vEyeDir = EyeDirection2D( ); + Vector vLeftDir, vRightDir; + float fSin, fCos; + SinCos( flViewRange, &fSin, &fCos ); + + vLeftDir.x = vEyeDir.x * fCos - vEyeDir.y * fSin; + vLeftDir.y = vEyeDir.x * fSin + vEyeDir.y * fCos; + vLeftDir.z = vEyeDir.z; + fSin = sin(-flViewRange); + fCos = cos(-flViewRange); + vRightDir.x = vEyeDir.x * fCos - vEyeDir.y * fSin; + vRightDir.y = vEyeDir.x * fSin + vEyeDir.y * fCos; + vRightDir.z = vEyeDir.z; + + // Visualize it + NDebugOverlay::VertArrow( EyePosition(), EyePosition() + ( vLeftDir * 200 ), 64, 255, 0, 0, 50, false, 0 ); + NDebugOverlay::VertArrow( EyePosition(), EyePosition() + ( vRightDir * 200 ), 64, 255, 0, 0, 50, false, 0 ); + NDebugOverlay::VertArrow( EyePosition(), EyePosition() + ( vEyeDir * 100 ), 8, 0, 255, 0, 50, false, 0 ); + NDebugOverlay::Box(EyePosition(), -Vector(2,2,2), Vector(2,2,2), 0, 255, 0, 128, 0 ); + } + + // ---------------------------------------------- + // Draw the relationships for this NPC to others + // ---------------------------------------------- + if ( m_debugOverlays & OVERLAY_NPC_RELATION_BIT ) + { + // Show the relationships to entities around us + int r = 0; + int g = 0; + int b = 0; + + int nRelationship; + CAI_BaseNPC **ppAIs = g_AI_Manager.AccessAIs(); + + // Rate all NPCs + for ( int i = 0; i < g_AI_Manager.NumAIs(); i++ ) + { + if ( ppAIs[i] == NULL || ppAIs[i] == this ) + continue; + + // Get our relation to the target + nRelationship = IRelationType( ppAIs[i] ); + + // Get the color for the arrow + UTIL_GetDebugColorForRelationship( nRelationship, r, g, b ); + + // Draw an arrow + NDebugOverlay::HorzArrow( GetAbsOrigin(), ppAIs[i]->GetAbsOrigin(), 16, r, g, b, 64, true, 0.0f ); + } + + // Also include all players + for ( int i = 1; i <= gpGlobals->maxClients; i++ ) + { + CBasePlayer *pPlayer = UTIL_PlayerByIndex( i ); + if ( pPlayer == NULL ) + continue; + + // Get our relation to the target + nRelationship = IRelationType( pPlayer ); + + // Get the color for the arrow + UTIL_GetDebugColorForRelationship( nRelationship, r, g, b ); + + // Draw an arrow + NDebugOverlay::HorzArrow( GetAbsOrigin(), pPlayer->GetAbsOrigin(), 16, r, g, b, 64, true, 0.0f ); + } + } + + // ------------------------------ + // Draw enemies if selected + // ------------------------------ + if ((m_debugOverlays & OVERLAY_NPC_ENEMIES_BIT)) + { + AIEnemiesIter_t iter; + for( AI_EnemyInfo_t *eMemory = GetEnemies()->GetFirst(&iter); eMemory != NULL; eMemory = GetEnemies()->GetNext(&iter) ) + { + if (eMemory->hEnemy) + { + CBaseCombatCharacter *npcEnemy = (eMemory->hEnemy)->MyCombatCharacterPointer(); + if (npcEnemy) + { + int r,g,b; + char debugText[255]; + debugText[0] = 0; + + if (npcEnemy == GetEnemy()) + { + Q_strncat(debugText,"Current Enemy", sizeof( debugText ), COPY_ALL_CHARACTERS ); + } + else if (npcEnemy == GetTarget()) + { + Q_strncat(debugText,"Current Target", sizeof( debugText ), COPY_ALL_CHARACTERS ); + } + else + { + Q_strncat(debugText,"Other Memory", sizeof( debugText ), COPY_ALL_CHARACTERS ); + } + if (IsUnreachable(npcEnemy)) + { + Q_strncat(debugText," (Unreachable)", sizeof( debugText ), COPY_ALL_CHARACTERS ); + } + if (eMemory->bEludedMe) + { + Q_strncat(debugText," (Eluded)", sizeof( debugText ), COPY_ALL_CHARACTERS ); + } + // Unreachable enemy drawn in green + if (IsUnreachable(npcEnemy)) + { + r = 0; + g = 255; + b = 0; + } + // Eluded enemy drawn in blue + else if (eMemory->bEludedMe) + { + r = 0; + g = 0; + b = 255; + } + // Current enemy drawn in red + else if (npcEnemy == GetEnemy()) + { + r = 255; + g = 0; + b = 0; + } + // Current traget drawn in magenta + else if (npcEnemy == GetTarget()) + { + r = 255; + g = 0; + b = 255; + } + // All other enemies drawn in pink + else + { + r = 255; + g = 100; + b = 100; + } + + + Vector drawPos = eMemory->vLastKnownLocation; + NDebugOverlay::Text( drawPos, debugText, false, 0.0 ); + + // If has a line on the player draw cross slightly in front so player can see + if (npcEnemy->IsPlayer() && + (eMemory->vLastKnownLocation - npcEnemy->GetAbsOrigin()).Length()<10 ) + { + Vector vEnemyFacing = npcEnemy->BodyDirection2D( ); + Vector eyePos = npcEnemy->EyePosition() + vEnemyFacing*10.0; + Vector upVec = Vector(0,0,2); + Vector sideVec; + CrossProduct( vEnemyFacing, upVec, sideVec); + NDebugOverlay::Line(eyePos+sideVec+upVec, eyePos-sideVec-upVec, r, g, b, false, 0); + NDebugOverlay::Line(eyePos+sideVec-upVec, eyePos-sideVec+upVec, r, g, b, false, 0); + + NDebugOverlay::Text( eyePos, debugText, false, 0.0 ); + } + else + { + NDebugOverlay::Cross3D(drawPos,NAI_Hull::Mins(npcEnemy->GetHullType()),NAI_Hull::Maxs(npcEnemy->GetHullType()),r,g,b,false,0); + } + } + } + } + } + + // ---------------------------------------------- + // Draw line to target and enemy entity if exist + // ---------------------------------------------- + if ((m_debugOverlays & OVERLAY_NPC_FOCUS_BIT)) + { + if (GetEnemy() != NULL) + { + NDebugOverlay::Line(EyePosition(),GetEnemy()->EyePosition(),255,0,0,true,0.0); + } + if (GetTarget() != NULL) + { + NDebugOverlay::Line(EyePosition(),GetTarget()->EyePosition(),0,0,255,true,0.0); + } + } + + + GetPathfinder()->DrawDebugGeometryOverlays(m_debugOverlays); + + CBaseEntity::DrawDebugGeometryOverlays(); +} + +//----------------------------------------------------------------------------- +// Purpose: Draw any debug text overlays +// Input : +// Output : Current text offset from the top +//----------------------------------------------------------------------------- +int CAI_BaseNPC::DrawDebugTextOverlays(void) +{ + int text_offset = 0; + + // --------------------- + // Print Baseclass text + // --------------------- + text_offset = BaseClass::DrawDebugTextOverlays(); + + if (m_debugOverlays & OVERLAY_NPC_SQUAD_BIT) + { + // Print health + char tempstr[512]; + Q_snprintf(tempstr,sizeof(tempstr),"Health: %i",m_iHealth); + EntityText(text_offset,tempstr,0); + text_offset++; + + // Print squad name + Q_strncpy(tempstr,"Squad: ",sizeof(tempstr)); + if (m_pSquad) + { + Q_strncat(tempstr,m_pSquad->GetName(),sizeof(tempstr), COPY_ALL_CHARACTERS); + + if( m_pSquad->GetLeader() == this ) + { + Q_strncat(tempstr," (LEADER)",sizeof(tempstr), COPY_ALL_CHARACTERS); + } + + Q_strncat(tempstr,"\n",sizeof(tempstr), COPY_ALL_CHARACTERS); + } + else + { + Q_strncat(tempstr," - \n",sizeof(tempstr), COPY_ALL_CHARACTERS); + } + EntityText(text_offset,tempstr,0); + text_offset++; + + // Print enemy name + Q_strncpy(tempstr,"Enemy: ",sizeof(tempstr)); + if (GetEnemy()) + { + if (GetEnemy()->GetEntityName() != NULL_STRING) + { + Q_strncat(tempstr,STRING(GetEnemy()->GetEntityName()),sizeof(tempstr), COPY_ALL_CHARACTERS); + Q_strncat(tempstr,"\n",sizeof(tempstr), COPY_ALL_CHARACTERS); + } + else + { + Q_strncat(tempstr,STRING(GetEnemy()->m_iClassname),sizeof(tempstr), COPY_ALL_CHARACTERS); + Q_strncat(tempstr,"\n",sizeof(tempstr), COPY_ALL_CHARACTERS); + } + } + else + { + Q_strncat(tempstr," - \n",sizeof(tempstr), COPY_ALL_CHARACTERS); + } + EntityText(text_offset,tempstr,0); + text_offset++; + + // Print slot + Q_snprintf(tempstr,sizeof(tempstr),"Slot: %s \n", + SquadSlotName(m_iMySquadSlot)); + EntityText(text_offset,tempstr,0); + text_offset++; + + } + + if (m_debugOverlays & OVERLAY_TEXT_BIT) + { + char tempstr[512]; + // -------------- + // Print Health + // -------------- + Q_snprintf(tempstr,sizeof(tempstr),"Health: %i (DACC:%1.2f)",m_iHealth, GetDamageAccumulator() ); + EntityText(text_offset,tempstr,0); + text_offset++; + + // -------------- + // Print State + // -------------- + static const char *pStateNames[] = { "None", "Idle", "Alert", "Combat", "Scripted", "PlayDead", "Dead" }; + if ( static_cast(m_NPCState) < ARRAYSIZE(pStateNames) ) + { + Q_snprintf(tempstr,sizeof(tempstr),"Stat: %s, ", pStateNames[m_NPCState] ); + EntityText(text_offset,tempstr,0); + text_offset++; + } + + // ----------------- + // Start Scripting? + // ----------------- + if( IsInAScript() ) + { + Q_snprintf(tempstr,sizeof(tempstr),"STARTSCRIPTING" ); + EntityText(text_offset,tempstr,0); + text_offset++; + } + + // ----------------- + // Print MotionType + // ----------------- + int navTypeIndex = (int)GetNavType() + 1; + static const char *pMoveNames[] = { "None", "Ground", "Jump", "Fly", "Climb" }; + Assert( navTypeIndex >= 0 && navTypeIndex < static_cast(ARRAYSIZE(pMoveNames)) ); + if ( static_cast(navTypeIndex) < ARRAYSIZE(pMoveNames) ) + { + Q_snprintf(tempstr,sizeof(tempstr),"Move: %s, ", pMoveNames[navTypeIndex] ); + EntityText(text_offset,tempstr,0); + text_offset++; + } + + // -------------- + // Print Schedule + // -------------- + if ( GetCurSchedule() ) + { + CAI_BehaviorBase *pBehavior = GetRunningBehavior(); + if ( pBehavior ) + { + Q_snprintf(tempstr,sizeof(tempstr),"Behv: %s, ", pBehavior->GetName() ); + EntityText(text_offset,tempstr,0); + text_offset++; + } + + const char *pName = NULL; + pName = GetCurSchedule()->GetName(); + if ( !pName ) + { + pName = "Unknown"; + } + Q_snprintf(tempstr,sizeof(tempstr),"Schd: %s, ", pName ); + EntityText(text_offset,tempstr,0); + text_offset++; + + if (m_debugOverlays & OVERLAY_NPC_TASK_BIT) + { + for (int i = 0 ; i < GetCurSchedule()->NumTasks(); i++) + { + Q_snprintf(tempstr,sizeof(tempstr),"%s%s%s%s", + ((i==0) ? "Task:":" "), + ((i==GetScheduleCurTaskIndex()) ? "->" :" "), + TaskName(GetCurSchedule()->GetTaskList()[i].iTask), + ((i==GetScheduleCurTaskIndex()) ? "<-" :"")); + + EntityText(text_offset,tempstr,0); + text_offset++; + } + } + else + { + const Task_t *pTask = GetTask(); + if ( pTask ) + { + Q_snprintf(tempstr,sizeof(tempstr),"Task: %s (#%d), ", TaskName(pTask->iTask), GetScheduleCurTaskIndex() ); + } + else + { + Q_strncpy(tempstr,"Task: None",sizeof(tempstr)); + } + EntityText(text_offset,tempstr,0); + text_offset++; + } + } + + // -------------- + // Print Acitivity + // -------------- + if( m_Activity != ACT_INVALID && m_IdealActivity != ACT_INVALID && m_Activity != ACT_RESET) + { + Activity iActivity = TranslateActivity( m_Activity ); + + Activity iIdealActivity = Weapon_TranslateActivity( m_IdealActivity ); + iIdealActivity = NPC_TranslateActivity( iIdealActivity ); + + const char *pszActivity = GetActivityName( iActivity ); + const char *pszIdealActivity = GetActivityName( iIdealActivity ); + const char *pszRootActivity = GetActivityName( m_Activity ); + + Q_snprintf(tempstr,sizeof(tempstr),"Actv: %s (%s) [%s]\n", pszActivity, pszIdealActivity, pszRootActivity ); + } + else if (m_Activity == ACT_RESET) + { + Q_strncpy(tempstr,"Actv: RESET",sizeof(tempstr) ); + } + else + { + Q_strncpy(tempstr,"Actv: INVALID", sizeof(tempstr) ); + } + EntityText(text_offset,tempstr,0); + text_offset++; + + // + // Print all the current conditions. + // + if (m_debugOverlays & OVERLAY_NPC_CONDITIONS_BIT) + { + bool bHasConditions = false; + for (int i = 0; i < MAX_CONDITIONS; i++) + { + if (m_Conditions.GetBit(i)) + { + Q_snprintf(tempstr, sizeof(tempstr), "Cond: %s\n", ConditionName(AI_RemapToGlobal(i))); + EntityText(text_offset, tempstr, 0); + text_offset++; + bHasConditions = true; + } + } + if (!bHasConditions) + { + Q_snprintf(tempstr,sizeof(tempstr),"(no conditions)",m_iHealth); + EntityText(text_offset,tempstr,0); + text_offset++; + } + } + + if ( GetFlags() & FL_FLY ) + { + EntityText(text_offset,"HAS FL_FLY",0); + text_offset++; + } + + // -------------- + // Print Interrupte + // -------------- + if (m_interuptSchedule) + { + const char *pName = NULL; + pName = m_interuptSchedule->GetName(); + if ( !pName ) + { + pName = "Unknown"; + } + + Q_snprintf(tempstr,sizeof(tempstr),"Intr: %s (%s)\n", pName, m_interruptText ); + EntityText(text_offset,tempstr,0); + text_offset++; + } + + // -------------- + // Print Failure + // -------------- + if (m_failedSchedule) + { + const char *pName = NULL; + pName = m_failedSchedule->GetName(); + if ( !pName ) + { + pName = "Unknown"; + } + Q_snprintf(tempstr,sizeof(tempstr),"Fail: %s (%s)\n", pName,m_failText ); + EntityText(text_offset,tempstr,0); + text_offset++; + } + + + // ------------------------------- + // Print any important condtions + // ------------------------------- + if (HasCondition(COND_ENEMY_TOO_FAR)) + { + EntityText(text_offset,"Enemy too far to attack",0); + text_offset++; + } + if ( GetAbsVelocity() != vec3_origin || GetLocalAngularVelocity() != vec3_angle ) + { + char tmp[512]; + Q_snprintf( tmp, sizeof(tmp), "Vel %.1f %.1f %.1f Ang: %.1f %.1f %.1f\n", + GetAbsVelocity().x, GetAbsVelocity().y, GetAbsVelocity().z, + GetLocalAngularVelocity().x, GetLocalAngularVelocity().y, GetLocalAngularVelocity().z ); + EntityText(text_offset,tmp,0); + text_offset++; + } + + // ------------------------------- + // Print shot accuracy + // ------------------------------- + if ( m_LastShootAccuracy != -1 && ai_shot_stats.GetBool() ) + { + CFmtStr msg; + EntityText(text_offset,msg.sprintf("Cur Accuracy: %.1f", m_LastShootAccuracy),0); + text_offset++; + if ( m_TotalShots ) + { + EntityText(text_offset,msg.sprintf("Act Accuracy: %.1f", ((float)m_TotalHits/(float)m_TotalShots)*100.0),0); + text_offset++; + } + + if ( GetActiveWeapon() && GetEnemy() ) + { + Vector curSpread = GetAttackSpread(GetActiveWeapon(), GetEnemy()); + float curCone = RAD2DEG(asin(curSpread.x)) * 2; + float bias = GetSpreadBias( GetActiveWeapon(), GetEnemy()); + EntityText(text_offset,msg.sprintf("Cone %.1f, Bias %.2f", curCone, bias),0); + text_offset++; + } + } + + if ( GetGoalEnt() && GetNavigator()->GetGoalType() == GOALTYPE_PATHCORNER ) + { + Q_strncpy(tempstr,"Pathcorner/goal ent: ",sizeof(tempstr)); + if (GetGoalEnt()->GetEntityName() != NULL_STRING) + { + Q_strncat(tempstr,STRING(GetGoalEnt()->GetEntityName()),sizeof(tempstr), COPY_ALL_CHARACTERS); + } + else + { + Q_strncat(tempstr,STRING(GetGoalEnt()->m_iClassname),sizeof(tempstr), COPY_ALL_CHARACTERS); + } + EntityText(text_offset, tempstr, 0); + text_offset++; + } + + if ( VPhysicsGetObject() ) + { + vphysics_objectstress_t stressOut; + CalculateObjectStress( VPhysicsGetObject(), this, &stressOut ); + Q_snprintf(tempstr, sizeof(tempstr),"Stress: %.2f", stressOut.receivedStress ); + EntityText(text_offset, tempstr, 0); + text_offset++; + } + if ( m_pSquad ) + { + if( m_pSquad->IsLeader(this) ) + { + Q_snprintf(tempstr, sizeof(tempstr),"**Squad Leader**" ); + EntityText(text_offset, tempstr, 0); + text_offset++; + } + + Q_snprintf(tempstr, sizeof(tempstr), "SquadSlot:%s", GetSquadSlotDebugName( GetMyStrategySlot() ) ); + EntityText(text_offset, tempstr, 0); + text_offset++; + } + } + return text_offset; +} + + +//----------------------------------------------------------------------------- +// Purpose: Displays information in the console about the state of this npc. +//----------------------------------------------------------------------------- +void CAI_BaseNPC::ReportAIState( void ) +{ + static const char *pStateNames[] = { "None", "Idle", "Alert", "Combat", "Scripted", "PlayDead", "Dead" }; + + DevMsg( "%s: ", GetClassname() ); + if ( static_cast(m_NPCState) < ARRAYSIZE(pStateNames) ) + DevMsg( "State: %s, ", pStateNames[m_NPCState] ); + + if( m_Activity != ACT_INVALID && m_IdealActivity != ACT_INVALID ) + { + const char *pszActivity = GetActivityName(m_Activity); + const char *pszIdealActivity = GetActivityName(m_IdealActivity); + + DevMsg( "Activity: %s - Ideal Activity: %s\n", pszActivity, pszIdealActivity ); + } + + if ( GetCurSchedule() ) + { + const char *pName = NULL; + pName = GetCurSchedule()->GetName(); + if ( !pName ) + pName = "Unknown"; + DevMsg( "Schedule %s, ", pName ); + const Task_t *pTask = GetTask(); + if ( pTask ) + DevMsg( "Task %d (#%d), ", pTask->iTask, GetScheduleCurTaskIndex() ); + } + else + DevMsg( "No Schedule, " ); + + if ( GetEnemy() != NULL ) + { + g_pEffects->Sparks( GetEnemy()->GetAbsOrigin() + Vector( 0, 0, 64 ) ); + DevMsg( "\nEnemy is %s", GetEnemy()->GetClassname() ); + } + else + DevMsg( "No enemy " ); + + if ( IsMoving() ) + { + DevMsg( " Moving " ); + if ( m_flMoveWaitFinished > gpGlobals->curtime ) + DevMsg( ": Stopped for %.2f. ", m_flMoveWaitFinished - gpGlobals->curtime ); + else if ( m_IdealActivity == GetStoppedActivity() ) + DevMsg( ": In stopped anim. " ); + } + + DevMsg( "Leader." ); + + DevMsg( "\n" ); + DevMsg( "Yaw speed:%3.1f,Health: %3d\n", GetMotor()->GetYawSpeed(), m_iHealth ); + + if ( GetGroundEntity() ) + { + DevMsg( "Groundent:%s\n\n", GetGroundEntity()->GetClassname() ); + } + else + { + DevMsg( "Groundent: NULL\n\n" ); + } +} + +//----------------------------------------------------------------------------- + +ConVar ai_report_task_timings_on_limit( "ai_report_task_timings_on_limit", "0", FCVAR_ARCHIVE ); +ConVar ai_think_limit_label( "ai_think_limit_label", "0", FCVAR_ARCHIVE ); + +void CAI_BaseNPC::ReportOverThinkLimit( float time ) +{ + DevMsg( "%s thinking for %.02fms!!! (%s); r%.2f (c%.2f, pst%.2f, ms%.2f), p-r%.2f, m%.2f\n", + GetDebugName(), time, GetCurSchedule()->GetName(), + g_AIRunTimer.GetDuration().GetMillisecondsF(), + g_AIConditionsTimer.GetDuration().GetMillisecondsF(), + g_AIPrescheduleThinkTimer.GetDuration().GetMillisecondsF(), + g_AIMaintainScheduleTimer.GetDuration().GetMillisecondsF(), + g_AIPostRunTimer.GetDuration().GetMillisecondsF(), + g_AIMoveTimer.GetDuration().GetMillisecondsF() ); + + if (ai_think_limit_label.GetBool()) + { + Vector tmp; + CollisionProp()->NormalizedToWorldSpace( Vector( 0.5f, 0.5f, 1.0f ), &tmp ); + tmp.z += 16; + + float max = -1; + const char *pszMax = "unknown"; + + if ( g_AIConditionsTimer.GetDuration().GetMillisecondsF() > max ) + { + max = g_AIConditionsTimer.GetDuration().GetMillisecondsF(); + pszMax = "Conditions"; + } + if ( g_AIPrescheduleThinkTimer.GetDuration().GetMillisecondsF() > max ) + { + max = g_AIPrescheduleThinkTimer.GetDuration().GetMillisecondsF(); + pszMax = "Pre-think"; + } + if ( g_AIMaintainScheduleTimer.GetDuration().GetMillisecondsF() > max ) + { + max = g_AIMaintainScheduleTimer.GetDuration().GetMillisecondsF(); + pszMax = "Schedule"; + } + if ( g_AIPostRunTimer.GetDuration().GetMillisecondsF() > max ) + { + max = g_AIPostRunTimer.GetDuration().GetMillisecondsF(); + pszMax = "Post-run"; + } + if ( g_AIMoveTimer.GetDuration().GetMillisecondsF() > max ) + { + max = g_AIMoveTimer.GetDuration().GetMillisecondsF(); + pszMax = "Move"; + } + NDebugOverlay::Text( tmp, (char*)(const char *)CFmtStr( "Slow %.1f, %s %.1f ", time, pszMax, max ), false, 1 ); + } + + if ( ai_report_task_timings_on_limit.GetBool() ) + DumpTaskTimings(); +} + +//----------------------------------------------------------------------------- +// Purpose: Returns whether or not this npc can play the scripted sequence or AI +// sequence that is trying to possess it. If DisregardState is set, the npc +// will be sucked into the script no matter what state it is in. ONLY +// Scripted AI ents should allow this. +// Input : fDisregardNPCState - +// interruptLevel - +// eMode - If the function returns true, eMode will be one of the following values: +// CAN_PLAY_NOW +// CAN_PLAY_ENQUEUED +// Output : +//----------------------------------------------------------------------------- +CanPlaySequence_t CAI_BaseNPC::CanPlaySequence( bool fDisregardNPCState, int interruptLevel ) +{ + CanPlaySequence_t eReturn = CAN_PLAY_NOW; + + if ( m_hCine ) + { + if ( !m_hCine->CanEnqueueAfter() ) + { + // npc is already running one scripted sequence and has an important script to play next + return CANNOT_PLAY; + } + + eReturn = CAN_PLAY_ENQUEUED; + } + + if ( !IsAlive() ) + { + // npc is dead! + return CANNOT_PLAY; + } + + if ( fDisregardNPCState ) + { + // ok to go, no matter what the npc state. (scripted AI) + + // No clue as to how to proced if they're climbing or jumping + // Assert( GetNavType() != NAV_JUMP && GetNavType() != NAV_CLIMB ); + + return eReturn; + } + + if ( m_NPCState == NPC_STATE_NONE || m_NPCState == NPC_STATE_IDLE || m_IdealNPCState == NPC_STATE_IDLE ) + { + // ok to go, but only in these states + return eReturn; + } + + if ( m_NPCState == NPC_STATE_ALERT && interruptLevel >= SS_INTERRUPT_BY_NAME ) + { + return eReturn; + } + + // unknown situation + return CANNOT_PLAY; +} + + +//----------------------------------------------------------------------------- + +void CAI_BaseNPC::SetHintGroup( string_t newGroup, bool bHintGroupNavLimiting ) +{ + string_t oldGroup = m_strHintGroup; + m_strHintGroup = newGroup; + m_bHintGroupNavLimiting = bHintGroupNavLimiting; + + if ( oldGroup != newGroup ) + OnChangeHintGroup( oldGroup, newGroup ); + +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : +// Output : +//----------------------------------------------------------------------------- +Vector CAI_BaseNPC::GetShootEnemyDir( const Vector &shootOrigin, bool bNoisy ) +{ + CBaseEntity *pEnemy = GetEnemy(); + + if ( pEnemy ) + { + Vector vecEnemyLKP = GetEnemyLKP(); + Vector retval = (pEnemy->BodyTarget( shootOrigin, bNoisy ) - pEnemy->GetAbsOrigin()) + vecEnemyLKP - shootOrigin; + VectorNormalize( retval ); + return retval; + } + else + { + Vector forward; + AngleVectors( GetLocalAngles(), &forward ); + return forward; + } +} + +//----------------------------------------------------------------------------- +// Simulates many times and reports statistical accuracy. +//----------------------------------------------------------------------------- +void CAI_BaseNPC::CollectShotStats( const Vector &vecShootOrigin, const Vector &vecShootDir ) +{ +#ifdef HL2_DLL + if( ai_shot_stats.GetBool() != 0 && GetEnemy()->IsPlayer() ) + { + int iterations = ai_shot_stats_term.GetInt(); + int iHits = 0; + Vector testDir = vecShootDir; + + CShotManipulator manipulator( testDir ); + + for( int i = 0 ; i < iterations ; i++ ) + { + // Apply appropriate accuracy. + manipulator.ApplySpread( GetAttackSpread( GetActiveWeapon(), GetEnemy() ), GetSpreadBias( GetActiveWeapon(), GetEnemy() ) ); + Vector shotDir = manipulator.GetResult(); + + Vector vecEnd = vecShootOrigin + shotDir * 8192; + + trace_t tr; + AI_TraceLine( vecShootOrigin, vecEnd, MASK_SHOT, this, COLLISION_GROUP_NONE, &tr); + + if( tr.m_pEnt && tr.m_pEnt == GetEnemy() ) + { + iHits++; + } + Vector vecProjectedPosition = GetActualShootPosition( vecShootOrigin ); + Vector testDir = vecProjectedPosition - vecShootOrigin; + VectorNormalize( testDir ); + manipulator.SetShootDir( testDir ); + } + + float flHitPercent = ((float)iHits / (float)iterations) * 100.0; + m_LastShootAccuracy = flHitPercent; + //DevMsg("Shots:%d Hits:%d Percentage:%.1f\n", iterations, iHits, flHitPercent); + } + else + { + m_LastShootAccuracy = -1; + } +#endif +} + +#ifdef HL2_DLL +//----------------------------------------------------------------------------- +// Purpose: Return the actual position the NPC wants to fire at when it's trying +// to hit it's current enemy. +//----------------------------------------------------------------------------- +Vector CAI_BaseNPC::GetActualShootPosition( const Vector &shootOrigin ) +{ + // Project the target's location into the future. + Vector vecEnemyLKP = GetEnemyLKP(); + Vector vecTargetPosition = (GetEnemy()->BodyTarget( shootOrigin ) - GetEnemy()->GetAbsOrigin()) + vecEnemyLKP; + + // lead for some fraction of a second. + return (vecTargetPosition + ( GetEnemy()->GetSmoothedVelocity() * ai_lead_time.GetFloat() )); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +float CAI_BaseNPC::GetSpreadBias( CBaseCombatWeapon *pWeapon, CBaseEntity *pTarget ) +{ + float bias = BaseClass::GetSpreadBias( pWeapon, pTarget ); + AI_EnemyInfo_t *pEnemyInfo = m_pEnemies->Find( pTarget ); + if ( ai_shot_bias.GetFloat() != 1.0 ) + bias = ai_shot_bias.GetFloat(); + if ( pEnemyInfo ) + { + float timeToFocus = ai_spread_pattern_focus_time.GetFloat(); + if ( timeToFocus > 0.0 ) + { + float timeSinceValidEnemy = gpGlobals->curtime - pEnemyInfo->timeValidEnemy; + if ( timeSinceValidEnemy < 0.0f ) + { + timeSinceValidEnemy = 0.0f; + } + float timeSinceReacquire = gpGlobals->curtime - pEnemyInfo->timeLastReacquired; + if ( timeSinceValidEnemy < timeToFocus ) + { + float scale = timeSinceValidEnemy / timeToFocus; + Assert( scale >= 0.0 && scale <= 1.0 ); + bias *= scale; + } + else if ( timeSinceReacquire < timeToFocus ) // handled seperately as might be tuning seperately + { + float scale = timeSinceReacquire / timeToFocus; + Assert( scale >= 0.0 && scale <= 1.0 ); + bias *= scale; + } + + } + } + return bias; +} + +//----------------------------------------------------------------------------- +Vector CAI_BaseNPC::GetAttackSpread( CBaseCombatWeapon *pWeapon, CBaseEntity *pTarget ) +{ + Vector baseResult = BaseClass::GetAttackSpread( pWeapon, pTarget ); + + AI_EnemyInfo_t *pEnemyInfo = m_pEnemies->Find( pTarget ); + if ( pEnemyInfo ) + { + float timeToFocus = ai_spread_cone_focus_time.GetFloat(); + if ( timeToFocus > 0.0 ) + { + float timeSinceValidEnemy = gpGlobals->curtime - pEnemyInfo->timeValidEnemy; + if ( timeSinceValidEnemy < 0 ) + timeSinceValidEnemy = 0; + if ( timeSinceValidEnemy < timeToFocus ) + { + float coneMultiplier = ai_spread_defocused_cone_multiplier.GetFloat(); + if ( coneMultiplier > 1.0 ) + { + float scale = 1.0 - timeSinceValidEnemy / timeToFocus; + Assert( scale >= 0.0 && scale <= 1.0 ); + float multiplier = ( (coneMultiplier - 1.0) * scale ) + 1.0; + baseResult *= multiplier; + } + } + } + } + return baseResult; +} + +//----------------------------------------------------------------------------- +// Similar to calling GetShootEnemyDir, but returns the exact trajectory to +// fire the bullet along, after calculating for target speed, location, +// concealment, etc. +// +// Ultimately, this code results in the shooter aiming his weapon somewhere ahead of +// a moving target. Since bullet traces are instant, aiming ahead of a target +// will result in more misses than hits. This is designed to provide targets with +// a bonus when moving perpendicular to the shooter's line of sight. +// +// Do not confuse this with leading a target in an effort to more easily hit it. +// +// This code PENALIZES a target for moving directly along the shooter's line of sight. +// +// This code REWARDS a target for moving perpendicular to the shooter's line of sight. +//----------------------------------------------------------------------------- +Vector CAI_BaseNPC::GetActualShootTrajectory( const Vector &shootOrigin ) +{ + if( !GetEnemy() ) + return GetShootEnemyDir( shootOrigin ); + + // If we're above water shooting at a player underwater, bias some of the shots forward of + // the player so that they see the cool bubble trails in the water ahead of them. + if (GetEnemy()->IsPlayer() && (GetWaterLevel() != 3) && (GetEnemy()->GetWaterLevel() == 3)) + { +#if 1 + if (random->RandomInt(0, 4) < 3) + { + Vector vecEnemyForward; + GetEnemy()->GetVectors( &vecEnemyForward, NULL, NULL ); + vecEnemyForward.z = 0; + + // Lead up to a second ahead of them unless they are moving backwards. + Vector vecEnemyVelocity = GetEnemy()->GetSmoothedVelocity(); + VectorNormalize( vecEnemyVelocity ); + float flVelocityScale = vecEnemyForward.Dot( vecEnemyVelocity ); + if ( flVelocityScale < 0.0f ) + { + flVelocityScale = 0.0f; + } + + Vector vecAimPos = GetEnemy()->EyePosition() + ( 48.0f * vecEnemyForward ) + (flVelocityScale * GetEnemy()->GetSmoothedVelocity() ); + //NDebugOverlay::Cross3D(vecAimPos, Vector(-16,-16,-16), Vector(16,16,16), 255, 255, 0, true, 1.0f ); + + //vecAimPos.z = UTIL_WaterLevel( vecAimPos, vecAimPos.z, vecAimPos.z + 400.0f ); + //NDebugOverlay::Cross3D(vecAimPos, Vector(-32,-32,-32), Vector(32,32,32), 255, 0, 0, true, 1.0f ); + + Vector vecShotDir = vecAimPos - shootOrigin; + VectorNormalize( vecShotDir ); + return vecShotDir; + } +#else + if (random->RandomInt(0, 4) < 3) + { + // Aim at a point a few feet in front of the player's eyes + Vector vecEnemyForward; + GetEnemy()->GetVectors( &vecEnemyForward, NULL, NULL ); + + Vector vecAimPos = GetEnemy()->EyePosition() + (120.0f * vecEnemyForward ); + + Vector vecShotDir = vecAimPos - shootOrigin; + VectorNormalize( vecShotDir ); + + CShotManipulator manipulator( vecShotDir ); + manipulator.ApplySpread( VECTOR_CONE_10DEGREES, 1 ); + vecShotDir = manipulator.GetResult(); + + return vecShotDir; + } +#endif + } + + Vector vecProjectedPosition = GetActualShootPosition( shootOrigin ); + + Vector shotDir = vecProjectedPosition - shootOrigin; + VectorNormalize( shotDir ); + + CollectShotStats( shootOrigin, shotDir ); + + // NOW we have a shoot direction. Where a 100% accurate bullet should go. + // Modify it by weapon proficiency. + // construct a manipulator + CShotManipulator manipulator( shotDir ); + + // Apply appropriate accuracy. + bool bUsePerfectAccuracy = false; + if ( GetEnemy() && GetEnemy()->Classify() == CLASS_BULLSEYE ) + { + CNPC_Bullseye *pBullseye = dynamic_cast(GetEnemy()); + if ( pBullseye && pBullseye->UsePerfectAccuracy() ) + { + bUsePerfectAccuracy = true; + } + } + + if ( !bUsePerfectAccuracy ) + { + manipulator.ApplySpread( GetAttackSpread( GetActiveWeapon(), GetEnemy() ), GetSpreadBias( GetActiveWeapon(), GetEnemy() ) ); + shotDir = manipulator.GetResult(); + } + + // Look for an opportunity to make misses hit interesting things. + CBaseCombatCharacter *pEnemy; + + pEnemy = GetEnemy()->MyCombatCharacterPointer(); + + if( pEnemy && pEnemy->ShouldShootMissTarget( this ) ) + { + Vector vecEnd = shootOrigin + shotDir * 8192; + trace_t tr; + + AI_TraceLine(shootOrigin, vecEnd, MASK_SHOT, this, COLLISION_GROUP_NONE, &tr); + + if( tr.fraction != 1.0 && tr.m_pEnt && tr.m_pEnt->m_takedamage != DAMAGE_NO ) + { + // Hit something we can harm. Just shoot it. + return manipulator.GetResult(); + } + + // Find something interesting around the enemy to shoot instead of just missing. + CBaseEntity *pMissTarget = pEnemy->FindMissTarget(); + + if( pMissTarget ) + { + shotDir = pMissTarget->WorldSpaceCenter() - shootOrigin; + VectorNormalize( shotDir ); + } + } + + return shotDir; +} +#endif // HL2_DLL + +//----------------------------------------------------------------------------- + +Vector CAI_BaseNPC::BodyTarget( const Vector &posSrc, bool bNoisy ) +{ + Vector low = WorldSpaceCenter() - ( WorldSpaceCenter() - GetAbsOrigin() ) * .25; + Vector high = EyePosition(); + Vector delta = high - low; + Vector result; + if ( bNoisy ) + { + // bell curve + float rand1 = random->RandomFloat( 0.0, 0.5 ); + float rand2 = random->RandomFloat( 0.0, 0.5 ); + result = low + delta * rand1 + delta * rand2; + } + else + result = low + delta * 0.5; + + return result; +} + +//----------------------------------------------------------------------------- + +bool CAI_BaseNPC::ShouldMoveAndShoot() +{ + return ( ( CapabilitiesGet() & bits_CAP_MOVE_SHOOT ) != 0 ); +} + + +//========================================================= +// FacingIdeal - tells us if a npc is facing its ideal +// yaw. Created this function because many spots in the +// code were checking the yawdiff against this magic +// number. Nicer to have it in one place if we're gonna +// be stuck with it. +//========================================================= +bool CAI_BaseNPC::FacingIdeal( void ) +{ + if ( fabs( GetMotor()->DeltaIdealYaw() ) <= 0.006 )//!!!BUGBUG - no magic numbers!!! + { + return true; + } + + return false; +} + +//--------------------------------- + +void CAI_BaseNPC::AddFacingTarget( CBaseEntity *pTarget, float flImportance, float flDuration, float flRamp ) +{ + GetMotor()->AddFacingTarget( pTarget, flImportance, flDuration, flRamp ); +} + +void CAI_BaseNPC::AddFacingTarget( const Vector &vecPosition, float flImportance, float flDuration, float flRamp ) +{ + GetMotor()->AddFacingTarget( vecPosition, flImportance, flDuration, flRamp ); +} + +void CAI_BaseNPC::AddFacingTarget( CBaseEntity *pTarget, const Vector &vecPosition, float flImportance, float flDuration, float flRamp ) +{ + GetMotor()->AddFacingTarget( pTarget, vecPosition, flImportance, flDuration, flRamp ); +} + +float CAI_BaseNPC::GetFacingDirection( Vector &vecDir ) +{ + return (GetMotor()->GetFacingDirection( vecDir )); +} + + +//--------------------------------- + + +int CAI_BaseNPC::PlaySentence( const char *pszSentence, float delay, float volume, soundlevel_t soundlevel, CBaseEntity *pListener ) +{ + int sentenceIndex = -1; + + if ( pszSentence && IsAlive() ) + { + + if ( pszSentence[0] == '!' ) + { + sentenceIndex = SENTENCEG_Lookup( pszSentence ); + CPASAttenuationFilter filter( this, soundlevel ); + CBaseEntity::EmitSentenceByIndex( filter, entindex(), CHAN_VOICE, sentenceIndex, volume, soundlevel, 0, PITCH_NORM ); + } + else + { + sentenceIndex = SENTENCEG_PlayRndSz( edict(), pszSentence, volume, soundlevel, 0, PITCH_NORM ); + } + } + + return sentenceIndex; +} + + +int CAI_BaseNPC::PlayScriptedSentence( const char *pszSentence, float delay, float volume, soundlevel_t soundlevel, bool bConcurrent, CBaseEntity *pListener ) +{ + return PlaySentence( pszSentence, delay, volume, soundlevel, pListener ); +} + + +void CAI_BaseNPC::SentenceStop( void ) +{ + EmitSound( "AI_BaseNPC.SentenceStop" ); +} + + + + +//----------------------------------------------------------------------------- +// Purpose: Play a one-shot scene +// Input : +// Output : +//----------------------------------------------------------------------------- +float CAI_BaseNPC::PlayScene( const char *pszScene, float flDelay, AI_Response *response ) +{ + return InstancedScriptedScene( this, pszScene, NULL, flDelay, false, response ); +} + +//----------------------------------------------------------------------------- +// Purpose: Generate a one-shot scene in memory with one track which is to play the named sound on the actor +// Input : *soundname - +// Output : float +//----------------------------------------------------------------------------- +float CAI_BaseNPC::PlayAutoGeneratedSoundScene( const char *soundname ) +{ + return InstancedAutoGeneratedSoundScene( this, soundname ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : +// Output : +//----------------------------------------------------------------------------- +CBaseEntity *CAI_BaseNPC::FindNamedEntity( const char *name, IEntityFindFilter *pFilter ) +{ + if ( !stricmp( name, "!player" )) + { + return ( CBaseEntity * )AI_GetSinglePlayer(); + } + else if ( !stricmp( name, "!enemy" ) ) + { + if (GetEnemy() != NULL) + return GetEnemy(); + } + else if ( !stricmp( name, "!self" ) || !stricmp( name, "!target1" ) ) + { + return this; + } + else if ( !stricmp( name, "!nearestfriend" ) || !stricmp( name, "!friend" ) ) + { + // FIXME: look at CBaseEntity *CNPCSimpleTalker::FindNearestFriend(bool fPlayer) + // punt for now + return ( CBaseEntity * )AI_GetSinglePlayer(); + } + else if (!stricmp( name, "self" )) + { + static int selfwarningcount = 0; + + // fix the vcd, the reserved names have changed + if ( ++selfwarningcount < 5 ) + { + DevMsg( "ERROR: \"self\" is no longer used, use \"!self\" in vcd instead!\n" ); + } + return this; + } + else if ( !stricmp( name, "Player" )) + { + static int playerwarningcount = 0; + if ( ++playerwarningcount < 5 ) + { + DevMsg( "ERROR: \"player\" is no longer used, use \"!player\" in vcd instead!\n" ); + } + return ( CBaseEntity * )AI_GetSinglePlayer(); + } + else + { + // search for up to 32 entities with the same name and choose one randomly + CBaseEntity *entityList[ FINDNAMEDENTITY_MAX_ENTITIES ]; + CBaseEntity *entity; + int iCount; + + entity = NULL; + for( iCount = 0; iCount < FINDNAMEDENTITY_MAX_ENTITIES; iCount++ ) + { + entity = gEntList.FindEntityByName( entity, name, NULL, NULL, NULL, pFilter ); + if ( !entity ) + { + break; + } + entityList[ iCount ] = entity; + } + + if ( iCount > 0 ) + { + int index = RandomInt( 0, iCount - 1 ); + entity = entityList[ index ]; + return entity; + } + } + + return NULL; +} + + +void CAI_BaseNPC::CorpseFallThink( void ) +{ + if ( GetFlags() & FL_ONGROUND ) + { + SetThink ( NULL ); + + SetSequenceBox( ); + } + else + { + SetNextThink( gpGlobals->curtime + 0.1f ); + } +} + +// Call after animation/pose is set up +void CAI_BaseNPC::NPCInitDead( void ) +{ + InitBoneControllers(); + + RemoveSolidFlags( FSOLID_NOT_SOLID ); + + // so he'll fall to ground + SetMoveType( MOVETYPE_FLYGRAVITY, MOVECOLLIDE_FLY_BOUNCE ); + + SetCycle( 0 ); + ResetSequenceInfo( ); + m_flPlaybackRate = 0; + + // Copy health + m_iMaxHealth = m_iHealth; + m_lifeState = LIFE_DEAD; + + UTIL_SetSize(this, vec3_origin, vec3_origin ); + + // Setup health counters, etc. + SetThink( &CAI_BaseNPC::CorpseFallThink ); + + SetNextThink( gpGlobals->curtime + 0.5f ); +} + +//========================================================= +// BBoxIsFlat - check to see if the npc's bounding box +// is lying flat on a surface (traces from all four corners +// are same length.) +//========================================================= +bool CAI_BaseNPC::BBoxFlat ( void ) +{ + trace_t tr; + Vector vecPoint; + float flXSize, flYSize; + float flLength; + float flLength2; + + flXSize = WorldAlignSize().x / 2; + flYSize = WorldAlignSize().y / 2; + + vecPoint.x = GetAbsOrigin().x + flXSize; + vecPoint.y = GetAbsOrigin().y + flYSize; + vecPoint.z = GetAbsOrigin().z; + + AI_TraceLine ( vecPoint, vecPoint - Vector ( 0, 0, 100 ), MASK_NPCSOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr ); + flLength = (vecPoint - tr.endpos).Length(); + + vecPoint.x = GetAbsOrigin().x - flXSize; + vecPoint.y = GetAbsOrigin().y - flYSize; + + AI_TraceLine ( vecPoint, vecPoint - Vector ( 0, 0, 100 ), MASK_NPCSOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr ); + flLength2 = (vecPoint - tr.endpos).Length(); + if ( flLength2 > flLength ) + { + return false; + } + flLength = flLength2; + + vecPoint.x = GetAbsOrigin().x - flXSize; + vecPoint.y = GetAbsOrigin().y + flYSize; + AI_TraceLine ( vecPoint, vecPoint - Vector ( 0, 0, 100 ), MASK_NPCSOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr ); + flLength2 = (vecPoint - tr.endpos).Length(); + if ( flLength2 > flLength ) + { + return false; + } + flLength = flLength2; + + vecPoint.x = GetAbsOrigin().x + flXSize; + vecPoint.y = GetAbsOrigin().y - flYSize; + AI_TraceLine ( vecPoint, vecPoint - Vector ( 0, 0, 100 ), MASK_NPCSOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr ); + flLength2 = (vecPoint - tr.endpos).Length(); + if ( flLength2 > flLength ) + { + return false; + } + flLength = flLength2; + + return true; +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *pEnemy - +// bSetCondNewEnemy - +//----------------------------------------------------------------------------- +void CAI_BaseNPC::SetEnemy( CBaseEntity *pEnemy, bool bSetCondNewEnemy ) +{ + if (m_hEnemy != pEnemy) + { + ClearAttackConditions( ); + VacateStrategySlot(); + m_GiveUpOnDeadEnemyTimer.Stop(); + + // If we've just found a new enemy, set the condition + if ( pEnemy && bSetCondNewEnemy ) + { + SetCondition( COND_NEW_ENEMY ); + } + } + + // Assert( (pEnemy == NULL) || (m_NPCState == NPC_STATE_COMBAT) ); + + m_hEnemy = pEnemy; + m_flTimeEnemyAcquired = gpGlobals->curtime; + + m_LastShootAccuracy = -1; + m_TotalShots = 0; + m_TotalHits = 0; + + if ( !pEnemy ) + ClearCondition( COND_NEW_ENEMY ); +} + +const Vector &CAI_BaseNPC::GetEnemyLKP() const +{ + return (const_cast(this))->GetEnemies()->LastKnownPosition( GetEnemy() ); +} + +float CAI_BaseNPC::GetEnemyLastTimeSeen() const +{ + return (const_cast(this))->GetEnemies()->LastTimeSeen( GetEnemy() ); +} + +void CAI_BaseNPC::MarkEnemyAsEluded() +{ + GetEnemies()->MarkAsEluded( GetEnemy() ); +} + +void CAI_BaseNPC::ClearEnemyMemory() +{ + GetEnemies()->ClearMemory( GetEnemy() ); +} + +bool CAI_BaseNPC::EnemyHasEludedMe() const +{ + return (const_cast(this))->GetEnemies()->HasEludedMe( GetEnemy() ); +} + +void CAI_BaseNPC::SetTarget( CBaseEntity *pTarget ) +{ + m_hTargetEnt = pTarget; +} + + +//========================================================= +// Choose Enemy - tries to find the best suitable enemy for the npc. +//========================================================= + +bool CAI_BaseNPC::ShouldChooseNewEnemy() +{ + CBaseEntity *pEnemy = GetEnemy(); + if ( pEnemy ) + { + if ( GetEnemies()->GetSerialNumber() != m_EnemiesSerialNumber ) + { + return true; + } + + m_EnemiesSerialNumber = GetEnemies()->GetSerialNumber(); + + if ( EnemyHasEludedMe() || (IRelationType( pEnemy ) != D_HT && IRelationType( pEnemy ) != D_FR) || !IsValidEnemy( pEnemy ) ) + { + DbgEnemyMsg( this, "ShouldChooseNewEnemy() --> true (1)\n" ); + return true; + } + if ( HasCondition(COND_SEE_HATE) || HasCondition(COND_SEE_DISLIKE) || HasCondition(COND_SEE_NEMESIS) || HasCondition(COND_SEE_FEAR) ) + { + DbgEnemyMsg( this, "ShouldChooseNewEnemy() --> true (2)\n" ); + return true; + } + if ( !pEnemy->IsAlive() ) + { + if ( m_GiveUpOnDeadEnemyTimer.IsRunning() ) + { + if ( m_GiveUpOnDeadEnemyTimer.Expired() ) + { + DbgEnemyMsg( this, "ShouldChooseNewEnemy() --> true (3)\n" ); + return true; + } + } + else + m_GiveUpOnDeadEnemyTimer.Start(); + } + + AI_EnemyInfo_t *pInfo = GetEnemies()->Find( pEnemy ); + + if ( m_FailChooseEnemyTimer.Expired() ) + { + m_FailChooseEnemyTimer.Set( 1.5 ); + if ( HasCondition( COND_TASK_FAILED ) || + ( pInfo && ( pInfo->timeAtFirstHand == AI_INVALID_TIME || gpGlobals->curtime - pInfo->timeLastSeen > 10 ) ) ) + { + return true; + } + } + + if ( pInfo && pInfo->timeValidEnemy < gpGlobals->curtime ) + { + DbgEnemyMsg( this, "ShouldChooseNewEnemy() --> false\n" ); + return false; + } + } + + DbgEnemyMsg( this, "ShouldChooseNewEnemy() --> true (4)\n" ); + m_EnemiesSerialNumber = GetEnemies()->GetSerialNumber(); + + return true; +} + +//------------------------------------- + +bool CAI_BaseNPC::ChooseEnemy( void ) +{ + AI_PROFILE_SCOPE(CAI_Enemies_ChooseEnemy); + + DbgEnemyMsg( this, "ChooseEnemy() {\n" ); + + //--------------------------------- + // + // Gather initial conditions + // + + CBaseEntity *pInitialEnemy = GetEnemy(); + CBaseEntity *pChosenEnemy = pInitialEnemy; + + // Use memory bits in case enemy pointer altered outside this function, (e.g., ehandle goes NULL) + bool fHadEnemy = ( HasMemory( bits_MEMORY_HAD_ENEMY | bits_MEMORY_HAD_PLAYER ) ); + bool fEnemyWasPlayer = HasMemory( bits_MEMORY_HAD_PLAYER ); + bool fEnemyWentNull = ( fHadEnemy && !pInitialEnemy ); + bool fEnemyEluded = ( fEnemyWentNull || ( pInitialEnemy && GetEnemies()->HasEludedMe( pInitialEnemy ) ) ); + + //--------------------------------- + // + // Establish suitability of choosing a new enemy + // + + bool fHaveCondNewEnemy; + bool fHaveCondLostEnemy; + + if ( GetCurSchedule() && !FScheduleDone() ) + { + Assert( InterruptFromCondition( COND_NEW_ENEMY ) == COND_NEW_ENEMY && InterruptFromCondition( COND_LOST_ENEMY ) == COND_LOST_ENEMY ); + fHaveCondNewEnemy = GetCurSchedule()->HasInterrupt( COND_NEW_ENEMY ); + fHaveCondLostEnemy = GetCurSchedule()->HasInterrupt( COND_LOST_ENEMY ); + + // See if they've been added as a custom interrupt + if ( !fHaveCondNewEnemy ) + { + fHaveCondNewEnemy = IsCustomInterruptConditionSet( COND_NEW_ENEMY ); + } + if ( !fHaveCondLostEnemy ) + { + fHaveCondLostEnemy = IsCustomInterruptConditionSet( COND_LOST_ENEMY ); + } + } + else + { + fHaveCondNewEnemy = true; // not having a schedule is the same as being interruptable by any condition + fHaveCondLostEnemy = true; + } + + if ( !fEnemyWentNull ) + { + if ( !fHaveCondNewEnemy && !( fHaveCondLostEnemy && fEnemyEluded ) ) + { + // DO NOT mess with the npc's enemy pointer unless the schedule the npc is currently + // running will be interrupted by COND_NEW_ENEMY or COND_LOST_ENEMY. This will + // eliminate the problem of npcs getting a new enemy while they are in a schedule + // that doesn't care, and then not realizing it by the time they get to a schedule + // that does. I don't feel this is a good permanent fix. + m_bSkippedChooseEnemy = true; + + DbgEnemyMsg( this, "Skipped enemy selection due to schedule restriction\n" ); + DbgEnemyMsg( this, "}\n" ); + return ( pChosenEnemy != NULL ); + } + } + else if ( !fHaveCondNewEnemy && !fHaveCondLostEnemy && GetCurSchedule() ) + { + DevMsg( 2, "WARNING: AI enemy went NULL but schedule (%s) is not interested\n", GetCurSchedule()->GetName() ); + } + + m_bSkippedChooseEnemy = false; + + //--------------------------------- + // + // Select a target + // + + if ( ShouldChooseNewEnemy() ) + { + pChosenEnemy = BestEnemy(); + } + + //--------------------------------- + // + // React to result of selection + // + + bool fChangingEnemy = ( pChosenEnemy != pInitialEnemy ); + + if ( fChangingEnemy || fEnemyWentNull ) + { + DbgEnemyMsg( this, "Enemy changed from %s to %s\n", pInitialEnemy->GetDebugName(), pChosenEnemy->GetDebugName() ); + Forget( bits_MEMORY_HAD_ENEMY | bits_MEMORY_HAD_PLAYER ); + + // Did our old enemy snuff it? + if ( pInitialEnemy && !pInitialEnemy->IsAlive() ) + { + SetCondition( COND_ENEMY_DEAD ); + } + + SetEnemy( pChosenEnemy ); + + if ( fHadEnemy ) + { + // Vacate any strategy slot on old enemy + VacateStrategySlot(); + + // Force output event for establishing LOS + Forget( bits_MEMORY_HAD_LOS ); + // m_flLastAttackTime = 0; + } + + if ( !pChosenEnemy ) + { + // Don't break on enemies going null if they've been killed + if ( !HasCondition(COND_ENEMY_DEAD) ) + { + SetCondition( COND_ENEMY_WENT_NULL ); + } + + if ( fEnemyEluded ) + { + SetCondition( COND_LOST_ENEMY ); + LostEnemySound(); + } + + if ( fEnemyWasPlayer ) + { + m_OnLostPlayer.FireOutput( pInitialEnemy, this ); + } + m_OnLostEnemy.FireOutput( pInitialEnemy, this); + } + else + { + Remember( ( pChosenEnemy->IsPlayer() ) ? bits_MEMORY_HAD_PLAYER : bits_MEMORY_HAD_ENEMY ); + } + } + + //--------------------------------- + + return ( pChosenEnemy != NULL ); +} + + +//========================================================= +void CAI_BaseNPC::PickupWeapon( CBaseCombatWeapon *pWeapon ) +{ + pWeapon->OnPickedUp( this ); + Weapon_Equip( pWeapon ); + m_iszPendingWeapon = NULL_STRING; +} + +//========================================================= +// DropItem - dead npc drops named item +//========================================================= +CBaseEntity *CAI_BaseNPC::DropItem ( char *pszItemName, Vector vecPos, QAngle vecAng ) +{ + if ( !pszItemName ) + { + DevMsg( "DropItem() - No item name!\n" ); + return NULL; + } + + CBaseEntity *pItem = CBaseEntity::Create( pszItemName, vecPos, vecAng, this ); + + if ( pItem ) + { + if ( g_pGameRules->IsAllowedToSpawn( pItem ) == false ) + { + UTIL_Remove( pItem ); + return NULL; + } + + IPhysicsObject *pPhys = pItem->VPhysicsGetObject(); + + if ( pPhys ) + { + // Add an extra push in a random direction + Vector vel = RandomVector( -64.0f, 64.0f ); + AngularImpulse angImp = RandomAngularImpulse( -300.0f, 300.0f ); + + vel[2] = 0.0f; + pPhys->AddVelocity( &vel, &angImp ); + } + else + { + // do we want this behavior to be default?! (sjb) + pItem->ApplyAbsVelocityImpulse( GetAbsVelocity() ); + pItem->ApplyLocalAngularVelocityImpulse( AngularImpulse( 0, random->RandomFloat( 0, 100 ), 0 ) ); + } + + return pItem; + } + else + { + DevMsg( "DropItem() - Didn't create!\n" ); + return NULL; + } + +} + +bool CAI_BaseNPC::ShouldFadeOnDeath( void ) +{ + if ( g_RagdollLVManager.IsLowViolence() ) + { + return true; + } + else + { + // if flagged to fade out + return HasSpawnFlags(SF_NPC_FADE_CORPSE); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Indicates whether or not this npc should play an idle sound now. +// +// +// Output : Returns true if yes, false if no. +//----------------------------------------------------------------------------- +bool CAI_BaseNPC::ShouldPlayIdleSound( void ) +{ + if ( ( m_NPCState == NPC_STATE_IDLE || m_NPCState == NPC_STATE_ALERT ) && + random->RandomInt(0,99) == 0 && !HasSpawnFlags(SF_NPC_GAG) ) + { + return true; + } + + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: Make a sound that other AI's can hear, to broadcast our presence +// Input : volume (radius) of the sound. +// Output : +//----------------------------------------------------------------------------- +void CAI_BaseNPC::MakeAIFootstepSound( float volume, float duration ) +{ + CSoundEnt::InsertSound( SOUND_COMBAT, EyePosition(), static_cast(volume), duration, this, SOUNDENT_CHANNEL_NPC_FOOTSTEP ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : +// Output : +//----------------------------------------------------------------------------- +bool CAI_BaseNPC::FOkToMakeSound( int soundPriority ) +{ + // ask the squad to filter sounds if I'm in one + if ( m_pSquad ) + { + if ( !m_pSquad->FOkToMakeSound( soundPriority ) ) + return false; + } + else + { + // otherwise, check my own sound timer + // Am I making uninterruptable sound? + if (gpGlobals->curtime <= m_flSoundWaitTime) + { + if ( soundPriority <= m_nSoundPriority ) + return false; + } + } + + // no talking outside of combat if gagged. + if ( HasSpawnFlags(SF_NPC_GAG) && ( m_NPCState != NPC_STATE_COMBAT ) ) + return false; + + return true; +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : +// Output : +//----------------------------------------------------------------------------- +void CAI_BaseNPC::JustMadeSound( int soundPriority, float flSoundLength ) +{ + m_flSoundWaitTime = gpGlobals->curtime + flSoundLength + random->RandomFloat(1.5, 2.0); + m_nSoundPriority = soundPriority; + + if (m_pSquad) + { + m_pSquad->JustMadeSound( soundPriority, gpGlobals->curtime + flSoundLength + random->RandomFloat(1.5, 2.0) ); + } +} + +Activity CAI_BaseNPC::GetStoppedActivity( void ) +{ + if (GetNavigator()->IsGoalActive()) + { + Activity activity = GetNavigator()->GetArrivalActivity(); + + if (activity > ACT_RESET) + { + return activity; + } + } + + return ACT_IDLE; +} + + +//========================================================= +//========================================================= +void CAI_BaseNPC::OnScheduleChange ( void ) +{ + EndTaskOverlay(); + + m_pNavigator->OnScheduleChange(); + + m_flMoveWaitFinished = 0; + + VacateStrategySlot(); + + // If I still have have a route, clear it + // FIXME: Routes should only be cleared inside of tasks (kenb) + GetNavigator()->ClearGoal(); + + UnlockBestSound(); + + // If I locked a hint node clear it + if ( HasMemory(bits_MEMORY_LOCKED_HINT) && GetHintNode() != NULL) + { + float hintDelay = GetHintDelay(GetHintNode()->HintType()); + GetHintNode()->Unlock(hintDelay); + SetHintNode( NULL ); + } +} + + + +CBaseCombatCharacter* CAI_BaseNPC::GetEnemyCombatCharacterPointer() +{ + if ( GetEnemy() == NULL ) + return NULL; + + return GetEnemy()->MyCombatCharacterPointer(); +} + + +// Global Savedata for npc +// +// This should be an exact copy of the var's in the header. Fields +// that aren't save/restored are commented out + +BEGIN_DATADESC( CAI_BaseNPC ) + + // m_pSchedule (reacquired on restore) + DEFINE_EMBEDDED( m_ScheduleState ), + DEFINE_FIELD( m_IdealSchedule, FIELD_INTEGER ), // handled specially but left in for "virtual" schedules + DEFINE_FIELD( m_failSchedule, FIELD_INTEGER ), // handled specially but left in for "virtual" schedules + DEFINE_FIELD( m_bUsingStandardThinkTime, FIELD_BOOLEAN ), + DEFINE_FIELD( m_flLastRealThinkTime, FIELD_TIME ), + // m_iFrameBlocked (not saved) + // m_bInChoreo (not saved) + // m_bDoPostRestoreRefindPath (not saved) + // gm_flTimeLastSpawn (static) + // gm_nSpawnedThisFrame (static) + // m_Conditions (custom save) + // m_CustomInterruptConditions (custom save) + // m_ConditionsPreIgnore (custom save) + // m_InverseIgnoreConditions (custom save) + DEFINE_FIELD( m_bForceConditionsGather, FIELD_BOOLEAN ), + DEFINE_FIELD( m_bConditionsGathered, FIELD_BOOLEAN ), + DEFINE_FIELD( m_bSkippedChooseEnemy, FIELD_BOOLEAN ), + DEFINE_FIELD( m_NPCState, FIELD_INTEGER ), + DEFINE_FIELD( m_IdealNPCState, FIELD_INTEGER ), + DEFINE_FIELD( m_flLastStateChangeTime, FIELD_TIME ), + DEFINE_FIELD( m_Efficiency, FIELD_INTEGER ), + DEFINE_FIELD( m_MoveEfficiency, FIELD_INTEGER ), + DEFINE_FIELD( m_flNextDecisionTime, FIELD_TIME ), + DEFINE_KEYFIELD( m_SleepState, FIELD_INTEGER, "sleepstate" ), + DEFINE_FIELD( m_SleepFlags, FIELD_INTEGER ), + DEFINE_KEYFIELD( m_flWakeRadius, FIELD_FLOAT, "wakeradius" ), + DEFINE_KEYFIELD( m_bWakeSquad, FIELD_BOOLEAN, "wakesquad" ), + DEFINE_FIELD( m_nWakeTick, FIELD_TICK ), + + DEFINE_CUSTOM_FIELD( m_Activity, ActivityDataOps() ), + DEFINE_CUSTOM_FIELD( m_translatedActivity, ActivityDataOps() ), + DEFINE_CUSTOM_FIELD( m_IdealActivity, ActivityDataOps() ), + DEFINE_CUSTOM_FIELD( m_IdealTranslatedActivity, ActivityDataOps() ), + DEFINE_CUSTOM_FIELD( m_IdealWeaponActivity, ActivityDataOps() ), + + DEFINE_FIELD( m_nIdealSequence, FIELD_INTEGER ), + DEFINE_EMBEDDEDBYREF( m_pSenses ), + DEFINE_EMBEDDEDBYREF( m_pLockedBestSound ), + DEFINE_FIELD( m_hEnemy, FIELD_EHANDLE ), + DEFINE_FIELD( m_flTimeEnemyAcquired, FIELD_TIME ), + DEFINE_FIELD( m_hTargetEnt, FIELD_EHANDLE ), + DEFINE_EMBEDDED( m_GiveUpOnDeadEnemyTimer ), + DEFINE_EMBEDDED( m_FailChooseEnemyTimer ), + DEFINE_FIELD( m_EnemiesSerialNumber, FIELD_INTEGER ), + DEFINE_FIELD( m_flAcceptableTimeSeenEnemy, FIELD_TIME ), + DEFINE_EMBEDDED( m_UpdateEnemyPosTimer ), + // m_flTimeAnyUpdateEnemyPos (static) + DEFINE_FIELD( m_vecCommandGoal, FIELD_VECTOR ), + DEFINE_EMBEDDED( m_CommandMoveMonitor ), + DEFINE_FIELD( m_flSoundWaitTime, FIELD_TIME ), + DEFINE_FIELD( m_nSoundPriority, FIELD_INTEGER ), + DEFINE_FIELD( m_flIgnoreDangerSoundsUntil, FIELD_TIME ), + DEFINE_FIELD( m_afCapability, FIELD_INTEGER ), + DEFINE_FIELD( m_flMoveWaitFinished, FIELD_TIME ), + DEFINE_FIELD( m_hOpeningDoor, FIELD_EHANDLE ), + DEFINE_EMBEDDEDBYREF( m_pNavigator ), + DEFINE_EMBEDDEDBYREF( m_pLocalNavigator ), + DEFINE_EMBEDDEDBYREF( m_pPathfinder ), + DEFINE_EMBEDDEDBYREF( m_pMoveProbe ), + DEFINE_EMBEDDEDBYREF( m_pMotor ), + DEFINE_UTLVECTOR(m_UnreachableEnts, FIELD_EMBEDDED), + DEFINE_FIELD( m_hInteractionPartner, FIELD_EHANDLE ), + DEFINE_FIELD( m_hLastInteractionTestTarget, FIELD_EHANDLE ), + DEFINE_FIELD( m_hForcedInteractionPartner, FIELD_EHANDLE ), + DEFINE_FIELD( m_vecForcedWorldPosition, FIELD_POSITION_VECTOR ), + DEFINE_FIELD( m_bCannotDieDuringInteraction, FIELD_BOOLEAN ), + DEFINE_FIELD( m_iInteractionState, FIELD_INTEGER ), + DEFINE_FIELD( m_iInteractionPlaying, FIELD_INTEGER ), + DEFINE_UTLVECTOR(m_ScriptedInteractions,FIELD_EMBEDDED), + DEFINE_FIELD( m_flInteractionYaw, FIELD_FLOAT ), + DEFINE_EMBEDDED( m_CheckOnGroundTimer ), + DEFINE_FIELD( m_vDefaultEyeOffset, FIELD_VECTOR ), + DEFINE_FIELD( m_flNextEyeLookTime, FIELD_TIME ), + DEFINE_FIELD( m_flEyeIntegRate, FIELD_FLOAT ), + DEFINE_FIELD( m_vEyeLookTarget, FIELD_POSITION_VECTOR ), + DEFINE_FIELD( m_vCurEyeTarget, FIELD_POSITION_VECTOR ), + DEFINE_FIELD( m_hEyeLookTarget, FIELD_EHANDLE ), + DEFINE_FIELD( m_flHeadYaw, FIELD_FLOAT ), + DEFINE_FIELD( m_flHeadPitch, FIELD_FLOAT ), + DEFINE_FIELD( m_flOriginalYaw, FIELD_FLOAT ), + DEFINE_FIELD( m_bInAScript, FIELD_BOOLEAN ), + DEFINE_FIELD( m_scriptState, FIELD_INTEGER ), + DEFINE_FIELD( m_hCine, FIELD_EHANDLE ), + DEFINE_CUSTOM_FIELD( m_ScriptArrivalActivity, ActivityDataOps() ), + DEFINE_FIELD( m_strScriptArrivalSequence, FIELD_STRING ), + DEFINE_FIELD( m_flSceneTime, FIELD_TIME ), + DEFINE_FIELD( m_iszSceneCustomMoveSeq, FIELD_STRING ), + // m_pEnemies Saved specially in ai_saverestore.cpp + DEFINE_FIELD( m_afMemory, FIELD_INTEGER ), + DEFINE_FIELD( m_hEnemyOccluder, FIELD_EHANDLE ), + DEFINE_FIELD( m_flSumDamage, FIELD_FLOAT ), + DEFINE_FIELD( m_flLastDamageTime, FIELD_TIME ), + DEFINE_FIELD( m_flLastPlayerDamageTime, FIELD_TIME ), + DEFINE_FIELD( m_flLastSawPlayerTime, FIELD_TIME ), + DEFINE_FIELD( m_flLastAttackTime, FIELD_TIME ), + DEFINE_FIELD( m_flLastEnemyTime, FIELD_TIME ), + DEFINE_FIELD( m_flNextWeaponSearchTime, FIELD_TIME ), + DEFINE_FIELD( m_iszPendingWeapon, FIELD_STRING ), + DEFINE_EMBEDDED( m_ShotRegulator ), + DEFINE_FIELD( m_iDesiredWeaponState, FIELD_INTEGER ), + // m_pSquad Saved specially in ai_saverestore.cpp + DEFINE_KEYFIELD(m_SquadName, FIELD_STRING, "squadname" ), + DEFINE_FIELD( m_iMySquadSlot, FIELD_INTEGER ), + DEFINE_KEYFIELD( m_strHintGroup, FIELD_STRING, "hintgroup" ), + DEFINE_KEYFIELD( m_bHintGroupNavLimiting, FIELD_BOOLEAN, "hintlimiting" ), + DEFINE_EMBEDDEDBYREF( m_pTacticalServices ), + DEFINE_FIELD( m_flWaitFinished, FIELD_TIME ), + DEFINE_FIELD( m_flNextFlinchTime, FIELD_TIME ), + DEFINE_FIELD( m_flNextDodgeTime, FIELD_TIME ), + DEFINE_EMBEDDED( m_MoveAndShootOverlay ), + DEFINE_FIELD( m_vecLastPosition, FIELD_POSITION_VECTOR ), + DEFINE_FIELD( m_vSavePosition, FIELD_POSITION_VECTOR ), + DEFINE_FIELD( m_vInterruptSavePosition, FIELD_POSITION_VECTOR ), + DEFINE_FIELD( m_pHintNode, FIELD_EHANDLE), + DEFINE_FIELD( m_cAmmoLoaded, FIELD_INTEGER ), + DEFINE_FIELD( m_flDistTooFar, FIELD_FLOAT ), + DEFINE_FIELD( m_hGoalEnt, FIELD_EHANDLE ), + DEFINE_FIELD( m_flTimeLastMovement, FIELD_TIME ), + DEFINE_KEYFIELD(m_spawnEquipment, FIELD_STRING, "additionalequipment" ), + DEFINE_FIELD( m_fNoDamageDecal, FIELD_BOOLEAN ), + DEFINE_FIELD( m_hStoredPathTarget, FIELD_EHANDLE ), + DEFINE_FIELD( m_vecStoredPathGoal, FIELD_POSITION_VECTOR ), + DEFINE_FIELD( m_nStoredPathType, FIELD_INTEGER ), + DEFINE_FIELD( m_fStoredPathFlags, FIELD_INTEGER ), + DEFINE_FIELD( m_bDidDeathCleanup, FIELD_BOOLEAN ), + DEFINE_FIELD( m_bCrouchDesired, FIELD_BOOLEAN ), + DEFINE_FIELD( m_bForceCrouch, FIELD_BOOLEAN ), + DEFINE_FIELD( m_bIsCrouching, FIELD_BOOLEAN ), + DEFINE_FIELD( m_bPerformAvoidance, FIELD_BOOLEAN ), + DEFINE_FIELD( m_bIsMoving, FIELD_BOOLEAN ), + DEFINE_FIELD( m_bFadeCorpse, FIELD_BOOLEAN ), + DEFINE_FIELD( m_iDeathPose, FIELD_INTEGER ), + DEFINE_FIELD( m_iDeathFrame, FIELD_INTEGER ), + DEFINE_FIELD( m_bCheckContacts, FIELD_BOOLEAN ), + DEFINE_FIELD( m_bSpeedModActive, FIELD_BOOLEAN ), + DEFINE_FIELD( m_iSpeedModRadius, FIELD_INTEGER ), + DEFINE_FIELD( m_iSpeedModSpeed, FIELD_INTEGER ), + DEFINE_FIELD( m_hEnemyFilter, FIELD_EHANDLE ), + DEFINE_KEYFIELD( m_iszEnemyFilterName, FIELD_STRING, "enemyfilter" ), + DEFINE_FIELD( m_bImportanRagdoll, FIELD_BOOLEAN ), + + // m_fIsUsingSmallHull TODO -- This needs more consideration than simple save/load + // m_failText DEBUG + // m_interruptText DEBUG + // m_failedSchedule DEBUG + // m_interuptSchedule DEBUG + // m_nDebugCurIndex DEBUG + + // m_LastShootAccuracy DEBUG + // m_RecentShotAccuracy DEBUG + // m_TotalShots DEBUG + // m_TotalHits DEBUG + // m_bSelected DEBUG + // m_TimeLastShotMark DEBUG + + + // Outputs + DEFINE_OUTPUT( m_OnDamaged, "OnDamaged" ), + DEFINE_OUTPUT( m_OnDeath, "OnDeath" ), + DEFINE_OUTPUT( m_OnHalfHealth, "OnHalfHealth" ), + DEFINE_OUTPUT( m_OnFoundEnemy, "OnFoundEnemy" ), + DEFINE_OUTPUT( m_OnLostEnemyLOS, "OnLostEnemyLOS" ), + DEFINE_OUTPUT( m_OnLostEnemy, "OnLostEnemy" ), + DEFINE_OUTPUT( m_OnFoundPlayer, "OnFoundPlayer" ), + DEFINE_OUTPUT( m_OnLostPlayerLOS, "OnLostPlayerLOS" ), + DEFINE_OUTPUT( m_OnLostPlayer, "OnLostPlayer" ), + DEFINE_OUTPUT( m_OnHearWorld, "OnHearWorld" ), + DEFINE_OUTPUT( m_OnHearPlayer, "OnHearPlayer" ), + DEFINE_OUTPUT( m_OnHearCombat, "OnHearCombat" ), + DEFINE_OUTPUT( m_OnDamagedByPlayer, "OnDamagedByPlayer" ), + DEFINE_OUTPUT( m_OnDamagedByPlayerSquad, "OnDamagedByPlayerSquad" ), + DEFINE_OUTPUT( m_OnDenyCommanderUse, "OnDenyCommanderUse" ), + DEFINE_OUTPUT( m_OnRappelTouchdown, "OnRappelTouchdown" ), + DEFINE_OUTPUT( m_OnWake, "OnWake" ), + DEFINE_OUTPUT( m_OnSleep, "OnSleep" ), + DEFINE_OUTPUT( m_OnForcedInteractionAborted, "OnForcedInteractionAborted" ), + DEFINE_OUTPUT( m_OnForcedInteractionFinished, "OnForcedInteractionFinished" ), + + // Inputs + DEFINE_INPUTFUNC( FIELD_STRING, "SetRelationship", InputSetRelationship ), + DEFINE_INPUTFUNC( FIELD_INTEGER, "SetHealth", InputSetHealth ), + DEFINE_INPUTFUNC( FIELD_VOID, "BeginRappel", InputBeginRappel ), + DEFINE_INPUTFUNC( FIELD_STRING, "SetSquad", InputSetSquad ), + DEFINE_INPUTFUNC( FIELD_VOID, "Wake", InputWake ), + DEFINE_INPUTFUNC( FIELD_STRING, "ForgetEntity", InputForgetEntity ), + DEFINE_INPUTFUNC( FIELD_FLOAT, "IgnoreDangerSounds", InputIgnoreDangerSounds ), + DEFINE_INPUTFUNC( FIELD_VOID, "Break", InputBreak ), + DEFINE_INPUTFUNC( FIELD_VOID, "StartScripting", InputStartScripting ), + DEFINE_INPUTFUNC( FIELD_VOID, "StopScripting", InputStopScripting ), + DEFINE_INPUTFUNC( FIELD_VOID, "GagEnable", InputGagEnable ), + DEFINE_INPUTFUNC( FIELD_VOID, "GagDisable", InputGagDisable ), + DEFINE_INPUTFUNC( FIELD_VOID, "InsideTransition", InputInsideTransition ), + DEFINE_INPUTFUNC( FIELD_VOID, "OutsideTransition", InputOutsideTransition ), + DEFINE_INPUTFUNC( FIELD_VOID, "ActivateSpeedModifier", InputActivateSpeedModifier ), + DEFINE_INPUTFUNC( FIELD_VOID, "DisableSpeedModifier", InputDisableSpeedModifier ), + DEFINE_INPUTFUNC( FIELD_INTEGER, "SetSpeedModRadius", InputSetSpeedModifierRadius ), + DEFINE_INPUTFUNC( FIELD_INTEGER, "SetSpeedModSpeed", InputSetSpeedModifierSpeed ), + DEFINE_INPUTFUNC( FIELD_VOID, "HolsterWeapon", InputHolsterWeapon ), + DEFINE_INPUTFUNC( FIELD_VOID, "HolsterAndDestroyWeapon", InputHolsterAndDestroyWeapon ), + DEFINE_INPUTFUNC( FIELD_VOID, "UnholsterWeapon", InputUnholsterWeapon ), + DEFINE_INPUTFUNC( FIELD_STRING, "ForceInteractionWithNPC", InputForceInteractionWithNPC ), + DEFINE_INPUTFUNC( FIELD_STRING, "UpdateEnemyMemory", InputUpdateEnemyMemory ), + + // Function pointers + DEFINE_USEFUNC( NPCUse ), + DEFINE_THINKFUNC( CallNPCThink ), + DEFINE_THINKFUNC( CorpseFallThink ), + DEFINE_THINKFUNC( NPCInitThink ), + +END_DATADESC() + +BEGIN_SIMPLE_DATADESC( AIScheduleState_t ) + DEFINE_FIELD( iCurTask, FIELD_INTEGER ), + DEFINE_FIELD( fTaskStatus, FIELD_INTEGER ), + DEFINE_FIELD( timeStarted, FIELD_TIME ), + DEFINE_FIELD( timeCurTaskStarted, FIELD_TIME ), + DEFINE_FIELD( taskFailureCode, FIELD_INTEGER ), + DEFINE_FIELD( iTaskInterrupt, FIELD_INTEGER ), + DEFINE_FIELD( bTaskRanAutomovement, FIELD_BOOLEAN ), + DEFINE_FIELD( bTaskUpdatedYaw, FIELD_BOOLEAN ), +END_DATADESC() + + +IMPLEMENT_SERVERCLASS_ST( CAI_BaseNPC, DT_AI_BaseNPC ) + SendPropInt( SENDINFO( m_lifeState ), 3, SPROP_UNSIGNED ), + SendPropBool( SENDINFO( m_bPerformAvoidance ) ), + SendPropBool( SENDINFO( m_bIsMoving ) ), + SendPropBool( SENDINFO( m_bFadeCorpse ) ), + SendPropInt( SENDINFO( m_iDeathPose ), ANIMATION_SEQUENCE_BITS ), + SendPropInt( SENDINFO( m_iDeathFrame ), 5 ), + SendPropBool( SENDINFO( m_bSpeedModActive ) ), + SendPropInt( SENDINFO( m_iSpeedModRadius ) ), + SendPropInt( SENDINFO( m_iSpeedModSpeed ) ), + SendPropBool( SENDINFO( m_bImportanRagdoll ) ), +END_SEND_TABLE() + +//------------------------------------- + +BEGIN_SIMPLE_DATADESC( UnreachableEnt_t ) + + DEFINE_FIELD( hUnreachableEnt, FIELD_EHANDLE ), + DEFINE_FIELD( fExpireTime, FIELD_TIME ), + DEFINE_FIELD( vLocationWhenUnreachable, FIELD_POSITION_VECTOR ), + +END_DATADESC() + +//------------------------------------- + +BEGIN_SIMPLE_DATADESC( ScriptedNPCInteraction_Phases_t ) +DEFINE_FIELD( iszSequence, FIELD_STRING ), +DEFINE_FIELD( iActivity, FIELD_INTEGER ), +END_DATADESC() + +//------------------------------------- + +BEGIN_SIMPLE_DATADESC( ScriptedNPCInteraction_t ) + DEFINE_FIELD( iszInteractionName, FIELD_STRING ), + DEFINE_FIELD( iFlags, FIELD_INTEGER ), + DEFINE_FIELD( iTriggerMethod, FIELD_INTEGER ), + DEFINE_FIELD( iLoopBreakTriggerMethod, FIELD_INTEGER ), + DEFINE_FIELD( vecRelativeOrigin, FIELD_VECTOR ), + DEFINE_FIELD( angRelativeAngles, FIELD_VECTOR ), + DEFINE_FIELD( vecRelativeVelocity, FIELD_VECTOR ), + DEFINE_FIELD( flDelay, FIELD_FLOAT ), + DEFINE_FIELD( flDistSqr, FIELD_FLOAT ), + DEFINE_FIELD( iszMyWeapon, FIELD_STRING ), + DEFINE_FIELD( iszTheirWeapon, FIELD_STRING ), + DEFINE_EMBEDDED_ARRAY( sPhases, SNPCINT_NUM_PHASES ), + DEFINE_FIELD( matDesiredLocalToWorld, FIELD_VMATRIX ), + DEFINE_FIELD( bValidOnCurrentEnemy, FIELD_BOOLEAN ), + DEFINE_FIELD( flNextAttemptTime, FIELD_TIME ), +END_DATADESC() + +//------------------------------------- + +void CAI_BaseNPC::PostConstructor( const char *szClassname ) +{ + BaseClass::PostConstructor( szClassname ); + CreateComponents(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CAI_BaseNPC::Activate( void ) +{ + BaseClass::Activate(); + + if ( GetModelPtr() ) + { + ParseScriptedNPCInteractions(); + } + + // Get a handle to my enemy filter entity if there is one. + if ( m_iszEnemyFilterName != NULL_STRING ) + { + CBaseEntity *pFilter = gEntList.FindEntityByName( NULL, m_iszEnemyFilterName ); + if ( pFilter != NULL ) + { + m_hEnemyFilter = dynamic_cast(pFilter); + } + } +} + +void CAI_BaseNPC::Precache( void ) +{ + gm_iszPlayerSquad = AllocPooledString( PLAYER_SQUADNAME ); // cache for fast IsPlayerSquad calls + + if ( m_spawnEquipment != NULL_STRING && strcmp(STRING(m_spawnEquipment), "0") ) + { + UTIL_PrecacheOther( STRING(m_spawnEquipment) ); + } + + // Make sure schedules are loaded for this NPC type + if (!LoadedSchedules()) + { + DevMsg("ERROR: Rejecting spawn of %s as error in NPC's schedules.\n",GetDebugName()); + UTIL_Remove(this); + return; + } + + PrecacheScriptSound( "AI_BaseNPC.SwishSound" ); + PrecacheScriptSound( "AI_BaseNPC.BodyDrop_Heavy" ); + PrecacheScriptSound( "AI_BaseNPC.BodyDrop_Light" ); + PrecacheScriptSound( "AI_BaseNPC.SentenceStop" ); + + BaseClass::Precache(); +} + + +//----------------------------------------------------------------------------- + +const short AI_EXTENDED_SAVE_HEADER_VERSION = 5; +const short AI_EXTENDED_SAVE_HEADER_RESET_VERSION = 3; + +const short AI_EXTENDED_SAVE_HEADER_FIRST_VERSION_WITH_CONDITIONS = 2; +const short AI_EXTENDED_SAVE_HEADER_FIRST_VERSION_WITH_SCHEDULE_ID_FIXUP = 3; +const short AI_EXTENDED_SAVE_HEADER_FIRST_VERSION_WITH_SEQUENCE = 4; +const short AI_EXTENDED_SAVE_HEADER_FIRST_VERSION_WITH_NAVIGATOR_SAVE = 5; + +struct AIExtendedSaveHeader_t +{ + AIExtendedSaveHeader_t() + : version(AI_EXTENDED_SAVE_HEADER_VERSION), + flags(0), + scheduleCrc(0) + { + szSchedule[0] = 0; + szIdealSchedule[0] = 0; + szFailSchedule[0] = 0; + szSequence[0] = 0; + } + + short version; + unsigned flags; + char szSchedule[128]; + CRC32_t scheduleCrc; + char szIdealSchedule[128]; + char szFailSchedule[128]; + char szSequence[128]; + + DECLARE_SIMPLE_DATADESC(); +}; + +enum AIExtendedSaveHeaderFlags_t +{ + AIESH_HAD_ENEMY = 0x01, + AIESH_HAD_TARGET = 0x02, + AIESH_HAD_NAVGOAL = 0x04, +}; + +//------------------------------------- + +BEGIN_SIMPLE_DATADESC( AIExtendedSaveHeader_t ) + DEFINE_FIELD( version, FIELD_SHORT ), + DEFINE_FIELD( flags, FIELD_INTEGER ), + DEFINE_AUTO_ARRAY( szSchedule, FIELD_CHARACTER ), + DEFINE_FIELD( scheduleCrc, FIELD_INTEGER ), + DEFINE_AUTO_ARRAY( szIdealSchedule, FIELD_CHARACTER ), + DEFINE_AUTO_ARRAY( szFailSchedule, FIELD_CHARACTER ), + DEFINE_AUTO_ARRAY( szSequence, FIELD_CHARACTER ), +END_DATADESC() + +//------------------------------------- + +int CAI_BaseNPC::Save( ISave &save ) +{ + AIExtendedSaveHeader_t saveHeader; + + if ( GetEnemy() ) + saveHeader.flags |= AIESH_HAD_ENEMY; + if ( GetTarget() ) + saveHeader.flags |= AIESH_HAD_TARGET; + if ( GetNavigator()->IsGoalActive() ) + saveHeader.flags |= AIESH_HAD_NAVGOAL; + + if ( m_pSchedule ) + { + const char *pszSchedule = m_pSchedule->GetName(); + + Assert( static_cast(Q_strlen( pszSchedule )) < sizeof( saveHeader.szSchedule ) - 1 ); + Q_strncpy( saveHeader.szSchedule, pszSchedule, sizeof( saveHeader.szSchedule ) ); + + CRC32_Init( &saveHeader.scheduleCrc ); + CRC32_ProcessBuffer( &saveHeader.scheduleCrc, (void *)m_pSchedule->GetTaskList(), m_pSchedule->NumTasks() * sizeof(Task_t) ); + CRC32_Final( &saveHeader.scheduleCrc ); + } + else + { + saveHeader.szSchedule[0] = 0; + saveHeader.scheduleCrc = 0; + } + + int idealSchedule = GetGlobalScheduleId( m_IdealSchedule ); + + if ( idealSchedule != -1 && idealSchedule != AI_RemapToGlobal( SCHED_NONE ) && idealSchedule != AI_RemapToGlobal( SCHED_AISCRIPT ) ) + { + CAI_Schedule *pIdealSchedule = GetSchedule( m_IdealSchedule ); + if ( pIdealSchedule ) + { + const char *pszIdealSchedule = pIdealSchedule->GetName(); + Assert( static_cast(Q_strlen( pszIdealSchedule )) < sizeof( saveHeader.szIdealSchedule ) - 1 ); + Q_strncpy( saveHeader.szIdealSchedule, pszIdealSchedule, sizeof( saveHeader.szIdealSchedule ) ); + } + } + + int failSchedule = GetGlobalScheduleId( m_failSchedule ); + if ( failSchedule != -1 && failSchedule != AI_RemapToGlobal( SCHED_NONE ) && failSchedule != AI_RemapToGlobal( SCHED_AISCRIPT ) ) + { + CAI_Schedule *pFailSchedule = GetSchedule( m_failSchedule ); + if ( pFailSchedule ) + { + const char *pszFailSchedule = pFailSchedule->GetName(); + Assert( static_cast(Q_strlen( pszFailSchedule )) < sizeof( saveHeader.szFailSchedule ) - 1 ); + Q_strncpy( saveHeader.szFailSchedule, pszFailSchedule, sizeof( saveHeader.szFailSchedule ) ); + } + } + + if ( GetSequence() != ACT_INVALID && GetModelPtr() ) + { + const char *pszSequenceName = GetSequenceName( GetSequence() ); + if ( pszSequenceName && *pszSequenceName ) + { + Assert( static_cast(Q_strlen( pszSequenceName )) < sizeof( saveHeader.szSequence ) - 1 ); + Q_strncpy( saveHeader.szSequence, pszSequenceName, sizeof(saveHeader.szSequence) ); + } + } + + save.WriteAll( &saveHeader ); + + save.StartBlock(); + SaveConditions( save, m_Conditions ); + SaveConditions( save, m_CustomInterruptConditions ); + SaveConditions( save, m_ConditionsPreIgnore ); + CAI_ScheduleBits ignoreConditions; + m_InverseIgnoreConditions.Not( &ignoreConditions ); + SaveConditions( save, ignoreConditions ); + save.EndBlock(); + + save.StartBlock(); + GetNavigator()->Save( save ); + save.EndBlock(); + + return BaseClass::Save(save); +} + +//------------------------------------- + +void CAI_BaseNPC::DiscardScheduleState() +{ + // We don't save/restore routes yet + GetNavigator()->ClearGoal(); + + // We don't save/restore schedules yet + ClearSchedule(); + + // Reset animation + m_Activity = ACT_RESET; + + // If we don't have an enemy, clear conditions like see enemy, etc. + if ( GetEnemy() == NULL ) + { + m_Conditions.ClearAllBits(); + } + + // went across a transition and lost my m_hCine + bool bLostScript = ( m_NPCState == NPC_STATE_SCRIPT && m_hCine == NULL ); + if ( bLostScript ) + { + // UNDONE: Do something better here? + // for now, just go back to idle and let the AI figure out what to do. + SetState( NPC_STATE_IDLE ); + SetIdealState( NPC_STATE_IDLE ); + DevMsg(1, "Scripted Sequence stripped on level transition for %s\n", GetDebugName() ); + } +} + +//------------------------------------- + +void CAI_BaseNPC::OnRestore() +{ + gm_iszPlayerSquad = AllocPooledString( PLAYER_SQUADNAME ); // cache for fast IsPlayerSquad calls + + if ( m_bDoPostRestoreRefindPath && CAI_NetworkManager::NetworksLoaded() ) + { + CAI_DynamicLink::InitDynamicLinks(); + if ( !GetNavigator()->RefindPathToGoal( false ) ) + DiscardScheduleState(); + } + else + { + GetNavigator()->ClearGoal(); + } + BaseClass::OnRestore(); + m_bCheckContacts = true; +} + + +//------------------------------------- + +int CAI_BaseNPC::Restore( IRestore &restore ) +{ + AIExtendedSaveHeader_t saveHeader; + restore.ReadAll( &saveHeader ); + + if ( saveHeader.version >= AI_EXTENDED_SAVE_HEADER_FIRST_VERSION_WITH_SCHEDULE_ID_FIXUP ) + { + if ( saveHeader.szIdealSchedule[0] ) + { + CAI_Schedule *pIdealSchedule = g_AI_SchedulesManager.GetScheduleByName( saveHeader.szIdealSchedule ); + m_IdealSchedule = ( pIdealSchedule ) ? pIdealSchedule->GetId() : SCHED_NONE; + } + + if ( saveHeader.szFailSchedule[0] ) + { + CAI_Schedule *pFailSchedule = g_AI_SchedulesManager.GetScheduleByName( saveHeader.szFailSchedule ); + m_failSchedule = ( pFailSchedule ) ? pFailSchedule->GetId() : SCHED_NONE; + } + } + + if ( saveHeader.version >= AI_EXTENDED_SAVE_HEADER_FIRST_VERSION_WITH_CONDITIONS ) + { + restore.StartBlock(); + RestoreConditions( restore, &m_Conditions ); + RestoreConditions( restore, &m_CustomInterruptConditions ); + RestoreConditions( restore, &m_ConditionsPreIgnore ); + CAI_ScheduleBits ignoreConditions; + RestoreConditions( restore, &ignoreConditions ); + ignoreConditions.Not( &m_InverseIgnoreConditions ); + restore.EndBlock(); + } + + if ( saveHeader.version >= AI_EXTENDED_SAVE_HEADER_FIRST_VERSION_WITH_NAVIGATOR_SAVE ) + { + restore.StartBlock(); + GetNavigator()->Restore( restore ); + restore.EndBlock(); + } + + // do a normal restore + int status = BaseClass::Restore(restore); + if ( !status ) + return 0; + + bool bLostSequence = false; + if ( saveHeader.version >= AI_EXTENDED_SAVE_HEADER_FIRST_VERSION_WITH_SEQUENCE && saveHeader.szSequence[0] && GetModelPtr() ) + { + SetSequence( LookupSequence( saveHeader.szSequence ) ); + if ( GetSequence() == ACT_INVALID ) + { + DevMsg( this, AIMF_IGNORE_SELECTED, "Discarding missing sequence %s on load.\n", saveHeader.szSequence ); + SetSequence( 0 ); + bLostSequence = true; + } + + Assert( IsValidSequence( GetSequence() ) ); + } + + bool bLostScript = ( m_NPCState == NPC_STATE_SCRIPT && m_hCine == NULL ); + bool bDiscardScheduleState = ( bLostScript || + bLostSequence || + saveHeader.szSchedule[0] == 0 || + saveHeader.version < AI_EXTENDED_SAVE_HEADER_RESET_VERSION || + ( (saveHeader.flags & AIESH_HAD_ENEMY) && !GetEnemy() ) || + ( (saveHeader.flags & AIESH_HAD_TARGET) && !GetTarget() ) ); + + if ( m_ScheduleState.taskFailureCode >= NUM_FAIL_CODES ) + m_ScheduleState.taskFailureCode = FAIL_NO_TARGET; // must have been a string, gotta punt + + if ( !bDiscardScheduleState ) + { + m_pSchedule = g_AI_SchedulesManager.GetScheduleByName( saveHeader.szSchedule ); + if ( m_pSchedule ) + { + CRC32_t scheduleCrc; + CRC32_Init( &scheduleCrc ); + CRC32_ProcessBuffer( &scheduleCrc, (void *)m_pSchedule->GetTaskList(), m_pSchedule->NumTasks() * sizeof(Task_t) ); + CRC32_Final( &scheduleCrc ); + + if ( scheduleCrc != saveHeader.scheduleCrc ) + { + m_pSchedule = NULL; + } + } + } + + if ( !m_pSchedule ) + bDiscardScheduleState = true; + + if ( !bDiscardScheduleState ) + m_bDoPostRestoreRefindPath = ( ( saveHeader.flags & AIESH_HAD_NAVGOAL) != 0 ); + else + { + m_bDoPostRestoreRefindPath = false; + DiscardScheduleState(); + } + + return status; +} + +//------------------------------------- + +void CAI_BaseNPC::SaveConditions( ISave &save, const CAI_ScheduleBits &conditions ) +{ + for (int i = 0; i < MAX_CONDITIONS; i++) + { + if (conditions.GetBit(i)) + { + const char *pszConditionName = ConditionName(AI_RemapToGlobal(i)); + if ( !pszConditionName ) + break; + save.WriteString( pszConditionName ); + } + } + save.WriteString( "" ); +} + +//------------------------------------- + +void CAI_BaseNPC::RestoreConditions( IRestore &restore, CAI_ScheduleBits *pConditions ) +{ + pConditions->ClearAllBits(); + char szCondition[256]; + for (;;) + { + restore.ReadString( szCondition, sizeof(szCondition), 0 ); + if ( !szCondition[0] ) + break; + int iCondition = GetSchedulingSymbols()->ConditionSymbolToId( szCondition ); + if ( iCondition != -1 ) + pConditions->SetBit( AI_RemapFromGlobal( iCondition ) ); + } +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +bool CAI_BaseNPC::KeyValue( const char *szKeyName, const char *szValue ) +{ + bool bResult = BaseClass::KeyValue( szKeyName, szValue ); + + if( !bResult ) + { + // Defer unhandled Keys to behaviors + CAI_BehaviorBase **ppBehaviors = AccessBehaviors(); + + for ( int i = 0; i < NumBehaviors(); i++ ) + { + if( ppBehaviors[ i ]->KeyValue( szKeyName, szValue ) ) + { + return true; + } + } + } + + return bResult; +} + +//----------------------------------------------------------------------------- +// Purpose: Debug function to make this NPC freeze in place (or unfreeze). +//----------------------------------------------------------------------------- +void CAI_BaseNPC::ToggleFreeze(void) +{ + if (!IsCurSchedule(SCHED_NPC_FREEZE)) + { + // Freeze them. + SetCondition(COND_NPC_FREEZE); + SetMoveType(MOVETYPE_NONE); + SetGravity(0); + SetLocalAngularVelocity(vec3_angle); + SetAbsVelocity( vec3_origin ); + } + else + { + // Unfreeze them. + SetCondition(COND_NPC_UNFREEZE); + m_Activity = ACT_RESET; + + // BUGBUG: this might not be the correct movetype! + SetMoveType( MOVETYPE_STEP ); + + // Doesn't restore gravity to the original value, but who cares? + SetGravity(1); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Written by subclasses macro to load schedules +// Input : +// Output : +//----------------------------------------------------------------------------- +bool CAI_BaseNPC::LoadSchedules(void) +{ + return true; +} + +//----------------------------------------------------------------------------- + +bool CAI_BaseNPC::LoadedSchedules(void) +{ + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: Constructor +// Input : +// Output : +//----------------------------------------------------------------------------- +CAI_BaseNPC::CAI_BaseNPC(void) + : m_UnreachableEnts( 0, 4 ) +{ + m_pMotor = NULL; + m_pMoveProbe = NULL; + m_pNavigator = NULL; + m_pSenses = NULL; + m_pPathfinder = NULL; + m_pLocalNavigator = NULL; + + m_pSchedule = NULL; + m_IdealSchedule = SCHED_NONE; + +#ifdef _DEBUG + // necessary since in debug, we initialize vectors to NAN for debugging + m_vecLastPosition.Init(); + m_vSavePosition.Init(); + m_vEyeLookTarget.Init(); + m_vCurEyeTarget.Init(); + m_vDefaultEyeOffset.Init(); + +#endif + m_bDidDeathCleanup = false; + + m_afCapability = 0; // Make sure this is cleared in the base class + + SetHullType(HULL_HUMAN); // Give human hull by default, subclasses should override + + m_iMySquadSlot = SQUAD_SLOT_NONE; + m_flSumDamage = 0; + m_flLastDamageTime = 0; + m_flLastAttackTime = 0; + m_flSoundWaitTime = 0; + m_flNextEyeLookTime = 0; + m_flHeadYaw = 0; + m_flHeadPitch = 0; + m_spawnEquipment = NULL_STRING; + m_pEnemies = new CAI_Enemies; + m_flEyeIntegRate = 0.95; + SetTarget( NULL ); + + m_pSquad = NULL; + + m_flMoveWaitFinished = 0; + + m_fIsUsingSmallHull = true; + + m_bHintGroupNavLimiting = false; + + m_fNoDamageDecal = false; + + SetInAScript( false ); + + m_pLockedBestSound = new CSound; + m_pLockedBestSound->m_iType = SOUND_NONE; + + // ---------------------------- + // Debugging fields + // ---------------------------- + m_interruptText = NULL; + m_failText = NULL; + m_failedSchedule = NULL; + m_interuptSchedule = NULL; + m_nDebugPauseIndex = 0; + + g_AI_Manager.AddAI( this ); + + if ( g_AI_Manager.NumAIs() == 1 ) + { + m_AnyUpdateEnemyPosTimer.Force(); + gm_flTimeLastSpawn = -1; + gm_nSpawnedThisFrame = 0; + gm_iNextThinkRebalanceTick = 0; + } + + m_iFrameBlocked = -1; + m_bInChoreo = true; // assume so until call to UpdateEfficiency() + + SetCollisionGroup( COLLISION_GROUP_NPC ); +} + +//----------------------------------------------------------------------------- +// Purpose: Destructor +// Input : +// Output : +//----------------------------------------------------------------------------- +CAI_BaseNPC::~CAI_BaseNPC(void) +{ + g_AI_Manager.RemoveAI( this ); + + delete m_pLockedBestSound; + + RemoveMemory(); + + delete m_pPathfinder; + delete m_pNavigator; + delete m_pMotor; + delete m_pLocalNavigator; + delete m_pMoveProbe; + delete m_pSenses; + delete m_pTacticalServices; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CAI_BaseNPC::UpdateOnRemove(void) +{ + if ( !m_bDidDeathCleanup ) + { + if ( m_NPCState == NPC_STATE_DEAD ) + DevMsg( "May not have cleaned up on NPC death\n"); + + CleanupOnDeath( NULL, false ); + } + + // Chain at end to mimic destructor unwind order + BaseClass::UpdateOnRemove(); +} + +//----------------------------------------------------------------------------- + +bool CAI_BaseNPC::CreateComponents() +{ + m_pSenses = CreateSenses(); + if ( !m_pSenses ) + return false; + + m_pMotor = CreateMotor(); + if ( !m_pMotor ) + return false; + + m_pLocalNavigator = CreateLocalNavigator(); + if ( !m_pLocalNavigator ) + return false; + + m_pMoveProbe = CreateMoveProbe(); + if ( !m_pMoveProbe ) + return false; + + m_pNavigator = CreateNavigator(); + if ( !m_pNavigator ) + return false; + + m_pPathfinder = CreatePathfinder(); + if ( !m_pPathfinder ) + return false; + + m_pTacticalServices = CreateTacticalServices(); + if ( !m_pTacticalServices ) + return false; + + m_MoveAndShootOverlay.SetOuter( this ); + + m_pMotor->Init( m_pLocalNavigator ); + m_pLocalNavigator->Init( m_pNavigator ); + m_pNavigator->Init( g_pBigAINet ); + m_pPathfinder->Init( g_pBigAINet ); + m_pTacticalServices->Init( g_pBigAINet ); + + return true; +} + +//----------------------------------------------------------------------------- + +CAI_Senses *CAI_BaseNPC::CreateSenses() +{ + CAI_Senses *pSenses = new CAI_Senses; + pSenses->SetOuter( this ); + return pSenses; +} + +//----------------------------------------------------------------------------- + +CAI_Motor *CAI_BaseNPC::CreateMotor() +{ + return new CAI_Motor( this ); +} + +//----------------------------------------------------------------------------- + +CAI_MoveProbe *CAI_BaseNPC::CreateMoveProbe() +{ + return new CAI_MoveProbe( this ); +} + +//----------------------------------------------------------------------------- + +CAI_LocalNavigator *CAI_BaseNPC::CreateLocalNavigator() +{ + return new CAI_LocalNavigator( this ); +} + +//----------------------------------------------------------------------------- + +CAI_TacticalServices *CAI_BaseNPC::CreateTacticalServices() +{ + return new CAI_TacticalServices( this ); +} + +//----------------------------------------------------------------------------- + +CAI_Navigator *CAI_BaseNPC::CreateNavigator() +{ + return new CAI_Navigator( this ); +} + +//----------------------------------------------------------------------------- + +CAI_Pathfinder *CAI_BaseNPC::CreatePathfinder() +{ + return new CAI_Pathfinder( this ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CAI_BaseNPC::InputSetRelationship( inputdata_t &inputdata ) +{ + AddRelationship( inputdata.value.String(), inputdata.pActivator ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CAI_BaseNPC::InputSetHealth( inputdata_t &inputdata ) +{ + int iNewHealth = inputdata.value.Int(); + int iDelta = abs(GetHealth() - iNewHealth); + if ( iNewHealth > GetHealth() ) + { + TakeHealth( iDelta, DMG_GENERIC ); + } + else if ( iNewHealth < GetHealth() ) + { + TakeDamage( CTakeDamageInfo( this, this, iDelta, DMG_GENERIC ) ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CAI_BaseNPC::InputBeginRappel( inputdata_t &inputdata ) +{ + BeginRappel(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CAI_BaseNPC::InputSetSquad( inputdata_t &inputdata ) +{ + if ( !( CapabilitiesGet() & bits_CAP_SQUAD ) ) + { + Warning("SetSquad Input received for NPC %s, but that NPC can't use squads.\n", GetDebugName() ); + return; + } + + m_SquadName = inputdata.value.StringID(); + + // Removing from squad? + if ( m_SquadName == NULL_STRING ) + { + if ( m_pSquad ) + { + m_pSquad->RemoveFromSquad(this, true); + m_pSquad = NULL; + } + } + else + { + m_pSquad = g_AI_SquadManager.FindCreateSquad(this, m_SquadName); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CAI_BaseNPC::InputWake( inputdata_t &inputdata ) +{ + Wake(); + + // Check if we have a path to follow. This is normally done in StartNPC, + // but putting the NPC to sleep will cancel it, so we have to do it again. + if ( m_target != NULL_STRING )// this npc has a target + { + // Find the npc's initial target entity, stash it + SetGoalEnt( gEntList.FindEntityByName( NULL, m_target ) ); + + if ( !GetGoalEnt() ) + { + Warning( "ReadyNPC()--%s couldn't find target %s\n", GetClassname(), STRING(m_target)); + } + else + { + StartTargetHandling( GetGoalEnt() ); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CAI_BaseNPC::InputForgetEntity( inputdata_t &inputdata ) +{ + const char *pszEntityToForget = inputdata.value.String(); + + if ( g_pDeveloper->GetInt() && pszEntityToForget[strlen( pszEntityToForget ) - 1] == '*' ) + DevMsg( "InputForgetEntity does not support wildcards\n" ); + + CBaseEntity *pEntity = gEntList.FindEntityByName( NULL, pszEntityToForget ); + if ( pEntity ) + { + if ( GetEnemy() == pEntity ) + { + SetEnemy( NULL ); + SetIdealState( NPC_STATE_ALERT ); + } + GetEnemies()->ClearMemory( pEntity ); + } +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CAI_BaseNPC::InputIgnoreDangerSounds( inputdata_t &inputdata ) +{ + // Default is 10 seconds. + float flDelay = 10.0f; + + if( inputdata.value.Float() > 0.0f ) + { + flDelay = inputdata.value.Float(); + } + + m_flIgnoreDangerSoundsUntil = gpGlobals->curtime + flDelay; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CAI_BaseNPC::InputUpdateEnemyMemory( inputdata_t &inputdata ) +{ + const char *pszEnemy = inputdata.value.String(); + CBaseEntity *pEnemy = gEntList.FindEntityByName( NULL, pszEnemy ); + + if( pEnemy ) + { + UpdateEnemyMemory( pEnemy, pEnemy->GetAbsOrigin(), this ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : &inputdata - +//----------------------------------------------------------------------------- +void CAI_BaseNPC::InputOutsideTransition( inputdata_t &inputdata ) +{ +} + +//----------------------------------------------------------------------------- +// Purpose: Called when this NPC transitions to another level with the player +// Input : &inputdata - +//----------------------------------------------------------------------------- +void CAI_BaseNPC::InputInsideTransition( inputdata_t &inputdata ) +{ + CleanupScriptsOnTeleport( true ); + + // If we're inside a vcd, tell it to stop + if ( IsCurSchedule( SCHED_SCENE_GENERIC, false ) ) + { + RemoveActorFromScriptedScenes( this, false ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CAI_BaseNPC::CleanupScriptsOnTeleport( bool bEnrouteAsWell ) +{ + // If I'm running a scripted sequence, I need to clean up + if ( m_NPCState == NPC_STATE_SCRIPT && m_hCine ) + { + if ( !bEnrouteAsWell ) + { + // Don't cancel scripts when they're teleporting an NPC + // to the script due to CINE_MOVETO_TELEPORT. + if ( m_hCine->IsTeleportingDueToMoveTo() ) + return; + } + + m_hCine->ScriptEntityCancel( m_hCine, true ); + } +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +bool CAI_BaseNPC::HandleInteraction(int interactionType, void *data, CBaseCombatCharacter* sourceEnt) +{ +#ifdef HL2_DLL + if ( interactionType == g_interactionBarnacleVictimGrab ) + { + // Make the victim stop thinking so they're as good as dead without + // shocking the system by destroying the entity. + StopLoopingSounds(); + BarnacleDeathSound(); + SetThink( NULL ); + + // Gag the NPC so they won't talk anymore + AddSpawnFlags( SF_NPC_GAG ); + + // Drop any weapon they're holding + if ( GetActiveWeapon() ) + { + Weapon_Drop( GetActiveWeapon() ); + } + + return true; + } +#endif // HL2_DLL + + return BaseClass::HandleInteraction( interactionType, data, sourceEnt ); +} + +CAI_BaseNPC *CAI_BaseNPC::GetInteractionPartner( void ) +{ + if ( m_hInteractionPartner == NULL ) + return NULL; + + return m_hInteractionPartner->MyNPCPointer(); +} + +//----------------------------------------------------------------------------- +// Purpose: Called when exiting a scripted sequence. +// Output : Returns true if alive, false if dead. +//----------------------------------------------------------------------------- +bool CAI_BaseNPC::ExitScriptedSequence( ) +{ + if ( m_lifeState == LIFE_DYING ) + { + // is this legal? + // BUGBUG -- This doesn't call Killed() + SetIdealState( NPC_STATE_DEAD ); + return false; + } + + if (m_hCine) + { + m_hCine->CancelScript( ); + } + + return true; +} + + +bool CAI_BaseNPC::CineCleanup() +{ + CAI_ScriptedSequence *pOldCine = m_hCine; + + bool bDestroyCine = false; + if ( IsRunningDynamicInteraction() ) + { + bDestroyCine = true; + + // Re-enable physics collisions between me & the other NPC + if ( m_hInteractionPartner ) + { + PhysEnableEntityCollisions( this, m_hInteractionPartner ); + //Msg("%s(%s) enabled collisions with %s(%s) at %0.2f\n", GetClassName(), GetDebugName(), m_hInteractionPartner->GetClassName(), m_hInteractionPartner->GetDebugName(), gpGlobals->curtime ); + } + + if ( m_hForcedInteractionPartner ) + { + // We've finished a forced interaction. Let the mapmaker know. + m_OnForcedInteractionFinished.FireOutput( this, this ); + } + + // Clear interaction partner, because we're not running a scripted sequence anymore + m_hInteractionPartner = NULL; + CleanupForcedInteraction(); + } + + // am I linked to a cinematic? + if (m_hCine) + { + // okay, reset me to what it thought I was before + m_hCine->SetTarget( NULL ); + // NOTE that this will have had EF_NODRAW removed in script.dll when it's cached off + SetEffects( m_hCine->m_saved_effects ); + } + else + { + // arg, punt + AddSolidFlags( FSOLID_NOT_STANDABLE ); + } + m_hCine = NULL; + SetTarget( NULL ); + SetGoalEnt( NULL ); + if (m_lifeState == LIFE_DYING) + { + // last frame of death animation? + m_iHealth = 0; + AddSolidFlags( FSOLID_NOT_SOLID ); + SetState( NPC_STATE_DEAD ); + m_lifeState = LIFE_DEAD; + UTIL_SetSize( this, WorldAlignMins(), Vector(WorldAlignMaxs().x, WorldAlignMaxs().y, WorldAlignMins().z + 2) ); + + if ( pOldCine && pOldCine->HasSpawnFlags( SF_SCRIPT_LEAVECORPSE ) ) + { + SetUse( NULL ); // BUGBUG -- This doesn't call Killed() + SetThink( NULL ); // This will probably break some stuff + SetTouch( NULL ); + } + else + SUB_StartFadeOut(); // SetThink( SUB_DoNothing ); + + + //Not becoming a ragdoll, so set the NOINTERP flag on. + if ( CanBecomeRagdoll() == false ) + { + StopAnimation(); + AddEffects( EF_NOINTERP ); // Don't interpolate either, assume the corpse is positioned in its final resting place + } + + SetMoveType( MOVETYPE_NONE ); + return false; + } + + // If we actually played a sequence + if ( pOldCine && pOldCine->m_iszPlay != NULL_STRING && pOldCine->PlayedSequence() ) + { + if ( !pOldCine->HasSpawnFlags(SF_SCRIPT_NOSCRIPTMOVEMENT) ) + { + // reset position + Vector new_origin; + QAngle new_angle; + GetBonePosition( 0, new_origin, new_angle ); + + // Figure out how far they have moved + // We can't really solve this problem because we can't query the movement of the origin relative + // to the sequence. We can get the root bone's position as we do here, but there are + // cases where the root bone is in a different relative position to the entity's origin + // before/after the sequence plays. So we are stuck doing this: + + // !!!HACKHACK: Float the origin up and drop to floor because some sequences have + // irregular motion that can't be properly accounted for. + + // UNDONE: THIS SHOULD ONLY HAPPEN IF WE ACTUALLY PLAYED THE SEQUENCE. + Vector oldOrigin = GetLocalOrigin(); + + // UNDONE: ugly hack. Don't move NPC if they don't "seem" to move + // this really needs to be done with the AX,AY,etc. flags, but that aren't consistantly + // being set, so animations that really do move won't be caught. + if ((oldOrigin - new_origin).Length2D() < 8.0) + new_origin = oldOrigin; + + Vector origin = GetLocalOrigin(); + + origin.x = new_origin.x; + origin.y = new_origin.y; + origin.z += 1; + + if ( GetFlags() & FL_FLY ) + { + origin.z = new_origin.z; + SetLocalOrigin( origin ); + } + else + { + SetLocalOrigin( origin ); + + int drop = UTIL_DropToFloor( this, MASK_NPCSOLID ); + + // Origin in solid? Set to org at the end of the sequence + if ( drop < 0 ) + { + SetLocalOrigin( oldOrigin ); + } + else if ( drop == 0 ) // Hanging in air? + { + Vector origin = GetLocalOrigin(); + origin.z = new_origin.z; + SetLocalOrigin( origin ); + SetGroundEntity( NULL ); + } + } + + origin = GetLocalOrigin(); + + // teleport if it's a non-trivial distance + if ((oldOrigin - origin).Length() > 8.0) + { + // Call teleport to notify + Teleport( &origin, NULL, NULL ); + SetLocalOrigin( origin ); + AddEffects( EF_NOINTERP ); + } + + if ( m_iHealth <= 0 ) + { + // Dropping out because he got killed + SetIdealState( NPC_STATE_DEAD ); + SetCondition( COND_LIGHT_DAMAGE ); + m_lifeState = LIFE_DYING; + } + } + + // We should have some animation to put these guys in, but for now it's idle. + // Due to NOINTERP above, there won't be any blending between this anim & the sequence + m_Activity = ACT_RESET; + } + + // set them back into a normal state + if ( m_iHealth > 0 ) + { + SetIdealState( NPC_STATE_IDLE ); + } + else + { + // Dropping out because he got killed + SetIdealState( NPC_STATE_DEAD ); + SetCondition( COND_LIGHT_DAMAGE ); + } + + // SetAnimation( m_NPCState ); + CLEARBITS(m_spawnflags, SF_NPC_WAIT_FOR_SCRIPT ); + + if ( bDestroyCine ) + { + UTIL_Remove( pOldCine ); + } + + return true; +} + +//----------------------------------------------------------------------------- + +void CAI_BaseNPC::Teleport( const Vector *newPosition, const QAngle *newAngles, const Vector *newVelocity ) +{ + CleanupScriptsOnTeleport( false ); + + BaseClass::Teleport( newPosition, newAngles, newVelocity ); +} + +//----------------------------------------------------------------------------- + +bool CAI_BaseNPC::FindSpotForNPCInRadius( Vector *pResult, const Vector &vStartPos, CAI_BaseNPC *pNPC, float radius, bool bOutOfPlayerViewcone ) +{ + CBasePlayer *pPlayer = AI_GetSinglePlayer(); + QAngle fan; + + fan.x = 0; + fan.z = 0; + + for( fan.y = 0 ; fan.y < 360 ; fan.y += 18.0 ) + { + Vector vecTest; + Vector vecDir; + + AngleVectors( fan, &vecDir ); + + vecTest = vStartPos + vecDir * radius; + + if ( bOutOfPlayerViewcone && pPlayer && !pPlayer->FInViewCone( vecTest ) ) + continue; + + trace_t tr; + + UTIL_TraceLine( vecTest, vecTest - Vector( 0, 0, 8192 ), MASK_SHOT, pNPC, COLLISION_GROUP_NONE, &tr ); + if( tr.fraction == 1.0 ) + { + continue; + } + + UTIL_TraceHull( tr.endpos, + tr.endpos + Vector( 0, 0, 10 ), + pNPC->GetHullMins(), + pNPC->GetHullMaxs(), + MASK_NPCSOLID, + pNPC, + COLLISION_GROUP_NONE, + &tr ); + + if( tr.fraction == 1.0 && pNPC->GetMoveProbe()->CheckStandPosition( tr.endpos, MASK_NPCSOLID ) ) + { + *pResult = tr.endpos; + return true; + } + } + return false; +} + +//----------------------------------------------------------------------------- + +bool CAI_BaseNPC::IsNavigationUrgent() +{ + // return true if the navigation is for something that can't react well to failure + if ( IsCurSchedule( SCHED_SCRIPTED_WALK, false ) || + IsCurSchedule( SCHED_SCRIPTED_RUN, false ) || + IsCurSchedule( SCHED_SCRIPTED_CUSTOM_MOVE, false ) || + ( IsCurSchedule( SCHED_SCENE_GENERIC, false ) && IsInLockedScene() ) ) + { + return true; + } + return false; +} + +//----------------------------------------------------------------------------- + +bool CAI_BaseNPC::ShouldFailNav( bool bMovementFailed ) +{ + // Robin: Only fail movement. Fail route building so that we'll keep trying to build routes. + // Otherwise, we can can get stuck inside a movement task without a route. + if ( bMovementFailed && IsNavigationUrgent()) + { + return false; + } + return true; +} + +Navigation_t CAI_BaseNPC::GetNavType() const +{ + return m_pNavigator->GetNavType(); +} + +void CAI_BaseNPC::SetNavType( Navigation_t navType ) +{ + m_pNavigator->SetNavType( navType ); +} + +//----------------------------------------------------------------------------- +// NPCs can override this to tweak with how costly particular movements are +//----------------------------------------------------------------------------- +bool CAI_BaseNPC::MovementCost( int moveType, const Vector &vecStart, const Vector &vecEnd, float *pCost ) +{ + // We have nothing to say on the matter, but derived classes might + return false; +} + +bool CAI_BaseNPC::OverrideMoveFacing( const AILocalMoveGoal_t &move, float flInterval ) +{ + return false; +} + +bool CAI_BaseNPC::OverrideMove( float flInterval ) +{ + return false; +} + + +//========================================================= +// VecToYaw - turns a directional vector into a yaw value +// that points down that vector. +//========================================================= +float CAI_BaseNPC::VecToYaw( const Vector &vecDir ) +{ + if (vecDir.x == 0 && vecDir.y == 0 && vecDir.z == 0) + return GetLocalAngles().y; + + return UTIL_VecToYaw( vecDir ); +} + +//----------------------------------------------------------------------------- +// Inherited from IAI_MotorMovementServices +//----------------------------------------------------------------------------- +float CAI_BaseNPC::CalcYawSpeed( void ) +{ + // Negative values are invalud + return -1.0f; +} + +bool CAI_BaseNPC::OnCalcBaseMove( AILocalMoveGoal_t *pMoveGoal, + float distClear, + AIMoveResult_t *pResult ) +{ + if ( pMoveGoal->directTrace.pObstruction ) + { + CBasePropDoor *pPropDoor = dynamic_cast( pMoveGoal->directTrace.pObstruction ); + if ( pPropDoor && OnUpcomingPropDoor( pMoveGoal, pPropDoor, distClear, pResult ) ) + { + return true; + } + } + + return false; +} + + +bool CAI_BaseNPC::OnObstructionPreSteer( AILocalMoveGoal_t *pMoveGoal, + float distClear, + AIMoveResult_t *pResult ) +{ + if ( pMoveGoal->directTrace.pObstruction ) + { + CBaseDoor *pDoor = dynamic_cast( pMoveGoal->directTrace.pObstruction ); + if ( pDoor && OnObstructingDoor( pMoveGoal, pDoor, distClear, pResult ) ) + { + return true; + } + } + + return false; +} + + +bool CAI_BaseNPC::OnObstructingDoor( AILocalMoveGoal_t *pMoveGoal, + CBaseDoor *pDoor, + float distClear, + AIMoveResult_t *pResult ) +{ + if ( pMoveGoal->maxDist < distClear ) + return false; + + // By default, NPCs don't know how to open doors + if ( pDoor->m_toggle_state == TS_AT_BOTTOM || pDoor->m_toggle_state == TS_GOING_DOWN ) + { + if ( distClear < 0.1 ) + { + *pResult = AIMR_BLOCKED_ENTITY; + } + else + { + pMoveGoal->maxDist = distClear; + *pResult = AIMR_OK; + } + + return true; + } + + return false; +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : pMoveGoal - +// pDoor - +// distClear - +// default - +// spawn - +// oldorg - +// pfPosition - +// neworg - +// Output : Returns true if movement is solved, false otherwise. +//----------------------------------------------------------------------------- + +bool CAI_BaseNPC::OnUpcomingPropDoor( AILocalMoveGoal_t *pMoveGoal, + CBasePropDoor *pDoor, + float distClear, + AIMoveResult_t *pResult ) +{ + if ( (pMoveGoal->flags & AILMG_TARGET_IS_GOAL) && pMoveGoal->maxDist < distClear ) + return false; + + if ( pMoveGoal->maxDist + GetHullWidth() * .25 < distClear ) + return false; + + if (pDoor == m_hOpeningDoor) + { + if ( pDoor->IsNPCOpening( this ) ) + { + // We're in the process of opening the door, don't be blocked by it. + pMoveGoal->maxDist = distClear; + *pResult = AIMR_OK; + return true; + } + m_hOpeningDoor = NULL; + } + + if ((CapabilitiesGet() & bits_CAP_DOORS_GROUP) && !pDoor->IsDoorLocked() && (pDoor->IsDoorClosed() || pDoor->IsDoorClosing())) + { + AI_Waypoint_t *pOpenDoorRoute = NULL; + + opendata_t opendata; + pDoor->GetNPCOpenData(this, opendata); + + // dvs: FIXME: local route might not be sufficient + pOpenDoorRoute = GetPathfinder()->BuildLocalRoute( + GetLocalOrigin(), + opendata.vecStandPos, + NULL, + bits_WP_TO_DOOR | bits_WP_DONT_SIMPLIFY, + NO_NODE, + bits_BUILD_GROUND | bits_BUILD_IGNORE_NPCS, + 0.0); + + if ( pOpenDoorRoute ) + { + if ( AIIsDebuggingDoors(this) ) + { + NDebugOverlay::Cross3D(opendata.vecStandPos + Vector(0,0,1), 32, 255, 255, 255, false, 1.0 ); + Msg( "Opening door!\n" ); + } + + // Attach the door to the waypoint so we open it when we get there. + // dvs: FIXME: this is kind of bullshit, I need to find the exact waypoint to open the door + // should I just walk the path until I find it? + pOpenDoorRoute->m_hData = pDoor; + + GetNavigator()->GetPath()->PrependWaypoints( pOpenDoorRoute ); + + m_hOpeningDoor = pDoor; + pMoveGoal->maxDist = distClear; + *pResult = AIMR_CHANGE_TYPE; + + return true; + } + else + AIDoorDebugMsg( this, "Failed create door route!\n" ); + } + + return false; +} + + +//----------------------------------------------------------------------------- +// Purpose: Called by the navigator to initiate the opening of a prop_door +// that is in our way. +//----------------------------------------------------------------------------- +void CAI_BaseNPC::OpenPropDoorBegin( CBasePropDoor *pDoor ) +{ + // dvs: not quite working, disabled for now. + //opendata_t opendata; + //pDoor->GetNPCOpenData(this, opendata); + // + //if (HaveSequenceForActivity(opendata.eActivity)) + //{ + // SetIdealActivity(opendata.eActivity); + //} + //else + { + // We don't have an appropriate sequence, just open the door magically. + OpenPropDoorNow( pDoor ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Called when we are trying to open a prop_door and it's time to start +// the door moving. This is called either in response to an anim event +// or as a fallback when we don't have an appropriate open activity. +//----------------------------------------------------------------------------- +void CAI_BaseNPC::OpenPropDoorNow( CBasePropDoor *pDoor ) +{ + // Start the door moving. + pDoor->NPCOpenDoor(this); + + // Wait for the door to finish opening before trying to move through the doorway. + m_flMoveWaitFinished = gpGlobals->curtime + pDoor->GetOpenInterval(); +} + + +//----------------------------------------------------------------------------- +// Purpose: Called when the door we were trying to open becomes fully open. +// Input : pDoor - +//----------------------------------------------------------------------------- +void CAI_BaseNPC::OnDoorFullyOpen(CBasePropDoor *pDoor) +{ + // We're done with the door. + m_hOpeningDoor = NULL; +} + + +//----------------------------------------------------------------------------- +// Purpose: Called when the door we were trying to open becomes blocked before opening. +// Input : pDoor - +//----------------------------------------------------------------------------- +void CAI_BaseNPC::OnDoorBlocked(CBasePropDoor *pDoor) +{ + // dvs: FIXME: do something so that we don't loop forever trying to open this door + // not clearing out the door handle will cause the NPC to invalidate the connection + // We're done with the door. + //m_hOpeningDoor = NULL; +} + + +//----------------------------------------------------------------------------- +// Purpose: Template NPCs are marked as templates by the level designer. They +// do not spawn, but their keyvalues are saved for use by a template +// spawner. +//----------------------------------------------------------------------------- +bool CAI_BaseNPC::IsTemplate( void ) +{ + return HasSpawnFlags( SF_NPC_TEMPLATE ); +} + + + +//----------------------------------------------------------------------------- +// +// Movement code for walking + flying +// +//----------------------------------------------------------------------------- +int CAI_BaseNPC::FlyMove( const Vector& pfPosition, unsigned int mask ) +{ + Vector oldorg, neworg; + trace_t trace; + + // try the move + VectorCopy( GetAbsOrigin(), oldorg ); + VectorAdd( oldorg, pfPosition, neworg ); + UTIL_TraceEntity( this, oldorg, neworg, mask, &trace ); + if (trace.fraction == 1) + { + if ( (GetFlags() & FL_SWIM) && enginetrace->GetPointContents(trace.endpos) == CONTENTS_EMPTY ) + return false; // swim monster left water + + SetAbsOrigin( trace.endpos ); + PhysicsTouchTriggers(); + return true; + } + + return false; +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : ent - +// Dir - Normalized direction vector for movement. +// dist - Distance along 'Dir' to move. +// iMode - +// Output : Returns nonzero on success, zero on failure. +//----------------------------------------------------------------------------- +int CAI_BaseNPC::WalkMove( const Vector& vecPosition, unsigned int mask ) +{ + if ( GetFlags() & (FL_FLY | FL_SWIM) ) + { + return FlyMove( vecPosition, mask ); + } + + if ( (GetFlags() & FL_ONGROUND) == 0 ) + { + return 0; + } + + trace_t trace; + Vector oldorg, neworg, end; + Vector move( vecPosition[0], vecPosition[1], 0.0f ); + VectorCopy( GetAbsOrigin(), oldorg ); + VectorAdd( oldorg, move, neworg ); + + // push down from a step height above the wished position + float flStepSize = sv_stepsize.GetFloat(); + neworg[2] += flStepSize; + VectorCopy(neworg, end); + end[2] -= flStepSize*2; + + UTIL_TraceEntity( this, neworg, end, mask, &trace ); + if ( trace.allsolid ) + return false; + + if (trace.startsolid) + { + neworg[2] -= flStepSize; + UTIL_TraceEntity( this, neworg, end, mask, &trace ); + if ( trace.allsolid || trace.startsolid ) + return false; + } + + if (trace.fraction == 1) + { + // if monster had the ground pulled out, go ahead and fall + if ( GetFlags() & FL_PARTIALGROUND ) + { + SetAbsOrigin( oldorg + move ); + PhysicsTouchTriggers(); + SetGroundEntity( NULL ); + return true; + } + + return false; // walked off an edge + } + + // check point traces down for dangling corners + SetAbsOrigin( trace.endpos ); + + if (UTIL_CheckBottom( this, NULL, flStepSize ) == 0) + { + if ( GetFlags() & FL_PARTIALGROUND ) + { + // entity had floor mostly pulled out from underneath it + // and is trying to correct + PhysicsTouchTriggers(); + return true; + } + + // Reset to original position + SetAbsOrigin( oldorg ); + return false; + } + + if ( GetFlags() & FL_PARTIALGROUND ) + { + // Con_Printf ("back on ground\n"); + RemoveFlag( FL_PARTIALGROUND ); + } + + // the move is ok + SetGroundEntity( trace.m_pEnt ); + PhysicsTouchTriggers(); + return true; +} + +//----------------------------------------------------------------------------- + +static void AIMsgGuts( CAI_BaseNPC *pAI, unsigned flags, const char *pszMsg ) +{ + int len = strlen( pszMsg ); + const char *pszFmt2 = NULL; + + if ( len && pszMsg[len-1] == '\n' ) + { + (const_cast(pszMsg))[len-1] = 0; + pszFmt2 = "%s (%s: %d/%s) [%d]\n"; + } + else + pszFmt2 = "%s (%s: %d/%s) [%d]"; + + DevMsg( pszFmt2, + pszMsg, + pAI->GetClassname(), + pAI->entindex(), + ( pAI->GetEntityName() == NULL_STRING ) ? "" : STRING(pAI->GetEntityName()), + gpGlobals->tickcount ); +} + +void DevMsg( CAI_BaseNPC *pAI, unsigned flags, const char *pszFormat, ... ) +{ + if ( (flags & AIMF_IGNORE_SELECTED) || (pAI->m_debugOverlays & OVERLAY_NPC_SELECTED_BIT) ) + { + AIMsgGuts( pAI, flags, CFmtStr( &pszFormat ) ); + } +} + +//----------------------------------------------------------------------------- + +void DevMsg( CAI_BaseNPC *pAI, const char *pszFormat, ... ) +{ + if ( (pAI->m_debugOverlays & OVERLAY_NPC_SELECTED_BIT) ) + { + AIMsgGuts( pAI, 0, CFmtStr( &pszFormat ) ); + } +} + +//----------------------------------------------------------------------------- + +bool CAI_BaseNPC::IsPlayerAlly( CBasePlayer *pPlayer ) +{ + if ( pPlayer == NULL ) + { + // in multiplayer mode we need a valid pPlayer + // or override this virtual function + if ( !AI_IsSinglePlayer() ) + return false; + + // NULL means single player mode + pPlayer = UTIL_GetLocalPlayer(); + } + + return ( !pPlayer || IRelationType( pPlayer ) == D_LI ); +} + +//----------------------------------------------------------------------------- + +void CAI_BaseNPC::SetCommandGoal( const Vector &vecGoal ) +{ + m_vecCommandGoal = vecGoal; + m_CommandMoveMonitor.ClearMark(); +} + +//----------------------------------------------------------------------------- + +void CAI_BaseNPC::ClearCommandGoal() +{ + m_vecCommandGoal = vec3_invalid; + m_CommandMoveMonitor.ClearMark(); +} + +//----------------------------------------------------------------------------- + +bool CAI_BaseNPC::IsInPlayerSquad() const +{ + return ( m_pSquad && MAKE_STRING(m_pSquad->GetName()) == GetPlayerSquadName() && !CAI_Squad::IsSilentMember(this) ); +} + + +//----------------------------------------------------------------------------- + +bool CAI_BaseNPC::CanBeUsedAsAFriend( void ) +{ + if ( IsCurSchedule(SCHED_FORCED_GO) || IsCurSchedule(SCHED_FORCED_GO_RUN) ) + return false; + return true; +} + +//----------------------------------------------------------------------------- + +Vector CAI_BaseNPC::GetSmoothedVelocity( void ) +{ + if( GetNavType() == NAV_GROUND || GetNavType() == NAV_FLY ) + { + return m_pMotor->GetCurVel(); + } + + return BaseClass::GetSmoothedVelocity(); +} + + +//----------------------------------------------------------------------------- + +bool CAI_BaseNPC::IsCoverPosition( const Vector &vecThreat, const Vector &vecPosition ) +{ + trace_t tr; + + // By default, we ignore the viewer (me) when determining cover positions + CTraceFilterLOS filter( NULL, COLLISION_GROUP_NONE, this ); + + // If I'm trying to find cover from the player, and the player is in a vehicle, + // ignore the vehicle for the purpose of determining line of sight. + CBaseEntity *pEnemy = GetEnemy(); + if ( pEnemy ) + { + // Hack to see if our threat position is our enemy + bool bThreatPosIsEnemy = ( (vecThreat - GetEnemy()->EyePosition()).LengthSqr() < 0.1f ); + if ( bThreatPosIsEnemy ) + { + CBaseCombatCharacter *pCCEnemy = GetEnemy()->MyCombatCharacterPointer(); + if ( pCCEnemy != NULL && pCCEnemy->IsInAVehicle() ) + { + // Ignore the vehicle + filter.SetPassEntity( pCCEnemy->GetVehicleEntity() ); + } + + if ( !filter.GetPassEntity() ) + { + filter.SetPassEntity( pEnemy ); + } + } + } + + AI_TraceLOS( vecThreat, vecPosition, this, &tr, &filter ); + + if( tr.fraction != 1.0 && hl2_episodic.GetBool() ) + { + if( tr.m_pEnt->m_iClassname == m_iClassname ) + { + // Don't hide behind buddies! + return false; + } + } + + return (tr.fraction != 1.0); +} + +//----------------------------------------------------------------------------- + +float CAI_BaseNPC::SetWait( float minWait, float maxWait ) +{ + int minThinks = Ceil2Int( minWait * 10 ); + + if ( maxWait == 0.0 ) + { + m_flWaitFinished = gpGlobals->curtime + ( 0.1 * minThinks ); + } + else + { + if ( minThinks == 0 ) // random 0..n is almost certain to not return 0 + minThinks = 1; + int maxThinks = Ceil2Int( maxWait * 10 ); + + m_flWaitFinished = gpGlobals->curtime + ( 0.1 * random->RandomInt( minThinks, maxThinks ) ); + } + return m_flWaitFinished; +} + +//----------------------------------------------------------------------------- + +void CAI_BaseNPC::ClearWait() +{ + m_flWaitFinished = FLT_MAX; +} + +//----------------------------------------------------------------------------- + +bool CAI_BaseNPC::IsWaitFinished() +{ + return ( gpGlobals->curtime >= m_flWaitFinished ); +} + +//----------------------------------------------------------------------------- + +bool CAI_BaseNPC::IsWaitSet() +{ + return ( m_flWaitFinished != FLT_MAX ); +} + +void CAI_BaseNPC::TestPlayerPushing( CBaseEntity *pEntity ) +{ + if ( HasSpawnFlags( SF_NPC_NO_PLAYER_PUSHAWAY ) ) + return; + + // Heuristic for determining if the player is pushing me away + CBasePlayer *pPlayer = ToBasePlayer( pEntity ); + if ( pPlayer && !( pPlayer->GetFlags() & FL_NOTARGET ) ) + { + if ( (pPlayer->m_nButtons & (IN_FORWARD|IN_BACK|IN_MOVELEFT|IN_MOVERIGHT)) || + pPlayer->GetAbsVelocity().AsVector2D().LengthSqr() > 50*50 ) + { + SetCondition( COND_PLAYER_PUSHING ); + Vector vecPush = GetAbsOrigin() - pPlayer->GetAbsOrigin(); + VectorNormalize( vecPush ); + CascadePlayerPush( vecPush, pPlayer->WorldSpaceCenter() ); + } + } +} + +void CAI_BaseNPC::CascadePlayerPush( const Vector &push, const Vector &pushOrigin ) +{ + // + // Try to push any friends that are in the way. + // + float hullWidth = GetHullWidth(); + const Vector & origin = GetAbsOrigin(); + const Vector2D &origin2D = origin.AsVector2D(); + + const float MIN_Z_TO_TRANSMIT = GetHullHeight() * 0.5 + 0.1; + const float DIST_REQD_TO_TRANSMIT_PUSH_SQ = Square( hullWidth * 5 + 0.1 ); + const float DIST_FROM_PUSH_VECTOR_REQD_SQ = Square( hullWidth + 0.1 ); + + Vector2D pushTestPoint = vec2_invalid; + + for ( int i = 0; i < g_AI_Manager.NumAIs(); i++ ) + { + CAI_BaseNPC *pOther = g_AI_Manager.AccessAIs()[i]; + if ( pOther != this && pOther->IRelationType(this) == D_LI && !pOther->HasCondition( COND_PLAYER_PUSHING ) ) + { + const Vector &friendOrigin = pOther->GetAbsOrigin(); + if ( fabsf( friendOrigin.z - origin.z ) < MIN_Z_TO_TRANSMIT && + ( friendOrigin.AsVector2D() - origin.AsVector2D() ).LengthSqr() < DIST_REQD_TO_TRANSMIT_PUSH_SQ ) + { + if ( pushTestPoint == vec2_invalid ) + { + pushTestPoint = origin2D - pushOrigin.AsVector2D(); + // No normalize, since it wants to just be a big number and we can't be less that a hull away + pushTestPoint *= 2000; + pushTestPoint += origin2D; + + } + float t; + float distSq = CalcDistanceSqrToLine2D( friendOrigin.AsVector2D(), origin2D, pushTestPoint, &t ); + if ( t > 0 && distSq < DIST_FROM_PUSH_VECTOR_REQD_SQ ) + { + pOther->SetCondition( COND_PLAYER_PUSHING ); + } + } + } + } +} + + +//----------------------------------------------------------------------------- +// Break into pieces! +//----------------------------------------------------------------------------- +void CAI_BaseNPC::Break( CBaseEntity *pBreaker ) +{ + m_takedamage = DAMAGE_NO; + + Vector velocity; + AngularImpulse angVelocity; + IPhysicsObject *pPhysics = VPhysicsGetObject(); + Vector origin; + QAngle angles; + AddSolidFlags( FSOLID_NOT_SOLID ); + if ( pPhysics ) + { + pPhysics->GetVelocity( &velocity, &angVelocity ); + pPhysics->GetPosition( &origin, &angles ); + pPhysics->RecheckCollisionFilter(); + } + else + { + velocity = GetAbsVelocity(); + QAngleToAngularImpulse( GetLocalAngularVelocity(), angVelocity ); + origin = GetAbsOrigin(); + angles = GetAbsAngles(); + } + + breakablepropparams_t params( GetAbsOrigin(), GetAbsAngles(), velocity, angVelocity ); + params.impactEnergyScale = m_impactEnergyScale; + params.defCollisionGroup = GetCollisionGroup(); + if ( params.defCollisionGroup == COLLISION_GROUP_NONE ) + { + // don't automatically make anything COLLISION_GROUP_NONE or it will + // collide with debris being ejected by breaking + params.defCollisionGroup = COLLISION_GROUP_INTERACTIVE; + } + + // no damage/damage force? set a burst of 100 for some movement + params.defBurstScale = 100;//pDamageInfo ? 0 : 100; + PropBreakableCreateAll( GetModelIndex(), pPhysics, params, this, -1, false ); + + UTIL_Remove(this); +} + + +//----------------------------------------------------------------------------- +// Purpose: Input handler for breaking the breakable immediately. +//----------------------------------------------------------------------------- +void CAI_BaseNPC::InputBreak( inputdata_t &inputdata ) +{ + Break( inputdata.pActivator ); +} + + +//----------------------------------------------------------------------------- + +bool CAI_BaseNPC::FindNearestValidGoalPos( const Vector &vTestPoint, Vector *pResult ) +{ + AIMoveTrace_t moveTrace; + Vector vCandidate = vec3_invalid; + if ( GetNavigator()->CanFitAtPosition( vTestPoint, MASK_SOLID_BRUSHONLY ) ) + { + if ( GetMoveProbe()->CheckStandPosition( vTestPoint, MASK_SOLID_BRUSHONLY ) ) + { + vCandidate = vTestPoint; + } + } + + if ( vCandidate == vec3_invalid ) + { + int iNearestNode = GetPathfinder()->NearestNodeToPoint( vTestPoint ); + if ( iNearestNode != NO_NODE ) + { + GetMoveProbe()->MoveLimit( NAV_GROUND, + g_pBigAINet->GetNodePosition(GetHullType(), iNearestNode ), + vTestPoint, + MASK_SOLID_BRUSHONLY, + NULL, + 0, + &moveTrace ); + if ( ( moveTrace.vEndPosition - vTestPoint ).Length2DSqr() < Square( GetHullWidth() * 3.0 ) && + GetMoveProbe()->CheckStandPosition( moveTrace.vEndPosition, MASK_SOLID_BRUSHONLY ) ) + { + vCandidate = moveTrace.vEndPosition; + } + } + } + + if ( vCandidate != vec3_invalid ) + { + AI_Waypoint_t *pPathToPoint = GetPathfinder()->BuildRoute( GetAbsOrigin(), vCandidate, AI_GetSinglePlayer(), 5*12, NAV_NONE, true ); + if ( pPathToPoint ) + { + GetPathfinder()->UnlockRouteNodes( pPathToPoint ); + CAI_Path tempPath; + tempPath.SetWaypoints( pPathToPoint ); // path object will delete waypoints + } + else + vCandidate = vec3_invalid; + } + + if ( vCandidate == vec3_invalid ) + { + GetMoveProbe()->MoveLimit( NAV_GROUND, + GetAbsOrigin(), + vTestPoint, + MASK_SOLID_BRUSHONLY, + NULL, + 0, + &moveTrace ); + vCandidate = moveTrace.vEndPosition; + } + + if ( vCandidate == vec3_invalid ) + return false; + + *pResult = vCandidate; + return true; +} + +//--------------------------------------------------------- +// Pass a direction to get how far an NPC would see if facing +// that direction. Pass nothing to get the length of the NPC's +// current line of sight. +//--------------------------------------------------------- +float CAI_BaseNPC::LineOfSightDist( const Vector &vecDir, float zEye ) +{ + Vector testDir; + if( vecDir == vec3_invalid ) + { + testDir = EyeDirection3D(); + } + else + { + testDir = vecDir; + } + + if ( zEye == FLT_MAX ) + zEye = EyePosition().z; + + trace_t tr; + // Need to center trace so don't get erratic results based on orientation + Vector testPos( GetAbsOrigin().x, GetAbsOrigin().y, zEye ); + AI_TraceLOS( testPos, testPos + testDir * MAX_COORD_RANGE, this, &tr ); + return (tr.startpos - tr.endpos ).Length(); +} + +ConVar ai_LOS_mode( "ai_LOS_mode", "0", FCVAR_REPLICATED ); + +//----------------------------------------------------------------------------- +// Purpose: Use this to perform AI tracelines that are trying to determine LOS between points. +// LOS checks between entities should use FVisible. +//----------------------------------------------------------------------------- +void AI_TraceLOS( const Vector& vecAbsStart, const Vector& vecAbsEnd, CBaseEntity *pLooker, trace_t *ptr, ITraceFilter *pFilter ) +{ + AI_PROFILE_SCOPE( AI_TraceLOS ); + + if ( ai_LOS_mode.GetBool() ) + { + // Don't use LOS tracefilter + UTIL_TraceLine( vecAbsStart, vecAbsEnd, MASK_OPAQUE, pLooker, COLLISION_GROUP_NONE, ptr ); + return; + } + + // Use the custom LOS trace filter + CTraceFilterLOS traceFilter( pLooker, COLLISION_GROUP_NONE ); + if ( !pFilter ) + pFilter = &traceFilter; + AI_TraceLine( vecAbsStart, vecAbsEnd, MASK_OPAQUE_AND_NPCS, pFilter, ptr ); +} + +void CAI_BaseNPC::InputSetSpeedModifierRadius( inputdata_t &inputdata ) +{ + m_iSpeedModRadius = inputdata.value.Int(); + m_iSpeedModRadius *= m_iSpeedModRadius; +} +void CAI_BaseNPC::InputSetSpeedModifierSpeed( inputdata_t &inputdata ) +{ + m_iSpeedModSpeed = inputdata.value.Int(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CAI_BaseNPC::IsAllowedToDodge( void ) +{ + // Can't do it if I'm not available + if ( m_NPCState != NPC_STATE_IDLE && m_NPCState != NPC_STATE_ALERT && m_NPCState != NPC_STATE_COMBAT ) + return false; + + return ( m_flNextDodgeTime <= gpGlobals->curtime ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CAI_BaseNPC::ParseScriptedNPCInteractions( void ) +{ + // Already parsed them? + if ( m_ScriptedInteractions.Count() ) + return; + + // Parse the model's key values and find any dynamic interactions + KeyValues *modelKeyValues = new KeyValues(""); + if ( modelKeyValues->LoadFromBuffer( modelinfo->GetModelName( GetModel() ), modelinfo->GetModelKeyValueText( GetModel() ) ) ) + { + // Do we have a dynamic interactions section? + KeyValues *pkvInteractions = modelKeyValues->FindKey("dynamic_interactions"); + if ( pkvInteractions ) + { + KeyValues *pkvNode = pkvInteractions->GetFirstSubKey(); + while ( pkvNode ) + { + ScriptedNPCInteraction_t sInteraction; + sInteraction.iszInteractionName = AllocPooledString( pkvNode->GetName() ); + + // Trigger method + const char *pszTrigger = pkvNode->GetString( "trigger", NULL ); + if ( pszTrigger ) + { + if ( !Q_strncmp( pszTrigger, "auto_in_combat", 14) ) + { + sInteraction.iTriggerMethod = SNPCINT_AUTOMATIC_IN_COMBAT; + } + } + + // Loop Break trigger method + pszTrigger = pkvNode->GetString( "loop_break_trigger", NULL ); + if ( pszTrigger ) + { + char szTrigger[256]; + Q_strncpy( szTrigger, pszTrigger, sizeof(szTrigger) ); + char *pszParam = strtok( szTrigger, " " ); + while (pszParam) + { + if ( !Q_strncmp( pszParam, "on_damage", 9) ) + { + sInteraction.iLoopBreakTriggerMethod |= SNPCINT_LOOPBREAK_ON_DAMAGE; + } + if ( !Q_strncmp( pszParam, "on_flashlight_illum", 19) ) + { + sInteraction.iLoopBreakTriggerMethod |= SNPCINT_LOOPBREAK_ON_FLASHLIGHT_ILLUM; + } + + pszParam = strtok(NULL," "); + } + } + + // Origin + const char *pszOrigin = pkvNode->GetString( "origin_relative", "0 0 0" ); + UTIL_StringToVector( sInteraction.vecRelativeOrigin.Base(), pszOrigin ); + + // Angles + const char *pszAngles = pkvNode->GetString( "angles_relative", NULL ); + if ( pszAngles ) + { + sInteraction.iFlags |= SCNPC_FLAG_TEST_OTHER_ANGLES; + UTIL_StringToVector( sInteraction.angRelativeAngles.Base(), pszAngles ); + } + + // Velocity + const char *pszVelocity = pkvNode->GetString( "velocity_relative", NULL ); + if ( pszVelocity ) + { + sInteraction.iFlags |= SCNPC_FLAG_TEST_OTHER_VELOCITY; + UTIL_StringToVector( sInteraction.vecRelativeVelocity.Base(), pszVelocity ); + } + + // Entry Sequence + const char *pszSequence = pkvNode->GetString( "entry_sequence", NULL ); + if ( pszSequence ) + { + sInteraction.sPhases[SNPCINT_ENTRY].iszSequence = AllocPooledString( pszSequence ); + } + // Entry Activity + const char *pszActivity = pkvNode->GetString( "entry_activity", NULL ); + if ( pszActivity ) + { + sInteraction.sPhases[SNPCINT_ENTRY].iActivity = GetActivityID( pszActivity ); + } + + // Sequence + pszSequence = pkvNode->GetString( "sequence", NULL ); + if ( pszSequence ) + { + sInteraction.sPhases[SNPCINT_SEQUENCE].iszSequence = AllocPooledString( pszSequence ); + } + // Activity + pszActivity = pkvNode->GetString( "activity", NULL ); + if ( pszActivity ) + { + sInteraction.sPhases[SNPCINT_SEQUENCE].iActivity = GetActivityID( pszActivity ); + } + + // Exit Sequence + pszSequence = pkvNode->GetString( "exit_sequence", NULL ); + if ( pszSequence ) + { + sInteraction.sPhases[SNPCINT_EXIT].iszSequence = AllocPooledString( pszSequence ); + } + // Exit Activity + pszActivity = pkvNode->GetString( "exit_activity", NULL ); + if ( pszActivity ) + { + sInteraction.sPhases[SNPCINT_EXIT].iActivity = GetActivityID( pszActivity ); + } + + // Delay + sInteraction.flDelay = pkvNode->GetFloat( "delay", 10.0 ); + + // Delta + sInteraction.flDistSqr = pkvNode->GetFloat( "origin_max_delta", (DSS_MAX_DIST * DSS_MAX_DIST) ); + + // Loop? + if ( pkvNode->GetFloat( "loop_in_action", 0 ) ) + { + sInteraction.iFlags |= SCNPC_FLAG_LOOP_IN_ACTION; + } + + // Needs a weapon? + const char *pszNeedsWeapon = pkvNode->GetString( "needs_weapon", NULL ); + if ( pszNeedsWeapon ) + { + if ( !Q_strncmp( pszNeedsWeapon, "ME", 2 ) ) + { + sInteraction.iFlags |= SCNPC_FLAG_NEEDS_WEAPON_ME; + } + else if ( !Q_strncmp( pszNeedsWeapon, "THEM", 4 ) ) + { + sInteraction.iFlags |= SCNPC_FLAG_NEEDS_WEAPON_THEM; + } + else if ( !Q_strncmp( pszNeedsWeapon, "BOTH", 4 ) ) + { + sInteraction.iFlags |= SCNPC_FLAG_NEEDS_WEAPON_ME; + sInteraction.iFlags |= SCNPC_FLAG_NEEDS_WEAPON_THEM; + } + } + + // Specific weapon types + const char *pszWeaponName = pkvNode->GetString( "weapon_mine", NULL ); + if ( pszWeaponName ) + { + sInteraction.iFlags |= SCNPC_FLAG_NEEDS_WEAPON_ME; + sInteraction.iszMyWeapon = AllocPooledString( pszWeaponName ); + } + pszWeaponName = pkvNode->GetString( "weapon_theirs", NULL ); + if ( pszWeaponName ) + { + sInteraction.iFlags |= SCNPC_FLAG_NEEDS_WEAPON_THEM; + sInteraction.iszTheirWeapon = AllocPooledString( pszWeaponName ); + } + + // Add it to the list + AddScriptedNPCInteraction( &sInteraction ); + + // Move to next interaction + pkvNode = pkvNode->GetNextKey(); + } + } + } + + modelKeyValues->deleteThis(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CAI_BaseNPC::AddScriptedNPCInteraction( ScriptedNPCInteraction_t *pInteraction ) +{ + int nNewIndex = m_ScriptedInteractions.AddToTail(); + + if ( ai_debug_dyninteractions.GetBool() ) + { + Msg("%s(%s): Added dynamic interaction: %s\n", GetClassName(), GetDebugName(), STRING(pInteraction->iszInteractionName) ); + } + + // Copy the interaction over + ScriptedNPCInteraction_t *pNewInt = &(m_ScriptedInteractions[nNewIndex]); + memcpy( pNewInt, pInteraction, sizeof(ScriptedNPCInteraction_t) ); + + // Calculate the local to world matrix + m_ScriptedInteractions[nNewIndex].matDesiredLocalToWorld.SetupMatrixOrgAngles( pInteraction->vecRelativeOrigin, pInteraction->angRelativeAngles ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +const char *CAI_BaseNPC::GetScriptedNPCInteractionSequence( ScriptedNPCInteraction_t *pInteraction, int iPhase ) +{ + if ( pInteraction->sPhases[iPhase].iActivity != ACT_INVALID ) + { + int iSequence = SelectWeightedSequence( (Activity)pInteraction->sPhases[iPhase].iActivity ); + return GetSequenceName( iSequence ); + } + + if ( pInteraction->sPhases[iPhase].iszSequence != NULL_STRING ) + return STRING(pInteraction->sPhases[iPhase].iszSequence); + + return NULL; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CAI_BaseNPC::StartRunningInteraction( CAI_BaseNPC *pOtherNPC, bool bActive ) +{ + m_hInteractionPartner = pOtherNPC; + if ( bActive ) + { + m_iInteractionState = NPCINT_RUNNING_ACTIVE; + } + else + { + m_iInteractionState = NPCINT_RUNNING_PARTNER; + } + m_bCannotDieDuringInteraction = true; + + // Force the NPC into an idle schedule so they don't move. + // NOTE: We must set SCHED_IDLE_STAND directly, to prevent derived NPC + // classes from translating the idle stand schedule away to do something bad. + SetSchedule( GetSchedule(SCHED_IDLE_STAND) ); + + // Prepare the NPC for the script. Setting this allows the scripted sequences + // that we're about to create to immediately grab & use this NPC right away. + // This prevents the NPC from being able to make any schedule decisions + // before the DSS gets underway. + m_scriptState = SCRIPT_PLAYING; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CAI_BaseNPC::StartScriptedNPCInteraction( CAI_BaseNPC *pOtherNPC, ScriptedNPCInteraction_t *pInteraction, Vector vecOtherOrigin, QAngle angOtherAngles ) +{ + variant_t emptyVariant; + + StartRunningInteraction( pOtherNPC, true ); + if ( pOtherNPC ) + { + pOtherNPC->StartRunningInteraction( this, false ); + + //Msg("%s(%s) disabled collisions with %s(%s) at %0.2f\n", GetClassName(), GetDebugName(), pOtherNPC->GetClassName(), pOtherNPC->GetDebugName(), gpGlobals->curtime ); + PhysDisableEntityCollisions( this, pOtherNPC ); + } + + // Determine which sequences we're going to use + const char *pszEntrySequence = GetScriptedNPCInteractionSequence( pInteraction, SNPCINT_ENTRY ); + const char *pszSequence = GetScriptedNPCInteractionSequence( pInteraction, SNPCINT_SEQUENCE ); + const char *pszExitSequence = GetScriptedNPCInteractionSequence( pInteraction, SNPCINT_EXIT ); + + // Debug + if ( ai_debug_dyninteractions.GetBool() ) + { + if ( pOtherNPC ) + { + Msg("%s(%s) starting dynamic interaction \"%s\" with %s(%s).\n", GetClassName(), GetDebugName(), STRING(pInteraction->iszInteractionName), pOtherNPC->GetClassName(), pOtherNPC->GetDebugName() ); + if ( pszEntrySequence ) + { + Msg( " - Entry sequence: %s\n", pszEntrySequence ); + } + Msg( " - Core sequence: %s\n", pszSequence ); + if ( pszExitSequence ) + { + Msg( " - Exit sequence: %s\n", pszExitSequence ); + } + } + } + + // Create a scripted sequence name that's guaranteed to be unique + char szSSName[256]; + if ( pOtherNPC ) + { + Q_snprintf( szSSName, sizeof(szSSName), "dss_%s%d%s%d", GetDebugName(), entindex(), pOtherNPC->GetDebugName(), pOtherNPC->entindex() ); + } + else + { + Q_snprintf( szSSName, sizeof(szSSName), "dss_%s%d", GetDebugName(), entindex() ); + } + string_t iszSSName = AllocPooledString(szSSName); + + // Setup next attempt + pInteraction->flNextAttemptTime = gpGlobals->curtime + pInteraction->flDelay + RandomFloat(-2,2); + + // Spawn a scripted sequence for this NPC to play the interaction anim + CAI_ScriptedSequence *pMySequence = (CAI_ScriptedSequence*)CreateEntityByName( "scripted_sequence" ); + pMySequence->KeyValue( "m_iszEntry", pszEntrySequence ); + pMySequence->KeyValue( "m_iszPlay", pszSequence ); + pMySequence->KeyValue( "m_iszPostIdle", pszExitSequence ); + pMySequence->KeyValue( "m_fMoveTo", "5" ); + pMySequence->SetAbsOrigin( GetAbsOrigin() ); + + QAngle angDesired = GetAbsAngles(); + angDesired[YAW] = m_flInteractionYaw; + + pMySequence->SetAbsAngles( angDesired ); + pMySequence->ForceSetTargetEntity( this, true ); + pMySequence->SetName( iszSSName ); + pMySequence->AddSpawnFlags( SF_SCRIPT_NOINTERRUPT | SF_SCRIPT_HIGH_PRIORITY | SF_SCRIPT_OVERRIDESTATE ); + pMySequence->SetLoopActionSequence( (pInteraction->iFlags & SCNPC_FLAG_LOOP_IN_ACTION) != 0 ); + pMySequence->SetSynchPostIdles( true ); + if ( ai_debug_dyninteractions.GetBool() ) + { + pMySequence->m_debugOverlays |= OVERLAY_TEXT_BIT | OVERLAY_PIVOT_BIT; + } + + // Spawn the matching scripted sequence for the other NPC + CAI_ScriptedSequence *pTheirSequence = NULL; + if ( pOtherNPC ) + { + pTheirSequence = (CAI_ScriptedSequence*)CreateEntityByName( "scripted_sequence" ); + pTheirSequence->KeyValue( "m_iszEntry", pszEntrySequence ); + pTheirSequence->KeyValue( "m_iszPlay", pszSequence ); + pTheirSequence->KeyValue( "m_iszPostIdle", pszExitSequence ); + pTheirSequence->KeyValue( "m_fMoveTo", "5" ); + pTheirSequence->SetAbsOrigin( vecOtherOrigin ); + pTheirSequence->SetAbsAngles( angOtherAngles ); + pTheirSequence->ForceSetTargetEntity( pOtherNPC, true ); + pTheirSequence->SetName( iszSSName ); + pTheirSequence->AddSpawnFlags( SF_SCRIPT_NOINTERRUPT | SF_SCRIPT_HIGH_PRIORITY | SF_SCRIPT_OVERRIDESTATE ); + pTheirSequence->SetLoopActionSequence( (pInteraction->iFlags & SCNPC_FLAG_LOOP_IN_ACTION) != 0 ); + pTheirSequence->SetSynchPostIdles( true ); + if ( ai_debug_dyninteractions.GetBool() ) + { + pTheirSequence->m_debugOverlays |= OVERLAY_TEXT_BIT | OVERLAY_PIVOT_BIT; + } + + // Tell their sequence to keep their position relative to me + pTheirSequence->SetupInteractionPosition( this, pInteraction->matDesiredLocalToWorld ); + } + + // Spawn both sequences at once + pMySequence->Spawn(); + if ( pTheirSequence ) + { + pTheirSequence->Spawn(); + } + + // Call activate on both sequences at once + pMySequence->Activate(); + if ( pTheirSequence ) + { + pTheirSequence->Activate(); + } + + // Setup the outputs for both sequences. The first kills them both when it finishes + pMySequence->KeyValue( "OnCancelFailedSequence", UTIL_VarArgs("%s,Kill,,0,-1", szSSName ) ); + if ( pszExitSequence ) + { + pMySequence->KeyValue( "OnPostIdleEndSequence", UTIL_VarArgs("%s,Kill,,0,-1", szSSName ) ); + if ( pTheirSequence ) + { + pTheirSequence->KeyValue( "OnPostIdleEndSequence", UTIL_VarArgs("%s,Kill,,0,-1", szSSName ) ); + } + } + else + { + pMySequence->KeyValue( "OnEndSequence", UTIL_VarArgs("%s,Kill,,0,-1", szSSName ) ); + if ( pTheirSequence ) + { + pTheirSequence->KeyValue( "OnEndSequence", UTIL_VarArgs("%s,Kill,,0,-1", szSSName ) ); + } + } + if ( pTheirSequence ) + { + pTheirSequence->KeyValue( "OnCancelFailedSequence", UTIL_VarArgs("%s,Kill,,0,-1", szSSName ) ); + } + + // Tell both sequences to start + pMySequence->AcceptInput( "BeginSequence", this, this, emptyVariant, 0 ); + if ( pTheirSequence ) + { + pTheirSequence->AcceptInput( "BeginSequence", this, this, emptyVariant, 0 ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CAI_BaseNPC::CanRunAScriptedNPCInteraction( bool bForced ) +{ + if ( m_NPCState != NPC_STATE_IDLE && m_NPCState != NPC_STATE_ALERT && m_NPCState != NPC_STATE_COMBAT ) + return false; + + if ( !IsAlive() ) + return false; + + if ( IsOnFire() ) + return false; + + if ( IsCrouching() ) + return false; + + // Not while running scripted sequences + if ( m_hCine ) + return false; + + if ( bForced ) + { + if ( !m_hForcedInteractionPartner ) + return false; + } + else + { + if ( m_hForcedInteractionPartner || m_hInteractionPartner ) + return false; + if ( IsInAScript() || !HasCondition(COND_IN_PVS) ) + return false; + if ( HasCondition(COND_HEAR_DANGER) || HasCondition(COND_HEAR_MOVE_AWAY) ) + return false; + + // Default AI prevents interactions while melee attacking, but not ranged attacking + if ( IsCurSchedule( SCHED_MELEE_ATTACK1 ) || IsCurSchedule( SCHED_MELEE_ATTACK2 ) ) + return false; + } + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CAI_BaseNPC::CheckForScriptedNPCInteractions( void ) +{ + // Are we being forced to interact with another NPC? If so, do that + if ( m_hForcedInteractionPartner ) + { + CheckForcedNPCInteractions(); + return; + } + + // Otherwise, see if we can interaction with our enemy + if ( !m_ScriptedInteractions.Count() || !GetEnemy() ) + return; + + CAI_BaseNPC *pNPC = GetEnemy()->MyNPCPointer(); + + if( !pNPC ) + return; + + // Recalculate interaction capability whenever we switch enemies + if ( m_hLastInteractionTestTarget != GetEnemy() ) + { + m_hLastInteractionTestTarget = GetEnemy(); + + CalculateValidEnemyInteractions(); + } + + // First, make sure I'm in a state where I can do this + if ( !CanRunAScriptedNPCInteraction() ) + return; + if ( pNPC && !pNPC->CanRunAScriptedNPCInteraction() ) + return; + + for ( int i = 0; i < m_ScriptedInteractions.Count(); i++ ) + { + ScriptedNPCInteraction_t *pInteraction = &m_ScriptedInteractions[i]; + + if ( !pInteraction->bValidOnCurrentEnemy ) + continue; + if ( pInteraction->flNextAttemptTime > gpGlobals->curtime ) + continue; + + Vector vecOrigin; + QAngle angAngles; + if ( !InteractionCouldStart( pNPC, pInteraction, vecOrigin, angAngles ) ) + continue; + + m_iInteractionPlaying = i; + StartScriptedNPCInteraction( pNPC, pInteraction, vecOrigin, angAngles ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Calculate all the valid dynamic interactions we can perform with our current enemy +//----------------------------------------------------------------------------- +void CAI_BaseNPC::CalculateValidEnemyInteractions( void ) +{ + CAI_BaseNPC *pNPC = GetEnemy()->MyNPCPointer(); + if ( !pNPC ) + return; + + bool bDebug = (m_debugOverlays & OVERLAY_NPC_SELECTED_BIT && ai_debug_dyninteractions.GetBool()); + if ( bDebug ) + { + Msg("%s(%s): Computing valid interactions with %s(%s)\n", GetClassName(), GetDebugName(), pNPC->GetClassName(), pNPC->GetDebugName() ); + } + + bool bFound = false; + for ( int i = 0; i < m_ScriptedInteractions.Count(); i++ ) + { + ScriptedNPCInteraction_t *pInteraction = &m_ScriptedInteractions[i]; + pInteraction->bValidOnCurrentEnemy = false; + + // If the trigger method of the interaction isn't the one we're after, we're done + if ( pInteraction->iTriggerMethod != SNPCINT_AUTOMATIC_IN_COMBAT ) + continue; + + if ( !pNPC->GetModelPtr() ) + continue; + + // If we have a damage filter that prevents us hurting the enemy, + // don't interact with him, since most interactions kill the enemy. + // Create a fake damage info to test it with. + CTakeDamageInfo tempinfo( this, this, vec3_origin, vec3_origin, 1.0, DMG_BULLET ); + if ( !pNPC->PassesDamageFilter( tempinfo ) ) + continue; + + // Check the weapon requirements for the interaction + if ( pInteraction->iFlags & SCNPC_FLAG_NEEDS_WEAPON_ME ) + { + if ( !GetActiveWeapon()) + continue; + + // Check the specific weapon type + if ( pInteraction->iszMyWeapon != NULL_STRING && GetActiveWeapon()->m_iClassname != pInteraction->iszMyWeapon ) + continue; + } + if ( pInteraction->iFlags & SCNPC_FLAG_NEEDS_WEAPON_THEM ) + { + if ( !pNPC->GetActiveWeapon() ) + continue; + + // Check the specific weapon type + if ( pInteraction->iszTheirWeapon != NULL_STRING && pNPC->GetActiveWeapon()->m_iClassname != pInteraction->iszTheirWeapon ) + continue; + } + + // Script needs the other NPC, so make sure they're not dead + if ( !pNPC->IsAlive() ) + continue; + + // Use sequence? or activity? + if ( pInteraction->sPhases[SNPCINT_SEQUENCE].iActivity != ACT_INVALID ) + { + // Resolve the activity to a sequence, and make sure our enemy has it + const char *pszSequence = GetScriptedNPCInteractionSequence( pInteraction, SNPCINT_SEQUENCE ); + if ( !pszSequence ) + continue; + if ( pNPC->LookupSequence( pszSequence ) == -1 ) + continue; + } + else + { + if ( pNPC->LookupSequence( STRING(pInteraction->sPhases[SNPCINT_SEQUENCE].iszSequence) ) == -1 ) + continue; + } + + pInteraction->bValidOnCurrentEnemy = true; + bFound = true; + + if ( bDebug ) + { + Msg(" Found: %s\n", STRING(pInteraction->iszInteractionName) ); + } + } + + if ( bDebug && !bFound ) + { + Msg(" No valid interactions found.\n"); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CAI_BaseNPC::CheckForcedNPCInteractions( void ) +{ + // If we don't have an interaction, we're waiting for our partner to start it. Do nothing. + if ( m_iInteractionPlaying == NPCINT_NONE ) + return; + + CAI_BaseNPC *pNPC = m_hForcedInteractionPartner->MyNPCPointer(); + + // First, make sure both NPCs are able to do this + if ( !CanRunAScriptedNPCInteraction( true ) || !pNPC->CanRunAScriptedNPCInteraction( true ) ) + { + // If we were still moving to our target, abort. + if ( m_iInteractionState == NPCINT_MOVING_TO_MARK ) + { + if ( m_hForcedInteractionPartner ) + { + // We've aborted a forced interaction. Let the mapmaker know. + m_OnForcedInteractionAborted.FireOutput( this, this ); + } + + CleanupForcedInteraction(); + pNPC->CleanupForcedInteraction(); + } + return; + } + + // Check to see if we can start our interaction. If we can, dance. + ScriptedNPCInteraction_t *pInteraction = &m_ScriptedInteractions[m_iInteractionPlaying]; + Vector vecOrigin; + QAngle angAngles; + if ( !InteractionCouldStart( pNPC, pInteraction, vecOrigin, angAngles ) ) + return;; + + StartScriptedNPCInteraction( pNPC, pInteraction, vecOrigin, angAngles ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CAI_BaseNPC::InteractionCouldStart( CAI_BaseNPC *pOtherNPC, ScriptedNPCInteraction_t *pInteraction, Vector &vecOrigin, QAngle &angAngles ) +{ + // Get a matrix that'll convert from my local interaction space to world space + VMatrix matMeToWorld, matLocalToWorld; + QAngle angMyCurrent = GetAbsAngles(); + angMyCurrent[YAW] = m_flInteractionYaw; + matMeToWorld.SetupMatrixOrgAngles( GetAbsOrigin(), angMyCurrent ); + MatrixMultiply( matMeToWorld, pInteraction->matDesiredLocalToWorld, matLocalToWorld ); + + // Get the desired NPC position in worldspace + vecOrigin = matLocalToWorld.GetTranslation(); + MatrixToAngles( matLocalToWorld, angAngles ); + + bool bDebug = ai_debug_dyninteractions.GetBool(); + if ( bDebug ) + { + NDebugOverlay::Axis( vecOrigin, angAngles, 20, true, 0.1 ); + } + + // Determine whether or not the enemy is on the target + float flDistSqr = (vecOrigin - pOtherNPC->GetAbsOrigin()).LengthSqr(); + if ( flDistSqr > pInteraction->flDistSqr ) + { + if ( bDebug ) + { + if ( m_debugOverlays & OVERLAY_NPC_SELECTED_BIT || pOtherNPC->m_debugOverlays & OVERLAY_NPC_SELECTED_BIT ) + { + if ( ai_debug_dyninteractions.GetFloat() == 2 ) + { + Msg(" %s distsqr: %0.2f (%0.2f %0.2f %0.2f), desired: (%0.2f %0.2f %0.2f)\n", GetDebugName(), flDistSqr, + pOtherNPC->GetAbsOrigin().x, pOtherNPC->GetAbsOrigin().y, pOtherNPC->GetAbsOrigin().z, vecOrigin.x, vecOrigin.y, vecOrigin.z ); + } + } + } + return false; + } + + if ( bDebug ) + { + Msg("DYNINT: (%s) testing interaction \"%s\"\n", GetDebugName(), STRING(pInteraction->iszInteractionName) ); + Msg(" %s is at: %0.2f %0.2f %0.2f\n", GetDebugName(), GetAbsOrigin().x, GetAbsOrigin().y, GetAbsOrigin().z ); + Msg(" %s distsqr: %0.2f (%0.2f %0.2f %0.2f), desired: (%0.2f %0.2f %0.2f)\n", GetDebugName(), flDistSqr, + pOtherNPC->GetAbsOrigin().x, pOtherNPC->GetAbsOrigin().y, pOtherNPC->GetAbsOrigin().z, vecOrigin.x, vecOrigin.y, vecOrigin.z ); + + if ( pOtherNPC ) + { + float flOtherSpeed = pOtherNPC->GetSequenceGroundSpeed( pOtherNPC->GetSequence() ); + Msg(" %s Speed: %.2f\n", pOtherNPC->GetSequenceName( pOtherNPC->GetSequence() ), flOtherSpeed); + } + } + + // Angle check, if we're supposed to + if ( pInteraction->iFlags & SCNPC_FLAG_TEST_OTHER_ANGLES ) + { + QAngle angEnemyAngles = pOtherNPC->GetAbsAngles(); + bool bMatches = true; + for ( int ang = 0; ang < 3; ang++ ) + { + float flAngDiff = AngleDiff( angEnemyAngles[ang], angAngles[ang] ); + if ( fabs(flAngDiff) > DSS_MAX_ANGLE_DIFF ) + { + bMatches = false; + break; + } + } + if ( !bMatches ) + return false; + + if ( bDebug ) + { + Msg(" %s angle matched: (%0.2f %0.2f %0.2f), desired (%0.2f, %0.2f, %0.2f)\n", GetDebugName(), + anglemod(angEnemyAngles.x), anglemod(angEnemyAngles.y), anglemod(angEnemyAngles.z), anglemod(angAngles.x), anglemod(angAngles.y), anglemod(angAngles.z) ); + } + } + + // TODO: Velocity check, if we're supposed to + if ( pInteraction->iFlags & SCNPC_FLAG_TEST_OTHER_VELOCITY ) + { + + } + + // Valid so far. Now check to make sure there's nothing in the way. + // This isn't a very good method of checking, but it's cheap and rules out the problems we're seeing so far. + // If we start getting interactions that start a fair distance apart, we're going to need to do more work here. + trace_t tr; + AI_TraceLine( EyePosition(), pOtherNPC->EyePosition(), MASK_NPCSOLID, this, COLLISION_GROUP_NONE, &tr); + if ( tr.fraction != 1.0 && tr.m_pEnt != pOtherNPC ) + { + if ( bDebug ) + { + Msg( " %s Interaction was blocked.\n", GetDebugName() ); + NDebugOverlay::Line( tr.startpos, tr.endpos, 0,255,0, true, 1.0 ); + NDebugOverlay::Line( pOtherNPC->EyePosition(), tr.endpos, 255,0,0, true, 1.0 ); + } + return false; + } + + if ( bDebug ) + { + NDebugOverlay::Line( tr.startpos, tr.endpos, 0,255,0, true, 1.0 ); + } + + // Do a knee-level trace to find low physics objects + Vector vecMyKnee, vecOtherKnee; + CollisionProp()->NormalizedToWorldSpace( Vector(0,0,0.25f), &vecMyKnee ); + pOtherNPC->CollisionProp()->NormalizedToWorldSpace( Vector(0,0,0.25f), &vecOtherKnee ); + AI_TraceLine( vecMyKnee, vecOtherKnee, MASK_NPCSOLID, this, COLLISION_GROUP_NONE, &tr); + if ( tr.fraction != 1.0 && tr.m_pEnt != pOtherNPC ) + { + if ( bDebug ) + { + Msg( " %s Interaction was blocked.\n", GetDebugName() ); + NDebugOverlay::Line( tr.startpos, tr.endpos, 0,255,0, true, 1.0 ); + NDebugOverlay::Line( vecOtherKnee, tr.endpos, 255,0,0, true, 1.0 ); + } + return false; + } + + if ( bDebug ) + { + NDebugOverlay::Line( tr.startpos, tr.endpos, 0,255,0, true, 1.0 ); + } + + // Finally, make sure the NPC can actually fit at the interaction position + // This solves problems with NPCs who are a few units or so above the + // interaction point, and would sink into the ground when playing the anim. + CTraceFilterSkipTwoEntities traceFilter( pOtherNPC, this, COLLISION_GROUP_NONE ); + AI_TraceHull( vecOrigin, vecOrigin, pOtherNPC->GetHullMins(), pOtherNPC->GetHullMaxs(), MASK_SOLID, &traceFilter, &tr ); + if ( tr.startsolid ) + { + if ( bDebug ) + { + NDebugOverlay::Box( vecOrigin, pOtherNPC->GetHullMins(), pOtherNPC->GetHullMaxs(), 255,0,0, true, 1.0 ); + } + return false; + } + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: Return true if this NPC cannot die because it's in an interaction +// and the flag has been set by the animation. +//----------------------------------------------------------------------------- +bool CAI_BaseNPC::HasInteractionCantDie( void ) +{ + return ( m_bCannotDieDuringInteraction && IsRunningDynamicInteraction() ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : &inputdata - +//----------------------------------------------------------------------------- +void CAI_BaseNPC::InputForceInteractionWithNPC( inputdata_t &inputdata ) +{ + // Get the interaction name & target + char parseString[255]; + Q_strncpy(parseString, inputdata.value.String(), sizeof(parseString)); + + // First, the target's name + char *pszParam = strtok(parseString," "); + if ( !pszParam || !pszParam[0] ) + { + Warning("%s(%s) received ForceInteractionWithNPC input with bad parameters: %s\nFormat should be: ForceInteractionWithNPC \n", GetClassname(), GetDebugName(), inputdata.value.String() ); + return; + } + // Find the target + CBaseEntity *pTarget = FindNamedEntity( pszParam ); + if ( !pTarget ) + { + Warning("%s(%s) received ForceInteractionWithNPC input, but couldn't find entity named: %s\n", GetClassname(), GetDebugName(), pszParam ); + return; + } + CAI_BaseNPC *pNPC = pTarget->MyNPCPointer(); + if ( !pNPC || !pNPC->GetModelPtr() ) + { + Warning("%s(%s) received ForceInteractionWithNPC input, but entity named %s cannot run dynamic interactions.\n", GetClassname(), GetDebugName(), pszParam ); + return; + } + + // Second, the interaction name + pszParam = strtok(NULL," "); + if ( !pszParam || !pszParam[0] ) + { + Warning("%s(%s) received ForceInteractionWithNPC input with bad parameters: %s\nFormat should be: ForceInteractionWithNPC \n", GetClassname(), GetDebugName(), inputdata.value.String() ); + return; + } + + // Find the interaction from the name, and ensure it's one that the target NPC can play + int iInteraction = -1; + for ( int i = 0; i < m_ScriptedInteractions.Count(); i++ ) + { + if ( Q_strncmp( pszParam, STRING(m_ScriptedInteractions[i].iszInteractionName), strlen(pszParam) ) ) + continue; + + // Use sequence? or activity? + if ( m_ScriptedInteractions[i].sPhases[SNPCINT_SEQUENCE].iActivity != ACT_INVALID ) + { + if ( !pNPC->HaveSequenceForActivity( (Activity)m_ScriptedInteractions[i].sPhases[SNPCINT_SEQUENCE].iActivity ) ) + { + // Other NPC may have all the matching sequences, but just without the activity specified. + // Lets find a single sequence for us, and ensure they have a matching one. + int iMySeq = SelectWeightedSequence( (Activity)m_ScriptedInteractions[i].sPhases[SNPCINT_SEQUENCE].iActivity ); + if ( pNPC->LookupSequence( GetSequenceName(iMySeq) ) == -1 ) + continue; + } + } + else + { + if ( pNPC->LookupSequence( STRING(m_ScriptedInteractions[i].sPhases[SNPCINT_SEQUENCE].iszSequence) ) == -1 ) + continue; + } + + iInteraction = i; + break; + } + + if ( iInteraction == -1 ) + { + Warning("%s(%s) received ForceInteractionWithNPC input, but couldn't find an interaction named %s that entity named %s could run.\n", GetClassname(), GetDebugName(), pszParam, pNPC->GetDebugName() ); + return; + } + + // Found both pieces of data, lets dance. + StartForcedInteraction( pNPC, iInteraction ); + pNPC->StartForcedInteraction( this, NPCINT_NONE ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CAI_BaseNPC::StartForcedInteraction( CAI_BaseNPC *pNPC, int iInteraction ) +{ + m_hForcedInteractionPartner = pNPC; + ClearSchedule(); + + m_iInteractionPlaying = iInteraction; + m_iInteractionState = NPCINT_MOVING_TO_MARK; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CAI_BaseNPC::CleanupForcedInteraction( void ) +{ + m_hForcedInteractionPartner = NULL; + m_iInteractionPlaying = NPCINT_NONE; + m_iInteractionState = NPCINT_NOT_RUNNING; +} + +//----------------------------------------------------------------------------- +// Purpose: Calculate a position to move to so that I can interact with my +// target NPC. +// +// FIXME: THIS ONLY WORKS FOR INTERACTIONS THAT REQUIRE THE TARGET +// NPC TO BE SOME DISTANCE DIRECTLY IN FRONT OF ME. +//----------------------------------------------------------------------------- +void CAI_BaseNPC::CalculateForcedInteractionPosition( void ) +{ + if ( m_iInteractionPlaying == NPCINT_NONE ) + return; + + ScriptedNPCInteraction_t *pInteraction = GetRunningDynamicInteraction(); + + // Pretend I was facing the target, and extrapolate from that the position I should be at + Vector vecToTarget = m_hForcedInteractionPartner->GetAbsOrigin() - GetAbsOrigin(); + VectorNormalize( vecToTarget ); + QAngle angToTarget; + VectorAngles( vecToTarget, angToTarget ); + + // Get the desired position in worldspace, relative to the target + VMatrix matMeToWorld, matLocalToWorld; + matMeToWorld.SetupMatrixOrgAngles( GetAbsOrigin(), angToTarget ); + MatrixMultiply( matMeToWorld, pInteraction->matDesiredLocalToWorld, matLocalToWorld ); + + Vector vecOrigin = GetAbsOrigin() - matLocalToWorld.GetTranslation(); + m_vecForcedWorldPosition = m_hForcedInteractionPartner->GetAbsOrigin() + vecOrigin; + + //NDebugOverlay::Axis( m_vecForcedWorldPosition, angToTarget, 20, true, 3.0 ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *pPlayer - +//----------------------------------------------------------------------------- +void CAI_BaseNPC::PlayerHasIlluminatedNPC( CBasePlayer *pPlayer, float flDot ) +{ +#ifdef HL2_DLL + if ( IsActiveDynamicInteraction() ) + { + ScriptedNPCInteraction_t *pInteraction = GetRunningDynamicInteraction(); + if ( pInteraction->iLoopBreakTriggerMethod & SNPCINT_LOOPBREAK_ON_FLASHLIGHT_ILLUM ) + { + // Only do this in alyx darkness mode + if ( HL2GameRules()->IsAlyxInDarknessMode() ) + { + // Can only break when we're in the action anim + if ( m_hCine->IsPlayingAction() ) + { + m_hCine->StopActionLoop( true ); + } + } + } + } +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CAI_BaseNPC::ModifyOrAppendCriteria( AI_CriteriaSet& set ) +{ + BaseClass::ModifyOrAppendCriteria( set ); + + // Append time since seen player + if ( m_flLastSawPlayerTime ) + { + set.AppendCriteria( "timesinceseenplayer", UTIL_VarArgs( "%f", gpGlobals->curtime - m_flLastSawPlayerTime ) ); + } + else + { + set.AppendCriteria( "timesinceseenplayer", "-1" ); + } + + // Append distance to my enemy + if ( GetEnemy() ) + { + set.AppendCriteria( "distancetoenemy", UTIL_VarArgs( "%f", EnemyDistance(GetEnemy()) ) ); + } + else + { + set.AppendCriteria( "distancetoenemy", "-1" ); + } +} + +//----------------------------------------------------------------------------- +// If I were crouching at my current location, could I shoot this target? +//----------------------------------------------------------------------------- +bool CAI_BaseNPC::CouldShootIfCrouching( CBaseEntity *pTarget ) +{ + bool bWasStanding = !IsCrouching(); + Crouch(); + + Vector vecTarget; + if (GetActiveWeapon()) + { + vecTarget = pTarget->BodyTarget( GetActiveWeapon()->GetLocalOrigin() ); + } + else + { + vecTarget = pTarget->BodyTarget( GetLocalOrigin() ); + } + + bool bResult = WeaponLOSCondition( GetLocalOrigin(), vecTarget, false ); + + if ( bWasStanding ) + { + Stand(); + } + + return bResult; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CAI_BaseNPC::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: + return true; + break; + default: + break; + } + + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: Get shoot position of BCC at an arbitrary position +//----------------------------------------------------------------------------- +Vector CAI_BaseNPC::Weapon_ShootPosition( void ) +{ + Vector right; + GetVectors( NULL, &right, NULL ); + + bool bStanding = !IsCrouching(); + if ( bStanding && (CapabilitiesGet() & bits_CAP_DUCK) ) + { + if ( IsCrouchedActivity( GetActivity() ) ) + { + bStanding = false; + } + } + + if ( !bStanding ) + return (GetAbsOrigin() + GetCrouchGunOffset() + right * 8); + + return BaseClass::Weapon_ShootPosition(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CAI_BaseNPC::ShouldProbeCollideAgainstEntity( CBaseEntity *pEntity ) +{ + if ( pEntity->GetMoveType() == MOVETYPE_VPHYSICS ) + { + if ( ai_test_moveprobe_ignoresmall.GetBool() && IsNavigationUrgent() ) + { + IPhysicsObject *pPhysics = pEntity->VPhysicsGetObject(); + + if ( pPhysics->IsMoveable() && pPhysics->GetMass() < 40.0 ) + return false; + } + } + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CAI_BaseNPC::Crouch( void ) +{ + m_bIsCrouching = true; + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CAI_BaseNPC::IsCrouching( void ) +{ + return ( (CapabilitiesGet() & bits_CAP_DUCK) && m_bIsCrouching ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CAI_BaseNPC::Stand( void ) +{ + if ( m_bForceCrouch ) + return false; + + m_bIsCrouching = false; + DesireStand(); + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CAI_BaseNPC::DesireCrouch( void ) +{ + m_bCrouchDesired = true; +} + +bool CAI_BaseNPC::IsInChoreo() const +{ + return m_bInChoreo; +} diff --git a/dlls/ai_basenpc_physicsflyer.cpp b/dlls/ai_basenpc_physicsflyer.cpp index d26ab945..2b8230b4 100644 --- a/dlls/ai_basenpc_physicsflyer.cpp +++ b/dlls/ai_basenpc_physicsflyer.cpp @@ -336,8 +336,6 @@ void CAI_BasePhysicsFlyingBot::TranslateNavGoal( CBaseEntity *pTarget, Vector &c //----------------------------------------------------------------------------- IMotionEvent::simresult_e CAI_BasePhysicsFlyingBot::Simulate( IPhysicsMotionController *pController, IPhysicsObject *pObject, float deltaTime, Vector &linear, AngularImpulse &angular ) { - static int count; - IPhysicsObject *pPhysicsObject = VPhysicsGetObject(); // Assert( pPhysicsObject ); if (!pPhysicsObject) diff --git a/dlls/ai_basenpc_schedule.cpp b/dlls/ai_basenpc_schedule.cpp index cccb5701..04f66130 100644 --- a/dlls/ai_basenpc_schedule.cpp +++ b/dlls/ai_basenpc_schedule.cpp @@ -1132,7 +1132,7 @@ void CAI_BaseNPC::StartTask( const Task_t *pTask ) { if (!GetHintNode()) { - SetHintNode( CAI_HintManager::FindHint( this, HINT_NONE, pTask->flTaskData, 2000 ) ); + SetHintNode( CAI_HintManager::FindHint( this, HINT_NONE, static_cast(pTask->flTaskData), 2000 ) ); } if (GetHintNode()) { @@ -1330,7 +1330,7 @@ void CAI_BaseNPC::StartTask( const Task_t *pTask ) break; case TASK_SET_SCHEDULE: - if ( !SetSchedule( pTask->flTaskData ) ) + if ( !SetSchedule( static_cast(pTask->flTaskData) ) ) TaskFail(FAIL_SCHEDULE_NOT_FOUND); break; @@ -2537,7 +2537,7 @@ void CAI_BaseNPC::StartTask( const Task_t *pTask ) } case TASK_SPEAK_SENTENCE: { - SpeakSentence(pTask->flTaskData); + SpeakSentence(static_cast(pTask->flTaskData)); TaskComplete(); break; } @@ -2842,7 +2842,7 @@ void CAI_BaseNPC::StartTask( const Task_t *pTask ) { int iMinDist, iMaxDist, iParameter; - iParameter = pTask->flTaskData; + iParameter = static_cast(pTask->flTaskData); iMinDist = iParameter / 10000; iMaxDist = iParameter - (iMinDist * 10000); diff --git a/dlls/ai_behavior.h b/dlls/ai_behavior.h index 851c2afa..113dd856 100644 --- a/dlls/ai_behavior.h +++ b/dlls/ai_behavior.h @@ -15,11 +15,15 @@ #include "networkvar.h" #ifdef DEBUG +#ifdef _MSC_VER #pragma warning(push) +#endif #include +#ifdef _MSC_VER #pragma warning(pop) #pragma warning(disable:4290) #endif +#endif #if defined( _WIN32 ) #pragma once @@ -262,28 +266,28 @@ public: { if ( condition >= ID_SPACE_OFFSET && condition < ID_SPACE_OFFSET + 10000 ) // it's local to us condition = GetClassScheduleIdSpace()->ConditionLocalToGlobal( condition ); - GetOuter()->SetCondition( condition ); + this->GetOuter()->SetCondition( condition ); } bool HasCondition( int condition ) { if ( condition >= ID_SPACE_OFFSET && condition < ID_SPACE_OFFSET + 10000 ) // it's local to us condition = GetClassScheduleIdSpace()->ConditionLocalToGlobal( condition ); - return GetOuter()->HasCondition( condition ); + return this->GetOuter()->HasCondition( condition ); } bool HasInterruptCondition( int condition ) { if ( condition >= ID_SPACE_OFFSET && condition < ID_SPACE_OFFSET + 10000 ) // it's local to us condition = GetClassScheduleIdSpace()->ConditionLocalToGlobal( condition ); - return GetOuter()->HasInterruptCondition( condition ); + return this->GetOuter()->HasInterruptCondition( condition ); } void ClearCondition( int condition ) { if ( condition >= ID_SPACE_OFFSET && condition < ID_SPACE_OFFSET + 10000 ) // it's local to us condition = GetClassScheduleIdSpace()->ConditionLocalToGlobal( condition ); - GetOuter()->ClearCondition( condition ); + this->GetOuter()->ClearCondition( condition ); } protected: @@ -298,7 +302,7 @@ protected: } virtual CAI_ClassScheduleIdSpace *GetClassScheduleIdSpace() { - return GetOuter()->GetClassScheduleIdSpace(); + return this->GetOuter()->GetClassScheduleIdSpace(); } static CAI_ClassScheduleIdSpace &AccessClassScheduleIdSpaceDirect() @@ -968,7 +972,7 @@ inline void CAI_BehaviorHost::ChangeBehaviorTo( CAI_BehaviorBase *pNew if ( pOldBehavior ) { pOldBehavior->EndScheduleSelection(); - VacateStrategySlot(); + this->VacateStrategySlot(); } OnChangeRunningBehavior( pOldBehavior, pNewBehavior ); diff --git a/dlls/ai_behavior_assault.cpp b/dlls/ai_behavior_assault.cpp index bcff8f9b..6dae7492 100644 --- a/dlls/ai_behavior_assault.cpp +++ b/dlls/ai_behavior_assault.cpp @@ -169,7 +169,7 @@ void CAI_AssaultBehavior::ClearAssaultPoint( void ) } else { - DevMsg("**ERROR: Can't find next assault point: %s\n", m_hAssaultPoint->m_NextAssaultPointName ); + DevMsg("**ERROR: Can't find next assault point: %s\n", STRING(m_hAssaultPoint->m_NextAssaultPointName) ); // Bomb out of assault behavior. m_AssaultCue = CUE_NO_ASSAULT; @@ -785,7 +785,7 @@ void CAI_AssaultBehavior::SetParameters( string_t rallypointname, AssaultCue_t a if( !pBest ) { - DevMsg("%s Didn't find a best rally point!\n", GetOuter()->GetEntityName() ); + DevMsg("%s Didn't find a best rally point!\n", STRING(GetOuter()->GetEntityName()) ); return; } @@ -887,6 +887,8 @@ bool CAI_AssaultBehavior::PollAssaultCue( void ) // Player told me to go, so go! return m_ReceivedAssaultCue == CUE_COMMANDER; break; + default: + break; } return false; diff --git a/dlls/ai_behavior_follow.cpp b/dlls/ai_behavior_follow.cpp index 7f8dfba2..7a8a4fca 100644 --- a/dlls/ai_behavior_follow.cpp +++ b/dlls/ai_behavior_follow.cpp @@ -2289,7 +2289,7 @@ AI_FollowFormation_t *AIGetFormation( AI_Formations_t formation ) { if ( formation < 0 ) formation = (AI_Formations_t)0; - else if ( formation >= ARRAYSIZE( g_AI_Formations ) ) + else if ( static_cast(formation) >= ARRAYSIZE( g_AI_Formations ) ) formation = (AI_Formations_t)(ARRAYSIZE( g_AI_Formations ) - 1 ); return g_AI_Formations[formation]; diff --git a/dlls/ai_behavior_passenger.cpp b/dlls/ai_behavior_passenger.cpp index b593db5c..82340bed 100644 --- a/dlls/ai_behavior_passenger.cpp +++ b/dlls/ai_behavior_passenger.cpp @@ -51,10 +51,10 @@ END_DATADESC(); //----------------------------------------------------------------------------- // Constructor //----------------------------------------------------------------------------- -CAI_PassengerBehavior::CAI_PassengerBehavior( void ) : -m_bEnabled( false ), -m_hVehicle( NULL ), +CAI_PassengerBehavior::CAI_PassengerBehavior( void ) : m_PassengerState( PASSENGER_STATE_OUTSIDE ), +m_hVehicle( NULL ), +m_bEnabled( false ), m_PassengerIntent( PASSENGER_INTENT_NONE ), m_nTransitionSequence( -1 ) { diff --git a/dlls/ai_behavior_rappel.cpp b/dlls/ai_behavior_rappel.cpp index 727ec639..6120fbbb 100644 --- a/dlls/ai_behavior_rappel.cpp +++ b/dlls/ai_behavior_rappel.cpp @@ -59,7 +59,7 @@ void CRopeAnchor::Spawn() flDist = fabs( GetOwnerEntity()->GetAbsOrigin().z - GetAbsOrigin().z ); } - m_hRope = CRopeKeyframe::CreateWithSecondPointDetached( this, -1, flDist, RAPPEL_ROPE_WIDTH, "cable/cable.vmt", 5, true ); + m_hRope = CRopeKeyframe::CreateWithSecondPointDetached( this, -1, static_cast(flDist), RAPPEL_ROPE_WIDTH, "cable/cable.vmt", 5, true ); ASSERT( m_hRope != NULL ); diff --git a/dlls/ai_behavior_standoff.cpp b/dlls/ai_behavior_standoff.cpp index 84361449..2037f840 100644 --- a/dlls/ai_behavior_standoff.cpp +++ b/dlls/ai_behavior_standoff.cpp @@ -1110,7 +1110,7 @@ void CAI_MappedActivityBehavior_Temporary::UpdateTranslateActivityMap() CBaseCombatWeapon *pWeapon = GetOuter()->GetActiveWeapon(); const char *pszWeaponClass = ( pWeapon ) ? pWeapon->GetClassname() : ""; - for ( int i = 0; i < ARRAYSIZE(mappings); i++ ) + for ( size_t i = 0; i < ARRAYSIZE(mappings); i++ ) { if ( !mappings[i].pszWeapon || stricmp( mappings[i].pszWeapon, pszWeaponClass ) == 0 ) { diff --git a/dlls/ai_blended_movement.cpp b/dlls/ai_blended_movement.cpp index 87fe135a..87810768 100644 --- a/dlls/ai_blended_movement.cpp +++ b/dlls/ai_blended_movement.cpp @@ -961,7 +961,7 @@ int CAI_BlendedMotor::BuildInsertNode( int i, float flTime ) Assert( flTime > 0.0 ); - for (i; i < m_scriptTurn.Count() - 1; i++) + for (; i < m_scriptTurn.Count() - 1; i++) { if (m_scriptTurn[i].flTime < flTime) { @@ -1203,12 +1203,8 @@ void CAI_BlendedMotor::BuildVelocityScript( const AILocalMoveGoal_t &move ) script.flMaxVelocity = 0; } break; - /* - case NAV_FLY: - // FIXME: can there be a NAV_GROUND -> NAV_FLY transition? - script.flMaxVelocity = 0; + default: break; - */ } } else diff --git a/dlls/ai_blended_movement.h b/dlls/ai_blended_movement.h index dbb8d17a..a02611a9 100644 --- a/dlls/ai_blended_movement.h +++ b/dlls/ai_blended_movement.h @@ -212,8 +212,8 @@ class CAI_BlendingHost : public BASE_NPC { DECLARE_CLASS_NOFRIEND( CAI_BlendingHost, BASE_NPC ); public: - const CAI_BlendedMotor *GetBlendedMotor() const { return assert_cast(GetMotor()); } - CAI_BlendedMotor * GetBlendedMotor() { return assert_cast(GetMotor()); } + const CAI_BlendedMotor *GetBlendedMotor() const { return assert_cast(this->GetMotor()); } + CAI_BlendedMotor * GetBlendedMotor() { return assert_cast(this->GetMotor()); } CAI_Motor *CreateMotor() { @@ -230,7 +230,7 @@ public: float MaxYawSpeed( void ) { - float override = GetBlendedMotor()->OverrideMaxYawSpeed( GetActivity() ); + float override = GetBlendedMotor()->OverrideMaxYawSpeed( this->GetActivity() ); if ( override != -1 ) return override; return BaseClass::MaxYawSpeed(); diff --git a/dlls/ai_default.cpp b/dlls/ai_default.cpp index bba29993..2d3e7cb2 100644 --- a/dlls/ai_default.cpp +++ b/dlls/ai_default.cpp @@ -329,6 +329,8 @@ int CAI_BaseNPC::TranslateSchedule( int scheduleType ) return SCHED_ALERT_WALK; case NPC_STATE_COMBAT: return SCHED_COMBAT_WALK; + default: + break; } } break; diff --git a/dlls/ai_dynamiclink.h b/dlls/ai_dynamiclink.h index 01ad7fa0..9ab21fb4 100644 --- a/dlls/ai_dynamiclink.h +++ b/dlls/ai_dynamiclink.h @@ -1,121 +1,121 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: A link that can be turned on and off. Unlike normal links -// dyanimc links must be entities so they and receive messages. -// They update the state of the actual links. Allows us to save -// a lot of memory by not making all links into entities -// -// $Workfile: $ -// $Date: $ -// -//----------------------------------------------------------------------------- -// $Log: $ -// -// $NoKeywords: $ -//=============================================================================// - -#ifndef AI_DYNAMICLINK_H -#define AI_DYNAMICLINK_H -#pragma once - -enum DynamicLinkState_t -{ - LINK_OFF = 0, - LINK_ON = 1, -}; - -class CAI_Link; - -//============================================================================= -// >> CAI_DynanicLink -//============================================================================= -class CAI_DynamicLink : public CServerOnlyEntity -{ - DECLARE_CLASS( CAI_DynamicLink, CServerOnlyEntity ); -public: - static void InitDynamicLinks(void); - static void ResetDynamicLinks(void); - static void PurgeDynamicLinks(void); - static void GenerateControllerLinks(); - - static bool gm_bInitialized; - - static CAI_DynamicLink* GetDynamicLink(int nSrcID, int nDstID); - - static CAI_DynamicLink* m_pAllDynamicLinks; // A linked list of all dynamic link - CAI_DynamicLink* m_pNextDynamicLink; // The next dynamic link in the list of dynamic links - - int m_nSrcEditID; // the node that 'owns' this link - int m_nDestEditID; // the node on the other end of the link. - - int m_nSrcID; // the node that 'owns' this link - int m_nDestID; // the node on the other end of the link. - DynamicLinkState_t m_nLinkState; // - string_t m_strAllowUse; - - bool m_bFixedUpIds; - bool m_bNotSaved; - int m_nLinkType; - - void SetLinkState( void ); - bool IsLinkValid( void ); - - CAI_Link * FindLink(); - - int ObjectCaps(); - - // ---------------- - // Inputs - // ---------------- - void InputTurnOn( inputdata_t &inputdata ); - void InputTurnOff( inputdata_t &inputdata ); - - DECLARE_DATADESC(); - - CAI_DynamicLink(); - ~CAI_DynamicLink(); -}; - -//============================================================================= -// >> CAI_DynanicLinkVolume -//============================================================================= -class CAI_DynamicLinkController : public CServerOnlyEntity -{ - DECLARE_CLASS( CAI_DynamicLinkController, CServerOnlyEntity ); -public: - void GenerateLinksFromVolume(); - - // ---------------- - // Inputs - // ---------------- - void InputTurnOn( inputdata_t &inputdata ); - void InputTurnOff( inputdata_t &inputdata ); - - CUtlVector< CHandle > m_ControlledLinks; - DynamicLinkState_t m_nLinkState; // - string_t m_strAllowUse; - - DECLARE_DATADESC(); -}; - -//============================================================================= -//============================================================================= -class CAI_RadialLinkController : public CBaseEntity -{ - DECLARE_CLASS( CAI_RadialLinkController, CBaseEntity ); - -public: - void Spawn(); - void Activate(); - void PollMotionThink(); - void ModifyNodeLinks( bool bMakeStale ); - -public: - float m_flRadius; - Vector m_vecAtRestOrigin; - bool m_bAtRest; - - DECLARE_DATADESC(); -}; - -#endif // AI_DYNAMICLINK_H \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: A link that can be turned on and off. Unlike normal links +// dyanimc links must be entities so they and receive messages. +// They update the state of the actual links. Allows us to save +// a lot of memory by not making all links into entities +// +// $Workfile: $ +// $Date: $ +// +//----------------------------------------------------------------------------- +// $Log: $ +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef AI_DYNAMICLINK_H +#define AI_DYNAMICLINK_H +#pragma once + +enum DynamicLinkState_t +{ + LINK_OFF = 0, + LINK_ON = 1, +}; + +class CAI_Link; + +//============================================================================= +// >> CAI_DynanicLink +//============================================================================= +class CAI_DynamicLink : public CServerOnlyEntity +{ + DECLARE_CLASS( CAI_DynamicLink, CServerOnlyEntity ); +public: + static void InitDynamicLinks(void); + static void ResetDynamicLinks(void); + static void PurgeDynamicLinks(void); + static void GenerateControllerLinks(); + + static bool gm_bInitialized; + + static CAI_DynamicLink* GetDynamicLink(int nSrcID, int nDstID); + + static CAI_DynamicLink* m_pAllDynamicLinks; // A linked list of all dynamic link + CAI_DynamicLink* m_pNextDynamicLink; // The next dynamic link in the list of dynamic links + + int m_nSrcEditID; // the node that 'owns' this link + int m_nDestEditID; // the node on the other end of the link. + + int m_nSrcID; // the node that 'owns' this link + int m_nDestID; // the node on the other end of the link. + DynamicLinkState_t m_nLinkState; // + string_t m_strAllowUse; + + bool m_bFixedUpIds; + bool m_bNotSaved; + int m_nLinkType; + + void SetLinkState( void ); + bool IsLinkValid( void ); + + CAI_Link * FindLink(); + + int ObjectCaps(); + + // ---------------- + // Inputs + // ---------------- + void InputTurnOn( inputdata_t &inputdata ); + void InputTurnOff( inputdata_t &inputdata ); + + DECLARE_DATADESC(); + + CAI_DynamicLink(); + ~CAI_DynamicLink(); +}; + +//============================================================================= +// >> CAI_DynanicLinkVolume +//============================================================================= +class CAI_DynamicLinkController : public CServerOnlyEntity +{ + DECLARE_CLASS( CAI_DynamicLinkController, CServerOnlyEntity ); +public: + void GenerateLinksFromVolume(); + + // ---------------- + // Inputs + // ---------------- + void InputTurnOn( inputdata_t &inputdata ); + void InputTurnOff( inputdata_t &inputdata ); + + CUtlVector< CHandle > m_ControlledLinks; + DynamicLinkState_t m_nLinkState; // + string_t m_strAllowUse; + + DECLARE_DATADESC(); +}; + +//============================================================================= +//============================================================================= +class CAI_RadialLinkController : public CBaseEntity +{ + DECLARE_CLASS( CAI_RadialLinkController, CBaseEntity ); + +public: + void Spawn(); + void Activate(); + void PollMotionThink(); + void ModifyNodeLinks( bool bMakeStale ); + +public: + float m_flRadius; + Vector m_vecAtRestOrigin; + bool m_bAtRest; + + DECLARE_DATADESC(); +}; + +#endif // AI_DYNAMICLINK_H diff --git a/dlls/ai_eventresponse.h b/dlls/ai_eventresponse.h index 590ce001..f5091cc5 100644 --- a/dlls/ai_eventresponse.h +++ b/dlls/ai_eventresponse.h @@ -1,47 +1,47 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: AI system that makes NPCs verbally respond to game events -// -//=============================================================================// - -#ifndef AI_EVENTRESPONSE_H -#define AI_EVENTRESPONSE_H - -#include "utldict.h" - -#define NPCEVENTRESPONSE_DISTANCE_SQR (768 * 768) // Maximum distance for responding to NPCs -#define NPCEVENTRESPONSE_REFIRE_TIME 15.0 // Time after giving a response before giving any more -#define NPCEVENTRESPONSE_GIVEUP_TIME 4.0 // Time after a response trigger was fired before discarding it without responding - -//----------------------------------------------------------------------------- -// Purpose: AI system that makes NPCs verbally respond to game events -//----------------------------------------------------------------------------- -class CNPCEventResponseSystem : public CAutoGameSystemPerFrame -{ -public: - CNPCEventResponseSystem( char const *name ) : CAutoGameSystemPerFrame( name ) - { - } - - void LevelInitPreEntity(); - void FrameUpdatePreEntityThink(); - void TriggerEvent( const char *pResponse, bool bForce = false ); - -private: - float m_flNextEventPoll; - - struct storedevent_t - { - float flEventTime; - float flNextResponseTime; - bool bForce; - bool bPreventExpiration; - }; - - typedef CUtlDict< storedevent_t, int > EventMap; - EventMap m_ActiveEvents; -}; - -CNPCEventResponseSystem *NPCEventResponse(); - -#endif // AI_EVENTRESPONSE_H \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: AI system that makes NPCs verbally respond to game events +// +//=============================================================================// + +#ifndef AI_EVENTRESPONSE_H +#define AI_EVENTRESPONSE_H + +#include "utldict.h" + +#define NPCEVENTRESPONSE_DISTANCE_SQR (768 * 768) // Maximum distance for responding to NPCs +#define NPCEVENTRESPONSE_REFIRE_TIME 15.0 // Time after giving a response before giving any more +#define NPCEVENTRESPONSE_GIVEUP_TIME 4.0 // Time after a response trigger was fired before discarding it without responding + +//----------------------------------------------------------------------------- +// Purpose: AI system that makes NPCs verbally respond to game events +//----------------------------------------------------------------------------- +class CNPCEventResponseSystem : public CAutoGameSystemPerFrame +{ +public: + CNPCEventResponseSystem( char const *name ) : CAutoGameSystemPerFrame( name ) + { + } + + void LevelInitPreEntity(); + void FrameUpdatePreEntityThink(); + void TriggerEvent( const char *pResponse, bool bForce = false ); + +private: + float m_flNextEventPoll; + + struct storedevent_t + { + float flEventTime; + float flNextResponseTime; + bool bForce; + bool bPreventExpiration; + }; + + typedef CUtlDict< storedevent_t, int > EventMap; + EventMap m_ActiveEvents; +}; + +CNPCEventResponseSystem *NPCEventResponse(); + +#endif // AI_EVENTRESPONSE_H diff --git a/dlls/ai_hint.cpp b/dlls/ai_hint.cpp index dc555c70..f2f9d84c 100644 --- a/dlls/ai_hint.cpp +++ b/dlls/ai_hint.cpp @@ -311,7 +311,7 @@ int CAI_HintManager::FindAllHints( CAI_BaseNPC *pNPC, const Vector &position, co // If we have no hints, bail int c = CAI_HintManager::gm_AllHints.Count(); if ( !c ) - return NULL; + return 0; // Remove the nearest flag. It makes now sense with random. bool hadNearest = hintCriteria.HasFlag( bits_HINT_NODE_NEAREST ); @@ -682,7 +682,7 @@ int CAI_HintManager::GetFlags( const char *token ) char *lowercase = (char *)_alloca( len + 1 ); Q_strncpy( lowercase, token, len+1 ); - strlwr( lowercase ); + Q_strlower( lowercase ); if ( strstr( "none", lowercase ) ) { @@ -1003,6 +1003,8 @@ bool CAI_Hint::IsViewable(void) { case HINT_WORLD_VISUALLY_INTERESTING: return true; + default: + break; } return false; @@ -1551,7 +1553,7 @@ hinttypedescs_t g_pszHintDescriptions[] = //----------------------------------------------------------------------------- const char *GetHintTypeDescription( Hint_e iHintType ) { - for ( int i = 0; i < ARRAYSIZE(g_pszHintDescriptions); i++ ) + for ( size_t i = 0; i < ARRAYSIZE(g_pszHintDescriptions); i++ ) { if ( g_pszHintDescriptions[i].iType == iHintType ) return g_pszHintDescriptions[i].pszDesc; @@ -1581,7 +1583,7 @@ void CC_ai_drop_hint( void ) { Msg("Invalid hint type specified. Format: ai_drop_hint \nValid hint types:\n"); - for ( int i = 0; i < ARRAYSIZE(g_pszHintDescriptions); i++ ) + for ( size_t i = 0; i < ARRAYSIZE(g_pszHintDescriptions); i++ ) { Msg("%d : %s\n", g_pszHintDescriptions[i].iType, g_pszHintDescriptions[i].pszDesc ); } diff --git a/dlls/ai_hull.cpp b/dlls/ai_hull.cpp index 0ea99010..febc3f2e 100644 --- a/dlls/ai_hull.cpp +++ b/dlls/ai_hull.cpp @@ -11,7 +11,7 @@ struct ai_hull_t { ai_hull_t( int bit, const char *pName, const Vector &_mins, const Vector &_maxs, const Vector &_smallMins, const Vector &_smallMaxs ) - : hullBit( bit ), mins( _mins ), maxs( _maxs ), smallMins( _smallMins ), smallMaxs( _smallMaxs ), name( pName ) {} + : hullBit( bit ), name( pName ), mins( _mins ), maxs( _maxs ), smallMins( _smallMins ), smallMaxs( _smallMaxs ) {} int hullBit; const char* name; diff --git a/dlls/ai_hull.h b/dlls/ai_hull.h index 1173d62a..fa53e3b3 100644 --- a/dlls/ai_hull.h +++ b/dlls/ai_hull.h @@ -1,75 +1,75 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -//=============================================================================// - -#ifndef AI_HULL_H -#define AI_HULL_H -#pragma once - -class Vector; -//========================================================= -// Link Properties. These hulls must correspond to the hulls -// in AI_Hull.cpp! -//========================================================= -enum Hull_t -{ - HULL_HUMAN, // Combine, Stalker, Zombie... - HULL_SMALL_CENTERED, // Scanner - HULL_WIDE_HUMAN, // Vortigaunt - HULL_TINY, // Headcrab - HULL_WIDE_SHORT, // Bullsquid - HULL_MEDIUM, // Cremator - HULL_TINY_CENTERED, // Manhack - HULL_LARGE, // Antlion Guard - HULL_LARGE_CENTERED, // Mortar Synth - HULL_MEDIUM_TALL, // Ministrider -//-------------------------------------------- - NUM_HULLS, - HULL_NONE // No Hull (appears after num hulls as we don't want to count it) -}; - -enum Hull_Bits_t -{ - bits_HUMAN_HULL = 0x00000001, - bits_SMALL_CENTERED_HULL = 0x00000002, - bits_WIDE_HUMAN_HULL = 0x00000004, - bits_TINY_HULL = 0x00000008, - bits_WIDE_SHORT_HULL = 0x00000010, - bits_MEDIUM_HULL = 0x00000020, - bits_TINY_CENTERED_HULL = 0x00000040, - bits_LARGE_HULL = 0x00000080, - bits_LARGE_CENTERED_HULL = 0x00000100, - bits_MEDIUM_TALL_HULL = 0x00000200, - bits_HULL_BITS_MASK = 0x000002ff, -}; - -inline int HullToBit( Hull_t hull ) -{ - return ( 1 << hull ); -} - - - -//============================================================================= -// >> CAI_Hull -//============================================================================= -namespace NAI_Hull -{ - const Vector &Mins(int id); - const Vector &Maxs(int id); - - const Vector &SmallMins(int id); - const Vector &SmallMaxs(int id); - - float Length(int id); - float Width(int id); - float Height(int id); - - int Bits(int id); - - const char* Name(int id); - - Hull_t LookupId(const char *szName); -}; - -#endif // AI_HULL_H \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +//=============================================================================// + +#ifndef AI_HULL_H +#define AI_HULL_H +#pragma once + +class Vector; +//========================================================= +// Link Properties. These hulls must correspond to the hulls +// in AI_Hull.cpp! +//========================================================= +enum Hull_t +{ + HULL_HUMAN, // Combine, Stalker, Zombie... + HULL_SMALL_CENTERED, // Scanner + HULL_WIDE_HUMAN, // Vortigaunt + HULL_TINY, // Headcrab + HULL_WIDE_SHORT, // Bullsquid + HULL_MEDIUM, // Cremator + HULL_TINY_CENTERED, // Manhack + HULL_LARGE, // Antlion Guard + HULL_LARGE_CENTERED, // Mortar Synth + HULL_MEDIUM_TALL, // Ministrider +//-------------------------------------------- + NUM_HULLS, + HULL_NONE // No Hull (appears after num hulls as we don't want to count it) +}; + +enum Hull_Bits_t +{ + bits_HUMAN_HULL = 0x00000001, + bits_SMALL_CENTERED_HULL = 0x00000002, + bits_WIDE_HUMAN_HULL = 0x00000004, + bits_TINY_HULL = 0x00000008, + bits_WIDE_SHORT_HULL = 0x00000010, + bits_MEDIUM_HULL = 0x00000020, + bits_TINY_CENTERED_HULL = 0x00000040, + bits_LARGE_HULL = 0x00000080, + bits_LARGE_CENTERED_HULL = 0x00000100, + bits_MEDIUM_TALL_HULL = 0x00000200, + bits_HULL_BITS_MASK = 0x000002ff, +}; + +inline int HullToBit( Hull_t hull ) +{ + return ( 1 << hull ); +} + + + +//============================================================================= +// >> CAI_Hull +//============================================================================= +namespace NAI_Hull +{ + const Vector &Mins(int id); + const Vector &Maxs(int id); + + const Vector &SmallMins(int id); + const Vector &SmallMaxs(int id); + + float Length(int id); + float Width(int id); + float Height(int id); + + int Bits(int id); + + const char* Name(int id); + + Hull_t LookupId(const char *szName); +}; + +#endif // AI_HULL_H diff --git a/dlls/ai_looktarget.h b/dlls/ai_looktarget.h index eff2c3e7..d4093c90 100644 --- a/dlls/ai_looktarget.h +++ b/dlls/ai_looktarget.h @@ -1,47 +1,47 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -//=============================================================================// -#pragma once -#ifndef AI_LOOKTARGET_H -#define AI_LOOKTARGET_H - -#define SF_LOOKTARGET_ONLYONCE 0x00000001 - -//============================================================================= -//============================================================================= -class CAI_LookTarget : public CPointEntity -{ -public: - DECLARE_CLASS( CAI_LookTarget, CPointEntity ); - DECLARE_DATADESC(); - - CAI_LookTarget() { m_flTimeNextAvailable = -1; } - - // Debugging - int DrawDebugTextOverlays(void); - - // Accessors & Availability - bool IsEligible( CBaseEntity *pLooker ); - bool IsEnabled() { return !m_bDisabled; } - bool IsAvailable() { return (gpGlobals->curtime > m_flTimeNextAvailable); } - void Reserve( float flDuration ); - - // Searching - static CAI_LookTarget *GetFirstLookTarget(); - static CAI_LookTarget *GetNextLookTarget( CAI_LookTarget *pCurrentTarget ); - - int m_iContext; - int m_iPriority; - - void Enable() { m_bDisabled = false; } - void Disable() { m_bDisabled = true; } - -private: - bool m_bDisabled; - float m_flTimeNextAvailable; - float m_flMaxDist; -}; - -#endif//AI_LOOKTARGET_H \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// +#pragma once +#ifndef AI_LOOKTARGET_H +#define AI_LOOKTARGET_H + +#define SF_LOOKTARGET_ONLYONCE 0x00000001 + +//============================================================================= +//============================================================================= +class CAI_LookTarget : public CPointEntity +{ +public: + DECLARE_CLASS( CAI_LookTarget, CPointEntity ); + DECLARE_DATADESC(); + + CAI_LookTarget() { m_flTimeNextAvailable = -1; } + + // Debugging + int DrawDebugTextOverlays(void); + + // Accessors & Availability + bool IsEligible( CBaseEntity *pLooker ); + bool IsEnabled() { return !m_bDisabled; } + bool IsAvailable() { return (gpGlobals->curtime > m_flTimeNextAvailable); } + void Reserve( float flDuration ); + + // Searching + static CAI_LookTarget *GetFirstLookTarget(); + static CAI_LookTarget *GetNextLookTarget( CAI_LookTarget *pCurrentTarget ); + + int m_iContext; + int m_iPriority; + + void Enable() { m_bDisabled = false; } + void Disable() { m_bDisabled = true; } + +private: + bool m_bDisabled; + float m_flTimeNextAvailable; + float m_flMaxDist; +}; + +#endif//AI_LOOKTARGET_H diff --git a/dlls/ai_memory.cpp b/dlls/ai_memory.cpp index 2e4898d1..8f1f912c 100644 --- a/dlls/ai_memory.cpp +++ b/dlls/ai_memory.cpp @@ -1,642 +1,642 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: An NPC's memory of potential enemies -// -//=============================================================================// - -#include "cbase.h" -#include "isaverestore.h" -#include "ai_debug.h" -#include "ai_memory.h" -#include "ai_basenpc.h" - -// memdbgon must be the last include file in a .cpp file!!! -#include "tier0/memdbgon.h" - -#define EMEMORY_POOL_SIZE 64 -#define AI_FREE_KNOWLEDGE_DURATION 1.75 - -//----------------------------------------------------------------------------- -// AI_EnemyInfo_t -// -//----------------------------------------------------------------------------- - -DEFINE_FIXEDSIZE_ALLOCATOR( AI_EnemyInfo_t, EMEMORY_POOL_SIZE, CMemoryPool::GROW_FAST ); - -//----------------------------------------------------------------------------- - -AI_EnemyInfo_t::AI_EnemyInfo_t(void) -{ - hEnemy = NULL; - vLastKnownLocation = vec3_origin; - vLastSeenLocation = vec3_origin; - timeLastSeen = 0; - timeFirstSeen = 0; - timeLastReacquired = 0; - timeValidEnemy = 0; - timeLastReceivedDamageFrom = 0; - timeAtFirstHand = AI_INVALID_TIME; - bDangerMemory = 0; - bEludedMe = 0; - bUnforgettable = 0; - bMobbedMe = 0; -} - - -//----------------------------------------------------------------------------- -// CAI_EnemiesListSaveRestoreOps -// -// Purpose: Handles save and load for enemy memories -// -//----------------------------------------------------------------------------- - -class CAI_EnemiesListSaveRestoreOps : public CDefSaveRestoreOps -{ -public: - CAI_EnemiesListSaveRestoreOps() - { - } - - virtual void Save( const SaveRestoreFieldInfo_t &fieldInfo, ISave *pSave ) - { - CAI_Enemies::CMemMap *pMemMap = (CAI_Enemies::CMemMap *)fieldInfo.pField; - - int nMemories = pMemMap->Count(); - pSave->WriteInt( &nMemories ); - - for ( CAI_Enemies::CMemMap::IndexType_t i = pMemMap->FirstInorder(); i != pMemMap->InvalidIndex(); i = pMemMap->NextInorder( i ) ) - { - pSave->WriteAll( (*pMemMap)[i] ); - } - } - - virtual void Restore( const SaveRestoreFieldInfo_t &fieldInfo, IRestore *pRestore ) - { - CAI_Enemies::CMemMap *pMemMap = (CAI_Enemies::CMemMap *)fieldInfo.pField; - Assert( pMemMap->Count() == 0 ); - - int nMemories = pRestore->ReadInt(); - - while ( nMemories-- ) - { - AI_EnemyInfo_t *pAddMemory = new AI_EnemyInfo_t; - - pRestore->ReadAll( pAddMemory ); - - if ( pAddMemory->hEnemy != NULL ) - { - pMemMap->Insert( pAddMemory->hEnemy, pAddMemory ); - } - else - delete pAddMemory; - } - } - - virtual void MakeEmpty( const SaveRestoreFieldInfo_t &fieldInfo ) - { - CAI_Enemies::CMemMap *pMemMap = (CAI_Enemies::CMemMap *)fieldInfo.pField; - - for ( CAI_Enemies::CMemMap::IndexType_t i = pMemMap->FirstInorder(); i != pMemMap->InvalidIndex(); i = pMemMap->NextInorder( i ) ) - { - delete (*pMemMap)[i]; - } - - pMemMap->RemoveAll(); - } - - virtual bool IsEmpty( const SaveRestoreFieldInfo_t &fieldInfo ) - { - CAI_Enemies::CMemMap *pMemMap = (CAI_Enemies::CMemMap *)fieldInfo.pField; - return ( pMemMap->Count() == 0 ); - } - -} g_AI_MemoryListSaveRestoreOps; - -//----------------------------------------------------------------------------- -// CAI_Enemies -// -// Purpose: Stores a set of AI_EnemyInfo_t's -// -//----------------------------------------------------------------------------- - -BEGIN_SIMPLE_DATADESC( CAI_Enemies ) - - DEFINE_CUSTOM_FIELD( m_Map, &g_AI_MemoryListSaveRestoreOps ), - DEFINE_FIELD( m_flFreeKnowledgeDuration, FIELD_FLOAT ), - DEFINE_FIELD( m_flEnemyDiscardTime, FIELD_FLOAT ), - DEFINE_FIELD( m_vecDefaultLKP, FIELD_POSITION_VECTOR ), - DEFINE_FIELD( m_vecDefaultLSP, FIELD_POSITION_VECTOR ), - DEFINE_FIELD( m_serial, FIELD_INTEGER ), - -END_DATADESC() - -BEGIN_SIMPLE_DATADESC( AI_EnemyInfo_t ) - DEFINE_FIELD( vLastKnownLocation, FIELD_POSITION_VECTOR ), - DEFINE_FIELD( vLastSeenLocation, FIELD_POSITION_VECTOR ), - DEFINE_FIELD( hEnemy, FIELD_EHANDLE ), - DEFINE_FIELD( timeLastSeen, FIELD_TIME ), - DEFINE_FIELD( timeFirstSeen, FIELD_TIME ), - DEFINE_FIELD( timeLastReacquired, FIELD_TIME ), - DEFINE_FIELD( timeValidEnemy, FIELD_TIME ), - DEFINE_FIELD( timeLastReceivedDamageFrom, FIELD_TIME ), - DEFINE_FIELD( timeAtFirstHand, FIELD_TIME ), - DEFINE_FIELD( bDangerMemory, FIELD_BOOLEAN ), - DEFINE_FIELD( bEludedMe, FIELD_BOOLEAN ), - DEFINE_FIELD( bUnforgettable, FIELD_BOOLEAN ), - DEFINE_FIELD( bMobbedMe, FIELD_BOOLEAN ), - // NOT SAVED nextEMemory -END_DATADESC() - -//----------------------------------------------------------------------------- - -CAI_Enemies::CAI_Enemies(void) -{ - m_flFreeKnowledgeDuration = AI_FREE_KNOWLEDGE_DURATION; - m_flEnemyDiscardTime = AI_DEF_ENEMY_DISCARD_TIME; - m_vecDefaultLKP = vec3_invalid; - m_vecDefaultLSP = vec3_invalid; - m_serial = 0; - SetDefLessFunc( m_Map ); -} - - -//----------------------------------------------------------------------------- - -CAI_Enemies::~CAI_Enemies() -{ - for ( CMemMap::IndexType_t i = m_Map.FirstInorder(); i != m_Map.InvalidIndex(); i = m_Map.NextInorder( i ) ) - { - delete m_Map[i]; - } -} - -//----------------------------------------------------------------------------- -// Purpose: Purges any dead enemies from memory -//----------------------------------------------------------------------------- - -AI_EnemyInfo_t *CAI_Enemies::GetFirst( AIEnemiesIter_t *pIter ) -{ - CMemMap::IndexType_t i = m_Map.FirstInorder(); - *pIter = (AIEnemiesIter_t)(unsigned)i; - - if ( i == m_Map.InvalidIndex() ) - return NULL; - - if ( m_Map[i]->hEnemy == NULL ) - return GetNext( pIter ); - - return m_Map[i]; -} - -//----------------------------------------------------------------------------- - -AI_EnemyInfo_t *CAI_Enemies::GetNext( AIEnemiesIter_t *pIter ) -{ - CMemMap::IndexType_t i = (CMemMap::IndexType_t)((unsigned)(*pIter)); - - if ( i == m_Map.InvalidIndex() ) - return NULL; - - i = m_Map.NextInorder( i ); - *pIter = (AIEnemiesIter_t)(unsigned)i; - if ( i == m_Map.InvalidIndex() ) - return NULL; - - if ( m_Map[i]->hEnemy == NULL ) - return GetNext( pIter ); - - return m_Map[i]; -} - -//----------------------------------------------------------------------------- - -AI_EnemyInfo_t *CAI_Enemies::Find( CBaseEntity *pEntity, bool bTryDangerMemory ) -{ - if ( pEntity == AI_UNKNOWN_ENEMY ) - pEntity = NULL; - - CMemMap::IndexType_t i = m_Map.Find( pEntity ); - if ( i == m_Map.InvalidIndex() ) - { - if ( !bTryDangerMemory || ( i = m_Map.Find( NULL ) ) == m_Map.InvalidIndex() ) - return NULL; - Assert(m_Map[i]->bDangerMemory == true); - } - return m_Map[i]; -} - - -//----------------------------------------------------------------------------- - -AI_EnemyInfo_t *CAI_Enemies::GetDangerMemory() -{ - CMemMap::IndexType_t i = m_Map.Find( NULL ); - if ( i == m_Map.InvalidIndex() ) - return NULL; - Assert(m_Map[i]->bDangerMemory == true); - return m_Map[i]; -} - -//----------------------------------------------------------------------------- - -bool CAI_Enemies::ShouldDiscardMemory( AI_EnemyInfo_t *pMemory ) -{ - CBaseEntity *pEnemy = pMemory->hEnemy; - - if ( pEnemy ) - { - CAI_BaseNPC *pEnemyNPC = pEnemy->MyNPCPointer(); - if ( pEnemyNPC && pEnemyNPC->GetState() == NPC_STATE_DEAD ) - return true; - } - else - { - if ( !pMemory->bDangerMemory ) - return true; - } - - if ( !pMemory->bUnforgettable && - gpGlobals->curtime > pMemory->timeLastSeen + m_flEnemyDiscardTime ) - { - return true; - } - - return false; -} - - -//----------------------------------------------------------------------------- - -void CAI_Enemies::RefreshMemories(void) -{ - AI_PROFILE_SCOPE(CAI_Enemies_RefreshMemories); - - // ------------------- - // Check each record - // ------------------- - - CMemMap::IndexType_t i = m_Map.FirstInorder(); - while ( i != m_Map.InvalidIndex() ) - { - AI_EnemyInfo_t *pMemory = m_Map[i]; - - CMemMap::IndexType_t iNext = m_Map.NextInorder( i ); // save so can remove - if ( ShouldDiscardMemory( pMemory ) ) - { - delete pMemory; - m_Map.RemoveAt(i); - } - else if ( pMemory->hEnemy ) - { - if ( gpGlobals->curtime <= pMemory->timeLastSeen + m_flFreeKnowledgeDuration ) - { - // Free knowledge is ignored if the target has notarget on - if ( !(pMemory->hEnemy->GetFlags() & FL_NOTARGET) ) - { - pMemory->vLastKnownLocation = pMemory->hEnemy->GetAbsOrigin(); - } - } - - if ( gpGlobals->curtime <= pMemory->timeLastSeen ) - { - pMemory->vLastSeenLocation = pMemory->hEnemy->GetAbsOrigin(); - } - } - i = iNext; - } -} - -//----------------------------------------------------------------------------- -// Purpose: Updates information about our enemies -// Output : Returns true if new enemy, false if already know of enemy -//----------------------------------------------------------------------------- - -bool CAI_Enemies::UpdateMemory(CAI_Network* pAINet, CBaseEntity *pEnemy, const Vector &vPosition, float reactionDelay, bool firstHand ) -{ - if ( pEnemy == AI_UNKNOWN_ENEMY ) - pEnemy = NULL; - - const float DIST_TRIGGER_REACQUIRE_SQ = Square(20.0 * 12.0); - const float TIME_TRIGGER_REACQUIRE = 4.0; - const float MIN_DIST_TIME_TRIGGER_REACQUIRE_SQ = Square(4.0 * 12.0); - - AI_EnemyInfo_t *pMemory = Find( pEnemy ); - // ------------------------------------------- - // Otherwise just update my own - // ------------------------------------------- - // Update enemy information - if ( pMemory ) - { - Assert(pEnemy || pMemory->bDangerMemory == true); - - if ( firstHand ) - pMemory->timeLastSeen = gpGlobals->curtime; - pMemory->bEludedMe = false; - - float deltaDist = (pMemory->vLastKnownLocation - vPosition).LengthSqr(); - - if (deltaDist>DIST_TRIGGER_REACQUIRE_SQ || ( deltaDist>MIN_DIST_TIME_TRIGGER_REACQUIRE_SQ && ( gpGlobals->curtime - pMemory->timeLastSeen ) > TIME_TRIGGER_REACQUIRE ) ) - { - pMemory->timeLastReacquired = gpGlobals->curtime; - } - - // Only update if the enemy has moved - if (deltaDist>Square(12.0)) - { - pMemory->vLastKnownLocation = vPosition; - - } - - // Update the time at which we first saw him firsthand - if ( firstHand && pMemory->timeAtFirstHand == AI_INVALID_TIME ) - { - pMemory->timeAtFirstHand = gpGlobals->curtime; - } - - return false; - } - - // If not on my list of enemies add it - AI_EnemyInfo_t *pAddMemory = new AI_EnemyInfo_t; - pAddMemory->vLastKnownLocation = vPosition; - - if ( firstHand ) - { - pAddMemory->timeLastReacquired = pAddMemory->timeFirstSeen = pAddMemory->timeLastSeen = pAddMemory->timeAtFirstHand = gpGlobals->curtime; - } - else - { - // Block free knowledge - pAddMemory->timeLastReacquired = pAddMemory->timeFirstSeen = pAddMemory->timeLastSeen = ( gpGlobals->curtime - (m_flFreeKnowledgeDuration + 0.01) ); - pAddMemory->timeAtFirstHand = AI_INVALID_TIME; - } - - if ( reactionDelay > 0.0 ) - pAddMemory->timeValidEnemy = gpGlobals->curtime + reactionDelay; - - pAddMemory->bEludedMe = false; - - // I'm either remembering a postion of an enmey of just a danger position - pAddMemory->hEnemy = pEnemy; - pAddMemory->bDangerMemory = ( pEnemy == NULL ); - - // add to the list - m_Map.Insert( pEnemy, pAddMemory ); - m_serial++; - - return true; -} - -//------------------------------------------------------------------------------ -// Purpose : Returns true if this enemy is part of my memory -//------------------------------------------------------------------------------ -void CAI_Enemies::OnTookDamageFrom( CBaseEntity *pEnemy ) -{ - AI_EnemyInfo_t *pMemory = Find( pEnemy, true ); - if ( pMemory ) - pMemory->timeLastReceivedDamageFrom = gpGlobals->curtime; -} - -//------------------------------------------------------------------------------ -// Purpose : Returns true if this enemy is part of my memory -//------------------------------------------------------------------------------ -bool CAI_Enemies::HasMemory( CBaseEntity *pEnemy ) -{ - return ( Find( pEnemy ) != NULL ); -} - -//----------------------------------------------------------------------------- -// Purpose: Clear information about our enemy -//----------------------------------------------------------------------------- -void CAI_Enemies::ClearMemory(CBaseEntity *pEnemy) -{ - CMemMap::IndexType_t i = m_Map.Find( pEnemy ); - if ( i != m_Map.InvalidIndex() ) - { - delete m_Map[i]; - m_Map.RemoveAt( i ); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Notes that the given enemy has eluded me -//----------------------------------------------------------------------------- -void CAI_Enemies::MarkAsEluded( CBaseEntity *pEnemy ) -{ - AI_EnemyInfo_t *pMemory = Find( pEnemy ); - if ( pMemory ) - { - pMemory->bEludedMe = true; - } -} - - -//----------------------------------------------------------------------------- -// Purpose: Returns last known posiiton of given enemy -//----------------------------------------------------------------------------- -const Vector &CAI_Enemies::LastKnownPosition( CBaseEntity *pEnemy ) -{ - AI_EnemyInfo_t *pMemory = Find( pEnemy, true ); - if ( pMemory ) - { - m_vecDefaultLKP = pMemory->vLastKnownLocation; - } - else - { - DevWarning( 2,"Asking LastKnownPosition for enemy that's not in my memory!!\n"); - } - return m_vecDefaultLKP; -} - -//----------------------------------------------------------------------------- -// Purpose: Returns the last position the enemy was SEEN at. This will always be -// different than LastKnownPosition() when the enemy is out of sight, because -// the last KNOWN position will be updated for a number of seconds after the -// player disappears. -//----------------------------------------------------------------------------- -const Vector &CAI_Enemies::LastSeenPosition( CBaseEntity *pEnemy ) -{ - AI_EnemyInfo_t *pMemory = Find( pEnemy, true ); - if ( pMemory ) - { - m_vecDefaultLSP = pMemory->vLastSeenLocation; - } - else - { - DevWarning( 2,"Asking LastSeenPosition for enemy that's not in my memory!!\n"); - } - return m_vecDefaultLSP; -} - -float CAI_Enemies::TimeLastReacquired( CBaseEntity *pEnemy ) -{ - // I've never seen something that doesn't exist - if (!pEnemy) - return 0; - - AI_EnemyInfo_t *pMemory = Find( pEnemy, true ); - if ( pMemory ) - return pMemory->timeLastReacquired; - - if ( pEnemy != AI_UNKNOWN_ENEMY ) - DevWarning( 2,"Asking TimeLastReacquired for enemy that's not in my memory!!\n"); - return AI_INVALID_TIME; -} - -//----------------------------------------------------------------------------- -// Purpose: Sets position to the last known position of an enemy. If enemy -// was not found returns last memory of danger position if it exists -// Output : Returns false is no position is known -//----------------------------------------------------------------------------- -float CAI_Enemies::LastTimeSeen( CBaseEntity *pEnemy, bool bCheckDangerMemory /*= true*/ ) -{ - // I've never seen something that doesn't exist - if (!pEnemy) - return 0; - - AI_EnemyInfo_t *pMemory = Find( pEnemy, bCheckDangerMemory ); - if ( pMemory ) - return pMemory->timeLastSeen; - - if ( pEnemy != AI_UNKNOWN_ENEMY ) - DevWarning( 2,"Asking LastTimeSeen for enemy that's not in my memory!!\n"); - return AI_INVALID_TIME; -} - -//----------------------------------------------------------------------------- -// Purpose: Get the time at which the enemy was first seen. -// Output : Returns false is no position is known -//----------------------------------------------------------------------------- -float CAI_Enemies::FirstTimeSeen( CBaseEntity *pEnemy) -{ - // I've never seen something that doesn't exist - if (!pEnemy) - return 0; - - AI_EnemyInfo_t *pMemory = Find( pEnemy, true ); - if ( pMemory ) - return pMemory->timeFirstSeen; - - if ( pEnemy != AI_UNKNOWN_ENEMY ) - DevWarning( 2,"Asking FirstTimeSeen for enemy that's not in my memory!!\n"); - return AI_INVALID_TIME; -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *pEnemy - -// Output : Returns true on success, false on failure. -//----------------------------------------------------------------------------- -bool CAI_Enemies::HasFreeKnowledgeOf( CBaseEntity *pEnemy ) -{ - // I've never seen something that doesn't exist - if (!pEnemy) - return 0; - - AI_EnemyInfo_t *pMemory = Find( pEnemy, true ); - if ( pMemory ) - { - float flFreeKnowledgeTime = pMemory->timeLastSeen + m_flFreeKnowledgeDuration; - return ( gpGlobals->curtime < flFreeKnowledgeTime ); - } - - if ( pEnemy != AI_UNKNOWN_ENEMY ) - DevWarning( 2,"Asking HasFreeKnowledgeOf for enemy that's not in my memory!!\n"); - return AI_INVALID_TIME; -} - -//----------------------------------------------------------------------------- -float CAI_Enemies::LastTimeTookDamageFrom( CBaseEntity *pEnemy) -{ - // I've never seen something that doesn't exist - if (!pEnemy) - return 0; - - AI_EnemyInfo_t *pMemory = Find( pEnemy, true ); - if ( pMemory ) - return pMemory->timeLastReceivedDamageFrom; - - if ( pEnemy != AI_UNKNOWN_ENEMY ) - DevWarning( 2,"Asking LastTimeTookDamageFrom for enemy that's not in my memory!!\n"); - return AI_INVALID_TIME; -} - -//----------------------------------------------------------------------------- -// Purpose: Returns the time at which the enemy was first seen firsthand -// Input : *pEnemy - -// Output : float -//----------------------------------------------------------------------------- -float CAI_Enemies::TimeAtFirstHand( CBaseEntity *pEnemy ) -{ - // I've never seen something that doesn't exist - if (!pEnemy) - return 0; - - AI_EnemyInfo_t *pMemory = Find( pEnemy, true ); - if ( pMemory ) - return pMemory->timeAtFirstHand; - - if ( pEnemy != AI_UNKNOWN_ENEMY ) - DevWarning( 2,"Asking TimeAtFirstHand for enemy that's not in my memory!!\n"); - return AI_INVALID_TIME; -} - -//----------------------------------------------------------------------------- -// Purpose: Sets position to the last known position of an enemy. If enemy -// was not found returns last memory of danger position if it exists -// Output : Returns false is no position is known -//----------------------------------------------------------------------------- -bool CAI_Enemies::HasEludedMe( CBaseEntity *pEnemy ) -{ - AI_EnemyInfo_t *pMemory = Find( pEnemy ); - if ( pMemory ) - return pMemory->bEludedMe; - return false; -} - -void CAI_Enemies::SetTimeValidEnemy( CBaseEntity *pEnemy, float flTime ) -{ - AI_EnemyInfo_t *pMemory = Find( pEnemy ); - if ( pMemory ) - pMemory->timeValidEnemy = flTime; -} - -//----------------------------------------------------------------------------- -void CAI_Enemies::SetUnforgettable( CBaseEntity *pEnemy, bool bUnforgettable ) -{ - AI_EnemyInfo_t *pMemory = Find( pEnemy ); - if ( pMemory ) - pMemory->bUnforgettable = bUnforgettable; -} - -//----------------------------------------------------------------------------- -void CAI_Enemies::SetMobbedMe( CBaseEntity *pEnemy, bool bMobbedMe ) -{ - AI_EnemyInfo_t *pMemory = Find( pEnemy ); - if ( pMemory ) - pMemory->bMobbedMe = bMobbedMe; -} - -//----------------------------------------------------------------------------- - -void CAI_Enemies::SetFreeKnowledgeDuration( float flDuration ) -{ - m_flFreeKnowledgeDuration = flDuration; - - // If your free knowledge time is greater than your discard time, - // you'll forget about secondhand enemies passed to you by squadmates - // as soon as you're given them. - Assert( m_flFreeKnowledgeDuration < m_flEnemyDiscardTime ); -} - -//----------------------------------------------------------------------------- - -void CAI_Enemies::SetEnemyDiscardTime( float flTime ) -{ - m_flEnemyDiscardTime = flTime; - - // If your free knowledge time is greater than your discard time, - // you'll forget about secondhand enemies passed to you by squadmates - // as soon as you're given them. - Assert( m_flFreeKnowledgeDuration < m_flEnemyDiscardTime ); -} \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: An NPC's memory of potential enemies +// +//=============================================================================// + +#include "cbase.h" +#include "isaverestore.h" +#include "ai_debug.h" +#include "ai_memory.h" +#include "ai_basenpc.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +#define EMEMORY_POOL_SIZE 64 +#define AI_FREE_KNOWLEDGE_DURATION 1.75 + +//----------------------------------------------------------------------------- +// AI_EnemyInfo_t +// +//----------------------------------------------------------------------------- + +DEFINE_FIXEDSIZE_ALLOCATOR( AI_EnemyInfo_t, EMEMORY_POOL_SIZE, CMemoryPool::GROW_FAST ); + +//----------------------------------------------------------------------------- + +AI_EnemyInfo_t::AI_EnemyInfo_t(void) +{ + hEnemy = NULL; + vLastKnownLocation = vec3_origin; + vLastSeenLocation = vec3_origin; + timeLastSeen = 0; + timeFirstSeen = 0; + timeLastReacquired = 0; + timeValidEnemy = 0; + timeLastReceivedDamageFrom = 0; + timeAtFirstHand = AI_INVALID_TIME; + bDangerMemory = 0; + bEludedMe = 0; + bUnforgettable = 0; + bMobbedMe = 0; +} + + +//----------------------------------------------------------------------------- +// CAI_EnemiesListSaveRestoreOps +// +// Purpose: Handles save and load for enemy memories +// +//----------------------------------------------------------------------------- + +class CAI_EnemiesListSaveRestoreOps : public CDefSaveRestoreOps +{ +public: + CAI_EnemiesListSaveRestoreOps() + { + } + + virtual void Save( const SaveRestoreFieldInfo_t &fieldInfo, ISave *pSave ) + { + CAI_Enemies::CMemMap *pMemMap = (CAI_Enemies::CMemMap *)fieldInfo.pField; + + int nMemories = pMemMap->Count(); + pSave->WriteInt( &nMemories ); + + for ( CAI_Enemies::CMemMap::IndexType_t i = pMemMap->FirstInorder(); i != pMemMap->InvalidIndex(); i = pMemMap->NextInorder( i ) ) + { + pSave->WriteAll( (*pMemMap)[i] ); + } + } + + virtual void Restore( const SaveRestoreFieldInfo_t &fieldInfo, IRestore *pRestore ) + { + CAI_Enemies::CMemMap *pMemMap = (CAI_Enemies::CMemMap *)fieldInfo.pField; + Assert( pMemMap->Count() == 0 ); + + int nMemories = pRestore->ReadInt(); + + while ( nMemories-- ) + { + AI_EnemyInfo_t *pAddMemory = new AI_EnemyInfo_t; + + pRestore->ReadAll( pAddMemory ); + + if ( pAddMemory->hEnemy != NULL ) + { + pMemMap->Insert( pAddMemory->hEnemy, pAddMemory ); + } + else + delete pAddMemory; + } + } + + virtual void MakeEmpty( const SaveRestoreFieldInfo_t &fieldInfo ) + { + CAI_Enemies::CMemMap *pMemMap = (CAI_Enemies::CMemMap *)fieldInfo.pField; + + for ( CAI_Enemies::CMemMap::IndexType_t i = pMemMap->FirstInorder(); i != pMemMap->InvalidIndex(); i = pMemMap->NextInorder( i ) ) + { + delete (*pMemMap)[i]; + } + + pMemMap->RemoveAll(); + } + + virtual bool IsEmpty( const SaveRestoreFieldInfo_t &fieldInfo ) + { + CAI_Enemies::CMemMap *pMemMap = (CAI_Enemies::CMemMap *)fieldInfo.pField; + return ( pMemMap->Count() == 0 ); + } + +} g_AI_MemoryListSaveRestoreOps; + +//----------------------------------------------------------------------------- +// CAI_Enemies +// +// Purpose: Stores a set of AI_EnemyInfo_t's +// +//----------------------------------------------------------------------------- + +BEGIN_SIMPLE_DATADESC( CAI_Enemies ) + + DEFINE_CUSTOM_FIELD( m_Map, &g_AI_MemoryListSaveRestoreOps ), + DEFINE_FIELD( m_flFreeKnowledgeDuration, FIELD_FLOAT ), + DEFINE_FIELD( m_flEnemyDiscardTime, FIELD_FLOAT ), + DEFINE_FIELD( m_vecDefaultLKP, FIELD_POSITION_VECTOR ), + DEFINE_FIELD( m_vecDefaultLSP, FIELD_POSITION_VECTOR ), + DEFINE_FIELD( m_serial, FIELD_INTEGER ), + +END_DATADESC() + +BEGIN_SIMPLE_DATADESC( AI_EnemyInfo_t ) + DEFINE_FIELD( vLastKnownLocation, FIELD_POSITION_VECTOR ), + DEFINE_FIELD( vLastSeenLocation, FIELD_POSITION_VECTOR ), + DEFINE_FIELD( hEnemy, FIELD_EHANDLE ), + DEFINE_FIELD( timeLastSeen, FIELD_TIME ), + DEFINE_FIELD( timeFirstSeen, FIELD_TIME ), + DEFINE_FIELD( timeLastReacquired, FIELD_TIME ), + DEFINE_FIELD( timeValidEnemy, FIELD_TIME ), + DEFINE_FIELD( timeLastReceivedDamageFrom, FIELD_TIME ), + DEFINE_FIELD( timeAtFirstHand, FIELD_TIME ), + DEFINE_FIELD( bDangerMemory, FIELD_BOOLEAN ), + DEFINE_FIELD( bEludedMe, FIELD_BOOLEAN ), + DEFINE_FIELD( bUnforgettable, FIELD_BOOLEAN ), + DEFINE_FIELD( bMobbedMe, FIELD_BOOLEAN ), + // NOT SAVED nextEMemory +END_DATADESC() + +//----------------------------------------------------------------------------- + +CAI_Enemies::CAI_Enemies(void) +{ + m_flFreeKnowledgeDuration = AI_FREE_KNOWLEDGE_DURATION; + m_flEnemyDiscardTime = AI_DEF_ENEMY_DISCARD_TIME; + m_vecDefaultLKP = vec3_invalid; + m_vecDefaultLSP = vec3_invalid; + m_serial = 0; + SetDefLessFunc( m_Map ); +} + + +//----------------------------------------------------------------------------- + +CAI_Enemies::~CAI_Enemies() +{ + for ( CMemMap::IndexType_t i = m_Map.FirstInorder(); i != m_Map.InvalidIndex(); i = m_Map.NextInorder( i ) ) + { + delete m_Map[i]; + } +} + +//----------------------------------------------------------------------------- +// Purpose: Purges any dead enemies from memory +//----------------------------------------------------------------------------- + +AI_EnemyInfo_t *CAI_Enemies::GetFirst( AIEnemiesIter_t *pIter ) +{ + CMemMap::IndexType_t i = m_Map.FirstInorder(); + *pIter = (AIEnemiesIter_t)(unsigned)i; + + if ( i == m_Map.InvalidIndex() ) + return NULL; + + if ( m_Map[i]->hEnemy == NULL ) + return GetNext( pIter ); + + return m_Map[i]; +} + +//----------------------------------------------------------------------------- + +AI_EnemyInfo_t *CAI_Enemies::GetNext( AIEnemiesIter_t *pIter ) +{ + CMemMap::IndexType_t i = (CMemMap::IndexType_t)((unsigned)(*pIter)); + + if ( i == m_Map.InvalidIndex() ) + return NULL; + + i = m_Map.NextInorder( i ); + *pIter = (AIEnemiesIter_t)(unsigned)i; + if ( i == m_Map.InvalidIndex() ) + return NULL; + + if ( m_Map[i]->hEnemy == NULL ) + return GetNext( pIter ); + + return m_Map[i]; +} + +//----------------------------------------------------------------------------- + +AI_EnemyInfo_t *CAI_Enemies::Find( CBaseEntity *pEntity, bool bTryDangerMemory ) +{ + if ( pEntity == AI_UNKNOWN_ENEMY ) + pEntity = NULL; + + CMemMap::IndexType_t i = m_Map.Find( pEntity ); + if ( i == m_Map.InvalidIndex() ) + { + if ( !bTryDangerMemory || ( i = m_Map.Find( NULL ) ) == m_Map.InvalidIndex() ) + return NULL; + Assert(m_Map[i]->bDangerMemory == true); + } + return m_Map[i]; +} + + +//----------------------------------------------------------------------------- + +AI_EnemyInfo_t *CAI_Enemies::GetDangerMemory() +{ + CMemMap::IndexType_t i = m_Map.Find( NULL ); + if ( i == m_Map.InvalidIndex() ) + return NULL; + Assert(m_Map[i]->bDangerMemory == true); + return m_Map[i]; +} + +//----------------------------------------------------------------------------- + +bool CAI_Enemies::ShouldDiscardMemory( AI_EnemyInfo_t *pMemory ) +{ + CBaseEntity *pEnemy = pMemory->hEnemy; + + if ( pEnemy ) + { + CAI_BaseNPC *pEnemyNPC = pEnemy->MyNPCPointer(); + if ( pEnemyNPC && pEnemyNPC->GetState() == NPC_STATE_DEAD ) + return true; + } + else + { + if ( !pMemory->bDangerMemory ) + return true; + } + + if ( !pMemory->bUnforgettable && + gpGlobals->curtime > pMemory->timeLastSeen + m_flEnemyDiscardTime ) + { + return true; + } + + return false; +} + + +//----------------------------------------------------------------------------- + +void CAI_Enemies::RefreshMemories(void) +{ + AI_PROFILE_SCOPE(CAI_Enemies_RefreshMemories); + + // ------------------- + // Check each record + // ------------------- + + CMemMap::IndexType_t i = m_Map.FirstInorder(); + while ( i != m_Map.InvalidIndex() ) + { + AI_EnemyInfo_t *pMemory = m_Map[i]; + + CMemMap::IndexType_t iNext = m_Map.NextInorder( i ); // save so can remove + if ( ShouldDiscardMemory( pMemory ) ) + { + delete pMemory; + m_Map.RemoveAt(i); + } + else if ( pMemory->hEnemy ) + { + if ( gpGlobals->curtime <= pMemory->timeLastSeen + m_flFreeKnowledgeDuration ) + { + // Free knowledge is ignored if the target has notarget on + if ( !(pMemory->hEnemy->GetFlags() & FL_NOTARGET) ) + { + pMemory->vLastKnownLocation = pMemory->hEnemy->GetAbsOrigin(); + } + } + + if ( gpGlobals->curtime <= pMemory->timeLastSeen ) + { + pMemory->vLastSeenLocation = pMemory->hEnemy->GetAbsOrigin(); + } + } + i = iNext; + } +} + +//----------------------------------------------------------------------------- +// Purpose: Updates information about our enemies +// Output : Returns true if new enemy, false if already know of enemy +//----------------------------------------------------------------------------- + +bool CAI_Enemies::UpdateMemory(CAI_Network* pAINet, CBaseEntity *pEnemy, const Vector &vPosition, float reactionDelay, bool firstHand ) +{ + if ( pEnemy == AI_UNKNOWN_ENEMY ) + pEnemy = NULL; + + const float DIST_TRIGGER_REACQUIRE_SQ = Square(20.0 * 12.0); + const float TIME_TRIGGER_REACQUIRE = 4.0; + const float MIN_DIST_TIME_TRIGGER_REACQUIRE_SQ = Square(4.0 * 12.0); + + AI_EnemyInfo_t *pMemory = Find( pEnemy ); + // ------------------------------------------- + // Otherwise just update my own + // ------------------------------------------- + // Update enemy information + if ( pMemory ) + { + Assert(pEnemy || pMemory->bDangerMemory == true); + + if ( firstHand ) + pMemory->timeLastSeen = gpGlobals->curtime; + pMemory->bEludedMe = false; + + float deltaDist = (pMemory->vLastKnownLocation - vPosition).LengthSqr(); + + if (deltaDist>DIST_TRIGGER_REACQUIRE_SQ || ( deltaDist>MIN_DIST_TIME_TRIGGER_REACQUIRE_SQ && ( gpGlobals->curtime - pMemory->timeLastSeen ) > TIME_TRIGGER_REACQUIRE ) ) + { + pMemory->timeLastReacquired = gpGlobals->curtime; + } + + // Only update if the enemy has moved + if (deltaDist>Square(12.0)) + { + pMemory->vLastKnownLocation = vPosition; + + } + + // Update the time at which we first saw him firsthand + if ( firstHand && pMemory->timeAtFirstHand == AI_INVALID_TIME ) + { + pMemory->timeAtFirstHand = gpGlobals->curtime; + } + + return false; + } + + // If not on my list of enemies add it + AI_EnemyInfo_t *pAddMemory = new AI_EnemyInfo_t; + pAddMemory->vLastKnownLocation = vPosition; + + if ( firstHand ) + { + pAddMemory->timeLastReacquired = pAddMemory->timeFirstSeen = pAddMemory->timeLastSeen = pAddMemory->timeAtFirstHand = gpGlobals->curtime; + } + else + { + // Block free knowledge + pAddMemory->timeLastReacquired = pAddMemory->timeFirstSeen = pAddMemory->timeLastSeen = ( gpGlobals->curtime - (m_flFreeKnowledgeDuration + 0.01) ); + pAddMemory->timeAtFirstHand = AI_INVALID_TIME; + } + + if ( reactionDelay > 0.0 ) + pAddMemory->timeValidEnemy = gpGlobals->curtime + reactionDelay; + + pAddMemory->bEludedMe = false; + + // I'm either remembering a postion of an enmey of just a danger position + pAddMemory->hEnemy = pEnemy; + pAddMemory->bDangerMemory = ( pEnemy == NULL ); + + // add to the list + m_Map.Insert( pEnemy, pAddMemory ); + m_serial++; + + return true; +} + +//------------------------------------------------------------------------------ +// Purpose : Returns true if this enemy is part of my memory +//------------------------------------------------------------------------------ +void CAI_Enemies::OnTookDamageFrom( CBaseEntity *pEnemy ) +{ + AI_EnemyInfo_t *pMemory = Find( pEnemy, true ); + if ( pMemory ) + pMemory->timeLastReceivedDamageFrom = gpGlobals->curtime; +} + +//------------------------------------------------------------------------------ +// Purpose : Returns true if this enemy is part of my memory +//------------------------------------------------------------------------------ +bool CAI_Enemies::HasMemory( CBaseEntity *pEnemy ) +{ + return ( Find( pEnemy ) != NULL ); +} + +//----------------------------------------------------------------------------- +// Purpose: Clear information about our enemy +//----------------------------------------------------------------------------- +void CAI_Enemies::ClearMemory(CBaseEntity *pEnemy) +{ + CMemMap::IndexType_t i = m_Map.Find( pEnemy ); + if ( i != m_Map.InvalidIndex() ) + { + delete m_Map[i]; + m_Map.RemoveAt( i ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Notes that the given enemy has eluded me +//----------------------------------------------------------------------------- +void CAI_Enemies::MarkAsEluded( CBaseEntity *pEnemy ) +{ + AI_EnemyInfo_t *pMemory = Find( pEnemy ); + if ( pMemory ) + { + pMemory->bEludedMe = true; + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Returns last known posiiton of given enemy +//----------------------------------------------------------------------------- +const Vector &CAI_Enemies::LastKnownPosition( CBaseEntity *pEnemy ) +{ + AI_EnemyInfo_t *pMemory = Find( pEnemy, true ); + if ( pMemory ) + { + m_vecDefaultLKP = pMemory->vLastKnownLocation; + } + else + { + DevWarning( 2,"Asking LastKnownPosition for enemy that's not in my memory!!\n"); + } + return m_vecDefaultLKP; +} + +//----------------------------------------------------------------------------- +// Purpose: Returns the last position the enemy was SEEN at. This will always be +// different than LastKnownPosition() when the enemy is out of sight, because +// the last KNOWN position will be updated for a number of seconds after the +// player disappears. +//----------------------------------------------------------------------------- +const Vector &CAI_Enemies::LastSeenPosition( CBaseEntity *pEnemy ) +{ + AI_EnemyInfo_t *pMemory = Find( pEnemy, true ); + if ( pMemory ) + { + m_vecDefaultLSP = pMemory->vLastSeenLocation; + } + else + { + DevWarning( 2,"Asking LastSeenPosition for enemy that's not in my memory!!\n"); + } + return m_vecDefaultLSP; +} + +float CAI_Enemies::TimeLastReacquired( CBaseEntity *pEnemy ) +{ + // I've never seen something that doesn't exist + if (!pEnemy) + return 0; + + AI_EnemyInfo_t *pMemory = Find( pEnemy, true ); + if ( pMemory ) + return pMemory->timeLastReacquired; + + if ( pEnemy != AI_UNKNOWN_ENEMY ) + DevWarning( 2,"Asking TimeLastReacquired for enemy that's not in my memory!!\n"); + return AI_INVALID_TIME; +} + +//----------------------------------------------------------------------------- +// Purpose: Sets position to the last known position of an enemy. If enemy +// was not found returns last memory of danger position if it exists +// Output : Returns false is no position is known +//----------------------------------------------------------------------------- +float CAI_Enemies::LastTimeSeen( CBaseEntity *pEnemy, bool bCheckDangerMemory /*= true*/ ) +{ + // I've never seen something that doesn't exist + if (!pEnemy) + return 0; + + AI_EnemyInfo_t *pMemory = Find( pEnemy, bCheckDangerMemory ); + if ( pMemory ) + return pMemory->timeLastSeen; + + if ( pEnemy != AI_UNKNOWN_ENEMY ) + DevWarning( 2,"Asking LastTimeSeen for enemy that's not in my memory!!\n"); + return AI_INVALID_TIME; +} + +//----------------------------------------------------------------------------- +// Purpose: Get the time at which the enemy was first seen. +// Output : Returns false is no position is known +//----------------------------------------------------------------------------- +float CAI_Enemies::FirstTimeSeen( CBaseEntity *pEnemy) +{ + // I've never seen something that doesn't exist + if (!pEnemy) + return 0; + + AI_EnemyInfo_t *pMemory = Find( pEnemy, true ); + if ( pMemory ) + return pMemory->timeFirstSeen; + + if ( pEnemy != AI_UNKNOWN_ENEMY ) + DevWarning( 2,"Asking FirstTimeSeen for enemy that's not in my memory!!\n"); + return AI_INVALID_TIME; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *pEnemy - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CAI_Enemies::HasFreeKnowledgeOf( CBaseEntity *pEnemy ) +{ + // I've never seen something that doesn't exist + if (!pEnemy) + return 0; + + AI_EnemyInfo_t *pMemory = Find( pEnemy, true ); + if ( pMemory ) + { + float flFreeKnowledgeTime = pMemory->timeLastSeen + m_flFreeKnowledgeDuration; + return ( gpGlobals->curtime < flFreeKnowledgeTime ); + } + + if ( pEnemy != AI_UNKNOWN_ENEMY ) + DevWarning( 2,"Asking HasFreeKnowledgeOf for enemy that's not in my memory!!\n"); + return AI_INVALID_TIME; +} + +//----------------------------------------------------------------------------- +float CAI_Enemies::LastTimeTookDamageFrom( CBaseEntity *pEnemy) +{ + // I've never seen something that doesn't exist + if (!pEnemy) + return 0; + + AI_EnemyInfo_t *pMemory = Find( pEnemy, true ); + if ( pMemory ) + return pMemory->timeLastReceivedDamageFrom; + + if ( pEnemy != AI_UNKNOWN_ENEMY ) + DevWarning( 2,"Asking LastTimeTookDamageFrom for enemy that's not in my memory!!\n"); + return AI_INVALID_TIME; +} + +//----------------------------------------------------------------------------- +// Purpose: Returns the time at which the enemy was first seen firsthand +// Input : *pEnemy - +// Output : float +//----------------------------------------------------------------------------- +float CAI_Enemies::TimeAtFirstHand( CBaseEntity *pEnemy ) +{ + // I've never seen something that doesn't exist + if (!pEnemy) + return 0; + + AI_EnemyInfo_t *pMemory = Find( pEnemy, true ); + if ( pMemory ) + return pMemory->timeAtFirstHand; + + if ( pEnemy != AI_UNKNOWN_ENEMY ) + DevWarning( 2,"Asking TimeAtFirstHand for enemy that's not in my memory!!\n"); + return AI_INVALID_TIME; +} + +//----------------------------------------------------------------------------- +// Purpose: Sets position to the last known position of an enemy. If enemy +// was not found returns last memory of danger position if it exists +// Output : Returns false is no position is known +//----------------------------------------------------------------------------- +bool CAI_Enemies::HasEludedMe( CBaseEntity *pEnemy ) +{ + AI_EnemyInfo_t *pMemory = Find( pEnemy ); + if ( pMemory ) + return pMemory->bEludedMe; + return false; +} + +void CAI_Enemies::SetTimeValidEnemy( CBaseEntity *pEnemy, float flTime ) +{ + AI_EnemyInfo_t *pMemory = Find( pEnemy ); + if ( pMemory ) + pMemory->timeValidEnemy = flTime; +} + +//----------------------------------------------------------------------------- +void CAI_Enemies::SetUnforgettable( CBaseEntity *pEnemy, bool bUnforgettable ) +{ + AI_EnemyInfo_t *pMemory = Find( pEnemy ); + if ( pMemory ) + pMemory->bUnforgettable = bUnforgettable; +} + +//----------------------------------------------------------------------------- +void CAI_Enemies::SetMobbedMe( CBaseEntity *pEnemy, bool bMobbedMe ) +{ + AI_EnemyInfo_t *pMemory = Find( pEnemy ); + if ( pMemory ) + pMemory->bMobbedMe = bMobbedMe; +} + +//----------------------------------------------------------------------------- + +void CAI_Enemies::SetFreeKnowledgeDuration( float flDuration ) +{ + m_flFreeKnowledgeDuration = flDuration; + + // If your free knowledge time is greater than your discard time, + // you'll forget about secondhand enemies passed to you by squadmates + // as soon as you're given them. + Assert( m_flFreeKnowledgeDuration < m_flEnemyDiscardTime ); +} + +//----------------------------------------------------------------------------- + +void CAI_Enemies::SetEnemyDiscardTime( float flTime ) +{ + m_flEnemyDiscardTime = flTime; + + // If your free knowledge time is greater than your discard time, + // you'll forget about secondhand enemies passed to you by squadmates + // as soon as you're given them. + Assert( m_flFreeKnowledgeDuration < m_flEnemyDiscardTime ); +} diff --git a/dlls/ai_memory.h b/dlls/ai_memory.h index be67523e..7c3dc3c2 100644 --- a/dlls/ai_memory.h +++ b/dlls/ai_memory.h @@ -118,3 +118,4 @@ private: //----------------------------------------------------------------------------- #endif // AI_MEMORY_H + diff --git a/dlls/ai_motor.cpp b/dlls/ai_motor.cpp index dc359925..e769116c 100644 --- a/dlls/ai_motor.cpp +++ b/dlls/ai_motor.cpp @@ -34,14 +34,19 @@ void DebugNoteMovementFailure() } // a place to put breakpoints +#ifdef _MSC_VER #pragma warning(push) #pragma warning(disable:4189) +#endif + AIMoveResult_t DbgResult( AIMoveResult_t result ) { +#ifdef _MSC_VER if ( result < AIMR_OK ) { int breakHere = 1; } +#endif switch ( result ) { @@ -750,7 +755,7 @@ void CAI_Motor::UpdateYaw( int yawSpeed ) float ideal, current, newYaw; if ( yawSpeed == -1 ) - yawSpeed = GetYawSpeed(); + yawSpeed = static_cast(GetYawSpeed()); // NOTE: GetIdealYaw() will never exactly be reached because UTIL_AngleMod // also truncates the angle to 16 bits of resolution. So lets truncate it here. @@ -908,7 +913,7 @@ AIMoveResult_t CAI_Motor::MoveNormalExecute( const AILocalMoveGoal_t &move ) AIMR_BLOCKED_WORLD, // AIM_PARTIAL_HIT_WORLD AIMR_BLOCKED_WORLD, // AIM_PARTIAL_HIT_TARGET }; - Assert( ARRAYSIZE( moveResults ) == AIM_NUM_RESULTS && fMotorResult >= 0 && fMotorResult <= ARRAYSIZE( moveResults ) ); + Assert( (AIMotorMoveResult_t)ARRAYSIZE( moveResults ) == AIM_NUM_RESULTS && fMotorResult >= 0 && fMotorResult <= (AIMotorMoveResult_t)ARRAYSIZE( moveResults ) ); AIMoveResult_t result = moveResults[fMotorResult]; @@ -1015,4 +1020,3 @@ void CAI_Motor::SetMoveType( MoveType_t val, MoveCollide_t moveCollide ) } //============================================================================= - diff --git a/dlls/ai_motor.h b/dlls/ai_motor.h index 7adfbad6..7e088abc 100644 --- a/dlls/ai_motor.h +++ b/dlls/ai_motor.h @@ -220,3 +220,4 @@ public: //============================================================================= #endif // AI_MOTOR_H + diff --git a/dlls/ai_moveprobe.h b/dlls/ai_moveprobe.h index 829abba3..0d66da36 100644 --- a/dlls/ai_moveprobe.h +++ b/dlls/ai_moveprobe.h @@ -154,3 +154,4 @@ inline bool CAI_MoveProbe::TestGroundMove( const Vector &vecActualStart, const V } #endif // AI_MOVEPROBE_H + diff --git a/dlls/ai_moveshoot.cpp b/dlls/ai_moveshoot.cpp index 001cb639..47064c34 100644 --- a/dlls/ai_moveshoot.cpp +++ b/dlls/ai_moveshoot.cpp @@ -138,6 +138,8 @@ void CAI_MoveAndShootOverlay::UpdateMoveShootActivity( bool bMoveAimAtEnemy ) case ACT_RUN: newActivity = ACT_RUN_AIM; break; + default: + break; } } else @@ -150,6 +152,8 @@ void CAI_MoveAndShootOverlay::UpdateMoveShootActivity( bool bMoveAimAtEnemy ) case ACT_RUN_AIM: newActivity = ACT_RUN; break; + default: + break; } } diff --git a/dlls/ai_movesolver.cpp b/dlls/ai_movesolver.cpp index f231f097..7b641075 100644 --- a/dlls/ai_movesolver.cpp +++ b/dlls/ai_movesolver.cpp @@ -114,7 +114,7 @@ bool CAI_MoveSolver::Solve( const AI_MoveSuggestion_t *pSuggestions, int nSugges AI_MoveSuggestion_t *pHighSuggestion; }; - Solution_t solutions[NUM_SOLUTIONS] = { 0 }; + Solution_t solutions[NUM_SOLUTIONS] = { {0.0f, 0.0f, NULL} }; //--------------------------------- @@ -133,15 +133,15 @@ bool CAI_MoveSolver::Solve( const AI_MoveSuggestion_t *pSuggestions, int nSugges // Convert arc values to solution indices relative to right post. Right is angle down, left is angle up. float halfSpan = current.arc.span * 0.5; - int center = round( ( halfSpan * NUM_SOLUTIONS ) / 360 ); - int left = ( current.arc.span * NUM_SOLUTIONS ) / 360; + int center = static_cast(round( ( halfSpan * NUM_SOLUTIONS ) / 360 )); + int left = static_cast(( current.arc.span * NUM_SOLUTIONS ) / 360); float angRight = current.arc.center - halfSpan; if (angRight < 0.0) angRight += 360; - int base = ( angRight * NUM_SOLUTIONS ) / 360; + int base = static_cast(( angRight * NUM_SOLUTIONS ) / 360); // Sweep from left to right, summing the bias. For positive suggestions, // the bias is further weighted to favor the center of the arc. diff --git a/dlls/ai_namespaces.cpp b/dlls/ai_namespaces.cpp index 7e8b6a9d..b58835ed 100644 --- a/dlls/ai_namespaces.cpp +++ b/dlls/ai_namespaces.cpp @@ -90,12 +90,12 @@ int CAI_GlobalNamespace::NextGlobalBase() const // CAI_LocalIdSpace::CAI_LocalIdSpace( bool fIsRoot ) - : m_pGlobalNamespace( NULL ), - m_pParentIDSpace( NULL ), - m_globalBase( (fIsRoot) ? 0 : -1 ), + : m_globalBase( (fIsRoot) ? 0 : -1 ), m_localBase( (fIsRoot) ? 0 : MAX_STRING_INDEX ), m_localTop( -1 ), - m_globalTop( -1 ) + m_globalTop( -1 ), + m_pParentIDSpace( NULL ), + m_pGlobalNamespace( NULL ) { }; diff --git a/dlls/ai_navigator.cpp b/dlls/ai_navigator.cpp index 39ba2fba..24526fe2 100644 --- a/dlls/ai_navigator.cpp +++ b/dlls/ai_navigator.cpp @@ -1116,7 +1116,6 @@ float CAI_Navigator::GetPathTimeToGoal() AI_PathNode_t CAI_Navigator::GetNearestNode() { - COMPILE_TIME_ASSERT( (int)AIN_NO_NODE == NO_NODE ); return (AI_PathNode_t)( GetPathfinder()->NearestNodeToNPC() ); } @@ -2159,6 +2158,8 @@ bool CAI_Navigator::PreMove() GetMotor()->MoveJumpStop(); break; } + default: + break; } SetNavType( NAV_GROUND ); @@ -2364,6 +2365,8 @@ bool CAI_Navigator::Move( float flInterval ) case NAV_FLY: OnMoveBlocked( &moveResult ); break; + default: + break; } break; } @@ -3625,6 +3628,8 @@ bool CAI_Navigator::DoFindPath( void ) } } break; + default: + break; } return returnCode; @@ -3927,18 +3932,18 @@ void CAI_Navigator::DrawDebugRouteOverlay(void) if (waypoint) { Vector RGB = GetRouteColor(waypoint->NavType(), waypoint->Flags()); - NDebugOverlay::Line(GetLocalOrigin(), waypoint->GetPos(), RGB[0],RGB[1],RGB[2], true,0); + NDebugOverlay::Line(GetLocalOrigin(), waypoint->GetPos(), static_cast(RGB[0]),static_cast(RGB[1]),static_cast(RGB[2]), true,0); } while (waypoint) { Vector RGB = GetWaypointColor(waypoint->NavType()); - NDebugOverlay::Box(waypoint->GetPos(), Vector(-3,-3,-3),Vector(3,3,3), RGB[0],RGB[1],RGB[2], true,0); + NDebugOverlay::Box(waypoint->GetPos(), Vector(-3,-3,-3),Vector(3,3,3), static_cast(RGB[0]),static_cast(RGB[1]),static_cast(RGB[2]), true,0); if (waypoint->GetNext()) { Vector RGB = GetRouteColor(waypoint->GetNext()->NavType(), waypoint->GetNext()->Flags()); - NDebugOverlay::Line(waypoint->GetPos(), waypoint->GetNext()->GetPos(),RGB[0],RGB[1],RGB[2], true,0); + NDebugOverlay::Line(waypoint->GetPos(), waypoint->GetNext()->GetPos(),static_cast(RGB[0]),static_cast(RGB[1]),static_cast(RGB[2]), true,0); } waypoint = waypoint->GetNext(); } diff --git a/dlls/ai_navigator.h b/dlls/ai_navigator.h index 04541d5c..4432b834 100644 --- a/dlls/ai_navigator.h +++ b/dlls/ai_navigator.h @@ -185,7 +185,7 @@ struct AI_NavGoal_t CBaseEntity * pTarget = AIN_DEF_TARGET); //---------------------------------- - + // What type of goal is this GoalType_t type; @@ -195,10 +195,6 @@ struct AI_NavGoal_t // The activity to use, or none if a previosly set activity should be used Activity activity; - - // The predicted activity used after arrival - Activity arrivalActivity; - int arrivalSequence; // The tolerance of success, or none if a previosly set tolerance should be used float tolerance; @@ -209,9 +205,13 @@ struct AI_NavGoal_t // Optional flags specifying unsigned flags; - + // The target of the navigation, primarily used to ignore the entity in hull and line traces CBaseEntity * pTarget; + + // The predicted activity used after arrival + Activity arrivalActivity; + int arrivalSequence; }; //------------------------------------- diff --git a/dlls/ai_network.cpp b/dlls/ai_network.cpp index d95a30e5..9ad9c757 100644 --- a/dlls/ai_network.cpp +++ b/dlls/ai_network.cpp @@ -47,13 +47,13 @@ public: class CNodeFilter : public INodeListFilter { public: - CNodeFilter( CAI_BaseNPC *pNPC, const Vector &pos ) : m_pNPC(pNPC), m_pos(pos) + CNodeFilter( CAI_BaseNPC *pNPC, const Vector &pos ) : m_pos(pos), m_pNPC(pNPC) { if ( m_pNPC ) m_capabilities = m_pNPC->CapabilitiesGet(); } - CNodeFilter( const Vector &pos ) : m_pNPC(NULL), m_pos(pos) + CNodeFilter( const Vector &pos ) : m_pos(pos), m_pNPC(NULL) { } diff --git a/dlls/ai_networkmanager.cpp b/dlls/ai_networkmanager.cpp index aea26199..29c9a6a8 100644 --- a/dlls/ai_networkmanager.cpp +++ b/dlls/ai_networkmanager.cpp @@ -2345,7 +2345,7 @@ void CAI_NetworkBuilder::InitClimbNodePosition(CAI_Network *pNetwork, CAI_Node * { float floorZ = GetFloorZ(origin); // FIXME: don't use this - if (abs(pNode->GetOrigin().z - floorZ) < 36) + if (abs(static_cast(pNode->GetOrigin().z - floorZ)) < 36) { CAI_Node *new_node = pNetwork->AddNode( pNode->GetOrigin(), pNode->m_flYaw ); new_node->m_pHint = NULL; diff --git a/dlls/ai_pathfinder.cpp b/dlls/ai_pathfinder.cpp index c9ca207a..c30890c8 100644 --- a/dlls/ai_pathfinder.cpp +++ b/dlls/ai_pathfinder.cpp @@ -1241,7 +1241,7 @@ AI_Waypoint_t *CAI_Pathfinder::BuildRadialRoute( const Vector &vStartPos, const vNextPos.y += flRadius * sin( flCurAngle ); // Build a route from the last position to the current one - pNextRoute = BuildLocalRoute( vLastPos, vNextPos, NULL, NULL, NO_NODE, fRouteBits, goalTolerance); + pNextRoute = BuildLocalRoute( vLastPos, vNextPos, NULL, 0, NO_NODE, fRouteBits, goalTolerance); // If we can't find a route, we failed if ( pNextRoute == NULL ) @@ -1270,7 +1270,7 @@ AI_Waypoint_t *CAI_Pathfinder::BuildRadialRoute( const Vector &vStartPos, const } // Append a path to the final position - pLastRoute = BuildLocalRoute( vLastPos, vGoalPos, NULL, NULL, NO_NODE, bAirRoute ? bits_BUILD_FLY : bits_BUILD_GROUND, goalTolerance ); + pLastRoute = BuildLocalRoute( vLastPos, vGoalPos, NULL, 0, NO_NODE, bAirRoute ? bits_BUILD_FLY : bits_BUILD_GROUND, goalTolerance ); if ( pLastRoute == NULL ) return NULL; @@ -1811,9 +1811,9 @@ void CAI_Pathfinder::CTriDebugOverlay::FadeTriOverlayLines(void) { for (int i=0;ir *= 0.5; - m_debugTriOverlayLine[i]->g *= 0.5; - m_debugTriOverlayLine[i]->b *= 0.5; + m_debugTriOverlayLine[i]->r = static_cast(m_debugTriOverlayLine[i]->r * 0.5); + m_debugTriOverlayLine[i]->g = static_cast(m_debugTriOverlayLine[i]->g * 0.5); + m_debugTriOverlayLine[i]->b = static_cast(m_debugTriOverlayLine[i]->b * 0.5); } } } diff --git a/dlls/ai_planesolver.cpp b/dlls/ai_planesolver.cpp index 8062cded..d70dad20 100644 --- a/dlls/ai_planesolver.cpp +++ b/dlls/ai_planesolver.cpp @@ -60,8 +60,8 @@ inline float cube( float f ) CAI_PlaneSolver::CAI_PlaneSolver( CAI_BaseNPC *pNpc ) : m_pNpc( pNpc ), - m_fSolvedPrev( false ), m_PrevTarget( FLT_MAX, FLT_MAX, FLT_MAX ), + m_fSolvedPrev( false ), m_PrevSolution( 0 ), m_ClosestHaveBeenToCurrent( FLT_MAX ), m_TimeLastProgress( FLT_MAX ), diff --git a/dlls/ai_planesolver.h b/dlls/ai_planesolver.h index 0f61c066..da134518 100644 --- a/dlls/ai_planesolver.h +++ b/dlls/ai_planesolver.h @@ -137,8 +137,8 @@ private: CircleObstacles_t( const Vector ¢er, float radius, CBaseEntity *pEntity, AI_MoveSuggType_t type ) : center(center), radius(radius), - hEntity(pEntity), - type(type) + type(type), + hEntity(pEntity) { } diff --git a/dlls/ai_playerally.cpp b/dlls/ai_playerally.cpp index bd7164d5..bf9292cf 100644 --- a/dlls/ai_playerally.cpp +++ b/dlls/ai_playerally.cpp @@ -133,7 +133,7 @@ static CUtlMap g_ConceptInfoMap; static void InitConcepts( void ) { g_ConceptInfoMap.SetLessFunc( CaselessStringLessThan ); - for ( int i = 0; i < ARRAYSIZE(g_ConceptInfos); i++ ) + for ( size_t i = 0; i < ARRAYSIZE(g_ConceptInfos); i++ ) { g_ConceptInfoMap.Insert( g_ConceptInfos[i].concept, &g_ConceptInfos[i] ); } @@ -158,7 +158,7 @@ void CAI_AllySpeechManager::Spawn() InitConcepts(); } - for ( int i = 0; i < ARRAYSIZE(g_ConceptInfos); i++ ) + for ( size_t i = 0; i < ARRAYSIZE(g_ConceptInfos); i++ ) m_ConceptTimers.Insert( AllocPooledString( g_ConceptInfos[i].concept ), CSimpleSimTimer() ); } @@ -1512,7 +1512,7 @@ bool CAI_PlayerAlly::ShouldSpeakRandom( AIConcept_t concept, int iChance ) if ( flModifier < 0.001 ) return false; - iChance = floor( (float)iChance / flModifier ); + iChance = static_cast(floor( (float)iChance / flModifier )); } } diff --git a/dlls/ai_schedule.cpp b/dlls/ai_schedule.cpp index 9edefc4a..eb84b89a 100644 --- a/dlls/ai_schedule.cpp +++ b/dlls/ai_schedule.cpp @@ -239,7 +239,7 @@ bool CAI_SchedulesManager::LoadSchedulesFromBuffer( const char *prefix, char *pS int taskNum = 0; pfile = engine->ParseFile(pfile, token, sizeof( token ) ); - while ((token[0]!=NULL) && (stricmp("Interrupts",token))) + while ((token[0] != 0) && (stricmp("Interrupts",token))) { // Convert generic ID to sub-class specific enum int taskID = CAI_BaseNPC::GetTaskID(token); @@ -477,7 +477,7 @@ bool CAI_SchedulesManager::LoadSchedulesFromBuffer( const char *prefix, char *pS // Now read in the interrupts // ========================== pfile = engine->ParseFile(pfile, token, sizeof( token ) ); - while ((token[0]!=NULL) && (stricmp("Schedule",token))) + while ((token[0] != 0) && (stricmp("Schedule",token))) { // Convert generic ID to sub-class specific enum int condID = CAI_BaseNPC::GetConditionID(token); diff --git a/dlls/ai_schedule.h b/dlls/ai_schedule.h index d6567792..85528e33 100644 --- a/dlls/ai_schedule.h +++ b/dlls/ai_schedule.h @@ -1,200 +1,200 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: A schedule -// -// $Workfile: $ -// $Date: $ -// -//----------------------------------------------------------------------------- -// $Log: $ -// -// $NoKeywords: $ -//=============================================================================// - -#include "bitstring.h" - -#ifndef AI_SCHEDULE_H -#define AI_SCHEDULE_H - -#pragma once - -class CStringRegistry; -class CAI_ClassScheduleIdSpace; -class CAI_BaseNPC; - -struct Task_t; - -#ifndef MAX_CONDITIONS -#define MAX_CONDITIONS 32*8 -#endif -typedef CFixedBitString CAI_ScheduleBits; - -//================================================== -// goalType_t -//================================================== - -enum goalType_t -{ - GOAL_NONE = -1, - GOAL_ENEMY, //Our current enemy's position - GOAL_TARGET, //Our current target's position - GOAL_ENEMY_LKP, //Our current enemy's last known position - GOAL_SAVED_POSITION, //Our saved position -}; - -//================================================== -// pathType_t -//================================================== - -enum pathType_t -{ - PATH_NONE = -1, - PATH_TRAVEL, //Path that will take us to the goal - PATH_LOS, //Path that gives us line of sight to our goal - //PATH_FLANK, //Path that will take us to a flanking position of our goal - //PATH_FLANK_LOS, //Path that will take us to within line of sight to the flanking position of our goal - PATH_COVER, //Path that will give us cover from our goal - //PATH_COVER_LOS, //Path that will give us line of sight to cover from our goal -}; - -//============================================================================= -// >> CAI_Schedule -//============================================================================= - -class CAI_Schedule; - -class CAI_SchedulesManager -{ -public: - CAI_SchedulesManager() - { - allSchedules = NULL; - m_CurLoadSig = 0; // Note when schedules reset - } - - int GetScheduleLoadSignature() { return m_CurLoadSig; } - CAI_Schedule* GetScheduleFromID( int schedID ); // Function to return schedule from linked list - CAI_Schedule* GetScheduleByName( const char *name ); - - bool LoadAllSchedules(void); - - bool LoadSchedules( const char* prefix, CAI_ClassScheduleIdSpace *pIdSpace ); - bool LoadSchedulesFromBuffer( const char *prefix, char *pfile, CAI_ClassScheduleIdSpace *pIdSpace ); - -private: - friend class CAI_SystemHook; - - int m_CurLoadSig; // Note when schedules reset - CAI_Schedule* allSchedules; // A linked list of all schedules - - CAI_Schedule * CreateSchedule(char *name, int schedule_id); - - void CreateStringRegistries( void ); - void DestroyStringRegistries( void ); - void DeleteAllSchedules(void); - - //static bool LoadSchedules( char* prefix, int taskIDOffset, int taskENOffset, - // int schedIDOffset, int schedENOffset, - // int condIDOffset, int condENOffset); - - // parsing helpers - int GetStateID(const char *state_name); - int GetMemoryID(const char *memory_name); - int GetPathID( const char *token ); - int GetGoalID( const char *token ); - -}; - -extern CAI_SchedulesManager g_AI_SchedulesManager; - -class CAI_Schedule -{ -// --------- -// Static -// --------- -// --------- -public: - int GetId() const - { - return m_iScheduleID; - } - - const Task_t *GetTaskList() const - { - return m_pTaskList; - } - - int NumTasks() const - { - return m_iNumTasks; - } - - void GetInterruptMask( CAI_ScheduleBits *pBits ) const - { - m_InterruptMask.Copy( pBits ); - } - - bool HasInterrupt( int condition ) const - { - return m_InterruptMask.GetBit( condition ); - } - - const char *GetName() const - { - return m_pName; - } - -private: - friend class CAI_SchedulesManager; - - int m_iScheduleID; // The id number of this schedule - - Task_t *m_pTaskList; - int m_iNumTasks; - - CAI_ScheduleBits m_InterruptMask; // a bit mask of conditions that can interrupt this schedule - char *m_pName; - - CAI_Schedule *nextSchedule; // The next schedule in the list of schedules - - CAI_Schedule(char *name,int schedule_id, CAI_Schedule *pNext); - ~CAI_Schedule( void ); -}; - -//----------------------------------------------------------------------------- -// -// In-memory schedules -// - -#define AI_DEFINE_SCHEDULE( name, text ) \ - const char * g_psz##name = \ - "\n Schedule" \ - "\n " #name \ - text \ - "\n" - - -#define AI_LOAD_SCHEDULE( classname, name ) \ - do \ - { \ - extern const char * g_psz##name; \ - if ( classname::gm_SchedLoadStatus.fValid ) \ - { \ - classname::gm_SchedLoadStatus.fValid = g_AI_SchedulesManager.LoadSchedulesFromBuffer( #classname,(char *)g_psz##name,&classname::gm_ClassScheduleIdSpace ); \ - } \ - } while (false) - - -// For loading default schedules in memory (see ai_default.cpp) -#define AI_LOAD_DEF_SCHEDULE( classname, name ) \ - do \ - { \ - extern const char * g_psz##name; \ - if (!g_AI_SchedulesManager.LoadSchedulesFromBuffer( #classname,(char *)g_psz##name,&classname::gm_ClassScheduleIdSpace )) \ - return false; \ - } while (false) - - -//----------------------------------------------------------------------------- - -#endif // AI_SCHEDULE_H \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: A schedule +// +// $Workfile: $ +// $Date: $ +// +//----------------------------------------------------------------------------- +// $Log: $ +// +// $NoKeywords: $ +//=============================================================================// + +#include "bitstring.h" + +#ifndef AI_SCHEDULE_H +#define AI_SCHEDULE_H + +#pragma once + +class CStringRegistry; +class CAI_ClassScheduleIdSpace; +class CAI_BaseNPC; + +struct Task_t; + +#ifndef MAX_CONDITIONS +#define MAX_CONDITIONS 32*8 +#endif +typedef CFixedBitString CAI_ScheduleBits; + +//================================================== +// goalType_t +//================================================== + +enum goalType_t +{ + GOAL_NONE = -1, + GOAL_ENEMY, //Our current enemy's position + GOAL_TARGET, //Our current target's position + GOAL_ENEMY_LKP, //Our current enemy's last known position + GOAL_SAVED_POSITION, //Our saved position +}; + +//================================================== +// pathType_t +//================================================== + +enum pathType_t +{ + PATH_NONE = -1, + PATH_TRAVEL, //Path that will take us to the goal + PATH_LOS, //Path that gives us line of sight to our goal + //PATH_FLANK, //Path that will take us to a flanking position of our goal + //PATH_FLANK_LOS, //Path that will take us to within line of sight to the flanking position of our goal + PATH_COVER, //Path that will give us cover from our goal + //PATH_COVER_LOS, //Path that will give us line of sight to cover from our goal +}; + +//============================================================================= +// >> CAI_Schedule +//============================================================================= + +class CAI_Schedule; + +class CAI_SchedulesManager +{ +public: + CAI_SchedulesManager() + { + allSchedules = NULL; + m_CurLoadSig = 0; // Note when schedules reset + } + + int GetScheduleLoadSignature() { return m_CurLoadSig; } + CAI_Schedule* GetScheduleFromID( int schedID ); // Function to return schedule from linked list + CAI_Schedule* GetScheduleByName( const char *name ); + + bool LoadAllSchedules(void); + + bool LoadSchedules( const char* prefix, CAI_ClassScheduleIdSpace *pIdSpace ); + bool LoadSchedulesFromBuffer( const char *prefix, char *pfile, CAI_ClassScheduleIdSpace *pIdSpace ); + +private: + friend class CAI_SystemHook; + + int m_CurLoadSig; // Note when schedules reset + CAI_Schedule* allSchedules; // A linked list of all schedules + + CAI_Schedule * CreateSchedule(char *name, int schedule_id); + + void CreateStringRegistries( void ); + void DestroyStringRegistries( void ); + void DeleteAllSchedules(void); + + //static bool LoadSchedules( char* prefix, int taskIDOffset, int taskENOffset, + // int schedIDOffset, int schedENOffset, + // int condIDOffset, int condENOffset); + + // parsing helpers + int GetStateID(const char *state_name); + int GetMemoryID(const char *memory_name); + int GetPathID( const char *token ); + int GetGoalID( const char *token ); + +}; + +extern CAI_SchedulesManager g_AI_SchedulesManager; + +class CAI_Schedule +{ +// --------- +// Static +// --------- +// --------- +public: + int GetId() const + { + return m_iScheduleID; + } + + const Task_t *GetTaskList() const + { + return m_pTaskList; + } + + int NumTasks() const + { + return m_iNumTasks; + } + + void GetInterruptMask( CAI_ScheduleBits *pBits ) const + { + m_InterruptMask.Copy( pBits ); + } + + bool HasInterrupt( int condition ) const + { + return m_InterruptMask.GetBit( condition ); + } + + const char *GetName() const + { + return m_pName; + } + +private: + friend class CAI_SchedulesManager; + + int m_iScheduleID; // The id number of this schedule + + Task_t *m_pTaskList; + int m_iNumTasks; + + CAI_ScheduleBits m_InterruptMask; // a bit mask of conditions that can interrupt this schedule + char *m_pName; + + CAI_Schedule *nextSchedule; // The next schedule in the list of schedules + + CAI_Schedule(char *name,int schedule_id, CAI_Schedule *pNext); + ~CAI_Schedule( void ); +}; + +//----------------------------------------------------------------------------- +// +// In-memory schedules +// + +#define AI_DEFINE_SCHEDULE( name, text ) \ + const char * g_psz##name = \ + "\n Schedule" \ + "\n " #name \ + text \ + "\n" + + +#define AI_LOAD_SCHEDULE( classname, name ) \ + do \ + { \ + extern const char * g_psz##name; \ + if ( classname::gm_SchedLoadStatus.fValid ) \ + { \ + classname::gm_SchedLoadStatus.fValid = g_AI_SchedulesManager.LoadSchedulesFromBuffer( #classname,(char *)g_psz##name,&classname::gm_ClassScheduleIdSpace ); \ + } \ + } while (false) + + +// For loading default schedules in memory (see ai_default.cpp) +#define AI_LOAD_DEF_SCHEDULE( classname, name ) \ + do \ + { \ + extern const char * g_psz##name; \ + if (!g_AI_SchedulesManager.LoadSchedulesFromBuffer( #classname,(char *)g_psz##name,&classname::gm_ClassScheduleIdSpace )) \ + return false; \ + } while (false) + + +//----------------------------------------------------------------------------- + +#endif // AI_SCHEDULE_H diff --git a/dlls/ai_scriptconditions.h b/dlls/ai_scriptconditions.h index 8290de51..168d8a74 100644 --- a/dlls/ai_scriptconditions.h +++ b/dlls/ai_scriptconditions.h @@ -1,249 +1,249 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -//=============================================================================// - -#ifndef AI_SCRIPTCONDITIONS_H -#define AI_SCRIPTCONDITIONS_H - -#include "baseentity.h" -#include "entityoutput.h" -#include "simtimer.h" -#include "ai_npcstate.h" - -#if defined( _WIN32 ) -#pragma once -#endif - -//----------------------------------------------------------------------------- - -class CAI_ProxTester -{ -public: - CAI_ProxTester() - : m_distSq( 0 ), - m_fInside( false ) - { - } - - void Init( float dist ) - { - m_fInside = ( dist > 0 ); - m_distSq = dist * dist; - } - - bool Check( CBaseEntity *pEntity1, CBaseEntity *pEntity2 ) - { - if ( m_distSq != 0 ) - { - float distSq = ( pEntity1->GetAbsOrigin() - pEntity2->GetAbsOrigin() ).LengthSqr(); - bool fInside = ( distSq < m_distSq ); - - return ( m_fInside == fInside ); - } - return true; - } - - DECLARE_SIMPLE_DATADESC(); - -private: - - float m_distSq; - bool m_fInside; -}; - -//----------------------------------------------------------------------------- -class CAI_ScriptConditionsElement -{ -public: - - DECLARE_SIMPLE_DATADESC(); - - void SetActor( CBaseEntity *pEntity ) { m_hActor = pEntity; } - CBaseEntity *GetActor( void ){ return m_hActor.Get(); } - - void SetTimer( CSimTimer timer ) { m_Timer = timer; } - CSimTimer *GetTimer( void ) { return &m_Timer; } - - void SetTimeOut( CSimTimer timeout) { m_Timeout = timeout; } - CSimTimer *GetTimeOut( void ) { return &m_Timeout; } - -private: - EHANDLE m_hActor; - CSimTimer m_Timer; - CSimTimer m_Timeout; -}; - -//----------------------------------------------------------------------------- -// class CAI_ScriptConditions -// -// Purpose: Watches a set of conditions relative to a given NPC, and when they -// are all satisfied, fires the relevant output -//----------------------------------------------------------------------------- - -class CAI_ScriptConditions : public CBaseEntity, public IEntityListener -{ - DECLARE_CLASS( CAI_ScriptConditions, CBaseEntity ); - -public: - CAI_ScriptConditions() - : m_fDisabled( true ), - m_flRequiredTime( 0 ), - m_fMinState( NPC_STATE_IDLE ), - m_fMaxState( NPC_STATE_IDLE ), - m_fScriptStatus( TRS_NONE ), - m_fActorSeePlayer( TRS_NONE ), - m_flPlayerActorProximity( 0 ), - m_flPlayerActorFOV( -1 ), - m_fPlayerActorLOS( TRS_NONE ), - m_fActorSeeTarget( TRS_NONE ), - m_flActorTargetProximity( 0 ), - m_flPlayerTargetProximity( 0 ), - m_flPlayerTargetFOV( 0 ), - m_fPlayerTargetLOS( TRS_NONE ), - m_fPlayerBlockingActor( TRS_NONE ), - m_flMinTimeout( 0 ), - m_flMaxTimeout( 0 ), - m_fActorInPVS( TRS_NONE ) - { -#ifndef HL2_EPISODIC - m_hActor = NULL; -#endif - } - -private: - void Spawn(); - void Activate(); - - void EvaluationThink(); - - void Enable(); - void Disable(); - - void SetThinkTime() { SetNextThink( gpGlobals->curtime + 0.250 ); } - - // Evaluators - struct EvalArgs_t - { - CBaseEntity *pActor; - CBaseEntity *pPlayer; - CBaseEntity *pTarget; - }; - - bool EvalState( const EvalArgs_t &args ); - bool EvalActorSeePlayer( const EvalArgs_t &args ); - bool EvalPlayerActorLook( const EvalArgs_t &args ); - bool EvalPlayerTargetLook( const EvalArgs_t &args ); - bool EvalPlayerActorProximity( const EvalArgs_t &args ); - bool EvalPlayerTargetProximity( const EvalArgs_t &args ); - bool EvalActorTargetProximity( const EvalArgs_t &args ); - bool EvalActorSeeTarget( const EvalArgs_t &args ); - bool EvalPlayerActorLOS( const EvalArgs_t &args ); - bool EvalPlayerTargetLOS( const EvalArgs_t &args ); - bool EvalPlayerBlockingActor( const EvalArgs_t &args ); - bool EvalActorInPVS( const EvalArgs_t &args ); - - void OnEntitySpawned( CBaseEntity *pEntity ); - - int AddNewElement( CBaseEntity *pActor ); - - bool ActorInList( CBaseEntity *pActor ); - void UpdateOnRemove( void ); - - // Input handlers - void InputEnable( inputdata_t &inputdata ); - void InputDisable( inputdata_t &inputdata ); - - // Output handlers - COutputEvent m_OnConditionsSatisfied; - COutputEvent m_OnConditionsTimeout; - COutputEvent m_NoValidActors; - - //--------------------------------- - -#ifndef HL2_EPISODIC - CBaseEntity *GetActor() { return m_hActor.Get(); } -#endif - CBaseEntity *GetPlayer() { return UTIL_GetLocalPlayer(); } - - //--------------------------------- - - // @Note (toml 07-17-02): At some point, it may be desireable to switch to using function objects instead of functions. Probably - // if support for NPCs addiing custom conditions becomes necessary - typedef bool (CAI_ScriptConditions::*EvaluationFunc_t)( const EvalArgs_t &args ); - - struct EvaluatorInfo_t - { - EvaluationFunc_t pfnEvaluator; - const char *pszName; - }; - - static EvaluatorInfo_t gm_Evaluators[]; - - //--------------------------------- - // Evaluation helpers - - static bool IsInFOV( CBaseEntity *pViewer, CBaseEntity *pViewed, float fov, bool bTrueCone ); - static bool PlayerHasLineOfSight( CBaseEntity *pViewer, CBaseEntity *pViewed, bool fNot ); - static bool ActorInPlayersPVS( CBaseEntity *pActor, bool bNot ); - - virtual void OnRestore( void ); - - //--------------------------------- - // General conditions info - - bool m_fDisabled; - bool m_bLeaveAsleep; - EHANDLE m_hTarget; - - float m_flRequiredTime; // How long should the conditions me true - -#ifndef HL2_EPISODIC - EHANDLE m_hActor; - CSimTimer m_Timer; // @TODO (toml 07-16-02): save/load of timer once Jay has save/load of contained objects - CSimTimer m_Timeout; -#endif - - //--------------------------------- - // Specific conditions data - NPC_STATE m_fMinState; - NPC_STATE m_fMaxState; - ThreeState_t m_fScriptStatus; - ThreeState_t m_fActorSeePlayer; - string_t m_Actor; - - float m_flPlayerActorProximity; - CAI_ProxTester m_PlayerActorProxTester; - - float m_flPlayerActorFOV; - bool m_bPlayerActorFOVTrueCone; - ThreeState_t m_fPlayerActorLOS; - ThreeState_t m_fActorSeeTarget; - - float m_flActorTargetProximity; - CAI_ProxTester m_ActorTargetProxTester; - - float m_flPlayerTargetProximity; - CAI_ProxTester m_PlayerTargetProxTester; - - float m_flPlayerTargetFOV; - bool m_bPlayerTargetFOVTrueCone; - ThreeState_t m_fPlayerTargetLOS; - ThreeState_t m_fPlayerBlockingActor; - ThreeState_t m_fActorInPVS; - - float m_flMinTimeout; - float m_flMaxTimeout; - - CUtlVector< CAI_ScriptConditionsElement > m_ElementList; - - //--------------------------------- - - DECLARE_DATADESC(); -}; - -//============================================================================= - -#endif // AI_SCRIPTCONDITIONS_H \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef AI_SCRIPTCONDITIONS_H +#define AI_SCRIPTCONDITIONS_H + +#include "baseentity.h" +#include "entityoutput.h" +#include "simtimer.h" +#include "ai_npcstate.h" + +#if defined( _WIN32 ) +#pragma once +#endif + +//----------------------------------------------------------------------------- + +class CAI_ProxTester +{ +public: + CAI_ProxTester() + : m_distSq( 0 ), + m_fInside( false ) + { + } + + void Init( float dist ) + { + m_fInside = ( dist > 0 ); + m_distSq = dist * dist; + } + + bool Check( CBaseEntity *pEntity1, CBaseEntity *pEntity2 ) + { + if ( m_distSq != 0 ) + { + float distSq = ( pEntity1->GetAbsOrigin() - pEntity2->GetAbsOrigin() ).LengthSqr(); + bool fInside = ( distSq < m_distSq ); + + return ( m_fInside == fInside ); + } + return true; + } + + DECLARE_SIMPLE_DATADESC(); + +private: + + float m_distSq; + bool m_fInside; +}; + +//----------------------------------------------------------------------------- +class CAI_ScriptConditionsElement +{ +public: + + DECLARE_SIMPLE_DATADESC(); + + void SetActor( CBaseEntity *pEntity ) { m_hActor = pEntity; } + CBaseEntity *GetActor( void ){ return m_hActor.Get(); } + + void SetTimer( CSimTimer timer ) { m_Timer = timer; } + CSimTimer *GetTimer( void ) { return &m_Timer; } + + void SetTimeOut( CSimTimer timeout) { m_Timeout = timeout; } + CSimTimer *GetTimeOut( void ) { return &m_Timeout; } + +private: + EHANDLE m_hActor; + CSimTimer m_Timer; + CSimTimer m_Timeout; +}; + +//----------------------------------------------------------------------------- +// class CAI_ScriptConditions +// +// Purpose: Watches a set of conditions relative to a given NPC, and when they +// are all satisfied, fires the relevant output +//----------------------------------------------------------------------------- + +class CAI_ScriptConditions : public CBaseEntity, public IEntityListener +{ + DECLARE_CLASS( CAI_ScriptConditions, CBaseEntity ); + +public: + CAI_ScriptConditions() + : m_fDisabled( true ), + m_flRequiredTime( 0 ), + m_fMinState( NPC_STATE_IDLE ), + m_fMaxState( NPC_STATE_IDLE ), + m_fScriptStatus( TRS_NONE ), + m_fActorSeePlayer( TRS_NONE ), + m_flPlayerActorProximity( 0 ), + m_flPlayerActorFOV( -1 ), + m_fPlayerActorLOS( TRS_NONE ), + m_fActorSeeTarget( TRS_NONE ), + m_flActorTargetProximity( 0 ), + m_flPlayerTargetProximity( 0 ), + m_flPlayerTargetFOV( 0 ), + m_fPlayerTargetLOS( TRS_NONE ), + m_fPlayerBlockingActor( TRS_NONE ), + m_fActorInPVS( TRS_NONE ), + m_flMinTimeout( 0 ), + m_flMaxTimeout( 0 ) + { +#ifndef HL2_EPISODIC + m_hActor = NULL; +#endif + } + +private: + void Spawn(); + void Activate(); + + void EvaluationThink(); + + void Enable(); + void Disable(); + + void SetThinkTime() { SetNextThink( gpGlobals->curtime + 0.250 ); } + + // Evaluators + struct EvalArgs_t + { + CBaseEntity *pActor; + CBaseEntity *pPlayer; + CBaseEntity *pTarget; + }; + + bool EvalState( const EvalArgs_t &args ); + bool EvalActorSeePlayer( const EvalArgs_t &args ); + bool EvalPlayerActorLook( const EvalArgs_t &args ); + bool EvalPlayerTargetLook( const EvalArgs_t &args ); + bool EvalPlayerActorProximity( const EvalArgs_t &args ); + bool EvalPlayerTargetProximity( const EvalArgs_t &args ); + bool EvalActorTargetProximity( const EvalArgs_t &args ); + bool EvalActorSeeTarget( const EvalArgs_t &args ); + bool EvalPlayerActorLOS( const EvalArgs_t &args ); + bool EvalPlayerTargetLOS( const EvalArgs_t &args ); + bool EvalPlayerBlockingActor( const EvalArgs_t &args ); + bool EvalActorInPVS( const EvalArgs_t &args ); + + void OnEntitySpawned( CBaseEntity *pEntity ); + + int AddNewElement( CBaseEntity *pActor ); + + bool ActorInList( CBaseEntity *pActor ); + void UpdateOnRemove( void ); + + // Input handlers + void InputEnable( inputdata_t &inputdata ); + void InputDisable( inputdata_t &inputdata ); + + // Output handlers + COutputEvent m_OnConditionsSatisfied; + COutputEvent m_OnConditionsTimeout; + COutputEvent m_NoValidActors; + + //--------------------------------- + +#ifndef HL2_EPISODIC + CBaseEntity *GetActor() { return m_hActor.Get(); } +#endif + CBaseEntity *GetPlayer() { return UTIL_GetLocalPlayer(); } + + //--------------------------------- + + // @Note (toml 07-17-02): At some point, it may be desireable to switch to using function objects instead of functions. Probably + // if support for NPCs addiing custom conditions becomes necessary + typedef bool (CAI_ScriptConditions::*EvaluationFunc_t)( const EvalArgs_t &args ); + + struct EvaluatorInfo_t + { + EvaluationFunc_t pfnEvaluator; + const char *pszName; + }; + + static EvaluatorInfo_t gm_Evaluators[]; + + //--------------------------------- + // Evaluation helpers + + static bool IsInFOV( CBaseEntity *pViewer, CBaseEntity *pViewed, float fov, bool bTrueCone ); + static bool PlayerHasLineOfSight( CBaseEntity *pViewer, CBaseEntity *pViewed, bool fNot ); + static bool ActorInPlayersPVS( CBaseEntity *pActor, bool bNot ); + + virtual void OnRestore( void ); + + //--------------------------------- + // General conditions info + + bool m_fDisabled; + bool m_bLeaveAsleep; + EHANDLE m_hTarget; + + float m_flRequiredTime; // How long should the conditions me true + +#ifndef HL2_EPISODIC + EHANDLE m_hActor; + CSimTimer m_Timer; // @TODO (toml 07-16-02): save/load of timer once Jay has save/load of contained objects + CSimTimer m_Timeout; +#endif + + //--------------------------------- + // Specific conditions data + NPC_STATE m_fMinState; + NPC_STATE m_fMaxState; + ThreeState_t m_fScriptStatus; + ThreeState_t m_fActorSeePlayer; + string_t m_Actor; + + float m_flPlayerActorProximity; + CAI_ProxTester m_PlayerActorProxTester; + + float m_flPlayerActorFOV; + bool m_bPlayerActorFOVTrueCone; + ThreeState_t m_fPlayerActorLOS; + ThreeState_t m_fActorSeeTarget; + + float m_flActorTargetProximity; + CAI_ProxTester m_ActorTargetProxTester; + + float m_flPlayerTargetProximity; + CAI_ProxTester m_PlayerTargetProxTester; + + float m_flPlayerTargetFOV; + bool m_bPlayerTargetFOVTrueCone; + ThreeState_t m_fPlayerTargetLOS; + ThreeState_t m_fPlayerBlockingActor; + ThreeState_t m_fActorInPVS; + + float m_flMinTimeout; + float m_flMaxTimeout; + + CUtlVector< CAI_ScriptConditionsElement > m_ElementList; + + //--------------------------------- + + DECLARE_DATADESC(); +}; + +//============================================================================= + +#endif // AI_SCRIPTCONDITIONS_H diff --git a/dlls/ai_senses.cpp b/dlls/ai_senses.cpp index 21e2b8d1..250f7999 100644 --- a/dlls/ai_senses.cpp +++ b/dlls/ai_senses.cpp @@ -37,8 +37,7 @@ CAI_SensedObjectsManager g_AI_SensedObjectsManager; //----------------------------------------------------------------------------- -#pragma pack(push) -#pragma pack(1) +#pragma pack(push, 1) struct AISightIterVal_t { @@ -243,7 +242,7 @@ CBaseEntity *CAI_Senses::GetFirstSeenEntity( AISightIter_t *pIter, seentype_t iS pIterVal->SeenArray = (char)iSeenType; int iFirstArray = ( iSeenType == SEEN_ALL ) ? 0 : iSeenType; - for ( int i = iFirstArray; i < ARRAYSIZE( m_SeenArrays ); i++ ) + for ( int i = iFirstArray; i < static_cast(ARRAYSIZE( m_SeenArrays )); i++ ) { if ( m_SeenArrays[i]->Count() != 0 ) { @@ -265,7 +264,7 @@ CBaseEntity *CAI_Senses::GetNextSeenEntity( AISightIter_t *pIter ) const { AISightIterVal_t *pIterVal = (AISightIterVal_t *)pIter; - for ( int i = pIterVal->array; i < ARRAYSIZE( m_SeenArrays ); i++ ) + for ( int i = pIterVal->array; i < static_cast(ARRAYSIZE( m_SeenArrays )); i++ ) { for ( int j = pIterVal->iNext; j < m_SeenArrays[i]->Count(); j++ ) { @@ -618,7 +617,7 @@ void CAI_Senses::PerformSensing( void ) // ----------------- // Look // ----------------- - Look( m_LookDist ); + Look( static_cast(m_LookDist) ); // ------------------ // Listen diff --git a/dlls/ai_speech.cpp b/dlls/ai_speech.cpp index de2c4b34..870a08e8 100644 --- a/dlls/ai_speech.cpp +++ b/dlls/ai_speech.cpp @@ -213,8 +213,8 @@ CAI_Expresser::CAI_Expresser( CAI_BaseNPC *pOuter ) : CAI_Component( pOuter ), m_pSink( NULL ), m_flStopTalkTime( 0 ), - m_flBlockedTalkTime( 0 ), m_flStopTalkTimeWithoutDelay( 0 ), + m_flBlockedTalkTime( 0 ), m_voicePitch( 100 ) { #ifdef DEBUG @@ -828,7 +828,7 @@ void CAI_ExpresserHost_DoModifyOrAppendCriteria( CAI_BaseNPC *pSpeaker, AI_Crite } static const char *pStateNames[] = { "None", "Idle", "Alert", "Combat", "Scripted", "PlayDead", "Dead" }; - if ( (int)pSpeaker->m_NPCState < ARRAYSIZE(pStateNames) ) + if ( (int)pSpeaker->m_NPCState < static_cast(ARRAYSIZE(pStateNames)) ) { set.AppendCriteria( "npcstate", UTIL_VarArgs( "[NPCState::%s]", pStateNames[pSpeaker->m_NPCState] ) ); } diff --git a/dlls/ai_speech.h b/dlls/ai_speech.h index cf8fd2e1..842cea49 100644 --- a/dlls/ai_speech.h +++ b/dlls/ai_speech.h @@ -260,15 +260,15 @@ public: virtual void PostSpeakDispatchResponse( AIConcept_t concept, AI_Response *response ) { return; } float GetResponseDuration( AI_Response *response ); - float GetTimeSpeechComplete() const { return GetExpresser()->GetTimeSpeechComplete(); } + float GetTimeSpeechComplete() const { return this->GetExpresser()->GetTimeSpeechComplete(); } - bool IsSpeaking() { return GetExpresser()->IsSpeaking(); } - bool CanSpeak() { return GetExpresser()->CanSpeak(); } - bool CanSpeakAfterMyself() { return GetExpresser()->CanSpeakAfterMyself(); } + bool IsSpeaking() { return this->GetExpresser()->IsSpeaking(); } + bool CanSpeak() { return this->GetExpresser()->CanSpeak(); } + bool CanSpeakAfterMyself() { return this->GetExpresser()->CanSpeakAfterMyself(); } - void SetSpokeConcept( AIConcept_t concept, AI_Response *response, bool bCallback = true ) { GetExpresser()->SetSpokeConcept( concept, response, bCallback ); } - float GetTimeSpokeConcept( AIConcept_t concept ) { return GetExpresser()->GetTimeSpokeConcept( concept ); } - bool SpokeConcept( AIConcept_t concept ) { return GetExpresser()->SpokeConcept( concept ); } + void SetSpokeConcept( AIConcept_t concept, AI_Response *response, bool bCallback = true ) { this->GetExpresser()->SetSpokeConcept( concept, response, bCallback ); } + float GetTimeSpokeConcept( AIConcept_t concept ) { return this->GetExpresser()->GetTimeSpokeConcept( concept ); } + bool SpokeConcept( AIConcept_t concept ) { return this->GetExpresser()->SpokeConcept( concept ); } protected: virtual bool Speak( AIConcept_t concept, const char *modifiers = NULL, char *pszOutResponseChosen = NULL, size_t bufsize = 0 ); @@ -285,7 +285,7 @@ protected: template inline void CAI_ExpresserHost::NoteSpeaking( float duration, float delay ) { - GetExpresser()->NoteSpeaking( duration, delay ); + this->GetExpresser()->NoteSpeaking( duration, delay ); } //----------------------------------------------------------------------------- @@ -293,8 +293,8 @@ inline void CAI_ExpresserHost::NoteSpeaking( float duration, float del template inline bool CAI_ExpresserHost::Speak( AIConcept_t concept, const char *modifiers /*= NULL*/, char *pszOutResponseChosen /*=NULL*/, size_t bufsize /* = 0 */ ) { - AssertOnce( GetExpresser()->GetOuter() == this ); - return GetExpresser()->Speak( concept, modifiers, pszOutResponseChosen, bufsize ); + AssertOnce( this->GetExpresser()->GetOuter() == this ); + return this->GetExpresser()->Speak( concept, modifiers, pszOutResponseChosen, bufsize ); } //----------------------------------------------------------------------------- @@ -302,7 +302,7 @@ inline bool CAI_ExpresserHost::Speak( AIConcept_t concept, const char template inline int CAI_ExpresserHost::PlaySentence( const char *pszSentence, float delay, float volume, soundlevel_t soundlevel, CBaseEntity *pListener ) { - return GetExpresser()->SpeakRawSentence( pszSentence, delay, volume, soundlevel, pListener ); + return this->GetExpresser()->SpeakRawSentence( pszSentence, delay, volume, soundlevel, pListener ); } //----------------------------------------------------------------------------- @@ -332,7 +332,7 @@ inline IResponseSystem *CAI_ExpresserHost::GetResponseSystem() template inline AI_Response *CAI_ExpresserHost::SpeakFindResponse( AIConcept_t concept, const char *modifiers /*= NULL*/ ) { - return GetExpresser()->SpeakFindResponse( concept, modifiers ); + return this->GetExpresser()->SpeakFindResponse( concept, modifiers ); } //----------------------------------------------------------------------------- @@ -340,7 +340,7 @@ inline AI_Response *CAI_ExpresserHost::SpeakFindResponse( AIConcept_t template inline bool CAI_ExpresserHost::SpeakDispatchResponse( AIConcept_t concept, AI_Response *response ) { - if ( GetExpresser()->SpeakDispatchResponse( concept, response ) ) + if ( this->GetExpresser()->SpeakDispatchResponse( concept, response ) ) { PostSpeakDispatchResponse( concept, response ); return true; @@ -354,7 +354,7 @@ inline bool CAI_ExpresserHost::SpeakDispatchResponse( AIConcept_t conc template inline float CAI_ExpresserHost::GetResponseDuration( AI_Response *response ) { - return GetExpresser()->GetResponseDuration( response ); + return this->GetExpresser()->GetResponseDuration( response ); } //----------------------------------------------------------------------------- diff --git a/dlls/ai_squad.cpp b/dlls/ai_squad.cpp index 22503827..b1fd020c 100644 --- a/dlls/ai_squad.cpp +++ b/dlls/ai_squad.cpp @@ -262,7 +262,7 @@ void CAI_Squad::AddToSquad(CAI_BaseNPC *pNPC) if (m_SquadMembers.Count() == MAX_SQUAD_MEMBERS) { - DevMsg("Error!! Squad %s is too big!!! Replacing last member\n",this->m_Name); + DevMsg("Error!! Squad %s is too big!!! Replacing last member\n", STRING(this->m_Name)); m_SquadMembers.Remove(m_SquadMembers.Count()-1); } m_SquadMembers.AddToTail(pNPC); diff --git a/dlls/ai_tacticalservices.cpp b/dlls/ai_tacticalservices.cpp index c1244c3c..cf51de8a 100644 --- a/dlls/ai_tacticalservices.cpp +++ b/dlls/ai_tacticalservices.cpp @@ -660,7 +660,7 @@ bool CAI_TacticalServices::FindLateralLos( const Vector &vecThreat, Vector *pRes int iDelta = COVER_DELTA; // If we're limited in how far we're allowed to move laterally, don't bother checking past it - int iMaxLateralDelta = GetOuter()->GetMaxTacticalLateralMovement(); + int iMaxLateralDelta = static_cast(GetOuter()->GetMaxTacticalLateralMovement()); if ( iMaxLateralDelta != MAXTACLAT_IGNORE && iMaxLateralDelta < iDelta ) { iChecks = 1; diff --git a/dlls/ai_trackpather.cpp b/dlls/ai_trackpather.cpp index 667a174c..75828186 100644 --- a/dlls/ai_trackpather.cpp +++ b/dlls/ai_trackpather.cpp @@ -1,1684 +1,1684 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -//=============================================================================// - -#include "cbase.h" - -#include "trains.h" -#include "ai_trackpather.h" - -// memdbgon must be the last include file in a .cpp file!!! -#include "tier0/memdbgon.h" - -#define TRACKPATHER_DEBUG_LEADING 1 -#define TRACKPATHER_DEBUG_PATH 2 -#define TRACKPATHER_DEBUG_TRACKS 3 -ConVar g_debug_trackpather( "g_debug_trackpather", "0", FCVAR_CHEAT ); - -//------------------------------------------------------------------------------ - -BEGIN_DATADESC( CAI_TrackPather ) - DEFINE_FIELD( m_vecDesiredPosition, FIELD_POSITION_VECTOR ), - DEFINE_FIELD( m_vecGoalOrientation, FIELD_VECTOR ), - - DEFINE_FIELD( m_pCurrentPathTarget, FIELD_CLASSPTR ), - DEFINE_FIELD( m_pDestPathTarget, FIELD_CLASSPTR ), - DEFINE_FIELD( m_pLastPathTarget, FIELD_CLASSPTR ), - DEFINE_FIELD( m_pTargetNearestPath, FIELD_CLASSPTR ), - - DEFINE_FIELD( m_strCurrentPathName, FIELD_STRING ), - DEFINE_FIELD( m_strDestPathName, FIELD_STRING ), - DEFINE_FIELD( m_strLastPathName, FIELD_STRING ), - DEFINE_FIELD( m_strTargetNearestPathName, FIELD_STRING ), - - DEFINE_FIELD( m_vecLastGoalCheckPosition, FIELD_POSITION_VECTOR ), - DEFINE_FIELD( m_flEnemyPathUpdateTime, FIELD_TIME ), - DEFINE_FIELD( m_bForcedMove, FIELD_BOOLEAN ), - DEFINE_FIELD( m_bPatrolling, FIELD_BOOLEAN ), - DEFINE_FIELD( m_bPatrolBreakable, FIELD_BOOLEAN ), - DEFINE_FIELD( m_bLeading, FIELD_BOOLEAN ), - - // Derived class pathing data - DEFINE_FIELD( m_flTargetDistanceThreshold, FIELD_FLOAT ), - DEFINE_FIELD( m_flAvoidDistance, FIELD_FLOAT ), - - DEFINE_FIELD( m_flTargetTolerance, FIELD_FLOAT ), - DEFINE_FIELD( m_vecSegmentStartPoint, FIELD_POSITION_VECTOR ), - DEFINE_FIELD( m_vecSegmentStartSplinePoint, FIELD_POSITION_VECTOR ), - DEFINE_FIELD( m_bMovingForward, FIELD_BOOLEAN ), - DEFINE_FIELD( m_bChooseFarthestPoint, FIELD_BOOLEAN ), - DEFINE_FIELD( m_flFarthestPathDist, FIELD_FLOAT ), - DEFINE_FIELD( m_flPathMaxSpeed, FIELD_FLOAT ), - DEFINE_FIELD( m_flTargetDistFromPath, FIELD_FLOAT ), - DEFINE_FIELD( m_flLeadDistance, FIELD_FLOAT ), - DEFINE_FIELD( m_vecTargetPathDir, FIELD_VECTOR ), - DEFINE_FIELD( m_vecTargetPathPoint, FIELD_POSITION_VECTOR ), - - DEFINE_FIELD( m_nPauseState, FIELD_INTEGER ), - - // Inputs - DEFINE_INPUTFUNC( FIELD_STRING, "SetTrack", InputSetTrack ), - DEFINE_INPUTFUNC( FIELD_STRING, "FlyToSpecificTrackViaPath", InputFlyToPathTrack ), - DEFINE_INPUTFUNC( FIELD_VOID, "StartPatrol", InputStartPatrol ), - DEFINE_INPUTFUNC( FIELD_VOID, "StopPatrol", InputStopPatrol ), - DEFINE_INPUTFUNC( FIELD_VOID, "StartBreakableMovement", InputStartBreakableMovement ), - DEFINE_INPUTFUNC( FIELD_VOID, "StopBreakableMovement", InputStopBreakableMovement ), - DEFINE_INPUTFUNC( FIELD_VOID, "ChooseFarthestPathPoint", InputChooseFarthestPathPoint ), - DEFINE_INPUTFUNC( FIELD_VOID, "ChooseNearestPathPoint", InputChooseNearestPathPoint ), - DEFINE_INPUTFUNC( FIELD_INTEGER,"InputStartLeading", InputStartLeading ), - DEFINE_INPUTFUNC( FIELD_VOID, "InputStopLeading", InputStopLeading ), - - // Obsolete, for backwards compatibility - DEFINE_INPUTFUNC( FIELD_VOID, "StartPatrolBreakable", InputStartPatrolBreakable ), - DEFINE_INPUTFUNC( FIELD_STRING, "FlyToPathTrack", InputFlyToPathTrack ), -END_DATADESC() - - -//----------------------------------------------------------------------------- -// Purpose: Initialize pathing data -//----------------------------------------------------------------------------- -void CAI_TrackPather::InitPathingData( float flTrackArrivalTolerance, float flTargetDistance, float flAvoidDistance ) -{ - m_flTargetTolerance = flTrackArrivalTolerance; - m_flTargetDistanceThreshold = flTargetDistance; - m_flAvoidDistance = flAvoidDistance; - - m_pCurrentPathTarget = NULL; - m_pDestPathTarget = NULL; - m_pLastPathTarget = NULL; - m_pTargetNearestPath = NULL; - m_bLeading = false; - - m_flEnemyPathUpdateTime = gpGlobals->curtime; - m_bForcedMove = false; - m_bPatrolling = false; - m_bPatrolBreakable = false; - m_flLeadDistance = 0.0f; - m_bMovingForward = true; - m_vecSegmentStartPoint = m_vecSegmentStartSplinePoint = m_vecDesiredPosition = GetAbsOrigin(); - m_bChooseFarthestPoint = true; - m_flFarthestPathDist = 1e10; - m_flPathMaxSpeed = 0; - m_nPauseState = PAUSE_NO_PAUSE; -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CAI_TrackPather::OnRestore( void ) -{ - BaseClass::OnRestore(); - - // Restore current path - if ( m_strCurrentPathName != NULL_STRING ) - { - m_pCurrentPathTarget = (CPathTrack *) gEntList.FindEntityByName( NULL, m_strCurrentPathName ); - } - else - { - m_pCurrentPathTarget = NULL; - } - - // Restore destination path - if ( m_strDestPathName != NULL_STRING ) - { - m_pDestPathTarget = (CPathTrack *) gEntList.FindEntityByName( NULL, m_strDestPathName ); - } - else - { - m_pDestPathTarget = NULL; - } - - // Restore last path - if ( m_strLastPathName != NULL_STRING ) - { - m_pLastPathTarget = (CPathTrack *) gEntList.FindEntityByName( NULL, m_strLastPathName ); - } - else - { - m_pLastPathTarget = NULL; - } - - // Restore target nearest path - if ( m_strTargetNearestPathName != NULL_STRING ) - { - m_pTargetNearestPath = (CPathTrack *) gEntList.FindEntityByName( NULL, m_strTargetNearestPathName ); - } - else - { - m_pTargetNearestPath = NULL; - } -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CAI_TrackPather::OnSave( IEntitySaveUtils *pUtils ) -{ - BaseClass::OnSave( pUtils ); - - // Stash all the paths into strings for restoration later - m_strCurrentPathName = ( m_pCurrentPathTarget != NULL ) ? m_pCurrentPathTarget->GetEntityName() : NULL_STRING; - m_strDestPathName = ( m_pDestPathTarget != NULL ) ? m_pDestPathTarget->GetEntityName() : NULL_STRING; - m_strLastPathName = ( m_pLastPathTarget != NULL ) ? m_pLastPathTarget->GetEntityName() : NULL_STRING; - m_strTargetNearestPathName = ( m_pTargetNearestPath != NULL ) ? m_pTargetNearestPath->GetEntityName() : NULL_STRING; -} - - -//----------------------------------------------------------------------------- -// Leading distance -//----------------------------------------------------------------------------- -void CAI_TrackPather::EnableLeading( bool bEnable ) -{ - m_bLeading = bEnable; - if ( m_bLeading ) - { - m_bPatrolling = false; - } -} - -void CAI_TrackPather::SetLeadingDistance( float flLeadDistance ) -{ - m_flLeadDistance = flLeadDistance; -} - -float CAI_TrackPather::GetLeadingDistance( ) const -{ - return m_flLeadDistance; -} - - -//----------------------------------------------------------------------------- -// Returns the next path along our current path -//----------------------------------------------------------------------------- -inline CPathTrack *CAI_TrackPather::NextAlongCurrentPath( CPathTrack *pPath ) const -{ - return CPathTrack::ValidPath( m_bMovingForward ? pPath->GetNext() : pPath->GetPrevious() ); -} - -inline CPathTrack *CAI_TrackPather::PreviousAlongCurrentPath( CPathTrack *pPath ) const -{ - return CPathTrack::ValidPath( m_bMovingForward ? pPath->GetPrevious() : pPath->GetNext() ); -} - -inline CPathTrack *CAI_TrackPather::AdjustForMovementDirection( CPathTrack *pPath ) const -{ - if ( !m_bMovingForward && CPathTrack::ValidPath( pPath->GetPrevious( ) ) ) - { - pPath = CPathTrack::ValidPath( pPath->GetPrevious() ); - } - return pPath; -} - - -//----------------------------------------------------------------------------- -// Enemy visibility check -//----------------------------------------------------------------------------- -CBaseEntity *CAI_TrackPather::FindTrackBlocker( const Vector &vecViewPoint, const Vector &vecTargetPos ) -{ - trace_t tr; - AI_TraceHull( vecViewPoint, vecTargetPos, -Vector(4,4,4), Vector(4,4,4), MASK_SHOT, this, COLLISION_GROUP_NONE, &tr ); - return (tr.fraction != 1.0f) ? tr.m_pEnt : NULL; -} - - -//----------------------------------------------------------------------------- -// Purpose: -// Input : &targetPos - -// Output : CBaseEntity -//----------------------------------------------------------------------------- -CPathTrack *CAI_TrackPather::BestPointOnPath( CPathTrack *pPath, const Vector &targetPos, float flAvoidRadius, bool visible, bool bFarthestPoint ) -{ - // Find the node nearest to the destination path target if a path is not specified - if ( pPath == NULL ) - { - pPath = m_pDestPathTarget; - } - - // If the path node we're trying to use is not valid, then we're done. - if ( CPathTrack::ValidPath( pPath ) == NULL ) - { - //FIXME: Implement - Assert(0); - return NULL; - } - - // Our target may be in a vehicle - CBaseEntity *pVehicle = NULL; - CBaseEntity *pTargetEnt = GetTrackPatherTargetEnt(); - if ( pTargetEnt != NULL ) - { - CBaseCombatCharacter *pCCTarget = pTargetEnt->MyCombatCharacterPointer(); - if ( pCCTarget != NULL && pCCTarget->IsInAVehicle() ) - { - pVehicle = pCCTarget->GetVehicleEntity(); - } - } - - // Faster math... - flAvoidRadius *= flAvoidRadius; - - // Find the nearest node to the target (going forward) - CPathTrack *pNearestPath = NULL; - float flNearestDist = bFarthestPoint ? 0 : 999999999; - float flPathDist; - - float flFarthestDistSqr = ( m_flFarthestPathDist - 2.0f * m_flTargetDistanceThreshold ); - flFarthestDistSqr *= flFarthestDistSqr; - - // NOTE: Gotta do it this crazy way because paths can be one-way. - for ( int i = 0; i < 2; ++i ) - { - int loopCheck = 0; - CPathTrack *pTravPath = pPath; - CPathTrack *pNextPath; - - BEGIN_PATH_TRACK_ITERATION(); - for ( ; CPathTrack::ValidPath( pTravPath ); pTravPath = pNextPath, loopCheck++ ) - { - // Circular loop checking - if ( pTravPath->HasBeenVisited() ) - break; - - pTravPath->Visit(); - - pNextPath = (i == 0) ? pTravPath->GetPrevious() : pTravPath->GetNext(); - - // Find the distance between this test point and our goal point - flPathDist = ( pTravPath->GetAbsOrigin() - targetPos ).LengthSqr(); - - // See if it's closer and it's also not within our avoidance radius - if ( bFarthestPoint ) - { - if ( ( flPathDist <= flNearestDist ) && ( flNearestDist <= flFarthestDistSqr ) ) - continue; - } - else - { - if ( flPathDist >= flNearestDist ) - continue; - } - - // Don't choose points that are within the avoid radius - if ( flAvoidRadius && ( pTravPath->GetAbsOrigin() - targetPos ).Length2DSqr() <= flAvoidRadius ) - continue; - - if ( visible ) - { - // If it has to be visible, run those checks - CBaseEntity *pBlocker = FindTrackBlocker( pTravPath->GetAbsOrigin(), targetPos ); - - // Check to see if we've hit the target, or the player's vehicle if it's a player in a vehicle - bool bHitTarget = ( pTargetEnt && ( pTargetEnt == pBlocker ) ) || - ( pVehicle && ( pVehicle == pBlocker ) ); - - // If we hit something, and it wasn't the target or his vehicle, then no dice - // If we hit the target and forced move was set, *still* no dice - if ( (pBlocker != NULL) && ( !bHitTarget || m_bForcedMove ) ) - continue; - } - - pNearestPath = pTravPath; - flNearestDist = flPathDist; - } - } - - return pNearestPath; -} - - -//----------------------------------------------------------------------------- -// Compute a point n units along a path -//----------------------------------------------------------------------------- -CPathTrack *CAI_TrackPather::ComputeLeadingPointAlongPath( const Vector &vecStartPoint, - CPathTrack *pFirstTrack, float flDistance, Vector *pTarget ) -{ - bool bMovingForward = (flDistance > 0.0f); - flDistance = fabs(flDistance); - - CPathTrack *pTravPath = pFirstTrack; - if ( (!bMovingForward) && pFirstTrack->GetPrevious() ) - { - pTravPath = pFirstTrack->GetPrevious(); - } - - *pTarget = vecStartPoint; - CPathTrack *pNextPath; - - // No circular loop checking needed; eventually, it'll run out of distance - for ( ; CPathTrack::ValidPath( pTravPath ); pTravPath = pNextPath ) - { - pNextPath = bMovingForward ? pTravPath->GetNext() : pTravPath->GetPrevious(); - - // Find the distance between this test point and our goal point - float flPathDist = pTarget->DistTo( pTravPath->GetAbsOrigin() ); - - // Find the distance between this test point and our goal point - if ( flPathDist <= flDistance ) - { - flDistance -= flPathDist; - *pTarget = pTravPath->GetAbsOrigin(); - if ( !CPathTrack::ValidPath(pNextPath) ) - return bMovingForward ? pTravPath : pTravPath->GetNext(); - - continue; - } - - ComputeClosestPoint( *pTarget, flDistance, pTravPath->GetAbsOrigin(), pTarget ); - return bMovingForward ? pTravPath : pTravPath->GetNext(); - } - - return NULL; -} - - -//----------------------------------------------------------------------------- -// Compute the distance to a particular point on the path -//----------------------------------------------------------------------------- -float CAI_TrackPather::ComputeDistanceAlongPathToPoint( CPathTrack *pStartTrack, - CPathTrack *pDestTrack, const Vector &vecDestPosition, bool bMovingForward ) -{ - float flTotalDist = 0.0f; - - Vector vecPoint; - ClosestPointToCurrentPath( &vecPoint ); - - CPathTrack *pTravPath = pStartTrack; - CPathTrack *pNextPath, *pTestPath; - BEGIN_PATH_TRACK_ITERATION(); - for ( ; CPathTrack::ValidPath( pTravPath ); pTravPath = pNextPath ) - { - // Circular loop checking - if ( pTravPath->HasBeenVisited() ) - break; - - // Mark it as being visited. - pTravPath->Visit(); - - pNextPath = bMovingForward ? pTravPath->GetNext() : pTravPath->GetPrevious(); - pTestPath = pTravPath; - Assert( pTestPath ); - - if ( pTravPath == pDestTrack ) - { - Vector vecDelta; - Vector vecPathDelta; - VectorSubtract( vecDestPosition, vecPoint, vecDelta ); - ComputePathDirection( pTravPath, &vecPathDelta ); - float flDot = DotProduct( vecDelta, vecPathDelta ); - flTotalDist += (flDot > 0.0f ? 1.0f : -1.0f) * vecDelta.Length2D(); - break; - } - - // NOTE: This would be made more accurate if we did the path direction check here too. - // The starting vecPoint is sometimes *not* within the bounds of the line segment. - - // Find the distance between this test point and our goal point - flTotalDist += (bMovingForward ? 1.0f : -1.0f) * vecPoint.AsVector2D().DistTo( pTestPath->GetAbsOrigin().AsVector2D() ); - vecPoint = pTestPath->GetAbsOrigin(); - } - - return flTotalDist; -} - - -//------------------------------------------------------------------------------ -// Track debugging info -//------------------------------------------------------------------------------ -void CAI_TrackPather::VisualizeDebugInfo( const Vector &vecNearestPoint, const Vector &vecTarget ) -{ - if ( g_debug_trackpather.GetInt() == TRACKPATHER_DEBUG_PATH ) - { - NDebugOverlay::Line( m_vecSegmentStartPoint, vecTarget, 0, 0, 255, true, 0.1f ); - NDebugOverlay::Cross3D( vecNearestPoint, -Vector(16,16,16), Vector(16,16,16), 255, 0, 0, true, 0.1f ); - NDebugOverlay::Cross3D( m_pCurrentPathTarget->GetAbsOrigin(), -Vector(16,16,16), Vector(16,16,16), 0, 255, 0, true, 0.1f ); - NDebugOverlay::Cross3D( m_vecDesiredPosition, -Vector(16,16,16), Vector(16,16,16), 0, 0, 255, true, 0.1f ); - NDebugOverlay::Cross3D( m_pDestPathTarget->GetAbsOrigin(), -Vector(16,16,16), Vector(16,16,16), 255, 255, 255, true, 0.1f ); - - if ( m_pTargetNearestPath ) - { - NDebugOverlay::Cross3D( m_pTargetNearestPath->GetAbsOrigin(), -Vector(24,24,24), Vector(24,24,24), 255, 0, 255, true, 0.1f ); - } - } - - if ( g_debug_trackpather.GetInt() == TRACKPATHER_DEBUG_TRACKS ) - { - if ( m_pCurrentPathTarget ) - { - CPathTrack *pPathTrack = m_pCurrentPathTarget; - for ( ; CPathTrack::ValidPath( pPathTrack ); pPathTrack = pPathTrack->GetNext() ) - { - NDebugOverlay::Box( pPathTrack->GetAbsOrigin(), -Vector(2,2,2), Vector(2,2,2), 0,255, 0, 8, 0.1 ); - if ( CPathTrack::ValidPath( pPathTrack->GetNext() ) ) - { - NDebugOverlay::Line( pPathTrack->GetAbsOrigin(), pPathTrack->GetNext()->GetAbsOrigin(), 0,255,0, true, 0.1 ); - } - - if ( pPathTrack->GetNext() == m_pCurrentPathTarget ) - break; - } - } - } -} - - -//------------------------------------------------------------------------------ -// Does this path track have LOS to the target? -//------------------------------------------------------------------------------ -bool CAI_TrackPather::HasLOSToTarget( CPathTrack *pTrack ) -{ - CBaseEntity *pTargetEnt = GetTrackPatherTargetEnt(); - if ( !pTargetEnt ) - return true; - - Vector targetPos; - if ( !GetTrackPatherTarget( &targetPos ) ) - return true; - - // Translate driver into vehicle for testing - CBaseEntity *pVehicle = NULL; - CBaseCombatCharacter *pCCTarget = pTargetEnt->MyCombatCharacterPointer(); - if ( pCCTarget != NULL && pCCTarget->IsInAVehicle() ) - { - pVehicle = pCCTarget->GetVehicleEntity(); - } - - // If it has to be visible, run those checks - CBaseEntity *pBlocker = FindTrackBlocker( pTrack->GetAbsOrigin(), targetPos ); - - // Check to see if we've hit the target, or the player's vehicle if it's a player in a vehicle - bool bHitTarget = ( pTargetEnt && ( pTargetEnt == pBlocker ) ) || - ( pVehicle && ( pVehicle == pBlocker ) ); - - return (pBlocker == NULL) || bHitTarget; -} - - -//------------------------------------------------------------------------------ -// Moves to the track -//------------------------------------------------------------------------------ -void CAI_TrackPather::UpdateCurrentTarget() -{ - // Find the point along the line that we're closest to. - const Vector &vecTarget = m_pCurrentPathTarget->GetAbsOrigin(); - Vector vecPoint; - float t = ClosestPointToCurrentPath( &vecPoint ); - if ( (t < 1.0f) && ( vecPoint.DistToSqr( vecTarget ) > m_flTargetTolerance * m_flTargetTolerance ) ) - goto visualizeDebugInfo; - - // Forced move is gone as soon as we've reached the first point on our path - if ( m_bLeading ) - { - m_bForcedMove = false; - } - - // Trip our "path_track reached" output - if ( m_pCurrentPathTarget != m_pLastPathTarget ) - { - // Get the path's specified max speed - m_flPathMaxSpeed = m_pCurrentPathTarget->m_flSpeed; - - variant_t emptyVariant; - m_pCurrentPathTarget->AcceptInput( "InPass", this, this, emptyVariant, 0 ); - m_pLastPathTarget = m_pCurrentPathTarget; - } - - if ( m_nPauseState == PAUSED_AT_POSITION ) - return; - - if ( m_nPauseState == PAUSE_AT_NEXT_LOS_POSITION ) - { - if ( HasLOSToTarget(m_pCurrentPathTarget) ) - { - m_nPauseState = PAUSED_AT_POSITION; - return; - } - } - - // Update our dest path target, if appropriate... - if ( m_pCurrentPathTarget == m_pDestPathTarget ) - { - m_bForcedMove = false; - SelectNewDestTarget(); - } - - // Did SelectNewDestTarget give us a new point to move to? - if ( m_pCurrentPathTarget != m_pDestPathTarget ) - { - // Update to the next path, if there is one... - m_pCurrentPathTarget = NextAlongCurrentPath( m_pCurrentPathTarget ); - if ( !m_pCurrentPathTarget ) - { - m_pCurrentPathTarget = m_pLastPathTarget; - } - } - else - { - // We're at rest (no patrolling behavior), which means we're moving forward now. - m_bMovingForward = true; - } - - SetDesiredPosition( m_pCurrentPathTarget->GetAbsOrigin() ); - m_vecSegmentStartSplinePoint = m_vecSegmentStartPoint; - m_vecSegmentStartPoint = m_pLastPathTarget->GetAbsOrigin(); - -visualizeDebugInfo: - VisualizeDebugInfo( vecPoint, vecTarget ); -} - - -//----------------------------------------------------------------------------- -// -// NOTE: All code below is used exclusively for leading/trailing behavior -// -//----------------------------------------------------------------------------- - -//----------------------------------------------------------------------------- -// Compute the distance to the leading position -//----------------------------------------------------------------------------- -float CAI_TrackPather::ComputeDistanceToLeadingPosition() -{ - return ComputeDistanceAlongPathToPoint( m_pCurrentPathTarget, m_pDestPathTarget, GetDesiredPosition(), m_bMovingForward ); -} - - -//----------------------------------------------------------------------------- -// Compute the distance to the *target* position -//----------------------------------------------------------------------------- -float CAI_TrackPather::ComputeDistanceToTargetPosition() -{ - Assert( m_pTargetNearestPath ); - - CPathTrack *pDest = m_bMovingForward ? m_pTargetNearestPath.Get() : m_pTargetNearestPath->GetPrevious(); - if ( !pDest ) - { - pDest = m_pTargetNearestPath; - } - bool bMovingForward = IsForwardAlongPath( m_pCurrentPathTarget, pDest ); - - CPathTrack *pStart = m_pCurrentPathTarget; - if ( bMovingForward != m_bMovingForward ) - { - if (bMovingForward) - { - if ( pStart->GetNext() ) - { - pStart = pStart->GetNext(); - } - if ( pDest->GetNext() ) - { - pDest = pDest->GetNext(); - } - } - else - { - if ( pStart->GetPrevious() ) - { - pStart = pStart->GetPrevious(); - } - if ( pDest->GetPrevious() ) - { - pDest = pDest->GetPrevious(); - } - } - } - - return ComputeDistanceAlongPathToPoint( pStart, pDest, m_vecTargetPathPoint, bMovingForward ); -} - - -//----------------------------------------------------------------------------- -// Compute a path direction -//----------------------------------------------------------------------------- -void CAI_TrackPather::ComputePathDirection( CPathTrack *pPath, Vector *pVecPathDir ) -{ - if ( pPath->GetPrevious() ) - { - VectorSubtract( pPath->GetAbsOrigin(), pPath->GetPrevious()->GetAbsOrigin(), *pVecPathDir ); - } - else - { - if ( pPath->GetNext() ) - { - VectorSubtract( pPath->GetNext()->GetAbsOrigin(), pPath->GetAbsOrigin(), *pVecPathDir ); - } - else - { - pVecPathDir->Init( 1, 0, 0 ); - } - } - VectorNormalize( *pVecPathDir ); -} - - -//----------------------------------------------------------------------------- -// What's the current path direction? -//----------------------------------------------------------------------------- -void CAI_TrackPather::CurrentPathDirection( Vector *pVecPathDir ) -{ - if ( m_pCurrentPathTarget ) - { - ComputePathDirection( m_pCurrentPathTarget, pVecPathDir ); - } - else - { - pVecPathDir->Init( 0, 0, 1 ); - } -} - - - -//----------------------------------------------------------------------------- -// Compute a point n units along the current path from our current position -// (but don't pass the desired target point) -//----------------------------------------------------------------------------- -void CAI_TrackPather::ComputePointAlongCurrentPath( float flDistance, float flPerpDist, Vector *pTarget ) -{ - Vector vecPathDir; - Vector vecStartPoint; - ClosestPointToCurrentPath( &vecStartPoint ); - *pTarget = vecStartPoint; - - if ( flDistance != 0.0f ) - { - Vector vecPrevPoint = vecStartPoint; - CPathTrack *pTravPath = m_pCurrentPathTarget; - CPathTrack *pAdjustedDest = AdjustForMovementDirection( m_pDestPathTarget ); - for ( ; CPathTrack::ValidPath( pTravPath ); pTravPath = NextAlongCurrentPath( pTravPath ) ) - { - if ( pTravPath == pAdjustedDest ) - { - ComputePathDirection( pTravPath, &vecPathDir ); - - float flPathDist = pTarget->DistTo( GetDesiredPosition() ); - if ( flDistance > flPathDist ) - { - *pTarget = GetDesiredPosition(); - } - else - { - ComputeClosestPoint( *pTarget, flDistance, GetDesiredPosition(), pTarget ); - } - break; - } - - // Find the distance between this test point and our goal point - float flPathDist = pTarget->DistTo( pTravPath->GetAbsOrigin() ); - - // Find the distance between this test point and our goal point - if ( flPathDist <= flDistance ) - { - flDistance -= flPathDist; - *pTarget = pTravPath->GetAbsOrigin(); - - // FIXME: Reduce the distance further based on the angle between this segment + the next - continue; - } - - ComputePathDirection( pTravPath, &vecPathDir ); - ComputeClosestPoint( *pTarget, flDistance, pTravPath->GetAbsOrigin(), pTarget ); - break; - } - } - else - { - VectorSubtract( m_pCurrentPathTarget->GetAbsOrigin(), m_vecSegmentStartPoint, vecPathDir ); - VectorNormalize( vecPathDir ); - } - - // Add in the horizontal component - ComputePointFromPerpDistance( *pTarget, vecPathDir, flPerpDist, pTarget ); -} - - -//----------------------------------------------------------------------------- -// Methods to find a signed perp distance from the track -// and to compute a point off the path based on the signed perp distance -//----------------------------------------------------------------------------- -float CAI_TrackPather::ComputePerpDistanceFromPath( const Vector &vecPointOnPath, const Vector &vecPathDir, const Vector &vecPointOffPath ) -{ - // Make it be a signed distance of the target from the path - // Positive means on the right side, negative means on the left side - Vector vecAcross, vecDelta; - CrossProduct( vecPathDir, Vector( 0, 0, 1 ), vecAcross ); - VectorSubtract( vecPointOffPath, vecPointOnPath, vecDelta ); - VectorMA( vecDelta, -DotProduct( vecPathDir, vecDelta ), vecPathDir, vecDelta ); - - float flDistanceFromPath = vecDelta.Length2D(); - if ( DotProduct2D( vecAcross.AsVector2D(), vecDelta.AsVector2D() ) < 0.0f ) - { - flDistanceFromPath *= -1.0f; - } - - return flDistanceFromPath; -} - -void CAI_TrackPather::ComputePointFromPerpDistance( const Vector &vecPointOnPath, const Vector &vecPathDir, float flPerpDist, Vector *pResult ) -{ - Vector vecAcross; - CrossProduct( vecPathDir, Vector( 0, 0, 1 ), vecAcross ); - VectorMA( vecPointOnPath, flPerpDist, vecAcross, *pResult ); -} - - -//----------------------------------------------------------------------------- -// Finds the closest point on the path, returns a signed perpendicular distance -// where negative means on the left side of the path (when travelled from prev to next) -// and positive means on the right side -//----------------------------------------------------------------------------- -CPathTrack *CAI_TrackPather::FindClosestPointOnPath( CPathTrack *pPath, - const Vector &targetPos, Vector *pVecClosestPoint, Vector *pVecPathDir, float *pDistanceFromPath ) -{ - // Find the node nearest to the destination path target if a path is not specified - if ( pPath == NULL ) - { - pPath = m_pDestPathTarget; - } - - // If the path node we're trying to use is not valid, then we're done. - if ( CPathTrack::ValidPath( pPath ) == NULL ) - { - //FIXME: Implement - Assert(0); - return NULL; - } - - // Find the nearest node to the target (going forward) - CPathTrack *pNearestPath = NULL; - float flNearestDist2D = 999999999; - float flNearestDist = 999999999; - float flPathDist, flPathDist2D; - - // NOTE: Gotta do it this crazy way because paths can be one-way. - Vector vecNearestPoint; - Vector vecNearestPathSegment; - for ( int i = 0; i < 2; ++i ) - { - int loopCheck = 0; - CPathTrack *pTravPath = pPath; - CPathTrack *pNextPath; - - BEGIN_PATH_TRACK_ITERATION(); - for ( ; CPathTrack::ValidPath( pTravPath ); pTravPath = pNextPath, loopCheck++ ) - { - // Circular loop checking - if ( pTravPath->HasBeenVisited() ) - break; - - // Mark it as being visited. - pTravPath->Visit(); - - pNextPath = (i == 0) ? pTravPath->GetPrevious() : pTravPath->GetNext(); - - // No alt paths allowed in leading mode. - if ( pTravPath->m_paltpath ) - { - Warning( "%s: Alternative paths in path_track not allowed when using the leading behavior!\n", GetEntityName() ); - } - - // Need line segments - if ( !CPathTrack::ValidPath(pNextPath) ) - break; - - // Find the closest point on the line segment on the path - Vector vecClosest; - CalcClosestPointOnLineSegment( targetPos, pTravPath->GetAbsOrigin(), pNextPath->GetAbsOrigin(), vecClosest ); - - // Find the distance between this test point and our goal point - flPathDist2D = vecClosest.AsVector2D().DistToSqr( targetPos.AsVector2D() ); - if ( flPathDist2D > flNearestDist2D ) - continue; - - flPathDist = vecClosest.z - targetPos.z; - flPathDist *= flPathDist; - flPathDist += flPathDist2D; - if (( flPathDist2D == flNearestDist2D ) && ( flPathDist >= flNearestDist )) - continue; - - pNearestPath = (i == 0) ? pTravPath : pNextPath; - flNearestDist2D = flPathDist2D; - flNearestDist = flPathDist; - vecNearestPoint = vecClosest; - VectorSubtract( pNextPath->GetAbsOrigin(), pTravPath->GetAbsOrigin(), vecNearestPathSegment ); - if ( i == 0 ) - { - vecNearestPathSegment *= -1.0f; - } - } - } - - VectorNormalize( vecNearestPathSegment ); - *pDistanceFromPath = ComputePerpDistanceFromPath( vecNearestPoint, vecNearestPathSegment, targetPos ); - *pVecClosestPoint = vecNearestPoint; - *pVecPathDir = vecNearestPathSegment; - return pNearestPath; -} - - -//----------------------------------------------------------------------------- -// Breakable paths? -//----------------------------------------------------------------------------- -void CAI_TrackPather::InputStartBreakableMovement( inputdata_t &inputdata ) -{ - m_bPatrolBreakable = true; -} - -void CAI_TrackPather::InputStopBreakableMovement( inputdata_t &inputdata ) -{ - m_bPatrolBreakable = false; -} - - -//----------------------------------------------------------------------------- -// Purpose: -// Input : &inputdata - -//----------------------------------------------------------------------------- -void CAI_TrackPather::InputStartPatrol( inputdata_t &inputdata ) -{ - m_bPatrolling = true; -} - - -//----------------------------------------------------------------------------- -// -//----------------------------------------------------------------------------- -void CAI_TrackPather::InputStopPatrol( inputdata_t &inputdata ) -{ - m_bPatrolling = false; -} - - -//----------------------------------------------------------------------------- -// Purpose: -// Input : &inputdata - -//----------------------------------------------------------------------------- -void CAI_TrackPather::InputStartPatrolBreakable( inputdata_t &inputdata ) -{ - m_bPatrolBreakable = true; - m_bPatrolling = true; -} - - -//------------------------------------------------------------------------------ -// Leading behaviors -//------------------------------------------------------------------------------ -void CAI_TrackPather::InputStartLeading( inputdata_t &inputdata ) -{ - EnableLeading( true ); - SetLeadingDistance( inputdata.value.Int() ); -} - -void CAI_TrackPather::InputStopLeading( inputdata_t &inputdata ) -{ - EnableLeading( false ); -} - - -//------------------------------------------------------------------------------ -// Selects a new destination target -//------------------------------------------------------------------------------ -void CAI_TrackPather::SelectNewDestTarget() -{ - if ( !m_bPatrolling ) - return; - - // NOTE: This version is bugged, but I didn't want to make the fix - // here for fear of breaking a lot of maps late in the day. - // So, only the chopper does the "right" thing. -#ifdef HL2_EPISODIC - // Episodic uses the fixed logic for all trackpathers - if ( 1 ) -#else - if ( ShouldUseFixedPatrolLogic() ) -#endif - { - CPathTrack *pOldDest = m_pDestPathTarget; - - // Only switch polarity of movement if we're at the *end* of the path - // This is really useful for initial conditions of patrolling - // NOTE: We've got to do some extra work for circular paths - bool bIsCircular = false; - { - BEGIN_PATH_TRACK_ITERATION(); - CPathTrack *pTravPath = m_pDestPathTarget; - while( CPathTrack::ValidPath( pTravPath ) ) - { - // Circular loop checking - if ( pTravPath->HasBeenVisited() ) - { - bIsCircular = true; - break; - } - - pTravPath->Visit(); - pTravPath = NextAlongCurrentPath( pTravPath ); - } - } - - if ( bIsCircular || (NextAlongCurrentPath( m_pDestPathTarget ) == NULL) ) - { - m_bMovingForward = !m_bMovingForward; - } - - BEGIN_PATH_TRACK_ITERATION(); - - while ( true ) - { - CPathTrack *pNextTrack = NextAlongCurrentPath( m_pDestPathTarget ); - if ( !pNextTrack || (pNextTrack == pOldDest) || pNextTrack->HasBeenVisited() ) - break; - - pNextTrack->Visit(); - m_pDestPathTarget = pNextTrack; - } - } - else - { - CPathTrack *pOldDest = m_pDestPathTarget; - - // For patrolling, switch the polarity of movement - m_bMovingForward = !m_bMovingForward; - - int loopCount = 0; - while ( true ) - { - CPathTrack *pNextTrack = NextAlongCurrentPath( m_pDestPathTarget ); - if ( !pNextTrack ) - break; - if ( ++loopCount > 1024 ) - { - DevMsg(1,"WARNING: Looping path for %s\n", GetDebugName() ); - break; - } - - m_pDestPathTarget = pNextTrack; - } - - if ( m_pDestPathTarget == pOldDest ) - { - // This can occur if we move to the first point on the path - SelectNewDestTarget(); - } - } -} - - -//------------------------------------------------------------------------------ -// Moves to the track -//------------------------------------------------------------------------------ -void CAI_TrackPather::UpdateCurrentTargetLeading() -{ - bool bRestingAtDest = false; - CPathTrack *pAdjustedDest; - - // Find the point along the line that we're closest to. - const Vector &vecTarget = m_pCurrentPathTarget->GetAbsOrigin(); - Vector vecPoint; - float t = ClosestPointToCurrentPath( &vecPoint ); - if ( (t < 1.0f) && ( vecPoint.DistToSqr( vecTarget ) > m_flTargetTolerance * m_flTargetTolerance ) ) - goto visualizeDebugInfo; - - // Trip our "path_track reached" output - if ( m_pCurrentPathTarget != m_pLastPathTarget ) - { - // Get the path's specified max speed - m_flPathMaxSpeed = m_pCurrentPathTarget->m_flSpeed; - - variant_t emptyVariant; - m_pCurrentPathTarget->AcceptInput( "InPass", this, this, emptyVariant, 0 ); - m_pLastPathTarget = m_pCurrentPathTarget; - } - - // NOTE: CurrentPathTarget doesn't mean the same thing as dest path target! - // It's the "next"most when moving forward + "prev"most when moving backward - // Must do the tests in the same space - pAdjustedDest = AdjustForMovementDirection( m_pDestPathTarget ); - - // Update our dest path target, if appropriate... - if ( m_pCurrentPathTarget == pAdjustedDest ) - { - m_bForcedMove = false; - SelectNewDestTarget(); - - // NOTE: Must do this again since SelectNewDestTarget may change m_pDestPathTarget - pAdjustedDest = AdjustForMovementDirection( m_pDestPathTarget ); - } - - if ( m_pCurrentPathTarget != pAdjustedDest ) - { - // Update to the next path, if there is one... - m_pCurrentPathTarget = NextAlongCurrentPath( m_pCurrentPathTarget ); - if ( !m_pCurrentPathTarget ) - { - m_pCurrentPathTarget = m_pLastPathTarget; - } - } - else - { - // NOTE: Have to do this here because the NextAlongCurrentPath call above - // could make m_pCurrentPathTarget == m_pDestPathTarget. - // In this case, we're at rest (no patrolling behavior) - bRestingAtDest = true; - } - - if ( bRestingAtDest ) - { - // NOTE: Must use current path target, instead of dest - // to get the PreviousAlongCurrentPath working correctly - CPathTrack *pSegmentStart = PreviousAlongCurrentPath( m_pCurrentPathTarget ); - if ( !pSegmentStart ) - { - pSegmentStart = m_pCurrentPathTarget; - } - m_vecSegmentStartPoint = pSegmentStart->GetAbsOrigin(); - } - else - { - m_vecSegmentStartPoint = m_pLastPathTarget->GetAbsOrigin(); - } - -visualizeDebugInfo: - VisualizeDebugInfo( vecPoint, vecTarget ); -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CAI_TrackPather::UpdateTargetPositionLeading( void ) -{ - Vector targetPos; - if ( !GetTrackPatherTarget( &targetPos ) ) - return; - - // NOTE: FindClosestPointOnPath *always* returns the point on the "far", - // end of the line segment containing the closest point (namely the 'next' - // track, as opposed to the 'prev' track) - Vector vecClosestPoint, vecPathDir; - float flTargetDistanceFromPath; - CPathTrack *pNextPath = FindClosestPointOnPath( m_pCurrentPathTarget, - targetPos, &vecClosestPoint, &vecPathDir, &flTargetDistanceFromPath ); - - // This means that a valid path could not be found to our target! - if ( CPathTrack::ValidPath( pNextPath ) == NULL ) - return; - -// NDebugOverlay::Cross3D( vecClosestPoint, -Vector(24,24,24), Vector(24,24,24), 0, 255, 255, true, 0.1f ); -// NDebugOverlay::Cross3D( pNextPath->GetAbsOrigin(), -Vector(24,24,24), Vector(24,24,24), 255, 255, 0, true, 0.1f ); - - // Here's how far we are from the path - m_flTargetDistFromPath = flTargetDistanceFromPath; - m_vecTargetPathDir = vecPathDir; - - // Here's info about where the target is along the path - m_vecTargetPathPoint = vecClosestPoint; - m_pTargetNearestPath = pNextPath; - - // Find the best position to be on our path - // NOTE: This will *also* return a path track on the "far" end of the line segment - // containing the leading position, namely the "next" end of the segment as opposed - // to the "prev" end of the segment. - CPathTrack *pDest = ComputeLeadingPointAlongPath( vecClosestPoint, pNextPath, m_flLeadDistance, &targetPos ); - SetDesiredPosition( targetPos ); - - // We only want to switch movement directions when absolutely necessary - // so convert dest into a more appropriate value based on the current movement direction - if ( pDest != m_pDestPathTarget ) - { - // NOTE: This is really tricky + subtle - // For leading, we don't want to ever change direction when the current target == the - // adjusted destination target. Namely, if we're going forward, both dest + curr - // mean the "next"most node so we can compare them directly against eath other. - // If we're moving backward, dest means "next"most, but curr means "prev"most. - // We first have to adjust the dest to mean "prev"most, and then do the comparison. - // If the adjusted dest == curr, then maintain direction. Otherwise, use the forward along path test. - bool bMovingForward = m_bMovingForward; - CPathTrack *pAdjustedDest = AdjustForMovementDirection( pDest ); - if ( m_pCurrentPathTarget != pAdjustedDest ) - { - bMovingForward = IsForwardAlongPath( m_pCurrentPathTarget, pAdjustedDest ); - } - - if ( bMovingForward != m_bMovingForward ) - { - // As a result of the tricky note above, this should never occur - Assert( pAdjustedDest != m_pCurrentPathTarget ); - - // Oops! Need to reverse direction - m_bMovingForward = bMovingForward; - m_vecSegmentStartPoint = m_pCurrentPathTarget->GetAbsOrigin(); - m_pCurrentPathTarget = NextAlongCurrentPath( m_pCurrentPathTarget ); - } - m_pDestPathTarget = pDest; - } - -// NDebugOverlay::Cross3D( m_pCurrentPathTarget->GetAbsOrigin(), -Vector(36,36,36), Vector(36,36,36), 255, 0, 0, true, 0.1f ); -// NDebugOverlay::Cross3D( m_pDestPathTarget->GetAbsOrigin(), -Vector(48,48,48), Vector(48,48,48), 0, 255, 0, true, 0.1f ); -// NDebugOverlay::Cross3D( targetPos, -Vector(36,36,36), Vector(36,36,36), 0, 0, 255, true, 0.1f ); -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CAI_TrackPather::UpdateTargetPosition( void ) -{ - // Don't update our target if we're being told to go somewhere - if ( m_bForcedMove && !m_bPatrolBreakable ) - return; - - // Don't update our target if we're patrolling - if ( m_bPatrolling ) - { - // If we have an enemy, and our patrol is breakable, stop patrolling - if ( !m_bPatrolBreakable || !GetEnemy() ) - return; - - m_bPatrolling = false; - } - - Vector targetPos; - if ( !GetTrackPatherTarget( &targetPos ) ) - return; - - // Not time to update again - if ( m_flEnemyPathUpdateTime > gpGlobals->curtime ) - return; - - // See if the target has moved enough to make us recheck - float flDistSqr = ( targetPos - m_vecLastGoalCheckPosition ).LengthSqr(); - if ( flDistSqr < m_flTargetDistanceThreshold * m_flTargetDistanceThreshold ) - return; - - // Find the best position to be on our path - CPathTrack *pDest = BestPointOnPath( m_pCurrentPathTarget, targetPos, m_flAvoidDistance, true, m_bChooseFarthestPoint ); - - if ( CPathTrack::ValidPath( pDest ) == NULL ) - { - // This means that a valid path could not be found to our target! -// Assert(0); - return; - } - - if ( pDest != m_pDestPathTarget ) - { - // This is our new destination - bool bMovingForward = IsForwardAlongPath( m_pCurrentPathTarget, pDest ); - if ( bMovingForward != m_bMovingForward ) - { - // Oops! Need to reverse direction - m_bMovingForward = bMovingForward; - if ( pDest != m_pCurrentPathTarget ) - { - SetupNewCurrentTarget( NextAlongCurrentPath( m_pCurrentPathTarget ) ); - } - } - m_pDestPathTarget = pDest; - } - - // Keep this goal point for comparisons later - m_vecLastGoalCheckPosition = targetPos; - - // Only do this on set intervals - m_flEnemyPathUpdateTime = gpGlobals->curtime + 1.0f; -} - - -//------------------------------------------------------------------------------ -// Returns the direction of the path at the closest point to the target -//------------------------------------------------------------------------------ -const Vector &CAI_TrackPather::TargetPathDirection() const -{ - return m_vecTargetPathDir; -} - -const Vector &CAI_TrackPather::TargetPathAcrossDirection() const -{ - static Vector s_Result; - CrossProduct( m_vecTargetPathDir, Vector( 0, 0, 1 ), s_Result ); - return s_Result; -} - - -//------------------------------------------------------------------------------ -// Returns the speed of the target relative to the path -//------------------------------------------------------------------------------ -float CAI_TrackPather::TargetSpeedAlongPath() const -{ - if ( !GetEnemy() || !IsLeading() ) - return 0.0f; - - Vector vecSmoothedVelocity = GetEnemy()->GetSmoothedVelocity(); - return DotProduct( vecSmoothedVelocity, TargetPathDirection() ); -} - - -//------------------------------------------------------------------------------ -// Returns the speed of the target *across* the path -//------------------------------------------------------------------------------ -float CAI_TrackPather::TargetSpeedAcrossPath() const -{ - if ( !GetEnemy() || !IsLeading() ) - return 0.0f; - - Vector vecSmoothedVelocity = GetEnemy()->GetSmoothedVelocity(); - return DotProduct( vecSmoothedVelocity, TargetPathAcrossDirection() ); -} - - -//------------------------------------------------------------------------------ -// Returns the max distance we can be from the path -//------------------------------------------------------------------------------ -float CAI_TrackPather::MaxDistanceFromCurrentPath() const -{ - if ( !IsLeading() || !m_pCurrentPathTarget ) - return 0.0f; - - CPathTrack *pPrevPath = PreviousAlongCurrentPath( m_pCurrentPathTarget ); - if ( !pPrevPath ) - { - pPrevPath = m_pCurrentPathTarget; - } - - // NOTE: Can't use m_vecSegmentStartPoint because we don't have a radius defined for it - float t; - Vector vecTemp; - CalcClosestPointOnLine( GetAbsOrigin(), pPrevPath->GetAbsOrigin(), - m_pCurrentPathTarget->GetAbsOrigin(), vecTemp, &t ); - t = clamp( t, 0.0f, 1.0f ); - float flRadius = (1.0f - t) * pPrevPath->GetRadius() + t * m_pCurrentPathTarget->GetRadius(); - return flRadius; -} - - -//------------------------------------------------------------------------------ -// Purpose : A different version of the track pather which is more explicit about -// the meaning of dest, current, and prev path points -//------------------------------------------------------------------------------ -void CAI_TrackPather::UpdateTrackNavigation( void ) -{ - // No target? Use the string specified. We have no spawn method (sucky!!) so this is how that works - if ( ( CPathTrack::ValidPath( m_pDestPathTarget ) == NULL ) && ( m_target != NULL_STRING ) ) - { - FlyToPathTrack( m_target ); - m_target = NULL_STRING; - } - - if ( !IsLeading() ) - { - if ( !m_pCurrentPathTarget ) - return; - - // Updates our destination node if we're tracking something - UpdateTargetPosition(); - - // Move along our path towards our current destination - UpdateCurrentTarget(); - } - else - { - // Updates our destination position if we're leading something - UpdateTargetPositionLeading(); - - // Move along our path towards our current destination - UpdateCurrentTargetLeading(); - } -} - - -//------------------------------------------------------------------------------ -// Sets the farthest path distance -//------------------------------------------------------------------------------ -void CAI_TrackPather::SetFarthestPathDist( float flMaxPathDist ) -{ - m_flFarthestPathDist = flMaxPathDist; -} - - -//------------------------------------------------------------------------------ -// Sets up a new current path target -//------------------------------------------------------------------------------ -void CAI_TrackPather::SetupNewCurrentTarget( CPathTrack *pTrack ) -{ - Assert( pTrack ); - m_vecSegmentStartPoint = GetAbsOrigin(); - VectorMA( m_vecSegmentStartPoint, -2.0f, GetAbsVelocity(), m_vecSegmentStartSplinePoint ); - m_pCurrentPathTarget = pTrack; - SetDesiredPosition( m_pCurrentPathTarget->GetAbsOrigin() ); -} - - -//------------------------------------------------------------------------------ -// Moves to an explicit track point -//------------------------------------------------------------------------------ -void CAI_TrackPather::MoveToTrackPoint( CPathTrack *pTrack ) -{ - if ( IsOnSameTrack( pTrack, m_pDestPathTarget ) ) - { - // The track must be valid - if ( CPathTrack::ValidPath( pTrack ) == NULL ) - return; - - m_pDestPathTarget = pTrack; - m_bMovingForward = IsForwardAlongPath( m_pCurrentPathTarget, pTrack ); - m_bForcedMove = true; - } - else - { - CPathTrack *pClosestTrack = BestPointOnPath( pTrack, WorldSpaceCenter(), 0.0f, false, false ); - - // The track must be valid - if ( CPathTrack::ValidPath( pClosestTrack ) == NULL ) - return; - - SetupNewCurrentTarget( pClosestTrack ); - m_pDestPathTarget = pTrack; - m_bMovingForward = IsForwardAlongPath( pClosestTrack, pTrack ); - m_bForcedMove = true; - } -} - - -//------------------------------------------------------------------------------ -// Moves to the closest track point -//------------------------------------------------------------------------------ -void CAI_TrackPather::MoveToClosestTrackPoint( CPathTrack *pTrack ) -{ - if ( IsOnSameTrack( pTrack, m_pDestPathTarget ) ) - return; - - CPathTrack *pClosestTrack = BestPointOnPath( pTrack, WorldSpaceCenter(), 0.0f, false, false ); - - // The track must be valid - if ( CPathTrack::ValidPath( pClosestTrack ) == NULL ) - return; - - SetupNewCurrentTarget( pClosestTrack ); - m_pDestPathTarget = pClosestTrack; - m_bMovingForward = true; - - // Force us to switch tracks if we're leading - if ( IsLeading() ) - { - m_bForcedMove = true; - } -} - - -//----------------------------------------------------------------------------- -// Are the two path tracks connected? -//----------------------------------------------------------------------------- -bool CAI_TrackPather::IsOnSameTrack( CPathTrack *pPath1, CPathTrack *pPath2 ) const -{ - if ( pPath1 == pPath2 ) - return true; - - { - BEGIN_PATH_TRACK_ITERATION(); - CPathTrack *pTravPath = pPath1->GetPrevious(); - while( CPathTrack::ValidPath( pTravPath ) && (pTravPath != pPath1) ) - { - // Circular loop checking - if ( pTravPath->HasBeenVisited() ) - break; - - pTravPath->Visit(); - - if ( pTravPath == pPath2 ) - return true; - - pTravPath = pTravPath->GetPrevious(); - } - } - - { - BEGIN_PATH_TRACK_ITERATION(); - CPathTrack *pTravPath = pPath1->GetNext(); - while( CPathTrack::ValidPath( pTravPath ) && (pTravPath != pPath1) ) - { - // Circular loop checking - if ( pTravPath->HasBeenVisited() ) - break; - - pTravPath->Visit(); - - if ( pTravPath == pPath2 ) - return true; - - pTravPath = pTravPath->GetNext(); - } - } - - return false; -} - - -//----------------------------------------------------------------------------- -// Deal with teleportation -//----------------------------------------------------------------------------- -void CAI_TrackPather::Teleported() -{ - // This updates the paths so they are reasonable - CPathTrack *pClosestTrack = BestPointOnPath( GetDestPathTarget(), WorldSpaceCenter(), 0.0f, false, false ); - m_pDestPathTarget = NULL; - MoveToClosestTrackPoint( pClosestTrack ); -} - - -//----------------------------------------------------------------------------- -// Returns distance along path to target, returns FLT_MAX if there's no path -//----------------------------------------------------------------------------- -float CAI_TrackPather::ComputePathDistance( CPathTrack *pPath, CPathTrack *pDest, bool bForward ) const -{ - float flDist = 0.0f; - CPathTrack *pLast = pPath; - - BEGIN_PATH_TRACK_ITERATION(); - while ( CPathTrack::ValidPath( pPath ) ) - { - // Ciruclar loop checking - if ( pPath->HasBeenVisited() ) - return FLT_MAX; - - pPath->Visit(); - - flDist += pLast->GetAbsOrigin().DistTo( pPath->GetAbsOrigin() ); - - if ( pDest == pPath ) - return flDist; - - pLast = pPath; - pPath = bForward ? pPath->GetNext() : pPath->GetPrevious(); - } - - return FLT_MAX; -} - - -//----------------------------------------------------------------------------- -// Is pPathTest in "front" of pPath on the same path? (Namely, does GetNext() get us there?) -//----------------------------------------------------------------------------- -bool CAI_TrackPather::IsForwardAlongPath( CPathTrack *pPath, CPathTrack *pPathTest ) const -{ - // Also, in the case of looping paths, we want to return the shortest path - float flForwardDist = ComputePathDistance( pPath, pPathTest, true ); - float flReverseDist = ComputePathDistance( pPath, pPathTest, false ); - - Assert( ( flForwardDist != FLT_MAX ) || ( flReverseDist != FLT_MAX ) ); - return ( flForwardDist <= flReverseDist ); -} - - -//----------------------------------------------------------------------------- -// Computes distance + nearest point from the current path.. -//----------------------------------------------------------------------------- -float CAI_TrackPather::ClosestPointToCurrentPath( Vector *pVecPoint ) const -{ - if (!m_pCurrentPathTarget) - { - *pVecPoint = GetAbsOrigin(); - return 0; - } - - float t; - CalcClosestPointOnLine( GetAbsOrigin(), m_vecSegmentStartPoint, - m_pCurrentPathTarget->GetAbsOrigin(), *pVecPoint, &t ); - return t; -} - - -//----------------------------------------------------------------------------- -// Computes a "path" velocity at a particular point along the current path -//----------------------------------------------------------------------------- -void CAI_TrackPather::ComputePathTangent( float t, Vector *pVecTangent ) const -{ - CPathTrack *pNextTrack = NextAlongCurrentPath(m_pCurrentPathTarget); - if ( !pNextTrack ) - { - pNextTrack = m_pCurrentPathTarget; - } - - t = clamp( t, 0.0f, 1.0f ); - pVecTangent->Init(0,0,0); - Catmull_Rom_Spline_Tangent( m_vecSegmentStartSplinePoint, m_vecSegmentStartPoint, - m_pCurrentPathTarget->GetAbsOrigin(), pNextTrack->GetAbsOrigin(), t, *pVecTangent ); - VectorNormalize( *pVecTangent ); -} - - -//----------------------------------------------------------------------------- -// Computes the *normalized* velocity at which the helicopter should approach the final point -//----------------------------------------------------------------------------- -void CAI_TrackPather::ComputeNormalizedDestVelocity( Vector *pVecVelocity ) const -{ - if ( m_nPauseState != PAUSE_NO_PAUSE ) - { - pVecVelocity->Init(0,0,0); - return; - } - - CPathTrack *pNextTrack = NextAlongCurrentPath(m_pCurrentPathTarget); - if ( !pNextTrack ) - { - pNextTrack = m_pCurrentPathTarget; - } - - if ( ( pNextTrack == m_pCurrentPathTarget ) || ( m_pCurrentPathTarget == m_pDestPathTarget ) ) - { - pVecVelocity->Init(0,0,0); - return; - } - - VectorSubtract( pNextTrack->GetAbsOrigin(), m_pCurrentPathTarget->GetAbsOrigin(), *pVecVelocity ); - VectorNormalize( *pVecVelocity ); - - // Slow it down if we're approaching a sharp corner - Vector vecDelta; - VectorSubtract( m_pCurrentPathTarget->GetAbsOrigin(), m_vecSegmentStartPoint, vecDelta ); - VectorNormalize( vecDelta ); - float flDot = DotProduct( *pVecVelocity, vecDelta ); - *pVecVelocity *= clamp( flDot, 0.0f, 1.0f ); -} - - -//----------------------------------------------------------------------------- -// Purpose: -// Input : &inputdata - -//----------------------------------------------------------------------------- -void CAI_TrackPather::SetTrack( CBaseEntity *pGoalEnt ) -{ - // Ignore this input if we're *already* on that path. - CPathTrack *pTrack = dynamic_cast(pGoalEnt); - if ( !pTrack ) - { - DevWarning( "%s: Specified entity '%s' must be a path_track!\n", pGoalEnt->GetClassname(), pGoalEnt->GetEntityName() ); - return; - } - - MoveToClosestTrackPoint( pTrack ); -} - -void CAI_TrackPather::SetTrack( string_t strTrackName ) -{ - // Find our specified target - CBaseEntity *pGoalEnt = gEntList.FindEntityByName( NULL, strTrackName ); - if ( pGoalEnt == NULL ) - { - DevWarning( "%s: Could not find path_track '%s'!\n", GetClassname(), STRING( strTrackName ) ); - return; - } - - SetTrack( pGoalEnt ); -} - - -//----------------------------------------------------------------------------- -// Purpose: -// Input : &inputdata - -//----------------------------------------------------------------------------- -void CAI_TrackPather::InputSetTrack( inputdata_t &inputdata ) -{ - string_t strTrackName = MAKE_STRING( inputdata.value.String() ); - SetTrack( MAKE_STRING( inputdata.value.String() ) ); -} - - -//----------------------------------------------------------------------------- -// Purpose: -// Input : strTrackName - -//----------------------------------------------------------------------------- -void CAI_TrackPather::FlyToPathTrack( string_t strTrackName ) -{ - CBaseEntity *pGoalEnt = gEntList.FindEntityByName( NULL, strTrackName ); - if ( pGoalEnt == NULL ) - { - DevWarning( "%s: Could not find path_track '%s'!\n", GetClassname(), STRING( strTrackName ) ); - return; - } - - // Ignore this input if we're *already* on that path. - CPathTrack *pTrack = dynamic_cast(pGoalEnt); - if ( !pTrack ) - { - DevWarning( "%s: Specified entity '%s' must be a path_track!\n", GetClassname(), STRING( strTrackName ) ); - return; - } - - // Find our specified target - MoveToTrackPoint( pTrack ); -} - - -//----------------------------------------------------------------------------- -// Purpose: -// Input : &inputdata - -//----------------------------------------------------------------------------- -void CAI_TrackPather::InputFlyToPathTrack( inputdata_t &inputdata ) -{ - // Find our specified target - string_t strTrackName = MAKE_STRING( inputdata.value.String() ); - FlyToPathTrack( strTrackName ); -} - - -//----------------------------------------------------------------------------- -// Changes the mode used to determine which path point to move to -//----------------------------------------------------------------------------- -void CAI_TrackPather::InputChooseFarthestPathPoint( inputdata_t &inputdata ) -{ - UseFarthestPathPoint( true ); -} - -void CAI_TrackPather::InputChooseNearestPathPoint( inputdata_t &inputdata ) -{ - UseFarthestPathPoint( false ); -} - -void CAI_TrackPather::UseFarthestPathPoint( bool useFarthest ) -{ - m_bChooseFarthestPoint = useFarthest; -} +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "cbase.h" + +#include "trains.h" +#include "ai_trackpather.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +#define TRACKPATHER_DEBUG_LEADING 1 +#define TRACKPATHER_DEBUG_PATH 2 +#define TRACKPATHER_DEBUG_TRACKS 3 +ConVar g_debug_trackpather( "g_debug_trackpather", "0", FCVAR_CHEAT ); + +//------------------------------------------------------------------------------ + +BEGIN_DATADESC( CAI_TrackPather ) + DEFINE_FIELD( m_vecDesiredPosition, FIELD_POSITION_VECTOR ), + DEFINE_FIELD( m_vecGoalOrientation, FIELD_VECTOR ), + + DEFINE_FIELD( m_pCurrentPathTarget, FIELD_CLASSPTR ), + DEFINE_FIELD( m_pDestPathTarget, FIELD_CLASSPTR ), + DEFINE_FIELD( m_pLastPathTarget, FIELD_CLASSPTR ), + DEFINE_FIELD( m_pTargetNearestPath, FIELD_CLASSPTR ), + + DEFINE_FIELD( m_strCurrentPathName, FIELD_STRING ), + DEFINE_FIELD( m_strDestPathName, FIELD_STRING ), + DEFINE_FIELD( m_strLastPathName, FIELD_STRING ), + DEFINE_FIELD( m_strTargetNearestPathName, FIELD_STRING ), + + DEFINE_FIELD( m_vecLastGoalCheckPosition, FIELD_POSITION_VECTOR ), + DEFINE_FIELD( m_flEnemyPathUpdateTime, FIELD_TIME ), + DEFINE_FIELD( m_bForcedMove, FIELD_BOOLEAN ), + DEFINE_FIELD( m_bPatrolling, FIELD_BOOLEAN ), + DEFINE_FIELD( m_bPatrolBreakable, FIELD_BOOLEAN ), + DEFINE_FIELD( m_bLeading, FIELD_BOOLEAN ), + + // Derived class pathing data + DEFINE_FIELD( m_flTargetDistanceThreshold, FIELD_FLOAT ), + DEFINE_FIELD( m_flAvoidDistance, FIELD_FLOAT ), + + DEFINE_FIELD( m_flTargetTolerance, FIELD_FLOAT ), + DEFINE_FIELD( m_vecSegmentStartPoint, FIELD_POSITION_VECTOR ), + DEFINE_FIELD( m_vecSegmentStartSplinePoint, FIELD_POSITION_VECTOR ), + DEFINE_FIELD( m_bMovingForward, FIELD_BOOLEAN ), + DEFINE_FIELD( m_bChooseFarthestPoint, FIELD_BOOLEAN ), + DEFINE_FIELD( m_flFarthestPathDist, FIELD_FLOAT ), + DEFINE_FIELD( m_flPathMaxSpeed, FIELD_FLOAT ), + DEFINE_FIELD( m_flTargetDistFromPath, FIELD_FLOAT ), + DEFINE_FIELD( m_flLeadDistance, FIELD_FLOAT ), + DEFINE_FIELD( m_vecTargetPathDir, FIELD_VECTOR ), + DEFINE_FIELD( m_vecTargetPathPoint, FIELD_POSITION_VECTOR ), + + DEFINE_FIELD( m_nPauseState, FIELD_INTEGER ), + + // Inputs + DEFINE_INPUTFUNC( FIELD_STRING, "SetTrack", InputSetTrack ), + DEFINE_INPUTFUNC( FIELD_STRING, "FlyToSpecificTrackViaPath", InputFlyToPathTrack ), + DEFINE_INPUTFUNC( FIELD_VOID, "StartPatrol", InputStartPatrol ), + DEFINE_INPUTFUNC( FIELD_VOID, "StopPatrol", InputStopPatrol ), + DEFINE_INPUTFUNC( FIELD_VOID, "StartBreakableMovement", InputStartBreakableMovement ), + DEFINE_INPUTFUNC( FIELD_VOID, "StopBreakableMovement", InputStopBreakableMovement ), + DEFINE_INPUTFUNC( FIELD_VOID, "ChooseFarthestPathPoint", InputChooseFarthestPathPoint ), + DEFINE_INPUTFUNC( FIELD_VOID, "ChooseNearestPathPoint", InputChooseNearestPathPoint ), + DEFINE_INPUTFUNC( FIELD_INTEGER,"InputStartLeading", InputStartLeading ), + DEFINE_INPUTFUNC( FIELD_VOID, "InputStopLeading", InputStopLeading ), + + // Obsolete, for backwards compatibility + DEFINE_INPUTFUNC( FIELD_VOID, "StartPatrolBreakable", InputStartPatrolBreakable ), + DEFINE_INPUTFUNC( FIELD_STRING, "FlyToPathTrack", InputFlyToPathTrack ), +END_DATADESC() + + +//----------------------------------------------------------------------------- +// Purpose: Initialize pathing data +//----------------------------------------------------------------------------- +void CAI_TrackPather::InitPathingData( float flTrackArrivalTolerance, float flTargetDistance, float flAvoidDistance ) +{ + m_flTargetTolerance = flTrackArrivalTolerance; + m_flTargetDistanceThreshold = flTargetDistance; + m_flAvoidDistance = flAvoidDistance; + + m_pCurrentPathTarget = NULL; + m_pDestPathTarget = NULL; + m_pLastPathTarget = NULL; + m_pTargetNearestPath = NULL; + m_bLeading = false; + + m_flEnemyPathUpdateTime = gpGlobals->curtime; + m_bForcedMove = false; + m_bPatrolling = false; + m_bPatrolBreakable = false; + m_flLeadDistance = 0.0f; + m_bMovingForward = true; + m_vecSegmentStartPoint = m_vecSegmentStartSplinePoint = m_vecDesiredPosition = GetAbsOrigin(); + m_bChooseFarthestPoint = true; + m_flFarthestPathDist = 1e10; + m_flPathMaxSpeed = 0; + m_nPauseState = PAUSE_NO_PAUSE; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CAI_TrackPather::OnRestore( void ) +{ + BaseClass::OnRestore(); + + // Restore current path + if ( m_strCurrentPathName != NULL_STRING ) + { + m_pCurrentPathTarget = (CPathTrack *) gEntList.FindEntityByName( NULL, m_strCurrentPathName ); + } + else + { + m_pCurrentPathTarget = NULL; + } + + // Restore destination path + if ( m_strDestPathName != NULL_STRING ) + { + m_pDestPathTarget = (CPathTrack *) gEntList.FindEntityByName( NULL, m_strDestPathName ); + } + else + { + m_pDestPathTarget = NULL; + } + + // Restore last path + if ( m_strLastPathName != NULL_STRING ) + { + m_pLastPathTarget = (CPathTrack *) gEntList.FindEntityByName( NULL, m_strLastPathName ); + } + else + { + m_pLastPathTarget = NULL; + } + + // Restore target nearest path + if ( m_strTargetNearestPathName != NULL_STRING ) + { + m_pTargetNearestPath = (CPathTrack *) gEntList.FindEntityByName( NULL, m_strTargetNearestPathName ); + } + else + { + m_pTargetNearestPath = NULL; + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CAI_TrackPather::OnSave( IEntitySaveUtils *pUtils ) +{ + BaseClass::OnSave( pUtils ); + + // Stash all the paths into strings for restoration later + m_strCurrentPathName = ( m_pCurrentPathTarget != NULL ) ? m_pCurrentPathTarget->GetEntityName() : NULL_STRING; + m_strDestPathName = ( m_pDestPathTarget != NULL ) ? m_pDestPathTarget->GetEntityName() : NULL_STRING; + m_strLastPathName = ( m_pLastPathTarget != NULL ) ? m_pLastPathTarget->GetEntityName() : NULL_STRING; + m_strTargetNearestPathName = ( m_pTargetNearestPath != NULL ) ? m_pTargetNearestPath->GetEntityName() : NULL_STRING; +} + + +//----------------------------------------------------------------------------- +// Leading distance +//----------------------------------------------------------------------------- +void CAI_TrackPather::EnableLeading( bool bEnable ) +{ + m_bLeading = bEnable; + if ( m_bLeading ) + { + m_bPatrolling = false; + } +} + +void CAI_TrackPather::SetLeadingDistance( float flLeadDistance ) +{ + m_flLeadDistance = flLeadDistance; +} + +float CAI_TrackPather::GetLeadingDistance( ) const +{ + return m_flLeadDistance; +} + + +//----------------------------------------------------------------------------- +// Returns the next path along our current path +//----------------------------------------------------------------------------- +inline CPathTrack *CAI_TrackPather::NextAlongCurrentPath( CPathTrack *pPath ) const +{ + return CPathTrack::ValidPath( m_bMovingForward ? pPath->GetNext() : pPath->GetPrevious() ); +} + +inline CPathTrack *CAI_TrackPather::PreviousAlongCurrentPath( CPathTrack *pPath ) const +{ + return CPathTrack::ValidPath( m_bMovingForward ? pPath->GetPrevious() : pPath->GetNext() ); +} + +inline CPathTrack *CAI_TrackPather::AdjustForMovementDirection( CPathTrack *pPath ) const +{ + if ( !m_bMovingForward && CPathTrack::ValidPath( pPath->GetPrevious( ) ) ) + { + pPath = CPathTrack::ValidPath( pPath->GetPrevious() ); + } + return pPath; +} + + +//----------------------------------------------------------------------------- +// Enemy visibility check +//----------------------------------------------------------------------------- +CBaseEntity *CAI_TrackPather::FindTrackBlocker( const Vector &vecViewPoint, const Vector &vecTargetPos ) +{ + trace_t tr; + AI_TraceHull( vecViewPoint, vecTargetPos, -Vector(4,4,4), Vector(4,4,4), MASK_SHOT, this, COLLISION_GROUP_NONE, &tr ); + return (tr.fraction != 1.0f) ? tr.m_pEnt : NULL; +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : &targetPos - +// Output : CBaseEntity +//----------------------------------------------------------------------------- +CPathTrack *CAI_TrackPather::BestPointOnPath( CPathTrack *pPath, const Vector &targetPos, float flAvoidRadius, bool visible, bool bFarthestPoint ) +{ + // Find the node nearest to the destination path target if a path is not specified + if ( pPath == NULL ) + { + pPath = m_pDestPathTarget; + } + + // If the path node we're trying to use is not valid, then we're done. + if ( CPathTrack::ValidPath( pPath ) == NULL ) + { + //FIXME: Implement + Assert(0); + return NULL; + } + + // Our target may be in a vehicle + CBaseEntity *pVehicle = NULL; + CBaseEntity *pTargetEnt = GetTrackPatherTargetEnt(); + if ( pTargetEnt != NULL ) + { + CBaseCombatCharacter *pCCTarget = pTargetEnt->MyCombatCharacterPointer(); + if ( pCCTarget != NULL && pCCTarget->IsInAVehicle() ) + { + pVehicle = pCCTarget->GetVehicleEntity(); + } + } + + // Faster math... + flAvoidRadius *= flAvoidRadius; + + // Find the nearest node to the target (going forward) + CPathTrack *pNearestPath = NULL; + float flNearestDist = bFarthestPoint ? 0 : 999999999; + float flPathDist; + + float flFarthestDistSqr = ( m_flFarthestPathDist - 2.0f * m_flTargetDistanceThreshold ); + flFarthestDistSqr *= flFarthestDistSqr; + + // NOTE: Gotta do it this crazy way because paths can be one-way. + for ( int i = 0; i < 2; ++i ) + { + int loopCheck = 0; + CPathTrack *pTravPath = pPath; + CPathTrack *pNextPath; + + BEGIN_PATH_TRACK_ITERATION(); + for ( ; CPathTrack::ValidPath( pTravPath ); pTravPath = pNextPath, loopCheck++ ) + { + // Circular loop checking + if ( pTravPath->HasBeenVisited() ) + break; + + pTravPath->Visit(); + + pNextPath = (i == 0) ? pTravPath->GetPrevious() : pTravPath->GetNext(); + + // Find the distance between this test point and our goal point + flPathDist = ( pTravPath->GetAbsOrigin() - targetPos ).LengthSqr(); + + // See if it's closer and it's also not within our avoidance radius + if ( bFarthestPoint ) + { + if ( ( flPathDist <= flNearestDist ) && ( flNearestDist <= flFarthestDistSqr ) ) + continue; + } + else + { + if ( flPathDist >= flNearestDist ) + continue; + } + + // Don't choose points that are within the avoid radius + if ( flAvoidRadius && ( pTravPath->GetAbsOrigin() - targetPos ).Length2DSqr() <= flAvoidRadius ) + continue; + + if ( visible ) + { + // If it has to be visible, run those checks + CBaseEntity *pBlocker = FindTrackBlocker( pTravPath->GetAbsOrigin(), targetPos ); + + // Check to see if we've hit the target, or the player's vehicle if it's a player in a vehicle + bool bHitTarget = ( pTargetEnt && ( pTargetEnt == pBlocker ) ) || + ( pVehicle && ( pVehicle == pBlocker ) ); + + // If we hit something, and it wasn't the target or his vehicle, then no dice + // If we hit the target and forced move was set, *still* no dice + if ( (pBlocker != NULL) && ( !bHitTarget || m_bForcedMove ) ) + continue; + } + + pNearestPath = pTravPath; + flNearestDist = flPathDist; + } + } + + return pNearestPath; +} + + +//----------------------------------------------------------------------------- +// Compute a point n units along a path +//----------------------------------------------------------------------------- +CPathTrack *CAI_TrackPather::ComputeLeadingPointAlongPath( const Vector &vecStartPoint, + CPathTrack *pFirstTrack, float flDistance, Vector *pTarget ) +{ + bool bMovingForward = (flDistance > 0.0f); + flDistance = fabs(flDistance); + + CPathTrack *pTravPath = pFirstTrack; + if ( (!bMovingForward) && pFirstTrack->GetPrevious() ) + { + pTravPath = pFirstTrack->GetPrevious(); + } + + *pTarget = vecStartPoint; + CPathTrack *pNextPath; + + // No circular loop checking needed; eventually, it'll run out of distance + for ( ; CPathTrack::ValidPath( pTravPath ); pTravPath = pNextPath ) + { + pNextPath = bMovingForward ? pTravPath->GetNext() : pTravPath->GetPrevious(); + + // Find the distance between this test point and our goal point + float flPathDist = pTarget->DistTo( pTravPath->GetAbsOrigin() ); + + // Find the distance between this test point and our goal point + if ( flPathDist <= flDistance ) + { + flDistance -= flPathDist; + *pTarget = pTravPath->GetAbsOrigin(); + if ( !CPathTrack::ValidPath(pNextPath) ) + return bMovingForward ? pTravPath : pTravPath->GetNext(); + + continue; + } + + ComputeClosestPoint( *pTarget, flDistance, pTravPath->GetAbsOrigin(), pTarget ); + return bMovingForward ? pTravPath : pTravPath->GetNext(); + } + + return NULL; +} + + +//----------------------------------------------------------------------------- +// Compute the distance to a particular point on the path +//----------------------------------------------------------------------------- +float CAI_TrackPather::ComputeDistanceAlongPathToPoint( CPathTrack *pStartTrack, + CPathTrack *pDestTrack, const Vector &vecDestPosition, bool bMovingForward ) +{ + float flTotalDist = 0.0f; + + Vector vecPoint; + ClosestPointToCurrentPath( &vecPoint ); + + CPathTrack *pTravPath = pStartTrack; + CPathTrack *pNextPath, *pTestPath; + BEGIN_PATH_TRACK_ITERATION(); + for ( ; CPathTrack::ValidPath( pTravPath ); pTravPath = pNextPath ) + { + // Circular loop checking + if ( pTravPath->HasBeenVisited() ) + break; + + // Mark it as being visited. + pTravPath->Visit(); + + pNextPath = bMovingForward ? pTravPath->GetNext() : pTravPath->GetPrevious(); + pTestPath = pTravPath; + Assert( pTestPath ); + + if ( pTravPath == pDestTrack ) + { + Vector vecDelta; + Vector vecPathDelta; + VectorSubtract( vecDestPosition, vecPoint, vecDelta ); + ComputePathDirection( pTravPath, &vecPathDelta ); + float flDot = DotProduct( vecDelta, vecPathDelta ); + flTotalDist += (flDot > 0.0f ? 1.0f : -1.0f) * vecDelta.Length2D(); + break; + } + + // NOTE: This would be made more accurate if we did the path direction check here too. + // The starting vecPoint is sometimes *not* within the bounds of the line segment. + + // Find the distance between this test point and our goal point + flTotalDist += (bMovingForward ? 1.0f : -1.0f) * vecPoint.AsVector2D().DistTo( pTestPath->GetAbsOrigin().AsVector2D() ); + vecPoint = pTestPath->GetAbsOrigin(); + } + + return flTotalDist; +} + + +//------------------------------------------------------------------------------ +// Track debugging info +//------------------------------------------------------------------------------ +void CAI_TrackPather::VisualizeDebugInfo( const Vector &vecNearestPoint, const Vector &vecTarget ) +{ + if ( g_debug_trackpather.GetInt() == TRACKPATHER_DEBUG_PATH ) + { + NDebugOverlay::Line( m_vecSegmentStartPoint, vecTarget, 0, 0, 255, true, 0.1f ); + NDebugOverlay::Cross3D( vecNearestPoint, -Vector(16,16,16), Vector(16,16,16), 255, 0, 0, true, 0.1f ); + NDebugOverlay::Cross3D( m_pCurrentPathTarget->GetAbsOrigin(), -Vector(16,16,16), Vector(16,16,16), 0, 255, 0, true, 0.1f ); + NDebugOverlay::Cross3D( m_vecDesiredPosition, -Vector(16,16,16), Vector(16,16,16), 0, 0, 255, true, 0.1f ); + NDebugOverlay::Cross3D( m_pDestPathTarget->GetAbsOrigin(), -Vector(16,16,16), Vector(16,16,16), 255, 255, 255, true, 0.1f ); + + if ( m_pTargetNearestPath ) + { + NDebugOverlay::Cross3D( m_pTargetNearestPath->GetAbsOrigin(), -Vector(24,24,24), Vector(24,24,24), 255, 0, 255, true, 0.1f ); + } + } + + if ( g_debug_trackpather.GetInt() == TRACKPATHER_DEBUG_TRACKS ) + { + if ( m_pCurrentPathTarget ) + { + CPathTrack *pPathTrack = m_pCurrentPathTarget; + for ( ; CPathTrack::ValidPath( pPathTrack ); pPathTrack = pPathTrack->GetNext() ) + { + NDebugOverlay::Box( pPathTrack->GetAbsOrigin(), -Vector(2,2,2), Vector(2,2,2), 0,255, 0, 8, 0.1 ); + if ( CPathTrack::ValidPath( pPathTrack->GetNext() ) ) + { + NDebugOverlay::Line( pPathTrack->GetAbsOrigin(), pPathTrack->GetNext()->GetAbsOrigin(), 0,255,0, true, 0.1 ); + } + + if ( pPathTrack->GetNext() == m_pCurrentPathTarget ) + break; + } + } + } +} + + +//------------------------------------------------------------------------------ +// Does this path track have LOS to the target? +//------------------------------------------------------------------------------ +bool CAI_TrackPather::HasLOSToTarget( CPathTrack *pTrack ) +{ + CBaseEntity *pTargetEnt = GetTrackPatherTargetEnt(); + if ( !pTargetEnt ) + return true; + + Vector targetPos; + if ( !GetTrackPatherTarget( &targetPos ) ) + return true; + + // Translate driver into vehicle for testing + CBaseEntity *pVehicle = NULL; + CBaseCombatCharacter *pCCTarget = pTargetEnt->MyCombatCharacterPointer(); + if ( pCCTarget != NULL && pCCTarget->IsInAVehicle() ) + { + pVehicle = pCCTarget->GetVehicleEntity(); + } + + // If it has to be visible, run those checks + CBaseEntity *pBlocker = FindTrackBlocker( pTrack->GetAbsOrigin(), targetPos ); + + // Check to see if we've hit the target, or the player's vehicle if it's a player in a vehicle + bool bHitTarget = ( pTargetEnt && ( pTargetEnt == pBlocker ) ) || + ( pVehicle && ( pVehicle == pBlocker ) ); + + return (pBlocker == NULL) || bHitTarget; +} + + +//------------------------------------------------------------------------------ +// Moves to the track +//------------------------------------------------------------------------------ +void CAI_TrackPather::UpdateCurrentTarget() +{ + // Find the point along the line that we're closest to. + const Vector &vecTarget = m_pCurrentPathTarget->GetAbsOrigin(); + Vector vecPoint; + float t = ClosestPointToCurrentPath( &vecPoint ); + if ( (t < 1.0f) && ( vecPoint.DistToSqr( vecTarget ) > m_flTargetTolerance * m_flTargetTolerance ) ) + goto visualizeDebugInfo; + + // Forced move is gone as soon as we've reached the first point on our path + if ( m_bLeading ) + { + m_bForcedMove = false; + } + + // Trip our "path_track reached" output + if ( m_pCurrentPathTarget != m_pLastPathTarget ) + { + // Get the path's specified max speed + m_flPathMaxSpeed = m_pCurrentPathTarget->m_flSpeed; + + variant_t emptyVariant; + m_pCurrentPathTarget->AcceptInput( "InPass", this, this, emptyVariant, 0 ); + m_pLastPathTarget = m_pCurrentPathTarget; + } + + if ( m_nPauseState == PAUSED_AT_POSITION ) + return; + + if ( m_nPauseState == PAUSE_AT_NEXT_LOS_POSITION ) + { + if ( HasLOSToTarget(m_pCurrentPathTarget) ) + { + m_nPauseState = PAUSED_AT_POSITION; + return; + } + } + + // Update our dest path target, if appropriate... + if ( m_pCurrentPathTarget == m_pDestPathTarget ) + { + m_bForcedMove = false; + SelectNewDestTarget(); + } + + // Did SelectNewDestTarget give us a new point to move to? + if ( m_pCurrentPathTarget != m_pDestPathTarget ) + { + // Update to the next path, if there is one... + m_pCurrentPathTarget = NextAlongCurrentPath( m_pCurrentPathTarget ); + if ( !m_pCurrentPathTarget ) + { + m_pCurrentPathTarget = m_pLastPathTarget; + } + } + else + { + // We're at rest (no patrolling behavior), which means we're moving forward now. + m_bMovingForward = true; + } + + SetDesiredPosition( m_pCurrentPathTarget->GetAbsOrigin() ); + m_vecSegmentStartSplinePoint = m_vecSegmentStartPoint; + m_vecSegmentStartPoint = m_pLastPathTarget->GetAbsOrigin(); + +visualizeDebugInfo: + VisualizeDebugInfo( vecPoint, vecTarget ); +} + + +//----------------------------------------------------------------------------- +// +// NOTE: All code below is used exclusively for leading/trailing behavior +// +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// Compute the distance to the leading position +//----------------------------------------------------------------------------- +float CAI_TrackPather::ComputeDistanceToLeadingPosition() +{ + return ComputeDistanceAlongPathToPoint( m_pCurrentPathTarget, m_pDestPathTarget, GetDesiredPosition(), m_bMovingForward ); +} + + +//----------------------------------------------------------------------------- +// Compute the distance to the *target* position +//----------------------------------------------------------------------------- +float CAI_TrackPather::ComputeDistanceToTargetPosition() +{ + Assert( m_pTargetNearestPath ); + + CPathTrack *pDest = m_bMovingForward ? m_pTargetNearestPath.Get() : m_pTargetNearestPath->GetPrevious(); + if ( !pDest ) + { + pDest = m_pTargetNearestPath; + } + bool bMovingForward = IsForwardAlongPath( m_pCurrentPathTarget, pDest ); + + CPathTrack *pStart = m_pCurrentPathTarget; + if ( bMovingForward != m_bMovingForward ) + { + if (bMovingForward) + { + if ( pStart->GetNext() ) + { + pStart = pStart->GetNext(); + } + if ( pDest->GetNext() ) + { + pDest = pDest->GetNext(); + } + } + else + { + if ( pStart->GetPrevious() ) + { + pStart = pStart->GetPrevious(); + } + if ( pDest->GetPrevious() ) + { + pDest = pDest->GetPrevious(); + } + } + } + + return ComputeDistanceAlongPathToPoint( pStart, pDest, m_vecTargetPathPoint, bMovingForward ); +} + + +//----------------------------------------------------------------------------- +// Compute a path direction +//----------------------------------------------------------------------------- +void CAI_TrackPather::ComputePathDirection( CPathTrack *pPath, Vector *pVecPathDir ) +{ + if ( pPath->GetPrevious() ) + { + VectorSubtract( pPath->GetAbsOrigin(), pPath->GetPrevious()->GetAbsOrigin(), *pVecPathDir ); + } + else + { + if ( pPath->GetNext() ) + { + VectorSubtract( pPath->GetNext()->GetAbsOrigin(), pPath->GetAbsOrigin(), *pVecPathDir ); + } + else + { + pVecPathDir->Init( 1, 0, 0 ); + } + } + VectorNormalize( *pVecPathDir ); +} + + +//----------------------------------------------------------------------------- +// What's the current path direction? +//----------------------------------------------------------------------------- +void CAI_TrackPather::CurrentPathDirection( Vector *pVecPathDir ) +{ + if ( m_pCurrentPathTarget ) + { + ComputePathDirection( m_pCurrentPathTarget, pVecPathDir ); + } + else + { + pVecPathDir->Init( 0, 0, 1 ); + } +} + + + +//----------------------------------------------------------------------------- +// Compute a point n units along the current path from our current position +// (but don't pass the desired target point) +//----------------------------------------------------------------------------- +void CAI_TrackPather::ComputePointAlongCurrentPath( float flDistance, float flPerpDist, Vector *pTarget ) +{ + Vector vecPathDir; + Vector vecStartPoint; + ClosestPointToCurrentPath( &vecStartPoint ); + *pTarget = vecStartPoint; + + if ( flDistance != 0.0f ) + { + Vector vecPrevPoint = vecStartPoint; + CPathTrack *pTravPath = m_pCurrentPathTarget; + CPathTrack *pAdjustedDest = AdjustForMovementDirection( m_pDestPathTarget ); + for ( ; CPathTrack::ValidPath( pTravPath ); pTravPath = NextAlongCurrentPath( pTravPath ) ) + { + if ( pTravPath == pAdjustedDest ) + { + ComputePathDirection( pTravPath, &vecPathDir ); + + float flPathDist = pTarget->DistTo( GetDesiredPosition() ); + if ( flDistance > flPathDist ) + { + *pTarget = GetDesiredPosition(); + } + else + { + ComputeClosestPoint( *pTarget, flDistance, GetDesiredPosition(), pTarget ); + } + break; + } + + // Find the distance between this test point and our goal point + float flPathDist = pTarget->DistTo( pTravPath->GetAbsOrigin() ); + + // Find the distance between this test point and our goal point + if ( flPathDist <= flDistance ) + { + flDistance -= flPathDist; + *pTarget = pTravPath->GetAbsOrigin(); + + // FIXME: Reduce the distance further based on the angle between this segment + the next + continue; + } + + ComputePathDirection( pTravPath, &vecPathDir ); + ComputeClosestPoint( *pTarget, flDistance, pTravPath->GetAbsOrigin(), pTarget ); + break; + } + } + else + { + VectorSubtract( m_pCurrentPathTarget->GetAbsOrigin(), m_vecSegmentStartPoint, vecPathDir ); + VectorNormalize( vecPathDir ); + } + + // Add in the horizontal component + ComputePointFromPerpDistance( *pTarget, vecPathDir, flPerpDist, pTarget ); +} + + +//----------------------------------------------------------------------------- +// Methods to find a signed perp distance from the track +// and to compute a point off the path based on the signed perp distance +//----------------------------------------------------------------------------- +float CAI_TrackPather::ComputePerpDistanceFromPath( const Vector &vecPointOnPath, const Vector &vecPathDir, const Vector &vecPointOffPath ) +{ + // Make it be a signed distance of the target from the path + // Positive means on the right side, negative means on the left side + Vector vecAcross, vecDelta; + CrossProduct( vecPathDir, Vector( 0, 0, 1 ), vecAcross ); + VectorSubtract( vecPointOffPath, vecPointOnPath, vecDelta ); + VectorMA( vecDelta, -DotProduct( vecPathDir, vecDelta ), vecPathDir, vecDelta ); + + float flDistanceFromPath = vecDelta.Length2D(); + if ( DotProduct2D( vecAcross.AsVector2D(), vecDelta.AsVector2D() ) < 0.0f ) + { + flDistanceFromPath *= -1.0f; + } + + return flDistanceFromPath; +} + +void CAI_TrackPather::ComputePointFromPerpDistance( const Vector &vecPointOnPath, const Vector &vecPathDir, float flPerpDist, Vector *pResult ) +{ + Vector vecAcross; + CrossProduct( vecPathDir, Vector( 0, 0, 1 ), vecAcross ); + VectorMA( vecPointOnPath, flPerpDist, vecAcross, *pResult ); +} + + +//----------------------------------------------------------------------------- +// Finds the closest point on the path, returns a signed perpendicular distance +// where negative means on the left side of the path (when travelled from prev to next) +// and positive means on the right side +//----------------------------------------------------------------------------- +CPathTrack *CAI_TrackPather::FindClosestPointOnPath( CPathTrack *pPath, + const Vector &targetPos, Vector *pVecClosestPoint, Vector *pVecPathDir, float *pDistanceFromPath ) +{ + // Find the node nearest to the destination path target if a path is not specified + if ( pPath == NULL ) + { + pPath = m_pDestPathTarget; + } + + // If the path node we're trying to use is not valid, then we're done. + if ( CPathTrack::ValidPath( pPath ) == NULL ) + { + //FIXME: Implement + Assert(0); + return NULL; + } + + // Find the nearest node to the target (going forward) + CPathTrack *pNearestPath = NULL; + float flNearestDist2D = 999999999; + float flNearestDist = 999999999; + float flPathDist, flPathDist2D; + + // NOTE: Gotta do it this crazy way because paths can be one-way. + Vector vecNearestPoint; + Vector vecNearestPathSegment; + for ( int i = 0; i < 2; ++i ) + { + int loopCheck = 0; + CPathTrack *pTravPath = pPath; + CPathTrack *pNextPath; + + BEGIN_PATH_TRACK_ITERATION(); + for ( ; CPathTrack::ValidPath( pTravPath ); pTravPath = pNextPath, loopCheck++ ) + { + // Circular loop checking + if ( pTravPath->HasBeenVisited() ) + break; + + // Mark it as being visited. + pTravPath->Visit(); + + pNextPath = (i == 0) ? pTravPath->GetPrevious() : pTravPath->GetNext(); + + // No alt paths allowed in leading mode. + if ( pTravPath->m_paltpath ) + { + Warning( "%s: Alternative paths in path_track not allowed when using the leading behavior!\n", STRING(GetEntityName()) ); + } + + // Need line segments + if ( !CPathTrack::ValidPath(pNextPath) ) + break; + + // Find the closest point on the line segment on the path + Vector vecClosest; + CalcClosestPointOnLineSegment( targetPos, pTravPath->GetAbsOrigin(), pNextPath->GetAbsOrigin(), vecClosest ); + + // Find the distance between this test point and our goal point + flPathDist2D = vecClosest.AsVector2D().DistToSqr( targetPos.AsVector2D() ); + if ( flPathDist2D > flNearestDist2D ) + continue; + + flPathDist = vecClosest.z - targetPos.z; + flPathDist *= flPathDist; + flPathDist += flPathDist2D; + if (( flPathDist2D == flNearestDist2D ) && ( flPathDist >= flNearestDist )) + continue; + + pNearestPath = (i == 0) ? pTravPath : pNextPath; + flNearestDist2D = flPathDist2D; + flNearestDist = flPathDist; + vecNearestPoint = vecClosest; + VectorSubtract( pNextPath->GetAbsOrigin(), pTravPath->GetAbsOrigin(), vecNearestPathSegment ); + if ( i == 0 ) + { + vecNearestPathSegment *= -1.0f; + } + } + } + + VectorNormalize( vecNearestPathSegment ); + *pDistanceFromPath = ComputePerpDistanceFromPath( vecNearestPoint, vecNearestPathSegment, targetPos ); + *pVecClosestPoint = vecNearestPoint; + *pVecPathDir = vecNearestPathSegment; + return pNearestPath; +} + + +//----------------------------------------------------------------------------- +// Breakable paths? +//----------------------------------------------------------------------------- +void CAI_TrackPather::InputStartBreakableMovement( inputdata_t &inputdata ) +{ + m_bPatrolBreakable = true; +} + +void CAI_TrackPather::InputStopBreakableMovement( inputdata_t &inputdata ) +{ + m_bPatrolBreakable = false; +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : &inputdata - +//----------------------------------------------------------------------------- +void CAI_TrackPather::InputStartPatrol( inputdata_t &inputdata ) +{ + m_bPatrolling = true; +} + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CAI_TrackPather::InputStopPatrol( inputdata_t &inputdata ) +{ + m_bPatrolling = false; +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : &inputdata - +//----------------------------------------------------------------------------- +void CAI_TrackPather::InputStartPatrolBreakable( inputdata_t &inputdata ) +{ + m_bPatrolBreakable = true; + m_bPatrolling = true; +} + + +//------------------------------------------------------------------------------ +// Leading behaviors +//------------------------------------------------------------------------------ +void CAI_TrackPather::InputStartLeading( inputdata_t &inputdata ) +{ + EnableLeading( true ); + SetLeadingDistance( inputdata.value.Int() ); +} + +void CAI_TrackPather::InputStopLeading( inputdata_t &inputdata ) +{ + EnableLeading( false ); +} + + +//------------------------------------------------------------------------------ +// Selects a new destination target +//------------------------------------------------------------------------------ +void CAI_TrackPather::SelectNewDestTarget() +{ + if ( !m_bPatrolling ) + return; + + // NOTE: This version is bugged, but I didn't want to make the fix + // here for fear of breaking a lot of maps late in the day. + // So, only the chopper does the "right" thing. +#ifdef HL2_EPISODIC + // Episodic uses the fixed logic for all trackpathers + if ( 1 ) +#else + if ( ShouldUseFixedPatrolLogic() ) +#endif + { + CPathTrack *pOldDest = m_pDestPathTarget; + + // Only switch polarity of movement if we're at the *end* of the path + // This is really useful for initial conditions of patrolling + // NOTE: We've got to do some extra work for circular paths + bool bIsCircular = false; + { + BEGIN_PATH_TRACK_ITERATION(); + CPathTrack *pTravPath = m_pDestPathTarget; + while( CPathTrack::ValidPath( pTravPath ) ) + { + // Circular loop checking + if ( pTravPath->HasBeenVisited() ) + { + bIsCircular = true; + break; + } + + pTravPath->Visit(); + pTravPath = NextAlongCurrentPath( pTravPath ); + } + } + + if ( bIsCircular || (NextAlongCurrentPath( m_pDestPathTarget ) == NULL) ) + { + m_bMovingForward = !m_bMovingForward; + } + + BEGIN_PATH_TRACK_ITERATION(); + + while ( true ) + { + CPathTrack *pNextTrack = NextAlongCurrentPath( m_pDestPathTarget ); + if ( !pNextTrack || (pNextTrack == pOldDest) || pNextTrack->HasBeenVisited() ) + break; + + pNextTrack->Visit(); + m_pDestPathTarget = pNextTrack; + } + } + else + { + CPathTrack *pOldDest = m_pDestPathTarget; + + // For patrolling, switch the polarity of movement + m_bMovingForward = !m_bMovingForward; + + int loopCount = 0; + while ( true ) + { + CPathTrack *pNextTrack = NextAlongCurrentPath( m_pDestPathTarget ); + if ( !pNextTrack ) + break; + if ( ++loopCount > 1024 ) + { + DevMsg(1,"WARNING: Looping path for %s\n", GetDebugName() ); + break; + } + + m_pDestPathTarget = pNextTrack; + } + + if ( m_pDestPathTarget == pOldDest ) + { + // This can occur if we move to the first point on the path + SelectNewDestTarget(); + } + } +} + + +//------------------------------------------------------------------------------ +// Moves to the track +//------------------------------------------------------------------------------ +void CAI_TrackPather::UpdateCurrentTargetLeading() +{ + bool bRestingAtDest = false; + CPathTrack *pAdjustedDest; + + // Find the point along the line that we're closest to. + const Vector &vecTarget = m_pCurrentPathTarget->GetAbsOrigin(); + Vector vecPoint; + float t = ClosestPointToCurrentPath( &vecPoint ); + if ( (t < 1.0f) && ( vecPoint.DistToSqr( vecTarget ) > m_flTargetTolerance * m_flTargetTolerance ) ) + goto visualizeDebugInfo; + + // Trip our "path_track reached" output + if ( m_pCurrentPathTarget != m_pLastPathTarget ) + { + // Get the path's specified max speed + m_flPathMaxSpeed = m_pCurrentPathTarget->m_flSpeed; + + variant_t emptyVariant; + m_pCurrentPathTarget->AcceptInput( "InPass", this, this, emptyVariant, 0 ); + m_pLastPathTarget = m_pCurrentPathTarget; + } + + // NOTE: CurrentPathTarget doesn't mean the same thing as dest path target! + // It's the "next"most when moving forward + "prev"most when moving backward + // Must do the tests in the same space + pAdjustedDest = AdjustForMovementDirection( m_pDestPathTarget ); + + // Update our dest path target, if appropriate... + if ( m_pCurrentPathTarget == pAdjustedDest ) + { + m_bForcedMove = false; + SelectNewDestTarget(); + + // NOTE: Must do this again since SelectNewDestTarget may change m_pDestPathTarget + pAdjustedDest = AdjustForMovementDirection( m_pDestPathTarget ); + } + + if ( m_pCurrentPathTarget != pAdjustedDest ) + { + // Update to the next path, if there is one... + m_pCurrentPathTarget = NextAlongCurrentPath( m_pCurrentPathTarget ); + if ( !m_pCurrentPathTarget ) + { + m_pCurrentPathTarget = m_pLastPathTarget; + } + } + else + { + // NOTE: Have to do this here because the NextAlongCurrentPath call above + // could make m_pCurrentPathTarget == m_pDestPathTarget. + // In this case, we're at rest (no patrolling behavior) + bRestingAtDest = true; + } + + if ( bRestingAtDest ) + { + // NOTE: Must use current path target, instead of dest + // to get the PreviousAlongCurrentPath working correctly + CPathTrack *pSegmentStart = PreviousAlongCurrentPath( m_pCurrentPathTarget ); + if ( !pSegmentStart ) + { + pSegmentStart = m_pCurrentPathTarget; + } + m_vecSegmentStartPoint = pSegmentStart->GetAbsOrigin(); + } + else + { + m_vecSegmentStartPoint = m_pLastPathTarget->GetAbsOrigin(); + } + +visualizeDebugInfo: + VisualizeDebugInfo( vecPoint, vecTarget ); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CAI_TrackPather::UpdateTargetPositionLeading( void ) +{ + Vector targetPos; + if ( !GetTrackPatherTarget( &targetPos ) ) + return; + + // NOTE: FindClosestPointOnPath *always* returns the point on the "far", + // end of the line segment containing the closest point (namely the 'next' + // track, as opposed to the 'prev' track) + Vector vecClosestPoint, vecPathDir; + float flTargetDistanceFromPath; + CPathTrack *pNextPath = FindClosestPointOnPath( m_pCurrentPathTarget, + targetPos, &vecClosestPoint, &vecPathDir, &flTargetDistanceFromPath ); + + // This means that a valid path could not be found to our target! + if ( CPathTrack::ValidPath( pNextPath ) == NULL ) + return; + +// NDebugOverlay::Cross3D( vecClosestPoint, -Vector(24,24,24), Vector(24,24,24), 0, 255, 255, true, 0.1f ); +// NDebugOverlay::Cross3D( pNextPath->GetAbsOrigin(), -Vector(24,24,24), Vector(24,24,24), 255, 255, 0, true, 0.1f ); + + // Here's how far we are from the path + m_flTargetDistFromPath = flTargetDistanceFromPath; + m_vecTargetPathDir = vecPathDir; + + // Here's info about where the target is along the path + m_vecTargetPathPoint = vecClosestPoint; + m_pTargetNearestPath = pNextPath; + + // Find the best position to be on our path + // NOTE: This will *also* return a path track on the "far" end of the line segment + // containing the leading position, namely the "next" end of the segment as opposed + // to the "prev" end of the segment. + CPathTrack *pDest = ComputeLeadingPointAlongPath( vecClosestPoint, pNextPath, m_flLeadDistance, &targetPos ); + SetDesiredPosition( targetPos ); + + // We only want to switch movement directions when absolutely necessary + // so convert dest into a more appropriate value based on the current movement direction + if ( pDest != m_pDestPathTarget ) + { + // NOTE: This is really tricky + subtle + // For leading, we don't want to ever change direction when the current target == the + // adjusted destination target. Namely, if we're going forward, both dest + curr + // mean the "next"most node so we can compare them directly against eath other. + // If we're moving backward, dest means "next"most, but curr means "prev"most. + // We first have to adjust the dest to mean "prev"most, and then do the comparison. + // If the adjusted dest == curr, then maintain direction. Otherwise, use the forward along path test. + bool bMovingForward = m_bMovingForward; + CPathTrack *pAdjustedDest = AdjustForMovementDirection( pDest ); + if ( m_pCurrentPathTarget != pAdjustedDest ) + { + bMovingForward = IsForwardAlongPath( m_pCurrentPathTarget, pAdjustedDest ); + } + + if ( bMovingForward != m_bMovingForward ) + { + // As a result of the tricky note above, this should never occur + Assert( pAdjustedDest != m_pCurrentPathTarget ); + + // Oops! Need to reverse direction + m_bMovingForward = bMovingForward; + m_vecSegmentStartPoint = m_pCurrentPathTarget->GetAbsOrigin(); + m_pCurrentPathTarget = NextAlongCurrentPath( m_pCurrentPathTarget ); + } + m_pDestPathTarget = pDest; + } + +// NDebugOverlay::Cross3D( m_pCurrentPathTarget->GetAbsOrigin(), -Vector(36,36,36), Vector(36,36,36), 255, 0, 0, true, 0.1f ); +// NDebugOverlay::Cross3D( m_pDestPathTarget->GetAbsOrigin(), -Vector(48,48,48), Vector(48,48,48), 0, 255, 0, true, 0.1f ); +// NDebugOverlay::Cross3D( targetPos, -Vector(36,36,36), Vector(36,36,36), 0, 0, 255, true, 0.1f ); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CAI_TrackPather::UpdateTargetPosition( void ) +{ + // Don't update our target if we're being told to go somewhere + if ( m_bForcedMove && !m_bPatrolBreakable ) + return; + + // Don't update our target if we're patrolling + if ( m_bPatrolling ) + { + // If we have an enemy, and our patrol is breakable, stop patrolling + if ( !m_bPatrolBreakable || !GetEnemy() ) + return; + + m_bPatrolling = false; + } + + Vector targetPos; + if ( !GetTrackPatherTarget( &targetPos ) ) + return; + + // Not time to update again + if ( m_flEnemyPathUpdateTime > gpGlobals->curtime ) + return; + + // See if the target has moved enough to make us recheck + float flDistSqr = ( targetPos - m_vecLastGoalCheckPosition ).LengthSqr(); + if ( flDistSqr < m_flTargetDistanceThreshold * m_flTargetDistanceThreshold ) + return; + + // Find the best position to be on our path + CPathTrack *pDest = BestPointOnPath( m_pCurrentPathTarget, targetPos, m_flAvoidDistance, true, m_bChooseFarthestPoint ); + + if ( CPathTrack::ValidPath( pDest ) == NULL ) + { + // This means that a valid path could not be found to our target! +// Assert(0); + return; + } + + if ( pDest != m_pDestPathTarget ) + { + // This is our new destination + bool bMovingForward = IsForwardAlongPath( m_pCurrentPathTarget, pDest ); + if ( bMovingForward != m_bMovingForward ) + { + // Oops! Need to reverse direction + m_bMovingForward = bMovingForward; + if ( pDest != m_pCurrentPathTarget ) + { + SetupNewCurrentTarget( NextAlongCurrentPath( m_pCurrentPathTarget ) ); + } + } + m_pDestPathTarget = pDest; + } + + // Keep this goal point for comparisons later + m_vecLastGoalCheckPosition = targetPos; + + // Only do this on set intervals + m_flEnemyPathUpdateTime = gpGlobals->curtime + 1.0f; +} + + +//------------------------------------------------------------------------------ +// Returns the direction of the path at the closest point to the target +//------------------------------------------------------------------------------ +const Vector &CAI_TrackPather::TargetPathDirection() const +{ + return m_vecTargetPathDir; +} + +const Vector &CAI_TrackPather::TargetPathAcrossDirection() const +{ + static Vector s_Result; + CrossProduct( m_vecTargetPathDir, Vector( 0, 0, 1 ), s_Result ); + return s_Result; +} + + +//------------------------------------------------------------------------------ +// Returns the speed of the target relative to the path +//------------------------------------------------------------------------------ +float CAI_TrackPather::TargetSpeedAlongPath() const +{ + if ( !GetEnemy() || !IsLeading() ) + return 0.0f; + + Vector vecSmoothedVelocity = GetEnemy()->GetSmoothedVelocity(); + return DotProduct( vecSmoothedVelocity, TargetPathDirection() ); +} + + +//------------------------------------------------------------------------------ +// Returns the speed of the target *across* the path +//------------------------------------------------------------------------------ +float CAI_TrackPather::TargetSpeedAcrossPath() const +{ + if ( !GetEnemy() || !IsLeading() ) + return 0.0f; + + Vector vecSmoothedVelocity = GetEnemy()->GetSmoothedVelocity(); + return DotProduct( vecSmoothedVelocity, TargetPathAcrossDirection() ); +} + + +//------------------------------------------------------------------------------ +// Returns the max distance we can be from the path +//------------------------------------------------------------------------------ +float CAI_TrackPather::MaxDistanceFromCurrentPath() const +{ + if ( !IsLeading() || !m_pCurrentPathTarget ) + return 0.0f; + + CPathTrack *pPrevPath = PreviousAlongCurrentPath( m_pCurrentPathTarget ); + if ( !pPrevPath ) + { + pPrevPath = m_pCurrentPathTarget; + } + + // NOTE: Can't use m_vecSegmentStartPoint because we don't have a radius defined for it + float t; + Vector vecTemp; + CalcClosestPointOnLine( GetAbsOrigin(), pPrevPath->GetAbsOrigin(), + m_pCurrentPathTarget->GetAbsOrigin(), vecTemp, &t ); + t = clamp( t, 0.0f, 1.0f ); + float flRadius = (1.0f - t) * pPrevPath->GetRadius() + t * m_pCurrentPathTarget->GetRadius(); + return flRadius; +} + + +//------------------------------------------------------------------------------ +// Purpose : A different version of the track pather which is more explicit about +// the meaning of dest, current, and prev path points +//------------------------------------------------------------------------------ +void CAI_TrackPather::UpdateTrackNavigation( void ) +{ + // No target? Use the string specified. We have no spawn method (sucky!!) so this is how that works + if ( ( CPathTrack::ValidPath( m_pDestPathTarget ) == NULL ) && ( m_target != NULL_STRING ) ) + { + FlyToPathTrack( m_target ); + m_target = NULL_STRING; + } + + if ( !IsLeading() ) + { + if ( !m_pCurrentPathTarget ) + return; + + // Updates our destination node if we're tracking something + UpdateTargetPosition(); + + // Move along our path towards our current destination + UpdateCurrentTarget(); + } + else + { + // Updates our destination position if we're leading something + UpdateTargetPositionLeading(); + + // Move along our path towards our current destination + UpdateCurrentTargetLeading(); + } +} + + +//------------------------------------------------------------------------------ +// Sets the farthest path distance +//------------------------------------------------------------------------------ +void CAI_TrackPather::SetFarthestPathDist( float flMaxPathDist ) +{ + m_flFarthestPathDist = flMaxPathDist; +} + + +//------------------------------------------------------------------------------ +// Sets up a new current path target +//------------------------------------------------------------------------------ +void CAI_TrackPather::SetupNewCurrentTarget( CPathTrack *pTrack ) +{ + Assert( pTrack ); + m_vecSegmentStartPoint = GetAbsOrigin(); + VectorMA( m_vecSegmentStartPoint, -2.0f, GetAbsVelocity(), m_vecSegmentStartSplinePoint ); + m_pCurrentPathTarget = pTrack; + SetDesiredPosition( m_pCurrentPathTarget->GetAbsOrigin() ); +} + + +//------------------------------------------------------------------------------ +// Moves to an explicit track point +//------------------------------------------------------------------------------ +void CAI_TrackPather::MoveToTrackPoint( CPathTrack *pTrack ) +{ + if ( IsOnSameTrack( pTrack, m_pDestPathTarget ) ) + { + // The track must be valid + if ( CPathTrack::ValidPath( pTrack ) == NULL ) + return; + + m_pDestPathTarget = pTrack; + m_bMovingForward = IsForwardAlongPath( m_pCurrentPathTarget, pTrack ); + m_bForcedMove = true; + } + else + { + CPathTrack *pClosestTrack = BestPointOnPath( pTrack, WorldSpaceCenter(), 0.0f, false, false ); + + // The track must be valid + if ( CPathTrack::ValidPath( pClosestTrack ) == NULL ) + return; + + SetupNewCurrentTarget( pClosestTrack ); + m_pDestPathTarget = pTrack; + m_bMovingForward = IsForwardAlongPath( pClosestTrack, pTrack ); + m_bForcedMove = true; + } +} + + +//------------------------------------------------------------------------------ +// Moves to the closest track point +//------------------------------------------------------------------------------ +void CAI_TrackPather::MoveToClosestTrackPoint( CPathTrack *pTrack ) +{ + if ( IsOnSameTrack( pTrack, m_pDestPathTarget ) ) + return; + + CPathTrack *pClosestTrack = BestPointOnPath( pTrack, WorldSpaceCenter(), 0.0f, false, false ); + + // The track must be valid + if ( CPathTrack::ValidPath( pClosestTrack ) == NULL ) + return; + + SetupNewCurrentTarget( pClosestTrack ); + m_pDestPathTarget = pClosestTrack; + m_bMovingForward = true; + + // Force us to switch tracks if we're leading + if ( IsLeading() ) + { + m_bForcedMove = true; + } +} + + +//----------------------------------------------------------------------------- +// Are the two path tracks connected? +//----------------------------------------------------------------------------- +bool CAI_TrackPather::IsOnSameTrack( CPathTrack *pPath1, CPathTrack *pPath2 ) const +{ + if ( pPath1 == pPath2 ) + return true; + + { + BEGIN_PATH_TRACK_ITERATION(); + CPathTrack *pTravPath = pPath1->GetPrevious(); + while( CPathTrack::ValidPath( pTravPath ) && (pTravPath != pPath1) ) + { + // Circular loop checking + if ( pTravPath->HasBeenVisited() ) + break; + + pTravPath->Visit(); + + if ( pTravPath == pPath2 ) + return true; + + pTravPath = pTravPath->GetPrevious(); + } + } + + { + BEGIN_PATH_TRACK_ITERATION(); + CPathTrack *pTravPath = pPath1->GetNext(); + while( CPathTrack::ValidPath( pTravPath ) && (pTravPath != pPath1) ) + { + // Circular loop checking + if ( pTravPath->HasBeenVisited() ) + break; + + pTravPath->Visit(); + + if ( pTravPath == pPath2 ) + return true; + + pTravPath = pTravPath->GetNext(); + } + } + + return false; +} + + +//----------------------------------------------------------------------------- +// Deal with teleportation +//----------------------------------------------------------------------------- +void CAI_TrackPather::Teleported() +{ + // This updates the paths so they are reasonable + CPathTrack *pClosestTrack = BestPointOnPath( GetDestPathTarget(), WorldSpaceCenter(), 0.0f, false, false ); + m_pDestPathTarget = NULL; + MoveToClosestTrackPoint( pClosestTrack ); +} + + +//----------------------------------------------------------------------------- +// Returns distance along path to target, returns FLT_MAX if there's no path +//----------------------------------------------------------------------------- +float CAI_TrackPather::ComputePathDistance( CPathTrack *pPath, CPathTrack *pDest, bool bForward ) const +{ + float flDist = 0.0f; + CPathTrack *pLast = pPath; + + BEGIN_PATH_TRACK_ITERATION(); + while ( CPathTrack::ValidPath( pPath ) ) + { + // Ciruclar loop checking + if ( pPath->HasBeenVisited() ) + return FLT_MAX; + + pPath->Visit(); + + flDist += pLast->GetAbsOrigin().DistTo( pPath->GetAbsOrigin() ); + + if ( pDest == pPath ) + return flDist; + + pLast = pPath; + pPath = bForward ? pPath->GetNext() : pPath->GetPrevious(); + } + + return FLT_MAX; +} + + +//----------------------------------------------------------------------------- +// Is pPathTest in "front" of pPath on the same path? (Namely, does GetNext() get us there?) +//----------------------------------------------------------------------------- +bool CAI_TrackPather::IsForwardAlongPath( CPathTrack *pPath, CPathTrack *pPathTest ) const +{ + // Also, in the case of looping paths, we want to return the shortest path + float flForwardDist = ComputePathDistance( pPath, pPathTest, true ); + float flReverseDist = ComputePathDistance( pPath, pPathTest, false ); + + Assert( ( flForwardDist != FLT_MAX ) || ( flReverseDist != FLT_MAX ) ); + return ( flForwardDist <= flReverseDist ); +} + + +//----------------------------------------------------------------------------- +// Computes distance + nearest point from the current path.. +//----------------------------------------------------------------------------- +float CAI_TrackPather::ClosestPointToCurrentPath( Vector *pVecPoint ) const +{ + if (!m_pCurrentPathTarget) + { + *pVecPoint = GetAbsOrigin(); + return 0; + } + + float t; + CalcClosestPointOnLine( GetAbsOrigin(), m_vecSegmentStartPoint, + m_pCurrentPathTarget->GetAbsOrigin(), *pVecPoint, &t ); + return t; +} + + +//----------------------------------------------------------------------------- +// Computes a "path" velocity at a particular point along the current path +//----------------------------------------------------------------------------- +void CAI_TrackPather::ComputePathTangent( float t, Vector *pVecTangent ) const +{ + CPathTrack *pNextTrack = NextAlongCurrentPath(m_pCurrentPathTarget); + if ( !pNextTrack ) + { + pNextTrack = m_pCurrentPathTarget; + } + + t = clamp( t, 0.0f, 1.0f ); + pVecTangent->Init(0,0,0); + Catmull_Rom_Spline_Tangent( m_vecSegmentStartSplinePoint, m_vecSegmentStartPoint, + m_pCurrentPathTarget->GetAbsOrigin(), pNextTrack->GetAbsOrigin(), t, *pVecTangent ); + VectorNormalize( *pVecTangent ); +} + + +//----------------------------------------------------------------------------- +// Computes the *normalized* velocity at which the helicopter should approach the final point +//----------------------------------------------------------------------------- +void CAI_TrackPather::ComputeNormalizedDestVelocity( Vector *pVecVelocity ) const +{ + if ( m_nPauseState != PAUSE_NO_PAUSE ) + { + pVecVelocity->Init(0,0,0); + return; + } + + CPathTrack *pNextTrack = NextAlongCurrentPath(m_pCurrentPathTarget); + if ( !pNextTrack ) + { + pNextTrack = m_pCurrentPathTarget; + } + + if ( ( pNextTrack == m_pCurrentPathTarget ) || ( m_pCurrentPathTarget == m_pDestPathTarget ) ) + { + pVecVelocity->Init(0,0,0); + return; + } + + VectorSubtract( pNextTrack->GetAbsOrigin(), m_pCurrentPathTarget->GetAbsOrigin(), *pVecVelocity ); + VectorNormalize( *pVecVelocity ); + + // Slow it down if we're approaching a sharp corner + Vector vecDelta; + VectorSubtract( m_pCurrentPathTarget->GetAbsOrigin(), m_vecSegmentStartPoint, vecDelta ); + VectorNormalize( vecDelta ); + float flDot = DotProduct( *pVecVelocity, vecDelta ); + *pVecVelocity *= clamp( flDot, 0.0f, 1.0f ); +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : &inputdata - +//----------------------------------------------------------------------------- +void CAI_TrackPather::SetTrack( CBaseEntity *pGoalEnt ) +{ + // Ignore this input if we're *already* on that path. + CPathTrack *pTrack = dynamic_cast(pGoalEnt); + if ( !pTrack ) + { + DevWarning( "%s: Specified entity '%s' must be a path_track!\n", pGoalEnt->GetClassname(), STRING(pGoalEnt->GetEntityName()) ); + return; + } + + MoveToClosestTrackPoint( pTrack ); +} + +void CAI_TrackPather::SetTrack( string_t strTrackName ) +{ + // Find our specified target + CBaseEntity *pGoalEnt = gEntList.FindEntityByName( NULL, strTrackName ); + if ( pGoalEnt == NULL ) + { + DevWarning( "%s: Could not find path_track '%s'!\n", GetClassname(), STRING( strTrackName ) ); + return; + } + + SetTrack( pGoalEnt ); +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : &inputdata - +//----------------------------------------------------------------------------- +void CAI_TrackPather::InputSetTrack( inputdata_t &inputdata ) +{ + string_t strTrackName = MAKE_STRING( inputdata.value.String() ); + SetTrack( MAKE_STRING( inputdata.value.String() ) ); +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : strTrackName - +//----------------------------------------------------------------------------- +void CAI_TrackPather::FlyToPathTrack( string_t strTrackName ) +{ + CBaseEntity *pGoalEnt = gEntList.FindEntityByName( NULL, strTrackName ); + if ( pGoalEnt == NULL ) + { + DevWarning( "%s: Could not find path_track '%s'!\n", GetClassname(), STRING( strTrackName ) ); + return; + } + + // Ignore this input if we're *already* on that path. + CPathTrack *pTrack = dynamic_cast(pGoalEnt); + if ( !pTrack ) + { + DevWarning( "%s: Specified entity '%s' must be a path_track!\n", GetClassname(), STRING( strTrackName ) ); + return; + } + + // Find our specified target + MoveToTrackPoint( pTrack ); +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : &inputdata - +//----------------------------------------------------------------------------- +void CAI_TrackPather::InputFlyToPathTrack( inputdata_t &inputdata ) +{ + // Find our specified target + string_t strTrackName = MAKE_STRING( inputdata.value.String() ); + FlyToPathTrack( strTrackName ); +} + + +//----------------------------------------------------------------------------- +// Changes the mode used to determine which path point to move to +//----------------------------------------------------------------------------- +void CAI_TrackPather::InputChooseFarthestPathPoint( inputdata_t &inputdata ) +{ + UseFarthestPathPoint( true ); +} + +void CAI_TrackPather::InputChooseNearestPathPoint( inputdata_t &inputdata ) +{ + UseFarthestPathPoint( false ); +} + +void CAI_TrackPather::UseFarthestPathPoint( bool useFarthest ) +{ + m_bChooseFarthestPoint = useFarthest; +} diff --git a/dlls/baseanimating.cpp b/dlls/baseanimating.cpp index c14776e6..23b5591a 100644 --- a/dlls/baseanimating.cpp +++ b/dlls/baseanimating.cpp @@ -500,12 +500,12 @@ void CBaseAnimating::SetLightingOriginRelative( string_t strLightingOriginRelati { if( !pLightingOrigin ) { - DevWarning( "%s: Cannot find Lighting Origin named: %s\n", GetEntityName(), strLightingOriginRelative ); + DevWarning( "%s: Cannot find Lighting Origin named: %s\n", STRING(GetEntityName()), STRING(strLightingOriginRelative) ); } else { DevWarning( "%s: Specified entity '%s' must be a info_lighting_relative!\n", - pLightingOrigin->GetClassname(), pLightingOrigin->GetEntityName() ); + pLightingOrigin->GetClassname(), STRING(pLightingOrigin->GetEntityName()) ); } return; } diff --git a/dlls/basebludgeonweapon.h b/dlls/basebludgeonweapon.h index 0f5eab5c..a8acc376 100644 --- a/dlls/basebludgeonweapon.h +++ b/dlls/basebludgeonweapon.h @@ -1,57 +1,57 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: The class from which all bludgeon melee -// weapons are derived. -// -// $Workfile: $ -// $Date: $ -// $NoKeywords: $ -//=============================================================================// - -#include "basehlcombatweapon.h" - -#ifndef BASEBLUDGEONWEAPON_H -#define BASEBLUDGEONWEAPON_H - -//========================================================= -// CBaseHLBludgeonWeapon -//========================================================= -class CBaseHLBludgeonWeapon : public CBaseHLCombatWeapon -{ - DECLARE_CLASS( CBaseHLBludgeonWeapon, CBaseHLCombatWeapon ); -public: - CBaseHLBludgeonWeapon(); - - DECLARE_SERVERCLASS(); - - virtual void Spawn( void ); - virtual void Precache( void ); - - //Attack functions - virtual void PrimaryAttack( void ); - virtual void SecondaryAttack( void ); - - virtual void ItemPostFrame( void ); - - //Functions to select animation sequences - virtual Activity GetPrimaryAttackActivity( void ) { return ACT_VM_HITCENTER; } - virtual Activity GetSecondaryAttackActivity( void ) { return ACT_VM_HITCENTER2; } - - virtual float GetFireRate( void ) { return 0.2f; } - virtual float GetRange( void ) { return 32.0f; } - virtual float GetDamageForActivity( Activity hitActivity ) { return 1.0f; } - - virtual int CapabilitiesGet( void ); - virtual int WeaponMeleeAttack1Condition( float flDot, float flDist ); - -protected: - virtual void ImpactEffect( trace_t &trace ); - -private: - bool ImpactWater( const Vector &start, const Vector &end ); - void Swing( int bIsSecondary ); - void Hit( trace_t &traceHit, Activity nHitActivity ); - Activity ChooseIntersectionPointAndActivity( trace_t &hitTrace, const Vector &mins, const Vector &maxs, CBasePlayer *pOwner ); -}; - -#endif \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: The class from which all bludgeon melee +// weapons are derived. +// +// $Workfile: $ +// $Date: $ +// $NoKeywords: $ +//=============================================================================// + +#include "basehlcombatweapon.h" + +#ifndef BASEBLUDGEONWEAPON_H +#define BASEBLUDGEONWEAPON_H + +//========================================================= +// CBaseHLBludgeonWeapon +//========================================================= +class CBaseHLBludgeonWeapon : public CBaseHLCombatWeapon +{ + DECLARE_CLASS( CBaseHLBludgeonWeapon, CBaseHLCombatWeapon ); +public: + CBaseHLBludgeonWeapon(); + + DECLARE_SERVERCLASS(); + + virtual void Spawn( void ); + virtual void Precache( void ); + + //Attack functions + virtual void PrimaryAttack( void ); + virtual void SecondaryAttack( void ); + + virtual void ItemPostFrame( void ); + + //Functions to select animation sequences + virtual Activity GetPrimaryAttackActivity( void ) { return ACT_VM_HITCENTER; } + virtual Activity GetSecondaryAttackActivity( void ) { return ACT_VM_HITCENTER2; } + + virtual float GetFireRate( void ) { return 0.2f; } + virtual float GetRange( void ) { return 32.0f; } + virtual float GetDamageForActivity( Activity hitActivity ) { return 1.0f; } + + virtual int CapabilitiesGet( void ); + virtual int WeaponMeleeAttack1Condition( float flDot, float flDist ); + +protected: + virtual void ImpactEffect( trace_t &trace ); + +private: + bool ImpactWater( const Vector &start, const Vector &end ); + void Swing( int bIsSecondary ); + void Hit( trace_t &traceHit, Activity nHitActivity ); + Activity ChooseIntersectionPointAndActivity( trace_t &hitTrace, const Vector &mins, const Vector &maxs, CBasePlayer *pOwner ); +}; + +#endif diff --git a/dlls/basecombatcharacter.cpp b/dlls/basecombatcharacter.cpp index 9a6ff9d8..da96f002 100644 --- a/dlls/basecombatcharacter.cpp +++ b/dlls/basecombatcharacter.cpp @@ -1300,8 +1300,6 @@ Killed */ void CBaseCombatCharacter::Event_Killed( const CTakeDamageInfo &info ) { - extern ConVar npc_vphysics; - // Advance life state to dying m_lifeState = LIFE_DYING; @@ -2858,7 +2856,7 @@ void RadiusDamage( const CTakeDamageInfo &info, const Vector &vecSrc, float flRa // be less than 128 units. float soundRadius = max( 128.0f, flRadius * 1.5 ); - CSoundEnt::InsertSound( SOUND_COMBAT | SOUND_CONTEXT_EXPLOSION, vecSrc, soundRadius, 0.25, info.GetInflictor() ); + CSoundEnt::InsertSound( SOUND_COMBAT | SOUND_CONTEXT_EXPLOSION, vecSrc, static_cast(soundRadius), 0.25, info.GetInflictor() ); } } diff --git a/dlls/basecombatweapon.cpp b/dlls/basecombatweapon.cpp index 1c8eac6a..15c72f01 100644 --- a/dlls/basecombatweapon.cpp +++ b/dlls/basecombatweapon.cpp @@ -208,7 +208,7 @@ class CWeaponLOSFilter : public CTraceFilterSkipTwoEntities { DECLARE_CLASS( CWeaponLOSFilter, CTraceFilterSkipTwoEntities ); public: - CWeaponLOSFilter::CWeaponLOSFilter( IHandleEntity *pHandleEntity, IHandleEntity *pHandleEntity2, int collisionGroup ) : + CWeaponLOSFilter( IHandleEntity *pHandleEntity, IHandleEntity *pHandleEntity2, int collisionGroup ) : CTraceFilterSkipTwoEntities( pHandleEntity, pHandleEntity2, collisionGroup ) { } diff --git a/dlls/baseentity.cpp b/dlls/baseentity.cpp index 010b82db..569bf263 100644 --- a/dlls/baseentity.cpp +++ b/dlls/baseentity.cpp @@ -547,8 +547,8 @@ void CBaseEntity::AddTimedOverlay( const char *msg, int endTime ) int len = strlen(msg); pNewTO->msg = new char[len + 1]; Q_strncpy(pNewTO->msg,msg, len+1); - pNewTO->msgEndTime = gpGlobals->curtime + endTime; - pNewTO->msgStartTime = gpGlobals->curtime; + pNewTO->msgEndTime = static_cast(gpGlobals->curtime + endTime); + pNewTO->msgStartTime = static_cast(gpGlobals->curtime); pNewTO->pNextTimedOverlay = m_pTimedOverlay; m_pTimedOverlay = pNewTO; } @@ -689,7 +689,7 @@ void CBaseEntity::DrawTimedOverlays(void) // If messages aren't paused fade out if (!CBaseEntity::Debug_IsPaused()) { - nAlpha = 255*((gpGlobals->curtime - pTO->msgStartTime)/(pTO->msgEndTime - pTO->msgStartTime)); + nAlpha = static_cast(255*((gpGlobals->curtime - pTO->msgStartTime)/(pTO->msgEndTime - pTO->msgStartTime))); } int r = 185; int g = 145; @@ -3300,7 +3300,7 @@ void CBaseEntity::DrawInputOverlay(const char *szInputName, CBaseEntity *pCaller { Q_snprintf( bigstring,sizeof(bigstring), "%3.1f (%s) <-- (%s)\n", gpGlobals->curtime, szInputName, pCaller ? pCaller->GetDebugName() : NULL); } - AddTimedOverlay(bigstring, 10.0); + AddTimedOverlay(bigstring, 10); if ( Value.FieldType() == FIELD_INTEGER ) { @@ -3331,7 +3331,7 @@ void CBaseEntity::DrawOutputOverlay(CEventAction *ev) { Q_snprintf( bigstring,sizeof(bigstring), "%3.1f (%s) --> (%s)\n", gpGlobals->curtime, STRING(ev->m_iTargetInput), STRING(ev->m_iTarget)); } - AddTimedOverlay(bigstring, 10.0); + AddTimedOverlay(bigstring, 10); // Now print to the console if ( ev->m_flDelay ) @@ -4545,7 +4545,7 @@ void CC_Find_Ent( void ) if ( bMatches ) { iCount++; - Msg(" '%s' : '%s' (entindex %d) \n", ent->GetClassname(), ent->GetEntityName(), ent->entindex() ); + Msg(" '%s' : '%s' (entindex %d) \n", ent->GetClassname(), STRING(ent->GetEntityName()), ent->entindex() ); } } @@ -4607,6 +4607,8 @@ void CC_Ent_Dump( void ) } } break; + default: + break; } // don't print out the duplicate keys @@ -6381,8 +6383,8 @@ void CBaseEntity::SUB_PerformFadeOut( void ) dt = 0.1f; } m_nRenderMode = kRenderTransTexture; - int speed = max(1,256*dt); // fade out over 1 second - SetRenderColorA( UTIL_Approach( 0, m_clrRender->a, speed ) ); + int speed = static_cast(max(1,256*dt)); // fade out over 1 second + SetRenderColorA( static_cast(UTIL_Approach( 0, m_clrRender->a, speed )) ); } bool CBaseEntity::SUB_AllowedToFade( void ) diff --git a/dlls/baseentity.h b/dlls/baseentity.h index a3bb8a14..c3a2ae84 100644 --- a/dlls/baseentity.h +++ b/dlls/baseentity.h @@ -1007,21 +1007,21 @@ public: ENTITYFUNCPTR TouchSet( ENTITYFUNCPTR func, char *name ) { - COMPILE_TIME_ASSERT( sizeof(func) == 4 ); + COMPILE_TIME_ASSERT( sizeof(func) == MFP_SIZE ); m_pfnTouch = func; FunctionCheck( (void *)*((int *)((char *)this + ( offsetof(CBaseEntity,m_pfnTouch)))), name ); return func; } USEPTR UseSet( USEPTR func, char *name ) { - COMPILE_TIME_ASSERT( sizeof(func) == 4 ); + COMPILE_TIME_ASSERT( sizeof(func) == MFP_SIZE ); m_pfnUse = func; FunctionCheck( (void *)*((int *)((char *)this + ( offsetof(CBaseEntity,m_pfnUse)))), name ); return func; } ENTITYFUNCPTR BlockedSet( ENTITYFUNCPTR func, char *name ) { - COMPILE_TIME_ASSERT( sizeof(func) == 4 ); + COMPILE_TIME_ASSERT( sizeof(func) == MFP_SIZE ); m_pfnBlocked = func; FunctionCheck( (void *)*((int *)((char *)this + ( offsetof(CBaseEntity,m_pfnBlocked)))), name ); return func; @@ -1789,7 +1789,7 @@ inline void CBaseEntity::SetName( string_t newName ) inline bool CBaseEntity::NameMatches( const char *pszNameOrWildcard ) { - if ( IDENT_STRINGS(m_iName, pszNameOrWildcard) ) + if ( IDENT_STRINGS(m_iName, MAKE_STRING(pszNameOrWildcard)) ) return true; return NameMatchesComplex( pszNameOrWildcard ); } @@ -1803,7 +1803,7 @@ inline bool CBaseEntity::NameMatches( string_t nameStr ) inline bool CBaseEntity::ClassMatches( const char *pszClassOrWildcard ) { - if ( IDENT_STRINGS(m_iClassname, pszClassOrWildcard ) ) + if ( IDENT_STRINGS(m_iClassname, MAKE_STRING(pszClassOrWildcard) ) ) return true; return ClassMatchesComplex( pszClassOrWildcard ); } diff --git a/dlls/baseflex.cpp b/dlls/baseflex.cpp index d2a30b78..4956e86e 100644 --- a/dlls/baseflex.cpp +++ b/dlls/baseflex.cpp @@ -361,6 +361,8 @@ bool CBaseFlex::ClearSceneEvent( CSceneEventInfo *info, bool fastKill, bool canc } } return true; + default: + break; } return false; } @@ -642,7 +644,7 @@ bool CBaseFlex::HandleStartGestureSceneEvent( CSceneEventInfo *info, CChoreoScen if ( looping ) { DevMsg( 1, "vcd error, gesture %s of model %s is marked as STUDIO_LOOPING!\n", - event->GetParameters(), GetModelName() ); + event->GetParameters(), STRING(GetModelName()) ); } SetLayerLooping( info->m_iLayer, false ); // force to not loop @@ -731,6 +733,9 @@ bool CBaseFlex::StartSceneEvent( CSceneEventInfo *info, CChoreoScene *scene, CCh case CChoreoEvent::EXPRESSION: // These are handled client-side return true; + + default: + break; } return false; diff --git a/dlls/basegrenade_concussion.cpp b/dlls/basegrenade_concussion.cpp index 530a45df..a00ca823 100644 --- a/dlls/basegrenade_concussion.cpp +++ b/dlls/basegrenade_concussion.cpp @@ -47,7 +47,7 @@ void CBaseGrenadeConcussion::FallThink(void) Remove( ); return; } - CSoundEnt::InsertSound ( SOUND_DANGER, GetAbsOrigin() + GetAbsVelocity() * 0.5, GetAbsVelocity().Length( ), 0.2 ); + CSoundEnt::InsertSound ( SOUND_DANGER, GetAbsOrigin() + GetAbsVelocity() * 0.5, static_cast(GetAbsVelocity().Length()), 0.2 ); SetNextThink( gpGlobals->curtime + random->RandomFloat(0.05, 0.1) ); diff --git a/dlls/basetempentity.h b/dlls/basetempentity.h index b6817307..2eeef8ce 100644 --- a/dlls/basetempentity.h +++ b/dlls/basetempentity.h @@ -1,65 +1,65 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $Workfile: $ -// $Date: $ -// -//----------------------------------------------------------------------------- -// $Log: $ -// -// $NoKeywords: $ -//=============================================================================// -#if !defined( BASETEMPENTITY_H ) -#define BASETEMPENTITY_H -#ifdef _WIN32 -#pragma once -#endif - -#include "edict.h" - -// This is the base class for TEMP ENTITIES that use the -// event system to propagate -class CBaseTempEntity -{ -public: - DECLARE_CLASS_NOBASE( CBaseTempEntity ); - DECLARE_SERVERCLASS(); - - CBaseTempEntity( const char *name ); - virtual ~CBaseTempEntity( void ); - - const char *GetName( void ); - - // Force all derived classes to implement a test - virtual void Test( const Vector& current_origin, const QAngle& current_angles ); - - virtual void Create( IRecipientFilter& filter, float delay = 0.0 ); - - virtual void Precache( void ); - - CBaseTempEntity *GetNext( void ); - - // Get list of tempentities - static CBaseTempEntity *GetList( void ); - - // Called at startup to allow temp entities to precache any models/sounds that they need - static void PrecacheTempEnts( void ); - - void NetworkStateChanged() {} // TE's are sent out right away so we don't track whether state changes or not, - // but we want to allow CNetworkVars. - void NetworkStateChanged( void *pVar ) {} - -private: - // Descriptive name, for when running tests - const char *m_pszName; - - // Next in chain - CBaseTempEntity *m_pNext; - - // ConVars add themselves to this list for the executable. Then ConVarMgr::Init() runs through - // all the console variables and registers them. - static CBaseTempEntity *s_pTempEntities; -}; - -#endif // BASETEMPENTITY_H \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $Workfile: $ +// $Date: $ +// +//----------------------------------------------------------------------------- +// $Log: $ +// +// $NoKeywords: $ +//=============================================================================// +#if !defined( BASETEMPENTITY_H ) +#define BASETEMPENTITY_H +#ifdef _WIN32 +#pragma once +#endif + +#include "edict.h" + +// This is the base class for TEMP ENTITIES that use the +// event system to propagate +class CBaseTempEntity +{ +public: + DECLARE_CLASS_NOBASE( CBaseTempEntity ); + DECLARE_SERVERCLASS(); + + CBaseTempEntity( const char *name ); + virtual ~CBaseTempEntity( void ); + + const char *GetName( void ); + + // Force all derived classes to implement a test + virtual void Test( const Vector& current_origin, const QAngle& current_angles ); + + virtual void Create( IRecipientFilter& filter, float delay = 0.0 ); + + virtual void Precache( void ); + + CBaseTempEntity *GetNext( void ); + + // Get list of tempentities + static CBaseTempEntity *GetList( void ); + + // Called at startup to allow temp entities to precache any models/sounds that they need + static void PrecacheTempEnts( void ); + + void NetworkStateChanged() {} // TE's are sent out right away so we don't track whether state changes or not, + // but we want to allow CNetworkVars. + void NetworkStateChanged( void *pVar ) {} + +private: + // Descriptive name, for when running tests + const char *m_pszName; + + // Next in chain + CBaseTempEntity *m_pNext; + + // ConVars add themselves to this list for the executable. Then ConVarMgr::Init() runs through + // all the console variables and registers them. + static CBaseTempEntity *s_pTempEntities; +}; + +#endif // BASETEMPENTITY_H diff --git a/dlls/baseviewmodel.h b/dlls/baseviewmodel.h index 3fddb478..b4dce934 100644 --- a/dlls/baseviewmodel.h +++ b/dlls/baseviewmodel.h @@ -1,17 +1,17 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: Server side view model object -// -// $Workfile: $ -// $Date: $ -// $NoKeywords: $ -//=============================================================================// -#if !defined( BASEVIEWMODEL_H ) -#define BASEVIEWMODEL_H -#ifdef _WIN32 -#pragma once -#endif - -#include "baseviewmodel_shared.h" - -#endif // BASEVIEWMODEL_H \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: Server side view model object +// +// $Workfile: $ +// $Date: $ +// $NoKeywords: $ +//=============================================================================// +#if !defined( BASEVIEWMODEL_H ) +#define BASEVIEWMODEL_H +#ifdef _WIN32 +#pragma once +#endif + +#include "baseviewmodel_shared.h" + +#endif // BASEVIEWMODEL_H diff --git a/dlls/bitstring.h b/dlls/bitstring.h index 59746d31..2e627d57 100644 --- a/dlls/bitstring.h +++ b/dlls/bitstring.h @@ -325,8 +325,8 @@ inline CBitStringT::CBitStringT(int numBits) template inline bool CBitStringT::GetBit( int bitNum ) const { - Assert( bitNum >= 0 && bitNum < Size() ); - const int *pInt = GetInts() + BitString_Int( bitNum ); + Assert( bitNum >= 0 && bitNum < this->Size() ); + const int *pInt = this->GetInts() + BitString_Int( bitNum ); return ( ( *pInt & BitString_Bit( bitNum ) ) != 0 ); } @@ -335,8 +335,8 @@ inline bool CBitStringT::GetBit( int bitNum ) const template inline void CBitStringT::SetBit( int bitNum ) { - Assert( bitNum >= 0 && bitNum < Size() ); - int *pInt = GetInts() + BitString_Int( bitNum ); + Assert( bitNum >= 0 && bitNum < this->Size() ); + int *pInt = this->GetInts() + BitString_Int( bitNum ); *pInt |= BitString_Bit( bitNum ); } @@ -345,8 +345,8 @@ inline void CBitStringT::SetBit( int bitNum ) template inline void CBitStringT::ClearBit(int bitNum) { - Assert( bitNum >= 0 && bitNum < Size() ); - int *pInt = GetInts() + BitString_Int( bitNum ); + Assert( bitNum >= 0 && bitNum < this->Size() ); + int *pInt = this->GetInts() + BitString_Int( bitNum ); *pInt &= ~BitString_Bit( bitNum ); } @@ -362,10 +362,10 @@ inline void CBitStringT::And(const CBitStringT &addStr, CBitStringT *o ValidateOperand( *out ); int * pDest = out->GetInts(); - const int *pOperand1 = GetInts(); + const int *pOperand1 = this->GetInts(); const int *pOperand2 = addStr.GetInts(); - for (int i = GetNumInts() - 1; i >= 0 ; --i) + for (int i = this->GetNumInts() - 1; i >= 0 ; --i) { pDest[i] = pOperand1[i] & pOperand2[i]; } @@ -383,10 +383,10 @@ inline void CBitStringT::Or(const CBitStringT &orStr, CBitStringT *out ValidateOperand( *out ); int * pDest = out->GetInts(); - const int *pOperand1 = GetInts(); + const int *pOperand1 = this->GetInts(); const int *pOperand2 = orStr.GetInts(); - for (int i = GetNumInts() - 1; i >= 0; --i) + for (int i = this->GetNumInts() - 1; i >= 0; --i) { pDest[i] = pOperand1[i] | pOperand2[i]; } @@ -401,10 +401,10 @@ template inline void CBitStringT::Xor(const CBitStringT &xorStr, CBitStringT *out) const { int * pDest = out->GetInts(); - const int *pOperand1 = GetInts(); + const int *pOperand1 = this->GetInts(); const int *pOperand2 = xorStr.GetInts(); - for (int i = GetNumInts() - 1; i >= 0; --i) + for (int i = this->GetNumInts() - 1; i >= 0; --i) { pDest[i] = pOperand1[i] ^ pOperand2[i]; } @@ -421,9 +421,9 @@ inline void CBitStringT::Not(CBitStringT *out) const ValidateOperand( *out ); int * pDest = out->GetInts(); - const int *pOperand = GetInts(); + const int *pOperand = this->GetInts(); - for (int i = GetNumInts() - 1; i >= 0; --i) + for (int i = this->GetNumInts() - 1; i >= 0; --i) { pDest[i] = ~(pOperand[i]); } @@ -440,7 +440,7 @@ inline void CBitStringT::Copy(CBitStringT *out) const ValidateOperand( *out ); Assert( out != this ); - memcpy( out->GetInts(), GetInts(), GetNumInts() * sizeof( int ) ); + memcpy( out->GetInts(), this->GetInts(), this->GetNumInts() * sizeof( int ) ); } //----------------------------------------------------------------------------- @@ -454,11 +454,11 @@ inline bool CBitStringT::IsAllClear(void) const // Number of available bits may be more than the number // actually used, so make sure to mask out unused bits // before testing for zero - (const_cast(this))->GetInts()[GetNumInts()-1] &= ~CBitStringT::GetEndMask(); // external semantics of const retained + (const_cast(this))->GetInts()[this->GetNumInts()-1] &= ~CBitStringT::GetEndMask(); // external semantics of const retained - for (int i = GetNumInts() - 1; i >= 0; --i) + for (int i = this->GetNumInts() - 1; i >= 0; --i) { - if ( GetInts()[i] !=0 ) + if ( this->GetInts()[i] !=0 ) { return false; } @@ -477,11 +477,11 @@ inline bool CBitStringT::IsAllSet(void) const // Number of available bits may be more than the number // actually used, so make sure to mask out unused bits // before testing for set bits - (const_cast(this))->GetInts()[GetNumInts()-1] |= CBitStringT::GetEndMask(); // external semantics of const retained + (const_cast(this))->GetInts()[this->GetNumInts()-1] |= CBitStringT::GetEndMask(); // external semantics of const retained - for (int i = GetNumInts() - 1; i >= 0; --i) + for (int i = this->GetNumInts() - 1; i >= 0; --i) { - if ( GetInts()[i] != ~0 ) + if ( this->GetInts()[i] != ~0 ) { return false; } @@ -497,8 +497,8 @@ inline bool CBitStringT::IsAllSet(void) const template inline void CBitStringT::SetAllBits(void) { - if ( GetInts() ) - memset( GetInts(), 0xff, GetNumInts() * sizeof(int) ); + if ( this->GetInts() ) + memset( this->GetInts(), 0xff, this->GetNumInts() * sizeof(int) ); } //----------------------------------------------------------------------------- @@ -509,8 +509,8 @@ inline void CBitStringT::SetAllBits(void) template inline void CBitStringT::ClearAllBits(void) { - if ( GetInts() ) - memset( GetInts(), 0, GetNumInts() * sizeof(int) ); + if ( this->GetInts() ) + memset( this->GetInts(), 0, this->GetNumInts() * sizeof(int) ); } //----------------------------------------------------------------------------- @@ -518,8 +518,8 @@ inline void CBitStringT::ClearAllBits(void) template inline void CBitStringT::DebugPrintBits(void) const { - (const_cast(this))->GetInts()[GetNumInts()-1] &= ~CBitStringT::GetEndMask(); // external semantics of const retained - DebugPrintBitStringBits( GetInts(), GetNumInts() ); + (const_cast(this))->GetInts()[this->GetNumInts()-1] &= ~CBitStringT::GetEndMask(); // external semantics of const retained + DebugPrintBitStringBits( this->GetInts(), this->GetNumInts() ); } //----------------------------------------------------------------------------- @@ -527,8 +527,8 @@ inline void CBitStringT::DebugPrintBits(void) const template inline void CBitStringT::SaveBitString(CUtlBuffer& buf) const { - (const_cast(this))->GetInts()[GetNumInts()-1] &= ~CBitStringT::GetEndMask(); // external semantics of const retained - ::SaveBitString( GetInts(), GetNumInts(), buf ); + (const_cast(this))->GetInts()[this->GetNumInts()-1] &= ~CBitStringT::GetEndMask(); // external semantics of const retained + ::SaveBitString( this->GetInts(), this->GetNumInts(), buf ); } //----------------------------------------------------------------------------- @@ -536,8 +536,8 @@ inline void CBitStringT::SaveBitString(CUtlBuffer& buf) const template inline void CBitStringT::LoadBitString(CUtlBuffer& buf) { - (const_cast(this))->GetInts()[GetNumInts()-1] &= ~CBitStringT::GetEndMask(); - ::LoadBitString( GetInts(), GetNumInts(), buf ); + (const_cast(this))->GetInts()[this->GetNumInts()-1] &= ~CBitStringT::GetEndMask(); + ::LoadBitString( this->GetInts(), this->GetNumInts(), buf ); } //----------------------------------------------------------------------------- diff --git a/dlls/bmodels.cpp b/dlls/bmodels.cpp index 5f1a689a..aaa72cd7 100644 --- a/dlls/bmodels.cpp +++ b/dlls/bmodels.cpp @@ -701,7 +701,7 @@ void CFuncRotating::RampPitchVol( void ) float fpitch = FANPITCHMIN + (FANPITCHMAX - FANPITCHMIN) * fpct; - int pitch = clamp(fpitch, 0, 255); + int pitch = static_cast(clamp(fpitch, 0, 255)); if (pitch == PITCH_NORM) { pitch = PITCH_NORM - 1; diff --git a/dlls/buttons.cpp b/dlls/buttons.cpp index 7d2735b5..ee872a62 100644 --- a/dlls/buttons.cpp +++ b/dlls/buttons.cpp @@ -1,1485 +1,1485 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: Implements buttons. -// -//=============================================================================// - -#include "cbase.h" -#include "doors.h" -#include "ndebugoverlay.h" -#include "spark.h" -#include "vstdlib/random.h" -#include "engine/IEngineSound.h" -#include "vstdlib/strtools.h" -#include "buttons.h" -#include "eventqueue.h" - -// memdbgon must be the last include file in a .cpp file!!! -#include "tier0/memdbgon.h" - -void PlayLockSounds( CBaseEntity *pEdict, locksound_t *pls, int flocked, int fbutton ); -string_t MakeButtonSound( int sound ); // get string of button sound number - - -#define SF_BUTTON_DONTMOVE 1 -#define SF_ROTBUTTON_NOTSOLID 1 -#define SF_BUTTON_TOGGLE 32 // button stays pushed until reactivated -#define SF_BUTTON_TOUCH_ACTIVATES 256 // Button fires when touched. -#define SF_BUTTON_DAMAGE_ACTIVATES 512 // Button fires when damaged. -#define SF_BUTTON_USE_ACTIVATES 1024 // Button fires when used. -#define SF_BUTTON_LOCKED 2048 // Whether the button is initially locked. -#define SF_BUTTON_SPARK_IF_OFF 4096 // button sparks in OFF state -#define SF_BUTTON_JIGGLE_ON_USE_LOCKED 8192 // whether to jiggle if someone uses us when we're locked - -BEGIN_DATADESC( CBaseButton ) - - DEFINE_KEYFIELD( m_vecMoveDir, FIELD_VECTOR, "movedir" ), - DEFINE_FIELD( m_fStayPushed, FIELD_BOOLEAN ), - DEFINE_FIELD( m_fRotating, FIELD_BOOLEAN ), - - DEFINE_FIELD( m_bLockedSound, FIELD_CHARACTER ), - DEFINE_FIELD( m_bLockedSentence, FIELD_CHARACTER ), - DEFINE_FIELD( m_bUnlockedSound, FIELD_CHARACTER ), - DEFINE_FIELD( m_bUnlockedSentence, FIELD_CHARACTER ), - DEFINE_FIELD( m_bLocked, FIELD_BOOLEAN ), - DEFINE_FIELD( m_sNoise, FIELD_SOUNDNAME ), - DEFINE_FIELD( m_flUseLockedTime, FIELD_TIME ), - DEFINE_FIELD( m_bSolidBsp, FIELD_BOOLEAN ), - - DEFINE_KEYFIELD( m_sounds, FIELD_INTEGER, "sounds" ), - -// DEFINE_FIELD( m_ls, FIELD_SOUNDNAME ), // This is restored in Precache() -// DEFINE_FIELD( m_nState, FIELD_INTEGER ), - - // Function Pointers - DEFINE_FUNCTION( ButtonTouch ), - DEFINE_FUNCTION( ButtonSpark ), - DEFINE_FUNCTION( TriggerAndWait ), - DEFINE_FUNCTION( ButtonReturn ), - DEFINE_FUNCTION( ButtonBackHome ), - DEFINE_FUNCTION( ButtonUse ), - - // Inputs - DEFINE_INPUTFUNC( FIELD_VOID, "Lock", InputLock ), - DEFINE_INPUTFUNC( FIELD_VOID, "Unlock", InputUnlock ), - DEFINE_INPUTFUNC( FIELD_VOID, "Press", InputPress ), - - // Outputs - DEFINE_OUTPUT( m_OnDamaged, "OnDamaged" ), - DEFINE_OUTPUT( m_OnPressed, "OnPressed" ), - DEFINE_OUTPUT( m_OnUseLocked, "OnUseLocked" ), - DEFINE_OUTPUT( m_OnIn, "OnIn" ), - DEFINE_OUTPUT( m_OnOut, "OnOut" ), - -END_DATADESC() - - -LINK_ENTITY_TO_CLASS( func_button, CBaseButton ); - - - -void CBaseButton::Precache( void ) -{ - // get door button sounds, for doors which require buttons to open - if (m_bLockedSound) - { - m_ls.sLockedSound = MakeButtonSound( (int)m_bLockedSound ); - PrecacheScriptSound(m_ls.sLockedSound.ToCStr()); - } - - if (m_bUnlockedSound) - { - m_ls.sUnlockedSound = MakeButtonSound( (int)m_bUnlockedSound ); - PrecacheScriptSound(m_ls.sUnlockedSound.ToCStr()); - } - - // get sentence group names, for doors which are directly 'touched' to open - - switch (m_bLockedSentence) - { - case 1: m_ls.sLockedSentence = MAKE_STRING("NA"); break; // access denied - case 2: m_ls.sLockedSentence = MAKE_STRING("ND"); break; // security lockout - case 3: m_ls.sLockedSentence = MAKE_STRING("NF"); break; // blast door - case 4: m_ls.sLockedSentence = MAKE_STRING("NFIRE"); break; // fire door - case 5: m_ls.sLockedSentence = MAKE_STRING("NCHEM"); break; // chemical door - case 6: m_ls.sLockedSentence = MAKE_STRING("NRAD"); break; // radiation door - case 7: m_ls.sLockedSentence = MAKE_STRING("NCON"); break; // gen containment - case 8: m_ls.sLockedSentence = MAKE_STRING("NH"); break; // maintenance door - case 9: m_ls.sLockedSentence = MAKE_STRING("NG"); break; // broken door - - default: m_ls.sLockedSentence = NULL_STRING; break; - } - - switch (m_bUnlockedSentence) - { - case 1: m_ls.sUnlockedSentence = MAKE_STRING("EA"); break; // access granted - case 2: m_ls.sUnlockedSentence = MAKE_STRING("ED"); break; // security door - case 3: m_ls.sUnlockedSentence = MAKE_STRING("EF"); break; // blast door - case 4: m_ls.sUnlockedSentence = MAKE_STRING("EFIRE"); break; // fire door - case 5: m_ls.sUnlockedSentence = MAKE_STRING("ECHEM"); break; // chemical door - case 6: m_ls.sUnlockedSentence = MAKE_STRING("ERAD"); break; // radiation door - case 7: m_ls.sUnlockedSentence = MAKE_STRING("ECON"); break; // gen containment - case 8: m_ls.sUnlockedSentence = MAKE_STRING("EH"); break; // maintenance door - - default: m_ls.sUnlockedSentence = NULL_STRING; break; - } - - if ( m_sNoise != NULL_STRING ) - { - PrecacheScriptSound( STRING( m_sNoise ) ); - } -} - - -//----------------------------------------------------------------------------- -// Purpose: Cache user-entity-field values until spawn is called. -// Input : szKeyName - -// szValue - -// Output : Returns true if handled, false if not. -//----------------------------------------------------------------------------- -bool CBaseButton::KeyValue( const char *szKeyName, const char *szValue ) -{ - if (FStrEq(szKeyName, "locked_sound")) - { - m_bLockedSound = atof(szValue); - } - else if (FStrEq(szKeyName, "locked_sentence")) - { - m_bLockedSentence = atof(szValue); - } - else if (FStrEq(szKeyName, "unlocked_sound")) - { - m_bUnlockedSound = atof(szValue); - } - else if (FStrEq(szKeyName, "unlocked_sentence")) - { - m_bUnlockedSentence = atof(szValue); - } - else - { - return BaseClass::KeyValue( szKeyName, szValue ); - } - - return true; -} - - -//----------------------------------------------------------------------------- -// Purpose: Locks the button. If locked, the button will play the locked sound -// when the player tries to use it. -//----------------------------------------------------------------------------- -void CBaseButton::Lock() -{ - m_bLocked = true; -} - - -//----------------------------------------------------------------------------- -// Purpose: Unlocks the button, making it able to be pressed again. -//----------------------------------------------------------------------------- -void CBaseButton::Unlock() -{ - m_bLocked = false; -} - - -//----------------------------------------------------------------------------- -// Purpose: Locks the button. If locked, the button will play the locked sound -// when the player tries to use it. -//----------------------------------------------------------------------------- -void CBaseButton::InputLock( inputdata_t &inputdata ) -{ - Lock(); -} - - -//----------------------------------------------------------------------------- -// Purpose: Unlocks the button, making it able to be pressed again. -//----------------------------------------------------------------------------- -void CBaseButton::InputUnlock( inputdata_t &inputdata ) -{ - Unlock(); -} - - -//----------------------------------------------------------------------------- -// Purpose: Locks the button. If locked, the button will play the locked sound -// when the player tries to use it. -//----------------------------------------------------------------------------- -void CBaseButton::InputPress( inputdata_t &inputdata ) -{ - if (m_toggle_state == TS_GOING_UP || m_toggle_state == TS_GOING_DOWN ) - { - return; - } - - // FIXME: consolidate all the button press code into one place! - if (m_bLocked) - { - // play button locked sound - PlayLockSounds(this, &m_ls, TRUE, TRUE); - return; - } - - // Temporarily disable the touch function, until movement is finished. - SetTouch( NULL ); - - if ( m_toggle_state == TS_AT_TOP) - { - if ( m_sNoise != NULL_STRING ) - { - CPASAttenuationFilter filter( this ); - - EmitSound_t ep; - ep.m_nChannel = CHAN_VOICE; - ep.m_pSoundName = (char*)STRING(m_sNoise); - ep.m_flVolume = 1; - ep.m_SoundLevel = SNDLVL_NORM; - - EmitSound( filter, entindex(), ep ); - } - - m_OnPressed.FireOutput(m_hActivator, this); - ButtonReturn(); - } - else - { - m_OnPressed.FireOutput(m_hActivator, this); - ButtonActivate( ); - } -} - - -//----------------------------------------------------------------------------- -// Purpose: We have been damaged. Possibly activate, depending on our flags. -// Input : pInflictor - -// pAttacker - -// flDamage - -// bitsDamageType - -// Output : -//----------------------------------------------------------------------------- -int CBaseButton::OnTakeDamage( const CTakeDamageInfo &info ) -{ - m_OnDamaged.FireOutput(m_hActivator, this); - - // dvsents2: remove obselete health keyvalue from func_button - if (!HasSpawnFlags(SF_BUTTON_DAMAGE_ACTIVATES) && (m_iHealth == 0)) - { - return(0); - } - - BUTTON_CODE code = ButtonResponseToTouch(); - - if ( code == BUTTON_NOTHING ) - return 0; - - m_hActivator = info.GetAttacker(); - - // dvsents2: why would activator be NULL here? - if ( m_hActivator == NULL ) - return 0; - - if (m_bLocked) - { - return(0); - } - - // Temporarily disable the touch function, until movement is finished. - SetTouch( NULL ); - - if ( code == BUTTON_RETURN ) - { - if ( m_sNoise != NULL_STRING ) - { - CPASAttenuationFilter filter( this ); - - EmitSound_t ep; - ep.m_nChannel = CHAN_VOICE; - ep.m_pSoundName = (char*)STRING(m_sNoise); - ep.m_flVolume = 1; - ep.m_SoundLevel = SNDLVL_NORM; - - EmitSound( filter, entindex(), ep ); - } - - m_OnPressed.FireOutput(m_hActivator, this); - ButtonReturn(); - } - else - { - // code == BUTTON_ACTIVATE - m_OnPressed.FireOutput(m_hActivator, this); - ButtonActivate( ); - } - - return 0; -} - - -void CBaseButton::Spawn( ) -{ - //---------------------------------------------------- - //determine sounds for buttons - //a sound of 0 should not make a sound - //---------------------------------------------------- - if ( m_sounds ) - { - m_sNoise = MakeButtonSound( m_sounds ); - PrecacheScriptSound(m_sNoise.ToCStr()); - } - else - { - m_sNoise = NULL_STRING; - } - - Precache(); - - if ( HasSpawnFlags( SF_BUTTON_SPARK_IF_OFF ) )// this button should spark in OFF state - { - SetThink ( &CBaseButton::ButtonSpark ); - SetNextThink( gpGlobals->curtime + 0.5f );// no hurry, make sure everything else spawns - } - - // Convert movedir from angles to a vector - QAngle angMoveDir = QAngle( m_vecMoveDir.x, m_vecMoveDir.y, m_vecMoveDir.z ); - AngleVectors( angMoveDir, &m_vecMoveDir ); - - SetMoveType( MOVETYPE_PUSH ); - SetSolid( SOLID_BSP ); - SetModel( STRING( GetModelName() ) ); - - if (m_flSpeed == 0) - { - m_flSpeed = 40; - } - - m_takedamage = DAMAGE_YES; - - if (m_flWait == 0) - { - m_flWait = 1; - } - - if (m_flLip == 0) - { - m_flLip = 4; - } - - m_toggle_state = TS_AT_BOTTOM; - m_vecPosition1 = GetLocalOrigin(); - - // Subtract 2 from size because the engine expands bboxes by 1 in all directions making the size too big - Vector vecButtonOBB = CollisionProp()->OBBSize(); - vecButtonOBB -= Vector( 2, 2, 2 ); - m_vecPosition2 = m_vecPosition1 + (m_vecMoveDir * (DotProductAbs( m_vecMoveDir, vecButtonOBB ) - m_flLip)); - - // Is this a non-moving button? - if ( ((m_vecPosition2 - m_vecPosition1).Length() < 1) || HasSpawnFlags(SF_BUTTON_DONTMOVE) ) - { - m_vecPosition2 = m_vecPosition1; - } - - m_fStayPushed = (m_flWait == -1 ? TRUE : FALSE); - m_fRotating = FALSE; - - if (HasSpawnFlags(SF_BUTTON_LOCKED)) - { - m_bLocked = true; - } - - // - // If using activates the button, set its use function. - // - if (HasSpawnFlags(SF_BUTTON_USE_ACTIVATES)) - { - SetUse(&CBaseButton::ButtonUse); - } - else - { - SetUse(NULL); - } - - // - // If touching activates the button, set its touch function. - // - if (HasSpawnFlags(SF_BUTTON_TOUCH_ACTIVATES)) - { - SetTouch( &CBaseButton::ButtonTouch ); - } - else - { - SetTouch ( NULL ); - } - - CreateVPhysics(); -} - -//----------------------------------------------------------------------------- - -bool CBaseButton::CreateVPhysics() -{ - VPhysicsInitShadow( false, false ); - return true; -} - - -//----------------------------------------------------------------------------- -// Purpose: Button sound table. -// Also used by CBaseDoor to get 'touched' door lock/unlock sounds -// Input : sound - index of sound to look up. -// Output : Returns a pointer to the corresponding sound file. -//----------------------------------------------------------------------------- -string_t MakeButtonSound( int sound ) -{ - char tmp[1024]; - Q_snprintf( tmp, sizeof(tmp), "Buttons.snd%d", sound ); - return AllocPooledString(tmp); -} - - -//----------------------------------------------------------------------------- -// Purpose: Think function that emits sparks at random intervals. -//----------------------------------------------------------------------------- -void CBaseButton::ButtonSpark ( void ) -{ - SetThink ( &CBaseButton::ButtonSpark ); - SetNextThink( gpGlobals->curtime + 0.1 + random->RandomFloat ( 0, 1.5 ) );// spark again at random interval - - DoSpark( this, WorldSpaceCenter(), 1, 1, true, vec3_origin ); -} - - -//----------------------------------------------------------------------------- -// Purpose: Called when someone uses us whilst we are locked. -//----------------------------------------------------------------------------- -bool CBaseButton::OnUseLocked( CBaseEntity *pActivator ) -{ - PlayLockSounds(this, &m_ls, TRUE, TRUE); - - if ( gpGlobals->curtime > m_flUseLockedTime ) - { - m_OnUseLocked.FireOutput( pActivator, this ); - m_flUseLockedTime = gpGlobals->curtime + 0.5; - return true; - } - - return false; -} - - -//----------------------------------------------------------------------------- -// Purpose: Use function that starts the button moving. -// Input : pActivator - -// pCaller - -// useType - -// value - -//----------------------------------------------------------------------------- -void CBaseButton::ButtonUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) -{ - // Ignore touches if button is moving, or pushed-in and waiting to auto-come-out. - // UNDONE: Should this use ButtonResponseToTouch() too? - if (m_toggle_state == TS_GOING_UP || m_toggle_state == TS_GOING_DOWN ) - return; - - if (m_bLocked) - { - OnUseLocked( pActivator ); - return; - } - - m_hActivator = pActivator; - - if ( m_toggle_state == TS_AT_TOP) - { - // - // If it's a toggle button it can return now. Otherwise, it will either - // return on its own or will stay pressed indefinitely. - // - if ( HasSpawnFlags(SF_BUTTON_TOGGLE)) - { - if ( m_sNoise != NULL_STRING ) - { - CPASAttenuationFilter filter( this ); - - EmitSound_t ep; - ep.m_nChannel = CHAN_VOICE; - ep.m_pSoundName = (char*)STRING(m_sNoise); - ep.m_flVolume = 1; - ep.m_SoundLevel = SNDLVL_NORM; - - EmitSound( filter, entindex(), ep ); - } - - m_OnPressed.FireOutput(m_hActivator, this); - ButtonReturn(); - } - } - else - { - m_OnPressed.FireOutput(m_hActivator, this); - ButtonActivate( ); - } -} - - -//----------------------------------------------------------------------------- -// Purpose: Returns a code indicating how the button should respond to being touched. -// Output : Returns one of the following: -// BUTTON_NOTHING - do nothing -// BUTTON_RETURN - -// BUTTON_ACTIVATE - act as if pressed -//----------------------------------------------------------------------------- -CBaseButton::BUTTON_CODE CBaseButton::ButtonResponseToTouch( void ) -{ - // Ignore touches if button is moving, or pushed-in and waiting to auto-come-out. - if (m_toggle_state == TS_GOING_UP || - m_toggle_state == TS_GOING_DOWN || - (m_toggle_state == TS_AT_TOP && !m_fStayPushed && !HasSpawnFlags(SF_BUTTON_TOGGLE) ) ) - return BUTTON_NOTHING; - - if (m_toggle_state == TS_AT_TOP) - { - if ( HasSpawnFlags(SF_BUTTON_TOGGLE) && !m_fStayPushed) - { - return BUTTON_RETURN; - } - } - else - return BUTTON_ACTIVATE; - - return BUTTON_NOTHING; -} - - -//----------------------------------------------------------------------------- -// Purpose: Touch function that activates the button if it responds to touch. -// Input : pOther - The entity that touched us. -//----------------------------------------------------------------------------- -void CBaseButton::ButtonTouch( CBaseEntity *pOther ) -{ - // Ignore touches by anything but players - if ( !pOther->IsPlayer() ) - return; - - m_hActivator = pOther; - - BUTTON_CODE code = ButtonResponseToTouch(); - - if ( code == BUTTON_NOTHING ) - return; - - if (!UTIL_IsMasterTriggered(m_sMaster, pOther) || m_bLocked) - { - // play button locked sound - PlayLockSounds(this, &m_ls, TRUE, TRUE); - return; - } - - // Temporarily disable the touch function, until movement is finished. - SetTouch( NULL ); - - if ( code == BUTTON_RETURN ) - { - if ( m_sNoise != NULL_STRING ) - { - CPASAttenuationFilter filter( this ); - - EmitSound_t ep; - ep.m_nChannel = CHAN_VOICE; - ep.m_pSoundName = (char*)STRING(m_sNoise); - ep.m_flVolume = 1; - ep.m_SoundLevel = SNDLVL_NORM; - - EmitSound( filter, entindex(), ep ); - } - - m_OnPressed.FireOutput(m_hActivator, this); - ButtonReturn(); - } - else - { - // code == BUTTON_ACTIVATE - m_OnPressed.FireOutput(m_hActivator, this); - ButtonActivate( ); - } -} - - -//----------------------------------------------------------------------------- -// Purpose: Starts the button moving "in/up". -// Input : *pOther - -//----------------------------------------------------------------------------- -void CBaseButton::ButtonActivate( void ) -{ - if ( m_sNoise != NULL_STRING ) - { - CPASAttenuationFilter filter( this ); - - EmitSound_t ep; - ep.m_nChannel = CHAN_VOICE; - ep.m_pSoundName = (char*)STRING(m_sNoise); - ep.m_flVolume = 1; - ep.m_SoundLevel = SNDLVL_NORM; - - EmitSound( filter, entindex(), ep ); - } - - if (!UTIL_IsMasterTriggered(m_sMaster, m_hActivator) || m_bLocked) - { - // button is locked, play locked sound - PlayLockSounds(this, &m_ls, TRUE, TRUE); - return; - } - else - { - // button is unlocked, play unlocked sound - PlayLockSounds(this, &m_ls, FALSE, TRUE); - } - - ASSERT(m_toggle_state == TS_AT_BOTTOM); - m_toggle_state = TS_GOING_UP; - - SetMoveDone( &CBaseButton::TriggerAndWait ); - if (!m_fRotating) - LinearMove( m_vecPosition2, m_flSpeed); - else - AngularMove( m_vecAngle2, m_flSpeed); -} - - -//----------------------------------------------------------------------------- -// Purpose: Enables or disables the use capability based on our spawnflags. -//----------------------------------------------------------------------------- -int CBaseButton::ObjectCaps(void) -{ - return((BaseClass::ObjectCaps() & ~FCAP_ACROSS_TRANSITION) | - (HasSpawnFlags(SF_BUTTON_USE_ACTIVATES) ? (FCAP_IMPULSE_USE | FCAP_USE_IN_RADIUS) : 0)); -} - - -//----------------------------------------------------------------------------- -// Purpose: Button has reached the "in/up" position. Activate its "targets", -// and pause before "popping out". -//----------------------------------------------------------------------------- -void CBaseButton::TriggerAndWait( void ) -{ - ASSERT(m_toggle_state == TS_GOING_UP); - - if (!UTIL_IsMasterTriggered(m_sMaster, m_hActivator) || m_bLocked) - { - return; - } - - m_toggle_state = TS_AT_TOP; - - // - // Re-instate touches if the button is of the toggle variety. - // - if (m_fStayPushed || HasSpawnFlags(SF_BUTTON_TOGGLE ) ) - { - if (HasSpawnFlags(SF_BUTTON_TOUCH_ACTIVATES)) - { - SetTouch(&CBaseButton::ButtonTouch); - } - else - { - // BUGBUG: ALL buttons no longer respond to touch - SetTouch (NULL); - } - } - - // - // If button automatically comes back out, start it moving out. - // - else - { - SetNextThink( gpGlobals->curtime + m_flWait ); - SetThink( &CBaseButton::ButtonReturn ); - } - - m_nState = 1; // use alternate textures - - m_OnIn.FireOutput(m_hActivator, this); -} - - -//----------------------------------------------------------------------------- -// Purpose: Starts the button moving "out/down". -//----------------------------------------------------------------------------- -void CBaseButton::ButtonReturn( void ) -{ - ASSERT(m_toggle_state == TS_AT_TOP); - m_toggle_state = TS_GOING_DOWN; - - SetMoveDone( &CBaseButton::ButtonBackHome ); - if (!m_fRotating) - LinearMove( m_vecPosition1, m_flSpeed); - else - AngularMove( m_vecAngle1, m_flSpeed); - - m_nState = 0; // use normal textures -} - - -//----------------------------------------------------------------------------- -// Purpose: Button has returned to start state. Quiesce it. -//----------------------------------------------------------------------------- -void CBaseButton::ButtonBackHome( void ) -{ - ASSERT(m_toggle_state == TS_GOING_DOWN); - m_toggle_state = TS_AT_BOTTOM; - - m_OnOut.FireOutput(m_hActivator, this); - - // - // Re-instate touch method, movement cycle is complete. - // - if (HasSpawnFlags(SF_BUTTON_TOUCH_ACTIVATES)) - { - SetTouch( &CBaseButton::ButtonTouch ); - } - else - { - // BUGBUG: ALL buttons no longer respond to touch - SetTouch ( NULL ); - } - - // reset think for a sparking button - if (HasSpawnFlags( SF_BUTTON_SPARK_IF_OFF ) ) - { - SetThink ( &CBaseButton::ButtonSpark ); - SetNextThink( gpGlobals->curtime + 0.5f );// no hurry - } -} - - -// -// Rotating button (aka "lever") -// -LINK_ENTITY_TO_CLASS( func_rot_button, CRotButton ); - - -void CRotButton::Spawn( void ) -{ - //---------------------------------------------------- - //determine sounds for buttons - //a sound of 0 should not make a sound - //---------------------------------------------------- - if ( m_sounds ) - { - m_sNoise = MakeButtonSound( m_sounds ); - PrecacheScriptSound(m_sNoise.ToCStr()); - } - else - { - m_sNoise = NULL_STRING; - } - - // set the axis of rotation - CBaseToggle::AxisDir(); - - // check for clockwise rotation - if ( HasSpawnFlags( SF_DOOR_ROTATE_BACKWARDS) ) - { - m_vecMoveAng = m_vecMoveAng * -1; - } - - SetMoveType( MOVETYPE_PUSH ); - -#ifdef HL1_DLL - SetSolid( SOLID_BSP ); -#else - SetSolid( SOLID_VPHYSICS ); -#endif - if ( HasSpawnFlags( SF_ROTBUTTON_NOTSOLID ) ) - { - AddEFlags( EFL_USE_PARTITION_WHEN_NOT_SOLID ); - AddSolidFlags( FSOLID_NOT_SOLID ); - } - - SetModel( STRING( GetModelName() ) ); - - if (m_flSpeed == 0) - m_flSpeed = 40; - - if (m_flWait == 0) - m_flWait = 1; - - if (m_iHealth > 0) - { - m_takedamage = DAMAGE_YES; - } - - m_toggle_state = TS_AT_BOTTOM; - m_vecAngle1 = GetLocalAngles(); - m_vecAngle2 = GetLocalAngles() + m_vecMoveAng * m_flMoveDistance; - ASSERTSZ(m_vecAngle1 != m_vecAngle2, "rotating button start/end positions are equal\n"); - - m_fStayPushed = (m_flWait == -1 ? TRUE : FALSE); - m_fRotating = TRUE; - - SetUse(&CRotButton::ButtonUse); - - // - // If touching activates the button, set its touch function. - // - if (!HasSpawnFlags(SF_BUTTON_TOUCH_ACTIVATES)) - { - SetTouch ( NULL ); - } - else - { - SetTouch( &CRotButton::ButtonTouch ); - } - - CreateVPhysics(); -} - - -bool CRotButton::CreateVPhysics( void ) -{ - VPhysicsInitShadow( false, false ); - return true; -} - - -//----------------------------------------------------------------------------- -// CMomentaryRotButton spawnflags -//----------------------------------------------------------------------------- -#define SF_MOMENTARY_DOOR 1 -#define SF_MOMENTARY_NOT_USABLE 2 -#define SF_MOMENTARY_AUTO_RETURN 16 - - -BEGIN_DATADESC( CMomentaryRotButton ) - - DEFINE_FIELD( m_lastUsed, FIELD_INTEGER ), - DEFINE_FIELD( m_start, FIELD_VECTOR ), - DEFINE_FIELD( m_end, FIELD_VECTOR ), - DEFINE_FIELD( m_IdealYaw, FIELD_FLOAT ), - DEFINE_FIELD( m_flTimeDelta, FIELD_FLOAT ), - DEFINE_FIELD( m_sNoise, FIELD_SOUNDNAME ), - DEFINE_FIELD( m_bUpdateTarget, FIELD_BOOLEAN ), - - DEFINE_KEYFIELD( m_direction, FIELD_INTEGER, "StartDirection" ), - DEFINE_KEYFIELD( m_returnSpeed, FIELD_FLOAT, "returnspeed" ), - DEFINE_KEYFIELD( m_flStartPosition, FIELD_FLOAT, "StartPosition"), - DEFINE_KEYFIELD( m_bSolidBsp, FIELD_BOOLEAN, "solidbsp" ), - - // Function Pointers - DEFINE_FUNCTION( UseMoveDone ), - DEFINE_FUNCTION( ReturnMoveDone ), - DEFINE_FUNCTION( SetPositionMoveDone ), - DEFINE_FUNCTION( UpdateThink ), - - // Inputs - DEFINE_INPUTFUNC( FIELD_FLOAT, "SetPosition", InputSetPosition ), - DEFINE_INPUTFUNC( FIELD_FLOAT, "SetPositionImmediately", InputSetPositionImmediately ), - DEFINE_INPUTFUNC( FIELD_VOID, "_DisableUpdateTarget", InputDisableUpdateTarget ), - DEFINE_INPUTFUNC( FIELD_VOID, "_EnableUpdateTarget", InputEnableUpdateTarget ), - - // Outputs - DEFINE_OUTPUT( m_Position, "Position" ), - DEFINE_OUTPUT( m_OnUnpressed, "OnUnpressed" ), - DEFINE_OUTPUT( m_OnFullyClosed, "OnFullyClosed" ), - DEFINE_OUTPUT( m_OnFullyOpen, "OnFullyOpen" ), - DEFINE_OUTPUT( m_OnReachedPosition, "OnReachedPosition" ), - - DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ), - DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ), - DEFINE_FIELD( m_bDisabled, FIELD_BOOLEAN ) - -END_DATADESC() - - -LINK_ENTITY_TO_CLASS( momentary_rot_button, CMomentaryRotButton ); - - -//----------------------------------------------------------------------------- -// Purpose: Called when spawning, after keyvalues have been handled. -//----------------------------------------------------------------------------- -void CMomentaryRotButton::Spawn( void ) -{ - CBaseToggle::AxisDir(); - - m_bUpdateTarget = true; - - if ( m_flSpeed == 0 ) - { - m_flSpeed = 100; - } - - // Clamp start position and issue bounds warning - if (m_flStartPosition < 0.0 || m_flStartPosition > 1.0) - { - Warning("WARNING: Momentary door (%s) start position not between 0 and 1. Clamping.\n",GetDebugName()); - m_flStartPosition = clamp(m_IdealYaw, 0, 1); - } - - // Check direction fields (for backward compatibility) - if (m_direction != 1 && m_direction != -1) - { - m_direction = 1; - } - - if (m_flMoveDistance < 0) - { - m_vecMoveAng = m_vecMoveAng * -1; - m_flMoveDistance = -m_flMoveDistance; - } - - m_start = GetLocalAngles() - m_vecMoveAng * m_flMoveDistance * m_flStartPosition; - m_end = GetLocalAngles() + m_vecMoveAng * m_flMoveDistance * (1-m_flStartPosition); - - m_IdealYaw = m_flStartPosition; - - // Force start direction at end points - if (m_flStartPosition == 0.0) - { - m_direction = -1; - } - else if (m_flStartPosition == 1.0) - { - m_direction = 1; - } - - if (HasSpawnFlags(SF_BUTTON_LOCKED)) - { - m_bLocked = true; - } - - if ( HasSpawnFlags( SF_BUTTON_USE_ACTIVATES ) ) - { - if ( m_sounds ) - { - m_sNoise = MakeButtonSound( m_sounds ); - PrecacheScriptSound(m_sNoise.ToCStr()); - } - else - { - m_sNoise = NULL_STRING; - } - - m_lastUsed = 0; - UpdateTarget(0,this); - } - -#ifdef HL1_DLL - SetSolid( SOLID_BSP ); -#else - SetSolid( SOLID_VPHYSICS ); -#endif - if (HasSpawnFlags(SF_ROTBUTTON_NOTSOLID)) - { - AddEFlags( EFL_USE_PARTITION_WHEN_NOT_SOLID ); - AddSolidFlags( FSOLID_NOT_SOLID ); - } - - SetMoveType( MOVETYPE_PUSH ); - SetModel( STRING( GetModelName() ) ); - - CreateVPhysics(); - - // Slam the object back to solid - if we really want it to be solid. - if ( m_bSolidBsp ) - { - SetSolid( SOLID_BSP ); - } - - m_bDisabled = false; -} - -int CMomentaryRotButton::ObjectCaps( void ) -{ - int flags = BaseClass::ObjectCaps(); - if (!HasSpawnFlags(SF_BUTTON_USE_ACTIVATES)) - { - return flags; - } - else - { - return (flags | FCAP_CONTINUOUS_USE | FCAP_USE_IN_RADIUS); - } -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -bool CMomentaryRotButton::CreateVPhysics( void ) -{ - VPhysicsInitShadow( false, false ); - - return true; -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CMomentaryRotButton::PlaySound( void ) -{ - if ( m_sNoise == NULL_STRING ) - return; - - CPASAttenuationFilter filter( this ); - - EmitSound_t ep; - ep.m_nChannel = CHAN_VOICE; - ep.m_pSoundName = (char*)STRING(m_sNoise); - ep.m_flVolume = 1; - ep.m_SoundLevel = SNDLVL_NORM; - - EmitSound( filter, entindex(), ep ); -} - - -//----------------------------------------------------------------------------- -// Purpose: Returns a given angular position as a value along our motion from 0 to 1. -// Input : vecAngles - -//----------------------------------------------------------------------------- -float CMomentaryRotButton::GetPos( const QAngle &vecAngles ) -{ - float flScale = 1; - if (( m_vecMoveAng[0] < 0 ) || ( m_vecMoveAng[1] < 0 ) || ( m_vecMoveAng[2] < 0 )) - { - flScale = -1; - } - - float flPos = flScale * CBaseToggle::AxisDelta( m_spawnflags, vecAngles, m_start ) / m_flMoveDistance; - return( clamp( flPos, 0, 1 )); -} - - -//------------------------------------------------------------------------------ -// Purpose : -// Input : flPosition -//------------------------------------------------------------------------------ -void CMomentaryRotButton::InputSetPosition( inputdata_t &inputdata ) -{ - m_IdealYaw = clamp( inputdata.value.Float(), 0, 1 ); - - float flCurPos = GetPos( GetLocalAngles() ); - if ( flCurPos < m_IdealYaw ) - { - // Moving forward (from start to end). - SetLocalAngularVelocity( m_flSpeed * m_vecMoveAng ); - m_direction = 1; - } - else if ( flCurPos > m_IdealYaw ) - { - // Moving backward (from end to start). - SetLocalAngularVelocity( -m_flSpeed * m_vecMoveAng ); - m_direction = -1; - } - else - { - // We're there already; nothing to do. - SetLocalAngularVelocity( vec3_angle ); - return; - } - - SetMoveDone( &CMomentaryRotButton::SetPositionMoveDone ); - - SetThink( &CMomentaryRotButton::UpdateThink ); - SetNextThink( gpGlobals->curtime ); - - // - // Think again in 0.1 seconds or the time that it will take us to reach our movement goal, - // whichever is the shorter interval. This prevents us from overshooting and stuttering when we - // are told to change position in very small increments. - // - QAngle vecNewAngles = m_start + m_vecMoveAng * ( m_IdealYaw * m_flMoveDistance ); - float flAngleDelta = fabs( AxisDelta( m_spawnflags, vecNewAngles, GetLocalAngles() )); - m_flTimeDelta = min( flAngleDelta / m_flSpeed, 0.1f ); - - SetMoveDoneTime( m_flTimeDelta ); -} - - -//------------------------------------------------------------------------------ -// Purpose : -// Input : flPosition -//------------------------------------------------------------------------------ -void CMomentaryRotButton::InputSetPositionImmediately( inputdata_t &inputdata ) -{ - m_IdealYaw = clamp( inputdata.value.Float(), 0, 1 ); - SetLocalAngles( m_start + m_vecMoveAng * ( m_IdealYaw * m_flMoveDistance ) ); -} - - -//------------------------------------------------------------------------------ -// Purpose: Turns off target updates so that we can change the wheel's position -// without changing the target's position. Used for jiggling when locked. -//------------------------------------------------------------------------------ -void CMomentaryRotButton::InputDisableUpdateTarget( inputdata_t &inputdata ) -{ - m_bUpdateTarget = false; -} - - -//------------------------------------------------------------------------------ -// Purpose: Turns target updates back on (after jiggling). -//------------------------------------------------------------------------------ -void CMomentaryRotButton::InputEnableUpdateTarget( inputdata_t &inputdata ) -{ - m_bUpdateTarget = true; -} - - -//----------------------------------------------------------------------------- -// Purpose: Locks the button. If locked, the button will play the locked sound -// when the player tries to use it. -//----------------------------------------------------------------------------- -void CMomentaryRotButton::Lock() -{ - BaseClass::Lock(); - - SetLocalAngularVelocity( vec3_angle ); - SetMoveDoneTime( -1 ); - SetMoveDone( NULL ); - - SetNextThink( TICK_NEVER_THINK ); - SetThink( NULL ); -} - - -//----------------------------------------------------------------------------- -// Purpose: Unlocks the button, making it able to be pressed again. -//----------------------------------------------------------------------------- -void CMomentaryRotButton::Unlock() -{ - BaseClass::Unlock(); - - SetMoveDone( &CMomentaryRotButton::ReturnMoveDone ); - - // Delay before autoreturn. - SetMoveDoneTime( 0.1f ); -} - - -//----------------------------------------------------------------------------- -// Purpose: Fires the appropriate outputs at the extremes of motion. -//----------------------------------------------------------------------------- -void CMomentaryRotButton::OutputMovementComplete( void ) -{ - if (m_IdealYaw == 1.0) - { - m_OnFullyClosed.FireOutput(this, this); - } - else if (m_IdealYaw == 0.0) - { - m_OnFullyOpen.FireOutput(this, this); - } - - m_OnReachedPosition.FireOutput( this, this ); -} - - -//------------------------------------------------------------------------------ -// Purpose: MoveDone function for the SetPosition input handler. Tracks our -// progress toward a movement goal and updates our outputs. -//------------------------------------------------------------------------------ -void CMomentaryRotButton::SetPositionMoveDone(void) -{ - float flCurPos = GetPos( GetLocalAngles() ); - - if ((( flCurPos >= m_IdealYaw ) && ( m_direction == 1 )) || - (( flCurPos <= m_IdealYaw ) && ( m_direction == -1 ))) - { - // - // We reached or surpassed our movement goal. - // - SetLocalAngularVelocity( vec3_angle ); - SetLocalAngles( m_start + m_vecMoveAng * ( m_IdealYaw * m_flMoveDistance ) ); - SetNextThink( TICK_NEVER_THINK ); - SetMoveDoneTime( -1 ); - UpdateTarget( m_IdealYaw, this ); - OutputMovementComplete(); - return; - } - - // TODO: change this to use a Think function like ReturnThink. - SetMoveDoneTime( m_flTimeDelta ); -} - - -//----------------------------------------------------------------------------- -// Purpose: -// Input : pActivator - -// pCaller - -// useType - -// value - -//----------------------------------------------------------------------------- -void CMomentaryRotButton::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) -{ - if ( m_bDisabled == true ) - return; - - if (m_bLocked) - { - if ( OnUseLocked( pActivator ) && HasSpawnFlags( SF_BUTTON_JIGGLE_ON_USE_LOCKED ) ) - { - // Jiggle two degrees. - float flDist = 2.0 / m_flMoveDistance; - - // Must be first! - g_EventQueue.AddEvent( this, "_DisableUpdateTarget", 0, this, this ); - - variant_t value; - value.SetFloat( flDist ); - g_EventQueue.AddEvent( this, "SetPosition", value, 0.01, this, this ); - - value.SetFloat( 0.0 ); - g_EventQueue.AddEvent( this, "SetPosition", value, 0.1, this, this ); - - value.SetFloat( 0.5 * flDist ); - g_EventQueue.AddEvent( this, "SetPosition", value, 0.2, this, this ); - - value.SetFloat( 0.0 ); - g_EventQueue.AddEvent( this, "SetPosition", value, 0.3, this, this ); - - // Must be last! And must be late enough to cover the settling time. - g_EventQueue.AddEvent( this, "_EnableUpdateTarget", 0.5, this, this ); - } - - return; - } - - // - // Reverse our direction and play movement sound every time the player - // pauses between uses. - // - bool bPlaySound = false; - - if ( !m_lastUsed ) - { - bPlaySound = true; - m_direction = -m_direction; - - //Alert that we've been pressed - m_OnPressed.FireOutput( m_hActivator, this ); - } - - m_lastUsed = 1; - - float flPos = GetPos( GetLocalAngles() ); - UpdateSelf( flPos, bPlaySound ); - - // - // Think every frame while we are moving. - // - SetThink( &CMomentaryRotButton::UpdateThink ); - SetNextThink( gpGlobals->curtime ); -} - - -//----------------------------------------------------------------------------- -// Purpose: Handles changing direction at the extremes of our range of motion -// and updating our avelocity while being used by the player. -// Input : value - Number from 0 to 1 indicating our desired position within -// our range of motion, 0 = start, 1 = end. -//----------------------------------------------------------------------------- -void CMomentaryRotButton::UpdateSelf( float value, bool bPlaySound ) -{ - // - // Set our move clock to 0.1 seconds in the future so we stop spinning unless we are - // used again before then. - // - SetMoveDoneTime( 0.1 ); - - // - // If we hit the end, zero our avelocity and snap to the end angles. - // - if ( m_direction > 0 && value >= 1.0 ) - { - SetLocalAngularVelocity( vec3_angle ); - SetLocalAngles( m_end ); - - m_OnFullyClosed.FireOutput(this, this); - return; - } - // - // If we returned to the start, zero our avelocity and snap to the start angles. - // - else if ( m_direction < 0 && value <= 0 ) - { - SetLocalAngularVelocity( vec3_angle ); - SetLocalAngles( m_start ); - - m_OnFullyOpen.FireOutput(this, this); - return; - } - - if ( bPlaySound ) - { - PlaySound(); - } - - SetLocalAngularVelocity( ( m_direction * m_flSpeed ) * m_vecMoveAng ); - SetMoveDone( &CMomentaryRotButton::UseMoveDone ); -} - - -//----------------------------------------------------------------------------- -// Purpose: Updates the value of our position, firing any targets. -// Input : value - New position, from 0 - 1. -//----------------------------------------------------------------------------- -void CMomentaryRotButton::UpdateTarget( float value, CBaseEntity *pActivator ) -{ - if ( !m_bUpdateTarget ) - return; - - if (m_Position.Get() != value) - { - m_Position.Set(value, pActivator, this); - } -} - - -//----------------------------------------------------------------------------- -// Purpose: Handles the end of motion caused by player use. -//----------------------------------------------------------------------------- -void CMomentaryRotButton::UseMoveDone( void ) -{ - SetLocalAngularVelocity( vec3_angle ); - - // Make sure our targets stop where we stopped. - float flPos = GetPos( GetLocalAngles() ); - UpdateTarget( flPos, this ); - - // Alert that we've been unpressed - m_OnUnpressed.FireOutput( m_hActivator, this ); - - m_lastUsed = 0; - - if ( !HasSpawnFlags( SF_BUTTON_TOGGLE ) && m_returnSpeed > 0 ) - { - SetMoveDone( &CMomentaryRotButton::ReturnMoveDone ); - m_direction = -1; - - // Delay before autoreturn. - SetMoveDoneTime( 0.1f ); - } - else - { - SetThink( NULL ); - SetMoveDone( NULL ); - } -} - - -//----------------------------------------------------------------------------- -// Purpose: MoveDone function for rotating back to the start position. -//----------------------------------------------------------------------------- -void CMomentaryRotButton::ReturnMoveDone( void ) -{ - float value = GetPos( GetLocalAngles() ); - if ( value <= 0 ) - { - // - // Got back to the start, stop spinning. - // - SetLocalAngularVelocity( vec3_angle ); - SetLocalAngles( m_start ); - - UpdateTarget( 0, NULL ); - - SetMoveDoneTime( -1 ); - SetMoveDone( NULL ); - - SetNextThink( TICK_NEVER_THINK ); - SetThink( NULL ); - } - else - { - SetLocalAngularVelocity( -m_returnSpeed * m_vecMoveAng ); - SetMoveDoneTime( 0.1f ); - - SetThink( &CMomentaryRotButton::UpdateThink ); - SetNextThink( gpGlobals->curtime + 0.01f ); - } -} - - -//----------------------------------------------------------------------------- -// Purpose: Think function for updating target as we move. -//----------------------------------------------------------------------------- -void CMomentaryRotButton::UpdateThink( void ) -{ - float value = GetPos( GetLocalAngles() ); - UpdateTarget( value, NULL ); - SetNextThink( gpGlobals->curtime ); -} - - -//----------------------------------------------------------------------------- -// Purpose: Draw any debug text overlays -// Output : Current text offset from the top -//----------------------------------------------------------------------------- -int CMomentaryRotButton::DrawDebugTextOverlays(void) -{ - int text_offset = BaseClass::DrawDebugTextOverlays(); - - if (m_debugOverlays & OVERLAY_TEXT_BIT) - { - char tempstr[255]; - - Q_snprintf(tempstr,sizeof(tempstr),"QAngle: %.2f %.2f %.2f", GetLocalAngles()[0], GetLocalAngles()[1], GetLocalAngles()[2]); - EntityText(text_offset,tempstr,0); - text_offset++; - - Q_snprintf(tempstr,sizeof(tempstr),"AVelocity: %.2f %.2f %.2f", GetLocalAngularVelocity()[0], GetLocalAngularVelocity()[1], GetLocalAngularVelocity()[2]); - EntityText(text_offset,tempstr,0); - text_offset++; - - Q_snprintf(tempstr,sizeof(tempstr),"Target Pos: %3.3f",m_IdealYaw); - EntityText(text_offset,tempstr,0); - text_offset++; - - float flCurPos = GetPos(GetLocalAngles()); - Q_snprintf(tempstr,sizeof(tempstr),"Current Pos: %3.3f",flCurPos); - EntityText(text_offset,tempstr,0); - text_offset++; - - Q_snprintf(tempstr,sizeof(tempstr),"Direction: %s",(m_direction == 1) ? "Forward" : "Backward"); - EntityText(text_offset,tempstr,0); - text_offset++; - - } - return text_offset; -} - -//----------------------------------------------------------------------------- -// Purpose: Input hander that starts the spawner -//----------------------------------------------------------------------------- -void CMomentaryRotButton::InputEnable( inputdata_t &inputdata ) -{ - Enable(); -} - - -//----------------------------------------------------------------------------- -// Purpose: Input hander that stops the spawner -//----------------------------------------------------------------------------- -void CMomentaryRotButton::InputDisable( inputdata_t &inputdata ) -{ - Disable(); -} - -//----------------------------------------------------------------------------- -// Purpose: Start the spawner -//----------------------------------------------------------------------------- -void CMomentaryRotButton::Enable( void ) -{ - m_bDisabled = false; -} - - -//----------------------------------------------------------------------------- -// Purpose: Stop the spawner -//----------------------------------------------------------------------------- -void CMomentaryRotButton::Disable( void ) -{ - m_bDisabled = true; -} \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: Implements buttons. +// +//=============================================================================// + +#include "cbase.h" +#include "doors.h" +#include "ndebugoverlay.h" +#include "spark.h" +#include "vstdlib/random.h" +#include "engine/IEngineSound.h" +#include "vstdlib/strtools.h" +#include "buttons.h" +#include "eventqueue.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +void PlayLockSounds( CBaseEntity *pEdict, locksound_t *pls, int flocked, int fbutton ); +string_t MakeButtonSound( int sound ); // get string of button sound number + + +#define SF_BUTTON_DONTMOVE 1 +#define SF_ROTBUTTON_NOTSOLID 1 +#define SF_BUTTON_TOGGLE 32 // button stays pushed until reactivated +#define SF_BUTTON_TOUCH_ACTIVATES 256 // Button fires when touched. +#define SF_BUTTON_DAMAGE_ACTIVATES 512 // Button fires when damaged. +#define SF_BUTTON_USE_ACTIVATES 1024 // Button fires when used. +#define SF_BUTTON_LOCKED 2048 // Whether the button is initially locked. +#define SF_BUTTON_SPARK_IF_OFF 4096 // button sparks in OFF state +#define SF_BUTTON_JIGGLE_ON_USE_LOCKED 8192 // whether to jiggle if someone uses us when we're locked + +BEGIN_DATADESC( CBaseButton ) + + DEFINE_KEYFIELD( m_vecMoveDir, FIELD_VECTOR, "movedir" ), + DEFINE_FIELD( m_fStayPushed, FIELD_BOOLEAN ), + DEFINE_FIELD( m_fRotating, FIELD_BOOLEAN ), + + DEFINE_FIELD( m_bLockedSound, FIELD_CHARACTER ), + DEFINE_FIELD( m_bLockedSentence, FIELD_CHARACTER ), + DEFINE_FIELD( m_bUnlockedSound, FIELD_CHARACTER ), + DEFINE_FIELD( m_bUnlockedSentence, FIELD_CHARACTER ), + DEFINE_FIELD( m_bLocked, FIELD_BOOLEAN ), + DEFINE_FIELD( m_sNoise, FIELD_SOUNDNAME ), + DEFINE_FIELD( m_flUseLockedTime, FIELD_TIME ), + DEFINE_FIELD( m_bSolidBsp, FIELD_BOOLEAN ), + + DEFINE_KEYFIELD( m_sounds, FIELD_INTEGER, "sounds" ), + +// DEFINE_FIELD( m_ls, FIELD_SOUNDNAME ), // This is restored in Precache() +// DEFINE_FIELD( m_nState, FIELD_INTEGER ), + + // Function Pointers + DEFINE_FUNCTION( ButtonTouch ), + DEFINE_FUNCTION( ButtonSpark ), + DEFINE_FUNCTION( TriggerAndWait ), + DEFINE_FUNCTION( ButtonReturn ), + DEFINE_FUNCTION( ButtonBackHome ), + DEFINE_FUNCTION( ButtonUse ), + + // Inputs + DEFINE_INPUTFUNC( FIELD_VOID, "Lock", InputLock ), + DEFINE_INPUTFUNC( FIELD_VOID, "Unlock", InputUnlock ), + DEFINE_INPUTFUNC( FIELD_VOID, "Press", InputPress ), + + // Outputs + DEFINE_OUTPUT( m_OnDamaged, "OnDamaged" ), + DEFINE_OUTPUT( m_OnPressed, "OnPressed" ), + DEFINE_OUTPUT( m_OnUseLocked, "OnUseLocked" ), + DEFINE_OUTPUT( m_OnIn, "OnIn" ), + DEFINE_OUTPUT( m_OnOut, "OnOut" ), + +END_DATADESC() + + +LINK_ENTITY_TO_CLASS( func_button, CBaseButton ); + + + +void CBaseButton::Precache( void ) +{ + // get door button sounds, for doors which require buttons to open + if (m_bLockedSound) + { + m_ls.sLockedSound = MakeButtonSound( (int)m_bLockedSound ); + PrecacheScriptSound(m_ls.sLockedSound.ToCStr()); + } + + if (m_bUnlockedSound) + { + m_ls.sUnlockedSound = MakeButtonSound( (int)m_bUnlockedSound ); + PrecacheScriptSound(m_ls.sUnlockedSound.ToCStr()); + } + + // get sentence group names, for doors which are directly 'touched' to open + + switch (m_bLockedSentence) + { + case 1: m_ls.sLockedSentence = MAKE_STRING("NA"); break; // access denied + case 2: m_ls.sLockedSentence = MAKE_STRING("ND"); break; // security lockout + case 3: m_ls.sLockedSentence = MAKE_STRING("NF"); break; // blast door + case 4: m_ls.sLockedSentence = MAKE_STRING("NFIRE"); break; // fire door + case 5: m_ls.sLockedSentence = MAKE_STRING("NCHEM"); break; // chemical door + case 6: m_ls.sLockedSentence = MAKE_STRING("NRAD"); break; // radiation door + case 7: m_ls.sLockedSentence = MAKE_STRING("NCON"); break; // gen containment + case 8: m_ls.sLockedSentence = MAKE_STRING("NH"); break; // maintenance door + case 9: m_ls.sLockedSentence = MAKE_STRING("NG"); break; // broken door + + default: m_ls.sLockedSentence = NULL_STRING; break; + } + + switch (m_bUnlockedSentence) + { + case 1: m_ls.sUnlockedSentence = MAKE_STRING("EA"); break; // access granted + case 2: m_ls.sUnlockedSentence = MAKE_STRING("ED"); break; // security door + case 3: m_ls.sUnlockedSentence = MAKE_STRING("EF"); break; // blast door + case 4: m_ls.sUnlockedSentence = MAKE_STRING("EFIRE"); break; // fire door + case 5: m_ls.sUnlockedSentence = MAKE_STRING("ECHEM"); break; // chemical door + case 6: m_ls.sUnlockedSentence = MAKE_STRING("ERAD"); break; // radiation door + case 7: m_ls.sUnlockedSentence = MAKE_STRING("ECON"); break; // gen containment + case 8: m_ls.sUnlockedSentence = MAKE_STRING("EH"); break; // maintenance door + + default: m_ls.sUnlockedSentence = NULL_STRING; break; + } + + if ( m_sNoise != NULL_STRING ) + { + PrecacheScriptSound( STRING( m_sNoise ) ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Cache user-entity-field values until spawn is called. +// Input : szKeyName - +// szValue - +// Output : Returns true if handled, false if not. +//----------------------------------------------------------------------------- +bool CBaseButton::KeyValue( const char *szKeyName, const char *szValue ) +{ + if (FStrEq(szKeyName, "locked_sound")) + { + m_bLockedSound = static_cast(atof(szValue)); + } + else if (FStrEq(szKeyName, "locked_sentence")) + { + m_bLockedSentence = static_cast(atof(szValue)); + } + else if (FStrEq(szKeyName, "unlocked_sound")) + { + m_bUnlockedSound = static_cast(atof(szValue)); + } + else if (FStrEq(szKeyName, "unlocked_sentence")) + { + m_bUnlockedSentence = static_cast(atof(szValue)); + } + else + { + return BaseClass::KeyValue( szKeyName, szValue ); + } + + return true; +} + + +//----------------------------------------------------------------------------- +// Purpose: Locks the button. If locked, the button will play the locked sound +// when the player tries to use it. +//----------------------------------------------------------------------------- +void CBaseButton::Lock() +{ + m_bLocked = true; +} + + +//----------------------------------------------------------------------------- +// Purpose: Unlocks the button, making it able to be pressed again. +//----------------------------------------------------------------------------- +void CBaseButton::Unlock() +{ + m_bLocked = false; +} + + +//----------------------------------------------------------------------------- +// Purpose: Locks the button. If locked, the button will play the locked sound +// when the player tries to use it. +//----------------------------------------------------------------------------- +void CBaseButton::InputLock( inputdata_t &inputdata ) +{ + Lock(); +} + + +//----------------------------------------------------------------------------- +// Purpose: Unlocks the button, making it able to be pressed again. +//----------------------------------------------------------------------------- +void CBaseButton::InputUnlock( inputdata_t &inputdata ) +{ + Unlock(); +} + + +//----------------------------------------------------------------------------- +// Purpose: Locks the button. If locked, the button will play the locked sound +// when the player tries to use it. +//----------------------------------------------------------------------------- +void CBaseButton::InputPress( inputdata_t &inputdata ) +{ + if (m_toggle_state == TS_GOING_UP || m_toggle_state == TS_GOING_DOWN ) + { + return; + } + + // FIXME: consolidate all the button press code into one place! + if (m_bLocked) + { + // play button locked sound + PlayLockSounds(this, &m_ls, TRUE, TRUE); + return; + } + + // Temporarily disable the touch function, until movement is finished. + SetTouch( NULL ); + + if ( m_toggle_state == TS_AT_TOP) + { + if ( m_sNoise != NULL_STRING ) + { + CPASAttenuationFilter filter( this ); + + EmitSound_t ep; + ep.m_nChannel = CHAN_VOICE; + ep.m_pSoundName = (char*)STRING(m_sNoise); + ep.m_flVolume = 1; + ep.m_SoundLevel = SNDLVL_NORM; + + EmitSound( filter, entindex(), ep ); + } + + m_OnPressed.FireOutput(m_hActivator, this); + ButtonReturn(); + } + else + { + m_OnPressed.FireOutput(m_hActivator, this); + ButtonActivate( ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: We have been damaged. Possibly activate, depending on our flags. +// Input : pInflictor - +// pAttacker - +// flDamage - +// bitsDamageType - +// Output : +//----------------------------------------------------------------------------- +int CBaseButton::OnTakeDamage( const CTakeDamageInfo &info ) +{ + m_OnDamaged.FireOutput(m_hActivator, this); + + // dvsents2: remove obselete health keyvalue from func_button + if (!HasSpawnFlags(SF_BUTTON_DAMAGE_ACTIVATES) && (m_iHealth == 0)) + { + return(0); + } + + BUTTON_CODE code = ButtonResponseToTouch(); + + if ( code == BUTTON_NOTHING ) + return 0; + + m_hActivator = info.GetAttacker(); + + // dvsents2: why would activator be NULL here? + if ( m_hActivator == NULL ) + return 0; + + if (m_bLocked) + { + return(0); + } + + // Temporarily disable the touch function, until movement is finished. + SetTouch( NULL ); + + if ( code == BUTTON_RETURN ) + { + if ( m_sNoise != NULL_STRING ) + { + CPASAttenuationFilter filter( this ); + + EmitSound_t ep; + ep.m_nChannel = CHAN_VOICE; + ep.m_pSoundName = (char*)STRING(m_sNoise); + ep.m_flVolume = 1; + ep.m_SoundLevel = SNDLVL_NORM; + + EmitSound( filter, entindex(), ep ); + } + + m_OnPressed.FireOutput(m_hActivator, this); + ButtonReturn(); + } + else + { + // code == BUTTON_ACTIVATE + m_OnPressed.FireOutput(m_hActivator, this); + ButtonActivate( ); + } + + return 0; +} + + +void CBaseButton::Spawn( ) +{ + //---------------------------------------------------- + //determine sounds for buttons + //a sound of 0 should not make a sound + //---------------------------------------------------- + if ( m_sounds ) + { + m_sNoise = MakeButtonSound( m_sounds ); + PrecacheScriptSound(m_sNoise.ToCStr()); + } + else + { + m_sNoise = NULL_STRING; + } + + Precache(); + + if ( HasSpawnFlags( SF_BUTTON_SPARK_IF_OFF ) )// this button should spark in OFF state + { + SetThink ( &CBaseButton::ButtonSpark ); + SetNextThink( gpGlobals->curtime + 0.5f );// no hurry, make sure everything else spawns + } + + // Convert movedir from angles to a vector + QAngle angMoveDir = QAngle( m_vecMoveDir.x, m_vecMoveDir.y, m_vecMoveDir.z ); + AngleVectors( angMoveDir, &m_vecMoveDir ); + + SetMoveType( MOVETYPE_PUSH ); + SetSolid( SOLID_BSP ); + SetModel( STRING( GetModelName() ) ); + + if (m_flSpeed == 0) + { + m_flSpeed = 40; + } + + m_takedamage = DAMAGE_YES; + + if (m_flWait == 0) + { + m_flWait = 1; + } + + if (m_flLip == 0) + { + m_flLip = 4; + } + + m_toggle_state = TS_AT_BOTTOM; + m_vecPosition1 = GetLocalOrigin(); + + // Subtract 2 from size because the engine expands bboxes by 1 in all directions making the size too big + Vector vecButtonOBB = CollisionProp()->OBBSize(); + vecButtonOBB -= Vector( 2, 2, 2 ); + m_vecPosition2 = m_vecPosition1 + (m_vecMoveDir * (DotProductAbs( m_vecMoveDir, vecButtonOBB ) - m_flLip)); + + // Is this a non-moving button? + if ( ((m_vecPosition2 - m_vecPosition1).Length() < 1) || HasSpawnFlags(SF_BUTTON_DONTMOVE) ) + { + m_vecPosition2 = m_vecPosition1; + } + + m_fStayPushed = (m_flWait == -1 ? TRUE : FALSE); + m_fRotating = FALSE; + + if (HasSpawnFlags(SF_BUTTON_LOCKED)) + { + m_bLocked = true; + } + + // + // If using activates the button, set its use function. + // + if (HasSpawnFlags(SF_BUTTON_USE_ACTIVATES)) + { + SetUse(&CBaseButton::ButtonUse); + } + else + { + SetUse(NULL); + } + + // + // If touching activates the button, set its touch function. + // + if (HasSpawnFlags(SF_BUTTON_TOUCH_ACTIVATES)) + { + SetTouch( &CBaseButton::ButtonTouch ); + } + else + { + SetTouch ( NULL ); + } + + CreateVPhysics(); +} + +//----------------------------------------------------------------------------- + +bool CBaseButton::CreateVPhysics() +{ + VPhysicsInitShadow( false, false ); + return true; +} + + +//----------------------------------------------------------------------------- +// Purpose: Button sound table. +// Also used by CBaseDoor to get 'touched' door lock/unlock sounds +// Input : sound - index of sound to look up. +// Output : Returns a pointer to the corresponding sound file. +//----------------------------------------------------------------------------- +string_t MakeButtonSound( int sound ) +{ + char tmp[1024]; + Q_snprintf( tmp, sizeof(tmp), "Buttons.snd%d", sound ); + return AllocPooledString(tmp); +} + + +//----------------------------------------------------------------------------- +// Purpose: Think function that emits sparks at random intervals. +//----------------------------------------------------------------------------- +void CBaseButton::ButtonSpark ( void ) +{ + SetThink ( &CBaseButton::ButtonSpark ); + SetNextThink( gpGlobals->curtime + 0.1 + random->RandomFloat ( 0, 1.5 ) );// spark again at random interval + + DoSpark( this, WorldSpaceCenter(), 1, 1, true, vec3_origin ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Called when someone uses us whilst we are locked. +//----------------------------------------------------------------------------- +bool CBaseButton::OnUseLocked( CBaseEntity *pActivator ) +{ + PlayLockSounds(this, &m_ls, TRUE, TRUE); + + if ( gpGlobals->curtime > m_flUseLockedTime ) + { + m_OnUseLocked.FireOutput( pActivator, this ); + m_flUseLockedTime = gpGlobals->curtime + 0.5; + return true; + } + + return false; +} + + +//----------------------------------------------------------------------------- +// Purpose: Use function that starts the button moving. +// Input : pActivator - +// pCaller - +// useType - +// value - +//----------------------------------------------------------------------------- +void CBaseButton::ButtonUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) +{ + // Ignore touches if button is moving, or pushed-in and waiting to auto-come-out. + // UNDONE: Should this use ButtonResponseToTouch() too? + if (m_toggle_state == TS_GOING_UP || m_toggle_state == TS_GOING_DOWN ) + return; + + if (m_bLocked) + { + OnUseLocked( pActivator ); + return; + } + + m_hActivator = pActivator; + + if ( m_toggle_state == TS_AT_TOP) + { + // + // If it's a toggle button it can return now. Otherwise, it will either + // return on its own or will stay pressed indefinitely. + // + if ( HasSpawnFlags(SF_BUTTON_TOGGLE)) + { + if ( m_sNoise != NULL_STRING ) + { + CPASAttenuationFilter filter( this ); + + EmitSound_t ep; + ep.m_nChannel = CHAN_VOICE; + ep.m_pSoundName = (char*)STRING(m_sNoise); + ep.m_flVolume = 1; + ep.m_SoundLevel = SNDLVL_NORM; + + EmitSound( filter, entindex(), ep ); + } + + m_OnPressed.FireOutput(m_hActivator, this); + ButtonReturn(); + } + } + else + { + m_OnPressed.FireOutput(m_hActivator, this); + ButtonActivate( ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Returns a code indicating how the button should respond to being touched. +// Output : Returns one of the following: +// BUTTON_NOTHING - do nothing +// BUTTON_RETURN - +// BUTTON_ACTIVATE - act as if pressed +//----------------------------------------------------------------------------- +CBaseButton::BUTTON_CODE CBaseButton::ButtonResponseToTouch( void ) +{ + // Ignore touches if button is moving, or pushed-in and waiting to auto-come-out. + if (m_toggle_state == TS_GOING_UP || + m_toggle_state == TS_GOING_DOWN || + (m_toggle_state == TS_AT_TOP && !m_fStayPushed && !HasSpawnFlags(SF_BUTTON_TOGGLE) ) ) + return BUTTON_NOTHING; + + if (m_toggle_state == TS_AT_TOP) + { + if ( HasSpawnFlags(SF_BUTTON_TOGGLE) && !m_fStayPushed) + { + return BUTTON_RETURN; + } + } + else + return BUTTON_ACTIVATE; + + return BUTTON_NOTHING; +} + + +//----------------------------------------------------------------------------- +// Purpose: Touch function that activates the button if it responds to touch. +// Input : pOther - The entity that touched us. +//----------------------------------------------------------------------------- +void CBaseButton::ButtonTouch( CBaseEntity *pOther ) +{ + // Ignore touches by anything but players + if ( !pOther->IsPlayer() ) + return; + + m_hActivator = pOther; + + BUTTON_CODE code = ButtonResponseToTouch(); + + if ( code == BUTTON_NOTHING ) + return; + + if (!UTIL_IsMasterTriggered(m_sMaster, pOther) || m_bLocked) + { + // play button locked sound + PlayLockSounds(this, &m_ls, TRUE, TRUE); + return; + } + + // Temporarily disable the touch function, until movement is finished. + SetTouch( NULL ); + + if ( code == BUTTON_RETURN ) + { + if ( m_sNoise != NULL_STRING ) + { + CPASAttenuationFilter filter( this ); + + EmitSound_t ep; + ep.m_nChannel = CHAN_VOICE; + ep.m_pSoundName = (char*)STRING(m_sNoise); + ep.m_flVolume = 1; + ep.m_SoundLevel = SNDLVL_NORM; + + EmitSound( filter, entindex(), ep ); + } + + m_OnPressed.FireOutput(m_hActivator, this); + ButtonReturn(); + } + else + { + // code == BUTTON_ACTIVATE + m_OnPressed.FireOutput(m_hActivator, this); + ButtonActivate( ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Starts the button moving "in/up". +// Input : *pOther - +//----------------------------------------------------------------------------- +void CBaseButton::ButtonActivate( void ) +{ + if ( m_sNoise != NULL_STRING ) + { + CPASAttenuationFilter filter( this ); + + EmitSound_t ep; + ep.m_nChannel = CHAN_VOICE; + ep.m_pSoundName = (char*)STRING(m_sNoise); + ep.m_flVolume = 1; + ep.m_SoundLevel = SNDLVL_NORM; + + EmitSound( filter, entindex(), ep ); + } + + if (!UTIL_IsMasterTriggered(m_sMaster, m_hActivator) || m_bLocked) + { + // button is locked, play locked sound + PlayLockSounds(this, &m_ls, TRUE, TRUE); + return; + } + else + { + // button is unlocked, play unlocked sound + PlayLockSounds(this, &m_ls, FALSE, TRUE); + } + + ASSERT(m_toggle_state == TS_AT_BOTTOM); + m_toggle_state = TS_GOING_UP; + + SetMoveDone( &CBaseButton::TriggerAndWait ); + if (!m_fRotating) + LinearMove( m_vecPosition2, m_flSpeed); + else + AngularMove( m_vecAngle2, m_flSpeed); +} + + +//----------------------------------------------------------------------------- +// Purpose: Enables or disables the use capability based on our spawnflags. +//----------------------------------------------------------------------------- +int CBaseButton::ObjectCaps(void) +{ + return((BaseClass::ObjectCaps() & ~FCAP_ACROSS_TRANSITION) | + (HasSpawnFlags(SF_BUTTON_USE_ACTIVATES) ? (FCAP_IMPULSE_USE | FCAP_USE_IN_RADIUS) : 0)); +} + + +//----------------------------------------------------------------------------- +// Purpose: Button has reached the "in/up" position. Activate its "targets", +// and pause before "popping out". +//----------------------------------------------------------------------------- +void CBaseButton::TriggerAndWait( void ) +{ + ASSERT(m_toggle_state == TS_GOING_UP); + + if (!UTIL_IsMasterTriggered(m_sMaster, m_hActivator) || m_bLocked) + { + return; + } + + m_toggle_state = TS_AT_TOP; + + // + // Re-instate touches if the button is of the toggle variety. + // + if (m_fStayPushed || HasSpawnFlags(SF_BUTTON_TOGGLE ) ) + { + if (HasSpawnFlags(SF_BUTTON_TOUCH_ACTIVATES)) + { + SetTouch(&CBaseButton::ButtonTouch); + } + else + { + // BUGBUG: ALL buttons no longer respond to touch + SetTouch (NULL); + } + } + + // + // If button automatically comes back out, start it moving out. + // + else + { + SetNextThink( gpGlobals->curtime + m_flWait ); + SetThink( &CBaseButton::ButtonReturn ); + } + + m_nState = 1; // use alternate textures + + m_OnIn.FireOutput(m_hActivator, this); +} + + +//----------------------------------------------------------------------------- +// Purpose: Starts the button moving "out/down". +//----------------------------------------------------------------------------- +void CBaseButton::ButtonReturn( void ) +{ + ASSERT(m_toggle_state == TS_AT_TOP); + m_toggle_state = TS_GOING_DOWN; + + SetMoveDone( &CBaseButton::ButtonBackHome ); + if (!m_fRotating) + LinearMove( m_vecPosition1, m_flSpeed); + else + AngularMove( m_vecAngle1, m_flSpeed); + + m_nState = 0; // use normal textures +} + + +//----------------------------------------------------------------------------- +// Purpose: Button has returned to start state. Quiesce it. +//----------------------------------------------------------------------------- +void CBaseButton::ButtonBackHome( void ) +{ + ASSERT(m_toggle_state == TS_GOING_DOWN); + m_toggle_state = TS_AT_BOTTOM; + + m_OnOut.FireOutput(m_hActivator, this); + + // + // Re-instate touch method, movement cycle is complete. + // + if (HasSpawnFlags(SF_BUTTON_TOUCH_ACTIVATES)) + { + SetTouch( &CBaseButton::ButtonTouch ); + } + else + { + // BUGBUG: ALL buttons no longer respond to touch + SetTouch ( NULL ); + } + + // reset think for a sparking button + if (HasSpawnFlags( SF_BUTTON_SPARK_IF_OFF ) ) + { + SetThink ( &CBaseButton::ButtonSpark ); + SetNextThink( gpGlobals->curtime + 0.5f );// no hurry + } +} + + +// +// Rotating button (aka "lever") +// +LINK_ENTITY_TO_CLASS( func_rot_button, CRotButton ); + + +void CRotButton::Spawn( void ) +{ + //---------------------------------------------------- + //determine sounds for buttons + //a sound of 0 should not make a sound + //---------------------------------------------------- + if ( m_sounds ) + { + m_sNoise = MakeButtonSound( m_sounds ); + PrecacheScriptSound(m_sNoise.ToCStr()); + } + else + { + m_sNoise = NULL_STRING; + } + + // set the axis of rotation + CBaseToggle::AxisDir(); + + // check for clockwise rotation + if ( HasSpawnFlags( SF_DOOR_ROTATE_BACKWARDS) ) + { + m_vecMoveAng = m_vecMoveAng * -1; + } + + SetMoveType( MOVETYPE_PUSH ); + +#ifdef HL1_DLL + SetSolid( SOLID_BSP ); +#else + SetSolid( SOLID_VPHYSICS ); +#endif + if ( HasSpawnFlags( SF_ROTBUTTON_NOTSOLID ) ) + { + AddEFlags( EFL_USE_PARTITION_WHEN_NOT_SOLID ); + AddSolidFlags( FSOLID_NOT_SOLID ); + } + + SetModel( STRING( GetModelName() ) ); + + if (m_flSpeed == 0) + m_flSpeed = 40; + + if (m_flWait == 0) + m_flWait = 1; + + if (m_iHealth > 0) + { + m_takedamage = DAMAGE_YES; + } + + m_toggle_state = TS_AT_BOTTOM; + m_vecAngle1 = GetLocalAngles(); + m_vecAngle2 = GetLocalAngles() + m_vecMoveAng * m_flMoveDistance; + ASSERTSZ(m_vecAngle1 != m_vecAngle2, "rotating button start/end positions are equal\n"); + + m_fStayPushed = (m_flWait == -1 ? TRUE : FALSE); + m_fRotating = TRUE; + + SetUse(&CRotButton::ButtonUse); + + // + // If touching activates the button, set its touch function. + // + if (!HasSpawnFlags(SF_BUTTON_TOUCH_ACTIVATES)) + { + SetTouch ( NULL ); + } + else + { + SetTouch( &CRotButton::ButtonTouch ); + } + + CreateVPhysics(); +} + + +bool CRotButton::CreateVPhysics( void ) +{ + VPhysicsInitShadow( false, false ); + return true; +} + + +//----------------------------------------------------------------------------- +// CMomentaryRotButton spawnflags +//----------------------------------------------------------------------------- +#define SF_MOMENTARY_DOOR 1 +#define SF_MOMENTARY_NOT_USABLE 2 +#define SF_MOMENTARY_AUTO_RETURN 16 + + +BEGIN_DATADESC( CMomentaryRotButton ) + + DEFINE_FIELD( m_lastUsed, FIELD_INTEGER ), + DEFINE_FIELD( m_start, FIELD_VECTOR ), + DEFINE_FIELD( m_end, FIELD_VECTOR ), + DEFINE_FIELD( m_IdealYaw, FIELD_FLOAT ), + DEFINE_FIELD( m_flTimeDelta, FIELD_FLOAT ), + DEFINE_FIELD( m_sNoise, FIELD_SOUNDNAME ), + DEFINE_FIELD( m_bUpdateTarget, FIELD_BOOLEAN ), + + DEFINE_KEYFIELD( m_direction, FIELD_INTEGER, "StartDirection" ), + DEFINE_KEYFIELD( m_returnSpeed, FIELD_FLOAT, "returnspeed" ), + DEFINE_KEYFIELD( m_flStartPosition, FIELD_FLOAT, "StartPosition"), + DEFINE_KEYFIELD( m_bSolidBsp, FIELD_BOOLEAN, "solidbsp" ), + + // Function Pointers + DEFINE_FUNCTION( UseMoveDone ), + DEFINE_FUNCTION( ReturnMoveDone ), + DEFINE_FUNCTION( SetPositionMoveDone ), + DEFINE_FUNCTION( UpdateThink ), + + // Inputs + DEFINE_INPUTFUNC( FIELD_FLOAT, "SetPosition", InputSetPosition ), + DEFINE_INPUTFUNC( FIELD_FLOAT, "SetPositionImmediately", InputSetPositionImmediately ), + DEFINE_INPUTFUNC( FIELD_VOID, "_DisableUpdateTarget", InputDisableUpdateTarget ), + DEFINE_INPUTFUNC( FIELD_VOID, "_EnableUpdateTarget", InputEnableUpdateTarget ), + + // Outputs + DEFINE_OUTPUT( m_Position, "Position" ), + DEFINE_OUTPUT( m_OnUnpressed, "OnUnpressed" ), + DEFINE_OUTPUT( m_OnFullyClosed, "OnFullyClosed" ), + DEFINE_OUTPUT( m_OnFullyOpen, "OnFullyOpen" ), + DEFINE_OUTPUT( m_OnReachedPosition, "OnReachedPosition" ), + + DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ), + DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ), + DEFINE_FIELD( m_bDisabled, FIELD_BOOLEAN ) + +END_DATADESC() + + +LINK_ENTITY_TO_CLASS( momentary_rot_button, CMomentaryRotButton ); + + +//----------------------------------------------------------------------------- +// Purpose: Called when spawning, after keyvalues have been handled. +//----------------------------------------------------------------------------- +void CMomentaryRotButton::Spawn( void ) +{ + CBaseToggle::AxisDir(); + + m_bUpdateTarget = true; + + if ( m_flSpeed == 0 ) + { + m_flSpeed = 100; + } + + // Clamp start position and issue bounds warning + if (m_flStartPosition < 0.0 || m_flStartPosition > 1.0) + { + Warning("WARNING: Momentary door (%s) start position not between 0 and 1. Clamping.\n",GetDebugName()); + m_flStartPosition = clamp(m_IdealYaw, 0, 1); + } + + // Check direction fields (for backward compatibility) + if (m_direction != 1 && m_direction != -1) + { + m_direction = 1; + } + + if (m_flMoveDistance < 0) + { + m_vecMoveAng = m_vecMoveAng * -1; + m_flMoveDistance = -m_flMoveDistance; + } + + m_start = GetLocalAngles() - m_vecMoveAng * m_flMoveDistance * m_flStartPosition; + m_end = GetLocalAngles() + m_vecMoveAng * m_flMoveDistance * (1-m_flStartPosition); + + m_IdealYaw = m_flStartPosition; + + // Force start direction at end points + if (m_flStartPosition == 0.0) + { + m_direction = -1; + } + else if (m_flStartPosition == 1.0) + { + m_direction = 1; + } + + if (HasSpawnFlags(SF_BUTTON_LOCKED)) + { + m_bLocked = true; + } + + if ( HasSpawnFlags( SF_BUTTON_USE_ACTIVATES ) ) + { + if ( m_sounds ) + { + m_sNoise = MakeButtonSound( m_sounds ); + PrecacheScriptSound(m_sNoise.ToCStr()); + } + else + { + m_sNoise = NULL_STRING; + } + + m_lastUsed = 0; + UpdateTarget(0,this); + } + +#ifdef HL1_DLL + SetSolid( SOLID_BSP ); +#else + SetSolid( SOLID_VPHYSICS ); +#endif + if (HasSpawnFlags(SF_ROTBUTTON_NOTSOLID)) + { + AddEFlags( EFL_USE_PARTITION_WHEN_NOT_SOLID ); + AddSolidFlags( FSOLID_NOT_SOLID ); + } + + SetMoveType( MOVETYPE_PUSH ); + SetModel( STRING( GetModelName() ) ); + + CreateVPhysics(); + + // Slam the object back to solid - if we really want it to be solid. + if ( m_bSolidBsp ) + { + SetSolid( SOLID_BSP ); + } + + m_bDisabled = false; +} + +int CMomentaryRotButton::ObjectCaps( void ) +{ + int flags = BaseClass::ObjectCaps(); + if (!HasSpawnFlags(SF_BUTTON_USE_ACTIVATES)) + { + return flags; + } + else + { + return (flags | FCAP_CONTINUOUS_USE | FCAP_USE_IN_RADIUS); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CMomentaryRotButton::CreateVPhysics( void ) +{ + VPhysicsInitShadow( false, false ); + + return true; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CMomentaryRotButton::PlaySound( void ) +{ + if ( m_sNoise == NULL_STRING ) + return; + + CPASAttenuationFilter filter( this ); + + EmitSound_t ep; + ep.m_nChannel = CHAN_VOICE; + ep.m_pSoundName = (char*)STRING(m_sNoise); + ep.m_flVolume = 1; + ep.m_SoundLevel = SNDLVL_NORM; + + EmitSound( filter, entindex(), ep ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Returns a given angular position as a value along our motion from 0 to 1. +// Input : vecAngles - +//----------------------------------------------------------------------------- +float CMomentaryRotButton::GetPos( const QAngle &vecAngles ) +{ + float flScale = 1; + if (( m_vecMoveAng[0] < 0 ) || ( m_vecMoveAng[1] < 0 ) || ( m_vecMoveAng[2] < 0 )) + { + flScale = -1; + } + + float flPos = flScale * CBaseToggle::AxisDelta( m_spawnflags, vecAngles, m_start ) / m_flMoveDistance; + return( clamp( flPos, 0, 1 )); +} + + +//------------------------------------------------------------------------------ +// Purpose : +// Input : flPosition +//------------------------------------------------------------------------------ +void CMomentaryRotButton::InputSetPosition( inputdata_t &inputdata ) +{ + m_IdealYaw = clamp( inputdata.value.Float(), 0, 1 ); + + float flCurPos = GetPos( GetLocalAngles() ); + if ( flCurPos < m_IdealYaw ) + { + // Moving forward (from start to end). + SetLocalAngularVelocity( m_flSpeed * m_vecMoveAng ); + m_direction = 1; + } + else if ( flCurPos > m_IdealYaw ) + { + // Moving backward (from end to start). + SetLocalAngularVelocity( -m_flSpeed * m_vecMoveAng ); + m_direction = -1; + } + else + { + // We're there already; nothing to do. + SetLocalAngularVelocity( vec3_angle ); + return; + } + + SetMoveDone( &CMomentaryRotButton::SetPositionMoveDone ); + + SetThink( &CMomentaryRotButton::UpdateThink ); + SetNextThink( gpGlobals->curtime ); + + // + // Think again in 0.1 seconds or the time that it will take us to reach our movement goal, + // whichever is the shorter interval. This prevents us from overshooting and stuttering when we + // are told to change position in very small increments. + // + QAngle vecNewAngles = m_start + m_vecMoveAng * ( m_IdealYaw * m_flMoveDistance ); + float flAngleDelta = fabs( AxisDelta( m_spawnflags, vecNewAngles, GetLocalAngles() )); + m_flTimeDelta = min( flAngleDelta / m_flSpeed, 0.1f ); + + SetMoveDoneTime( m_flTimeDelta ); +} + + +//------------------------------------------------------------------------------ +// Purpose : +// Input : flPosition +//------------------------------------------------------------------------------ +void CMomentaryRotButton::InputSetPositionImmediately( inputdata_t &inputdata ) +{ + m_IdealYaw = clamp( inputdata.value.Float(), 0, 1 ); + SetLocalAngles( m_start + m_vecMoveAng * ( m_IdealYaw * m_flMoveDistance ) ); +} + + +//------------------------------------------------------------------------------ +// Purpose: Turns off target updates so that we can change the wheel's position +// without changing the target's position. Used for jiggling when locked. +//------------------------------------------------------------------------------ +void CMomentaryRotButton::InputDisableUpdateTarget( inputdata_t &inputdata ) +{ + m_bUpdateTarget = false; +} + + +//------------------------------------------------------------------------------ +// Purpose: Turns target updates back on (after jiggling). +//------------------------------------------------------------------------------ +void CMomentaryRotButton::InputEnableUpdateTarget( inputdata_t &inputdata ) +{ + m_bUpdateTarget = true; +} + + +//----------------------------------------------------------------------------- +// Purpose: Locks the button. If locked, the button will play the locked sound +// when the player tries to use it. +//----------------------------------------------------------------------------- +void CMomentaryRotButton::Lock() +{ + BaseClass::Lock(); + + SetLocalAngularVelocity( vec3_angle ); + SetMoveDoneTime( -1 ); + SetMoveDone( NULL ); + + SetNextThink( TICK_NEVER_THINK ); + SetThink( NULL ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Unlocks the button, making it able to be pressed again. +//----------------------------------------------------------------------------- +void CMomentaryRotButton::Unlock() +{ + BaseClass::Unlock(); + + SetMoveDone( &CMomentaryRotButton::ReturnMoveDone ); + + // Delay before autoreturn. + SetMoveDoneTime( 0.1f ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Fires the appropriate outputs at the extremes of motion. +//----------------------------------------------------------------------------- +void CMomentaryRotButton::OutputMovementComplete( void ) +{ + if (m_IdealYaw == 1.0) + { + m_OnFullyClosed.FireOutput(this, this); + } + else if (m_IdealYaw == 0.0) + { + m_OnFullyOpen.FireOutput(this, this); + } + + m_OnReachedPosition.FireOutput( this, this ); +} + + +//------------------------------------------------------------------------------ +// Purpose: MoveDone function for the SetPosition input handler. Tracks our +// progress toward a movement goal and updates our outputs. +//------------------------------------------------------------------------------ +void CMomentaryRotButton::SetPositionMoveDone(void) +{ + float flCurPos = GetPos( GetLocalAngles() ); + + if ((( flCurPos >= m_IdealYaw ) && ( m_direction == 1 )) || + (( flCurPos <= m_IdealYaw ) && ( m_direction == -1 ))) + { + // + // We reached or surpassed our movement goal. + // + SetLocalAngularVelocity( vec3_angle ); + SetLocalAngles( m_start + m_vecMoveAng * ( m_IdealYaw * m_flMoveDistance ) ); + SetNextThink( TICK_NEVER_THINK ); + SetMoveDoneTime( -1 ); + UpdateTarget( m_IdealYaw, this ); + OutputMovementComplete(); + return; + } + + // TODO: change this to use a Think function like ReturnThink. + SetMoveDoneTime( m_flTimeDelta ); +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : pActivator - +// pCaller - +// useType - +// value - +//----------------------------------------------------------------------------- +void CMomentaryRotButton::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) +{ + if ( m_bDisabled == true ) + return; + + if (m_bLocked) + { + if ( OnUseLocked( pActivator ) && HasSpawnFlags( SF_BUTTON_JIGGLE_ON_USE_LOCKED ) ) + { + // Jiggle two degrees. + float flDist = 2.0 / m_flMoveDistance; + + // Must be first! + g_EventQueue.AddEvent( this, "_DisableUpdateTarget", 0, this, this ); + + variant_t value; + value.SetFloat( flDist ); + g_EventQueue.AddEvent( this, "SetPosition", value, 0.01, this, this ); + + value.SetFloat( 0.0 ); + g_EventQueue.AddEvent( this, "SetPosition", value, 0.1, this, this ); + + value.SetFloat( 0.5 * flDist ); + g_EventQueue.AddEvent( this, "SetPosition", value, 0.2, this, this ); + + value.SetFloat( 0.0 ); + g_EventQueue.AddEvent( this, "SetPosition", value, 0.3, this, this ); + + // Must be last! And must be late enough to cover the settling time. + g_EventQueue.AddEvent( this, "_EnableUpdateTarget", 0.5, this, this ); + } + + return; + } + + // + // Reverse our direction and play movement sound every time the player + // pauses between uses. + // + bool bPlaySound = false; + + if ( !m_lastUsed ) + { + bPlaySound = true; + m_direction = -m_direction; + + //Alert that we've been pressed + m_OnPressed.FireOutput( m_hActivator, this ); + } + + m_lastUsed = 1; + + float flPos = GetPos( GetLocalAngles() ); + UpdateSelf( flPos, bPlaySound ); + + // + // Think every frame while we are moving. + // + SetThink( &CMomentaryRotButton::UpdateThink ); + SetNextThink( gpGlobals->curtime ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Handles changing direction at the extremes of our range of motion +// and updating our avelocity while being used by the player. +// Input : value - Number from 0 to 1 indicating our desired position within +// our range of motion, 0 = start, 1 = end. +//----------------------------------------------------------------------------- +void CMomentaryRotButton::UpdateSelf( float value, bool bPlaySound ) +{ + // + // Set our move clock to 0.1 seconds in the future so we stop spinning unless we are + // used again before then. + // + SetMoveDoneTime( 0.1 ); + + // + // If we hit the end, zero our avelocity and snap to the end angles. + // + if ( m_direction > 0 && value >= 1.0 ) + { + SetLocalAngularVelocity( vec3_angle ); + SetLocalAngles( m_end ); + + m_OnFullyClosed.FireOutput(this, this); + return; + } + // + // If we returned to the start, zero our avelocity and snap to the start angles. + // + else if ( m_direction < 0 && value <= 0 ) + { + SetLocalAngularVelocity( vec3_angle ); + SetLocalAngles( m_start ); + + m_OnFullyOpen.FireOutput(this, this); + return; + } + + if ( bPlaySound ) + { + PlaySound(); + } + + SetLocalAngularVelocity( ( m_direction * m_flSpeed ) * m_vecMoveAng ); + SetMoveDone( &CMomentaryRotButton::UseMoveDone ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Updates the value of our position, firing any targets. +// Input : value - New position, from 0 - 1. +//----------------------------------------------------------------------------- +void CMomentaryRotButton::UpdateTarget( float value, CBaseEntity *pActivator ) +{ + if ( !m_bUpdateTarget ) + return; + + if (m_Position.Get() != value) + { + m_Position.Set(value, pActivator, this); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Handles the end of motion caused by player use. +//----------------------------------------------------------------------------- +void CMomentaryRotButton::UseMoveDone( void ) +{ + SetLocalAngularVelocity( vec3_angle ); + + // Make sure our targets stop where we stopped. + float flPos = GetPos( GetLocalAngles() ); + UpdateTarget( flPos, this ); + + // Alert that we've been unpressed + m_OnUnpressed.FireOutput( m_hActivator, this ); + + m_lastUsed = 0; + + if ( !HasSpawnFlags( SF_BUTTON_TOGGLE ) && m_returnSpeed > 0 ) + { + SetMoveDone( &CMomentaryRotButton::ReturnMoveDone ); + m_direction = -1; + + // Delay before autoreturn. + SetMoveDoneTime( 0.1f ); + } + else + { + SetThink( NULL ); + SetMoveDone( NULL ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: MoveDone function for rotating back to the start position. +//----------------------------------------------------------------------------- +void CMomentaryRotButton::ReturnMoveDone( void ) +{ + float value = GetPos( GetLocalAngles() ); + if ( value <= 0 ) + { + // + // Got back to the start, stop spinning. + // + SetLocalAngularVelocity( vec3_angle ); + SetLocalAngles( m_start ); + + UpdateTarget( 0, NULL ); + + SetMoveDoneTime( -1 ); + SetMoveDone( NULL ); + + SetNextThink( TICK_NEVER_THINK ); + SetThink( NULL ); + } + else + { + SetLocalAngularVelocity( -m_returnSpeed * m_vecMoveAng ); + SetMoveDoneTime( 0.1f ); + + SetThink( &CMomentaryRotButton::UpdateThink ); + SetNextThink( gpGlobals->curtime + 0.01f ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Think function for updating target as we move. +//----------------------------------------------------------------------------- +void CMomentaryRotButton::UpdateThink( void ) +{ + float value = GetPos( GetLocalAngles() ); + UpdateTarget( value, NULL ); + SetNextThink( gpGlobals->curtime ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Draw any debug text overlays +// Output : Current text offset from the top +//----------------------------------------------------------------------------- +int CMomentaryRotButton::DrawDebugTextOverlays(void) +{ + int text_offset = BaseClass::DrawDebugTextOverlays(); + + if (m_debugOverlays & OVERLAY_TEXT_BIT) + { + char tempstr[255]; + + Q_snprintf(tempstr,sizeof(tempstr),"QAngle: %.2f %.2f %.2f", GetLocalAngles()[0], GetLocalAngles()[1], GetLocalAngles()[2]); + EntityText(text_offset,tempstr,0); + text_offset++; + + Q_snprintf(tempstr,sizeof(tempstr),"AVelocity: %.2f %.2f %.2f", GetLocalAngularVelocity()[0], GetLocalAngularVelocity()[1], GetLocalAngularVelocity()[2]); + EntityText(text_offset,tempstr,0); + text_offset++; + + Q_snprintf(tempstr,sizeof(tempstr),"Target Pos: %3.3f",m_IdealYaw); + EntityText(text_offset,tempstr,0); + text_offset++; + + float flCurPos = GetPos(GetLocalAngles()); + Q_snprintf(tempstr,sizeof(tempstr),"Current Pos: %3.3f",flCurPos); + EntityText(text_offset,tempstr,0); + text_offset++; + + Q_snprintf(tempstr,sizeof(tempstr),"Direction: %s",(m_direction == 1) ? "Forward" : "Backward"); + EntityText(text_offset,tempstr,0); + text_offset++; + + } + return text_offset; +} + +//----------------------------------------------------------------------------- +// Purpose: Input hander that starts the spawner +//----------------------------------------------------------------------------- +void CMomentaryRotButton::InputEnable( inputdata_t &inputdata ) +{ + Enable(); +} + + +//----------------------------------------------------------------------------- +// Purpose: Input hander that stops the spawner +//----------------------------------------------------------------------------- +void CMomentaryRotButton::InputDisable( inputdata_t &inputdata ) +{ + Disable(); +} + +//----------------------------------------------------------------------------- +// Purpose: Start the spawner +//----------------------------------------------------------------------------- +void CMomentaryRotButton::Enable( void ) +{ + m_bDisabled = false; +} + + +//----------------------------------------------------------------------------- +// Purpose: Stop the spawner +//----------------------------------------------------------------------------- +void CMomentaryRotButton::Disable( void ) +{ + m_bDisabled = true; +} diff --git a/dlls/cbase.cpp b/dlls/cbase.cpp index 2c2f703c..3c5cf209 100644 --- a/dlls/cbase.cpp +++ b/dlls/cbase.cpp @@ -1200,6 +1200,7 @@ void variant_t::SetOther( void *data ) case FIELD_EHANDLE: *((EHANDLE *)data) = eVal; break; case FIELD_CLASSPTR: *((CBaseEntity **)data) = eVal; break; + default: break; } } @@ -1252,6 +1253,8 @@ bool variant_t::Convert( fieldtype_t newType ) SetBool( iVal != 0 ); return true; } + default: + break; } break; } @@ -1271,6 +1274,8 @@ bool variant_t::Convert( fieldtype_t newType ) SetBool( flVal != 0 ); return true; } + default: + break; } break; } @@ -1358,6 +1363,9 @@ bool variant_t::Convert( fieldtype_t newType ) SetEntity( ent ); return true; } + + default: + break; } break; @@ -1377,9 +1385,13 @@ bool variant_t::Convert( fieldtype_t newType ) } return true; } + default: + break; } break; } + default: + break; } // invalid conversion @@ -1457,6 +1469,9 @@ const char *variant_t::ToString( void ) const Q_strncpy( szBuf, pszName, 512 ); return (szBuf); } + + default: + break; } return("No conversion to string"); diff --git a/dlls/client.cpp b/dlls/client.cpp index 695e196d..37574d99 100644 --- a/dlls/client.cpp +++ b/dlls/client.cpp @@ -830,7 +830,7 @@ void CC_Player_TestDispatchEffect( void ) AngleVectors( vecAngles, &data.m_vNormal ); } data.m_nEntIndex = pPlayer->entindex(); - data.m_fFlags = flags; + data.m_fFlags = static_cast(flags); data.m_flMagnitude = magnitude; data.m_flScale = scale; DispatchEffect( (char *)engine->Cmd_Argv(1), data ); @@ -1148,6 +1148,7 @@ void CC_HurtMe_f(void) static ConCommand hurtme("hurtme", CC_HurtMe_f, "Hurts the player.\n\tArguments: ", FCVAR_CHEAT); +#ifdef _DEBUG static bool IsInGroundList( CBaseEntity *ent, CBaseEntity *ground ) { if ( !ground || !ent ) @@ -1169,6 +1170,7 @@ static bool IsInGroundList( CBaseEntity *ent, CBaseEntity *ground ) return false; } +#endif static int DescribeGroundList( CBaseEntity *ent ) { diff --git a/dlls/controlentities.cpp b/dlls/controlentities.cpp index 17713e24..49a5c38c 100644 --- a/dlls/controlentities.cpp +++ b/dlls/controlentities.cpp @@ -128,7 +128,7 @@ void CTargetChangeGravity::InputChangeGrav( inputdata_t &inputdata ) // Save the gravity to restore it in InputResetGrav if ( m_iOldGrav ) - m_iOldGrav = pl->GetGravity(); + m_iOldGrav = static_cast(pl->GetGravity()); pl->SetGravity(m_iGravity); } diff --git a/dlls/cplane.cpp b/dlls/cplane.cpp index c6051056..fbfc8e57 100644 --- a/dlls/cplane.cpp +++ b/dlls/cplane.cpp @@ -1,71 +1,71 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -//=============================================================================// -#include "cbase.h" -#include "game.h" -#include "cplane.h" - -// memdbgon must be the last include file in a .cpp file!!! -#include "tier0/memdbgon.h" - -//========================================================= -// Plane -//========================================================= -CPlane::CPlane ( void ) -{ - m_fInitialized = FALSE; -} - -//========================================================= -// InitializePlane - Takes a normal for the plane and a -// point on the plane and -//========================================================= -void CPlane::InitializePlane ( const Vector &vecNormal, const Vector &vecPoint ) -{ - m_vecNormal = vecNormal; - m_flDist = DotProduct ( m_vecNormal, vecPoint ); - m_fInitialized = TRUE; -} - - -//========================================================= -// PointInFront - determines whether the given vector is -// in front of the plane. -//========================================================= -bool CPlane::PointInFront ( const Vector &vecPoint ) -{ - float flFace; - - if ( !m_fInitialized ) - { - return FALSE; - } - - flFace = DotProduct ( m_vecNormal, vecPoint ) - m_flDist; - - if ( flFace >= 0 ) - { - return TRUE; - } - - return FALSE; -} - -//========================================================= -//========================================================= -float CPlane::PointDist ( const Vector &vecPoint ) -{ - float flDist; - - if ( !m_fInitialized ) - { - return FALSE; - } - - flDist = DotProduct ( m_vecNormal, vecPoint ) - m_flDist; - - return flDist; -} \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// +#include "cbase.h" +#include "game.h" +#include "cplane.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +//========================================================= +// Plane +//========================================================= +CPlane::CPlane ( void ) +{ + m_fInitialized = FALSE; +} + +//========================================================= +// InitializePlane - Takes a normal for the plane and a +// point on the plane and +//========================================================= +void CPlane::InitializePlane ( const Vector &vecNormal, const Vector &vecPoint ) +{ + m_vecNormal = vecNormal; + m_flDist = DotProduct ( m_vecNormal, vecPoint ); + m_fInitialized = TRUE; +} + + +//========================================================= +// PointInFront - determines whether the given vector is +// in front of the plane. +//========================================================= +bool CPlane::PointInFront ( const Vector &vecPoint ) +{ + float flFace; + + if ( !m_fInitialized ) + { + return FALSE; + } + + flFace = DotProduct ( m_vecNormal, vecPoint ) - m_flDist; + + if ( flFace >= 0 ) + { + return TRUE; + } + + return FALSE; +} + +//========================================================= +//========================================================= +float CPlane::PointDist ( const Vector &vecPoint ) +{ + float flDist; + + if ( !m_fInitialized ) + { + return FALSE; + } + + flDist = DotProduct ( m_vecNormal, vecPoint ) - m_flDist; + + return flDist; +} diff --git a/dlls/cplane.h b/dlls/cplane.h index e1ecd9ec..61bfedf3 100644 --- a/dlls/cplane.h +++ b/dlls/cplane.h @@ -1,44 +1,44 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -// -//=============================================================================// -#pragma once - -#ifndef CPLANE_H -#define CPLANE_H - -//========================================================= -// Plane -//========================================================= -class CPlane -{ -public: - CPlane ( void ); - - //========================================================= - // InitializePlane - Takes a normal for the plane and a - // point on the plane and - //========================================================= - void InitializePlane ( const Vector &vecNormal, const Vector &vecPoint ); - - //========================================================= - // PointInFront - determines whether the given vector is - // in front of the plane. - //========================================================= - bool PointInFront ( const Vector &vecPoint ); - - //========================================================= - // How far off the plane is this point? - //========================================================= - float PointDist( const Vector &vecPoint ); - -private: - Vector m_vecNormal; - float m_flDist; - bool m_fInitialized; -}; - -#endif //CPLANE_H \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// +#pragma once + +#ifndef CPLANE_H +#define CPLANE_H + +//========================================================= +// Plane +//========================================================= +class CPlane +{ +public: + CPlane ( void ); + + //========================================================= + // InitializePlane - Takes a normal for the plane and a + // point on the plane and + //========================================================= + void InitializePlane ( const Vector &vecNormal, const Vector &vecPoint ); + + //========================================================= + // PointInFront - determines whether the given vector is + // in front of the plane. + //========================================================= + bool PointInFront ( const Vector &vecPoint ); + + //========================================================= + // How far off the plane is this point? + //========================================================= + float PointDist( const Vector &vecPoint ); + +private: + Vector m_vecNormal; + float m_flDist; + bool m_fInitialized; +}; + +#endif //CPLANE_H diff --git a/dlls/cterrainmorph.cpp b/dlls/cterrainmorph.cpp index 0e53c786..18227504 100644 --- a/dlls/cterrainmorph.cpp +++ b/dlls/cterrainmorph.cpp @@ -175,7 +175,7 @@ void CTerrainMorph::MorphThink( void ) //Msg( "Strength: %f - Radius: %f\n", params.m_flStrength, params.m_flRadius ); TerrainMod_Add( type, params ); - m_iIterations += params.m_flStrength; + m_iIterations += static_cast(params.m_flStrength); } #if 0 NDebugOverlay::Line( m_Params.m_vecLocation, m_Params.m_vecLocation + Vector( 200, 200, 0 ), 0,255,0, true, 3 ); diff --git a/dlls/doors.cpp b/dlls/doors.cpp index 8e13a1d9..a758f6fa 100644 --- a/dlls/doors.cpp +++ b/dlls/doors.cpp @@ -214,11 +214,11 @@ bool CBaseDoor::KeyValue( const char *szKeyName, const char *szValue ) { if (FStrEq(szKeyName, "locked_sentence")) { - m_bLockedSentence = atof(szValue); + m_bLockedSentence = static_cast(atof(szValue)); } else if (FStrEq(szKeyName, "unlocked_sentence")) { - m_bUnlockedSentence = atof(szValue); + m_bUnlockedSentence = static_cast(atof(szValue)); } else return BaseClass::KeyValue( szKeyName, szValue ); @@ -454,6 +454,8 @@ void CBaseDoor::Activate( void ) case TS_AT_BOTTOM: UpdateAreaPortals( false ); break; + default: + break; } #ifdef HL1_DLL diff --git a/dlls/effects.cpp b/dlls/effects.cpp index b3441cf8..945df75f 100644 --- a/dlls/effects.cpp +++ b/dlls/effects.cpp @@ -968,7 +968,7 @@ void CTestEffect::Think( void ) for (i = 0; i < m_iBeam; i++) { t = (gpGlobals->curtime - m_flBeamTime[i]) / ( 3 + m_flStartTime - m_flBeamTime[i]); - m_pBeam[i]->SetBrightness( 255 * t ); + m_pBeam[i]->SetBrightness( static_cast(255 * t) ); // m_pBeam[i]->SetScrollRate( 20 * t ); } SetNextThink( gpGlobals->curtime + 0.1f ); @@ -1149,11 +1149,11 @@ void CBlood::InputEmitBlood( inputdata_t &inputdata ) { if ( HasSpawnFlags( SF_BLOOD_STREAM ) ) { - UTIL_BloodStream( BloodPosition(inputdata.pActivator), Direction(), Color(), BloodAmount() ); + UTIL_BloodStream( BloodPosition(inputdata.pActivator), Direction(), Color(), static_cast(BloodAmount()) ); } else { - UTIL_BloodDrips( BloodPosition(inputdata.pActivator), Direction(), Color(), BloodAmount() ); + UTIL_BloodDrips( BloodPosition(inputdata.pActivator), Direction(), Color(), static_cast(BloodAmount()) ); } if ( HasSpawnFlags( SF_BLOOD_DECAL ) ) @@ -1190,7 +1190,7 @@ void CBlood::InputEmitBlood( inputdata_t &inputdata ) nFlags |= FX_BLOODSPRAY_GORE; } - UTIL_BloodSpray(GetAbsOrigin(), Direction(), Color(), BloodAmount(), nFlags); + UTIL_BloodSpray(GetAbsOrigin(), Direction(), Color(), static_cast(BloodAmount()), nFlags); } } @@ -1591,7 +1591,7 @@ void CEnvWind::Spawn( void ) SetSolid( SOLID_NONE ); AddEffects( EF_NODRAW ); - m_EnvWindShared.Init( entindex(), 0, gpGlobals->frametime, GetLocalAngles().y, 0 ); + m_EnvWindShared.Init( entindex(), 0, gpGlobals->frametime, static_cast(GetLocalAngles().y), 0 ); SetThink( &CEnvWind::WindThink ); SetNextThink( gpGlobals->curtime ); diff --git a/dlls/entityblocker.cpp b/dlls/entityblocker.cpp index 21878dad..9674cf7e 100644 --- a/dlls/entityblocker.cpp +++ b/dlls/entityblocker.cpp @@ -1,76 +1,76 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -//=============================================================================// - -#include "cbase.h" -#include "entityblocker.h" - -// memdbgon must be the last include file in a .cpp file!!! -#include "tier0/memdbgon.h" - -LINK_ENTITY_TO_CLASS( entity_blocker, CEntityBlocker ); - -//----------------------------------------------------------------------------- -// Purpose: -// Input : &origin - -// &mins - -// &maxs - -// NULL - -// Output : CEntityBlocker -//----------------------------------------------------------------------------- -CEntityBlocker *CEntityBlocker::Create( const Vector &origin, const Vector &mins, const Vector &maxs, CBaseEntity *pOwner, bool bBlockPhysics ) -{ - CEntityBlocker *pBlocker = (CEntityBlocker *) CBaseEntity::Create( "entity_blocker", origin, vec3_angle, pOwner ); - - if ( pBlocker != NULL ) - { - pBlocker->SetSize( mins, maxs ); - if ( bBlockPhysics ) - { - pBlocker->VPhysicsInitStatic(); - } - } - - return pBlocker; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CEntityBlocker::Spawn( void ) -{ - SetSolid( SOLID_BBOX ); - AddSolidFlags( FSOLID_CUSTOMRAYTEST ); -} - -//----------------------------------------------------------------------------- -// Purpose: Entity blockers don't block tracelines so they don't screw up weapon fire, etc -//----------------------------------------------------------------------------- -bool CEntityBlocker::TestCollision( const Ray_t &ray, unsigned int mask, trace_t& trace ) -{ - return false; -} - -//------------------------------------------------------------------------------ -// Purpose : -// Input : -// Output : -//------------------------------------------------------------------------------ -void CC_Test_Entity_Blocker( void ) -{ - CBasePlayer *pPlayer = UTIL_GetCommandClient(); - Vector vecForward; - pPlayer->GetVectors( &vecForward, NULL, NULL ); - - trace_t tr; - Vector vecOrigin = pPlayer->GetAbsOrigin() + (vecForward * 256); - UTIL_TraceHull( vecOrigin + Vector(0,0,256), vecOrigin - Vector(0,0,256), VEC_HULL_MIN, VEC_HULL_MAX, MASK_SOLID, pPlayer, COLLISION_GROUP_NONE, &tr ); - if ( !tr.allsolid && !tr.startsolid ) - { - CEntityBlocker::Create( tr.endpos, VEC_HULL_MIN, VEC_HULL_MAX, NULL, true ); - NDebugOverlay::Box( tr.endpos, VEC_HULL_MIN, VEC_HULL_MAX, 0, 255, 0, 64, 1000.0 ); - } -} -static ConCommand test_entity_blocker("test_entity_blocker", CC_Test_Entity_Blocker, "Test command that drops an entity blocker out in front of the player.", FCVAR_CHEAT ); \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "cbase.h" +#include "entityblocker.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +LINK_ENTITY_TO_CLASS( entity_blocker, CEntityBlocker ); + +//----------------------------------------------------------------------------- +// Purpose: +// Input : &origin - +// &mins - +// &maxs - +// NULL - +// Output : CEntityBlocker +//----------------------------------------------------------------------------- +CEntityBlocker *CEntityBlocker::Create( const Vector &origin, const Vector &mins, const Vector &maxs, CBaseEntity *pOwner, bool bBlockPhysics ) +{ + CEntityBlocker *pBlocker = (CEntityBlocker *) CBaseEntity::Create( "entity_blocker", origin, vec3_angle, pOwner ); + + if ( pBlocker != NULL ) + { + pBlocker->SetSize( mins, maxs ); + if ( bBlockPhysics ) + { + pBlocker->VPhysicsInitStatic(); + } + } + + return pBlocker; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CEntityBlocker::Spawn( void ) +{ + SetSolid( SOLID_BBOX ); + AddSolidFlags( FSOLID_CUSTOMRAYTEST ); +} + +//----------------------------------------------------------------------------- +// Purpose: Entity blockers don't block tracelines so they don't screw up weapon fire, etc +//----------------------------------------------------------------------------- +bool CEntityBlocker::TestCollision( const Ray_t &ray, unsigned int mask, trace_t& trace ) +{ + return false; +} + +//------------------------------------------------------------------------------ +// Purpose : +// Input : +// Output : +//------------------------------------------------------------------------------ +void CC_Test_Entity_Blocker( void ) +{ + CBasePlayer *pPlayer = UTIL_GetCommandClient(); + Vector vecForward; + pPlayer->GetVectors( &vecForward, NULL, NULL ); + + trace_t tr; + Vector vecOrigin = pPlayer->GetAbsOrigin() + (vecForward * 256); + UTIL_TraceHull( vecOrigin + Vector(0,0,256), vecOrigin - Vector(0,0,256), VEC_HULL_MIN, VEC_HULL_MAX, MASK_SOLID, pPlayer, COLLISION_GROUP_NONE, &tr ); + if ( !tr.allsolid && !tr.startsolid ) + { + CEntityBlocker::Create( tr.endpos, VEC_HULL_MIN, VEC_HULL_MAX, NULL, true ); + NDebugOverlay::Box( tr.endpos, VEC_HULL_MIN, VEC_HULL_MAX, 0, 255, 0, 64, 1000.0 ); + } +} +static ConCommand test_entity_blocker("test_entity_blocker", CC_Test_Entity_Blocker, "Test command that drops an entity blocker out in front of the player.", FCVAR_CHEAT ); diff --git a/dlls/entitylist.cpp b/dlls/entitylist.cpp index 355f60b0..610e03bd 100644 --- a/dlls/entitylist.cpp +++ b/dlls/entitylist.cpp @@ -146,7 +146,7 @@ public: void Clear() { m_simThinkList.Purge(); - for ( int i = 0; i < ARRAYSIZE(m_entinfoIndex); i++ ) + for ( size_t i = 0; i < ARRAYSIZE(m_entinfoIndex); i++ ) { m_entinfoIndex[i] = 0xFFFF; } diff --git a/dlls/env_entity_maker.cpp b/dlls/env_entity_maker.cpp index c505511c..449f6cfb 100644 --- a/dlls/env_entity_maker.cpp +++ b/dlls/env_entity_maker.cpp @@ -109,7 +109,7 @@ void CEnvEntityMaker::Activate( void ) // check for valid template if ( m_iszTemplate == NULL_STRING ) { - Warning( "env_entity_maker %s has no template entity!\n", GetEntityName() ); + Warning( "env_entity_maker %s has no template entity!\n", STRING(GetEntityName()) ); UTIL_Remove( this ); return; } @@ -131,7 +131,7 @@ CPointTemplate *CEnvEntityMaker::FindTemplate() CPointTemplate *pTemplate = dynamic_cast(gEntList.FindEntityByName( NULL, STRING(m_iszTemplate) )); if ( !pTemplate ) { - Warning( "env_entity_maker %s failed to find template %s.\n", GetEntityName(), STRING(m_iszTemplate) ); + Warning( "env_entity_maker %s failed to find template %s.\n", STRING(GetEntityName()), STRING(m_iszTemplate) ); } return pTemplate; diff --git a/dlls/env_zoom.h b/dlls/env_zoom.h index 960aabec..64e5eefd 100644 --- a/dlls/env_zoom.h +++ b/dlls/env_zoom.h @@ -1,13 +1,13 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -//=============================================================================// - -#ifndef ENV_ZOOM_H -#define ENV_ZOOM_H - -bool CanOverrideEnvZoomOwner( CBaseEntity *pZoomOwner ); -float GetZoomOwnerDesiredFOV( CBaseEntity *pZoomOwner ); - -#endif //ENV_ZOOM_H \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#ifndef ENV_ZOOM_H +#define ENV_ZOOM_H + +bool CanOverrideEnvZoomOwner( CBaseEntity *pZoomOwner ); +float GetZoomOwnerDesiredFOV( CBaseEntity *pZoomOwner ); + +#endif //ENV_ZOOM_H diff --git a/dlls/envmicrophone.cpp b/dlls/envmicrophone.cpp index dda4314e..3e099b61 100644 --- a/dlls/envmicrophone.cpp +++ b/dlls/envmicrophone.cpp @@ -92,7 +92,7 @@ void CEnvMicrophone::Spawn(void) { SF_MICROPHONE_SOUND_EXPLOSION, SOUND_CONTEXT_EXPLOSION }, }; - for (int i = 0; i < sizeof(nFlags) / sizeof(nFlags[0]); i++) + for (size_t i = 0; i < sizeof(nFlags) / sizeof(nFlags[0]); i++) { if (m_spawnflags & nFlags[i][0]) { diff --git a/dlls/episodic/ai_behavior_passenger_companion.cpp b/dlls/episodic/ai_behavior_passenger_companion.cpp index 3b23a62d..bc05b79c 100644 --- a/dlls/episodic/ai_behavior_passenger_companion.cpp +++ b/dlls/episodic/ai_behavior_passenger_companion.cpp @@ -64,9 +64,9 @@ BEGIN_SIMPLE_DATADESC( FailPosition_t ) END_DATADESC(); CAI_PassengerBehaviorCompanion::CAI_PassengerBehaviorCompanion( void ) : -m_flUnseenDuration( 0.0f ), m_flNextOverturnWarning( 0.0f ), m_flOverturnedDuration( 0.0f ), +m_flUnseenDuration( 0.0f ), m_nExitAttempts( 0 ) { memset( &m_vehicleState, 0, sizeof( m_vehicleState ) ); diff --git a/dlls/episodic/ai_behavior_passenger_zombie.cpp b/dlls/episodic/ai_behavior_passenger_zombie.cpp index a0a23e7c..b17750bf 100644 --- a/dlls/episodic/ai_behavior_passenger_zombie.cpp +++ b/dlls/episodic/ai_behavior_passenger_zombie.cpp @@ -70,8 +70,8 @@ impactdamagetable_t gZombiePassengerImpactDamageTable = // Constructor //----------------------------------------------------------------------------- CAI_PassengerBehaviorZombie::CAI_PassengerBehaviorZombie( void ) : -m_flLastVerticalLean( 0.0f ), m_flLastLateralLean( 0.0f ), +m_flLastVerticalLean( 0.0f ), m_flNextLeapTime( 0.0f ) { } diff --git a/dlls/episodic/grenade_hopwire.cpp b/dlls/episodic/grenade_hopwire.cpp index 714c397c..bd3bd9e8 100644 --- a/dlls/episodic/grenade_hopwire.cpp +++ b/dlls/episodic/grenade_hopwire.cpp @@ -38,7 +38,7 @@ class CGravityVortexController : public CBaseEntity public: - CGravityVortexController( void ) : m_flEndTime( 0.0f ), m_flRadius( 256 ), m_flStrength( 256 ), m_flMass( 0.0f ) {} + CGravityVortexController( void ) : m_flMass( 0.0f ), m_flEndTime( 0.0f ), m_flRadius( 256 ), m_flStrength( 256 ) {} float GetConsumedMass( void ) const; static CGravityVortexController *Create( const Vector &origin, float radius, float strength, float duration ); diff --git a/dlls/episodic/grenade_hopwire.h b/dlls/episodic/grenade_hopwire.h index fc063618..3b5cee4b 100644 --- a/dlls/episodic/grenade_hopwire.h +++ b/dlls/episodic/grenade_hopwire.h @@ -11,7 +11,7 @@ #endif #include "basegrenade_shared.h" -#include "sprite.h" +#include "Sprite.h" extern ConVar hopwire_trap; diff --git a/dlls/episodic/prop_coreball.cpp b/dlls/episodic/prop_coreball.cpp index 7c4ed909..d45cc80a 100644 --- a/dlls/episodic/prop_coreball.cpp +++ b/dlls/episodic/prop_coreball.cpp @@ -1,133 +1,133 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: Big pulsating ball inside the core of the citadel -// -//=============================================================================// - -#include "cbase.h" -#include "baseentity.h" - -#define COREBALL_MODEL "models/props_combine/coreball.mdl" - -class CPropCoreBall : public CBaseAnimating -{ -public: - DECLARE_CLASS( CPropCoreBall, CBaseAnimating ); - DECLARE_DATADESC(); - DECLARE_SERVERCLASS(); - - CPropCoreBall(); - - virtual void Spawn( void ); - virtual void Precache( void ); - - CNetworkVar( float, m_flScaleX ); - CNetworkVar( float, m_flScaleY ); - CNetworkVar( float, m_flScaleZ ); - - CNetworkVar( float, m_flLerpTimeX ); - CNetworkVar( float, m_flLerpTimeY ); - CNetworkVar( float, m_flLerpTimeZ ); - - CNetworkVar( float, m_flGoalTimeX ); - CNetworkVar( float, m_flGoalTimeY ); - CNetworkVar( float, m_flGoalTimeZ ); - - void InputSetScaleX( inputdata_t &inputdata ); - void InputSetScaleY( inputdata_t &inputdata ); - void InputSetScaleZ( inputdata_t &inputdata ); -}; - -LINK_ENTITY_TO_CLASS( prop_coreball, CPropCoreBall ); - -BEGIN_DATADESC( CPropCoreBall ) - DEFINE_INPUTFUNC( FIELD_VECTOR, "SetScaleX", InputSetScaleX ), - DEFINE_INPUTFUNC( FIELD_VECTOR, "SetScaleY", InputSetScaleY ), - DEFINE_INPUTFUNC( FIELD_VECTOR, "SetScaleZ", InputSetScaleZ ), - - DEFINE_FIELD( m_flScaleX, FIELD_FLOAT ), - DEFINE_FIELD( m_flScaleY, FIELD_FLOAT ), - DEFINE_FIELD( m_flScaleZ, FIELD_FLOAT ), - - DEFINE_FIELD( m_flLerpTimeX, FIELD_FLOAT ), - DEFINE_FIELD( m_flLerpTimeY, FIELD_FLOAT ), - DEFINE_FIELD( m_flLerpTimeZ, FIELD_FLOAT ), - - DEFINE_FIELD( m_flGoalTimeX, FIELD_FLOAT ), - DEFINE_FIELD( m_flGoalTimeY, FIELD_FLOAT ), - DEFINE_FIELD( m_flGoalTimeZ, FIELD_FLOAT ), -END_DATADESC() - -IMPLEMENT_SERVERCLASS_ST( CPropCoreBall, DT_PropCoreBall ) - SendPropFloat( SENDINFO(m_flScaleX), 0, SPROP_NOSCALE ), - SendPropFloat( SENDINFO(m_flScaleY), 0, SPROP_NOSCALE ), - SendPropFloat( SENDINFO(m_flScaleZ), 0, SPROP_NOSCALE ), - - SendPropFloat( SENDINFO(m_flLerpTimeX), 0, SPROP_NOSCALE ), - SendPropFloat( SENDINFO(m_flLerpTimeY), 0, SPROP_NOSCALE ), - SendPropFloat( SENDINFO(m_flLerpTimeZ), 0, SPROP_NOSCALE ), - - SendPropFloat( SENDINFO(m_flGoalTimeX), 0, SPROP_NOSCALE ), - SendPropFloat( SENDINFO(m_flGoalTimeY), 0, SPROP_NOSCALE ), - SendPropFloat( SENDINFO(m_flGoalTimeZ), 0, SPROP_NOSCALE ), -END_SEND_TABLE() - -CPropCoreBall::CPropCoreBall( void ) -{ - m_flScaleX = 1.0f; - m_flScaleY = 1.0f; - m_flScaleZ = 1.0f; - - UseClientSideAnimation(); -} - -void CPropCoreBall::Spawn( void ) -{ - Precache(); - SetModel( COREBALL_MODEL ); - - SetMoveType( MOVETYPE_NONE ); - - BaseClass::Spawn(); - - AddEffects( EF_NOSHADOW ); - - SetSequence( 0 ); - SetPlaybackRate( 1.0f ); -} - -void CPropCoreBall::Precache( void ) -{ - BaseClass::Precache(); - PrecacheModel( COREBALL_MODEL ); -} - -void CPropCoreBall::InputSetScaleX( inputdata_t &inputdata ) -{ - Vector vecScale; - inputdata.value.Vector3D( vecScale ); - - m_flScaleX = vecScale.x; - m_flLerpTimeX = vecScale.y; - m_flGoalTimeX = gpGlobals->curtime; -} - -void CPropCoreBall::InputSetScaleY( inputdata_t &inputdata ) -{ - Vector vecScale; - inputdata.value.Vector3D( vecScale ); - - m_flScaleY = vecScale.x; - m_flLerpTimeY = vecScale.y; - m_flGoalTimeY = gpGlobals->curtime; -} - -void CPropCoreBall::InputSetScaleZ( inputdata_t &inputdata ) -{ - Vector vecScale; - inputdata.value.Vector3D( vecScale ); - - m_flScaleZ = vecScale.x; - m_flLerpTimeZ = vecScale.y; - m_flGoalTimeZ = gpGlobals->curtime; -} \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: Big pulsating ball inside the core of the citadel +// +//=============================================================================// + +#include "cbase.h" +#include "baseentity.h" + +#define COREBALL_MODEL "models/props_combine/coreball.mdl" + +class CPropCoreBall : public CBaseAnimating +{ +public: + DECLARE_CLASS( CPropCoreBall, CBaseAnimating ); + DECLARE_DATADESC(); + DECLARE_SERVERCLASS(); + + CPropCoreBall(); + + virtual void Spawn( void ); + virtual void Precache( void ); + + CNetworkVar( float, m_flScaleX ); + CNetworkVar( float, m_flScaleY ); + CNetworkVar( float, m_flScaleZ ); + + CNetworkVar( float, m_flLerpTimeX ); + CNetworkVar( float, m_flLerpTimeY ); + CNetworkVar( float, m_flLerpTimeZ ); + + CNetworkVar( float, m_flGoalTimeX ); + CNetworkVar( float, m_flGoalTimeY ); + CNetworkVar( float, m_flGoalTimeZ ); + + void InputSetScaleX( inputdata_t &inputdata ); + void InputSetScaleY( inputdata_t &inputdata ); + void InputSetScaleZ( inputdata_t &inputdata ); +}; + +LINK_ENTITY_TO_CLASS( prop_coreball, CPropCoreBall ); + +BEGIN_DATADESC( CPropCoreBall ) + DEFINE_INPUTFUNC( FIELD_VECTOR, "SetScaleX", InputSetScaleX ), + DEFINE_INPUTFUNC( FIELD_VECTOR, "SetScaleY", InputSetScaleY ), + DEFINE_INPUTFUNC( FIELD_VECTOR, "SetScaleZ", InputSetScaleZ ), + + DEFINE_FIELD( m_flScaleX, FIELD_FLOAT ), + DEFINE_FIELD( m_flScaleY, FIELD_FLOAT ), + DEFINE_FIELD( m_flScaleZ, FIELD_FLOAT ), + + DEFINE_FIELD( m_flLerpTimeX, FIELD_FLOAT ), + DEFINE_FIELD( m_flLerpTimeY, FIELD_FLOAT ), + DEFINE_FIELD( m_flLerpTimeZ, FIELD_FLOAT ), + + DEFINE_FIELD( m_flGoalTimeX, FIELD_FLOAT ), + DEFINE_FIELD( m_flGoalTimeY, FIELD_FLOAT ), + DEFINE_FIELD( m_flGoalTimeZ, FIELD_FLOAT ), +END_DATADESC() + +IMPLEMENT_SERVERCLASS_ST( CPropCoreBall, DT_PropCoreBall ) + SendPropFloat( SENDINFO(m_flScaleX), 0, SPROP_NOSCALE ), + SendPropFloat( SENDINFO(m_flScaleY), 0, SPROP_NOSCALE ), + SendPropFloat( SENDINFO(m_flScaleZ), 0, SPROP_NOSCALE ), + + SendPropFloat( SENDINFO(m_flLerpTimeX), 0, SPROP_NOSCALE ), + SendPropFloat( SENDINFO(m_flLerpTimeY), 0, SPROP_NOSCALE ), + SendPropFloat( SENDINFO(m_flLerpTimeZ), 0, SPROP_NOSCALE ), + + SendPropFloat( SENDINFO(m_flGoalTimeX), 0, SPROP_NOSCALE ), + SendPropFloat( SENDINFO(m_flGoalTimeY), 0, SPROP_NOSCALE ), + SendPropFloat( SENDINFO(m_flGoalTimeZ), 0, SPROP_NOSCALE ), +END_SEND_TABLE() + +CPropCoreBall::CPropCoreBall( void ) +{ + m_flScaleX = 1.0f; + m_flScaleY = 1.0f; + m_flScaleZ = 1.0f; + + UseClientSideAnimation(); +} + +void CPropCoreBall::Spawn( void ) +{ + Precache(); + SetModel( COREBALL_MODEL ); + + SetMoveType( MOVETYPE_NONE ); + + BaseClass::Spawn(); + + AddEffects( EF_NOSHADOW ); + + SetSequence( 0 ); + SetPlaybackRate( 1.0f ); +} + +void CPropCoreBall::Precache( void ) +{ + BaseClass::Precache(); + PrecacheModel( COREBALL_MODEL ); +} + +void CPropCoreBall::InputSetScaleX( inputdata_t &inputdata ) +{ + Vector vecScale; + inputdata.value.Vector3D( vecScale ); + + m_flScaleX = vecScale.x; + m_flLerpTimeX = vecScale.y; + m_flGoalTimeX = gpGlobals->curtime; +} + +void CPropCoreBall::InputSetScaleY( inputdata_t &inputdata ) +{ + Vector vecScale; + inputdata.value.Vector3D( vecScale ); + + m_flScaleY = vecScale.x; + m_flLerpTimeY = vecScale.y; + m_flGoalTimeY = gpGlobals->curtime; +} + +void CPropCoreBall::InputSetScaleZ( inputdata_t &inputdata ) +{ + Vector vecScale; + inputdata.value.Vector3D( vecScale ); + + m_flScaleZ = vecScale.x; + m_flLerpTimeZ = vecScale.y; + m_flGoalTimeZ = gpGlobals->curtime; +} diff --git a/dlls/event_tempentity_tester.cpp b/dlls/event_tempentity_tester.cpp index ab8a9e09..ba0350d3 100644 --- a/dlls/event_tempentity_tester.cpp +++ b/dlls/event_tempentity_tester.cpp @@ -1,125 +1,125 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $Workfile: $ -// $Date: $ -// $NoKeywords: $ -//=============================================================================// -#include "cbase.h" -#include "basetempentity.h" -#include "event_tempentity_tester.h" -#include "vstdlib/strtools.h" - -// memdbgon must be the last include file in a .cpp file!!! -#include "tier0/memdbgon.h" - -#define TEMPENT_TEST_GAP 1.0f - -//----------------------------------------------------------------------------- -// Purpose: -// Input : &vecOrigin - -// &vecAngles - -// *single_te - -// Output : CBaseEntity -//----------------------------------------------------------------------------- -CBaseEntity *CTempEntTester::Create( const Vector &vecOrigin, const QAngle &vecAngles, const char *lifetime, const char *single_te ) -{ - float life; - char classname[ 128 ]; - if ( lifetime && lifetime[0] ) - { - life = atoi( lifetime ); - life = max( 1.0, life ); - life = min( 1000.0, life ); - - life += gpGlobals->curtime; - } - else - { - Msg( "Usage: te \n" ); - return NULL; - } - - if ( single_te && single_te[0] ) - { - Q_strncpy( classname, single_te ,sizeof(classname)); - strlwr( classname ); - } - else - { - Msg( "Usage: te \n" ); - return NULL; - } - - CTempEntTester *p = ( CTempEntTester * )CBaseEntity::CreateNoSpawn( "te_tester", vecOrigin, vecAngles ); - if ( !p ) - { - return NULL; - } - - Q_strncpy( p->m_szClass, classname ,sizeof(p->m_szClass)); - p->m_fLifeTime = life; - - p->Spawn(); - - return p; -} - -LINK_ENTITY_TO_CLASS( te_tester, CTempEntTester ); - -//----------------------------------------------------------------------------- -// Purpose: Called when object is being created -//----------------------------------------------------------------------------- -void CTempEntTester::Spawn( void ) -{ - // Not a physical thing... - AddEffects( EF_NODRAW ); - - m_pCurrent = CBaseTempEntity::GetList(); - while ( m_pCurrent ) - { - char name[ 128 ]; - Q_strncpy( name, m_pCurrent->GetName() ,sizeof(name)); - strlwr( name ); - if ( strstr( name, m_szClass ) ) - { - break; - } - - m_pCurrent = m_pCurrent->GetNext(); - } - - if ( !m_pCurrent ) - { - DevMsg("Couldn't find temp entity '%s'\n", m_szClass ); - UTIL_Remove( this ); - return; - } - - // Think right away - SetNextThink( gpGlobals->curtime ); -} - -//----------------------------------------------------------------------------- -// Purpose: Called when object should fire itself and move on -//----------------------------------------------------------------------------- -void CTempEntTester::Think( void ) -{ - // Should never happen - if ( !m_pCurrent ) - { - UTIL_Remove( this ); - return; - } - - m_pCurrent->Test( GetLocalOrigin(), GetLocalAngles() ); - SetNextThink( gpGlobals->curtime + TEMPENT_TEST_GAP ); - - // Time to destroy? - if ( gpGlobals->curtime >= m_fLifeTime ) - { - UTIL_Remove( this ); - return; - } -} \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $Workfile: $ +// $Date: $ +// $NoKeywords: $ +//=============================================================================// +#include "cbase.h" +#include "basetempentity.h" +#include "event_tempentity_tester.h" +#include "vstdlib/strtools.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +#define TEMPENT_TEST_GAP 1.0f + +//----------------------------------------------------------------------------- +// Purpose: +// Input : &vecOrigin - +// &vecAngles - +// *single_te - +// Output : CBaseEntity +//----------------------------------------------------------------------------- +CBaseEntity *CTempEntTester::Create( const Vector &vecOrigin, const QAngle &vecAngles, const char *lifetime, const char *single_te ) +{ + float life; + char classname[ 128 ]; + if ( lifetime && lifetime[0] ) + { + life = atoi( lifetime ); + life = max( 1.0, life ); + life = min( 1000.0, life ); + + life += gpGlobals->curtime; + } + else + { + Msg( "Usage: te \n" ); + return NULL; + } + + if ( single_te && single_te[0] ) + { + Q_strncpy( classname, single_te ,sizeof(classname)); + Q_strlower( classname ); + } + else + { + Msg( "Usage: te \n" ); + return NULL; + } + + CTempEntTester *p = ( CTempEntTester * )CBaseEntity::CreateNoSpawn( "te_tester", vecOrigin, vecAngles ); + if ( !p ) + { + return NULL; + } + + Q_strncpy( p->m_szClass, classname ,sizeof(p->m_szClass)); + p->m_fLifeTime = life; + + p->Spawn(); + + return p; +} + +LINK_ENTITY_TO_CLASS( te_tester, CTempEntTester ); + +//----------------------------------------------------------------------------- +// Purpose: Called when object is being created +//----------------------------------------------------------------------------- +void CTempEntTester::Spawn( void ) +{ + // Not a physical thing... + AddEffects( EF_NODRAW ); + + m_pCurrent = CBaseTempEntity::GetList(); + while ( m_pCurrent ) + { + char name[ 128 ]; + Q_strncpy( name, m_pCurrent->GetName() ,sizeof(name)); + Q_strlower( name ); + if ( strstr( name, m_szClass ) ) + { + break; + } + + m_pCurrent = m_pCurrent->GetNext(); + } + + if ( !m_pCurrent ) + { + DevMsg("Couldn't find temp entity '%s'\n", m_szClass ); + UTIL_Remove( this ); + return; + } + + // Think right away + SetNextThink( gpGlobals->curtime ); +} + +//----------------------------------------------------------------------------- +// Purpose: Called when object should fire itself and move on +//----------------------------------------------------------------------------- +void CTempEntTester::Think( void ) +{ + // Should never happen + if ( !m_pCurrent ) + { + UTIL_Remove( this ); + return; + } + + m_pCurrent->Test( GetLocalOrigin(), GetLocalAngles() ); + SetNextThink( gpGlobals->curtime + TEMPENT_TEST_GAP ); + + // Time to destroy? + if ( gpGlobals->curtime >= m_fLifeTime ) + { + UTIL_Remove( this ); + return; + } +} diff --git a/dlls/event_tempentity_tester.h b/dlls/event_tempentity_tester.h index 6a810d65..67af11cc 100644 --- a/dlls/event_tempentity_tester.h +++ b/dlls/event_tempentity_tester.h @@ -1,40 +1,40 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $Workfile: $ -// $Date: $ -// $NoKeywords: $ -//=============================================================================// -#if !defined( EVENT_TEMPENTITY_TESTER_H ) -#define EVENT_TEMPENTITY_TESTER_H -#ifdef _WIN32 -#pragma once -#endif - -class CBaseTempEntity; - - -//----------------------------------------------------------------------------- -// Purpose: A server object that is used to test all registered temp entities -//----------------------------------------------------------------------------- -class CTempEntTester : public CPointEntity -{ -public: - DECLARE_CLASS( CTempEntTester, CPointEntity ); - - void Spawn( void ); - void Think( void ); - - static CBaseEntity *Create( const Vector &vecOrigin, const QAngle &vecAngles, const char *lifetime, const char *single_te ); -private: - // Current temp entity to test - CBaseTempEntity *m_pCurrent; - - // Lifetime - float m_fLifeTime; - - char m_szClass[ 64 ]; -}; - -#endif // EVENT_TEMPENTITY_TESTER_H \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $Workfile: $ +// $Date: $ +// $NoKeywords: $ +//=============================================================================// +#if !defined( EVENT_TEMPENTITY_TESTER_H ) +#define EVENT_TEMPENTITY_TESTER_H +#ifdef _WIN32 +#pragma once +#endif + +class CBaseTempEntity; + + +//----------------------------------------------------------------------------- +// Purpose: A server object that is used to test all registered temp entities +//----------------------------------------------------------------------------- +class CTempEntTester : public CPointEntity +{ +public: + DECLARE_CLASS( CTempEntTester, CPointEntity ); + + void Spawn( void ); + void Think( void ); + + static CBaseEntity *Create( const Vector &vecOrigin, const QAngle &vecAngles, const char *lifetime, const char *single_te ); +private: + // Current temp entity to test + CBaseTempEntity *m_pCurrent; + + // Lifetime + float m_fLifeTime; + + char m_szClass[ 64 ]; +}; + +#endif // EVENT_TEMPENTITY_TESTER_H diff --git a/dlls/explode.cpp b/dlls/explode.cpp index 3511592c..5fa7c5bd 100644 --- a/dlls/explode.cpp +++ b/dlls/explode.cpp @@ -293,7 +293,7 @@ void CEnvExplosion::InputExplode( inputdata_t &inputdata ) } //Get the damage override if specified - int iRadius = ( m_iRadiusOverride > 0 ) ? m_iRadiusOverride : ( m_iMagnitude * 2.5f ); + int iRadius = ( m_iRadiusOverride > 0 ) ? m_iRadiusOverride : ( static_cast(m_iMagnitude * 2.5f) ); CPASFilter filter( vecExplodeOrigin ); te->Explosion( filter, 0.0, diff --git a/dlls/fire.cpp b/dlls/fire.cpp index 29f54f9a..fa711485 100644 --- a/dlls/fire.cpp +++ b/dlls/fire.cpp @@ -940,7 +940,7 @@ void CFire::Update( float simTime ) if ( m_flDamageTime <= gpGlobals->curtime ) { m_flDamageTime = gpGlobals->curtime + fire_dmginterval.GetFloat(); - outputDamage = (fire_dmgbase.GetFloat() + outputHeat * fire_dmgscale.GetFloat() * m_flDamageScale) * fire_dmginterval.GetFloat(); + outputDamage = static_cast((fire_dmgbase.GetFloat() + outputHeat * fire_dmgscale.GetFloat() * m_flDamageScale) * fire_dmginterval.GetFloat()); if ( outputDamage ) { damage = true; @@ -957,7 +957,7 @@ void CFire::Update( float simTime ) } else if ( FClassnameIs( pOther, "env_fire" ) ) { - if ( fireCount < ARRAYSIZE(pFires) ) + if ( fireCount < static_cast(ARRAYSIZE(pFires)) ) { pFires[fireCount] = (CFire *)pOther; fireCount++; diff --git a/dlls/fire_smoke.cpp b/dlls/fire_smoke.cpp index 6da8dc1f..204fc71b 100644 --- a/dlls/fire_smoke.cpp +++ b/dlls/fire_smoke.cpp @@ -1,195 +1,195 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -// -//=============================================================================// -#include "cbase.h" -#include "fire_smoke.h" - -// memdbgon must be the last include file in a .cpp file!!! -#include "tier0/memdbgon.h" - -//--------------------------------------------------------- -// Save/Restore -//--------------------------------------------------------- -BEGIN_DATADESC( CBaseFire ) - - DEFINE_FIELD( m_flStartScale, FIELD_FLOAT ), - DEFINE_FIELD( m_flScale, FIELD_FLOAT ), - DEFINE_FIELD( m_flScaleTime, FIELD_TIME ), - DEFINE_FIELD( m_nFlags, FIELD_INTEGER ), - -END_DATADESC() - - -//================================================== -// CBaseFire -//================================================== - -CBaseFire::CBaseFire( void ) -{ - m_flStartScale = 0.0f; - m_flScale = 0.0f; - m_flScaleTime = 0.0f; - m_nFlags = bitsFIRE_NONE; -} - -CBaseFire::~CBaseFire( void ) -{ -} - -//----------------------------------------------------------------------------- -// Purpose: Take the current scale of the flame and move it towards a destination -// Input : size - destination size -// time - time to scale across -//----------------------------------------------------------------------------- -void CBaseFire::Scale( float size, float time ) -{ - //Send to the client - m_flScale = size; - m_flScaleTime = time; -} - -//----------------------------------------------------------------------------- -// Purpose: Overloaded Scale() function to set size -// Input : start - beginning sizek -// size - destination size -// time - time to scale across -//----------------------------------------------------------------------------- -void CBaseFire::Scale( float start, float size, float time ) -{ - //Send to the client - m_flStartScale = start; - m_flScale = size; - m_flScaleTime = time; -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : state - -//----------------------------------------------------------------------------- -void CBaseFire::Enable( int state ) -{ - if ( state ) - { - m_nFlags |= bitsFIRE_ACTIVE; - } - else - { - m_nFlags &= ~bitsFIRE_ACTIVE; - } -} - -//================================================== -// CFireSmoke -//================================================== - -//Link the entity -LINK_ENTITY_TO_CLASS( _firesmoke, CFireSmoke ); - -//Send datatable -IMPLEMENT_SERVERCLASS_ST( CFireSmoke, DT_FireSmoke ) - SendPropFloat( SENDINFO( m_flStartScale ), 0, SPROP_NOSCALE), - SendPropFloat( SENDINFO( m_flScale ), 0, SPROP_NOSCALE), - SendPropFloat( SENDINFO( m_flScaleTime ), 0, SPROP_NOSCALE), - SendPropInt( SENDINFO( m_nFlags ), 8, SPROP_UNSIGNED ), - SendPropModelIndex( SENDINFO( m_nFlameModelIndex ) ), - SendPropModelIndex( SENDINFO( m_nFlameFromAboveModelIndex ) ), -END_SEND_TABLE() - -//Data description -BEGIN_DATADESC( CFireSmoke ) - - DEFINE_FIELD( m_flStartScale, FIELD_FLOAT ), - DEFINE_FIELD( m_flScale, FIELD_FLOAT ), - DEFINE_FIELD( m_flScaleTime, FIELD_FLOAT ), - DEFINE_FIELD( m_nFlags, FIELD_INTEGER ), - DEFINE_FIELD( m_nFlameFromAboveModelIndex, FIELD_INTEGER ), - -END_DATADESC() - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *name - -//----------------------------------------------------------------------------- -CFireSmoke::CFireSmoke( void ) -{ - //Client-side - m_flScale = 0.0f; - m_flScaleTime = 0.0f; - m_nFlags = bitsFIRE_NONE; - - //Server-side - AddEFlags( EFL_FORCE_CHECK_TRANSMIT ); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -CFireSmoke::~CFireSmoke( void ) -{ -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CFireSmoke::Precache() -{ - BaseClass::Precache(); - m_nFlameModelIndex = PrecacheModel( "sprites/fire1.vmt" ); - - // This asset doesn't appear to exist anymore. What's going on? - // Commenting this out so that level designers don't get a red error about missing material. (sjb) - //m_nFlameFromAboveModelIndex = PrecacheModel( "sprites/flamefromabove.vmt" ); -} - -void CFireSmoke::Spawn() -{ - Precache(); -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : state - -//----------------------------------------------------------------------------- -void CFireSmoke::EnableSmoke( int state ) -{ - if ( state ) - { - m_nFlags |= bitsFIRESMOKE_SMOKE; - } - else - { - m_nFlags &= ~bitsFIRESMOKE_SMOKE; - } -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : state - -//----------------------------------------------------------------------------- -void CFireSmoke::EnableGlow( int state ) -{ - if ( state ) - { - m_nFlags |= bitsFIRESMOKE_GLOW; - } - else - { - m_nFlags &= ~bitsFIRESMOKE_GLOW; - } -} - -void CFireSmoke::EnableVisibleFromAbove( int state ) -{ - if ( state ) - { - m_nFlags |= bitsFIRESMOKE_VISIBLE_FROM_ABOVE; - } - else - { - m_nFlags &= ~bitsFIRESMOKE_VISIBLE_FROM_ABOVE; - } -} \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// +#include "cbase.h" +#include "fire_smoke.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +//--------------------------------------------------------- +// Save/Restore +//--------------------------------------------------------- +BEGIN_DATADESC( CBaseFire ) + + DEFINE_FIELD( m_flStartScale, FIELD_FLOAT ), + DEFINE_FIELD( m_flScale, FIELD_FLOAT ), + DEFINE_FIELD( m_flScaleTime, FIELD_TIME ), + DEFINE_FIELD( m_nFlags, FIELD_INTEGER ), + +END_DATADESC() + + +//================================================== +// CBaseFire +//================================================== + +CBaseFire::CBaseFire( void ) +{ + m_flStartScale = 0.0f; + m_flScale = 0.0f; + m_flScaleTime = 0.0f; + m_nFlags = bitsFIRE_NONE; +} + +CBaseFire::~CBaseFire( void ) +{ +} + +//----------------------------------------------------------------------------- +// Purpose: Take the current scale of the flame and move it towards a destination +// Input : size - destination size +// time - time to scale across +//----------------------------------------------------------------------------- +void CBaseFire::Scale( float size, float time ) +{ + //Send to the client + m_flScale = size; + m_flScaleTime = time; +} + +//----------------------------------------------------------------------------- +// Purpose: Overloaded Scale() function to set size +// Input : start - beginning sizek +// size - destination size +// time - time to scale across +//----------------------------------------------------------------------------- +void CBaseFire::Scale( float start, float size, float time ) +{ + //Send to the client + m_flStartScale = start; + m_flScale = size; + m_flScaleTime = time; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : state - +//----------------------------------------------------------------------------- +void CBaseFire::Enable( int state ) +{ + if ( state ) + { + m_nFlags |= bitsFIRE_ACTIVE; + } + else + { + m_nFlags &= ~bitsFIRE_ACTIVE; + } +} + +//================================================== +// CFireSmoke +//================================================== + +//Link the entity +LINK_ENTITY_TO_CLASS( _firesmoke, CFireSmoke ); + +//Send datatable +IMPLEMENT_SERVERCLASS_ST( CFireSmoke, DT_FireSmoke ) + SendPropFloat( SENDINFO( m_flStartScale ), 0, SPROP_NOSCALE), + SendPropFloat( SENDINFO( m_flScale ), 0, SPROP_NOSCALE), + SendPropFloat( SENDINFO( m_flScaleTime ), 0, SPROP_NOSCALE), + SendPropInt( SENDINFO( m_nFlags ), 8, SPROP_UNSIGNED ), + SendPropModelIndex( SENDINFO( m_nFlameModelIndex ) ), + SendPropModelIndex( SENDINFO( m_nFlameFromAboveModelIndex ) ), +END_SEND_TABLE() + +//Data description +BEGIN_DATADESC( CFireSmoke ) + + DEFINE_FIELD( m_flStartScale, FIELD_FLOAT ), + DEFINE_FIELD( m_flScale, FIELD_FLOAT ), + DEFINE_FIELD( m_flScaleTime, FIELD_FLOAT ), + DEFINE_FIELD( m_nFlags, FIELD_INTEGER ), + DEFINE_FIELD( m_nFlameFromAboveModelIndex, FIELD_INTEGER ), + +END_DATADESC() + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *name - +//----------------------------------------------------------------------------- +CFireSmoke::CFireSmoke( void ) +{ + //Client-side + m_flScale = 0.0f; + m_flScaleTime = 0.0f; + m_nFlags = bitsFIRE_NONE; + + //Server-side + AddEFlags( EFL_FORCE_CHECK_TRANSMIT ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CFireSmoke::~CFireSmoke( void ) +{ +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CFireSmoke::Precache() +{ + BaseClass::Precache(); + m_nFlameModelIndex = PrecacheModel( "sprites/fire1.vmt" ); + + // This asset doesn't appear to exist anymore. What's going on? + // Commenting this out so that level designers don't get a red error about missing material. (sjb) + //m_nFlameFromAboveModelIndex = PrecacheModel( "sprites/flamefromabove.vmt" ); +} + +void CFireSmoke::Spawn() +{ + Precache(); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : state - +//----------------------------------------------------------------------------- +void CFireSmoke::EnableSmoke( int state ) +{ + if ( state ) + { + m_nFlags |= bitsFIRESMOKE_SMOKE; + } + else + { + m_nFlags &= ~bitsFIRESMOKE_SMOKE; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : state - +//----------------------------------------------------------------------------- +void CFireSmoke::EnableGlow( int state ) +{ + if ( state ) + { + m_nFlags |= bitsFIRESMOKE_GLOW; + } + else + { + m_nFlags &= ~bitsFIRESMOKE_GLOW; + } +} + +void CFireSmoke::EnableVisibleFromAbove( int state ) +{ + if ( state ) + { + m_nFlags |= bitsFIRESMOKE_VISIBLE_FROM_ABOVE; + } + else + { + m_nFlags &= ~bitsFIRESMOKE_VISIBLE_FROM_ABOVE; + } +} diff --git a/dlls/fogcontroller.cpp b/dlls/fogcontroller.cpp index 1cf2c26b..ebc2b0da 100644 --- a/dlls/fogcontroller.cpp +++ b/dlls/fogcontroller.cpp @@ -48,7 +48,7 @@ public: void InputStartFogTransition(inputdata_t &data); - int CFogController::DrawDebugTextOverlays(void); + int DrawDebugTextOverlays(void); void SetLerpValues( void ); void Spawn( void ); diff --git a/dlls/fourwheelvehiclephysics.cpp b/dlls/fourwheelvehiclephysics.cpp index 5f107bdf..4ae8cfbe 100644 --- a/dlls/fourwheelvehiclephysics.cpp +++ b/dlls/fourwheelvehiclephysics.cpp @@ -741,7 +741,7 @@ bool CFourWheelVehiclePhysics::Think() m_nLastSpeed = m_nSpeed; m_nSpeed = ( int )carSpeed; m_nRPM = ( int )carState.engineRPM; - m_nHasBoost = vehicleData.engine.boostDelay; // if we have any boost delay, vehicle has boost ability + m_nHasBoost = static_cast(vehicleData.engine.boostDelay); // if we have any boost delay, vehicle has boost ability m_pVehicle->Update( gpGlobals->frametime, m_controls); diff --git a/dlls/fourwheelvehiclephysics.h b/dlls/fourwheelvehiclephysics.h index 250f59f9..0a9db41b 100644 --- a/dlls/fourwheelvehiclephysics.h +++ b/dlls/fourwheelvehiclephysics.h @@ -183,7 +183,7 @@ inline int CFourWheelVehiclePhysics::GetSpeed() const inline int CFourWheelVehiclePhysics::GetMaxSpeed() const { - return INS2MPH(m_pVehicle->GetVehicleParams().engine.maxSpeed); + return static_cast(INS2MPH(m_pVehicle->GetVehicleParams().engine.maxSpeed)); } inline int CFourWheelVehiclePhysics::GetRPM() const diff --git a/dlls/func_break.cpp b/dlls/func_break.cpp index 7e58f841..057965be 100644 --- a/dlls/func_break.cpp +++ b/dlls/func_break.cpp @@ -55,11 +55,11 @@ extern Vector g_vecAttackDir; "item_rpg_round", // 12 "unused (item_smg1_grenade) 13",// 13 "item_box_sniper_rounds", // 14 - "unused (???) 15", // 15 + "unused (?) 15", // 15 "weapon_stunstick", // 16 "unused (weapon_ar1) 17", // 17 "weapon_ar2", // 18 - "unused (???) 19", // 19 + "unused (?) 19", // 19 "weapon_rpg", // 20 "weapon_smg1", // 21 "unused (weapon_smg2) 22", // 22 @@ -209,13 +209,13 @@ bool CBreakable::KeyValue( const char *szKeyName, const char *szValue ) else if (FStrEq(szKeyName, "spawnobject") ) { int object = atoi( szValue ); - if ( object > 0 && object < ARRAYSIZE(pSpawnObjects) ) + if ( object > 0 && object < static_cast(ARRAYSIZE(pSpawnObjects)) ) m_iszSpawnObject = MAKE_STRING( pSpawnObjects[object] ); } else if (FStrEq(szKeyName, "propdata") ) { int pdata = atoi( szValue ); - if ( pdata > 0 && pdata < ARRAYSIZE(pFGDPropData) ) + if ( pdata > 0 && pdata < static_cast(ARRAYSIZE(pFGDPropData)) ) { m_iszPropData = MAKE_STRING( pFGDPropData[pdata] ); } @@ -452,7 +452,7 @@ void CBreakable::Precache( void ) else { // Actually, precache all possible objects... - for ( int i = 0; i < ARRAYSIZE(pSpawnObjects) ; ++i ) + for ( size_t i = 0; i < ARRAYSIZE(pSpawnObjects) ; ++i ) { if ( !pSpawnObjects[ i ] ) continue; @@ -716,7 +716,10 @@ void CBreakable::TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, case matUnbreakableGlass: g_pEffects->Ricochet( ptr->endpos, (vecDir*-1.0f) ); - break; + break; + + default: + break; } } @@ -978,7 +981,7 @@ void CBreakable::Die( void ) CCollisionProperty *pCollisionProp = CollisionProp(); Vector vSize = pCollisionProp->OBBSize(); - int iCount = ( vSize[0] * vSize[1] + vSize[1] * vSize[2] + vSize[2] * vSize[0] ) / ( 3 * 12 * 12 ); + int iCount = static_cast(( vSize[0] * vSize[1] + vSize[1] * vSize[2] + vSize[2] * vSize[0] ) / ( 3 * 12 * 12 )); if ( iCount > func_break_max_pieces.GetInt() ) { @@ -1034,7 +1037,7 @@ void CBreakable::Die( void ) if ( Explodable() ) { - ExplosionCreate( vecSpot, pCollisionProp->GetCollisionAngles(), this, GetExplosiveDamage(), GetExplosiveRadius(), true ); + ExplosionCreate( vecSpot, pCollisionProp->GetCollisionAngles(), this, static_cast(GetExplosiveDamage()), static_cast(GetExplosiveRadius()), true ); } } diff --git a/dlls/func_break.h b/dlls/func_break.h index 85cb5eda..87868be2 100644 --- a/dlls/func_break.h +++ b/dlls/func_break.h @@ -91,7 +91,7 @@ public: float GetDmgModClub( void ) { return m_flDmgModClub; } float GetDmgModExplosive( void ) { return m_flDmgModExplosive; } void SetExplosiveRadius( float flRadius ) { m_explodeRadius = flRadius; } - void SetExplosiveDamage( float flDamage ) { m_ExplosionMagnitude = flDamage; } + void SetExplosiveDamage( float flDamage ) { m_ExplosionMagnitude = static_cast(flDamage); } float GetExplosiveRadius( void ) { return m_explodeRadius; } float GetExplosiveDamage( void ) { return m_ExplosionMagnitude; } void SetPhysicsDamageTable( string_t iszTableName ) { m_iszPhysicsDamageTableName = iszTableName; } diff --git a/dlls/func_breakablesurf.cpp b/dlls/func_breakablesurf.cpp index e5234846..2b0fc5d4 100644 --- a/dlls/func_breakablesurf.cpp +++ b/dlls/func_breakablesurf.cpp @@ -367,8 +367,8 @@ void CBreakableSurface::TraceAttack( const CTakeDamageInfo &info, const Vector & // Figure out which panel has taken the damage and break it float flWidth,flHeight; PanePos(ptr->endpos,&flWidth,&flHeight); - int nWidth = flWidth; - int nHeight = flHeight; + int nWidth = static_cast(flWidth); + int nHeight = static_cast(flHeight); if ( ShatterPane(nWidth, nHeight,vecDir*500,ptr->endpos) ) { @@ -438,8 +438,8 @@ void CBreakableSurface::TraceAttack( const CTakeDamageInfo &info, const Vector & { PanePos(ptr->endpos,&flWidth,&flHeight); } - int nWidth = flWidth; - int nHeight = flHeight; + int nWidth = static_cast(flWidth); + int nHeight = static_cast(flHeight); // Blow out a roughly circular patch of tile with some randomness for (int width =nWidth-4;width( GetNextTarget() ); - if ( !other ) - { - DevMsg( 1, "func_ladderendpoint(%s) without matching target\n", GetEntityName() ); - return false; - } - - Vector endPos = other->GetAbsOrigin(); - - CFuncLadder *ladder = ( CFuncLadder * )CreateEntityByName( "func_useableladder" ); - if ( ladder ) - { - ladder->SetEndPoints( startPos, endPos ); - ladder->SetAbsOrigin( GetAbsOrigin() ); - ladder->SetParent( GetParent() ); - ladder->SetName( GetEntityName() ); - ladder->Spawn(); - } - - // Delete both endpoints - UTIL_Remove( other ); - UTIL_Remove( this ); - - return true; -} \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// +#include "cbase.h" +#include "func_ladder.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +//----------------------------------------------------------------------------- +// Purpose: A transient entity used to construct a true CFuncLadder +// FIXME: THIS ENTITY IS OBSOLETE NOW, SHOULD BE REMOVED FROM HERE AND .FGD AT SOME POINT!!! +//----------------------------------------------------------------------------- +class CFuncLadderEndPoint : public CBaseEntity +{ +public: + DECLARE_CLASS( CFuncLadderEndPoint, CBaseEntity ); + + virtual void Activate(); + +private: + bool Validate(); +}; + +LINK_ENTITY_TO_CLASS( func_ladderendpoint, CFuncLadderEndPoint ); + +void CFuncLadderEndPoint::Activate() +{ + BaseClass::Activate(); + + if ( IsMarkedForDeletion() ) + return; + + Validate(); +} + +bool CFuncLadderEndPoint::Validate() +{ + // Find the the other end + Vector startPos = GetAbsOrigin(); + + CFuncLadderEndPoint *other = dynamic_cast< CFuncLadderEndPoint * >( GetNextTarget() ); + if ( !other ) + { + DevMsg( 1, "func_ladderendpoint(%s) without matching target\n", STRING(GetEntityName()) ); + return false; + } + + Vector endPos = other->GetAbsOrigin(); + + CFuncLadder *ladder = ( CFuncLadder * )CreateEntityByName( "func_useableladder" ); + if ( ladder ) + { + ladder->SetEndPoints( startPos, endPos ); + ladder->SetAbsOrigin( GetAbsOrigin() ); + ladder->SetParent( GetParent() ); + ladder->SetName( GetEntityName() ); + ladder->Spawn(); + } + + // Delete both endpoints + UTIL_Remove( other ); + UTIL_Remove( this ); + + return true; +} diff --git a/dlls/game_ui.cpp b/dlls/game_ui.cpp index dbdd6134..21d82a04 100644 --- a/dlls/game_ui.cpp +++ b/dlls/game_ui.cpp @@ -182,7 +182,7 @@ void CGameUI::InputActivate( inputdata_t &inputdata ) CBaseEntity *pEntity = gEntList.FindEntityByName( NULL, inputdata.value.String(), this, inputdata.pActivator, inputdata.pCaller ); if ( pEntity == NULL || pEntity->IsPlayer() == false ) { - Warning( "%s InputActivate: entity %s not found or is not a player!\n", GetEntityName(), inputdata.value.String() ); + Warning( "%s InputActivate: entity %s not found or is not a player!\n", STRING(GetEntityName()), inputdata.value.String() ); return; } @@ -193,7 +193,7 @@ void CGameUI::InputActivate( inputdata_t &inputdata ) // Otherwise try to use the activator if ( inputdata.pActivator == NULL || inputdata.pActivator->IsPlayer() == false ) { - Warning( "%s InputActivate: invalid or missing !activator!\n", GetEntityName(), inputdata.value.String() ); + Warning( "%s InputActivate: invalid or missing !activator!\n", STRING(GetEntityName()), inputdata.value.String() ); return; } diff --git a/dlls/gameinterface.cpp b/dlls/gameinterface.cpp index 175c07d9..50b634c5 100644 --- a/dlls/gameinterface.cpp +++ b/dlls/gameinterface.cpp @@ -1,2591 +1,2590 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: encapsulates and implements all the accessing of the game dll from external -// sources (only the engine at the time of writing) -// This files ONLY contains functions and data necessary to build an interface -// to external modules -//=============================================================================// - -#include "cbase.h" -#include "gamestringpool.h" -#include "mapentities_shared.h" -#include "game.h" -#include "entityapi.h" -#include "client.h" -#include "saverestore.h" -#include "entitylist.h" -#include "gamerules.h" -#include "soundent.h" -#include "player.h" -#include "server_class.h" -#include "ai_node.h" -#include "ai_link.h" -#include "ai_saverestore.h" -#include "ai_networkmanager.h" -#include "ndebugoverlay.h" -#include "ivoiceserver.h" -#include -#include "movehelper_server.h" -#include "networkstringtable_gamedll.h" -#include "filesystem.h" -#include "terrainmodmgr.h" -#include "func_areaportalwindow.h" -#include "igamesystem.h" -#include "init_factory.h" -#include "vstdlib/random.h" -#include "env_wind_shared.h" -#include "engine/IEngineSound.h" -#include "ispatialpartition.h" -#include "textstatsmgr.h" -#include "bitbuf.h" -#include "saverestoretypes.h" -#include "physics_saverestore.h" -#include "tier0/vprof.h" -#include "effect_dispatch_data.h" -#include "engine/IStaticPropMgr.h" -#include "TemplateEntities.h" -#include "ai_speech.h" -#include "soundenvelope.h" -#include "usermessages.h" -#include "physics.h" -#include "mapentities.h" -#include "igameevents.h" -#include "EventLog.h" -#include "datacache/idatacache.h" -#include "engine/ivdebugoverlay.h" -#include "shareddefs.h" -#include "props.h" -#include "timedeventmgr.h" -#include "gameinterface.h" -#include "eventqueue.h" -#include "hltvdirector.h" -#include "SoundEmitterSystem/isoundemittersystembase.h" -#include "nav_mesh.h" -#include "AI_ResponseSystem.h" -#include "saverestore_stringtable.h" -#include "util.h" -#include "vstdlib/ICommandLine.h" -#include "datacache/imdlcache.h" -#include "engine/iserverplugin.h" -#ifdef _WIN32 -#include "ienginevgui.h" -#include "vgui_gamedll_int.h" -#include "vgui_controls/AnimationController.h" -#endif -#include "ragdoll_shared.h" -#include "toolframework/iserverenginetools.h" -#include "sceneentity.h" -#include "appframework/IAppSystemGroup.h" -#include "scenefilecache/ISceneFileCache.h" - -#if !defined( _RETAIL ) -#ifdef _XBOX -#include "xbox/xbox_platform.h" -#include "xbox/xbox_win32stubs.h" -#include "xbox/xbox_core.h" -#endif -#endif - -#ifdef CSTRIKE_DLL // BOTPORT: TODO: move these ifdefs out -#include "bot/bot.h" -#endif - -IUploadGameStats *gamestatsuploader = NULL; - -// memdbgon must be the last include file in a .cpp file!!! -#include "tier0/memdbgon.h" - -CTimedEventMgr g_NetworkPropertyEventMgr; - -ISaveRestoreBlockHandler *GetEventQueueSaveRestoreBlockHandler(); -ISaveRestoreBlockHandler *GetCommentarySaveRestoreBlockHandler(); - -// Engine interfaces. -IVEngineServer *engine = NULL; -IVoiceServer *g_pVoiceServer = NULL; -ICvar *cvar = NULL; -#if !defined(_STATIC_LINKED) -IFileSystem *filesystem = NULL; -#else -extern IFileSystem *filesystem; -#endif -INetworkStringTableContainer *networkstringtable = NULL; -IStaticPropMgrServer *staticpropmgr = NULL; -IUniformRandomStream *random = NULL; -IEngineSound *enginesound = NULL; -ISpatialPartition *partition = NULL; -IVModelInfo *modelinfo = NULL; -IEngineTrace *enginetrace = NULL; -IGameEventManager2 *gameeventmanager = NULL; -IDataCache *datacache = NULL; -IVDebugOverlay * debugoverlay = NULL; -ISoundEmitterSystemBase *soundemitterbase = NULL; -IMDLCache *mdlcache = NULL; -IServerPluginHelpers *serverpluginhelpers = NULL; -#ifdef _WIN32 -IEngineVGui *enginevgui = NULL; -#endif -IServerEngineTools *serverenginetools = NULL; -ISceneFileCache *scenefilecache = NULL; - -IGameSystem *SoundEmitterSystem(); - -bool SceneCacheInit(); -void SceneCacheShutdown(); -bool ModelSoundsCacheInit(); -void ModelSoundsCacheShutdown(); - -void SceneManager_ClientActive( CBasePlayer *player ); - -class IMaterialSystem; -class IStudioRender; - -#ifdef _DEBUG -static ConVar s_UseNetworkVars( "UseNetworkVars", "1", FCVAR_CHEAT, "For profiling, toggle network vars." ); -#endif - -extern ConVar sv_noclipduringpause; -ConVar sv_massreport( "sv_massreport", "0" ); - -ConVar sv_autosave( "sv_autosave", "1", 0, "Set to 1 to save game on level transition. Does not affect autosave triggers." ); -ConVar *sv_maxreplay = NULL; - -// String tables -INetworkStringTable *g_pStringTableEffectDispatch = NULL; -INetworkStringTable *g_pStringTableVguiScreen = NULL; -INetworkStringTable *g_pStringTableMaterials = NULL; -INetworkStringTable *g_pStringTableInfoPanel = NULL; -INetworkStringTable *g_pStringTableClientSideChoreoScenes = NULL; - -CStringTableSaveRestoreOps g_VguiScreenStringOps; - -// Holds global variables shared between engine and game. -CGlobalVars *gpGlobals; -edict_t *g_pDebugEdictBase = 0; -static int g_nCommandClientIndex = 0; - -static ConVar sv_showhitboxes( "sv_showhitboxes", "-1", FCVAR_CHEAT, "Send server-side hitboxes for specified entity to client (NOTE: this uses lots of bandwidth, use on listen server only)." ); - -void PrecachePointTemplates(); - -static ClientPutInServerOverrideFn g_pClientPutInServerOverride = NULL; -static void UpdateChapterRestrictions( const char *mapname ); - - -#if !defined( _XBOX ) // Don't doubly define this symbol. -CSharedEdictChangeInfo *g_pSharedChangeInfo = NULL; - -#endif - -IChangeInfoAccessor *CBaseEdict::GetChangeAccessor() -{ - return engine->GetChangeAccessor( (const edict_t *)this ); -} - -const IChangeInfoAccessor *CBaseEdict::GetChangeAccessor() const -{ - return engine->GetChangeAccessor( (const edict_t *)this ); -} - -const char *GetHintTypeDescription( CAI_Hint *pHint ); - -void ClientPutInServerOverride( ClientPutInServerOverrideFn fn ) -{ - g_pClientPutInServerOverride = fn; -} - - - -//----------------------------------------------------------------------------- -// Purpose: -// Output : int -//----------------------------------------------------------------------------- -int UTIL_GetCommandClientIndex( void ) -{ - // -1 == unknown,dedicated server console - // 0 == player 1 - - // Convert to 1 based offset - return (g_nCommandClientIndex+1); -} - -//----------------------------------------------------------------------------- -// Purpose: -// Output : CBasePlayer -//----------------------------------------------------------------------------- -CBasePlayer *UTIL_GetCommandClient( void ) -{ - int idx = UTIL_GetCommandClientIndex(); - if ( idx > 0 ) - { - return UTIL_PlayerByIndex( idx ); - } - - // HLDS console issued command - return NULL; -} - -extern void InitializeCvars( void ); - -CBaseEntity* FindPickerEntity( CBasePlayer* pPlayer ); -CAI_Node* FindPickerAINode( CBasePlayer* pPlayer, NodeType_e nNodeType ); -CAI_Link* FindPickerAILink( CBasePlayer* pPlayer ); -float GetFloorZ(const Vector &origin); -void UpdateAllClientData( void ); -void DrawMessageEntities(); - -#include "ai_network.h" - -// For now just using one big AI network -extern ConVar think_limit; - - -#if 0 -//----------------------------------------------------------------------------- -// Purpose: Draw output overlays for any measure sections -// Input : -//----------------------------------------------------------------------------- -void DrawMeasuredSections(void) -{ - int row = 1; - float rowheight = 0.025; - - CMeasureSection *p = CMeasureSection::GetList(); - while ( p ) - { - char str[256]; - Q_snprintf(str,sizeof(str),"%s",p->GetName()); - NDebugOverlay::ScreenText( 0.01,0.51+(row*rowheight),str, 255,255,255,255, 0.0 ); - - Q_snprintf(str,sizeof(str),"%5.2f\n",p->GetTime().GetMillisecondsF()); - //Q_snprintf(str,sizeof(str),"%3.3f\n",p->GetTime().GetSeconds() * 100.0 / engine->Time()); - NDebugOverlay::ScreenText( 0.28,0.51+(row*rowheight),str, 255,255,255,255, 0.0 ); - - Q_snprintf(str,sizeof(str),"%5.2f\n",p->GetMaxTime().GetMillisecondsF()); - //Q_snprintf(str,sizeof(str),"%3.3f\n",p->GetTime().GetSeconds() * 100.0 / engine->Time()); - NDebugOverlay::ScreenText( 0.34,0.51+(row*rowheight),str, 255,255,255,255, 0.0 ); - - - row++; - - p = p->GetNext(); - } - - bool sort_reset = false; - - // Time to redo sort? - if ( measure_resort.GetFloat() > 0.0 && - engine->Time() >= CMeasureSection::m_dNextResort ) - { - // Redo it - CMeasureSection::SortSections(); - // Set next time - CMeasureSection::m_dNextResort = engine->Time() + measure_resort.GetFloat(); - // Flag to reset sort accumulator, too - sort_reset = true; - } - - // Iterate through the sections now - p = CMeasureSection::GetList(); - while ( p ) - { - // Update max - p->UpdateMax(); - - // Reset regular accum. - p->Reset(); - // Reset sort accum less often - if ( sort_reset ) - { - p->SortReset(); - } - p = p->GetNext(); - } - -} -#endif - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void DrawAllDebugOverlays( void ) -{ - // If in debug select mode print the selection entities name or classname - if (CBaseEntity::m_bInDebugSelect) - { - CBasePlayer* pPlayer = UTIL_PlayerByIndex( CBaseEntity::m_nDebugPlayer ); - - if (pPlayer) - { - // First try to trace a hull to an entity - CBaseEntity *pEntity = FindPickerEntity( pPlayer ); - - if ( pEntity ) - { - pEntity->DrawDebugTextOverlays(); - pEntity->DrawBBoxOverlay(); - pEntity->SendDebugPivotOverlay(); - } - } - } - - // -------------------------------------------------------- - // Draw debug overlay lines - // -------------------------------------------------------- - UTIL_DrawOverlayLines(); - - // ------------------------------------------------------------------------ - // If in wc_edit mode draw a box to highlight which node I'm looking at - // ------------------------------------------------------------------------ - if (engine->IsInEditMode()) - { - CBasePlayer* pPlayer = UTIL_PlayerByIndex( CBaseEntity::m_nDebugPlayer ); - if (pPlayer) - { - if (g_pAINetworkManager->GetEditOps()->m_bLinkEditMode) - { - CAI_Link* pAILink = FindPickerAILink(pPlayer); - if (pAILink) - { - // For now just using one big AI network - Vector startPos = g_pBigAINet->GetNode(pAILink->m_iSrcID)->GetPosition(g_pAINetworkManager->GetEditOps()->m_iHullDrawNum); - Vector endPos = g_pBigAINet->GetNode(pAILink->m_iDestID)->GetPosition(g_pAINetworkManager->GetEditOps()->m_iHullDrawNum); - Vector linkDir = startPos-endPos; - float linkLen = VectorNormalize( linkDir ); - - // Draw in green if link that's been turned off - if (pAILink->m_LinkInfo & bits_LINK_OFF) - { - NDebugOverlay::BoxDirection(startPos, Vector(-4,-4,-4), Vector(-linkLen,4,4), linkDir, 0,255,0,40,0); - } - else - { - NDebugOverlay::BoxDirection(startPos, Vector(-4,-4,-4), Vector(-linkLen,4,4), linkDir, 255,0,0,40,0); - } - } - } - else - { - CAI_Node* pAINode; - if (g_pAINetworkManager->GetEditOps()->m_bAirEditMode) - { - pAINode = FindPickerAINode(pPlayer,NODE_AIR); - } - else - { - pAINode = FindPickerAINode(pPlayer,NODE_GROUND); - } - - if (pAINode) - { - Vector vecPos = pAINode->GetPosition(g_pAINetworkManager->GetEditOps()->m_iHullDrawNum); - NDebugOverlay::Box( vecPos, Vector(-8,-8,-8), Vector(8,8,8), 255,0,0,40,0); - - if ( pAINode->GetHint() ) - { - CBaseEntity *pEnt = (CBaseEntity *)pAINode->GetHint(); - if ( pEnt->GetEntityName() != NULL_STRING ) - { - NDebugOverlay::Text( vecPos + Vector(0,0,6), STRING(pEnt->GetEntityName()), false, 0 ); - } - NDebugOverlay::Text( vecPos, GetHintTypeDescription( pAINode->GetHint() ), false, 0 ); - } - } - } - // ------------------------------------ - // If in air edit mode draw guide line - // ------------------------------------ - if (g_pAINetworkManager->GetEditOps()->m_bAirEditMode) - { - UTIL_DrawPositioningOverlay(g_pAINetworkManager->GetEditOps()->m_flAirEditDistance); - } - else - { - NDebugOverlay::DrawGroundCrossHairOverlay(); - } - } - } - - // For not just using one big AI Network - if ( g_pAINetworkManager ) - { - g_pAINetworkManager->GetEditOps()->DrawAINetworkOverlay(); - } - - // PERFORMANCE: only do this in developer mode - if ( g_pDeveloper->GetInt() ) - { - // iterate through all objects for debug overlays - const CEntInfo *pInfo = gEntList.FirstEntInfo(); - - for ( ;pInfo; pInfo = pInfo->m_pNext ) - { - CBaseEntity *ent = (CBaseEntity *)pInfo->m_pEntity; - // HACKHACK: to flag off these calls - if ( ent->m_debugOverlays || ent->m_pTimedOverlay ) - { - MDLCACHE_CRITICAL_SECTION(); - ent->DrawDebugGeometryOverlays(); - } - } - } - - if ( sv_massreport.GetInt() ) - { - // iterate through all objects for debug overlays - const CEntInfo *pInfo = gEntList.FirstEntInfo(); - - for ( ;pInfo; pInfo = pInfo->m_pNext ) - { - CBaseEntity *ent = (CBaseEntity *)pInfo->m_pEntity; - if (!ent->VPhysicsGetObject()) - continue; - - char tempstr[512]; - Q_snprintf(tempstr, sizeof(tempstr),"%s: Mass: %.2f kg / %.2f lb (%s)", - ent->GetModelName(), ent->VPhysicsGetObject()->GetMass(), - kg2lbs(ent->VPhysicsGetObject()->GetMass()), - GetMassEquivalent(ent->VPhysicsGetObject()->GetMass())); - ent->EntityText(0, tempstr, 0); - } - } - - // A hack to draw point_message entities w/o developer required - DrawMessageEntities(); -} - -CServerGameDLL g_ServerGameDLL; -EXPOSE_SINGLE_INTERFACE_GLOBALVAR(CServerGameDLL, IServerGameDLL, INTERFACEVERSION_SERVERGAMEDLL, g_ServerGameDLL); - -bool CServerGameDLL::DLLInit(CreateInterfaceFn engineFactory, - CreateInterfaceFn physicsFactory, CreateInterfaceFn fileSystemFactory, - CGlobalVars *pGlobals) -{ - // init each (seperated for ease of debugging) - if ( (engine = (IVEngineServer*)engineFactory(INTERFACEVERSION_VENGINESERVER, NULL)) == NULL ) - return false; - if ( (g_pVoiceServer = (IVoiceServer*)engineFactory(INTERFACEVERSION_VOICESERVER, NULL)) == NULL ) - return false; - if ( (cvar = (ICvar*)engineFactory(VENGINE_CVAR_INTERFACE_VERSION, NULL)) == NULL ) - return false; - if ( (networkstringtable = (INetworkStringTableContainer *)engineFactory(INTERFACENAME_NETWORKSTRINGTABLESERVER,NULL)) == NULL ) - return false; - if ( (staticpropmgr = (IStaticPropMgrServer *)engineFactory(INTERFACEVERSION_STATICPROPMGR_SERVER,NULL)) == NULL ) - return false; - if ( (random = (IUniformRandomStream *)engineFactory(VENGINE_SERVER_RANDOM_INTERFACE_VERSION, NULL)) == NULL ) - return false; - if ( (enginesound = (IEngineSound *)engineFactory(IENGINESOUND_SERVER_INTERFACE_VERSION, NULL)) == NULL ) - return false; - if ( (partition = (ISpatialPartition *)engineFactory(INTERFACEVERSION_SPATIALPARTITION, NULL)) == NULL ) - return false; - if ( (modelinfo = (IVModelInfo *)engineFactory(VMODELINFO_SERVER_INTERFACE_VERSION, NULL)) == NULL ) - return false; - if ( (enginetrace = (IEngineTrace *)engineFactory(INTERFACEVERSION_ENGINETRACE_SERVER,NULL)) == NULL ) - return false; - if ( (filesystem = (IFileSystem *)fileSystemFactory(FILESYSTEM_INTERFACE_VERSION,NULL)) == NULL ) - return false; - if ( (gameeventmanager = (IGameEventManager2 *)engineFactory(INTERFACEVERSION_GAMEEVENTSMANAGER2,NULL)) == NULL ) - return false; - if ( (datacache = (IDataCache*)engineFactory(DATACACHE_INTERFACE_VERSION, NULL )) == NULL ) - return false; - if ( (soundemitterbase = (ISoundEmitterSystemBase *)engineFactory(SOUNDEMITTERSYSTEM_INTERFACE_VERSION, NULL)) == NULL ) - return false; -#ifndef _XBOX - if ( (gamestatsuploader = (IUploadGameStats *)engineFactory( INTERFACEVERSION_UPLOADGAMESTATS, NULL )) == NULL ) - return false; -#endif - if ( (mdlcache = (IMDLCache*)engineFactory( MDLCACHE_INTERFACE_VERSION, NULL )) == NULL ) - return false; - if ( (serverpluginhelpers = (IServerPluginHelpers *)engineFactory(INTERFACEVERSION_ISERVERPLUGINHELPERS, NULL)) == NULL ) - return false; - if ( (scenefilecache = (ISceneFileCache *)engineFactory( SCENE_FILE_CACHE_INTERFACE_VERSION, NULL )) == NULL ) - return false; - - // If not running dedicated, grab the engine vgui interface - if ( !engine->IsDedicatedServer() ) - { -#ifdef _WIN32 - if ( ( enginevgui = ( IEngineVGui * )engineFactory(VENGINE_VGUI_VERSION, NULL)) == NULL ) - return false; - - // This interface is optional, and is only valid when running with -tools - serverenginetools = ( IServerEngineTools * )engineFactory( VSERVERENGINETOOLS_INTERFACE_VERSION, NULL ); -#endif - } - - // Yes, both the client and game .dlls will try to Connect, the soundemittersystem.dll will handle this gracefully - if ( !soundemitterbase->Connect( engineFactory ) ) - return false; - - // cache the globals - gpGlobals = pGlobals; - - g_pSharedChangeInfo = engine->GetSharedEdictChangeInfo(); - - MathLib_Init( 2.2f, 2.2f, 0.0f, 2.0f ); - - // save these in case other system inits need them - factorylist_t factories; - factories.engineFactory = engineFactory; - factories.fileSystemFactory = fileSystemFactory; - factories.physicsFactory = physicsFactory; - FactoryList_Store( factories ); - - // load used game events - gameeventmanager->LoadEventsFromFile("resource/gameevents.res"); - - // init the cvar list first in case inits want to reference them - InitializeCvars(); - - sv_cheats = (ConVar*) ConCommandBase::FindCommand( "sv_cheats" ); - if ( !sv_cheats ) - return false; - - sv_maxreplay = (ConVar*) ConCommandBase::FindCommand( "sv_maxreplay" ); - - g_pGameSaveRestoreBlockSet->AddBlockHandler( GetEntitySaveRestoreBlockHandler() ); - g_pGameSaveRestoreBlockSet->AddBlockHandler( GetPhysSaveRestoreBlockHandler() ); - g_pGameSaveRestoreBlockSet->AddBlockHandler( GetAISaveRestoreBlockHandler() ); - g_pGameSaveRestoreBlockSet->AddBlockHandler( GetTemplateSaveRestoreBlockHandler() ); - g_pGameSaveRestoreBlockSet->AddBlockHandler( GetDefaultResponseSystemSaveRestoreBlockHandler() ); -#if !defined( _CONSOLE ) - g_pGameSaveRestoreBlockSet->AddBlockHandler( GetCommentarySaveRestoreBlockHandler() ); -#endif - g_pGameSaveRestoreBlockSet->AddBlockHandler( GetEventQueueSaveRestoreBlockHandler() ); - - // The string system must init first + shutdown last - IGameSystem::Add( GameStringSystem() ); - - // Physics must occur before the sound envelope manager - IGameSystem::Add( PhysicsGameSystem() ); - - // Add game log system - IGameSystem::Add( GameLogSystem() ); -#ifndef _XBOX - // Add HLTV director - IGameSystem::Add( HLTVDirectorSystem() ); -#endif - // Add sound emitter - IGameSystem::Add( SoundEmitterSystem() ); - -#ifdef _WIN32 - // Startup vgui - if ( enginevgui ) - { - if(!VGui_Startup( engineFactory )) - return false; - } -#endif - - // load Mod specific game events ( MUST be before InitAllSystems() so it can pickup the mod specific events) - gameeventmanager->LoadEventsFromFile("resource/ModEvents.res"); - -#ifdef CSTRIKE_DLL // BOTPORT: TODO: move these ifdefs out - InstallBotControl(); -#endif - - if ( !IGameSystem::InitAllSystems() ) - return false; - - // Due to dependencies, these are not autogamesystems - if ( !SceneCacheInit() ) - { - return false; - } - - // Due to dependencies, these are not autogamesystems - if ( !ModelSoundsCacheInit() ) - { - return false; - } - - // try to get debug overlay, may be NULL if on HLDS - debugoverlay = (IVDebugOverlay *)engineFactory( VDEBUG_OVERLAY_INTERFACE_VERSION, NULL ); - -#ifndef _XBOX - // create the Navigation Mesh interface - TheNavMesh = new CNavMesh; - - // init the gamestatsupload connection - gamestatsuploader->InitConnection(); -#endif - -#if !defined( _RETAIL ) -#if defined( _XBOX ) - XBX_rTimeStampLog( Plat_FloatTime(), "DLLInit finished" ); -#endif -#endif - return true; -} - -void CServerGameDLL::PostInit() -{ -#ifdef _WIN32 - if ( enginevgui ) - { - if ( VGui_PostInit() ) - { - // all good - } - } -#endif -} - -void CServerGameDLL::DLLShutdown( void ) -{ - - // Due to dependencies, these are not autogamesystems - ModelSoundsCacheShutdown(); - SceneCacheShutdown(); - -#if !defined( _CONSOLE ) - g_pGameSaveRestoreBlockSet->RemoveBlockHandler( GetCommentarySaveRestoreBlockHandler() ); -#endif - g_pGameSaveRestoreBlockSet->RemoveBlockHandler( GetEventQueueSaveRestoreBlockHandler() ); - g_pGameSaveRestoreBlockSet->RemoveBlockHandler( GetDefaultResponseSystemSaveRestoreBlockHandler() ); - g_pGameSaveRestoreBlockSet->RemoveBlockHandler( GetTemplateSaveRestoreBlockHandler() ); - g_pGameSaveRestoreBlockSet->RemoveBlockHandler( GetAISaveRestoreBlockHandler() ); - g_pGameSaveRestoreBlockSet->RemoveBlockHandler( GetPhysSaveRestoreBlockHandler() ); - g_pGameSaveRestoreBlockSet->RemoveBlockHandler( GetEntitySaveRestoreBlockHandler() ); - - char *pFilename = g_TextStatsMgr.GetStatsFilename(); - if ( !pFilename || !pFilename[0] ) - { - g_TextStatsMgr.SetStatsFilename( "stats.txt" ); - } - g_TextStatsMgr.WriteFile( filesystem ); - - IGameSystem::ShutdownAllSystems(); - -#ifdef _WIN32 - if ( enginevgui ) - { - VGui_Shutdown(); - } -#endif - -#ifdef CSTRIKE_DLL // BOTPORT: TODO: move these ifdefs out - RemoveBotControl(); -#endif - -#ifndef _XBOX - // destroy the Navigation Mesh interface - if (TheNavMesh) - { - delete TheNavMesh; - TheNavMesh = NULL; - } -#endif -} - -//----------------------------------------------------------------------------- -// Purpose: See shareddefs.h for redefining this. Don't even think about it, though, for HL2. Or you will pay. ywb 9/22/03 -// Output : float -//----------------------------------------------------------------------------- - -float CServerGameDLL::GetTickInterval( void ) const -{ - float tickinterval = DEFAULT_TICK_INTERVAL; - -#if defined( CSTRIKE_DLL ) - // in CS reduce tickrate/sec by defualt - tickinterval *= 2; -#endif - - // override if tick rate specified in command line - if ( CommandLine()->CheckParm( "-tickrate" ) ) - { - float tickrate = CommandLine()->ParmValue( "-tickrate", 0 ); - if ( tickrate > 10 ) - tickinterval = 1.0f / tickrate; - } - - return tickinterval; -} - -// This is called when a new game is started. (restart, map) -bool CServerGameDLL::GameInit( void ) -{ - ResetGlobalState(); - engine->ServerCommand( "exec game.cfg\n" ); - engine->ServerExecute( ); - // clear out any old game's temporary save data -// engine->ClearSaveDir(); - return true; -} - -// This is called when a game ends (server disconnect, death, restart, load) -// NOT on level transitions within a game -void CServerGameDLL::GameShutdown( void ) -{ - ResetGlobalState(); -} - -static bool g_OneWayTransition = false; -void Game_SetOneWayTransition( void ) -{ - g_OneWayTransition = true; -} - -static CUtlVector g_RestoredEntities; -// just for debugging, assert that this is the only time this function is called -static bool g_InRestore = false; - -void AddRestoredEntity( CBaseEntity *pEntity ) -{ - Assert(g_InRestore); - if ( !pEntity ) - return; - - g_RestoredEntities.AddToTail( EHANDLE(pEntity) ); -} - -void EndRestoreEntities() -{ - if ( !g_InRestore ) - return; - - // Call all entities' OnRestore handlers - for ( int i = g_RestoredEntities.Count()-1; i >=0; --i ) - { - CBaseEntity *pEntity = g_RestoredEntities[i].Get(); - if ( pEntity && !pEntity->IsDormant() ) - { - MDLCACHE_CRITICAL_SECTION(); - pEntity->OnRestore(); - } - } - - g_RestoredEntities.Purge(); - - IGameSystem::OnRestoreAllSystems(); - - g_InRestore = false; - gEntList.CleanupDeleteList(); - - // HACKHACK: UNDONE: We need to redesign the main loop with respect to save/load/server activate - g_ServerGameDLL.ServerActivate( NULL, 0, 0 ); - CBaseEntity::SetAllowPrecache( false ); -} - -void BeginRestoreEntities() -{ - if ( g_InRestore ) - { - DevMsg( "BeginRestoreEntities without previous EndRestoreEntities.\n" ); - gEntList.CleanupDeleteList(); - } - g_RestoredEntities.Purge(); - g_InRestore = true; - CBaseEntity::SetAllowPrecache( true ); -} - -//----------------------------------------------------------------------------- -// Purpose: This prevents sv.tickcount/gpGlobals->tickcount from advancing during restore which -// would cause a lot of the NPCs to fast forward their think times to the same -// tick due to some ticks being elapsed during restore where there was no simulation going on -//----------------------------------------------------------------------------- -bool CServerGameDLL::IsRestoring() -{ - return g_InRestore; -} - -// Called any time a new level is started (after GameInit() also on level transitions within a game) -bool CServerGameDLL::LevelInit( const char *pMapName, char const *pMapEntities, char const *pOldLevel, char const *pLandmarkName, bool loadGame, bool background ) -{ - VPROF("CServerGameDLL::LevelInit"); - ResetWindspeed(); - UpdateChapterRestrictions( pMapName ); - - // IGameSystem::LevelInitPreEntityAllSystems() is called when the world is precached - // That happens either in LoadGameState() or in MapEntity_ParseAllEntities() - if ( loadGame ) - { - BeginRestoreEntities(); - if ( !engine->LoadGameState( pMapName, 1 ) ) - { - MapEntity_ParseAllEntities( pMapEntities ); - } - - if ( pOldLevel ) - { - gpGlobals->eLoadType = MapLoad_Transition; - engine->LoadAdjacentEnts( pOldLevel, pLandmarkName ); - } - else - { - gpGlobals->eLoadType = MapLoad_LoadGame; - } - - if ( g_OneWayTransition ) - { - engine->ClearSaveDirAfterClientLoad(); - } - - if ( pOldLevel && sv_autosave.GetBool() == true ) - { - // This is a single-player style level transition. - // Queue up an autosave one second into the level - CBaseEntity *pAutosave = CBaseEntity::Create( "logic_autosave", vec3_origin, vec3_angle, NULL ); - if ( pAutosave ) - { - g_EventQueue.AddEvent( pAutosave, "Save", 1.0, NULL, NULL ); - g_EventQueue.AddEvent( pAutosave, "Kill", 1.1, NULL, NULL ); - } - } - } - else - { - if ( background ) - { - gpGlobals->eLoadType = MapLoad_Background; - } - else - { - gpGlobals->eLoadType = MapLoad_NewGame; - } - - LevelInit_ParseAllEntities( pMapEntities ); - } - - // Check low violence settings for this map - g_RagdollLVManager.SetLowViolence( pMapName ); - - // Now that all of the active entities have been loaded in, precache any entities who need point_template parameters - // to be parsed (the above code has loaded all point_template entities) - PrecachePointTemplates(); - - // load MOTD from file into stringtable - LoadMessageOfTheDay(); - - // Sometimes an ent will Remove() itself during its precache, so RemoveImmediate won't happen. - // This makes sure those ents get cleaned up. - gEntList.CleanupDeleteList(); - - g_AIFriendliesTalkSemaphore.Release(); - g_AIFoesTalkSemaphore.Release(); - g_OneWayTransition = false; - return true; -} - -//----------------------------------------------------------------------------- -// Purpose: called after every level change and load game, iterates through all the -// active entities and gives them a chance to fix up their state -//----------------------------------------------------------------------------- -#ifdef DEBUG -bool g_bReceivedChainedActivate; -bool g_bCheckForChainedActivate; -#define BeginCheckChainedActivate() if (0) ; else { g_bCheckForChainedActivate = true; g_bReceivedChainedActivate = false; } -#define EndCheckChainedActivate( bCheck ) \ - if (0) ; else \ - { \ - if ( bCheck ) \ - { \ - char msg[ 1024 ]; \ - Q_snprintf( msg, sizeof( msg ), "Entity (%i/%s/%s) failed to call base class Activate()\n", pClass->entindex(), pClass->GetClassname(), STRING( pClass->GetEntityName() ) ); \ - AssertMsg( g_bReceivedChainedActivate == true, msg ); \ - } \ - g_bCheckForChainedActivate = false; \ - } -#else -#define BeginCheckChainedActivate() ((void)0) -#define EndCheckChainedActivate( bCheck ) ((void)0) -#endif - -void CServerGameDLL::ServerActivate( edict_t *pEdictList, int edictCount, int clientMax ) -{ - // HACKHACK: UNDONE: We need to redesign the main loop with respect to save/load/server activate - if ( g_InRestore ) - return; - - if ( gEntList.ResetDeleteList() != 0 ) - { - Msg( "ERROR: Entity delete queue not empty on level start!\n" ); - } - - for ( CBaseEntity *pClass = gEntList.FirstEnt(); pClass != NULL; pClass = gEntList.NextEnt(pClass) ) - { - if ( pClass && !pClass->IsDormant() ) - { - MDLCACHE_CRITICAL_SECTION(); - - BeginCheckChainedActivate(); - pClass->Activate(); - - // We don't care if it finished activating if it decided to remove itself. - EndCheckChainedActivate( !( pClass->GetEFlags() & EFL_KILLME ) ); - } - } - - IGameSystem::LevelInitPostEntityAllSystems(); - // No more precaching after PostEntityAllSystems!!! - CBaseEntity::SetAllowPrecache( false ); - - // only display the think limit when the game is run with "developer" mode set - if ( !g_pDeveloper->GetInt() ) - { - think_limit.SetValue( 0 ); - } - -#ifndef _XBOX - // load the Navigation Mesh for this map - TheNavMesh->Load(); -#endif - -#ifdef CSTRIKE_DLL // BOTPORT: TODO: move these ifdefs out - TheBots->ServerActivate(); -#endif -} - -//----------------------------------------------------------------------------- -// Purpose: Called at the start of every game frame -//----------------------------------------------------------------------------- -ConVar trace_report( "trace_report", "0" ); - -void CServerGameDLL::GameFrame( bool simulating ) -{ - VPROF( "CServerGameDLL::GameFrame" ); - - // Don't run frames until fully restored - if ( g_InRestore ) - return; - - static bool skipframe = false; - - // If server is skipping frames, don't run simulation this time through - if ( skipframe ) - { - UpdateAllClientData(); - skipframe = false; - return; - } - - float oldframetime = gpGlobals->frametime; - if ( CBaseEntity::IsSimulatingOnAlternateTicks() ) - { - skipframe = true; - // If we're skipping frames, then the frametime is 2x the normal tick - gpGlobals->frametime *= 2.0f; - } - -#ifdef _DEBUG - // For profiling.. let them enable/disable the networkvar manual mode stuff. - g_bUseNetworkVars = s_UseNetworkVars.GetBool(); -#endif - - extern void GameStartFrame( void ); - extern void ServiceEventQueue( void ); - extern void Physics_RunThinkFunctions( bool simulating ); - - // Delete anything that was marked for deletion - // outside of server frameloop (e.g., in response to concommand) - gEntList.CleanupDeleteList(); - - IGameSystem::FrameUpdatePreEntityThinkAllSystems(); - GameStartFrame(); - -#ifndef _XBOX - TheNavMesh->Update(); - - gamestatsuploader->UpdateConnection(); -#endif - - Physics_RunThinkFunctions( simulating ); - - IGameSystem::FrameUpdatePostEntityThinkAllSystems(); - - // UNDONE: Make these systems IGameSystems and move these calls into FrameUpdatePostEntityThink() - // service event queue, firing off any actions whos time has come - ServiceEventQueue(); - - // free all ents marked in think functions - gEntList.CleanupDeleteList(); - - // FIXME: Should this only occur on the final tick? - UpdateAllClientData(); - - if ( g_pGameRules ) - { - g_pGameRules->EndGameFrame(); - } - - if ( trace_report.GetBool() ) - { - int total = 0, totals[3]; - for ( int i = 0; i < 3; i++ ) - { - totals[i] = enginetrace->GetStatByIndex( i, true ); - if ( totals[i] > 0 ) - { - total += totals[i]; - } - } - - if ( total ) - { - Msg("Trace: %d, contents %d, enumerate %d\n", totals[0], totals[1], totals[2] ); - } - } - - // Any entities that detect network state changes on a timer do it here. - g_NetworkPropertyEventMgr.FireEvents(); - - gpGlobals->frametime = oldframetime; -} - -//----------------------------------------------------------------------------- -// Purpose: Called every frame even if not ticking -// Input : simulating - -//----------------------------------------------------------------------------- -void CServerGameDLL::PreClientUpdate( bool simulating ) -{ - if ( !simulating ) - return; - - /* - if (game_speeds.GetInt()) - { - DrawMeasuredSections(); - } - */ - -//#ifdef _DEBUG - allow this in release for now - DrawAllDebugOverlays(); -//#endif - - IGameSystem::PreClientUpdateAllSystems(); - - if ( sv_showhitboxes.GetInt() == -1 ) - return; - - if ( sv_showhitboxes.GetInt() == 0 ) - { - // assume it's text - CBaseEntity *pEntity = NULL; - - while (1) - { - pEntity = gEntList.FindEntityByName( pEntity, sv_showhitboxes.GetString() ); - if ( !pEntity ) - break; - - CBaseAnimating *anim = dynamic_cast< CBaseAnimating * >( pEntity ); - - if (anim) - { - anim->DrawServerHitboxes(); - } - } - return; - } - - CBaseAnimating *anim = dynamic_cast< CBaseAnimating * >( CBaseEntity::Instance( engine->PEntityOfEntIndex( sv_showhitboxes.GetInt() ) ) ); - if ( !anim ) - return; - - anim->DrawServerHitboxes(); -} - -void CServerGameDLL::Think( bool finalTick ) -{ - if ( m_fAutoSaveDangerousTime != 0.0f && m_fAutoSaveDangerousTime < gpGlobals->curtime ) - { - // The safety timer for a dangerous auto save has expired - CBasePlayer *pPlayer = UTIL_PlayerByIndex( 1 ); - - if ( pPlayer->GetDeathTime() == 0.0f || pPlayer->GetDeathTime() > gpGlobals->curtime ) - { - // The player isn't dead, so make the dangerous auto save safe - engine->ServerCommand( "autosavedangerousissafe\n" ); - } - - m_fAutoSaveDangerousTime = 0.0f; - } -} - - -// Called when a level is shutdown (including changing levels) -void CServerGameDLL::LevelShutdown( void ) -{ - IGameSystem::LevelShutdownPreEntityAllSystems(); - - // YWB: - // This entity pointer is going away now and is corrupting memory on level transitions/restarts - CSoundEnt::ShutdownSoundEnt(); - - gEntList.Clear(); - - IGameSystem::LevelShutdownPostEntityAllSystems(); - - // In case we quit out during initial load - CBaseEntity::SetAllowPrecache( false ); -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : -// Output : ServerClass* -//----------------------------------------------------------------------------- -ServerClass* CServerGameDLL::GetAllServerClasses() -{ - return g_pServerClassHead; -} - - -const char *CServerGameDLL::GetGameDescription( void ) -{ - return ::GetGameDescription(); -} - -void CServerGameDLL::CreateNetworkStringTables( void ) -{ - // Create any shared string tables here (and only here!) - // E.g.: xxx = networkstringtable->CreateStringTable( "SceneStrings", 512 ); - g_pStringTableEffectDispatch = networkstringtable->CreateStringTable( "EffectDispatch", MAX_EFFECT_DISPATCH_STRINGS ); - g_pStringTableVguiScreen = networkstringtable->CreateStringTable( "VguiScreen", MAX_VGUI_SCREEN_STRINGS ); - g_pStringTableMaterials = networkstringtable->CreateStringTable( "Materials", MAX_MATERIAL_STRINGS ); - g_pStringTableInfoPanel = networkstringtable->CreateStringTable( "InfoPanel", MAX_INFOPANEL_STRINGS ); - g_pStringTableClientSideChoreoScenes = networkstringtable->CreateStringTable( "Scenes", MAX_CHOREO_SCENES_STRINGS ); - - Assert( g_pStringTableEffectDispatch && - g_pStringTableVguiScreen && - g_pStringTableMaterials && - g_pStringTableInfoPanel && - g_pStringTableClientSideChoreoScenes ); - - // Need this so we have the error material always handy - PrecacheMaterial( "debug/debugempty" ); - Assert( GetMaterialIndex( "debug/debugempty" ) == 0 ); - - CreateNetworkStringTables_GameRules(); - - // Set up save/load utilities for string tables - g_VguiScreenStringOps.Init( g_pStringTableVguiScreen ); -} - -CSaveRestoreData *CServerGameDLL::SaveInit( int size ) -{ - return ::SaveInit(size); -} - -//----------------------------------------------------------------------------- -// Purpose: Saves data from a struct into a saverestore object, to be saved to disk -// Input : *pSaveData - the saverestore object -// char *pname - the name of the data to write -// *pBaseData - the struct into which the data is to be read -// *pFields - pointer to an array of data field descriptions -// fieldCount - the size of the array (number of field descriptions) -//----------------------------------------------------------------------------- -void CServerGameDLL::SaveWriteFields( CSaveRestoreData *pSaveData, const char *pname, void *pBaseData, datamap_t *pMap, typedescription_t *pFields, int fieldCount ) -{ - CSave saveHelper( pSaveData ); - saveHelper.WriteFields( pname, pBaseData, pMap, pFields, fieldCount ); -} - - -//----------------------------------------------------------------------------- -// Purpose: Reads data from a save/restore block into a structure -// Input : *pSaveData - the saverestore object -// char *pname - the name of the data to extract from -// *pBaseData - the struct into which the data is to be restored -// *pFields - pointer to an array of data field descriptions -// fieldCount - the size of the array (number of field descriptions) -//----------------------------------------------------------------------------- - -//----------------------------------------------------------------------------- - -void CServerGameDLL::SaveReadFields( CSaveRestoreData *pSaveData, const char *pname, void *pBaseData, datamap_t *pMap, typedescription_t *pFields, int fieldCount ) -{ - CRestore restoreHelper( pSaveData ); - restoreHelper.ReadFields( pname, pBaseData, pMap, pFields, fieldCount ); -} - -//----------------------------------------------------------------------------- - -void CServerGameDLL::SaveGlobalState( CSaveRestoreData *s ) -{ - ::SaveGlobalState(s); -} - -void CServerGameDLL::RestoreGlobalState(CSaveRestoreData *s) -{ - ::RestoreGlobalState(s); -} - -void CServerGameDLL::Save( CSaveRestoreData *s ) -{ - CSave saveHelper( s ); - g_pGameSaveRestoreBlockSet->Save( &saveHelper ); -} - -void CServerGameDLL::Restore( CSaveRestoreData *s, bool b) -{ - CRestore restore(s); - g_pGameSaveRestoreBlockSet->Restore( &restore, b ); - g_pGameSaveRestoreBlockSet->PostRestore(); -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : msg_type - -// *name - -// size - -// Output : Returns true on success, false on failure. -//----------------------------------------------------------------------------- - -bool CServerGameDLL::GetUserMessageInfo( int msg_type, char *name, int maxnamelength, int& size ) -{ - if ( !usermessages->IsValidIndex( msg_type ) ) - return false; - - Q_strncpy( name, usermessages->GetUserMessageName( msg_type ), maxnamelength ); - size = usermessages->GetUserMessageSize( msg_type ); - return true; -} - -CStandardSendProxies* CServerGameDLL::GetStandardSendProxies() -{ - return &g_StandardSendProxies; -} - -int CServerGameDLL::CreateEntityTransitionList( CSaveRestoreData *s, int a) -{ - CRestore restoreHelper( s ); - // save off file base - int base = restoreHelper.GetReadPos(); - - int movedCount = ::CreateEntityTransitionList(s, a); - if ( movedCount ) - { - g_pGameSaveRestoreBlockSet->CallBlockHandlerRestore( GetPhysSaveRestoreBlockHandler(), base, &restoreHelper, false ); - g_pGameSaveRestoreBlockSet->CallBlockHandlerRestore( GetAISaveRestoreBlockHandler(), base, &restoreHelper, false ); - } - - GetPhysSaveRestoreBlockHandler()->PostRestore(); - GetAISaveRestoreBlockHandler()->PostRestore(); - - return movedCount; -} - -void CServerGameDLL::PreSave( CSaveRestoreData *s ) -{ - g_pGameSaveRestoreBlockSet->PreSave( s ); -} - -#include "client_textmessage.h" - -// This little hack lets me marry BSP names to messages in titles.txt -typedef struct -{ - char *pBSPName; - char *pTitleName; -} TITLECOMMENT; - -// this list gets searched for the first partial match, so some are out of order -static TITLECOMMENT gTitleComments[] = -{ -#ifdef HL1_DLL - { "t0a0", "#T0A0TITLE" }, - { "c0a0", "#HL1_Chapter1_Title" }, - { "c1a0", "#HL1_Chapter2_Title" }, - { "c1a1", "#HL1_Chapter3_Title" }, - { "c1a2", "#HL1_Chapter4_Title" }, - { "c1a3", "#HL1_Chapter5_Title" }, - { "c1a4", "#HL1_Chapter6_Title" }, - { "c2a1", "#HL1_Chapter7_Title" }, - { "c2a2", "#HL1_Chapter8_Title" }, - { "c2a3", "#HL1_Chapter9_Title" }, - { "c2a4d", "#HL1_Chapter11_Title" }, // These must appear before "C2A4" so all other map names starting with C2A4 get that title - { "c2a4e", "#HL1_Chapter11_Title" }, - { "c2a4f", "#HL1_Chapter11_Title" }, - { "c2a4g", "#HL1_Chapter11_Title" }, - { "c2a4", "#HL1_Chapter10_Title" }, - { "c2a5", "#HL1_Chapter12_Title" }, - { "c3a1", "#HL1_Chapter13_Title" }, - { "c3a2", "#HL1_Chapter14_Title" }, - { "c4a1a", "#HL1_Chapter17_Title" }, // Order is important, see above - { "c4a1b", "#HL1_Chapter17_Title" }, - { "c4a1c", "#HL1_Chapter17_Title" }, - { "c4a1d", "#HL1_Chapter17_Title" }, - { "c4a1e", "#HL1_Chapter17_Title" }, - { "c4a1", "#HL1_Chapter15_Title" }, - { "c4a2", "#HL1_Chapter16_Title" }, - { "c4a3", "#HL1_Chapter18_Title" }, - { "c5a1", "#HL1_Chapter19_Title" }, -#else - { "intro", "#HL2_Chapter1_Title" }, - - { "d1_trainstation_05", "#HL2_Chapter2_Title" }, - { "d1_trainstation_06", "#HL2_Chapter2_Title" }, - - { "d1_trainstation_", "#HL2_Chapter1_Title" }, - - { "d1_canals_06a", "#HL2_Chapter4_Title" }, - { "d1_canals_06b", "#HL2_Chapter4_Title" }, - { "d1_canals_07a", "#HL2_Chapter4_Title" }, - { "d1_canals_07b", "#HL2_Chapter4_Title" }, - { "d1_canals_08", "#HL2_Chapter4_Title" }, - { "d1_canals_09", "#HL2_Chapter4_Title" }, - { "d1_canals_1", "#HL2_Chapter4_Title" }, - - { "d1_canals_0", "#HL2_Chapter3_Title" }, - - { "d1_eli_", "#HL2_Chapter5_Title" }, - - { "d1_town_", "#HL2_Chapter6_Title" }, - - { "d2_coast_09a", "#HL2_Chapter8_Title" }, - { "d2_coast_09b", "#HL2_Chapter8_Title" }, - { "d2_coast_1", "#HL2_Chapter8_Title" }, - { "d2_prison_01", "#HL2_Chapter8_Title" }, - - { "d2_coast_", "#HL2_Chapter7_Title" }, - - { "d2_prison_06a", "#HL2_Chapter10_Title" }, - { "d2_prison_06b", "#HL2_Chapter10_Title" }, - { "d2_prison_07a", "#HL2_Chapter10_Title" }, - { "d2_prison_07b", "#HL2_Chapter10_Title" }, - { "d2_prison_08", "#HL2_Chapter10_Title" }, - { "d3_c17_01", "#HL2_Chapter10_Title" }, - - { "d2_prison_", "#HL2_Chapter9_Title" }, - - { "d3_c17_09", "#HL2_Chapter12_Title" }, - { "d3_c17_1", "#HL2_Chapter12_Title" }, - - { "d3_c17_", "#HL2_Chapter11_Title" }, - - { "d3_citadel_", "#HL2_Chapter13_Title" }, - - { "d3_breen_", "#HL2_Chapter14_Title" }, - { "credits", "#HL2_Chapter15_Title" }, - - { "ep1_citadel_00", "#episodic_Chapter1_Title" }, - { "ep1_citadel_01", "#episodic_Chapter1_Title" }, - { "ep1_citadel_02", "#episodic_Chapter1_Title" }, - { "ep1_citadel_02b", "#episodic_Chapter1_Title" }, - { "ep1_citadel_03", "#episodic_Chapter2_Title" }, - { "ep1_citadel_04", "#episodic_Chapter2_Title" }, - { "ep1_c17_00", "#episodic_Chapter3_Title" }, - { "ep1_c17_00a", "#episodic_Chapter3_Title" }, - { "ep1_c17_01", "#episodic_Chapter4_Title" }, - { "ep1_c17_02", "#episodic_Chapter4_Title" }, - { "ep1_c17_02b", "#episodic_Chapter4_Title" }, - { "ep1_c17_05", "#episodic_Chapter5_Title" }, - { "ep1_c17_06", "#episodic_Chapter5_Title" }, -#endif -}; - -#ifdef _XBOX -void CServerGameDLL::GetTitleName( const char *pMapName, char* pTitleBuff, int titleBuffSize ) -{ - // Try to find a matching title comment for this mapname - for ( int i = 0; i < ARRAYSIZE(gTitleComments); i++ ) - { - if ( !Q_strnicmp( pMapName, gTitleComments[i].pBSPName, strlen(gTitleComments[i].pBSPName) ) ) - { - Q_strncpy( pTitleBuff, gTitleComments[i].pTitleName, titleBuffSize ); - return; - } - } - Q_strncpy( pTitleBuff, pMapName, titleBuffSize ); -} -#endif - -void CServerGameDLL::GetSaveCommentEx( char *text, int maxlength, float flMinutes, float flSeconds ) -{ - char comment[64]; - const char *pName; - int i; - - char const *mapname = STRING( gpGlobals->mapname ); - - pName = NULL; - - // Try to find a matching title comment for this mapname - for ( i = 0; i < ARRAYSIZE(gTitleComments) && !pName; i++ ) - { - if ( !Q_strnicmp( mapname, gTitleComments[i].pBSPName, strlen(gTitleComments[i].pBSPName) ) ) - { - // found one - int j; - - // Got a message, post-process it to be save name friendly - Q_strncpy( comment, gTitleComments[i].pTitleName, sizeof( comment ) ); - pName = comment; - j = 0; - // Strip out CRs - while ( j < 64 && comment[j] ) - { - if ( comment[j] == '\n' || comment[j] == '\r' ) - comment[j] = 0; - else - j++; - } - break; - } - } - - // If we didn't get one, use the designer's map name, or the BSP name itself - if ( !pName ) - { - pName = mapname; - } - - if ( flMinutes == 0 && flSeconds == 0 ) - { - Q_snprintf( text, maxlength, "%-64.64s", pName ); - } - else - { - int totalSeconds = gpGlobals->curtime + flSeconds; - int minutes = (int)( totalSeconds / 60.0f ) + flMinutes; - int seconds = (int)fmod( totalSeconds, 60.0f ); - - // Wow, this guy/gal must suck...! - if ( minutes >= 1000 ) - { - minutes = 999; - seconds = 59; - } - - // add the elapsed time at the end of the comment, for the ui to parse out - Q_snprintf( text, maxlength, "%-64.64s %03d:%02d", pName, minutes, seconds ); - } -} - -void CServerGameDLL::WriteSaveHeaders( CSaveRestoreData *s ) -{ - CSave saveHelper( s ); - g_pGameSaveRestoreBlockSet->WriteSaveHeaders( &saveHelper ); - g_pGameSaveRestoreBlockSet->PostSave(); -} - -void CServerGameDLL::ReadRestoreHeaders( CSaveRestoreData *s ) -{ - CRestore restoreHelper( s ); - g_pGameSaveRestoreBlockSet->PreRestore(); - g_pGameSaveRestoreBlockSet->ReadRestoreHeaders( &restoreHelper ); -} - - -//----------------------------------------------------------------------------- -// Purpose: Called during a transition, to build a map adjacency list -//----------------------------------------------------------------------------- -void CServerGameDLL::BuildAdjacentMapList( void ) -{ - // retrieve the pointer to the save data - CSaveRestoreData *pSaveData = gpGlobals->pSaveData; - - if ( pSaveData ) - pSaveData->levelInfo.connectionCount = BuildChangeList( pSaveData->levelInfo.levelList, MAX_LEVEL_CONNECTIONS ); -} - -//----------------------------------------------------------------------------- -// Purpose: Sanity-check to verify that a path is a relative path inside the game dir -// Taken From: engine/cmd.cpp -//----------------------------------------------------------------------------- -static bool IsValidPath( const char *pszFilename ) -{ - if ( !pszFilename ) - { - return false; - } - - if ( Q_strlen( pszFilename ) <= 0 || - Q_IsAbsolutePath( pszFilename ) || // to protect absolute paths - Q_strstr( pszFilename, ".." ) ) // to protect relative paths - { - return false; - } - - return true; -} - -static void ValidateMOTDFilename( ConVar *var, const char *oldValue ) -{ - if ( !IsValidPath( var->GetString() ) ) - { - var->Revert(); - } -} - -static ConVar motdfile( "motdfile", "motd.txt", 0, "The MOTD file to load.", ValidateMOTDFilename ); -void CServerGameDLL::LoadMessageOfTheDay() -{ -#ifndef _XBOX - char data[2048]; - - int length = filesystem->Size( motdfile.GetString(), "GAME" ); - - if ( length <= 0 || length >= (sizeof(data)-1) ) - { - DevMsg("Invalid file size for %s\n", motdfile.GetString() ); - return; - } - - FileHandle_t hFile = filesystem->Open( motdfile.GetString(), "rb", "GAME" ); - - if ( hFile == FILESYSTEM_INVALID_HANDLE ) - return; - - filesystem->Read( data, length, hFile ); - filesystem->Close( hFile ); - - data[length] = 0; - - g_pStringTableInfoPanel->AddString( "motd", length+1, data ); -#endif -} - -// keeps track of which chapters the user has unlocked -ConVar sv_unlockedchapters( "sv_unlockedchapters", "1", FCVAR_ARCHIVE | FCVAR_ARCHIVE_XBOX ); - -//----------------------------------------------------------------------------- -// Purpose: Updates which chapters are unlocked -//----------------------------------------------------------------------------- -void UpdateChapterRestrictions( const char *mapname ) -{ - // look at the chapter for this map - char chapterTitle[64]; - chapterTitle[0] = 0; - for ( int i = 0; i < ARRAYSIZE(gTitleComments); i++ ) - { - if ( !Q_strnicmp( mapname, gTitleComments[i].pBSPName, strlen(gTitleComments[i].pBSPName) ) ) - { - // found - Q_strncpy( chapterTitle, gTitleComments[i].pTitleName, sizeof( chapterTitle ) ); - int j = 0; - while ( j < 64 && chapterTitle[j] ) - { - if ( chapterTitle[j] == '\n' || chapterTitle[j] == '\r' ) - chapterTitle[j] = 0; - else - j++; - } - - break; - } - } - - if ( !chapterTitle[0] ) - return; - - // make sure the specified chapter title is unlocked - strlwr( chapterTitle ); - - const char *pGameDir = CommandLine()->ParmValue( "-game", "hl2" ); - - char chapterNumberPrefix[64]; - Q_snprintf(chapterNumberPrefix, sizeof(chapterNumberPrefix), "#%s_chapter", pGameDir); - - const char *newChapterNumber = strstr( chapterTitle, chapterNumberPrefix ); - if ( newChapterNumber ) - { - // cut off the front - newChapterNumber += strlen( chapterNumberPrefix ); - char newChapter[32]; - Q_strncpy( newChapter, newChapterNumber, sizeof(newChapter) ); - - // cut off the end - char *end = strstr( newChapter, "_title" ); - if ( end ) - { - *end = 0; - } - - // ok we have the string, see if it's newer - const char *unlockedChapter = sv_unlockedchapters.GetString(); - if ( atoi(unlockedChapter) < atoi(newChapter) - || (atoi(unlockedChapter) == atoi(newChapter) && stricmp(unlockedChapter, newChapter) < 0) ) - { - // ok we're at a higher chapter, unlock - sv_unlockedchapters.SetValue( newChapter ); - } - } -} - -//----------------------------------------------------------------------------- -// Precaches a vgui screen overlay material -//----------------------------------------------------------------------------- -void PrecacheMaterial( const char *pMaterialName ) -{ - Assert( pMaterialName && pMaterialName[0] ); - g_pStringTableMaterials->AddString( pMaterialName ); -} - - -//----------------------------------------------------------------------------- -// Converts a previously precached material into an index -//----------------------------------------------------------------------------- -int GetMaterialIndex( const char *pMaterialName ) -{ - if (pMaterialName) - { - int nIndex = g_pStringTableMaterials->FindStringIndex( pMaterialName ); - - if (nIndex != INVALID_STRING_INDEX ) - { - return nIndex; - } - else - { - DevMsg("Warning! GetMaterialIndex: couldn't find material %s\n ", pMaterialName ); - return 0; - } - } - - // This is the invalid string index - return 0; -} - -//----------------------------------------------------------------------------- -// Converts a previously precached material index into a string -//----------------------------------------------------------------------------- -const char *GetMaterialNameFromIndex( int nMaterialIndex ) -{ - return g_pStringTableMaterials->GetString( nMaterialIndex ); -} - - -class CServerGameEnts : public IServerGameEnts -{ -public: - virtual void SetDebugEdictBase(edict_t *base); - virtual void MarkEntitiesAsTouching( edict_t *e1, edict_t *e2 ); - virtual void FreeContainingEntity( edict_t * ); - virtual edict_t* BaseEntityToEdict( CBaseEntity *pEnt ); - virtual CBaseEntity* EdictToBaseEntity( edict_t *pEdict ); - virtual void CheckTransmit( CCheckTransmitInfo *pInfo, const unsigned short *pEdictIndices, int nEdicts ); -}; -EXPOSE_SINGLE_INTERFACE(CServerGameEnts, IServerGameEnts, INTERFACEVERSION_SERVERGAMEENTS); - -void CServerGameEnts::SetDebugEdictBase(edict_t *base) -{ - g_pDebugEdictBase = base; -} - -//----------------------------------------------------------------------------- -// Purpose: Marks entities as touching -// Input : *e1 - -// *e2 - -//----------------------------------------------------------------------------- -void CServerGameEnts::MarkEntitiesAsTouching( edict_t *e1, edict_t *e2 ) -{ - CBaseEntity *entity = GetContainingEntity( e1 ); - CBaseEntity *entityTouched = GetContainingEntity( e2 ); - if ( entity && entityTouched ) - { - // HACKHACK: UNDONE: Pass in the trace here??!?!? - trace_t tr; - UTIL_ClearTrace( tr ); - tr.endpos = (entity->GetAbsOrigin() + entityTouched->GetAbsOrigin()) * 0.5; - entity->PhysicsMarkEntitiesAsTouching( entityTouched, tr ); - } -} - -void CServerGameEnts::FreeContainingEntity( edict_t *e ) -{ - ::FreeContainingEntity(e); -} - -edict_t* CServerGameEnts::BaseEntityToEdict( CBaseEntity *pEnt ) -{ - if ( pEnt ) - return pEnt->edict(); - else - return NULL; -} - -CBaseEntity* CServerGameEnts::EdictToBaseEntity( edict_t *pEdict ) -{ - if ( pEdict ) - return CBaseEntity::Instance( pEdict ); - else - return NULL; -} - - -/* Yuck.. ideally this would be in CServerNetworkProperty's header, but it requires CBaseEntity and -// inlining it gives a nice speedup. -inline void CServerNetworkProperty::CheckTransmit( CCheckTransmitInfo *pInfo ) -{ - // If we have a transmit proxy, let it hook our ShouldTransmit return value. - if ( m_pTransmitProxy ) - { - nShouldTransmit = m_pTransmitProxy->ShouldTransmit( pInfo, nShouldTransmit ); - } - - if ( m_pOuter->ShouldTransmit( pInfo ) ) - { - m_pOuter->SetTransmit( pInfo ); - } -} */ - -void CServerGameEnts::CheckTransmit( CCheckTransmitInfo *pInfo, const unsigned short *pEdictIndices, int nEdicts ) -{ - // NOTE: for speed's sake, this assumes that all networkables are CBaseEntities and that the edict list - // is consecutive in memory. If either of these things change, then this routine needs to change, but - // ideally we won't be calling any virtual from this routine. This speedy routine was added as an - // optimization which would be nice to keep. - edict_t *pBaseEdict = engine->PEntityOfEntIndex( 0 ); - - // get recipient player's skybox: - CBaseEntity *pRecipientEntity = CBaseEntity::Instance( pInfo->m_pClientEnt ); - - Assert( pRecipientEntity && pRecipientEntity->IsPlayer() ); - - if ( !pRecipientEntity ) - return; - - MDLCACHE_CRITICAL_SECTION(); - CBasePlayer *pRecipientPlayer = static_cast( pRecipientEntity ); - - const int skyBoxArea = pRecipientPlayer->m_Local.m_skybox3d.area; - const bool bIsHLTV = pRecipientPlayer->IsHLTV(); - - // m_pTransmitAlways must be set if HLTV client - Assert( bIsHLTV == ( pInfo->m_pTransmitAlways != NULL) ); - - // int dontSend = 0; int always = 0; int fullCheck = 0; int PVS = 0; - - - for ( int i=0; i < nEdicts; i++ ) - { - int iEdict = pEdictIndices[i]; - - edict_t *pEdict = &pBaseEdict[iEdict]; - Assert( pEdict == engine->PEntityOfEntIndex( iEdict ) ); - int nFlags = pEdict->m_fStateFlags & (FL_EDICT_DONTSEND|FL_EDICT_ALWAYS|FL_EDICT_PVSCHECK|FL_EDICT_FULLCHECK); - - if ( nFlags & FL_EDICT_DONTSEND ) - { - // entity needs no transmit - continue; - } - - if ( pInfo->m_pTransmitEdict->Get( iEdict ) ) - { - // entity is already marked for sending - continue; - } - else if ( pEdict->m_fStateFlags & FL_EDICT_DONTSEND ) - { - continue; - } - - if ( nFlags & FL_EDICT_ALWAYS ) - { - while ( true ) - { - // mark entity for sending - pInfo->m_pTransmitEdict->Set( iEdict ); - - if ( bIsHLTV ) - { - pInfo->m_pTransmitAlways->Set( iEdict ); - } - - CBaseEntity *pEnt = (CBaseEntity*)pEdict->GetUnknown(); - if ( !pEnt ) - break; - - CBaseEntity *pParent = pEnt->GetMoveParent(); - if ( !pParent ) - break; - - pEdict = pParent->edict(); - iEdict = pParent->entindex(); - } - - - continue; - } - - // get the Baseentity - - CBaseEntity *pEnt = ( CBaseEntity * )pEdict->GetUnknown(); - Assert( dynamic_cast< CBaseEntity* >( pEdict->GetUnknown() ) == pEnt ); - - if ( nFlags == FL_EDICT_FULLCHECK ) - { - // do a full ShouldTransmit() check, may return FL_EDICT_CHECKPVS - nFlags = pEnt->ShouldTransmit( pInfo ); - - Assert( !(nFlags & FL_EDICT_FULLCHECK) ); - - if ( nFlags & FL_EDICT_ALWAYS ) - { - pEnt->SetTransmit( pInfo, true ); - continue; - } - } - - if ( !( nFlags & FL_EDICT_PVSCHECK ) ) - { - // dont send this entity - continue; - } - - CServerNetworkProperty *netProp = pEnt->NetworkProp(); - if ( bIsHLTV ) - { - // for the HLTV we don't cull against PVS - if ( netProp->AreaNum() == skyBoxArea ) - { - pEnt->SetTransmit( pInfo, true ); - } - else - { - pEnt->SetTransmit( pInfo, false ); - } - continue; - } - - - bool bSameAreaAsSky = netProp->AreaNum() == skyBoxArea; - // Always send entities in the player's 3d skybox. - // Sidenote: call of AreaNum() ensures that PVS data is up to date for this entity - if ( bSameAreaAsSky ) - { - pEnt->SetTransmit( pInfo, true ); - } - else - { - bool bInPVS = netProp->IsInPVS( pInfo ); - if ( bInPVS ) - { - // only send if entity is in PVS - pEnt->SetTransmit( pInfo, false ); - } - else - { - // If the entity is marked "check PVS" but it's in hierarchy, walk up the hierarchy looking for the - // for any parent which is also in the PVS. If none are found, then we don't need to worry about sending ourself - CBaseEntity *orig = pEnt; - CBaseEntity *check = pEnt->GetMoveParent(); - - // BUG BUG: I think it might be better to build up a list of edict indices which "depend" on other answers and then - // resolve them in a second pass. Not sure what happens if an entity has two parents who both request PVS check? - - while ( check ) - { - int checkIndex = check->entindex(); - - // Parent already being sent - if ( pInfo->m_pTransmitEdict->Get( checkIndex ) ) - { - orig->SetTransmit( pInfo, true ); - break; - } - - edict_t *checkEdict = check->edict(); - int checkFlags = checkEdict->m_fStateFlags & (FL_EDICT_DONTSEND|FL_EDICT_ALWAYS|FL_EDICT_PVSCHECK|FL_EDICT_FULLCHECK); - if ( checkFlags & FL_EDICT_DONTSEND ) - { - break; - } - if ( checkFlags & FL_EDICT_ALWAYS ) - { - orig->SetTransmit( pInfo, true ); - break; - } - if ( checkFlags == FL_EDICT_FULLCHECK ) - { - // do a full ShouldTransmit() check, may return FL_EDICT_CHECKPVS - nFlags = check->ShouldTransmit( pInfo ); - Assert( !(nFlags & FL_EDICT_FULLCHECK) ); - if ( nFlags & FL_EDICT_ALWAYS ) - { - check->SetTransmit( pInfo, true ); - orig->SetTransmit( pInfo, true ); - } - break; - } - if ( checkFlags & FL_EDICT_PVSCHECK ) - { - // Check pvs - CServerNetworkProperty *netProp = check->NetworkProp(); - netProp->RecomputePVSInformation(); - bool bMoveParentInPVS = netProp->IsInPVS( pInfo ); - if ( bMoveParentInPVS ) - { - orig->SetTransmit( pInfo, true ); - break; - } - } - - // Continue up chain just in case the parent itself has a parent that's in the PVS... - check = check->GetMoveParent(); - } - } - } - } - -// Msg("A:%i, N:%i, F: %i, P: %i\n", always, dontSend, fullCheck, PVS ); -} - - -CServerGameClients g_ServerGameClients; -EXPOSE_SINGLE_INTERFACE_GLOBALVAR(CServerGameClients, IServerGameClients, INTERFACEVERSION_SERVERGAMECLIENTS, g_ServerGameClients ); - - -//----------------------------------------------------------------------------- -// Purpose: called when a player tries to connect to the server -// Input : *pEdict - the new player -// char *pszName - the players name -// char *pszAddress - the IP address of the player -// reject - output - fill in with the reason why -// maxrejectlen -- sizeof output buffer -// the player was not allowed to connect. -// Output : Returns TRUE if player is allowed to join, FALSE if connection is denied. -//----------------------------------------------------------------------------- -bool CServerGameClients::ClientConnect( edict_t *pEdict, const char *pszName, const char *pszAddress, char *reject, int maxrejectlen ) -{ - return g_pGameRules->ClientConnected( pEdict, pszName, pszAddress, reject, maxrejectlen ); -} - -//----------------------------------------------------------------------------- -// Purpose: Called when a player is fully active (i.e. ready to receive messages) -// Input : *pEntity - the player -//----------------------------------------------------------------------------- -void CServerGameClients::ClientActive( edict_t *pEdict, bool bLoadGame ) -{ - MDLCACHE_CRITICAL_SECTION(); - - ::ClientActive( pEdict, bLoadGame ); - - // If we just loaded from a save file, call OnRestore on valid entities - EndRestoreEntities(); - - if ( gpGlobals->eLoadType != MapLoad_LoadGame ) - { - // notify all entities that the player is now in the game - for ( CBaseEntity *pEntity = gEntList.FirstEnt(); pEntity != NULL; pEntity = gEntList.NextEnt(pEntity) ) - { - pEntity->PostClientActive(); - } - } - - SimThink_SortThinkList(); - // Tell the sound controller to check looping sounds - CBasePlayer *pPlayer = ( CBasePlayer * )CBaseEntity::Instance( pEdict ); - CSoundEnvelopeController::GetController().CheckLoopingSoundsForPlayer( pPlayer ); - SceneManager_ClientActive( pPlayer ); -} - -//----------------------------------------------------------------------------- -// Purpose: called when a player disconnects from a server -// Input : *pEdict - the player -//----------------------------------------------------------------------------- -void CServerGameClients::ClientDisconnect( edict_t *pEdict ) -{ - extern bool g_fGameOver; - - CBasePlayer *player = ( CBasePlayer * )CBaseEntity::Instance( pEdict ); - if ( player ) - { - if ( !g_fGameOver ) - { - player->SetMaxSpeed( 0.0f ); - - CSound *pSound; - pSound = CSoundEnt::SoundPointerForIndex( CSoundEnt::ClientSoundIndex( pEdict ) ); - { - // since this client isn't around to think anymore, reset their sound. - if ( pSound ) - { - pSound->Reset(); - } - } - - // since the edict doesn't get deleted, fix it so it doesn't interfere. - player->RemoveFlag( FL_AIMTARGET ); // don't attract autoaim - player->AddFlag( FL_DONTTOUCH ); // stop it touching anything - player->AddFlag( FL_NOTARGET ); // stop NPCs noticing it - player->AddSolidFlags( FSOLID_NOT_SOLID ); // nonsolid - - if ( g_pGameRules ) - { - g_pGameRules->ClientDisconnected( pEdict ); - } - } - - // Make sure all Untouch()'s are called for this client leaving - CBaseEntity::PhysicsRemoveTouchedList( player ); - CBaseEntity::PhysicsRemoveGroundList( player ); - -#if !defined( NO_ENTITY_PREDICTION ) - // Make sure anything we "own" is simulated by the server from now on - player->ClearPlayerSimulationList(); -#endif - } -} - -void CServerGameClients::ClientPutInServer( edict_t *pEntity, const char *playername ) -{ - if ( g_pClientPutInServerOverride ) - g_pClientPutInServerOverride( pEntity, playername ); - else - ::ClientPutInServer( pEntity, playername ); -} - -void CServerGameClients::ClientCommand( edict_t *pEntity ) -{ - CBasePlayer *player = ToBasePlayer( GetContainingEntity( pEntity ) ); - ::ClientCommand(player); -} - -//----------------------------------------------------------------------------- -// Purpose: called after the player changes userinfo - gives dll a chance to modify -// it before it gets sent into the rest of the engine-> -// Input : *pEdict - the player -// *infobuffer - their infobuffer -//----------------------------------------------------------------------------- -void CServerGameClients::ClientSettingsChanged( edict_t *pEdict ) -{ - // Is the client spawned yet? - if ( !pEdict->GetUnknown() ) - return; - - CBasePlayer *player = ( CBasePlayer * )CBaseEntity::Instance( pEdict ); - - if ( !player ) - return; - -#define QUICKGETCVARVALUE(v) (engine->GetClientConVarValue( player->entindex(), v )) - - // get network setting for prediction & lag compensation - player->m_nUpdateRate = Q_atoi( QUICKGETCVARVALUE("cl_updaterate") ); - - bool useInterpolation = Q_atoi( QUICKGETCVARVALUE("cl_interpolate") ) != 0; - - if ( useInterpolation ) - { - player->m_fLerpTime = Q_atof( QUICKGETCVARVALUE("cl_interp") ); - } - else - { - player->m_fLerpTime = 0.0f; - } - -#if !defined( NO_ENTITY_PREDICTION ) - bool usePrediction = Q_atoi( QUICKGETCVARVALUE("cl_predict")) != 0; - - if ( usePrediction ) - { - player->m_bPredictWeapons = Q_atoi( QUICKGETCVARVALUE("cl_predictweapons")) != 0; - player->m_bLagCompensation = Q_atoi( QUICKGETCVARVALUE("cl_lagcompensation")) != 0; - } - else -#endif - { - player->m_bPredictWeapons = false; - player->m_bLagCompensation = false; - } - - -#undef QUICKGETCVARVALUE - - g_pGameRules->ClientSettingsChanged( player ); -} - - -//----------------------------------------------------------------------------- -// Purpose: A client can have a separate "view entity" indicating that his/her view should depend on the origin of that -// view entity. If that's the case, then pViewEntity will be non-NULL and will be used. Otherwise, the current -// entity's origin is used. Either is offset by the m_vecViewOffset to get the eye position. -// From the eye position, we set up the PAS and PVS to use for filtering network messages to the client. At this point, we could -// override the actual PAS or PVS values, or use a different origin. -// NOTE: Do not cache the values of pas and pvs, as they depend on reusable memory in the engine, they are only good for this one frame -// Input : *pViewEntity - -// *pClient - -// **pvs - -// **pas - -//----------------------------------------------------------------------------- -void CServerGameClients::ClientSetupVisibility( edict_t *pViewEntity, edict_t *pClient, unsigned char *pvs, int pvssize ) -{ - Vector org; - - // Reset the PVS!!! - engine->ResetPVS( pvs, pvssize ); - - // Find the client's PVS - CBaseEntity *pVE = NULL; - if ( pViewEntity ) - { - pVE = GetContainingEntity( pViewEntity ); - // If we have a viewentity, it overrides the player's origin - if ( pVE ) - { - org = pVE->EyePosition(); - engine->AddOriginToPVS( org ); - } - } - - float fovDistanceAdjustFactor = 1; - - CBasePlayer *pPlayer = ( CBasePlayer * )GetContainingEntity( pClient ); - if ( pPlayer ) - { - org = pPlayer->EyePosition(); - pPlayer->SetupVisibility( pVE, pvs, pvssize ); - UTIL_SetClientVisibilityPVS( pClient, pvs, pvssize ); - fovDistanceAdjustFactor = pPlayer->GetFOVDistanceAdjustFactor(); - } - - unsigned char portalBits[MAX_AREA_PORTAL_STATE_BYTES]; - memset( portalBits, 0, sizeof( portalBits ) ); - - int portalNums[512]; - int isOpen[512]; - int iOutPortal = 0; - - for( unsigned short i = g_AreaPortals.Head(); i != g_AreaPortals.InvalidIndex(); i = g_AreaPortals.Next(i) ) - { - CFuncAreaPortalBase *pCur = g_AreaPortals[i]; - - bool bIsOpenOnClient = true; - - // Update our array of which portals are open and flush it if necessary. - portalNums[iOutPortal] = pCur->m_portalNumber; - isOpen[iOutPortal] = pCur->UpdateVisibility( org, fovDistanceAdjustFactor, bIsOpenOnClient ); - ++iOutPortal; - if ( iOutPortal >= ARRAYSIZE( portalNums ) ) - { - engine->SetAreaPortalStates( portalNums, isOpen, iOutPortal ); - iOutPortal = 0; - } - - // Version 0 portals (ie: shipping Half-Life 2 era) are always treated as open - // for purposes of the m_chAreaPortalBits array on the client. - if ( pCur->m_iPortalVersion == 0 ) - bIsOpenOnClient = true; - - if ( bIsOpenOnClient ) - { - if ( pCur->m_portalNumber < 0 ) - continue; - else if ( pCur->m_portalNumber >= sizeof( portalBits ) * 8 ) - Error( "ClientSetupVisibility: portal number (%d) too large", pCur->m_portalNumber ); - else - portalBits[pCur->m_portalNumber >> 3] |= (1 << (pCur->m_portalNumber & 7)); - } - } - - // Flush the remaining areaportal states. - engine->SetAreaPortalStates( portalNums, isOpen, iOutPortal ); - - // Update the area bits that get sent to the client. - pPlayer->m_Local.UpdateAreaBits( pPlayer, portalBits ); -} - - - - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *player - -// *buf - -// numcmds - -// totalcmds - -// dropped_packets - -// ignore - -// paused - -// Output : float -//----------------------------------------------------------------------------- -float CServerGameClients::ProcessUsercmds( edict_t *player, bf_read *buf, int numcmds, int totalcmds, - int dropped_packets, bool ignore, bool paused ) -{ - int i; - CUserCmd *from, *to; - - // We track last three command in case we drop some - // packets but get them back. - CUserCmd cmds[ CMD_MAXBACKUP ]; - - CUserCmd cmdNull; // For delta compression - - Assert( numcmds >= 0 ); - Assert( ( totalcmds - numcmds ) >= 0 ); - - CBasePlayer *pPlayer = NULL; - CBaseEntity *pEnt = CBaseEntity::Instance(player); - if ( pEnt && pEnt->IsPlayer() ) - { - pPlayer = static_cast< CBasePlayer * >( pEnt ); - } - // Too many commands? - if ( totalcmds < 0 || totalcmds >= ( CMD_MAXBACKUP - 1 ) ) - { - const char *name = "unknown"; - if ( pPlayer ) - { - name = pPlayer->GetPlayerName(); - } - - Msg("CBasePlayer::ProcessUsercmds: too many cmds %i sent for player %s\n", totalcmds, name ); - // FIXME: Need a way to drop the client from here - //SV_DropClient ( host_client, false, "CMD_MAXBACKUP hit" ); - buf->SetOverflowFlag(); - return 0.0f; - } - - // Initialize for reading delta compressed usercmds - cmdNull.Reset(); - from = &cmdNull; - for ( i = totalcmds - 1; i >= 0; i-- ) - { - to = &cmds[ i ]; - ReadUsercmd( buf, to, from ); - from = to; - } - - // Client not fully connected or server has gone inactive or is paused, just ignore - if ( ignore || !pPlayer ) - { - return 0.0f; - } - - MDLCACHE_CRITICAL_SECTION(); - pPlayer->ProcessUsercmds( cmds, numcmds, totalcmds, dropped_packets, paused ); - - return TICK_INTERVAL; -} - - -void CServerGameClients::PostClientMessagesSent( void ) -{ - VPROF("CServerGameClients::PostClient"); - gEntList.PostClientMessagesSent(); -} - -// Sets the client index for the client who typed the command into his/her console -void CServerGameClients::SetCommandClient( int index ) -{ - g_nCommandClientIndex = index; -} - -int CServerGameClients::GetReplayDelay( edict_t *pEdict, int &entity ) -{ - CBasePlayer *pPlayer = ( CBasePlayer * )CBaseEntity::Instance( pEdict ); - - if ( !pPlayer ) - return 0; - - entity = pPlayer->GetReplayEntity(); - - return pPlayer->GetDelayTicks(); -} - - -//----------------------------------------------------------------------------- -// The client's userinfo data lump has changed -//----------------------------------------------------------------------------- -void CServerGameClients::ClientEarPosition( edict_t *pEdict, Vector *pEarOrigin ) -{ - CBasePlayer *pPlayer = ( CBasePlayer * )CBaseEntity::Instance( pEdict ); - if (pPlayer) - { - *pEarOrigin = pPlayer->EarPosition(); - } - else - { - // Shouldn't happen - Assert(0); - *pEarOrigin = vec3_origin; - } -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *player - -// Output : CPlayerState -//----------------------------------------------------------------------------- -CPlayerState *CServerGameClients::GetPlayerState( edict_t *player ) -{ - // Is the client spawned yet? - if ( !player || !player->GetUnknown() ) - return NULL; - - CBasePlayer *pBasePlayer = ( CBasePlayer * )CBaseEntity::Instance( player ); - if ( !pBasePlayer ) - return NULL; - - return &pBasePlayer->pl; -} - -//----------------------------------------------------------------------------- -// Purpose: Anything this game .dll wants to add to the bug reporter text (e.g., the entity/model under the picker crosshair) -// can be added here -// Input : *buf - -// buflen - -//----------------------------------------------------------------------------- -void CServerGameClients::GetBugReportInfo( char *buf, int buflen ) -{ - recentNPCSpeech_t speech[ SPEECH_LIST_MAX_SOUNDS ]; - int num; - int i; - - buf[ 0 ] = 0; - - if ( gpGlobals->maxClients == 1 ) - { - CBaseEntity *ent = FindPickerEntity( UTIL_PlayerByIndex(1) ); - if ( ent ) - { - Q_snprintf( buf, buflen, "Picker %i/%s - ent %s model %s\n", - ent->entindex(), - ent->GetClassname(), - STRING( ent->GetEntityName() ), - ent->GetModelName() ); - } - - // get any sounds that were spoken by NPCs recently - num = GetRecentNPCSpeech( speech ); - if ( num > 0 ) - { - Q_snprintf( buf, buflen, "%sRecent NPC speech:\n", buf ); - for( i = 0; i < num; i++ ) - { - Q_snprintf( buf, buflen, "%s time: %6.3f sound name: %s scene: %s\n", buf, speech[ i ].time, speech[ i ].name, speech[ i ].sceneName ); - } - Q_snprintf( buf, buflen, "%sCurrent time: %6.3f\n", buf, gpGlobals->curtime ); - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -static bf_write *g_pMsgBuffer = NULL; - -void EntityMessageBegin( CBaseEntity * entity, bool reliable /*= false*/ ) -{ - Assert( !g_pMsgBuffer ); - - Assert ( entity ); - - g_pMsgBuffer = engine->EntityMessageBegin( entity->entindex(), entity->GetServerClass(), reliable ); -} - -void UserMessageBegin( IRecipientFilter& filter, const char *messagename ) -{ - Assert( !g_pMsgBuffer ); - - Assert( messagename ); - - int msg_type = usermessages->LookupUserMessage( messagename ); - - if ( msg_type == -1 ) - { - Error( "UserMessageBegin: Unregistered message '%s'\n", messagename ); - } - - g_pMsgBuffer = engine->UserMessageBegin( &filter, msg_type ); -} - -void MessageEnd( void ) -{ - Assert( g_pMsgBuffer ); - - engine->MessageEnd(); - - g_pMsgBuffer = NULL; -} - -void MessageWriteByte( int iValue) -{ - if (!g_pMsgBuffer) - Error( "WRITE_BYTE called with no active message\n" ); - - g_pMsgBuffer->WriteByte( iValue ); -} - -void MessageWriteChar( int iValue) -{ - if (!g_pMsgBuffer) - Error( "WRITE_CHAR called with no active message\n" ); - - g_pMsgBuffer->WriteChar( iValue ); -} - -void MessageWriteShort( int iValue) -{ - if (!g_pMsgBuffer) - Error( "WRITE_SHORT called with no active message\n" ); - - g_pMsgBuffer->WriteShort( iValue ); -} - -void MessageWriteWord( int iValue ) -{ - if (!g_pMsgBuffer) - Error( "WRITE_WORD called with no active message\n" ); - - g_pMsgBuffer->WriteWord( iValue ); -} - -void MessageWriteLong( int iValue) -{ - if (!g_pMsgBuffer) - Error( "WriteLong called with no active message\n" ); - - g_pMsgBuffer->WriteLong( iValue ); -} - -void MessageWriteFloat( float flValue) -{ - if (!g_pMsgBuffer) - Error( "WriteFloat called with no active message\n" ); - - g_pMsgBuffer->WriteFloat( flValue ); -} - -void MessageWriteAngle( float flValue) -{ - if (!g_pMsgBuffer) - Error( "WriteAngle called with no active message\n" ); - - g_pMsgBuffer->WriteBitAngle( flValue, 8 ); -} - -void MessageWriteCoord( float flValue) -{ - if (!g_pMsgBuffer) - Error( "WriteCoord called with no active message\n" ); - - g_pMsgBuffer->WriteBitCoord( flValue ); -} - -void MessageWriteVec3Coord( const Vector& rgflValue) -{ - if (!g_pMsgBuffer) - Error( "WriteVec3Coord called with no active message\n" ); - - g_pMsgBuffer->WriteBitVec3Coord( rgflValue ); -} - -void MessageWriteVec3Normal( const Vector& rgflValue) -{ - if (!g_pMsgBuffer) - Error( "WriteVec3Normal called with no active message\n" ); - - g_pMsgBuffer->WriteBitVec3Normal( rgflValue ); -} - -void MessageWriteAngles( const QAngle& rgflValue) -{ - if (!g_pMsgBuffer) - Error( "WriteVec3Normal called with no active message\n" ); - - g_pMsgBuffer->WriteBitAngles( rgflValue ); -} - -void MessageWriteString( const char *sz ) -{ - if (!g_pMsgBuffer) - Error( "WriteString called with no active message\n" ); - - g_pMsgBuffer->WriteString( sz ); -} - -void MessageWriteEntity( int iValue) -{ - if (!g_pMsgBuffer) - Error( "WriteEntity called with no active message\n" ); - - g_pMsgBuffer->WriteShort( iValue ); -} - -void MessageWriteEHandle( CBaseEntity *pEntity ) -{ - if (!g_pMsgBuffer) - Error( "WriteEHandle called with no active message\n" ); - - long iEncodedEHandle; - - if( pEntity ) - { - EHANDLE hEnt = pEntity; - - int iSerialNum = hEnt.GetSerialNumber() & (1 << NUM_NETWORKED_EHANDLE_SERIAL_NUMBER_BITS) - 1; - iEncodedEHandle = hEnt.GetEntryIndex() | (iSerialNum << MAX_EDICT_BITS); - } - else - { - iEncodedEHandle = INVALID_NETWORKED_EHANDLE_VALUE; - } - - g_pMsgBuffer->WriteLong( iEncodedEHandle ); -} - -// bitwise -void MessageWriteBool( bool bValue ) -{ - if (!g_pMsgBuffer) - Error( "WriteBool called with no active message\n" ); - - g_pMsgBuffer->WriteOneBit( bValue ? 1 : 0 ); -} - -void MessageWriteUBitLong( unsigned int data, int numbits ) -{ - if (!g_pMsgBuffer) - Error( "WriteUBitLong called with no active message\n" ); - - g_pMsgBuffer->WriteUBitLong( data, numbits ); -} - -void MessageWriteSBitLong( int data, int numbits ) -{ - if (!g_pMsgBuffer) - Error( "WriteSBitLong called with no active message\n" ); - - g_pMsgBuffer->WriteSBitLong( data, numbits ); -} - -void MessageWriteBits( const void *pIn, int nBits ) -{ - if (!g_pMsgBuffer) - Error( "WriteBits called with no active message\n" ); - - g_pMsgBuffer->WriteBits( pIn, nBits ); -} - -class CServerDLLSharedAppSystems : public IServerDLLSharedAppSystems -{ -public: - CServerDLLSharedAppSystems() - { - AddAppSystem( "soundemittersystem", SOUNDEMITTERSYSTEM_INTERFACE_VERSION ); - AddAppSystem( "scenefilecache", SCENE_FILE_CACHE_INTERFACE_VERSION ); - } - - virtual int Count() - { - return m_Systems.Count(); - } - virtual char const *GetDllName( int idx ) - { - return m_Systems[ idx ].m_pModuleName; - } - virtual char const *GetInterfaceName( int idx ) - { - return m_Systems[ idx ].m_pInterfaceName; - } -private: - void AddAppSystem( char const *moduleName, char const *interfaceName ) - { - AppSystemInfo_t sys; - sys.m_pModuleName = moduleName; - sys.m_pInterfaceName = interfaceName; - m_Systems.AddToTail( sys ); - } - - CUtlVector< AppSystemInfo_t > m_Systems; -}; - -EXPOSE_SINGLE_INTERFACE( CServerDLLSharedAppSystems, IServerDLLSharedAppSystems, SERVER_DLL_SHARED_APPSYSTEMS ); - - +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: encapsulates and implements all the accessing of the game dll from external +// sources (only the engine at the time of writing) +// This files ONLY contains functions and data necessary to build an interface +// to external modules +//=============================================================================// + +#include "cbase.h" +#include "gamestringpool.h" +#include "mapentities_shared.h" +#include "game.h" +#include "entityapi.h" +#include "client.h" +#include "saverestore.h" +#include "entitylist.h" +#include "gamerules.h" +#include "soundent.h" +#include "player.h" +#include "server_class.h" +#include "ai_node.h" +#include "ai_link.h" +#include "ai_saverestore.h" +#include "ai_networkmanager.h" +#include "ndebugoverlay.h" +#include "ivoiceserver.h" +#include +#include "movehelper_server.h" +#include "networkstringtable_gamedll.h" +#include "filesystem.h" +#include "terrainmodmgr.h" +#include "func_areaportalwindow.h" +#include "igamesystem.h" +#include "init_factory.h" +#include "vstdlib/random.h" +#include "env_wind_shared.h" +#include "engine/IEngineSound.h" +#include "ispatialpartition.h" +#include "textstatsmgr.h" +#include "bitbuf.h" +#include "saverestoretypes.h" +#include "physics_saverestore.h" +#include "tier0/vprof.h" +#include "effect_dispatch_data.h" +#include "engine/IStaticPropMgr.h" +#include "TemplateEntities.h" +#include "ai_speech.h" +#include "soundenvelope.h" +#include "usermessages.h" +#include "physics.h" +#include "mapentities.h" +#include "igameevents.h" +#include "EventLog.h" +#include "datacache/idatacache.h" +#include "engine/ivdebugoverlay.h" +#include "shareddefs.h" +#include "props.h" +#include "timedeventmgr.h" +#include "gameinterface.h" +#include "eventqueue.h" +#include "hltvdirector.h" +#include "SoundEmitterSystem/isoundemittersystembase.h" +#include "nav_mesh.h" +#include "AI_ResponseSystem.h" +#include "saverestore_stringtable.h" +#include "util.h" +#include "vstdlib/ICommandLine.h" +#include "datacache/imdlcache.h" +#include "engine/iserverplugin.h" +#ifdef _WIN32 +#include "ienginevgui.h" +#include "vgui_gamedll_int.h" +#include "vgui_controls/AnimationController.h" +#endif +#include "ragdoll_shared.h" +#include "toolframework/iserverenginetools.h" +#include "sceneentity.h" +#include "appframework/IAppSystemGroup.h" +#include "scenefilecache/ISceneFileCache.h" + +#if !defined( _RETAIL ) +#ifdef _XBOX +#include "xbox/xbox_platform.h" +#include "xbox/xbox_win32stubs.h" +#include "xbox/xbox_core.h" +#endif +#endif + +#ifdef CSTRIKE_DLL // BOTPORT: TODO: move these ifdefs out +#include "bot/bot.h" +#endif + +IUploadGameStats *gamestatsuploader = NULL; + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +CTimedEventMgr g_NetworkPropertyEventMgr; + +ISaveRestoreBlockHandler *GetEventQueueSaveRestoreBlockHandler(); +ISaveRestoreBlockHandler *GetCommentarySaveRestoreBlockHandler(); + +// Engine interfaces. +IVEngineServer *engine = NULL; +IVoiceServer *g_pVoiceServer = NULL; +ICvar *cvar = NULL; +#if !defined(_STATIC_LINKED) +IFileSystem *filesystem = NULL; +#else +extern IFileSystem *filesystem; +#endif +INetworkStringTableContainer *networkstringtable = NULL; +IStaticPropMgrServer *staticpropmgr = NULL; +IUniformRandomStream *random = NULL; +IEngineSound *enginesound = NULL; +ISpatialPartition *partition = NULL; +IVModelInfo *modelinfo = NULL; +IEngineTrace *enginetrace = NULL; +IGameEventManager2 *gameeventmanager = NULL; +IDataCache *datacache = NULL; +IVDebugOverlay * debugoverlay = NULL; +ISoundEmitterSystemBase *soundemitterbase = NULL; +IMDLCache *mdlcache = NULL; +IServerPluginHelpers *serverpluginhelpers = NULL; +#ifdef _WIN32 +IEngineVGui *enginevgui = NULL; +#endif +IServerEngineTools *serverenginetools = NULL; +ISceneFileCache *scenefilecache = NULL; + +IGameSystem *SoundEmitterSystem(); + +bool SceneCacheInit(); +void SceneCacheShutdown(); +bool ModelSoundsCacheInit(); +void ModelSoundsCacheShutdown(); + +void SceneManager_ClientActive( CBasePlayer *player ); + +class IMaterialSystem; +class IStudioRender; + +#ifdef _DEBUG +static ConVar s_UseNetworkVars( "UseNetworkVars", "1", FCVAR_CHEAT, "For profiling, toggle network vars." ); +#endif + +extern ConVar sv_noclipduringpause; +ConVar sv_massreport( "sv_massreport", "0" ); + +ConVar sv_autosave( "sv_autosave", "1", 0, "Set to 1 to save game on level transition. Does not affect autosave triggers." ); +ConVar *sv_maxreplay = NULL; + +// String tables +INetworkStringTable *g_pStringTableEffectDispatch = NULL; +INetworkStringTable *g_pStringTableVguiScreen = NULL; +INetworkStringTable *g_pStringTableMaterials = NULL; +INetworkStringTable *g_pStringTableInfoPanel = NULL; +INetworkStringTable *g_pStringTableClientSideChoreoScenes = NULL; + +CStringTableSaveRestoreOps g_VguiScreenStringOps; + +// Holds global variables shared between engine and game. +CGlobalVars *gpGlobals; +edict_t *g_pDebugEdictBase = 0; +static int g_nCommandClientIndex = 0; + +static ConVar sv_showhitboxes( "sv_showhitboxes", "-1", FCVAR_CHEAT, "Send server-side hitboxes for specified entity to client (NOTE: this uses lots of bandwidth, use on listen server only)." ); + +void PrecachePointTemplates(); + +static ClientPutInServerOverrideFn g_pClientPutInServerOverride = NULL; +static void UpdateChapterRestrictions( const char *mapname ); + + +#if !defined( _XBOX ) // Don't doubly define this symbol. +CSharedEdictChangeInfo *g_pSharedChangeInfo = NULL; + +#endif + +IChangeInfoAccessor *CBaseEdict::GetChangeAccessor() +{ + return engine->GetChangeAccessor( (const edict_t *)this ); +} + +const IChangeInfoAccessor *CBaseEdict::GetChangeAccessor() const +{ + return engine->GetChangeAccessor( (const edict_t *)this ); +} + +const char *GetHintTypeDescription( CAI_Hint *pHint ); + +void ClientPutInServerOverride( ClientPutInServerOverrideFn fn ) +{ + g_pClientPutInServerOverride = fn; +} + + + +//----------------------------------------------------------------------------- +// Purpose: +// Output : int +//----------------------------------------------------------------------------- +int UTIL_GetCommandClientIndex( void ) +{ + // -1 == unknown,dedicated server console + // 0 == player 1 + + // Convert to 1 based offset + return (g_nCommandClientIndex+1); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : CBasePlayer +//----------------------------------------------------------------------------- +CBasePlayer *UTIL_GetCommandClient( void ) +{ + int idx = UTIL_GetCommandClientIndex(); + if ( idx > 0 ) + { + return UTIL_PlayerByIndex( idx ); + } + + // HLDS console issued command + return NULL; +} + +extern void InitializeCvars( void ); + +CBaseEntity* FindPickerEntity( CBasePlayer* pPlayer ); +CAI_Node* FindPickerAINode( CBasePlayer* pPlayer, NodeType_e nNodeType ); +CAI_Link* FindPickerAILink( CBasePlayer* pPlayer ); +float GetFloorZ(const Vector &origin); +void UpdateAllClientData( void ); +void DrawMessageEntities(); + +#include "ai_network.h" + +// For now just using one big AI network +extern ConVar think_limit; + + +#if 0 +//----------------------------------------------------------------------------- +// Purpose: Draw output overlays for any measure sections +// Input : +//----------------------------------------------------------------------------- +void DrawMeasuredSections(void) +{ + int row = 1; + float rowheight = 0.025; + + CMeasureSection *p = CMeasureSection::GetList(); + while ( p ) + { + char str[256]; + Q_snprintf(str,sizeof(str),"%s",p->GetName()); + NDebugOverlay::ScreenText( 0.01,0.51+(row*rowheight),str, 255,255,255,255, 0.0 ); + + Q_snprintf(str,sizeof(str),"%5.2f\n",p->GetTime().GetMillisecondsF()); + //Q_snprintf(str,sizeof(str),"%3.3f\n",p->GetTime().GetSeconds() * 100.0 / engine->Time()); + NDebugOverlay::ScreenText( 0.28,0.51+(row*rowheight),str, 255,255,255,255, 0.0 ); + + Q_snprintf(str,sizeof(str),"%5.2f\n",p->GetMaxTime().GetMillisecondsF()); + //Q_snprintf(str,sizeof(str),"%3.3f\n",p->GetTime().GetSeconds() * 100.0 / engine->Time()); + NDebugOverlay::ScreenText( 0.34,0.51+(row*rowheight),str, 255,255,255,255, 0.0 ); + + + row++; + + p = p->GetNext(); + } + + bool sort_reset = false; + + // Time to redo sort? + if ( measure_resort.GetFloat() > 0.0 && + engine->Time() >= CMeasureSection::m_dNextResort ) + { + // Redo it + CMeasureSection::SortSections(); + // Set next time + CMeasureSection::m_dNextResort = engine->Time() + measure_resort.GetFloat(); + // Flag to reset sort accumulator, too + sort_reset = true; + } + + // Iterate through the sections now + p = CMeasureSection::GetList(); + while ( p ) + { + // Update max + p->UpdateMax(); + + // Reset regular accum. + p->Reset(); + // Reset sort accum less often + if ( sort_reset ) + { + p->SortReset(); + } + p = p->GetNext(); + } + +} +#endif + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void DrawAllDebugOverlays( void ) +{ + // If in debug select mode print the selection entities name or classname + if (CBaseEntity::m_bInDebugSelect) + { + CBasePlayer* pPlayer = UTIL_PlayerByIndex( CBaseEntity::m_nDebugPlayer ); + + if (pPlayer) + { + // First try to trace a hull to an entity + CBaseEntity *pEntity = FindPickerEntity( pPlayer ); + + if ( pEntity ) + { + pEntity->DrawDebugTextOverlays(); + pEntity->DrawBBoxOverlay(); + pEntity->SendDebugPivotOverlay(); + } + } + } + + // -------------------------------------------------------- + // Draw debug overlay lines + // -------------------------------------------------------- + UTIL_DrawOverlayLines(); + + // ------------------------------------------------------------------------ + // If in wc_edit mode draw a box to highlight which node I'm looking at + // ------------------------------------------------------------------------ + if (engine->IsInEditMode()) + { + CBasePlayer* pPlayer = UTIL_PlayerByIndex( CBaseEntity::m_nDebugPlayer ); + if (pPlayer) + { + if (g_pAINetworkManager->GetEditOps()->m_bLinkEditMode) + { + CAI_Link* pAILink = FindPickerAILink(pPlayer); + if (pAILink) + { + // For now just using one big AI network + Vector startPos = g_pBigAINet->GetNode(pAILink->m_iSrcID)->GetPosition(g_pAINetworkManager->GetEditOps()->m_iHullDrawNum); + Vector endPos = g_pBigAINet->GetNode(pAILink->m_iDestID)->GetPosition(g_pAINetworkManager->GetEditOps()->m_iHullDrawNum); + Vector linkDir = startPos-endPos; + float linkLen = VectorNormalize( linkDir ); + + // Draw in green if link that's been turned off + if (pAILink->m_LinkInfo & bits_LINK_OFF) + { + NDebugOverlay::BoxDirection(startPos, Vector(-4,-4,-4), Vector(-linkLen,4,4), linkDir, 0,255,0,40,0); + } + else + { + NDebugOverlay::BoxDirection(startPos, Vector(-4,-4,-4), Vector(-linkLen,4,4), linkDir, 255,0,0,40,0); + } + } + } + else + { + CAI_Node* pAINode; + if (g_pAINetworkManager->GetEditOps()->m_bAirEditMode) + { + pAINode = FindPickerAINode(pPlayer,NODE_AIR); + } + else + { + pAINode = FindPickerAINode(pPlayer,NODE_GROUND); + } + + if (pAINode) + { + Vector vecPos = pAINode->GetPosition(g_pAINetworkManager->GetEditOps()->m_iHullDrawNum); + NDebugOverlay::Box( vecPos, Vector(-8,-8,-8), Vector(8,8,8), 255,0,0,40,0); + + if ( pAINode->GetHint() ) + { + CBaseEntity *pEnt = (CBaseEntity *)pAINode->GetHint(); + if ( pEnt->GetEntityName() != NULL_STRING ) + { + NDebugOverlay::Text( vecPos + Vector(0,0,6), STRING(pEnt->GetEntityName()), false, 0 ); + } + NDebugOverlay::Text( vecPos, GetHintTypeDescription( pAINode->GetHint() ), false, 0 ); + } + } + } + // ------------------------------------ + // If in air edit mode draw guide line + // ------------------------------------ + if (g_pAINetworkManager->GetEditOps()->m_bAirEditMode) + { + UTIL_DrawPositioningOverlay(g_pAINetworkManager->GetEditOps()->m_flAirEditDistance); + } + else + { + NDebugOverlay::DrawGroundCrossHairOverlay(); + } + } + } + + // For not just using one big AI Network + if ( g_pAINetworkManager ) + { + g_pAINetworkManager->GetEditOps()->DrawAINetworkOverlay(); + } + + // PERFORMANCE: only do this in developer mode + if ( g_pDeveloper->GetInt() ) + { + // iterate through all objects for debug overlays + const CEntInfo *pInfo = gEntList.FirstEntInfo(); + + for ( ;pInfo; pInfo = pInfo->m_pNext ) + { + CBaseEntity *ent = (CBaseEntity *)pInfo->m_pEntity; + // HACKHACK: to flag off these calls + if ( ent->m_debugOverlays || ent->m_pTimedOverlay ) + { + MDLCACHE_CRITICAL_SECTION(); + ent->DrawDebugGeometryOverlays(); + } + } + } + + if ( sv_massreport.GetInt() ) + { + // iterate through all objects for debug overlays + const CEntInfo *pInfo = gEntList.FirstEntInfo(); + + for ( ;pInfo; pInfo = pInfo->m_pNext ) + { + CBaseEntity *ent = (CBaseEntity *)pInfo->m_pEntity; + if (!ent->VPhysicsGetObject()) + continue; + + char tempstr[512]; + Q_snprintf(tempstr, sizeof(tempstr),"%s: Mass: %.2f kg / %.2f lb (%s)", + STRING(ent->GetModelName()), ent->VPhysicsGetObject()->GetMass(), + kg2lbs(ent->VPhysicsGetObject()->GetMass()), + GetMassEquivalent(ent->VPhysicsGetObject()->GetMass())); + ent->EntityText(0, tempstr, 0); + } + } + + // A hack to draw point_message entities w/o developer required + DrawMessageEntities(); +} + +CServerGameDLL g_ServerGameDLL; +EXPOSE_SINGLE_INTERFACE_GLOBALVAR(CServerGameDLL, IServerGameDLL, INTERFACEVERSION_SERVERGAMEDLL, g_ServerGameDLL); + +bool CServerGameDLL::DLLInit(CreateInterfaceFn engineFactory, + CreateInterfaceFn physicsFactory, CreateInterfaceFn fileSystemFactory, + CGlobalVars *pGlobals) +{ + // init each (seperated for ease of debugging) + if ( (engine = (IVEngineServer*)engineFactory(INTERFACEVERSION_VENGINESERVER, NULL)) == NULL ) + return false; + if ( (g_pVoiceServer = (IVoiceServer*)engineFactory(INTERFACEVERSION_VOICESERVER, NULL)) == NULL ) + return false; + if ( (cvar = (ICvar*)engineFactory(VENGINE_CVAR_INTERFACE_VERSION, NULL)) == NULL ) + return false; + if ( (networkstringtable = (INetworkStringTableContainer *)engineFactory(INTERFACENAME_NETWORKSTRINGTABLESERVER,NULL)) == NULL ) + return false; + if ( (staticpropmgr = (IStaticPropMgrServer *)engineFactory(INTERFACEVERSION_STATICPROPMGR_SERVER,NULL)) == NULL ) + return false; + if ( (random = (IUniformRandomStream *)engineFactory(VENGINE_SERVER_RANDOM_INTERFACE_VERSION, NULL)) == NULL ) + return false; + if ( (enginesound = (IEngineSound *)engineFactory(IENGINESOUND_SERVER_INTERFACE_VERSION, NULL)) == NULL ) + return false; + if ( (partition = (ISpatialPartition *)engineFactory(INTERFACEVERSION_SPATIALPARTITION, NULL)) == NULL ) + return false; + if ( (modelinfo = (IVModelInfo *)engineFactory(VMODELINFO_SERVER_INTERFACE_VERSION, NULL)) == NULL ) + return false; + if ( (enginetrace = (IEngineTrace *)engineFactory(INTERFACEVERSION_ENGINETRACE_SERVER,NULL)) == NULL ) + return false; + if ( (filesystem = (IFileSystem *)fileSystemFactory(FILESYSTEM_INTERFACE_VERSION,NULL)) == NULL ) + return false; + if ( (gameeventmanager = (IGameEventManager2 *)engineFactory(INTERFACEVERSION_GAMEEVENTSMANAGER2,NULL)) == NULL ) + return false; + if ( (datacache = (IDataCache*)engineFactory(DATACACHE_INTERFACE_VERSION, NULL )) == NULL ) + return false; + if ( (soundemitterbase = (ISoundEmitterSystemBase *)engineFactory(SOUNDEMITTERSYSTEM_INTERFACE_VERSION, NULL)) == NULL ) + return false; +#ifndef _XBOX + if ( (gamestatsuploader = (IUploadGameStats *)engineFactory( INTERFACEVERSION_UPLOADGAMESTATS, NULL )) == NULL ) + return false; +#endif + if ( (mdlcache = (IMDLCache*)engineFactory( MDLCACHE_INTERFACE_VERSION, NULL )) == NULL ) + return false; + if ( (serverpluginhelpers = (IServerPluginHelpers *)engineFactory(INTERFACEVERSION_ISERVERPLUGINHELPERS, NULL)) == NULL ) + return false; + if ( (scenefilecache = (ISceneFileCache *)engineFactory( SCENE_FILE_CACHE_INTERFACE_VERSION, NULL )) == NULL ) + return false; + + // If not running dedicated, grab the engine vgui interface + if ( !engine->IsDedicatedServer() ) + { +#ifdef _WIN32 + if ( ( enginevgui = ( IEngineVGui * )engineFactory(VENGINE_VGUI_VERSION, NULL)) == NULL ) + return false; + + // This interface is optional, and is only valid when running with -tools + serverenginetools = ( IServerEngineTools * )engineFactory( VSERVERENGINETOOLS_INTERFACE_VERSION, NULL ); +#endif + } + + // Yes, both the client and game .dlls will try to Connect, the soundemittersystem.dll will handle this gracefully + if ( !soundemitterbase->Connect( engineFactory ) ) + return false; + + // cache the globals + gpGlobals = pGlobals; + + g_pSharedChangeInfo = engine->GetSharedEdictChangeInfo(); + + MathLib_Init( 2.2f, 2.2f, 0.0f, 2 ); + + // save these in case other system inits need them + factorylist_t factories; + factories.engineFactory = engineFactory; + factories.fileSystemFactory = fileSystemFactory; + factories.physicsFactory = physicsFactory; + FactoryList_Store( factories ); + + // load used game events + gameeventmanager->LoadEventsFromFile("resource/gameevents.res"); + + // init the cvar list first in case inits want to reference them + InitializeCvars(); + + sv_cheats = (ConVar*) ConCommandBase::FindCommand( "sv_cheats" ); + if ( !sv_cheats ) + return false; + + sv_maxreplay = (ConVar*) ConCommandBase::FindCommand( "sv_maxreplay" ); + + g_pGameSaveRestoreBlockSet->AddBlockHandler( GetEntitySaveRestoreBlockHandler() ); + g_pGameSaveRestoreBlockSet->AddBlockHandler( GetPhysSaveRestoreBlockHandler() ); + g_pGameSaveRestoreBlockSet->AddBlockHandler( GetAISaveRestoreBlockHandler() ); + g_pGameSaveRestoreBlockSet->AddBlockHandler( GetTemplateSaveRestoreBlockHandler() ); + g_pGameSaveRestoreBlockSet->AddBlockHandler( GetDefaultResponseSystemSaveRestoreBlockHandler() ); +#if !defined( _CONSOLE ) + g_pGameSaveRestoreBlockSet->AddBlockHandler( GetCommentarySaveRestoreBlockHandler() ); +#endif + g_pGameSaveRestoreBlockSet->AddBlockHandler( GetEventQueueSaveRestoreBlockHandler() ); + + // The string system must init first + shutdown last + IGameSystem::Add( GameStringSystem() ); + + // Physics must occur before the sound envelope manager + IGameSystem::Add( PhysicsGameSystem() ); + + // Add game log system + IGameSystem::Add( GameLogSystem() ); +#ifndef _XBOX + // Add HLTV director + IGameSystem::Add( HLTVDirectorSystem() ); +#endif + // Add sound emitter + IGameSystem::Add( SoundEmitterSystem() ); + +#ifdef _WIN32 + // Startup vgui + if ( enginevgui ) + { + if(!VGui_Startup( engineFactory )) + return false; + } +#endif + + // load Mod specific game events ( MUST be before InitAllSystems() so it can pickup the mod specific events) + gameeventmanager->LoadEventsFromFile("resource/ModEvents.res"); + +#ifdef CSTRIKE_DLL // BOTPORT: TODO: move these ifdefs out + InstallBotControl(); +#endif + + if ( !IGameSystem::InitAllSystems() ) + return false; + + // Due to dependencies, these are not autogamesystems + if ( !SceneCacheInit() ) + { + return false; + } + + // Due to dependencies, these are not autogamesystems + if ( !ModelSoundsCacheInit() ) + { + return false; + } + + // try to get debug overlay, may be NULL if on HLDS + debugoverlay = (IVDebugOverlay *)engineFactory( VDEBUG_OVERLAY_INTERFACE_VERSION, NULL ); + +#ifndef _XBOX + // create the Navigation Mesh interface + TheNavMesh = new CNavMesh; + + // init the gamestatsupload connection + gamestatsuploader->InitConnection(); +#endif + +#if !defined( _RETAIL ) +#if defined( _XBOX ) + XBX_rTimeStampLog( Plat_FloatTime(), "DLLInit finished" ); +#endif +#endif + return true; +} + +void CServerGameDLL::PostInit() +{ +#ifdef _WIN32 + if ( enginevgui ) + { + if ( VGui_PostInit() ) + { + // all good + } + } +#endif +} + +void CServerGameDLL::DLLShutdown( void ) +{ + + // Due to dependencies, these are not autogamesystems + ModelSoundsCacheShutdown(); + SceneCacheShutdown(); + +#if !defined( _CONSOLE ) + g_pGameSaveRestoreBlockSet->RemoveBlockHandler( GetCommentarySaveRestoreBlockHandler() ); +#endif + g_pGameSaveRestoreBlockSet->RemoveBlockHandler( GetEventQueueSaveRestoreBlockHandler() ); + g_pGameSaveRestoreBlockSet->RemoveBlockHandler( GetDefaultResponseSystemSaveRestoreBlockHandler() ); + g_pGameSaveRestoreBlockSet->RemoveBlockHandler( GetTemplateSaveRestoreBlockHandler() ); + g_pGameSaveRestoreBlockSet->RemoveBlockHandler( GetAISaveRestoreBlockHandler() ); + g_pGameSaveRestoreBlockSet->RemoveBlockHandler( GetPhysSaveRestoreBlockHandler() ); + g_pGameSaveRestoreBlockSet->RemoveBlockHandler( GetEntitySaveRestoreBlockHandler() ); + + char *pFilename = g_TextStatsMgr.GetStatsFilename(); + if ( !pFilename || !pFilename[0] ) + { + g_TextStatsMgr.SetStatsFilename( "stats.txt" ); + } + g_TextStatsMgr.WriteFile( filesystem ); + + IGameSystem::ShutdownAllSystems(); + +#ifdef _WIN32 + if ( enginevgui ) + { + VGui_Shutdown(); + } +#endif + +#ifdef CSTRIKE_DLL // BOTPORT: TODO: move these ifdefs out + RemoveBotControl(); +#endif + +#ifndef _XBOX + // destroy the Navigation Mesh interface + if (TheNavMesh) + { + delete TheNavMesh; + TheNavMesh = NULL; + } +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: See shareddefs.h for redefining this. Don't even think about it, though, for HL2. Or you will pay. ywb 9/22/03 +// Output : float +//----------------------------------------------------------------------------- + +float CServerGameDLL::GetTickInterval( void ) const +{ + float tickinterval = DEFAULT_TICK_INTERVAL; + +#if defined( CSTRIKE_DLL ) + // in CS reduce tickrate/sec by defualt + tickinterval *= 2; +#endif + + // override if tick rate specified in command line + if ( CommandLine()->CheckParm( "-tickrate" ) ) + { + float tickrate = CommandLine()->ParmValue( "-tickrate", 0 ); + if ( tickrate > 10 ) + tickinterval = 1.0f / tickrate; + } + + return tickinterval; +} + +// This is called when a new game is started. (restart, map) +bool CServerGameDLL::GameInit( void ) +{ + ResetGlobalState(); + engine->ServerCommand( "exec game.cfg\n" ); + engine->ServerExecute( ); + // clear out any old game's temporary save data +// engine->ClearSaveDir(); + return true; +} + +// This is called when a game ends (server disconnect, death, restart, load) +// NOT on level transitions within a game +void CServerGameDLL::GameShutdown( void ) +{ + ResetGlobalState(); +} + +static bool g_OneWayTransition = false; +void Game_SetOneWayTransition( void ) +{ + g_OneWayTransition = true; +} + +static CUtlVector g_RestoredEntities; +// just for debugging, assert that this is the only time this function is called +static bool g_InRestore = false; + +void AddRestoredEntity( CBaseEntity *pEntity ) +{ + Assert(g_InRestore); + if ( !pEntity ) + return; + + g_RestoredEntities.AddToTail( EHANDLE(pEntity) ); +} + +void EndRestoreEntities() +{ + if ( !g_InRestore ) + return; + + // Call all entities' OnRestore handlers + for ( int i = g_RestoredEntities.Count()-1; i >=0; --i ) + { + CBaseEntity *pEntity = g_RestoredEntities[i].Get(); + if ( pEntity && !pEntity->IsDormant() ) + { + MDLCACHE_CRITICAL_SECTION(); + pEntity->OnRestore(); + } + } + + g_RestoredEntities.Purge(); + + IGameSystem::OnRestoreAllSystems(); + + g_InRestore = false; + gEntList.CleanupDeleteList(); + + // HACKHACK: UNDONE: We need to redesign the main loop with respect to save/load/server activate + g_ServerGameDLL.ServerActivate( NULL, 0, 0 ); + CBaseEntity::SetAllowPrecache( false ); +} + +void BeginRestoreEntities() +{ + if ( g_InRestore ) + { + DevMsg( "BeginRestoreEntities without previous EndRestoreEntities.\n" ); + gEntList.CleanupDeleteList(); + } + g_RestoredEntities.Purge(); + g_InRestore = true; + CBaseEntity::SetAllowPrecache( true ); +} + +//----------------------------------------------------------------------------- +// Purpose: This prevents sv.tickcount/gpGlobals->tickcount from advancing during restore which +// would cause a lot of the NPCs to fast forward their think times to the same +// tick due to some ticks being elapsed during restore where there was no simulation going on +//----------------------------------------------------------------------------- +bool CServerGameDLL::IsRestoring() +{ + return g_InRestore; +} + +// Called any time a new level is started (after GameInit() also on level transitions within a game) +bool CServerGameDLL::LevelInit( const char *pMapName, char const *pMapEntities, char const *pOldLevel, char const *pLandmarkName, bool loadGame, bool background ) +{ + VPROF("CServerGameDLL::LevelInit"); + ResetWindspeed(); + UpdateChapterRestrictions( pMapName ); + + // IGameSystem::LevelInitPreEntityAllSystems() is called when the world is precached + // That happens either in LoadGameState() or in MapEntity_ParseAllEntities() + if ( loadGame ) + { + BeginRestoreEntities(); + if ( !engine->LoadGameState( pMapName, 1 ) ) + { + MapEntity_ParseAllEntities( pMapEntities ); + } + + if ( pOldLevel ) + { + gpGlobals->eLoadType = MapLoad_Transition; + engine->LoadAdjacentEnts( pOldLevel, pLandmarkName ); + } + else + { + gpGlobals->eLoadType = MapLoad_LoadGame; + } + + if ( g_OneWayTransition ) + { + engine->ClearSaveDirAfterClientLoad(); + } + + if ( pOldLevel && sv_autosave.GetBool() == true ) + { + // This is a single-player style level transition. + // Queue up an autosave one second into the level + CBaseEntity *pAutosave = CBaseEntity::Create( "logic_autosave", vec3_origin, vec3_angle, NULL ); + if ( pAutosave ) + { + g_EventQueue.AddEvent( pAutosave, "Save", 1.0, NULL, NULL ); + g_EventQueue.AddEvent( pAutosave, "Kill", 1.1, NULL, NULL ); + } + } + } + else + { + if ( background ) + { + gpGlobals->eLoadType = MapLoad_Background; + } + else + { + gpGlobals->eLoadType = MapLoad_NewGame; + } + + LevelInit_ParseAllEntities( pMapEntities ); + } + + // Check low violence settings for this map + g_RagdollLVManager.SetLowViolence( pMapName ); + + // Now that all of the active entities have been loaded in, precache any entities who need point_template parameters + // to be parsed (the above code has loaded all point_template entities) + PrecachePointTemplates(); + + // load MOTD from file into stringtable + LoadMessageOfTheDay(); + + // Sometimes an ent will Remove() itself during its precache, so RemoveImmediate won't happen. + // This makes sure those ents get cleaned up. + gEntList.CleanupDeleteList(); + + g_AIFriendliesTalkSemaphore.Release(); + g_AIFoesTalkSemaphore.Release(); + g_OneWayTransition = false; + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: called after every level change and load game, iterates through all the +// active entities and gives them a chance to fix up their state +//----------------------------------------------------------------------------- +#ifdef DEBUG +bool g_bReceivedChainedActivate; +bool g_bCheckForChainedActivate; +#define BeginCheckChainedActivate() if (0) ; else { g_bCheckForChainedActivate = true; g_bReceivedChainedActivate = false; } +#define EndCheckChainedActivate( bCheck ) \ + if (0) ; else \ + { \ + if ( bCheck ) \ + { \ + char msg[ 1024 ]; \ + Q_snprintf( msg, sizeof( msg ), "Entity (%i/%s/%s) failed to call base class Activate()\n", pClass->entindex(), pClass->GetClassname(), STRING( pClass->GetEntityName() ) ); \ + AssertMsg( g_bReceivedChainedActivate == true, msg ); \ + } \ + g_bCheckForChainedActivate = false; \ + } +#else +#define BeginCheckChainedActivate() ((void)0) +#define EndCheckChainedActivate( bCheck ) ((void)0) +#endif + +void CServerGameDLL::ServerActivate( edict_t *pEdictList, int edictCount, int clientMax ) +{ + // HACKHACK: UNDONE: We need to redesign the main loop with respect to save/load/server activate + if ( g_InRestore ) + return; + + if ( gEntList.ResetDeleteList() != 0 ) + { + Msg( "ERROR: Entity delete queue not empty on level start!\n" ); + } + + for ( CBaseEntity *pClass = gEntList.FirstEnt(); pClass != NULL; pClass = gEntList.NextEnt(pClass) ) + { + if ( pClass && !pClass->IsDormant() ) + { + MDLCACHE_CRITICAL_SECTION(); + + BeginCheckChainedActivate(); + pClass->Activate(); + + // We don't care if it finished activating if it decided to remove itself. + EndCheckChainedActivate( !( pClass->GetEFlags() & EFL_KILLME ) ); + } + } + + IGameSystem::LevelInitPostEntityAllSystems(); + // No more precaching after PostEntityAllSystems!!! + CBaseEntity::SetAllowPrecache( false ); + + // only display the think limit when the game is run with "developer" mode set + if ( !g_pDeveloper->GetInt() ) + { + think_limit.SetValue( 0 ); + } + +#ifndef _XBOX + // load the Navigation Mesh for this map + TheNavMesh->Load(); +#endif + +#ifdef CSTRIKE_DLL // BOTPORT: TODO: move these ifdefs out + TheBots->ServerActivate(); +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: Called at the start of every game frame +//----------------------------------------------------------------------------- +ConVar trace_report( "trace_report", "0" ); + +void CServerGameDLL::GameFrame( bool simulating ) +{ + VPROF( "CServerGameDLL::GameFrame" ); + + // Don't run frames until fully restored + if ( g_InRestore ) + return; + + static bool skipframe = false; + + // If server is skipping frames, don't run simulation this time through + if ( skipframe ) + { + UpdateAllClientData(); + skipframe = false; + return; + } + + float oldframetime = gpGlobals->frametime; + if ( CBaseEntity::IsSimulatingOnAlternateTicks() ) + { + skipframe = true; + // If we're skipping frames, then the frametime is 2x the normal tick + gpGlobals->frametime *= 2.0f; + } + +#ifdef _DEBUG + // For profiling.. let them enable/disable the networkvar manual mode stuff. + g_bUseNetworkVars = s_UseNetworkVars.GetBool(); +#endif + + extern void GameStartFrame( void ); + extern void ServiceEventQueue( void ); + extern void Physics_RunThinkFunctions( bool simulating ); + + // Delete anything that was marked for deletion + // outside of server frameloop (e.g., in response to concommand) + gEntList.CleanupDeleteList(); + + IGameSystem::FrameUpdatePreEntityThinkAllSystems(); + GameStartFrame(); + +#ifndef _XBOX + TheNavMesh->Update(); + + gamestatsuploader->UpdateConnection(); +#endif + + Physics_RunThinkFunctions( simulating ); + + IGameSystem::FrameUpdatePostEntityThinkAllSystems(); + + // UNDONE: Make these systems IGameSystems and move these calls into FrameUpdatePostEntityThink() + // service event queue, firing off any actions whos time has come + ServiceEventQueue(); + + // free all ents marked in think functions + gEntList.CleanupDeleteList(); + + // FIXME: Should this only occur on the final tick? + UpdateAllClientData(); + + if ( g_pGameRules ) + { + g_pGameRules->EndGameFrame(); + } + + if ( trace_report.GetBool() ) + { + int total = 0, totals[3]; + for ( int i = 0; i < 3; i++ ) + { + totals[i] = enginetrace->GetStatByIndex( i, true ); + if ( totals[i] > 0 ) + { + total += totals[i]; + } + } + + if ( total ) + { + Msg("Trace: %d, contents %d, enumerate %d\n", totals[0], totals[1], totals[2] ); + } + } + + // Any entities that detect network state changes on a timer do it here. + g_NetworkPropertyEventMgr.FireEvents(); + + gpGlobals->frametime = oldframetime; +} + +//----------------------------------------------------------------------------- +// Purpose: Called every frame even if not ticking +// Input : simulating - +//----------------------------------------------------------------------------- +void CServerGameDLL::PreClientUpdate( bool simulating ) +{ + if ( !simulating ) + return; + + /* + if (game_speeds.GetInt()) + { + DrawMeasuredSections(); + } + */ + +//#ifdef _DEBUG - allow this in release for now + DrawAllDebugOverlays(); +//#endif + + IGameSystem::PreClientUpdateAllSystems(); + + if ( sv_showhitboxes.GetInt() == -1 ) + return; + + if ( sv_showhitboxes.GetInt() == 0 ) + { + // assume it's text + CBaseEntity *pEntity = NULL; + + while (1) + { + pEntity = gEntList.FindEntityByName( pEntity, sv_showhitboxes.GetString() ); + if ( !pEntity ) + break; + + CBaseAnimating *anim = dynamic_cast< CBaseAnimating * >( pEntity ); + + if (anim) + { + anim->DrawServerHitboxes(); + } + } + return; + } + + CBaseAnimating *anim = dynamic_cast< CBaseAnimating * >( CBaseEntity::Instance( engine->PEntityOfEntIndex( sv_showhitboxes.GetInt() ) ) ); + if ( !anim ) + return; + + anim->DrawServerHitboxes(); +} + +void CServerGameDLL::Think( bool finalTick ) +{ + if ( m_fAutoSaveDangerousTime != 0.0f && m_fAutoSaveDangerousTime < gpGlobals->curtime ) + { + // The safety timer for a dangerous auto save has expired + CBasePlayer *pPlayer = UTIL_PlayerByIndex( 1 ); + + if ( pPlayer->GetDeathTime() == 0.0f || pPlayer->GetDeathTime() > gpGlobals->curtime ) + { + // The player isn't dead, so make the dangerous auto save safe + engine->ServerCommand( "autosavedangerousissafe\n" ); + } + + m_fAutoSaveDangerousTime = 0.0f; + } +} + + +// Called when a level is shutdown (including changing levels) +void CServerGameDLL::LevelShutdown( void ) +{ + IGameSystem::LevelShutdownPreEntityAllSystems(); + + // YWB: + // This entity pointer is going away now and is corrupting memory on level transitions/restarts + CSoundEnt::ShutdownSoundEnt(); + + gEntList.Clear(); + + IGameSystem::LevelShutdownPostEntityAllSystems(); + + // In case we quit out during initial load + CBaseEntity::SetAllowPrecache( false ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : +// Output : ServerClass* +//----------------------------------------------------------------------------- +ServerClass* CServerGameDLL::GetAllServerClasses() +{ + return g_pServerClassHead; +} + + +const char *CServerGameDLL::GetGameDescription( void ) +{ + return ::GetGameDescription(); +} + +void CServerGameDLL::CreateNetworkStringTables( void ) +{ + // Create any shared string tables here (and only here!) + // E.g.: xxx = networkstringtable->CreateStringTable( "SceneStrings", 512 ); + g_pStringTableEffectDispatch = networkstringtable->CreateStringTable( "EffectDispatch", MAX_EFFECT_DISPATCH_STRINGS ); + g_pStringTableVguiScreen = networkstringtable->CreateStringTable( "VguiScreen", MAX_VGUI_SCREEN_STRINGS ); + g_pStringTableMaterials = networkstringtable->CreateStringTable( "Materials", MAX_MATERIAL_STRINGS ); + g_pStringTableInfoPanel = networkstringtable->CreateStringTable( "InfoPanel", MAX_INFOPANEL_STRINGS ); + g_pStringTableClientSideChoreoScenes = networkstringtable->CreateStringTable( "Scenes", MAX_CHOREO_SCENES_STRINGS ); + + Assert( g_pStringTableEffectDispatch && + g_pStringTableVguiScreen && + g_pStringTableMaterials && + g_pStringTableInfoPanel && + g_pStringTableClientSideChoreoScenes ); + + // Need this so we have the error material always handy + PrecacheMaterial( "debug/debugempty" ); + Assert( GetMaterialIndex( "debug/debugempty" ) == 0 ); + + CreateNetworkStringTables_GameRules(); + + // Set up save/load utilities for string tables + g_VguiScreenStringOps.Init( g_pStringTableVguiScreen ); +} + +CSaveRestoreData *CServerGameDLL::SaveInit( int size ) +{ + return ::SaveInit(size); +} + +//----------------------------------------------------------------------------- +// Purpose: Saves data from a struct into a saverestore object, to be saved to disk +// Input : *pSaveData - the saverestore object +// char *pname - the name of the data to write +// *pBaseData - the struct into which the data is to be read +// *pFields - pointer to an array of data field descriptions +// fieldCount - the size of the array (number of field descriptions) +//----------------------------------------------------------------------------- +void CServerGameDLL::SaveWriteFields( CSaveRestoreData *pSaveData, const char *pname, void *pBaseData, datamap_t *pMap, typedescription_t *pFields, int fieldCount ) +{ + CSave saveHelper( pSaveData ); + saveHelper.WriteFields( pname, pBaseData, pMap, pFields, fieldCount ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Reads data from a save/restore block into a structure +// Input : *pSaveData - the saverestore object +// char *pname - the name of the data to extract from +// *pBaseData - the struct into which the data is to be restored +// *pFields - pointer to an array of data field descriptions +// fieldCount - the size of the array (number of field descriptions) +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- + +void CServerGameDLL::SaveReadFields( CSaveRestoreData *pSaveData, const char *pname, void *pBaseData, datamap_t *pMap, typedescription_t *pFields, int fieldCount ) +{ + CRestore restoreHelper( pSaveData ); + restoreHelper.ReadFields( pname, pBaseData, pMap, pFields, fieldCount ); +} + +//----------------------------------------------------------------------------- + +void CServerGameDLL::SaveGlobalState( CSaveRestoreData *s ) +{ + ::SaveGlobalState(s); +} + +void CServerGameDLL::RestoreGlobalState(CSaveRestoreData *s) +{ + ::RestoreGlobalState(s); +} + +void CServerGameDLL::Save( CSaveRestoreData *s ) +{ + CSave saveHelper( s ); + g_pGameSaveRestoreBlockSet->Save( &saveHelper ); +} + +void CServerGameDLL::Restore( CSaveRestoreData *s, bool b) +{ + CRestore restore(s); + g_pGameSaveRestoreBlockSet->Restore( &restore, b ); + g_pGameSaveRestoreBlockSet->PostRestore(); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : msg_type - +// *name - +// size - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- + +bool CServerGameDLL::GetUserMessageInfo( int msg_type, char *name, int maxnamelength, int& size ) +{ + if ( !usermessages->IsValidIndex( msg_type ) ) + return false; + + Q_strncpy( name, usermessages->GetUserMessageName( msg_type ), maxnamelength ); + size = usermessages->GetUserMessageSize( msg_type ); + return true; +} + +CStandardSendProxies* CServerGameDLL::GetStandardSendProxies() +{ + return &g_StandardSendProxies; +} + +int CServerGameDLL::CreateEntityTransitionList( CSaveRestoreData *s, int a) +{ + CRestore restoreHelper( s ); + // save off file base + int base = restoreHelper.GetReadPos(); + + int movedCount = ::CreateEntityTransitionList(s, a); + if ( movedCount ) + { + g_pGameSaveRestoreBlockSet->CallBlockHandlerRestore( GetPhysSaveRestoreBlockHandler(), base, &restoreHelper, false ); + g_pGameSaveRestoreBlockSet->CallBlockHandlerRestore( GetAISaveRestoreBlockHandler(), base, &restoreHelper, false ); + } + + GetPhysSaveRestoreBlockHandler()->PostRestore(); + GetAISaveRestoreBlockHandler()->PostRestore(); + + return movedCount; +} + +void CServerGameDLL::PreSave( CSaveRestoreData *s ) +{ + g_pGameSaveRestoreBlockSet->PreSave( s ); +} + +#include "client_textmessage.h" + +// This little hack lets me marry BSP names to messages in titles.txt +typedef struct +{ + char *pBSPName; + char *pTitleName; +} TITLECOMMENT; + +// this list gets searched for the first partial match, so some are out of order +static TITLECOMMENT gTitleComments[] = +{ +#ifdef HL1_DLL + { "t0a0", "#T0A0TITLE" }, + { "c0a0", "#HL1_Chapter1_Title" }, + { "c1a0", "#HL1_Chapter2_Title" }, + { "c1a1", "#HL1_Chapter3_Title" }, + { "c1a2", "#HL1_Chapter4_Title" }, + { "c1a3", "#HL1_Chapter5_Title" }, + { "c1a4", "#HL1_Chapter6_Title" }, + { "c2a1", "#HL1_Chapter7_Title" }, + { "c2a2", "#HL1_Chapter8_Title" }, + { "c2a3", "#HL1_Chapter9_Title" }, + { "c2a4d", "#HL1_Chapter11_Title" }, // These must appear before "C2A4" so all other map names starting with C2A4 get that title + { "c2a4e", "#HL1_Chapter11_Title" }, + { "c2a4f", "#HL1_Chapter11_Title" }, + { "c2a4g", "#HL1_Chapter11_Title" }, + { "c2a4", "#HL1_Chapter10_Title" }, + { "c2a5", "#HL1_Chapter12_Title" }, + { "c3a1", "#HL1_Chapter13_Title" }, + { "c3a2", "#HL1_Chapter14_Title" }, + { "c4a1a", "#HL1_Chapter17_Title" }, // Order is important, see above + { "c4a1b", "#HL1_Chapter17_Title" }, + { "c4a1c", "#HL1_Chapter17_Title" }, + { "c4a1d", "#HL1_Chapter17_Title" }, + { "c4a1e", "#HL1_Chapter17_Title" }, + { "c4a1", "#HL1_Chapter15_Title" }, + { "c4a2", "#HL1_Chapter16_Title" }, + { "c4a3", "#HL1_Chapter18_Title" }, + { "c5a1", "#HL1_Chapter19_Title" }, +#else + { "intro", "#HL2_Chapter1_Title" }, + + { "d1_trainstation_05", "#HL2_Chapter2_Title" }, + { "d1_trainstation_06", "#HL2_Chapter2_Title" }, + + { "d1_trainstation_", "#HL2_Chapter1_Title" }, + + { "d1_canals_06a", "#HL2_Chapter4_Title" }, + { "d1_canals_06b", "#HL2_Chapter4_Title" }, + { "d1_canals_07a", "#HL2_Chapter4_Title" }, + { "d1_canals_07b", "#HL2_Chapter4_Title" }, + { "d1_canals_08", "#HL2_Chapter4_Title" }, + { "d1_canals_09", "#HL2_Chapter4_Title" }, + { "d1_canals_1", "#HL2_Chapter4_Title" }, + + { "d1_canals_0", "#HL2_Chapter3_Title" }, + + { "d1_eli_", "#HL2_Chapter5_Title" }, + + { "d1_town_", "#HL2_Chapter6_Title" }, + + { "d2_coast_09a", "#HL2_Chapter8_Title" }, + { "d2_coast_09b", "#HL2_Chapter8_Title" }, + { "d2_coast_1", "#HL2_Chapter8_Title" }, + { "d2_prison_01", "#HL2_Chapter8_Title" }, + + { "d2_coast_", "#HL2_Chapter7_Title" }, + + { "d2_prison_06a", "#HL2_Chapter10_Title" }, + { "d2_prison_06b", "#HL2_Chapter10_Title" }, + { "d2_prison_07a", "#HL2_Chapter10_Title" }, + { "d2_prison_07b", "#HL2_Chapter10_Title" }, + { "d2_prison_08", "#HL2_Chapter10_Title" }, + { "d3_c17_01", "#HL2_Chapter10_Title" }, + + { "d2_prison_", "#HL2_Chapter9_Title" }, + + { "d3_c17_09", "#HL2_Chapter12_Title" }, + { "d3_c17_1", "#HL2_Chapter12_Title" }, + + { "d3_c17_", "#HL2_Chapter11_Title" }, + + { "d3_citadel_", "#HL2_Chapter13_Title" }, + + { "d3_breen_", "#HL2_Chapter14_Title" }, + { "credits", "#HL2_Chapter15_Title" }, + + { "ep1_citadel_00", "#episodic_Chapter1_Title" }, + { "ep1_citadel_01", "#episodic_Chapter1_Title" }, + { "ep1_citadel_02", "#episodic_Chapter1_Title" }, + { "ep1_citadel_02b", "#episodic_Chapter1_Title" }, + { "ep1_citadel_03", "#episodic_Chapter2_Title" }, + { "ep1_citadel_04", "#episodic_Chapter2_Title" }, + { "ep1_c17_00", "#episodic_Chapter3_Title" }, + { "ep1_c17_00a", "#episodic_Chapter3_Title" }, + { "ep1_c17_01", "#episodic_Chapter4_Title" }, + { "ep1_c17_02", "#episodic_Chapter4_Title" }, + { "ep1_c17_02b", "#episodic_Chapter4_Title" }, + { "ep1_c17_05", "#episodic_Chapter5_Title" }, + { "ep1_c17_06", "#episodic_Chapter5_Title" }, +#endif +}; + +#ifdef _XBOX +void CServerGameDLL::GetTitleName( const char *pMapName, char* pTitleBuff, int titleBuffSize ) +{ + // Try to find a matching title comment for this mapname + for ( int i = 0; i < ARRAYSIZE(gTitleComments); i++ ) + { + if ( !Q_strnicmp( pMapName, gTitleComments[i].pBSPName, strlen(gTitleComments[i].pBSPName) ) ) + { + Q_strncpy( pTitleBuff, gTitleComments[i].pTitleName, titleBuffSize ); + return; + } + } + Q_strncpy( pTitleBuff, pMapName, titleBuffSize ); +} +#endif + +void CServerGameDLL::GetSaveCommentEx( char *text, int maxlength, float flMinutes, float flSeconds ) +{ + char comment[64]; + const char *pName; + + char const *mapname = STRING( gpGlobals->mapname ); + + pName = NULL; + + // Try to find a matching title comment for this mapname + for ( size_t i = 0; i < ARRAYSIZE(gTitleComments) && !pName; i++ ) + { + if ( !Q_strnicmp( mapname, gTitleComments[i].pBSPName, strlen(gTitleComments[i].pBSPName) ) ) + { + // found one + int j; + + // Got a message, post-process it to be save name friendly + Q_strncpy( comment, gTitleComments[i].pTitleName, sizeof( comment ) ); + pName = comment; + j = 0; + // Strip out CRs + while ( j < 64 && comment[j] ) + { + if ( comment[j] == '\n' || comment[j] == '\r' ) + comment[j] = 0; + else + j++; + } + break; + } + } + + // If we didn't get one, use the designer's map name, or the BSP name itself + if ( !pName ) + { + pName = mapname; + } + + if ( flMinutes == 0 && flSeconds == 0 ) + { + Q_snprintf( text, maxlength, "%-64.64s", pName ); + } + else + { + int totalSeconds = static_cast(gpGlobals->curtime + flSeconds); + int minutes = static_cast(( totalSeconds / 60.0f ) + flMinutes); + int seconds = static_cast(fmod( totalSeconds, 60.0f )); + + // Wow, this guy/gal must suck...! + if ( minutes >= 1000 ) + { + minutes = 999; + seconds = 59; + } + + // add the elapsed time at the end of the comment, for the ui to parse out + Q_snprintf( text, maxlength, "%-64.64s %03d:%02d", pName, minutes, seconds ); + } +} + +void CServerGameDLL::WriteSaveHeaders( CSaveRestoreData *s ) +{ + CSave saveHelper( s ); + g_pGameSaveRestoreBlockSet->WriteSaveHeaders( &saveHelper ); + g_pGameSaveRestoreBlockSet->PostSave(); +} + +void CServerGameDLL::ReadRestoreHeaders( CSaveRestoreData *s ) +{ + CRestore restoreHelper( s ); + g_pGameSaveRestoreBlockSet->PreRestore(); + g_pGameSaveRestoreBlockSet->ReadRestoreHeaders( &restoreHelper ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Called during a transition, to build a map adjacency list +//----------------------------------------------------------------------------- +void CServerGameDLL::BuildAdjacentMapList( void ) +{ + // retrieve the pointer to the save data + CSaveRestoreData *pSaveData = gpGlobals->pSaveData; + + if ( pSaveData ) + pSaveData->levelInfo.connectionCount = BuildChangeList( pSaveData->levelInfo.levelList, MAX_LEVEL_CONNECTIONS ); +} + +//----------------------------------------------------------------------------- +// Purpose: Sanity-check to verify that a path is a relative path inside the game dir +// Taken From: engine/cmd.cpp +//----------------------------------------------------------------------------- +static bool IsValidPath( const char *pszFilename ) +{ + if ( !pszFilename ) + { + return false; + } + + if ( Q_strlen( pszFilename ) <= 0 || + Q_IsAbsolutePath( pszFilename ) || // to protect absolute paths + Q_strstr( pszFilename, ".." ) ) // to protect relative paths + { + return false; + } + + return true; +} + +static void ValidateMOTDFilename( ConVar *var, const char *oldValue ) +{ + if ( !IsValidPath( var->GetString() ) ) + { + var->Revert(); + } +} + +static ConVar motdfile( "motdfile", "motd.txt", 0, "The MOTD file to load.", ValidateMOTDFilename ); +void CServerGameDLL::LoadMessageOfTheDay() +{ +#ifndef _XBOX + char data[2048]; + + int length = filesystem->Size( motdfile.GetString(), "GAME" ); + + if ( length <= 0 || length >= static_cast((sizeof(data)-1)) ) + { + DevMsg("Invalid file size for %s\n", motdfile.GetString() ); + return; + } + + FileHandle_t hFile = filesystem->Open( motdfile.GetString(), "rb", "GAME" ); + + if ( hFile == FILESYSTEM_INVALID_HANDLE ) + return; + + filesystem->Read( data, length, hFile ); + filesystem->Close( hFile ); + + data[length] = 0; + + g_pStringTableInfoPanel->AddString( "motd", length+1, data ); +#endif +} + +// keeps track of which chapters the user has unlocked +ConVar sv_unlockedchapters( "sv_unlockedchapters", "1", FCVAR_ARCHIVE | FCVAR_ARCHIVE_XBOX ); + +//----------------------------------------------------------------------------- +// Purpose: Updates which chapters are unlocked +//----------------------------------------------------------------------------- +void UpdateChapterRestrictions( const char *mapname ) +{ + // look at the chapter for this map + char chapterTitle[64]; + chapterTitle[0] = 0; + for ( size_t i = 0; i < ARRAYSIZE(gTitleComments); i++ ) + { + if ( !Q_strnicmp( mapname, gTitleComments[i].pBSPName, strlen(gTitleComments[i].pBSPName) ) ) + { + // found + Q_strncpy( chapterTitle, gTitleComments[i].pTitleName, sizeof( chapterTitle ) ); + int j = 0; + while ( j < 64 && chapterTitle[j] ) + { + if ( chapterTitle[j] == '\n' || chapterTitle[j] == '\r' ) + chapterTitle[j] = 0; + else + j++; + } + + break; + } + } + + if ( !chapterTitle[0] ) + return; + + // make sure the specified chapter title is unlocked + Q_strlower( chapterTitle ); + + const char *pGameDir = CommandLine()->ParmValue( "-game", "hl2" ); + + char chapterNumberPrefix[64]; + Q_snprintf(chapterNumberPrefix, sizeof(chapterNumberPrefix), "#%s_chapter", pGameDir); + + const char *newChapterNumber = strstr( chapterTitle, chapterNumberPrefix ); + if ( newChapterNumber ) + { + // cut off the front + newChapterNumber += strlen( chapterNumberPrefix ); + char newChapter[32]; + Q_strncpy( newChapter, newChapterNumber, sizeof(newChapter) ); + + // cut off the end + char *end = strstr( newChapter, "_title" ); + if ( end ) + { + *end = 0; + } + + // ok we have the string, see if it's newer + const char *unlockedChapter = sv_unlockedchapters.GetString(); + if ( atoi(unlockedChapter) < atoi(newChapter) + || (atoi(unlockedChapter) == atoi(newChapter) && stricmp(unlockedChapter, newChapter) < 0) ) + { + // ok we're at a higher chapter, unlock + sv_unlockedchapters.SetValue( newChapter ); + } + } +} + +//----------------------------------------------------------------------------- +// Precaches a vgui screen overlay material +//----------------------------------------------------------------------------- +void PrecacheMaterial( const char *pMaterialName ) +{ + Assert( pMaterialName && pMaterialName[0] ); + g_pStringTableMaterials->AddString( pMaterialName ); +} + + +//----------------------------------------------------------------------------- +// Converts a previously precached material into an index +//----------------------------------------------------------------------------- +int GetMaterialIndex( const char *pMaterialName ) +{ + if (pMaterialName) + { + int nIndex = g_pStringTableMaterials->FindStringIndex( pMaterialName ); + + if (nIndex != INVALID_STRING_INDEX ) + { + return nIndex; + } + else + { + DevMsg("Warning! GetMaterialIndex: couldn't find material %s\n ", pMaterialName ); + return 0; + } + } + + // This is the invalid string index + return 0; +} + +//----------------------------------------------------------------------------- +// Converts a previously precached material index into a string +//----------------------------------------------------------------------------- +const char *GetMaterialNameFromIndex( int nMaterialIndex ) +{ + return g_pStringTableMaterials->GetString( nMaterialIndex ); +} + + +class CServerGameEnts : public IServerGameEnts +{ +public: + virtual void SetDebugEdictBase(edict_t *base); + virtual void MarkEntitiesAsTouching( edict_t *e1, edict_t *e2 ); + virtual void FreeContainingEntity( edict_t * ); + virtual edict_t* BaseEntityToEdict( CBaseEntity *pEnt ); + virtual CBaseEntity* EdictToBaseEntity( edict_t *pEdict ); + virtual void CheckTransmit( CCheckTransmitInfo *pInfo, const unsigned short *pEdictIndices, int nEdicts ); +}; +EXPOSE_SINGLE_INTERFACE(CServerGameEnts, IServerGameEnts, INTERFACEVERSION_SERVERGAMEENTS); + +void CServerGameEnts::SetDebugEdictBase(edict_t *base) +{ + g_pDebugEdictBase = base; +} + +//----------------------------------------------------------------------------- +// Purpose: Marks entities as touching +// Input : *e1 - +// *e2 - +//----------------------------------------------------------------------------- +void CServerGameEnts::MarkEntitiesAsTouching( edict_t *e1, edict_t *e2 ) +{ + CBaseEntity *entity = GetContainingEntity( e1 ); + CBaseEntity *entityTouched = GetContainingEntity( e2 ); + if ( entity && entityTouched ) + { + // HACKHACK: UNDONE: Pass in the trace here??!?!? + trace_t tr; + UTIL_ClearTrace( tr ); + tr.endpos = (entity->GetAbsOrigin() + entityTouched->GetAbsOrigin()) * 0.5; + entity->PhysicsMarkEntitiesAsTouching( entityTouched, tr ); + } +} + +void CServerGameEnts::FreeContainingEntity( edict_t *e ) +{ + ::FreeContainingEntity(e); +} + +edict_t* CServerGameEnts::BaseEntityToEdict( CBaseEntity *pEnt ) +{ + if ( pEnt ) + return pEnt->edict(); + else + return NULL; +} + +CBaseEntity* CServerGameEnts::EdictToBaseEntity( edict_t *pEdict ) +{ + if ( pEdict ) + return CBaseEntity::Instance( pEdict ); + else + return NULL; +} + + +/* Yuck.. ideally this would be in CServerNetworkProperty's header, but it requires CBaseEntity and +// inlining it gives a nice speedup. +inline void CServerNetworkProperty::CheckTransmit( CCheckTransmitInfo *pInfo ) +{ + // If we have a transmit proxy, let it hook our ShouldTransmit return value. + if ( m_pTransmitProxy ) + { + nShouldTransmit = m_pTransmitProxy->ShouldTransmit( pInfo, nShouldTransmit ); + } + + if ( m_pOuter->ShouldTransmit( pInfo ) ) + { + m_pOuter->SetTransmit( pInfo ); + } +} */ + +void CServerGameEnts::CheckTransmit( CCheckTransmitInfo *pInfo, const unsigned short *pEdictIndices, int nEdicts ) +{ + // NOTE: for speed's sake, this assumes that all networkables are CBaseEntities and that the edict list + // is consecutive in memory. If either of these things change, then this routine needs to change, but + // ideally we won't be calling any virtual from this routine. This speedy routine was added as an + // optimization which would be nice to keep. + edict_t *pBaseEdict = engine->PEntityOfEntIndex( 0 ); + + // get recipient player's skybox: + CBaseEntity *pRecipientEntity = CBaseEntity::Instance( pInfo->m_pClientEnt ); + + Assert( pRecipientEntity && pRecipientEntity->IsPlayer() ); + + if ( !pRecipientEntity ) + return; + + MDLCACHE_CRITICAL_SECTION(); + CBasePlayer *pRecipientPlayer = static_cast( pRecipientEntity ); + + const int skyBoxArea = pRecipientPlayer->m_Local.m_skybox3d.area; + const bool bIsHLTV = pRecipientPlayer->IsHLTV(); + + // m_pTransmitAlways must be set if HLTV client + Assert( bIsHLTV == ( pInfo->m_pTransmitAlways != NULL) ); + + // int dontSend = 0; int always = 0; int fullCheck = 0; int PVS = 0; + + + for ( int i=0; i < nEdicts; i++ ) + { + int iEdict = pEdictIndices[i]; + + edict_t *pEdict = &pBaseEdict[iEdict]; + Assert( pEdict == engine->PEntityOfEntIndex( iEdict ) ); + int nFlags = pEdict->m_fStateFlags & (FL_EDICT_DONTSEND|FL_EDICT_ALWAYS|FL_EDICT_PVSCHECK|FL_EDICT_FULLCHECK); + + if ( nFlags & FL_EDICT_DONTSEND ) + { + // entity needs no transmit + continue; + } + + if ( pInfo->m_pTransmitEdict->Get( iEdict ) ) + { + // entity is already marked for sending + continue; + } + else if ( pEdict->m_fStateFlags & FL_EDICT_DONTSEND ) + { + continue; + } + + if ( nFlags & FL_EDICT_ALWAYS ) + { + while ( true ) + { + // mark entity for sending + pInfo->m_pTransmitEdict->Set( iEdict ); + + if ( bIsHLTV ) + { + pInfo->m_pTransmitAlways->Set( iEdict ); + } + + CBaseEntity *pEnt = (CBaseEntity*)pEdict->GetUnknown(); + if ( !pEnt ) + break; + + CBaseEntity *pParent = pEnt->GetMoveParent(); + if ( !pParent ) + break; + + pEdict = pParent->edict(); + iEdict = pParent->entindex(); + } + + + continue; + } + + // get the Baseentity + + CBaseEntity *pEnt = ( CBaseEntity * )pEdict->GetUnknown(); + Assert( dynamic_cast< CBaseEntity* >( pEdict->GetUnknown() ) == pEnt ); + + if ( nFlags == FL_EDICT_FULLCHECK ) + { + // do a full ShouldTransmit() check, may return FL_EDICT_CHECKPVS + nFlags = pEnt->ShouldTransmit( pInfo ); + + Assert( !(nFlags & FL_EDICT_FULLCHECK) ); + + if ( nFlags & FL_EDICT_ALWAYS ) + { + pEnt->SetTransmit( pInfo, true ); + continue; + } + } + + if ( !( nFlags & FL_EDICT_PVSCHECK ) ) + { + // dont send this entity + continue; + } + + CServerNetworkProperty *netProp = pEnt->NetworkProp(); + if ( bIsHLTV ) + { + // for the HLTV we don't cull against PVS + if ( netProp->AreaNum() == skyBoxArea ) + { + pEnt->SetTransmit( pInfo, true ); + } + else + { + pEnt->SetTransmit( pInfo, false ); + } + continue; + } + + + bool bSameAreaAsSky = netProp->AreaNum() == skyBoxArea; + // Always send entities in the player's 3d skybox. + // Sidenote: call of AreaNum() ensures that PVS data is up to date for this entity + if ( bSameAreaAsSky ) + { + pEnt->SetTransmit( pInfo, true ); + } + else + { + bool bInPVS = netProp->IsInPVS( pInfo ); + if ( bInPVS ) + { + // only send if entity is in PVS + pEnt->SetTransmit( pInfo, false ); + } + else + { + // If the entity is marked "check PVS" but it's in hierarchy, walk up the hierarchy looking for the + // for any parent which is also in the PVS. If none are found, then we don't need to worry about sending ourself + CBaseEntity *orig = pEnt; + CBaseEntity *check = pEnt->GetMoveParent(); + + // BUG BUG: I think it might be better to build up a list of edict indices which "depend" on other answers and then + // resolve them in a second pass. Not sure what happens if an entity has two parents who both request PVS check? + + while ( check ) + { + int checkIndex = check->entindex(); + + // Parent already being sent + if ( pInfo->m_pTransmitEdict->Get( checkIndex ) ) + { + orig->SetTransmit( pInfo, true ); + break; + } + + edict_t *checkEdict = check->edict(); + int checkFlags = checkEdict->m_fStateFlags & (FL_EDICT_DONTSEND|FL_EDICT_ALWAYS|FL_EDICT_PVSCHECK|FL_EDICT_FULLCHECK); + if ( checkFlags & FL_EDICT_DONTSEND ) + { + break; + } + if ( checkFlags & FL_EDICT_ALWAYS ) + { + orig->SetTransmit( pInfo, true ); + break; + } + if ( checkFlags == FL_EDICT_FULLCHECK ) + { + // do a full ShouldTransmit() check, may return FL_EDICT_CHECKPVS + nFlags = check->ShouldTransmit( pInfo ); + Assert( !(nFlags & FL_EDICT_FULLCHECK) ); + if ( nFlags & FL_EDICT_ALWAYS ) + { + check->SetTransmit( pInfo, true ); + orig->SetTransmit( pInfo, true ); + } + break; + } + if ( checkFlags & FL_EDICT_PVSCHECK ) + { + // Check pvs + CServerNetworkProperty *netProp = check->NetworkProp(); + netProp->RecomputePVSInformation(); + bool bMoveParentInPVS = netProp->IsInPVS( pInfo ); + if ( bMoveParentInPVS ) + { + orig->SetTransmit( pInfo, true ); + break; + } + } + + // Continue up chain just in case the parent itself has a parent that's in the PVS... + check = check->GetMoveParent(); + } + } + } + } + +// Msg("A:%i, N:%i, F: %i, P: %i\n", always, dontSend, fullCheck, PVS ); +} + + +CServerGameClients g_ServerGameClients; +EXPOSE_SINGLE_INTERFACE_GLOBALVAR(CServerGameClients, IServerGameClients, INTERFACEVERSION_SERVERGAMECLIENTS, g_ServerGameClients ); + + +//----------------------------------------------------------------------------- +// Purpose: called when a player tries to connect to the server +// Input : *pEdict - the new player +// char *pszName - the players name +// char *pszAddress - the IP address of the player +// reject - output - fill in with the reason why +// maxrejectlen -- sizeof output buffer +// the player was not allowed to connect. +// Output : Returns TRUE if player is allowed to join, FALSE if connection is denied. +//----------------------------------------------------------------------------- +bool CServerGameClients::ClientConnect( edict_t *pEdict, const char *pszName, const char *pszAddress, char *reject, int maxrejectlen ) +{ + return g_pGameRules->ClientConnected( pEdict, pszName, pszAddress, reject, maxrejectlen ); +} + +//----------------------------------------------------------------------------- +// Purpose: Called when a player is fully active (i.e. ready to receive messages) +// Input : *pEntity - the player +//----------------------------------------------------------------------------- +void CServerGameClients::ClientActive( edict_t *pEdict, bool bLoadGame ) +{ + MDLCACHE_CRITICAL_SECTION(); + + ::ClientActive( pEdict, bLoadGame ); + + // If we just loaded from a save file, call OnRestore on valid entities + EndRestoreEntities(); + + if ( gpGlobals->eLoadType != MapLoad_LoadGame ) + { + // notify all entities that the player is now in the game + for ( CBaseEntity *pEntity = gEntList.FirstEnt(); pEntity != NULL; pEntity = gEntList.NextEnt(pEntity) ) + { + pEntity->PostClientActive(); + } + } + + SimThink_SortThinkList(); + // Tell the sound controller to check looping sounds + CBasePlayer *pPlayer = ( CBasePlayer * )CBaseEntity::Instance( pEdict ); + CSoundEnvelopeController::GetController().CheckLoopingSoundsForPlayer( pPlayer ); + SceneManager_ClientActive( pPlayer ); +} + +//----------------------------------------------------------------------------- +// Purpose: called when a player disconnects from a server +// Input : *pEdict - the player +//----------------------------------------------------------------------------- +void CServerGameClients::ClientDisconnect( edict_t *pEdict ) +{ + extern bool g_fGameOver; + + CBasePlayer *player = ( CBasePlayer * )CBaseEntity::Instance( pEdict ); + if ( player ) + { + if ( !g_fGameOver ) + { + player->SetMaxSpeed( 0.0f ); + + CSound *pSound; + pSound = CSoundEnt::SoundPointerForIndex( CSoundEnt::ClientSoundIndex( pEdict ) ); + { + // since this client isn't around to think anymore, reset their sound. + if ( pSound ) + { + pSound->Reset(); + } + } + + // since the edict doesn't get deleted, fix it so it doesn't interfere. + player->RemoveFlag( FL_AIMTARGET ); // don't attract autoaim + player->AddFlag( FL_DONTTOUCH ); // stop it touching anything + player->AddFlag( FL_NOTARGET ); // stop NPCs noticing it + player->AddSolidFlags( FSOLID_NOT_SOLID ); // nonsolid + + if ( g_pGameRules ) + { + g_pGameRules->ClientDisconnected( pEdict ); + } + } + + // Make sure all Untouch()'s are called for this client leaving + CBaseEntity::PhysicsRemoveTouchedList( player ); + CBaseEntity::PhysicsRemoveGroundList( player ); + +#if !defined( NO_ENTITY_PREDICTION ) + // Make sure anything we "own" is simulated by the server from now on + player->ClearPlayerSimulationList(); +#endif + } +} + +void CServerGameClients::ClientPutInServer( edict_t *pEntity, const char *playername ) +{ + if ( g_pClientPutInServerOverride ) + g_pClientPutInServerOverride( pEntity, playername ); + else + ::ClientPutInServer( pEntity, playername ); +} + +void CServerGameClients::ClientCommand( edict_t *pEntity ) +{ + CBasePlayer *player = ToBasePlayer( GetContainingEntity( pEntity ) ); + ::ClientCommand(player); +} + +//----------------------------------------------------------------------------- +// Purpose: called after the player changes userinfo - gives dll a chance to modify +// it before it gets sent into the rest of the engine-> +// Input : *pEdict - the player +// *infobuffer - their infobuffer +//----------------------------------------------------------------------------- +void CServerGameClients::ClientSettingsChanged( edict_t *pEdict ) +{ + // Is the client spawned yet? + if ( !pEdict->GetUnknown() ) + return; + + CBasePlayer *player = ( CBasePlayer * )CBaseEntity::Instance( pEdict ); + + if ( !player ) + return; + +#define QUICKGETCVARVALUE(v) (engine->GetClientConVarValue( player->entindex(), v )) + + // get network setting for prediction & lag compensation + player->m_nUpdateRate = Q_atoi( QUICKGETCVARVALUE("cl_updaterate") ); + + bool useInterpolation = Q_atoi( QUICKGETCVARVALUE("cl_interpolate") ) != 0; + + if ( useInterpolation ) + { + player->m_fLerpTime = Q_atof( QUICKGETCVARVALUE("cl_interp") ); + } + else + { + player->m_fLerpTime = 0.0f; + } + +#if !defined( NO_ENTITY_PREDICTION ) + bool usePrediction = Q_atoi( QUICKGETCVARVALUE("cl_predict")) != 0; + + if ( usePrediction ) + { + player->m_bPredictWeapons = Q_atoi( QUICKGETCVARVALUE("cl_predictweapons")) != 0; + player->m_bLagCompensation = Q_atoi( QUICKGETCVARVALUE("cl_lagcompensation")) != 0; + } + else +#endif + { + player->m_bPredictWeapons = false; + player->m_bLagCompensation = false; + } + + +#undef QUICKGETCVARVALUE + + g_pGameRules->ClientSettingsChanged( player ); +} + + +//----------------------------------------------------------------------------- +// Purpose: A client can have a separate "view entity" indicating that his/her view should depend on the origin of that +// view entity. If that's the case, then pViewEntity will be non-NULL and will be used. Otherwise, the current +// entity's origin is used. Either is offset by the m_vecViewOffset to get the eye position. +// From the eye position, we set up the PAS and PVS to use for filtering network messages to the client. At this point, we could +// override the actual PAS or PVS values, or use a different origin. +// NOTE: Do not cache the values of pas and pvs, as they depend on reusable memory in the engine, they are only good for this one frame +// Input : *pViewEntity - +// *pClient - +// **pvs - +// **pas - +//----------------------------------------------------------------------------- +void CServerGameClients::ClientSetupVisibility( edict_t *pViewEntity, edict_t *pClient, unsigned char *pvs, int pvssize ) +{ + Vector org; + + // Reset the PVS!!! + engine->ResetPVS( pvs, pvssize ); + + // Find the client's PVS + CBaseEntity *pVE = NULL; + if ( pViewEntity ) + { + pVE = GetContainingEntity( pViewEntity ); + // If we have a viewentity, it overrides the player's origin + if ( pVE ) + { + org = pVE->EyePosition(); + engine->AddOriginToPVS( org ); + } + } + + float fovDistanceAdjustFactor = 1; + + CBasePlayer *pPlayer = ( CBasePlayer * )GetContainingEntity( pClient ); + if ( pPlayer ) + { + org = pPlayer->EyePosition(); + pPlayer->SetupVisibility( pVE, pvs, pvssize ); + UTIL_SetClientVisibilityPVS( pClient, pvs, pvssize ); + fovDistanceAdjustFactor = pPlayer->GetFOVDistanceAdjustFactor(); + } + + unsigned char portalBits[MAX_AREA_PORTAL_STATE_BYTES]; + memset( portalBits, 0, sizeof( portalBits ) ); + + int portalNums[512]; + int isOpen[512]; + int iOutPortal = 0; + + for( unsigned short i = g_AreaPortals.Head(); i != g_AreaPortals.InvalidIndex(); i = g_AreaPortals.Next(i) ) + { + CFuncAreaPortalBase *pCur = g_AreaPortals[i]; + + bool bIsOpenOnClient = true; + + // Update our array of which portals are open and flush it if necessary. + portalNums[iOutPortal] = pCur->m_portalNumber; + isOpen[iOutPortal] = pCur->UpdateVisibility( org, fovDistanceAdjustFactor, bIsOpenOnClient ); + ++iOutPortal; + if ( iOutPortal >= static_cast(ARRAYSIZE( portalNums )) ) + { + engine->SetAreaPortalStates( portalNums, isOpen, iOutPortal ); + iOutPortal = 0; + } + + // Version 0 portals (ie: shipping Half-Life 2 era) are always treated as open + // for purposes of the m_chAreaPortalBits array on the client. + if ( pCur->m_iPortalVersion == 0 ) + bIsOpenOnClient = true; + + if ( bIsOpenOnClient ) + { + if ( pCur->m_portalNumber < 0 ) + continue; + else if ( pCur->m_portalNumber >= static_cast((sizeof( portalBits ) * 8)) ) + Error( "ClientSetupVisibility: portal number (%d) too large", pCur->m_portalNumber ); + else + portalBits[pCur->m_portalNumber >> 3] |= (1 << (pCur->m_portalNumber & 7)); + } + } + + // Flush the remaining areaportal states. + engine->SetAreaPortalStates( portalNums, isOpen, iOutPortal ); + + // Update the area bits that get sent to the client. + pPlayer->m_Local.UpdateAreaBits( pPlayer, portalBits ); +} + + + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *player - +// *buf - +// numcmds - +// totalcmds - +// dropped_packets - +// ignore - +// paused - +// Output : float +//----------------------------------------------------------------------------- +float CServerGameClients::ProcessUsercmds( edict_t *player, bf_read *buf, int numcmds, int totalcmds, + int dropped_packets, bool ignore, bool paused ) +{ + int i; + CUserCmd *from, *to; + + // We track last three command in case we drop some + // packets but get them back. + CUserCmd cmds[ CMD_MAXBACKUP ]; + + CUserCmd cmdNull; // For delta compression + + Assert( numcmds >= 0 ); + Assert( ( totalcmds - numcmds ) >= 0 ); + + CBasePlayer *pPlayer = NULL; + CBaseEntity *pEnt = CBaseEntity::Instance(player); + if ( pEnt && pEnt->IsPlayer() ) + { + pPlayer = static_cast< CBasePlayer * >( pEnt ); + } + // Too many commands? + if ( totalcmds < 0 || totalcmds >= ( CMD_MAXBACKUP - 1 ) ) + { + const char *name = "unknown"; + if ( pPlayer ) + { + name = pPlayer->GetPlayerName(); + } + + Msg("CBasePlayer::ProcessUsercmds: too many cmds %i sent for player %s\n", totalcmds, name ); + // FIXME: Need a way to drop the client from here + //SV_DropClient ( host_client, false, "CMD_MAXBACKUP hit" ); + buf->SetOverflowFlag(); + return 0.0f; + } + + // Initialize for reading delta compressed usercmds + cmdNull.Reset(); + from = &cmdNull; + for ( i = totalcmds - 1; i >= 0; i-- ) + { + to = &cmds[ i ]; + ReadUsercmd( buf, to, from ); + from = to; + } + + // Client not fully connected or server has gone inactive or is paused, just ignore + if ( ignore || !pPlayer ) + { + return 0.0f; + } + + MDLCACHE_CRITICAL_SECTION(); + pPlayer->ProcessUsercmds( cmds, numcmds, totalcmds, dropped_packets, paused ); + + return TICK_INTERVAL; +} + + +void CServerGameClients::PostClientMessagesSent( void ) +{ + VPROF("CServerGameClients::PostClient"); + gEntList.PostClientMessagesSent(); +} + +// Sets the client index for the client who typed the command into his/her console +void CServerGameClients::SetCommandClient( int index ) +{ + g_nCommandClientIndex = index; +} + +int CServerGameClients::GetReplayDelay( edict_t *pEdict, int &entity ) +{ + CBasePlayer *pPlayer = ( CBasePlayer * )CBaseEntity::Instance( pEdict ); + + if ( !pPlayer ) + return 0; + + entity = pPlayer->GetReplayEntity(); + + return pPlayer->GetDelayTicks(); +} + + +//----------------------------------------------------------------------------- +// The client's userinfo data lump has changed +//----------------------------------------------------------------------------- +void CServerGameClients::ClientEarPosition( edict_t *pEdict, Vector *pEarOrigin ) +{ + CBasePlayer *pPlayer = ( CBasePlayer * )CBaseEntity::Instance( pEdict ); + if (pPlayer) + { + *pEarOrigin = pPlayer->EarPosition(); + } + else + { + // Shouldn't happen + Assert(0); + *pEarOrigin = vec3_origin; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *player - +// Output : CPlayerState +//----------------------------------------------------------------------------- +CPlayerState *CServerGameClients::GetPlayerState( edict_t *player ) +{ + // Is the client spawned yet? + if ( !player || !player->GetUnknown() ) + return NULL; + + CBasePlayer *pBasePlayer = ( CBasePlayer * )CBaseEntity::Instance( player ); + if ( !pBasePlayer ) + return NULL; + + return &pBasePlayer->pl; +} + +//----------------------------------------------------------------------------- +// Purpose: Anything this game .dll wants to add to the bug reporter text (e.g., the entity/model under the picker crosshair) +// can be added here +// Input : *buf - +// buflen - +//----------------------------------------------------------------------------- +void CServerGameClients::GetBugReportInfo( char *buf, int buflen ) +{ + recentNPCSpeech_t speech[ SPEECH_LIST_MAX_SOUNDS ]; + int num; + int i; + + buf[ 0 ] = 0; + + if ( gpGlobals->maxClients == 1 ) + { + CBaseEntity *ent = FindPickerEntity( UTIL_PlayerByIndex(1) ); + if ( ent ) + { + Q_snprintf( buf, buflen, "Picker %i/%s - ent %s model %s\n", + ent->entindex(), + ent->GetClassname(), + STRING( ent->GetEntityName() ), + STRING( ent->GetModelName() ) ); + } + + // get any sounds that were spoken by NPCs recently + num = GetRecentNPCSpeech( speech ); + if ( num > 0 ) + { + Q_snprintf( buf, buflen, "%sRecent NPC speech:\n", buf ); + for( i = 0; i < num; i++ ) + { + Q_snprintf( buf, buflen, "%s time: %6.3f sound name: %s scene: %s\n", buf, speech[ i ].time, speech[ i ].name, speech[ i ].sceneName ); + } + Q_snprintf( buf, buflen, "%sCurrent time: %6.3f\n", buf, gpGlobals->curtime ); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +static bf_write *g_pMsgBuffer = NULL; + +void EntityMessageBegin( CBaseEntity * entity, bool reliable /*= false*/ ) +{ + Assert( !g_pMsgBuffer ); + + Assert ( entity ); + + g_pMsgBuffer = engine->EntityMessageBegin( entity->entindex(), entity->GetServerClass(), reliable ); +} + +void UserMessageBegin( IRecipientFilter& filter, const char *messagename ) +{ + Assert( !g_pMsgBuffer ); + + Assert( messagename ); + + int msg_type = usermessages->LookupUserMessage( messagename ); + + if ( msg_type == -1 ) + { + Error( "UserMessageBegin: Unregistered message '%s'\n", messagename ); + } + + g_pMsgBuffer = engine->UserMessageBegin( &filter, msg_type ); +} + +void MessageEnd( void ) +{ + Assert( g_pMsgBuffer ); + + engine->MessageEnd(); + + g_pMsgBuffer = NULL; +} + +void MessageWriteByte( int iValue) +{ + if (!g_pMsgBuffer) + Error( "WRITE_BYTE called with no active message\n" ); + + g_pMsgBuffer->WriteByte( iValue ); +} + +void MessageWriteChar( int iValue) +{ + if (!g_pMsgBuffer) + Error( "WRITE_CHAR called with no active message\n" ); + + g_pMsgBuffer->WriteChar( iValue ); +} + +void MessageWriteShort( int iValue) +{ + if (!g_pMsgBuffer) + Error( "WRITE_SHORT called with no active message\n" ); + + g_pMsgBuffer->WriteShort( iValue ); +} + +void MessageWriteWord( int iValue ) +{ + if (!g_pMsgBuffer) + Error( "WRITE_WORD called with no active message\n" ); + + g_pMsgBuffer->WriteWord( iValue ); +} + +void MessageWriteLong( int iValue) +{ + if (!g_pMsgBuffer) + Error( "WriteLong called with no active message\n" ); + + g_pMsgBuffer->WriteLong( iValue ); +} + +void MessageWriteFloat( float flValue) +{ + if (!g_pMsgBuffer) + Error( "WriteFloat called with no active message\n" ); + + g_pMsgBuffer->WriteFloat( flValue ); +} + +void MessageWriteAngle( float flValue) +{ + if (!g_pMsgBuffer) + Error( "WriteAngle called with no active message\n" ); + + g_pMsgBuffer->WriteBitAngle( flValue, 8 ); +} + +void MessageWriteCoord( float flValue) +{ + if (!g_pMsgBuffer) + Error( "WriteCoord called with no active message\n" ); + + g_pMsgBuffer->WriteBitCoord( flValue ); +} + +void MessageWriteVec3Coord( const Vector& rgflValue) +{ + if (!g_pMsgBuffer) + Error( "WriteVec3Coord called with no active message\n" ); + + g_pMsgBuffer->WriteBitVec3Coord( rgflValue ); +} + +void MessageWriteVec3Normal( const Vector& rgflValue) +{ + if (!g_pMsgBuffer) + Error( "WriteVec3Normal called with no active message\n" ); + + g_pMsgBuffer->WriteBitVec3Normal( rgflValue ); +} + +void MessageWriteAngles( const QAngle& rgflValue) +{ + if (!g_pMsgBuffer) + Error( "WriteVec3Normal called with no active message\n" ); + + g_pMsgBuffer->WriteBitAngles( rgflValue ); +} + +void MessageWriteString( const char *sz ) +{ + if (!g_pMsgBuffer) + Error( "WriteString called with no active message\n" ); + + g_pMsgBuffer->WriteString( sz ); +} + +void MessageWriteEntity( int iValue) +{ + if (!g_pMsgBuffer) + Error( "WriteEntity called with no active message\n" ); + + g_pMsgBuffer->WriteShort( iValue ); +} + +void MessageWriteEHandle( CBaseEntity *pEntity ) +{ + if (!g_pMsgBuffer) + Error( "WriteEHandle called with no active message\n" ); + + long iEncodedEHandle; + + if( pEntity ) + { + EHANDLE hEnt = pEntity; + + int iSerialNum = hEnt.GetSerialNumber() & (1 << NUM_NETWORKED_EHANDLE_SERIAL_NUMBER_BITS) - 1; + iEncodedEHandle = hEnt.GetEntryIndex() | (iSerialNum << MAX_EDICT_BITS); + } + else + { + iEncodedEHandle = INVALID_NETWORKED_EHANDLE_VALUE; + } + + g_pMsgBuffer->WriteLong( iEncodedEHandle ); +} + +// bitwise +void MessageWriteBool( bool bValue ) +{ + if (!g_pMsgBuffer) + Error( "WriteBool called with no active message\n" ); + + g_pMsgBuffer->WriteOneBit( bValue ? 1 : 0 ); +} + +void MessageWriteUBitLong( unsigned int data, int numbits ) +{ + if (!g_pMsgBuffer) + Error( "WriteUBitLong called with no active message\n" ); + + g_pMsgBuffer->WriteUBitLong( data, numbits ); +} + +void MessageWriteSBitLong( int data, int numbits ) +{ + if (!g_pMsgBuffer) + Error( "WriteSBitLong called with no active message\n" ); + + g_pMsgBuffer->WriteSBitLong( data, numbits ); +} + +void MessageWriteBits( const void *pIn, int nBits ) +{ + if (!g_pMsgBuffer) + Error( "WriteBits called with no active message\n" ); + + g_pMsgBuffer->WriteBits( pIn, nBits ); +} + +class CServerDLLSharedAppSystems : public IServerDLLSharedAppSystems +{ +public: + CServerDLLSharedAppSystems() + { + AddAppSystem( "soundemittersystem", SOUNDEMITTERSYSTEM_INTERFACE_VERSION ); + AddAppSystem( "scenefilecache", SCENE_FILE_CACHE_INTERFACE_VERSION ); + } + + virtual int Count() + { + return m_Systems.Count(); + } + virtual char const *GetDllName( int idx ) + { + return m_Systems[ idx ].m_pModuleName; + } + virtual char const *GetInterfaceName( int idx ) + { + return m_Systems[ idx ].m_pInterfaceName; + } +private: + void AddAppSystem( char const *moduleName, char const *interfaceName ) + { + AppSystemInfo_t sys; + sys.m_pModuleName = moduleName; + sys.m_pInterfaceName = interfaceName; + m_Systems.AddToTail( sys ); + } + + CUtlVector< AppSystemInfo_t > m_Systems; +}; + +EXPOSE_SINGLE_INTERFACE( CServerDLLSharedAppSystems, IServerDLLSharedAppSystems, SERVER_DLL_SHARED_APPSYSTEMS ); + + diff --git a/dlls/genericactor.cpp b/dlls/genericactor.cpp index 43760448..5b7bbdff 100644 --- a/dlls/genericactor.cpp +++ b/dlls/genericactor.cpp @@ -92,7 +92,7 @@ void CGenericActor::HandleAnimEvent( animevent_t *pEvent ) //========================================================= int CGenericActor::GetSoundInterests ( void ) { - return NULL; + return 0; } //========================================================= diff --git a/dlls/genericmonster.cpp b/dlls/genericmonster.cpp index 7830cbac..a34f4624 100644 --- a/dlls/genericmonster.cpp +++ b/dlls/genericmonster.cpp @@ -126,7 +126,7 @@ void CGenericNPC::HandleAnimEvent( animevent_t *pEvent ) //========================================================= int CGenericNPC::GetSoundInterests ( void ) { - return NULL; + return 0; } //========================================================= diff --git a/dlls/hl2_dll/ai_behavior_functank.cpp b/dlls/hl2_dll/ai_behavior_functank.cpp index 68d52043..36c211a8 100644 --- a/dlls/hl2_dll/ai_behavior_functank.cpp +++ b/dlls/hl2_dll/ai_behavior_functank.cpp @@ -645,7 +645,7 @@ CBaseEntity *CAI_FuncTankBehavior::BestEnemy( void ) { bBestSeen = ( pNPC->GetSenses()->DidSeeEntity( pEnemy ) || pNPC->FVisible( pEnemy ) ); // @TODO (toml 04-02-03): Need to optimize CanSeeEntity() so multiple calls in frame do not recalculate, rather cache iBestPriority = pNPC->IRelationPriority( pEnemy ); - iBestDistSq = (pEnemy->GetAbsOrigin() - GetAbsOrigin() ).LengthSqr(); + iBestDistSq = static_cast((pEnemy->GetAbsOrigin() - GetAbsOrigin() ).LengthSqr()); pBestEnemy = pEnemy; bBestUnreachable = bUnreachable; } @@ -656,7 +656,7 @@ CBaseEntity *CAI_FuncTankBehavior::BestEnemy( void ) // currently think is the best visible enemy. No need to do // a distance check, just get mad at this one for now. iBestPriority = pNPC->IRelationPriority ( pEnemy ); - iBestDistSq = ( pEnemy->GetAbsOrigin() - GetAbsOrigin() ).LengthSqr(); + iBestDistSq = static_cast(( pEnemy->GetAbsOrigin() - GetAbsOrigin() ).LengthSqr()); pBestEnemy = pEnemy; bBestUnreachable = bUnreachable; } @@ -665,7 +665,7 @@ CBaseEntity *CAI_FuncTankBehavior::BestEnemy( void ) // this entity is disliked just as much as the entity that // we currently think is the best visible enemy, so we only // get mad at it if it is closer. - iDistSq = ( pEnemy->GetAbsOrigin() - GetAbsOrigin() ).LengthSqr(); + iDistSq = static_cast(( pEnemy->GetAbsOrigin() - GetAbsOrigin() ).LengthSqr()); bool bCloser = ( iDistSq < iBestDistSq ) ; diff --git a/dlls/hl2_dll/ai_behavior_functank.h b/dlls/hl2_dll/ai_behavior_functank.h index d9f1f33f..002ae52d 100644 --- a/dlls/hl2_dll/ai_behavior_functank.h +++ b/dlls/hl2_dll/ai_behavior_functank.h @@ -1,120 +1,120 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -//=============================================================================// - -#ifndef AI_BEHAVIOR_FUNCTANK_H -#define AI_BEHAVIOR_FUNCTANK_H -#ifdef _WIN32 -#pragma once -#endif - -#include "simtimer.h" -#include "ai_behavior.h" -#include "func_tank.h" - -#define AI_FUNCTANK_BEHAVIOR_BUSYTIME 10.0f - -enum -{ - FUNCTANK_SENTENCE_MOVE_TO_MOUNT = SENTENCE_BASE_BEHAVIOR_INDEX, - FUNCTANK_SENTENCE_JUST_MOUNTED, - FUNCTANK_SENTENCE_SCAN_FOR_ENEMIES, - FUNCTANK_SENTENCE_DISMOUNTING, -}; - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -class CAI_FuncTankBehavior : public CAI_SimpleBehavior -{ - DECLARE_CLASS( CAI_FuncTankBehavior, CAI_SimpleBehavior ); - DEFINE_CUSTOM_SCHEDULE_PROVIDER; - DECLARE_DATADESC(); - -public: - // Contructor/Deconstructor - CAI_FuncTankBehavior(); - ~CAI_FuncTankBehavior(); - - void UpdateOnRemove(); - - // Identifier - const char *GetName() { return "FuncTank"; } - - // Schedule - bool CanSelectSchedule(); - void BeginScheduleSelection(); - void EndScheduleSelection(); - void PrescheduleThink(); - - Activity NPC_TranslateActivity( Activity activity ); - - // Conditions: - virtual void GatherConditions(); - - enum - { - SCHED_MOVE_TO_FUNCTANK = BaseClass::NEXT_SCHEDULE, - SCHED_FIRE_FUNCTANK, - SCHED_SCAN_WITH_FUNCTANK, - SCHED_FAIL_MOVE_TO_FUNCTANK, - }; - - // Tasks - void StartTask( const Task_t *pTask ); - void RunTask( const Task_t *pTask ); - - enum - { - TASK_GET_PATH_TO_FUNCTANK = BaseClass::NEXT_TASK, - TASK_FACE_FUNCTANK, - TASK_HOLSTER_WEAPON, - TASK_FIRE_FUNCTANK, - TASK_SCAN_LEFT_FUNCTANK, - TASK_SCAN_RIGHT_FUNCTANK, - TASK_FORGET_ABOUT_FUNCTANK, - TASK_FUNCTANK_ANNOUNCE_SCAN, - }; - - enum - { - COND_FUNCTANK_DISMOUNT = BaseClass::NEXT_CONDITION, - NEXT_CONDITION, - }; - - // Combat. - CBaseEntity *BestEnemy( void ); - void Event_Killed( const CTakeDamageInfo &info ); - - bool HasFuncTank( void ) { return ( m_hFuncTank != NULL ); } - void SetFuncTank( CHandle hFuncTank ); - CFuncTank *GetFuncTank() { return m_hFuncTank; } - void AimGun( void ); - - void Dismount( void ); - - int OnTakeDamage_Alive( const CTakeDamageInfo &info ); - - // Time. - void SetBusy( float flTime ) { m_flBusyTime = flTime; } - bool IsBusy( void ) { return ( gpGlobals->curtime < m_flBusyTime ); } - - bool IsMounted( void ) { return m_bMounted; } - -private: - - // Schedule - int SelectSchedule(); - -private: - - CHandle m_hFuncTank; - bool m_bMounted; - float m_flBusyTime; - bool m_bSpottedPlayerOutOfCover; -}; - -#endif // AI_BEHAVIOR_FUNCTANK_H \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#ifndef AI_BEHAVIOR_FUNCTANK_H +#define AI_BEHAVIOR_FUNCTANK_H +#ifdef _WIN32 +#pragma once +#endif + +#include "simtimer.h" +#include "ai_behavior.h" +#include "func_tank.h" + +#define AI_FUNCTANK_BEHAVIOR_BUSYTIME 10.0f + +enum +{ + FUNCTANK_SENTENCE_MOVE_TO_MOUNT = SENTENCE_BASE_BEHAVIOR_INDEX, + FUNCTANK_SENTENCE_JUST_MOUNTED, + FUNCTANK_SENTENCE_SCAN_FOR_ENEMIES, + FUNCTANK_SENTENCE_DISMOUNTING, +}; + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +class CAI_FuncTankBehavior : public CAI_SimpleBehavior +{ + DECLARE_CLASS( CAI_FuncTankBehavior, CAI_SimpleBehavior ); + DEFINE_CUSTOM_SCHEDULE_PROVIDER; + DECLARE_DATADESC(); + +public: + // Contructor/Deconstructor + CAI_FuncTankBehavior(); + ~CAI_FuncTankBehavior(); + + void UpdateOnRemove(); + + // Identifier + const char *GetName() { return "FuncTank"; } + + // Schedule + bool CanSelectSchedule(); + void BeginScheduleSelection(); + void EndScheduleSelection(); + void PrescheduleThink(); + + Activity NPC_TranslateActivity( Activity activity ); + + // Conditions: + virtual void GatherConditions(); + + enum + { + SCHED_MOVE_TO_FUNCTANK = BaseClass::NEXT_SCHEDULE, + SCHED_FIRE_FUNCTANK, + SCHED_SCAN_WITH_FUNCTANK, + SCHED_FAIL_MOVE_TO_FUNCTANK, + }; + + // Tasks + void StartTask( const Task_t *pTask ); + void RunTask( const Task_t *pTask ); + + enum + { + TASK_GET_PATH_TO_FUNCTANK = BaseClass::NEXT_TASK, + TASK_FACE_FUNCTANK, + TASK_HOLSTER_WEAPON, + TASK_FIRE_FUNCTANK, + TASK_SCAN_LEFT_FUNCTANK, + TASK_SCAN_RIGHT_FUNCTANK, + TASK_FORGET_ABOUT_FUNCTANK, + TASK_FUNCTANK_ANNOUNCE_SCAN, + }; + + enum + { + COND_FUNCTANK_DISMOUNT = BaseClass::NEXT_CONDITION, + NEXT_CONDITION, + }; + + // Combat. + CBaseEntity *BestEnemy( void ); + void Event_Killed( const CTakeDamageInfo &info ); + + bool HasFuncTank( void ) { return ( m_hFuncTank != NULL ); } + void SetFuncTank( CHandle hFuncTank ); + CFuncTank *GetFuncTank() { return m_hFuncTank; } + void AimGun( void ); + + void Dismount( void ); + + int OnTakeDamage_Alive( const CTakeDamageInfo &info ); + + // Time. + void SetBusy( float flTime ) { m_flBusyTime = flTime; } + bool IsBusy( void ) { return ( gpGlobals->curtime < m_flBusyTime ); } + + bool IsMounted( void ) { return m_bMounted; } + +private: + + // Schedule + int SelectSchedule(); + +private: + + CHandle m_hFuncTank; + bool m_bMounted; + float m_flBusyTime; + bool m_bSpottedPlayerOutOfCover; +}; + +#endif // AI_BEHAVIOR_FUNCTANK_H diff --git a/dlls/hl2_dll/ai_behavior_police.cpp b/dlls/hl2_dll/ai_behavior_police.cpp index 6fc85b0a..eb80e7dd 100644 --- a/dlls/hl2_dll/ai_behavior_police.cpp +++ b/dlls/hl2_dll/ai_behavior_police.cpp @@ -333,7 +333,7 @@ void CAI_PolicingBehavior::StartTask( const Task_t *pTask ) } } - if ( GetNavigator()->SetGoal( harassPos, pTask->flTaskData ) ) + if ( GetNavigator()->SetGoal( harassPos, static_cast(pTask->flTaskData) ) ) { GetNavigator()->SetMovementActivity( (Activity) ACT_WALK_ANGRY ); GetNavigator()->SetArrivalDirection( m_hPoliceGoal->GetTarget() ); @@ -348,7 +348,7 @@ void CAI_PolicingBehavior::StartTask( const Task_t *pTask ) case TASK_POLICE_GET_PATH_TO_POLICE_GOAL: { - if ( GetNavigator()->SetGoal( m_hPoliceGoal->GetAbsOrigin(), pTask->flTaskData ) ) + if ( GetNavigator()->SetGoal( m_hPoliceGoal->GetAbsOrigin(), static_cast(pTask->flTaskData) ) ) { GetNavigator()->SetArrivalDirection( m_hPoliceGoal->GetAbsAngles() ); TaskComplete(); @@ -368,7 +368,7 @@ void CAI_PolicingBehavior::StartTask( const Task_t *pTask ) m_flNextHarassTime = gpGlobals->curtime + random->RandomInt( 4, 6 ); // Scatter rubber-neckers - CSoundEnt::InsertSound( SOUND_MOVE_AWAY, GetAbsOrigin(), 256.0f, 2.0f, GetOuter() ); + CSoundEnt::InsertSound( SOUND_MOVE_AWAY, GetAbsOrigin(), 256, 2.0f, GetOuter() ); } TaskComplete(); break; diff --git a/dlls/hl2_dll/ai_goal_police.cpp b/dlls/hl2_dll/ai_goal_police.cpp index c6ecb2ad..0c2bd803 100644 --- a/dlls/hl2_dll/ai_goal_police.cpp +++ b/dlls/hl2_dll/ai_goal_police.cpp @@ -65,7 +65,7 @@ CBaseEntity *CAI_PoliceGoal::GetTarget( void ) if ( pTarget == NULL ) { - DevMsg( "Unable to find ai_goal_police target: %s\n", m_iszTarget ); + DevMsg( "Unable to find ai_goal_police target: %s\n", STRING(m_iszTarget) ); return NULL; } diff --git a/dlls/hl2_dll/ai_interactions.h b/dlls/hl2_dll/ai_interactions.h index ba4ac6b9..0c4cbe78 100644 --- a/dlls/hl2_dll/ai_interactions.h +++ b/dlls/hl2_dll/ai_interactions.h @@ -1,80 +1,80 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -// -//=============================================================================// - -//================================================== -// Definition for all AI interactions -//================================================== - -#ifndef AI_INTERACTIONS_H -#define AI_INTERACTIONS_H - -#ifdef _WIN32 -#pragma once -#endif - -//Antlion -extern int g_interactionAntlionKilled; - -//Barnacle -extern int g_interactionBarnacleVictimDangle; -extern int g_interactionBarnacleVictimReleased; -extern int g_interactionBarnacleVictimGrab; - -//Bullsquid -//extern int g_interactionBullsquidPlay; -//extern int g_interactionBullsquidThrow; - -//Combine -extern int g_interactionCombineBash; -extern int g_interactionCombineRequestCover; - -//Houndeye -//extern int g_interactionHoundeyeGroupAttack; -//extern int g_interactionHoundeyeGroupRetreat; -//extern int g_interactionHoundeyeGroupRalley; - -//Scanner -extern int g_interactionScannerInspect; -extern int g_interactionScannerInspectBegin; -extern int g_interactionScannerInspectDone; -extern int g_interactionScannerInspectHandsUp; -extern int g_interactionScannerInspectShowArmband; -extern int g_interactionScannerSupportEntity; -extern int g_interactionScannerSupportPosition; - -//Metrocop -extern int g_interactionMetrocopPointed; -extern int g_interactionMetrocopStartedStitch; - -//ScriptedTarget -extern int g_interactionScriptedTarget; - -//Stalker -extern int g_interactionStalkerBurn; - -//Vortigaunt -extern int g_interactionVortigauntStomp; -extern int g_interactionVortigauntStompFail; -extern int g_interactionVortigauntStompHit; -extern int g_interactionVortigauntKick; -extern int g_interactionVortigauntClaw; - -//Floor turret -extern int g_interactionTurretStillStanding; - -// AI Interaction for being hit by a physics object -extern int g_interactionHitByPlayerThrownPhysObj; - -// Alerts vital allies when the player punts a large object (car) -extern int g_interactionPlayerPuntedHeavyObject; - -// Zombie -// Melee attack will land in one second or so. -extern int g_interactionZombieMeleeWarning; - -#endif //AI_INTERACTIONS_H \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + +//================================================== +// Definition for all AI interactions +//================================================== + +#ifndef AI_INTERACTIONS_H +#define AI_INTERACTIONS_H + +#ifdef _WIN32 +#pragma once +#endif + +//Antlion +extern int g_interactionAntlionKilled; + +//Barnacle +extern int g_interactionBarnacleVictimDangle; +extern int g_interactionBarnacleVictimReleased; +extern int g_interactionBarnacleVictimGrab; + +//Bullsquid +//extern int g_interactionBullsquidPlay; +//extern int g_interactionBullsquidThrow; + +//Combine +extern int g_interactionCombineBash; +extern int g_interactionCombineRequestCover; + +//Houndeye +//extern int g_interactionHoundeyeGroupAttack; +//extern int g_interactionHoundeyeGroupRetreat; +//extern int g_interactionHoundeyeGroupRalley; + +//Scanner +extern int g_interactionScannerInspect; +extern int g_interactionScannerInspectBegin; +extern int g_interactionScannerInspectDone; +extern int g_interactionScannerInspectHandsUp; +extern int g_interactionScannerInspectShowArmband; +extern int g_interactionScannerSupportEntity; +extern int g_interactionScannerSupportPosition; + +//Metrocop +extern int g_interactionMetrocopPointed; +extern int g_interactionMetrocopStartedStitch; + +//ScriptedTarget +extern int g_interactionScriptedTarget; + +//Stalker +extern int g_interactionStalkerBurn; + +//Vortigaunt +extern int g_interactionVortigauntStomp; +extern int g_interactionVortigauntStompFail; +extern int g_interactionVortigauntStompHit; +extern int g_interactionVortigauntKick; +extern int g_interactionVortigauntClaw; + +//Floor turret +extern int g_interactionTurretStillStanding; + +// AI Interaction for being hit by a physics object +extern int g_interactionHitByPlayerThrownPhysObj; + +// Alerts vital allies when the player punts a large object (car) +extern int g_interactionPlayerPuntedHeavyObject; + +// Zombie +// Melee attack will land in one second or so. +extern int g_interactionZombieMeleeWarning; + +#endif //AI_INTERACTIONS_H diff --git a/dlls/hl2_dll/ai_spotlight.cpp b/dlls/hl2_dll/ai_spotlight.cpp index bac29efa..5495848f 100644 --- a/dlls/hl2_dll/ai_spotlight.cpp +++ b/dlls/hl2_dll/ai_spotlight.cpp @@ -365,12 +365,12 @@ void CAI_Spotlight::UpdateSpotlightEndpoint( void ) } else if (m_flSpotlightCurLength > m_flSpotlightMaxLength) { - m_hSpotlightTarget->SetRenderColorA( (1-((m_flSpotlightCurLength-m_flSpotlightMaxLength)/m_flSpotlightMaxLength)) ); + m_hSpotlightTarget->SetRenderColorA( static_cast((1-((m_flSpotlightCurLength-m_flSpotlightMaxLength)/m_flSpotlightMaxLength))) ); m_hSpotlight->SetFadeLength(m_flSpotlightMaxLength); } else { - m_hSpotlightTarget->SetRenderColorA( 1.0 ); + m_hSpotlightTarget->SetRenderColorA( 1 ); m_hSpotlight->SetFadeLength(m_flSpotlightCurLength); } diff --git a/dlls/hl2_dll/antlion_dust.h b/dlls/hl2_dll/antlion_dust.h index 110716b9..a79ba0fc 100644 --- a/dlls/hl2_dll/antlion_dust.h +++ b/dlls/hl2_dll/antlion_dust.h @@ -1,32 +1,32 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -// -//=============================================================================// -#ifndef ANTLION_DUST_H -#define ANTLION_DUST_H - -#include "te_particlesystem.h" - -class CTEAntlionDust : public CTEParticleSystem -{ -public: - - DECLARE_CLASS( CTEAntlionDust, CTEParticleSystem ); - DECLARE_SERVERCLASS(); - - CTEAntlionDust( const char *name ); - virtual ~CTEAntlionDust( void ); - - virtual void Test( const Vector& current_origin, const QAngle& current_angles ) { }; - - CNetworkVector( m_vecOrigin ); - CNetworkVar( QAngle, m_vecAngles ); - CNetworkVar( bool, m_bBlockedSpawner ); -}; - -void UTIL_CreateAntlionDust( const Vector &origin, const QAngle &angles, bool bBlockedSpawner = false ); - -#endif //ANTLION_DUST_H \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// +#ifndef ANTLION_DUST_H +#define ANTLION_DUST_H + +#include "te_particlesystem.h" + +class CTEAntlionDust : public CTEParticleSystem +{ +public: + + DECLARE_CLASS( CTEAntlionDust, CTEParticleSystem ); + DECLARE_SERVERCLASS(); + + CTEAntlionDust( const char *name ); + virtual ~CTEAntlionDust( void ); + + virtual void Test( const Vector& current_origin, const QAngle& current_angles ) { }; + + CNetworkVector( m_vecOrigin ); + CNetworkVar( QAngle, m_vecAngles ); + CNetworkVar( bool, m_bBlockedSpawner ); +}; + +void UTIL_CreateAntlionDust( const Vector &origin, const QAngle &angles, bool bBlockedSpawner = false ); + +#endif //ANTLION_DUST_H diff --git a/dlls/hl2_dll/antlion_maker.cpp b/dlls/hl2_dll/antlion_maker.cpp index 26fb32cb..f16399dc 100644 --- a/dlls/hl2_dll/antlion_maker.cpp +++ b/dlls/hl2_dll/antlion_maker.cpp @@ -1634,20 +1634,20 @@ void CAntlionTemplateMaker::DrawDebugGeometryOverlays( void ) if ( m_debugOverlays & OVERLAY_TEXT_BIT ) { - float r, g, b; + int r, g, b; // Color by active state if ( m_bDisabled ) { - r = 255.0f; - g = 0.0f; - b = 0.0f; + r = 255; + g = 0; + b = 0; } else { - r = 0.0f; - g = 255.0f; - b = 0.0f; + r = 0; + g = 255; + b = 0; } // Draw ourself diff --git a/dlls/hl2_dll/basehlcombatweapon.cpp b/dlls/hl2_dll/basehlcombatweapon.cpp index 01a685c1..fa97dfc4 100644 --- a/dlls/hl2_dll/basehlcombatweapon.cpp +++ b/dlls/hl2_dll/basehlcombatweapon.cpp @@ -100,7 +100,7 @@ void CHLMachineGun::PrimaryAttack( void ) //Factor in the view kick AddViewKick(); - CSoundEnt::InsertSound( SOUND_COMBAT, GetAbsOrigin(), SOUNDENT_VOLUME_MACHINEGUN, 0.2, pPlayer ); + CSoundEnt::InsertSound( SOUND_COMBAT, GetAbsOrigin(), static_cast(SOUNDENT_VOLUME_MACHINEGUN), 0.2, pPlayer ); if (!m_iClip1 && pPlayer->GetAmmoCount(m_iPrimaryAmmoType) <= 0) { diff --git a/dlls/hl2_dll/cbasehelicopter.cpp b/dlls/hl2_dll/cbasehelicopter.cpp index 362102c5..ef09b1fd 100644 --- a/dlls/hl2_dll/cbasehelicopter.cpp +++ b/dlls/hl2_dll/cbasehelicopter.cpp @@ -674,7 +674,7 @@ void CBaseHelicopter::UpdateEnemy() // be sure and change my prevseen/lastseen timers. if( m_lifeState == LIFE_ALIVE ) { - GetSenses()->Look( EnemySearchDistance() ); + GetSenses()->Look( static_cast(EnemySearchDistance()) ); GetEnemies()->RefreshMemories(); ChooseEnemy(); @@ -834,7 +834,7 @@ void CBaseHelicopter::UpdatePlayerDopplerShift( ) float velReceiver = DotProduct( pPlayer->GetAbsVelocity(), dir ); float velTransmitter = -DotProduct( GetAbsVelocity(), dir ); // speed of sound == 13049in/s - int iPitch = 100 * ((1 - velReceiver / 13049) / (1 + velTransmitter / 13049)); + int iPitch = static_cast(100 * ((1 - velReceiver / 13049) / (1 + velTransmitter / 13049))); #else // This is a bogus doppler shift, but I like it better float relV = DotProduct( GetAbsVelocity() - pPlayer->GetAbsVelocity(), dir ); diff --git a/dlls/hl2_dll/cbasespriteprojectile.h b/dlls/hl2_dll/cbasespriteprojectile.h index 1c163c3d..0d9d78f8 100644 --- a/dlls/hl2_dll/cbasespriteprojectile.h +++ b/dlls/hl2_dll/cbasespriteprojectile.h @@ -27,7 +27,7 @@ class CBaseSpriteProjectile : public CSprite public: void Touch( CBaseEntity *pOther ); - void CBaseSpriteProjectile::Spawn( char *pszModel, + void Spawn( char *pszModel, const Vector &vecOrigin, const Vector &vecVelocity, edict_t *pOwner, diff --git a/dlls/hl2_dll/combine_mine.cpp b/dlls/hl2_dll/combine_mine.cpp index fe6386ed..694b361b 100644 --- a/dlls/hl2_dll/combine_mine.cpp +++ b/dlls/hl2_dll/combine_mine.cpp @@ -955,7 +955,7 @@ void CBounceBomb::ExplodeThink() CBaseEntity *pThrower = HasPhysicsAttacker( 0.5 ); - ExplosionCreate( GetAbsOrigin(), GetAbsAngles(), (pThrower) ? pThrower : this, BOUNCEBOMB_EXPLODE_DAMAGE, BOUNCEBOMB_EXPLODE_RADIUS, true ); + ExplosionCreate( GetAbsOrigin(), GetAbsAngles(), (pThrower) ? pThrower : this, static_cast(BOUNCEBOMB_EXPLODE_DAMAGE), static_cast(BOUNCEBOMB_EXPLODE_RADIUS), true ); UTIL_Remove( this ); } @@ -1098,7 +1098,7 @@ void CBounceBomb::OnPhysGunPickup( CBasePlayer *pPhysGunUser, PhysGunPickup_t re // Try to scatter NPCs without panicking them. Make a move away sound up around their // ear level. - CSoundEnt::InsertSound( SOUND_MOVE_AWAY, GetAbsOrigin() + Vector( 0, 0, 60), 32.0f, 0.2f ); + CSoundEnt::InsertSound( SOUND_MOVE_AWAY, GetAbsOrigin() + Vector( 0, 0, 60), 32, 0.2f ); return; } else diff --git a/dlls/hl2_dll/energy_wave.h b/dlls/hl2_dll/energy_wave.h index b1af1196..fa7d92f7 100644 --- a/dlls/hl2_dll/energy_wave.h +++ b/dlls/hl2_dll/energy_wave.h @@ -1,37 +1,37 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -//=============================================================================// - -#ifndef ENERGYWAVE_H -#define ENERGYWAVE_H -#ifdef _WIN32 -#pragma once -#endif - -#include "basecombatweapon.h" -#include "energy_wave.h" - - -//----------------------------------------------------------------------------- -// Purpose: Shield -//----------------------------------------------------------------------------- -class CEnergyWave : public CBaseEntity -{ - DECLARE_DATADESC(); -public: - DECLARE_CLASS( CEnergyWave, CBaseEntity ); - DECLARE_SERVERCLASS(); - -public: - void Spawn( void ); - void Precache( void ); - -public: - static CEnergyWave* Create( CBaseEntity *pentOwner ); -}; - - -#endif //ENERGYWAVE_H \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef ENERGYWAVE_H +#define ENERGYWAVE_H +#ifdef _WIN32 +#pragma once +#endif + +#include "basecombatweapon.h" +#include "energy_wave.h" + + +//----------------------------------------------------------------------------- +// Purpose: Shield +//----------------------------------------------------------------------------- +class CEnergyWave : public CBaseEntity +{ + DECLARE_DATADESC(); +public: + DECLARE_CLASS( CEnergyWave, CBaseEntity ); + DECLARE_SERVERCLASS(); + +public: + void Spawn( void ); + void Precache( void ); + +public: + static CEnergyWave* Create( CBaseEntity *pentOwner ); +}; + + +#endif //ENERGYWAVE_H diff --git a/dlls/hl2_dll/env_alyxemp.cpp b/dlls/hl2_dll/env_alyxemp.cpp index c41695f4..dcf66761 100644 --- a/dlls/hl2_dll/env_alyxemp.cpp +++ b/dlls/hl2_dll/env_alyxemp.cpp @@ -84,7 +84,7 @@ void CAlyxEmpEffect::SetTargetEntity( const char *szEntityName ) if ( pTargetEnt == NULL ) { Assert(0); - DevMsg( "Unable to find env_alyxemp (%s) target %s!\n", GetEntityName(), szEntityName ); + DevMsg( "Unable to find env_alyxemp (%s) target %s!\n", STRING(GetEntityName()), szEntityName ); } } diff --git a/dlls/hl2_dll/env_headcrabcanister.cpp b/dlls/hl2_dll/env_headcrabcanister.cpp index aca15151..730e311b 100644 --- a/dlls/hl2_dll/env_headcrabcanister.cpp +++ b/dlls/hl2_dll/env_headcrabcanister.cpp @@ -381,7 +381,7 @@ CSkyCamera *CEnvHeadcrabCanister::PlaceCanisterInWorld() CBaseEntity *pLaunchPos = gEntList.FindEntityByName( NULL, m_iszLaunchPositionName ); if ( !pLaunchPos ) { - Warning("%s (%s) could not find an entity matching LaunchPositionName of '%s'\n", GetEntityName(), GetDebugName(), STRING(m_iszLaunchPositionName) ); + Warning("%s (%s) could not find an entity matching LaunchPositionName of '%s'\n", STRING(GetEntityName()), GetDebugName(), STRING(m_iszLaunchPositionName) ); SUB_Remove(); } else @@ -937,7 +937,7 @@ void CEnvHeadcrabCanister::Detonate( ) if ( !HasSpawnFlags( SF_NO_IMPACT_EFFECTS ) ) { // Normal explosion - ExplosionCreate( m_vecImpactPosition, GetAbsAngles(), this, 50.0f, 500.0f, + ExplosionCreate( m_vecImpactPosition, GetAbsAngles(), this, 50, 500, SF_ENVEXPLOSION_NODLIGHTS | SF_ENVEXPLOSION_NOSPARKS | SF_ENVEXPLOSION_NODAMAGE | SF_ENVEXPLOSION_NOSOUND, 1300.0f ); // Dust explosion diff --git a/dlls/hl2_dll/env_starfield.cpp b/dlls/hl2_dll/env_starfield.cpp index 8c287efd..249bd60d 100644 --- a/dlls/hl2_dll/env_starfield.cpp +++ b/dlls/hl2_dll/env_starfield.cpp @@ -1,107 +1,107 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -//=============================================================================// - -#include "cbase.h" -#include "baseparticleentity.h" -#include "sendproxy.h" - -// memdbgon must be the last include file in a .cpp file!!! -#include "tier0/memdbgon.h" - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -class CEnvStarfield : public CBaseEntity -{ - DECLARE_CLASS( CEnvStarfield, CBaseEntity ); -public: - DECLARE_SERVERCLASS(); - DECLARE_DATADESC(); - - virtual void Precache(); - virtual void Spawn( void ); - virtual int UpdateTransmitState(void); - - // Inputs - void InputTurnOn( inputdata_t &inputdata ); - void InputTurnOff( inputdata_t &inputdata ); - void InputSetDensity( inputdata_t &inputdata ); - -private: - CNetworkVar( bool, m_bOn ); - CNetworkVar( float, m_flDensity ); -}; - -BEGIN_DATADESC( CEnvStarfield ) - DEFINE_FIELD( m_bOn, FIELD_BOOLEAN ), - DEFINE_FIELD( m_flDensity, FIELD_FLOAT ), - - DEFINE_INPUTFUNC( FIELD_VOID, "TurnOn", InputTurnOn ), - DEFINE_INPUTFUNC( FIELD_VOID, "TurnOff", InputTurnOff ), - DEFINE_INPUTFUNC( FIELD_FLOAT, "SetDensity", InputSetDensity ), -END_DATADESC() - -IMPLEMENT_SERVERCLASS_ST( CEnvStarfield, DT_EnvStarfield ) - SendPropInt( SENDINFO(m_bOn), 1, SPROP_UNSIGNED ), - SendPropFloat( SENDINFO(m_flDensity), 0, SPROP_NOSCALE), -END_SEND_TABLE() - -LINK_ENTITY_TO_CLASS( env_starfield, CEnvStarfield ); - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CEnvStarfield::Spawn() -{ - BaseClass::Spawn(); - - m_flDensity = 1.0; - m_bOn = false; - - Precache(); -} - -void CEnvStarfield::Precache() -{ - BaseClass::Precache(); - - PrecacheMaterial( "effects/spark_noz" ); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -int CEnvStarfield::UpdateTransmitState() -{ - return SetTransmitState( FL_EDICT_ALWAYS ); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CEnvStarfield::InputTurnOn( inputdata_t &inputdata ) -{ - m_bOn = true; -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : &inputdata - -//----------------------------------------------------------------------------- -void CEnvStarfield::InputTurnOff( inputdata_t &inputdata ) -{ - m_bOn = false; -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : &inputdata - -//----------------------------------------------------------------------------- -void CEnvStarfield::InputSetDensity( inputdata_t &inputdata ) -{ - m_flDensity = inputdata.value.Float(); -} \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include "cbase.h" +#include "baseparticleentity.h" +#include "sendproxy.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +class CEnvStarfield : public CBaseEntity +{ + DECLARE_CLASS( CEnvStarfield, CBaseEntity ); +public: + DECLARE_SERVERCLASS(); + DECLARE_DATADESC(); + + virtual void Precache(); + virtual void Spawn( void ); + virtual int UpdateTransmitState(void); + + // Inputs + void InputTurnOn( inputdata_t &inputdata ); + void InputTurnOff( inputdata_t &inputdata ); + void InputSetDensity( inputdata_t &inputdata ); + +private: + CNetworkVar( bool, m_bOn ); + CNetworkVar( float, m_flDensity ); +}; + +BEGIN_DATADESC( CEnvStarfield ) + DEFINE_FIELD( m_bOn, FIELD_BOOLEAN ), + DEFINE_FIELD( m_flDensity, FIELD_FLOAT ), + + DEFINE_INPUTFUNC( FIELD_VOID, "TurnOn", InputTurnOn ), + DEFINE_INPUTFUNC( FIELD_VOID, "TurnOff", InputTurnOff ), + DEFINE_INPUTFUNC( FIELD_FLOAT, "SetDensity", InputSetDensity ), +END_DATADESC() + +IMPLEMENT_SERVERCLASS_ST( CEnvStarfield, DT_EnvStarfield ) + SendPropInt( SENDINFO(m_bOn), 1, SPROP_UNSIGNED ), + SendPropFloat( SENDINFO(m_flDensity), 0, SPROP_NOSCALE), +END_SEND_TABLE() + +LINK_ENTITY_TO_CLASS( env_starfield, CEnvStarfield ); + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CEnvStarfield::Spawn() +{ + BaseClass::Spawn(); + + m_flDensity = 1.0; + m_bOn = false; + + Precache(); +} + +void CEnvStarfield::Precache() +{ + BaseClass::Precache(); + + PrecacheMaterial( "effects/spark_noz" ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +int CEnvStarfield::UpdateTransmitState() +{ + return SetTransmitState( FL_EDICT_ALWAYS ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CEnvStarfield::InputTurnOn( inputdata_t &inputdata ) +{ + m_bOn = true; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : &inputdata - +//----------------------------------------------------------------------------- +void CEnvStarfield::InputTurnOff( inputdata_t &inputdata ) +{ + m_bOn = false; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : &inputdata - +//----------------------------------------------------------------------------- +void CEnvStarfield::InputSetDensity( inputdata_t &inputdata ) +{ + m_flDensity = inputdata.value.Float(); +} diff --git a/dlls/hl2_dll/func_recharge.cpp b/dlls/hl2_dll/func_recharge.cpp index c849e95f..b73cad2b 100644 --- a/dlls/hl2_dll/func_recharge.cpp +++ b/dlls/hl2_dll/func_recharge.cpp @@ -123,7 +123,7 @@ void CRecharge::Spawn() SetModel( STRING( GetModelName() ) ); - UpdateJuice( MaxJuice() ); + UpdateJuice( static_cast(MaxJuice()) ); m_nState = 0; @@ -303,7 +303,7 @@ void CRecharge::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE use void CRecharge::Recharge(void) { - UpdateJuice( MaxJuice() ); + UpdateJuice( static_cast(MaxJuice()) ); m_nState = 0; SetThink( &CRecharge::SUB_DoNothing ); } @@ -318,7 +318,7 @@ void CRecharge::Off(void) m_iOn = 0; - if ((!m_iJuice) && ( ( m_iReactivate = g_pGameRules->FlHEVChargerRechargeTime() ) > 0) ) + if ((!m_iJuice) && ( ( m_iReactivate = static_cast(g_pGameRules->FlHEVChargerRechargeTime()) ) > 0) ) { SetNextThink( gpGlobals->curtime + m_iReactivate ); SetThink(&CRecharge::Recharge); @@ -449,17 +449,17 @@ void CNewRecharge::SetInitialCharge( void ) if ( HasSpawnFlags( SF_KLEINER_RECHARGER ) ) { // The charger in Kleiner's lab. - m_iMaxJuice = 25.0f; + m_iMaxJuice = 25; return; } if ( HasSpawnFlags( SF_CITADEL_RECHARGER ) ) { - m_iMaxJuice = sk_suitcharger_citadel.GetFloat(); + m_iMaxJuice = sk_suitcharger_citadel.GetInt(); return; } - m_iMaxJuice = sk_suitcharger.GetFloat(); + m_iMaxJuice = sk_suitcharger.GetInt(); } void CNewRecharge::Spawn() @@ -477,7 +477,7 @@ void CNewRecharge::Spawn() SetInitialCharge(); - UpdateJuice( MaxJuice() ); + UpdateJuice( static_cast(MaxJuice()) ); m_nState = 0; m_iCaps = FCAP_CONTINUOUS_USE; @@ -728,7 +728,7 @@ void CNewRecharge::Recharge(void) EmitSound( "SuitRecharge.Start" ); ResetSequence( LookupSequence( "idle" ) ); - UpdateJuice( MaxJuice() ); + UpdateJuice( static_cast(MaxJuice()) ); m_nState = 0; m_flJuice = m_iJuice; @@ -760,11 +760,11 @@ void CNewRecharge::Off(void) { if ( HasSpawnFlags( SF_CITADEL_RECHARGER ) ) { - m_iReactivate = g_pGameRules->FlHEVChargerRechargeTime() * 2; + m_iReactivate = static_cast(g_pGameRules->FlHEVChargerRechargeTime() * 2); } else { - m_iReactivate = g_pGameRules->FlHEVChargerRechargeTime(); + m_iReactivate = static_cast(g_pGameRules->FlHEVChargerRechargeTime()); } SetNextThink( gpGlobals->curtime + m_iReactivate ); SetThink(&CNewRecharge::Recharge); diff --git a/dlls/hl2_dll/func_tank.cpp b/dlls/hl2_dll/func_tank.cpp index b34487c0..7bc3d538 100644 --- a/dlls/hl2_dll/func_tank.cpp +++ b/dlls/hl2_dll/func_tank.cpp @@ -824,7 +824,7 @@ void CFuncTank::Spawn( void ) m_sightOrigin = WorldBarrelPosition(); // Point at the end of the barrel - if ( m_spread > MAX_FIRING_SPREADS ) + if ( m_spread > static_cast(MAX_FIRING_SPREADS) ) { m_spread = 0; } @@ -1143,7 +1143,7 @@ void CFuncTank::ControllerPostFrame( void ) AngleVectors( GetAbsAngles(), &forward ); m_fireLast = gpGlobals->curtime - (1/m_fireRate) - 0.01; // to make sure the gun doesn't fire too many bullets - int bulletCount = (gpGlobals->curtime - m_fireLast) * m_fireRate; + int bulletCount = static_cast((gpGlobals->curtime - m_fireLast) * m_fireRate); if( HasSpawnFlags( SF_TANK_AIM_ASSISTANCE ) ) { @@ -1417,7 +1417,7 @@ float CFuncTank::GetRandomFireTime( void ) //----------------------------------------------------------------------------- int CFuncTank::GetRandomBurst( void ) { - return random->RandomInt( m_fireRate-2, m_fireRate+2 ); + return random->RandomInt( static_cast(m_fireRate-2), static_cast(m_fireRate+2) ); } //----------------------------------------------------------------------------- @@ -2110,7 +2110,7 @@ void CFuncTank::FiringSequence( const Vector &barrelEnd, const Vector &forward, { if ( m_fireLast != 0 ) { - int bulletCount = (gpGlobals->curtime - m_fireLast) * m_fireRate; + int bulletCount = static_cast((gpGlobals->curtime - m_fireLast) * m_fireRate); if ( bulletCount > 0 ) { @@ -2227,7 +2227,7 @@ void CFuncTank::Fire( int bulletCount, const Vector &barrelEnd, const Vector &fo #ifdef _XBOX UTIL_PlayerByIndex(1)->RumbleEffect( RUMBLE_AR2, 0, RUMBLE_FLAG_RESTART | RUMBLE_FLAG_RANDOM_AMPLITUDE ); #else - CSoundEnt::InsertSound( SOUND_MOVE_AWAY, barrelEnd + forward * 32.0f, 32.0f, 0.2f, pAttacker, SOUNDENT_CHANNEL_WEAPON ); + CSoundEnt::InsertSound( SOUND_MOVE_AWAY, barrelEnd + forward * 32.0f, 32, 0.2f, pAttacker, SOUNDENT_CHANNEL_WEAPON ); #endif } @@ -3517,7 +3517,7 @@ void CMortarShell::FlyThink() if ( gpGlobals->curtime > m_flNPCWarnTime ) { // Warn the AI. Make this radius a little larger than the explosion will be, and make the sound last a little longer. - CSoundEnt::InsertSound ( SOUND_DANGER | SOUND_CONTEXT_MORTAR, GetAbsOrigin(), MORTAR_BLAST_RADIUS * 1.25, (m_flImpactTime - m_flNPCWarnTime) + 0.15 ); + CSoundEnt::InsertSound ( SOUND_DANGER | SOUND_CONTEXT_MORTAR, GetAbsOrigin(), static_cast(MORTAR_BLAST_RADIUS * 1.25), (m_flImpactTime - m_flNPCWarnTime) + 0.15 ); m_flNPCWarnTime = FLT_MAX; } @@ -3531,11 +3531,11 @@ void CMortarShell::FlyThink() // Beam updates START - m_pBeamEffect[0]->SetBrightness( 255 * curve1 ); + m_pBeamEffect[0]->SetBrightness( static_cast(255 * curve1) ); m_pBeamEffect[0]->SetWidth( 64.0f * curve1 ); m_pBeamEffect[0]->SetEndWidth( 64.0f * curve1 ); - m_pBeamEffect[1]->SetBrightness( 255 * curve1 ); + m_pBeamEffect[1]->SetBrightness( static_cast(255 * curve1) ); m_pBeamEffect[1]->SetWidth( 8.0f * curve1 ); m_pBeamEffect[1]->SetEndWidth( 8.0f * curve1 ); @@ -3543,14 +3543,14 @@ void CMortarShell::FlyThink() if ( m_pBeamEffect[2] ) { - m_pBeamEffect[2]->SetBrightness( 255 * curve2 ); + m_pBeamEffect[2]->SetBrightness( static_cast(255 * curve2) ); m_pBeamEffect[2]->SetWidth( 32.0f * curve2 ); m_pBeamEffect[2]->SetEndWidth( 32.0f * curve2 ); } if ( m_pBeamEffect[3] ) { - m_pBeamEffect[3]->SetBrightness( 255 * curve2 ); + m_pBeamEffect[3]->SetBrightness( static_cast(255 * curve2) ); m_pBeamEffect[3]->SetWidth( 8.0f * curve2 ); m_pBeamEffect[3]->SetEndWidth( 8.0f * curve2 ); } @@ -3687,11 +3687,11 @@ void CMortarShell::FadeThink( void ) // Beam updates START - m_pBeamEffect[0]->SetBrightness( 255 * curve1 ); + m_pBeamEffect[0]->SetBrightness( static_cast(255 * curve1) ); m_pBeamEffect[0]->SetWidth( 64.0f * curve1 ); m_pBeamEffect[0]->SetEndWidth( 64.0f * curve1 ); - m_pBeamEffect[1]->SetBrightness( 255 * curve1 ); + m_pBeamEffect[1]->SetBrightness( static_cast(255 * curve1) ); m_pBeamEffect[1]->SetWidth( 8.0f * curve1 ); m_pBeamEffect[1]->SetEndWidth( 8.0f * curve1 ); @@ -3699,14 +3699,14 @@ void CMortarShell::FadeThink( void ) if ( m_pBeamEffect[2] ) { - m_pBeamEffect[2]->SetBrightness( 255 * curve2 ); + m_pBeamEffect[2]->SetBrightness( static_cast(255 * curve2) ); m_pBeamEffect[2]->SetWidth( 32.0f * curve2 ); m_pBeamEffect[2]->SetEndWidth( 32.0f * curve2 ); } if ( m_pBeamEffect[3] ) { - m_pBeamEffect[3]->SetBrightness( 255 * curve2 ); + m_pBeamEffect[3]->SetBrightness( static_cast(255 * curve2) ); m_pBeamEffect[3]->SetWidth( 8.0f * curve2 ); m_pBeamEffect[3]->SetEndWidth( 8.0f * curve2 ); } diff --git a/dlls/hl2_dll/func_tank.h b/dlls/hl2_dll/func_tank.h index 07b3efa2..7891b579 100644 --- a/dlls/hl2_dll/func_tank.h +++ b/dlls/hl2_dll/func_tank.h @@ -1,341 +1,341 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -//=============================================================================// - -#ifndef FUNC_TANK_H -#define FUNC_TANK_H -#ifdef _WIN32 -#pragma once -#endif - -#include "triggers.h" - -#define SF_TANK_ACTIVE 0x0001 -#define SF_TANK_PLAYER 0x0002 -#define SF_TANK_HUMANS 0x0004 -#define SF_TANK_ALIENS 0x0008 -#define SF_TANK_LINEOFSIGHT 0x0010 -#define SF_TANK_CANCONTROL 0x0020 -#define SF_TANK_DAMAGE_KICK 0x0040 // Kick when take damage -#define SF_TANK_AIM_AT_POS 0x0080 // Aim at a particular position -#define SF_TANK_AIM_ASSISTANCE 0x0100 -#define SF_TANK_NPC 0x0200 -#define SF_TANK_NPC_CONTROLLABLE 0x0400 // 1024 -#define SF_TANK_NPC_SET_CONTROLLER 0x0800 // 2048 -#define SF_TANK_ALLOW_PLAYER_HITS 0x1000 // 4096 Allow friendly NPCs to fire upon enemies near the player -#define SF_TANK_IGNORE_RANGE_IN_VIEWCONE 0x2000 // 8192 Don't use range as a factor in determining if something is in view cone -#define SF_TANK_NOTSOLID 0x8000 // 32768 -#define SF_TANK_SOUNDON 0x10000 // FIXME: This is not really a spawnflag! It holds transient state!!! -#define SF_TANK_HACKPLAYERHIT 0x20000 // 131072 Make this func_tank cheat and hit the player regularly - -#define FUNCTANK_DISTANCE_MAX 1200 // 100 ft. -#define FUNCTANK_DISTANCE_MIN_TO_ENEMY 180 -#define FUNCTANK_FIREVOLUME 1000 -#define FUNCTANK_NPC_ROUTE_TIME 5.0f - -// Effect handling -// If the func_tank has a chosen method of handling effects, use that -// instead of the individual effect settings. (muzzleflash, sound, tracer, etc) -enum FUNCTANK_EFFECT_HANDLING -{ - EH_NONE, // Use the effect settings - EH_AR2, // Use AR2 effects - EH_COMBINE_CANNON // Large Combine cannon -}; - -enum TANKBULLET -{ - TANK_BULLET_NONE = 0, - TANK_BULLET_SMALL = 1, - TANK_BULLET_MEDIUM = 2, - TANK_BULLET_LARGE = 3, -}; - -#define MORTAR_BLAST_RADIUS 350 - - -// Custom damage -// env_laser (duration is 0.5 rate of fire) -// rockets -// explosion? - -class CFuncTank : public CBaseEntity -{ - - DECLARE_CLASS( CFuncTank, CBaseEntity ); - -public: - - CFuncTank(); - ~CFuncTank(); - void Spawn( void ); - void Activate( void ); - void Precache( void ); - bool CreateVPhysics( void ); - bool KeyValue( const char *szKeyName, const char *szValue ); - void UpdateOnRemove(); - - int ObjectCaps( void ) - { - return ( BaseClass::ObjectCaps() | FCAP_IMPULSE_USE | FCAP_USE_IN_RADIUS ); - } - - void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); - void Think( void ); - - int GetAmmoCount( void ) { return m_iAmmoCount; } - - // NPC - bool NPC_FindManPoint( Vector &vecPos ); - bool NPC_HasEnemy( void ); - void NPC_Fire( void ); - void NPC_InterruptRoute( void ); - void NPC_JustSawPlayer( CBaseEntity *pTarget ); - void NPC_SetInRoute( bool bInRoute ) { m_bNPCInRoute = bInRoute; } - void NPC_SetIdleAngle( Vector vecIdle ) { m_vecNPCIdleTarget = vecIdle; } - - // LOS - bool IsEntityInViewCone( CBaseEntity *pEntity ); - bool HasLOSTo( CBaseEntity *pEntity ); - - // Controller - CBaseCombatCharacter *GetController( void ); - bool StartControl( CBaseCombatCharacter *pController ); - void StopControl( void ); - - const float YawCenter() const { return m_yawCenter; } - const float YawCenterWorld() const { return m_yawCenterWorld; } - const float YawRange() const { return m_yawRange; } - const float PitchCenter() const { return m_pitchCenter; } - const float PitchCenterWorld() const { return m_pitchCenterWorld; } - const float PitchRange() const { return m_pitchRange; } - - virtual void PhysicsSimulate( void ); - - virtual void OnStartControlled() {} - virtual void OnStopControlled() {} - - // SF Tests. - inline bool IsControllable( void ) { return ( m_spawnflags & SF_TANK_CANCONTROL ) ? true : false; } - inline bool IsActive( void ) { return ( m_spawnflags & SF_TANK_ACTIVE ) ? true : false; } - inline bool IsNPCControllable( void ) { return ( m_spawnflags & SF_TANK_NPC_CONTROLLABLE ) ? true : false; } - inline bool IsNPCSetController( void ) { return ( m_spawnflags & SF_TANK_NPC_SET_CONTROLLER ) ? true : false; } - - virtual void DoMuzzleFlash( void ); - virtual const char *GetTracerType( void ); - -protected: - virtual float GetShotSpeed() { return 0; } - - virtual Vector WorldBarrelPosition( void ); - void UpdateMatrix( void ); - - float GetNextAttack() const { return m_flNextAttack; } - virtual void SetNextAttack( float flWait ) { m_flNextAttack = flWait; } - - virtual void Fire( int bulletCount, const Vector &barrelEnd, const Vector &forward, CBaseEntity *pAttacker, bool bIgnoreSpread ); - void TankTrace( const Vector &vecStart, const Vector &vecForward, const Vector &vecSpread, trace_t &tr ); - int GetRandomBurst( void ); - float GetRandomFireTime( void ); - - void CalcPlayerCrosshairTarget( Vector *pVecTarget ); - void CalcNPCEnemyTarget( Vector *pVecTarget ); - - inline bool IsPlayerManned( void ) { return m_hController && m_hController->IsPlayer() && ( m_spawnflags & SF_TANK_PLAYER ); } - inline bool IsNPCManned( void ) { return m_hController && m_hController->MyNPCPointer() && ( m_spawnflags & SF_TANK_NPC ); } - -private: - void TrackTarget( void ); - int DrawDebugTextOverlays(void); - void DrawDebugGeometryOverlays(void); - - virtual void FiringSequence( const Vector &barrelEnd, const Vector &forward, CBaseEntity *pAttacker ); - - void StartRotSound( void ); - void StopRotSound( void ); - - // Input handlers. - void InputActivate( inputdata_t &inputdata ); - void InputDeactivate( inputdata_t &inputdata ); - void InputSetFireRate( inputdata_t &inputdata ); - void InputSetDamage( inputdata_t &inputdata ); - void InputSetTargetDir( inputdata_t &inputdata ); - void InputSetTargetPosition( inputdata_t &inputdata ); - void InputSetTargetEntityName( inputdata_t &inputdata ); - void InputSetTargetEntity( inputdata_t &inputdata ); - void InputClearTargetEntity( inputdata_t &inputdata ); - void InputFindNPCToManTank( inputdata_t &inputdata ); - void InputStopFindingNPCs( inputdata_t &inputdata ); - void InputStartFindingNPCs( inputdata_t &inputdata ); - void InputForceNPCOff( inputdata_t &inputdata ); - void InputSetMaxRange( inputdata_t &inputdata ); - - void TankActivate(void); - void TankDeactivate(void); - - inline bool CanFire( void ); - bool InRange( float range ); - bool InRange2( float flRange2 ); - - void TraceAttack( CBaseEntity *pAttacker, float flDamage, const Vector &vecDir, trace_t *ptr, int bitsDamageType); - - QAngle AimBarrelAt( const Vector &parentTarget ); - - DECLARE_DATADESC(); - - bool OnControls( CBaseEntity *pTest ); - bool HasController( void ); - - CBaseEntity *FindTarget( string_t targetName, CBaseEntity *pActivator ); - - // NPC - void NPC_FindController( void ); - bool NPC_InRoute( void ) { return m_bNPCInRoute; } - bool NPC_InterruptController( void ); - - // Aim the tank at the player crosshair - void AimBarrelAtPlayerCrosshair( QAngle *pAngles ); - - // Aim the tank at the NPC's enemy - void AimBarrelAtNPCEnemy( QAngle *pAngles ); - - // Aim the tank at the func_tank's enemy - void AimFuncTankAtTarget( void ); - - // Returns true if the desired angles are out of range - bool RotateTankToAngles( const QAngle &angles, float *pDistX = NULL, float *pDistY = NULL ); - - // We lost our target! - void LostTarget( void ); - - // Purpose: - void ComputeLeadingPosition( const Vector &vecShootPosition, CBaseEntity *pTarget, Vector *pLeadPosition ); - -protected: - virtual void ControllerPostFrame( void ); - - float m_fireLast; // Last time I fired - float m_fireRate; // How many rounds/second - - EHANDLE m_hTarget; - - TANKBULLET m_bulletType; // Bullet type - int m_iBulletDamage; // 0 means use Bullet type's default damage - int m_iBulletDamageVsPlayer; // Damage vs player. 0 means use m_iBulletDamage - -#ifdef HL2_EPISODIC - string_t m_iszAmmoType; // The name of the ammodef that we use when we fire. Bullet damage still comes from keyvalues. - int m_iAmmoType; // The cached index of the ammodef that we use when we fire. -#else - int m_iSmallAmmoType; - int m_iMediumAmmoType; - int m_iLargeAmmoType; -#endif // HL2_EPISODIC - - int m_spread; // firing spread - - EntityMatrix m_parentMatrix; - - Vector m_sightOrigin; // Last sight of target - EHANDLE m_hFuncTankTarget; - - int m_nBulletCount; - -private: - - // This is either the player manning the func_tank, or an NPC. The NPC is either manning the tank, or running - // to the man point. If he's en-route, m_bNPCInRoute will be true. - CHandle m_hController; - - float m_flNextAttack; - Vector m_vecControllerUsePos; - - float m_yawCenter; // "Center" yaw - float m_yawCenterWorld; // "Center" yaw in world space - float m_yawRate; // Max turn rate to track targets - float m_yawRange; // Range of turning motion (one-sided: 30 is +/- 30 degress from center) - // Zero is full rotation - float m_yawTolerance; // Tolerance angle - - float m_pitchCenter; // "Center" pitch - float m_pitchCenterWorld; // "Center" pitch in world space - float m_pitchRate; // Max turn rate on pitch - float m_pitchRange; // Range of pitch motion as above - float m_pitchTolerance; // Tolerance angle - - float m_fireTime; // How much time has been used to fire the weapon so far. - float m_lastSightTime;// Last time I saw target - float m_persist; // Persistence of firing (how long do I shoot when I can't see) - float m_persist2; // Secondary persistence of firing (randomly shooting when I can't see) - float m_persist2burst;// How long secondary persistence burst lasts - float m_minRange; // Minimum range to aim/track - float m_maxRange; // Max range to aim/track - float m_flMinRange2; - float m_flMaxRange2; - int m_iAmmoCount; // ammo - - Vector m_barrelPos; // Length of the freakin barrel - float m_spriteScale; // Scale of any sprites we shoot - string_t m_iszSpriteSmoke; - string_t m_iszSpriteFlash; - - string_t m_iszMaster; // Master entity (game_team_master or multisource) - - string_t m_soundStartRotate; - string_t m_soundStopRotate; - string_t m_soundLoopRotate; - - float m_flPlayerGracePeriod; - float m_flIgnoreGraceUpto; - float m_flPlayerLockTimeBeforeFire; - float m_flLastSawNonPlayer; - - string_t m_targetEntityName; - Vector m_vTargetPosition; - Vector m_vecNPCIdleTarget; - - // Used for when the gun is attached to another entity - string_t m_iszBarrelAttachment; - int m_nBarrelAttachment; - string_t m_iszBaseAttachment; - - // Used when the gun is actually a part of the parent entity, and pose params aim it - string_t m_iszYawPoseParam; - string_t m_iszPitchPoseParam; - float m_flYawPoseCenter; - float m_flPitchPoseCenter; - bool m_bUsePoseParameters; - - // Lead the target? - bool m_bPerformLeading; - float m_flStartLeadFactor; - float m_flStartLeadFactorTime; - float m_flNextLeadFactor; - float m_flNextLeadFactorTime; - - COutputEvent m_OnFire; - COutputEvent m_OnLoseTarget; - COutputEvent m_OnAquireTarget; - COutputEvent m_OnAmmoDepleted; - COutputEvent m_OnGotController; - COutputEvent m_OnLostController; - COutputEvent m_OnGotPlayerController; - COutputEvent m_OnLostPlayerController; - COutputEvent m_OnReadyToFire; - - CHandle m_hControlVolume; - string_t m_iszControlVolume; - - float m_flNextControllerSearch; - bool m_bShouldFindNPCs; - bool m_bNPCInRoute; - string_t m_iszNPCManPoint; - - bool m_bReadyToFire; - - int m_iEffectHandling; -}; - -#endif // FUNC_TANK_H \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#ifndef FUNC_TANK_H +#define FUNC_TANK_H +#ifdef _WIN32 +#pragma once +#endif + +#include "triggers.h" + +#define SF_TANK_ACTIVE 0x0001 +#define SF_TANK_PLAYER 0x0002 +#define SF_TANK_HUMANS 0x0004 +#define SF_TANK_ALIENS 0x0008 +#define SF_TANK_LINEOFSIGHT 0x0010 +#define SF_TANK_CANCONTROL 0x0020 +#define SF_TANK_DAMAGE_KICK 0x0040 // Kick when take damage +#define SF_TANK_AIM_AT_POS 0x0080 // Aim at a particular position +#define SF_TANK_AIM_ASSISTANCE 0x0100 +#define SF_TANK_NPC 0x0200 +#define SF_TANK_NPC_CONTROLLABLE 0x0400 // 1024 +#define SF_TANK_NPC_SET_CONTROLLER 0x0800 // 2048 +#define SF_TANK_ALLOW_PLAYER_HITS 0x1000 // 4096 Allow friendly NPCs to fire upon enemies near the player +#define SF_TANK_IGNORE_RANGE_IN_VIEWCONE 0x2000 // 8192 Don't use range as a factor in determining if something is in view cone +#define SF_TANK_NOTSOLID 0x8000 // 32768 +#define SF_TANK_SOUNDON 0x10000 // FIXME: This is not really a spawnflag! It holds transient state!!! +#define SF_TANK_HACKPLAYERHIT 0x20000 // 131072 Make this func_tank cheat and hit the player regularly + +#define FUNCTANK_DISTANCE_MAX 1200 // 100 ft. +#define FUNCTANK_DISTANCE_MIN_TO_ENEMY 180 +#define FUNCTANK_FIREVOLUME 1000 +#define FUNCTANK_NPC_ROUTE_TIME 5.0f + +// Effect handling +// If the func_tank has a chosen method of handling effects, use that +// instead of the individual effect settings. (muzzleflash, sound, tracer, etc) +enum FUNCTANK_EFFECT_HANDLING +{ + EH_NONE, // Use the effect settings + EH_AR2, // Use AR2 effects + EH_COMBINE_CANNON // Large Combine cannon +}; + +enum TANKBULLET +{ + TANK_BULLET_NONE = 0, + TANK_BULLET_SMALL = 1, + TANK_BULLET_MEDIUM = 2, + TANK_BULLET_LARGE = 3, +}; + +#define MORTAR_BLAST_RADIUS 350 + + +// Custom damage +// env_laser (duration is 0.5 rate of fire) +// rockets +// explosion? + +class CFuncTank : public CBaseEntity +{ + + DECLARE_CLASS( CFuncTank, CBaseEntity ); + +public: + + CFuncTank(); + ~CFuncTank(); + void Spawn( void ); + void Activate( void ); + void Precache( void ); + bool CreateVPhysics( void ); + bool KeyValue( const char *szKeyName, const char *szValue ); + void UpdateOnRemove(); + + int ObjectCaps( void ) + { + return ( BaseClass::ObjectCaps() | FCAP_IMPULSE_USE | FCAP_USE_IN_RADIUS ); + } + + void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); + void Think( void ); + + int GetAmmoCount( void ) { return m_iAmmoCount; } + + // NPC + bool NPC_FindManPoint( Vector &vecPos ); + bool NPC_HasEnemy( void ); + void NPC_Fire( void ); + void NPC_InterruptRoute( void ); + void NPC_JustSawPlayer( CBaseEntity *pTarget ); + void NPC_SetInRoute( bool bInRoute ) { m_bNPCInRoute = bInRoute; } + void NPC_SetIdleAngle( Vector vecIdle ) { m_vecNPCIdleTarget = vecIdle; } + + // LOS + bool IsEntityInViewCone( CBaseEntity *pEntity ); + bool HasLOSTo( CBaseEntity *pEntity ); + + // Controller + CBaseCombatCharacter *GetController( void ); + bool StartControl( CBaseCombatCharacter *pController ); + void StopControl( void ); + + const float YawCenter() const { return m_yawCenter; } + const float YawCenterWorld() const { return m_yawCenterWorld; } + const float YawRange() const { return m_yawRange; } + const float PitchCenter() const { return m_pitchCenter; } + const float PitchCenterWorld() const { return m_pitchCenterWorld; } + const float PitchRange() const { return m_pitchRange; } + + virtual void PhysicsSimulate( void ); + + virtual void OnStartControlled() {} + virtual void OnStopControlled() {} + + // SF Tests. + inline bool IsControllable( void ) { return ( m_spawnflags & SF_TANK_CANCONTROL ) ? true : false; } + inline bool IsActive( void ) { return ( m_spawnflags & SF_TANK_ACTIVE ) ? true : false; } + inline bool IsNPCControllable( void ) { return ( m_spawnflags & SF_TANK_NPC_CONTROLLABLE ) ? true : false; } + inline bool IsNPCSetController( void ) { return ( m_spawnflags & SF_TANK_NPC_SET_CONTROLLER ) ? true : false; } + + virtual void DoMuzzleFlash( void ); + virtual const char *GetTracerType( void ); + +protected: + virtual float GetShotSpeed() { return 0; } + + virtual Vector WorldBarrelPosition( void ); + void UpdateMatrix( void ); + + float GetNextAttack() const { return m_flNextAttack; } + virtual void SetNextAttack( float flWait ) { m_flNextAttack = flWait; } + + virtual void Fire( int bulletCount, const Vector &barrelEnd, const Vector &forward, CBaseEntity *pAttacker, bool bIgnoreSpread ); + void TankTrace( const Vector &vecStart, const Vector &vecForward, const Vector &vecSpread, trace_t &tr ); + int GetRandomBurst( void ); + float GetRandomFireTime( void ); + + void CalcPlayerCrosshairTarget( Vector *pVecTarget ); + void CalcNPCEnemyTarget( Vector *pVecTarget ); + + inline bool IsPlayerManned( void ) { return m_hController && m_hController->IsPlayer() && ( m_spawnflags & SF_TANK_PLAYER ); } + inline bool IsNPCManned( void ) { return m_hController && m_hController->MyNPCPointer() && ( m_spawnflags & SF_TANK_NPC ); } + +private: + void TrackTarget( void ); + int DrawDebugTextOverlays(void); + void DrawDebugGeometryOverlays(void); + + virtual void FiringSequence( const Vector &barrelEnd, const Vector &forward, CBaseEntity *pAttacker ); + + void StartRotSound( void ); + void StopRotSound( void ); + + // Input handlers. + void InputActivate( inputdata_t &inputdata ); + void InputDeactivate( inputdata_t &inputdata ); + void InputSetFireRate( inputdata_t &inputdata ); + void InputSetDamage( inputdata_t &inputdata ); + void InputSetTargetDir( inputdata_t &inputdata ); + void InputSetTargetPosition( inputdata_t &inputdata ); + void InputSetTargetEntityName( inputdata_t &inputdata ); + void InputSetTargetEntity( inputdata_t &inputdata ); + void InputClearTargetEntity( inputdata_t &inputdata ); + void InputFindNPCToManTank( inputdata_t &inputdata ); + void InputStopFindingNPCs( inputdata_t &inputdata ); + void InputStartFindingNPCs( inputdata_t &inputdata ); + void InputForceNPCOff( inputdata_t &inputdata ); + void InputSetMaxRange( inputdata_t &inputdata ); + + void TankActivate(void); + void TankDeactivate(void); + + inline bool CanFire( void ); + bool InRange( float range ); + bool InRange2( float flRange2 ); + + void TraceAttack( CBaseEntity *pAttacker, float flDamage, const Vector &vecDir, trace_t *ptr, int bitsDamageType); + + QAngle AimBarrelAt( const Vector &parentTarget ); + + DECLARE_DATADESC(); + + bool OnControls( CBaseEntity *pTest ); + bool HasController( void ); + + CBaseEntity *FindTarget( string_t targetName, CBaseEntity *pActivator ); + + // NPC + void NPC_FindController( void ); + bool NPC_InRoute( void ) { return m_bNPCInRoute; } + bool NPC_InterruptController( void ); + + // Aim the tank at the player crosshair + void AimBarrelAtPlayerCrosshair( QAngle *pAngles ); + + // Aim the tank at the NPC's enemy + void AimBarrelAtNPCEnemy( QAngle *pAngles ); + + // Aim the tank at the func_tank's enemy + void AimFuncTankAtTarget( void ); + + // Returns true if the desired angles are out of range + bool RotateTankToAngles( const QAngle &angles, float *pDistX = NULL, float *pDistY = NULL ); + + // We lost our target! + void LostTarget( void ); + + // Purpose: + void ComputeLeadingPosition( const Vector &vecShootPosition, CBaseEntity *pTarget, Vector *pLeadPosition ); + +protected: + virtual void ControllerPostFrame( void ); + + float m_fireLast; // Last time I fired + float m_fireRate; // How many rounds/second + + EHANDLE m_hTarget; + + TANKBULLET m_bulletType; // Bullet type + int m_iBulletDamage; // 0 means use Bullet type's default damage + int m_iBulletDamageVsPlayer; // Damage vs player. 0 means use m_iBulletDamage + +#ifdef HL2_EPISODIC + string_t m_iszAmmoType; // The name of the ammodef that we use when we fire. Bullet damage still comes from keyvalues. + int m_iAmmoType; // The cached index of the ammodef that we use when we fire. +#else + int m_iSmallAmmoType; + int m_iMediumAmmoType; + int m_iLargeAmmoType; +#endif // HL2_EPISODIC + + int m_spread; // firing spread + + EntityMatrix m_parentMatrix; + + Vector m_sightOrigin; // Last sight of target + EHANDLE m_hFuncTankTarget; + + int m_nBulletCount; + +private: + + // This is either the player manning the func_tank, or an NPC. The NPC is either manning the tank, or running + // to the man point. If he's en-route, m_bNPCInRoute will be true. + CHandle m_hController; + + float m_flNextAttack; + Vector m_vecControllerUsePos; + + float m_yawCenter; // "Center" yaw + float m_yawCenterWorld; // "Center" yaw in world space + float m_yawRate; // Max turn rate to track targets + float m_yawRange; // Range of turning motion (one-sided: 30 is +/- 30 degress from center) + // Zero is full rotation + float m_yawTolerance; // Tolerance angle + + float m_pitchCenter; // "Center" pitch + float m_pitchCenterWorld; // "Center" pitch in world space + float m_pitchRate; // Max turn rate on pitch + float m_pitchRange; // Range of pitch motion as above + float m_pitchTolerance; // Tolerance angle + + float m_fireTime; // How much time has been used to fire the weapon so far. + float m_lastSightTime;// Last time I saw target + float m_persist; // Persistence of firing (how long do I shoot when I can't see) + float m_persist2; // Secondary persistence of firing (randomly shooting when I can't see) + float m_persist2burst;// How long secondary persistence burst lasts + float m_minRange; // Minimum range to aim/track + float m_maxRange; // Max range to aim/track + float m_flMinRange2; + float m_flMaxRange2; + int m_iAmmoCount; // ammo + + Vector m_barrelPos; // Length of the freakin barrel + float m_spriteScale; // Scale of any sprites we shoot + string_t m_iszSpriteSmoke; + string_t m_iszSpriteFlash; + + string_t m_iszMaster; // Master entity (game_team_master or multisource) + + string_t m_soundStartRotate; + string_t m_soundStopRotate; + string_t m_soundLoopRotate; + + float m_flPlayerGracePeriod; + float m_flIgnoreGraceUpto; + float m_flPlayerLockTimeBeforeFire; + float m_flLastSawNonPlayer; + + string_t m_targetEntityName; + Vector m_vTargetPosition; + Vector m_vecNPCIdleTarget; + + // Used for when the gun is attached to another entity + string_t m_iszBarrelAttachment; + int m_nBarrelAttachment; + string_t m_iszBaseAttachment; + + // Used when the gun is actually a part of the parent entity, and pose params aim it + string_t m_iszYawPoseParam; + string_t m_iszPitchPoseParam; + float m_flYawPoseCenter; + float m_flPitchPoseCenter; + bool m_bUsePoseParameters; + + // Lead the target? + bool m_bPerformLeading; + float m_flStartLeadFactor; + float m_flStartLeadFactorTime; + float m_flNextLeadFactor; + float m_flNextLeadFactorTime; + + COutputEvent m_OnFire; + COutputEvent m_OnLoseTarget; + COutputEvent m_OnAquireTarget; + COutputEvent m_OnAmmoDepleted; + COutputEvent m_OnGotController; + COutputEvent m_OnLostController; + COutputEvent m_OnGotPlayerController; + COutputEvent m_OnLostPlayerController; + COutputEvent m_OnReadyToFire; + + CHandle m_hControlVolume; + string_t m_iszControlVolume; + + float m_flNextControllerSearch; + bool m_bShouldFindNPCs; + bool m_bNPCInRoute; + string_t m_iszNPCManPoint; + + bool m_bReadyToFire; + + int m_iEffectHandling; +}; + +#endif // FUNC_TANK_H diff --git a/dlls/hl2_dll/grenade_ar2.cpp b/dlls/hl2_dll/grenade_ar2.cpp index 97cbe70b..41406cad 100644 --- a/dlls/hl2_dll/grenade_ar2.cpp +++ b/dlls/hl2_dll/grenade_ar2.cpp @@ -152,7 +152,7 @@ void CGrenadeAR2::GrenadeAR2Think( void ) m_fDangerRadius += ( AR2_GRENADE_MAX_DANGER_RADIUS * 0.05 ); } - CSoundEnt::InsertSound( SOUND_DANGER, GetAbsOrigin() + GetAbsVelocity() * 0.5, m_fDangerRadius, 0.2, this, SOUNDENT_CHANNEL_REPEATED_DANGER ); + CSoundEnt::InsertSound( SOUND_DANGER, GetAbsOrigin() + GetAbsVelocity() * 0.5, static_cast(m_fDangerRadius), 0.2, this, SOUNDENT_CHANNEL_REPEATED_DANGER ); } void CGrenadeAR2::Event_Killed( const CTakeDamageInfo &info ) diff --git a/dlls/hl2_dll/grenade_bugbait.cpp b/dlls/hl2_dll/grenade_bugbait.cpp index b06c15e5..58f01dc2 100644 --- a/dlls/hl2_dll/grenade_bugbait.cpp +++ b/dlls/hl2_dll/grenade_bugbait.cpp @@ -161,7 +161,7 @@ void CGrenadeBugBait::BugBaitTouch( CBaseEntity *pOther ) pSporeExplosion->SetLocalOrigin( GetAbsOrigin() ); pSporeExplosion->m_flSpawnRate = 8.0f; pSporeExplosion->m_flParticleLifetime = 2.0f; - pSporeExplosion->SetRenderColor( 0.0f, 0.5f, 0.25f, 0.15f ); + pSporeExplosion->SetRenderColor( 0, 1, 0, 0 ); pSporeExplosion->m_flStartSize = 32.0f; pSporeExplosion->m_flEndSize = 64.0f; diff --git a/dlls/hl2_dll/grenade_frag.cpp b/dlls/hl2_dll/grenade_frag.cpp index 15ad3b75..0fbfe1a6 100644 --- a/dlls/hl2_dll/grenade_frag.cpp +++ b/dlls/hl2_dll/grenade_frag.cpp @@ -1,392 +1,392 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -//=============================================================================// - -#include "cbase.h" -#include "basegrenade_shared.h" -#include "grenade_frag.h" -#include "Sprite.h" -#include "SpriteTrail.h" -#include "soundent.h" - -// memdbgon must be the last include file in a .cpp file!!! -#include "tier0/memdbgon.h" - -#define FRAG_GRENADE_BLIP_FREQUENCY 1.0f -#define FRAG_GRENADE_BLIP_FAST_FREQUENCY 0.3f - -#define FRAG_GRENADE_GRACE_TIME_AFTER_PICKUP 1.5f - -const float GRENADE_COEFFICIENT_OF_RESTITUTION = 0.2f; - -ConVar sk_plr_dmg_fraggrenade ( "sk_plr_dmg_fraggrenade","0"); -ConVar sk_npc_dmg_fraggrenade ( "sk_npc_dmg_fraggrenade","0"); -ConVar sk_fraggrenade_radius ( "sk_fraggrenade_radius", "0"); - -#define GRENADE_MODEL "models/Weapons/w_grenade.mdl" - -class CGrenadeFrag : public CBaseGrenade -{ - DECLARE_CLASS( CGrenadeFrag, CBaseGrenade ); - -#if !defined( CLIENT_DLL ) - DECLARE_DATADESC(); -#endif - - ~CGrenadeFrag( void ); - -public: - void Spawn( void ); - void OnRestore( void ); - void Precache( void ); - bool CreateVPhysics( void ); - void CreateEffects( void ); - void SetTimer( float detonateDelay, float warnDelay ); - void SetVelocity( const Vector &velocity, const AngularImpulse &angVelocity ); - int OnTakeDamage( const CTakeDamageInfo &inputInfo ); - void BlipSound() { EmitSound( "Grenade.Blip" ); } - void DelayThink(); - void VPhysicsUpdate( IPhysicsObject *pPhysics ); - void OnPhysGunPickup( CBasePlayer *pPhysGunUser, PhysGunPickup_t reason ); - void SetCombineSpawned( bool combineSpawned ) { m_combineSpawned = combineSpawned; } - bool IsCombineSpawned( void ) const { return m_combineSpawned; } - void SetPunted( bool punt ) { m_punted = punt; } - bool WasPunted( void ) const { return m_punted; } - -protected: - CHandle m_pMainGlow; - CHandle m_pGlowTrail; - - float m_flNextBlipTime; - bool m_inSolid; - bool m_combineSpawned; - bool m_punted; -}; - -LINK_ENTITY_TO_CLASS( npc_grenade_frag, CGrenadeFrag ); - -BEGIN_DATADESC( CGrenadeFrag ) - - // Fields - DEFINE_FIELD( m_pMainGlow, FIELD_EHANDLE ), - DEFINE_FIELD( m_pGlowTrail, FIELD_EHANDLE ), - DEFINE_FIELD( m_flNextBlipTime, FIELD_TIME ), - DEFINE_FIELD( m_inSolid, FIELD_BOOLEAN ), - DEFINE_FIELD( m_combineSpawned, FIELD_BOOLEAN ), - DEFINE_FIELD( m_punted, FIELD_BOOLEAN ), - - // Function Pointers - DEFINE_THINKFUNC( DelayThink ), - -END_DATADESC() - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -CGrenadeFrag::~CGrenadeFrag( void ) -{ -} - -void CGrenadeFrag::Spawn( void ) -{ - Precache( ); - - SetModel( GRENADE_MODEL ); - - if( GetOwnerEntity() && GetOwnerEntity()->IsPlayer() ) - { - m_flDamage = sk_plr_dmg_fraggrenade.GetFloat(); - m_DmgRadius = sk_fraggrenade_radius.GetFloat(); - } - else - { - m_flDamage = sk_npc_dmg_fraggrenade.GetFloat(); - m_DmgRadius = sk_fraggrenade_radius.GetFloat(); - } - - m_takedamage = DAMAGE_YES; - m_iHealth = 1; - - SetSize( -Vector(4,4,4), Vector(4,4,4) ); - SetCollisionGroup( COLLISION_GROUP_WEAPON ); - CreateVPhysics(); - - CreateEffects(); - - BlipSound(); - m_flNextBlipTime = gpGlobals->curtime + FRAG_GRENADE_BLIP_FREQUENCY; - - AddSolidFlags( FSOLID_NOT_STANDABLE ); - - m_combineSpawned = false; - m_punted = false; - - BaseClass::Spawn(); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CGrenadeFrag::OnRestore( void ) -{ - CreateEffects(); - - BaseClass::OnRestore(); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CGrenadeFrag::CreateEffects( void ) -{ - // Start up the eye glow - m_pMainGlow = CSprite::SpriteCreate( "sprites/redglow1.vmt", GetLocalOrigin(), false ); - - int nAttachment = LookupAttachment( "fuse" ); - - if ( m_pMainGlow != NULL ) - { - m_pMainGlow->FollowEntity( this ); - m_pMainGlow->SetAttachment( this, nAttachment ); - m_pMainGlow->SetTransparency( kRenderGlow, 255, 255, 255, 200, kRenderFxNoDissipation ); - m_pMainGlow->SetScale( 0.2f ); - m_pMainGlow->SetGlowProxySize( 4.0f ); - } - - // Start up the eye trail - m_pGlowTrail = CSpriteTrail::SpriteTrailCreate( "sprites/bluelaser1.vmt", GetLocalOrigin(), false ); - - if ( m_pGlowTrail != NULL ) - { - m_pGlowTrail->FollowEntity( this ); - m_pGlowTrail->SetAttachment( this, nAttachment ); - m_pGlowTrail->SetTransparency( kRenderTransAdd, 255, 0, 0, 255, kRenderFxNone ); - m_pGlowTrail->SetStartWidth( 8.0f ); - m_pGlowTrail->SetEndWidth( 1.0f ); - m_pGlowTrail->SetLifeTime( 0.5f ); - } -} - -bool CGrenadeFrag::CreateVPhysics() -{ - // Create the object in the physics system - VPhysicsInitNormal( SOLID_BBOX, 0, false ); - return true; -} - -// this will hit only things that are in newCollisionGroup, but NOT in collisionGroupAlreadyChecked -class CTraceFilterCollisionGroupDelta : public CTraceFilterEntitiesOnly -{ -public: - // It does have a base, but we'll never network anything below here.. - DECLARE_CLASS_NOBASE( CTraceFilterCollisionGroupDelta ); - - CTraceFilterCollisionGroupDelta( const IHandleEntity *passentity, int collisionGroupAlreadyChecked, int newCollisionGroup ) - : m_pPassEnt(passentity), m_collisionGroupAlreadyChecked( collisionGroupAlreadyChecked ), m_newCollisionGroup( newCollisionGroup ) - { - } - - virtual bool ShouldHitEntity( IHandleEntity *pHandleEntity, int contentsMask ) - { - if ( !PassServerEntityFilter( pHandleEntity, m_pPassEnt ) ) - return false; - CBaseEntity *pEntity = EntityFromEntityHandle( pHandleEntity ); - - if ( pEntity ) - { - if ( g_pGameRules->ShouldCollide( m_collisionGroupAlreadyChecked, pEntity->GetCollisionGroup() ) ) - return false; - if ( g_pGameRules->ShouldCollide( m_newCollisionGroup, pEntity->GetCollisionGroup() ) ) - return true; - } - - return false; - } - -protected: - const IHandleEntity *m_pPassEnt; - int m_collisionGroupAlreadyChecked; - int m_newCollisionGroup; -}; - -void CGrenadeFrag::VPhysicsUpdate( IPhysicsObject *pPhysics ) -{ - BaseClass::VPhysicsUpdate( pPhysics ); - Vector vel; - AngularImpulse angVel; - pPhysics->GetVelocity( &vel, &angVel ); - - Vector start = GetAbsOrigin(); - // find all entities that my collision group wouldn't hit, but COLLISION_GROUP_NONE would and bounce off of them as a ray cast - CTraceFilterCollisionGroupDelta filter( this, GetCollisionGroup(), COLLISION_GROUP_NONE ); - trace_t tr; - - // UNDONE: Hull won't work with hitboxes - hits outer hull. But the whole point of this test is to hit hitboxes. -#if 0 - UTIL_TraceHull( start, start + vel * gpGlobals->frametime, CollisionProp()->OBBMins(), CollisionProp()->OBBMaxs(), CONTENTS_HITBOX|CONTENTS_MONSTER|CONTENTS_SOLID, &filter, &tr ); -#else - UTIL_TraceLine( start, start + vel * gpGlobals->frametime, CONTENTS_HITBOX|CONTENTS_MONSTER|CONTENTS_SOLID, &filter, &tr ); -#endif - if ( tr.startsolid ) - { - if ( !m_inSolid ) - { - // UNDONE: Do a better contact solution that uses relative velocity? - vel *= -GRENADE_COEFFICIENT_OF_RESTITUTION; // bounce backwards - pPhysics->SetVelocity( &vel, NULL ); - } - m_inSolid = true; - return; - } - m_inSolid = false; - if ( tr.DidHit() ) - { - Vector dir = vel; - VectorNormalize(dir); - // send a tiny amount of damage so the character will react to getting bonked - CTakeDamageInfo info( this, GetThrower(), pPhysics->GetMass() * vel, GetAbsOrigin(), 0.1f, DMG_CRUSH ); - tr.m_pEnt->TakeDamage( info ); - - // reflect velocity around normal - vel = -2.0f * tr.plane.normal * DotProduct(vel,tr.plane.normal) + vel; - - // absorb 80% in impact - vel *= GRENADE_COEFFICIENT_OF_RESTITUTION; - angVel *= -0.5f; - pPhysics->SetVelocity( &vel, &angVel ); - } -} - - -void CGrenadeFrag::Precache( void ) -{ - PrecacheModel( GRENADE_MODEL ); - - PrecacheScriptSound( "Grenade.Blip" ); - - PrecacheModel( "sprites/redglow1.vmt" ); - PrecacheModel( "sprites/bluelaser1.vmt" ); - - BaseClass::Precache(); -} - -void CGrenadeFrag::SetTimer( float detonateDelay, float warnDelay ) -{ - m_flDetonateTime = gpGlobals->curtime + detonateDelay; - m_flWarnAITime = gpGlobals->curtime + warnDelay; - SetThink( &CGrenadeFrag::DelayThink ); - SetNextThink( gpGlobals->curtime ); -} - -void CGrenadeFrag::OnPhysGunPickup( CBasePlayer *pPhysGunUser, PhysGunPickup_t reason ) -{ -#ifdef HL2MP - SetTimer( FRAG_GRENADE_GRACE_TIME_AFTER_PICKUP, FRAG_GRENADE_GRACE_TIME_AFTER_PICKUP / 2); - SetThrower( pPhysGunUser ); - - BlipSound(); - m_flNextBlipTime = gpGlobals->curtime + FRAG_GRENADE_BLIP_FAST_FREQUENCY; - m_bHasWarnedAI = true; -#endif - -#ifdef HL2_EPISODIC - SetThrower( pPhysGunUser ); - SetPunted( true ); -#endif - - BaseClass::OnPhysGunPickup( pPhysGunUser, reason ); -} - -void CGrenadeFrag::DelayThink() -{ - if( gpGlobals->curtime > m_flDetonateTime ) - { - Detonate(); - return; - } - - if( !m_bHasWarnedAI && gpGlobals->curtime >= m_flWarnAITime ) - { -#if !defined( CLIENT_DLL ) - CSoundEnt::InsertSound ( SOUND_DANGER, GetAbsOrigin(), 400, 1.5, this ); -#endif - m_bHasWarnedAI = true; - } - - if( gpGlobals->curtime > m_flNextBlipTime ) - { - BlipSound(); - - if( m_bHasWarnedAI ) - { - m_flNextBlipTime = gpGlobals->curtime + FRAG_GRENADE_BLIP_FAST_FREQUENCY; - } - else - { - m_flNextBlipTime = gpGlobals->curtime + FRAG_GRENADE_BLIP_FREQUENCY; - } - } - - SetNextThink( gpGlobals->curtime + 0.1 ); -} - -void CGrenadeFrag::SetVelocity( const Vector &velocity, const AngularImpulse &angVelocity ) -{ - IPhysicsObject *pPhysicsObject = VPhysicsGetObject(); - if ( pPhysicsObject ) - { - pPhysicsObject->AddVelocity( &velocity, &angVelocity ); - } -} - -int CGrenadeFrag::OnTakeDamage( const CTakeDamageInfo &inputInfo ) -{ - // Manually apply vphysics because BaseCombatCharacter takedamage doesn't call back to CBaseEntity OnTakeDamage - VPhysicsTakeDamage( inputInfo ); - - // Grenades only suffer blast damage and burn damage. - if( !(inputInfo.GetDamageType() & (DMG_BLAST|DMG_BURN) ) ) - return 0; - - return BaseClass::OnTakeDamage( inputInfo ); -} - -CBaseGrenade *Fraggrenade_Create( const Vector &position, const QAngle &angles, const Vector &velocity, const AngularImpulse &angVelocity, CBaseEntity *pOwner, float timer, bool combineSpawned ) -{ - // Don't set the owner here, or the player can't interact with grenades he's thrown - CGrenadeFrag *pGrenade = (CGrenadeFrag *)CBaseEntity::Create( "npc_grenade_frag", position, angles, pOwner ); - - pGrenade->SetTimer( timer, timer - 1.5f ); - pGrenade->SetVelocity( velocity, angVelocity ); - pGrenade->SetThrower( ToBaseCombatCharacter( pOwner ) ); - pGrenade->m_takedamage = DAMAGE_EVENTS_ONLY; - pGrenade->SetCombineSpawned( combineSpawned ); - - return pGrenade; -} - -bool Fraggrenade_WasPunted( const CBaseEntity *pEntity ) -{ - const CGrenadeFrag *pFrag = dynamic_cast( pEntity ); - if ( pFrag ) - { - return pFrag->WasPunted(); - } - - return false; -} - -bool Fraggrenade_WasCreatedByCombine( const CBaseEntity *pEntity ) -{ - const CGrenadeFrag *pFrag = dynamic_cast( pEntity ); - if ( pFrag ) - { - return pFrag->IsCombineSpawned(); - } - - return false; -} \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include "cbase.h" +#include "basegrenade_shared.h" +#include "grenade_frag.h" +#include "Sprite.h" +#include "SpriteTrail.h" +#include "soundent.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +#define FRAG_GRENADE_BLIP_FREQUENCY 1.0f +#define FRAG_GRENADE_BLIP_FAST_FREQUENCY 0.3f + +#define FRAG_GRENADE_GRACE_TIME_AFTER_PICKUP 1.5f + +const float GRENADE_COEFFICIENT_OF_RESTITUTION = 0.2f; + +ConVar sk_plr_dmg_fraggrenade ( "sk_plr_dmg_fraggrenade","0"); +ConVar sk_npc_dmg_fraggrenade ( "sk_npc_dmg_fraggrenade","0"); +ConVar sk_fraggrenade_radius ( "sk_fraggrenade_radius", "0"); + +#define GRENADE_MODEL "models/Weapons/w_grenade.mdl" + +class CGrenadeFrag : public CBaseGrenade +{ + DECLARE_CLASS( CGrenadeFrag, CBaseGrenade ); + +#if !defined( CLIENT_DLL ) + DECLARE_DATADESC(); +#endif + + ~CGrenadeFrag( void ); + +public: + void Spawn( void ); + void OnRestore( void ); + void Precache( void ); + bool CreateVPhysics( void ); + void CreateEffects( void ); + void SetTimer( float detonateDelay, float warnDelay ); + void SetVelocity( const Vector &velocity, const AngularImpulse &angVelocity ); + int OnTakeDamage( const CTakeDamageInfo &inputInfo ); + void BlipSound() { EmitSound( "Grenade.Blip" ); } + void DelayThink(); + void VPhysicsUpdate( IPhysicsObject *pPhysics ); + void OnPhysGunPickup( CBasePlayer *pPhysGunUser, PhysGunPickup_t reason ); + void SetCombineSpawned( bool combineSpawned ) { m_combineSpawned = combineSpawned; } + bool IsCombineSpawned( void ) const { return m_combineSpawned; } + void SetPunted( bool punt ) { m_punted = punt; } + bool WasPunted( void ) const { return m_punted; } + +protected: + CHandle m_pMainGlow; + CHandle m_pGlowTrail; + + float m_flNextBlipTime; + bool m_inSolid; + bool m_combineSpawned; + bool m_punted; +}; + +LINK_ENTITY_TO_CLASS( npc_grenade_frag, CGrenadeFrag ); + +BEGIN_DATADESC( CGrenadeFrag ) + + // Fields + DEFINE_FIELD( m_pMainGlow, FIELD_EHANDLE ), + DEFINE_FIELD( m_pGlowTrail, FIELD_EHANDLE ), + DEFINE_FIELD( m_flNextBlipTime, FIELD_TIME ), + DEFINE_FIELD( m_inSolid, FIELD_BOOLEAN ), + DEFINE_FIELD( m_combineSpawned, FIELD_BOOLEAN ), + DEFINE_FIELD( m_punted, FIELD_BOOLEAN ), + + // Function Pointers + DEFINE_THINKFUNC( DelayThink ), + +END_DATADESC() + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CGrenadeFrag::~CGrenadeFrag( void ) +{ +} + +void CGrenadeFrag::Spawn( void ) +{ + Precache( ); + + SetModel( GRENADE_MODEL ); + + if( GetOwnerEntity() && GetOwnerEntity()->IsPlayer() ) + { + m_flDamage = sk_plr_dmg_fraggrenade.GetFloat(); + m_DmgRadius = sk_fraggrenade_radius.GetFloat(); + } + else + { + m_flDamage = sk_npc_dmg_fraggrenade.GetFloat(); + m_DmgRadius = sk_fraggrenade_radius.GetFloat(); + } + + m_takedamage = DAMAGE_YES; + m_iHealth = 1; + + SetSize( -Vector(4,4,4), Vector(4,4,4) ); + SetCollisionGroup( COLLISION_GROUP_WEAPON ); + CreateVPhysics(); + + CreateEffects(); + + BlipSound(); + m_flNextBlipTime = gpGlobals->curtime + FRAG_GRENADE_BLIP_FREQUENCY; + + AddSolidFlags( FSOLID_NOT_STANDABLE ); + + m_combineSpawned = false; + m_punted = false; + + BaseClass::Spawn(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CGrenadeFrag::OnRestore( void ) +{ + CreateEffects(); + + BaseClass::OnRestore(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CGrenadeFrag::CreateEffects( void ) +{ + // Start up the eye glow + m_pMainGlow = CSprite::SpriteCreate( "sprites/redglow1.vmt", GetLocalOrigin(), false ); + + int nAttachment = LookupAttachment( "fuse" ); + + if ( m_pMainGlow != NULL ) + { + m_pMainGlow->FollowEntity( this ); + m_pMainGlow->SetAttachment( this, nAttachment ); + m_pMainGlow->SetTransparency( kRenderGlow, 255, 255, 255, 200, kRenderFxNoDissipation ); + m_pMainGlow->SetScale( 0.2f ); + m_pMainGlow->SetGlowProxySize( 4.0f ); + } + + // Start up the eye trail + m_pGlowTrail = CSpriteTrail::SpriteTrailCreate( "sprites/bluelaser1.vmt", GetLocalOrigin(), false ); + + if ( m_pGlowTrail != NULL ) + { + m_pGlowTrail->FollowEntity( this ); + m_pGlowTrail->SetAttachment( this, nAttachment ); + m_pGlowTrail->SetTransparency( kRenderTransAdd, 255, 0, 0, 255, kRenderFxNone ); + m_pGlowTrail->SetStartWidth( 8.0f ); + m_pGlowTrail->SetEndWidth( 1.0f ); + m_pGlowTrail->SetLifeTime( 0.5f ); + } +} + +bool CGrenadeFrag::CreateVPhysics() +{ + // Create the object in the physics system + VPhysicsInitNormal( SOLID_BBOX, 0, false ); + return true; +} + +// this will hit only things that are in newCollisionGroup, but NOT in collisionGroupAlreadyChecked +class CTraceFilterCollisionGroupDelta : public CTraceFilterEntitiesOnly +{ +public: + // It does have a base, but we'll never network anything below here.. + DECLARE_CLASS_NOBASE( CTraceFilterCollisionGroupDelta ); + + CTraceFilterCollisionGroupDelta( const IHandleEntity *passentity, int collisionGroupAlreadyChecked, int newCollisionGroup ) + : m_pPassEnt(passentity), m_collisionGroupAlreadyChecked( collisionGroupAlreadyChecked ), m_newCollisionGroup( newCollisionGroup ) + { + } + + virtual bool ShouldHitEntity( IHandleEntity *pHandleEntity, int contentsMask ) + { + if ( !PassServerEntityFilter( pHandleEntity, m_pPassEnt ) ) + return false; + CBaseEntity *pEntity = EntityFromEntityHandle( pHandleEntity ); + + if ( pEntity ) + { + if ( g_pGameRules->ShouldCollide( m_collisionGroupAlreadyChecked, pEntity->GetCollisionGroup() ) ) + return false; + if ( g_pGameRules->ShouldCollide( m_newCollisionGroup, pEntity->GetCollisionGroup() ) ) + return true; + } + + return false; + } + +protected: + const IHandleEntity *m_pPassEnt; + int m_collisionGroupAlreadyChecked; + int m_newCollisionGroup; +}; + +void CGrenadeFrag::VPhysicsUpdate( IPhysicsObject *pPhysics ) +{ + BaseClass::VPhysicsUpdate( pPhysics ); + Vector vel; + AngularImpulse angVel; + pPhysics->GetVelocity( &vel, &angVel ); + + Vector start = GetAbsOrigin(); + // find all entities that my collision group wouldn't hit, but COLLISION_GROUP_NONE would and bounce off of them as a ray cast + CTraceFilterCollisionGroupDelta filter( this, GetCollisionGroup(), COLLISION_GROUP_NONE ); + trace_t tr; + + // UNDONE: Hull won't work with hitboxes - hits outer hull. But the whole point of this test is to hit hitboxes. +#if 0 + UTIL_TraceHull( start, start + vel * gpGlobals->frametime, CollisionProp()->OBBMins(), CollisionProp()->OBBMaxs(), CONTENTS_HITBOX|CONTENTS_MONSTER|CONTENTS_SOLID, &filter, &tr ); +#else + UTIL_TraceLine( start, start + vel * gpGlobals->frametime, CONTENTS_HITBOX|CONTENTS_MONSTER|CONTENTS_SOLID, &filter, &tr ); +#endif + if ( tr.startsolid ) + { + if ( !m_inSolid ) + { + // UNDONE: Do a better contact solution that uses relative velocity? + vel *= -GRENADE_COEFFICIENT_OF_RESTITUTION; // bounce backwards + pPhysics->SetVelocity( &vel, NULL ); + } + m_inSolid = true; + return; + } + m_inSolid = false; + if ( tr.DidHit() ) + { + Vector dir = vel; + VectorNormalize(dir); + // send a tiny amount of damage so the character will react to getting bonked + CTakeDamageInfo info( this, GetThrower(), pPhysics->GetMass() * vel, GetAbsOrigin(), 0.1f, DMG_CRUSH ); + tr.m_pEnt->TakeDamage( info ); + + // reflect velocity around normal + vel = -2.0f * tr.plane.normal * DotProduct(vel,tr.plane.normal) + vel; + + // absorb 80% in impact + vel *= GRENADE_COEFFICIENT_OF_RESTITUTION; + angVel *= -0.5f; + pPhysics->SetVelocity( &vel, &angVel ); + } +} + + +void CGrenadeFrag::Precache( void ) +{ + PrecacheModel( GRENADE_MODEL ); + + PrecacheScriptSound( "Grenade.Blip" ); + + PrecacheModel( "sprites/redglow1.vmt" ); + PrecacheModel( "sprites/bluelaser1.vmt" ); + + BaseClass::Precache(); +} + +void CGrenadeFrag::SetTimer( float detonateDelay, float warnDelay ) +{ + m_flDetonateTime = gpGlobals->curtime + detonateDelay; + m_flWarnAITime = gpGlobals->curtime + warnDelay; + SetThink( &CGrenadeFrag::DelayThink ); + SetNextThink( gpGlobals->curtime ); +} + +void CGrenadeFrag::OnPhysGunPickup( CBasePlayer *pPhysGunUser, PhysGunPickup_t reason ) +{ +#ifdef HL2MP + SetTimer( FRAG_GRENADE_GRACE_TIME_AFTER_PICKUP, FRAG_GRENADE_GRACE_TIME_AFTER_PICKUP / 2); + SetThrower( pPhysGunUser ); + + BlipSound(); + m_flNextBlipTime = gpGlobals->curtime + FRAG_GRENADE_BLIP_FAST_FREQUENCY; + m_bHasWarnedAI = true; +#endif + +#ifdef HL2_EPISODIC + SetThrower( pPhysGunUser ); + SetPunted( true ); +#endif + + BaseClass::OnPhysGunPickup( pPhysGunUser, reason ); +} + +void CGrenadeFrag::DelayThink() +{ + if( gpGlobals->curtime > m_flDetonateTime ) + { + Detonate(); + return; + } + + if( !m_bHasWarnedAI && gpGlobals->curtime >= m_flWarnAITime ) + { +#if !defined( CLIENT_DLL ) + CSoundEnt::InsertSound ( SOUND_DANGER, GetAbsOrigin(), 400, 1.5, this ); +#endif + m_bHasWarnedAI = true; + } + + if( gpGlobals->curtime > m_flNextBlipTime ) + { + BlipSound(); + + if( m_bHasWarnedAI ) + { + m_flNextBlipTime = gpGlobals->curtime + FRAG_GRENADE_BLIP_FAST_FREQUENCY; + } + else + { + m_flNextBlipTime = gpGlobals->curtime + FRAG_GRENADE_BLIP_FREQUENCY; + } + } + + SetNextThink( gpGlobals->curtime + 0.1 ); +} + +void CGrenadeFrag::SetVelocity( const Vector &velocity, const AngularImpulse &angVelocity ) +{ + IPhysicsObject *pPhysicsObject = VPhysicsGetObject(); + if ( pPhysicsObject ) + { + pPhysicsObject->AddVelocity( &velocity, &angVelocity ); + } +} + +int CGrenadeFrag::OnTakeDamage( const CTakeDamageInfo &inputInfo ) +{ + // Manually apply vphysics because BaseCombatCharacter takedamage doesn't call back to CBaseEntity OnTakeDamage + VPhysicsTakeDamage( inputInfo ); + + // Grenades only suffer blast damage and burn damage. + if( !(inputInfo.GetDamageType() & (DMG_BLAST|DMG_BURN) ) ) + return 0; + + return BaseClass::OnTakeDamage( inputInfo ); +} + +CBaseGrenade *Fraggrenade_Create( const Vector &position, const QAngle &angles, const Vector &velocity, const AngularImpulse &angVelocity, CBaseEntity *pOwner, float timer, bool combineSpawned ) +{ + // Don't set the owner here, or the player can't interact with grenades he's thrown + CGrenadeFrag *pGrenade = (CGrenadeFrag *)CBaseEntity::Create( "npc_grenade_frag", position, angles, pOwner ); + + pGrenade->SetTimer( timer, timer - 1.5f ); + pGrenade->SetVelocity( velocity, angVelocity ); + pGrenade->SetThrower( ToBaseCombatCharacter( pOwner ) ); + pGrenade->m_takedamage = DAMAGE_EVENTS_ONLY; + pGrenade->SetCombineSpawned( combineSpawned ); + + return pGrenade; +} + +bool Fraggrenade_WasPunted( const CBaseEntity *pEntity ) +{ + const CGrenadeFrag *pFrag = dynamic_cast( pEntity ); + if ( pFrag ) + { + return pFrag->WasPunted(); + } + + return false; +} + +bool Fraggrenade_WasCreatedByCombine( const CBaseEntity *pEntity ) +{ + const CGrenadeFrag *pFrag = dynamic_cast( pEntity ); + if ( pFrag ) + { + return pFrag->IsCombineSpawned(); + } + + return false; +} diff --git a/dlls/hl2_dll/grenade_homer.cpp b/dlls/hl2_dll/grenade_homer.cpp index cb866ca8..08f60910 100644 --- a/dlls/hl2_dll/grenade_homer.cpp +++ b/dlls/hl2_dll/grenade_homer.cpp @@ -145,7 +145,7 @@ void CGrenadeHomer::SetSpin(float flSpinMagnitude, float flSpinSpeed) { m_flSpinMagnitude = flSpinMagnitude; m_flSpinSpeed = flSpinSpeed; - m_flSpinOffset = random->RandomInt(-m_flSpinSpeed,m_flSpinSpeed); + m_flSpinOffset = random->RandomFloat(-m_flSpinSpeed,m_flSpinSpeed); } //------------------------------------------------------------------------------ diff --git a/dlls/hl2_dll/hl2_client.cpp b/dlls/hl2_dll/hl2_client.cpp index 9169891e..e4abea42 100644 --- a/dlls/hl2_dll/hl2_client.cpp +++ b/dlls/hl2_dll/hl2_client.cpp @@ -18,7 +18,7 @@ #include "hl2_gamerules.h" #include "gamerules.h" #include "teamplay_gamerules.h" -#include "EntityList.h" +#include "entitylist.h" #include "physics.h" #include "game.h" #include "player_resource.h" diff --git a/dlls/hl2_dll/hl2_player.cpp b/dlls/hl2_dll/hl2_player.cpp index 873d6a6d..65a29574 100644 --- a/dlls/hl2_dll/hl2_player.cpp +++ b/dlls/hl2_dll/hl2_player.cpp @@ -1,3465 +1,3465 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: Player for HL2. -// -//=============================================================================// - -#include "cbase.h" -#include "hl2_player.h" -#include "globalstate.h" -#include "game.h" -#include "gamerules.h" -#include "trains.h" -#include "basehlcombatweapon_shared.h" -#include "vcollide_parse.h" -#include "in_buttons.h" -#include "ai_interactions.h" -#include "ai_squad.h" -#include "igamemovement.h" -#include "ai_hull.h" -#include "hl2_shareddefs.h" -#include "info_camera_link.h" -#include "point_camera.h" -#include "engine/IEngineSound.h" -#include "ndebugoverlay.h" -#include "iservervehicle.h" -#include "IVehicle.h" -#include "globals.h" -#include "collisionutils.h" -#include "coordsize.h" -#include "effect_color_tables.h" -#include "vphysics/player_controller.h" -#include "player_pickup.h" -#include "weapon_physcannon.h" -#include "script_intro.h" -#include "effect_dispatch_data.h" -#include "te_effect_dispatch.h" -#include "ai_basenpc.h" -#include "AI_Criteria.h" -#include "npc_barnacle.h" -#include "entitylist.h" -#include "env_zoom.h" -#include "hl2_gamerules.h" -#include "prop_combine_ball.h" -#include "datacache/imdlcache.h" -#include "eventqueue.h" - -#ifdef HL2_EPISODIC -#include "npc_alyx_episodic.h" -#endif - -// memdbgon must be the last include file in a .cpp file!!! -#include "tier0/memdbgon.h" - -extern ConVar weapon_showproficiency; -extern ConVar autoaim_max_dist; - -// Do not touch with without seeing me, please! (sjb) -// For consistency's sake, enemy gunfire is traced against a scaled down -// version of the player's hull, not the hitboxes for the player's model -// because the player isn't aware of his model, and can't do anything about -// preventing headshots and other such things. Also, game difficulty will -// not change if the model changes. This is the value by which to scale -// the X/Y of the player's hull to get the volume to trace bullets against. -#define PLAYER_HULL_REDUCTION 0.70 - -// This switches between the single primary weapon, and multiple weapons with buckets approach (jdw) -#define HL2_SINGLE_PRIMARY_WEAPON_MODE 0 - -#define TIME_IGNORE_FALL_DAMAGE 10.0 - -extern int gEvilImpulse101; - -ConVar sv_autojump( "sv_autojump", "0" ); - -ConVar hl2_walkspeed( "hl2_walkspeed", "150" ); -ConVar hl2_normspeed( "hl2_normspeed", "190" ); -ConVar hl2_sprintspeed( "hl2_sprintspeed", "320" ); - -ConVar hl2_xbox_aiming( "hl2_xbox_aiming", "1" ); - -ConVar hl2_darkness_flashlight_factor ( "hl2_darkness_flashlight_factor", "1" ); - -#ifdef HL2MP - #define HL2_WALK_SPEED 150 - #define HL2_NORM_SPEED 190 - #define HL2_SPRINT_SPEED 320 -#else - #define HL2_WALK_SPEED hl2_walkspeed.GetFloat() - #define HL2_NORM_SPEED hl2_normspeed.GetFloat() - #define HL2_SPRINT_SPEED hl2_sprintspeed.GetFloat() -#endif - -#ifdef _XBOX - #define AUTOSPRINT_MIN_DURATION 1.0f -#endif//_XBOX - -ConVar player_showpredictedposition( "player_showpredictedposition", "0" ); -ConVar player_showpredictedposition_timestep( "player_showpredictedposition_timestep", "1.0" ); - -ConVar player_squad_transient_commands( "player_squad_transient_commands", "1", FCVAR_REPLICATED ); -ConVar player_squad_double_tap_time( "player_squad_double_tap_time", "0.25" ); - -ConVar sv_infinite_aux_power( "sv_infinite_aux_power", "0", FCVAR_CHEAT ); - -ConVar autoaim_unlock_target( "autoaim_unlock_target", "0.8666" ); - -//------------------------------------------------------------------------------ -//------------------------------------------------------------------------------ -void CC_ToggleZoom( void ) -{ - CBasePlayer* pPlayer = UTIL_GetCommandClient(); - - if( pPlayer ) - { - CHL2_Player *pHL2Player = dynamic_cast(pPlayer); - - if( pHL2Player && pHL2Player->IsSuitEquipped() ) - { - pHL2Player->ToggleZoom(); - } - } -} - -static ConCommand toggle_zoom("toggle_zoom", CC_ToggleZoom, "Toggles zoom display" ); - -// ConVar cl_forwardspeed( "cl_forwardspeed", "400", FCVAR_CHEAT ); // Links us to the client's version -ConVar xc_crouch_range( "xc_crouch_range", "0.85", FCVAR_ARCHIVE, "Percentarge [1..0] of joystick range to allow ducking within" ); // Only 1/2 of the range is used -ConVar xc_use_crouch_limiter( "xc_use_crouch_limiter", "0", FCVAR_ARCHIVE, "Use the crouch limiting logic on the controller" ); - -//------------------------------------------------------------------------------ -//------------------------------------------------------------------------------ -void CC_ToggleDuck( void ) -{ - CBasePlayer* pPlayer = UTIL_GetCommandClient(); - if ( pPlayer == NULL ) - return; - - // Cannot be frozen - if ( pPlayer->GetFlags() & FL_FROZEN ) - return; - - static bool bChecked = false; - static ConVar *pCVcl_forwardspeed = NULL; - if ( !bChecked ) - { - bChecked = true; - pCVcl_forwardspeed = ( ConVar * )cvar->FindVar( "cl_forwardspeed" ); - } - - - // If we're not ducked, do extra checking - if ( xc_use_crouch_limiter.GetBool() ) - { - if ( pPlayer->GetToggledDuckState() == false ) - { - float flForwardSpeed = 400.0f; - if ( pCVcl_forwardspeed ) - { - flForwardSpeed = pCVcl_forwardspeed->GetFloat(); - } - - flForwardSpeed = max( 1.0f, flForwardSpeed ); - - // Make sure we're not in the blindspot on the crouch detection - float flStickDistPerc = ( pPlayer->GetStickDist() / flForwardSpeed ); // Speed is the magnitude - if ( flStickDistPerc > xc_crouch_range.GetFloat() ) - return; - } - } - - // Toggle the duck - pPlayer->ToggleDuck(); -} - -static ConCommand toggle_duck("toggle_duck", CC_ToggleDuck, "Toggles duck" ); - -#ifndef HL2MP -LINK_ENTITY_TO_CLASS( player, CHL2_Player ); -#endif - -PRECACHE_REGISTER(player); - -CBaseEntity *FindEntityForward( CBasePlayer *pMe, bool fHull ); - -BEGIN_SIMPLE_DATADESC( LadderMove_t ) - DEFINE_FIELD( m_bForceLadderMove, FIELD_BOOLEAN ), - DEFINE_FIELD( m_bForceMount, FIELD_BOOLEAN ), - DEFINE_FIELD( m_flStartTime, FIELD_TIME ), - DEFINE_FIELD( m_flArrivalTime, FIELD_TIME ), - DEFINE_FIELD( m_vecGoalPosition, FIELD_POSITION_VECTOR ), - DEFINE_FIELD( m_vecStartPosition, FIELD_POSITION_VECTOR ), - DEFINE_FIELD( m_hForceLadder, FIELD_EHANDLE ), - DEFINE_FIELD( m_hReservedSpot, FIELD_EHANDLE ), -END_DATADESC() - -// Global Savedata for HL2 player -BEGIN_DATADESC( CHL2_Player ) - - DEFINE_FIELD( m_nControlClass, FIELD_INTEGER ), - DEFINE_EMBEDDED( m_HL2Local ), - - DEFINE_FIELD( m_bSprintEnabled, FIELD_BOOLEAN ), - DEFINE_FIELD( m_flTimeAllSuitDevicesOff, FIELD_TIME ), - DEFINE_FIELD( m_fIsSprinting, FIELD_BOOLEAN ), - DEFINE_FIELD( m_fIsWalking, FIELD_BOOLEAN ), - - /* - // These are initialized every time the player calls Activate() - DEFINE_FIELD( m_bIsAutoSprinting, FIELD_BOOLEAN ), - DEFINE_FIELD( m_fAutoSprintMinTime, FIELD_TIME ), - */ - - // Field is used within a single tick, no need to save restore - // DEFINE_FIELD( m_bPlayUseDenySound, FIELD_BOOLEAN ), - // m_pPlayerAISquad reacquired on load - - DEFINE_AUTO_ARRAY( m_vecMissPositions, FIELD_POSITION_VECTOR ), - DEFINE_FIELD( m_nNumMissPositions, FIELD_INTEGER ), - - // m_pPlayerAISquad - DEFINE_EMBEDDED( m_CommanderUpdateTimer ), - // m_RealTimeLastSquadCommand - DEFINE_FIELD( m_QueuedCommand, FIELD_INTEGER ), - - DEFINE_FIELD( m_flTimeIgnoreFallDamage, FIELD_TIME ), - - // Suit power fields - DEFINE_FIELD( m_flSuitPowerLoad, FIELD_FLOAT ), - - DEFINE_FIELD( m_flIdleTime, FIELD_TIME ), - DEFINE_FIELD( m_flMoveTime, FIELD_TIME ), - DEFINE_FIELD( m_flLastDamageTime, FIELD_TIME ), - DEFINE_FIELD( m_flTargetFindTime, FIELD_TIME ), - - DEFINE_FIELD( m_flAdmireGlovesAnimTime, FIELD_TIME ), - DEFINE_FIELD( m_flNextFlashlightCheckTime, FIELD_TIME ), - DEFINE_FIELD( m_flFlashlightPowerDrainScale, FIELD_FLOAT ), - DEFINE_FIELD( m_bFlashlightDisabled, FIELD_BOOLEAN ), - - DEFINE_FIELD( m_hLockedAutoAimEntity, FIELD_EHANDLE ), - - DEFINE_EMBEDDED( m_LowerWeaponTimer ), - DEFINE_EMBEDDED( m_AutoaimTimer ), - - DEFINE_INPUTFUNC( FIELD_FLOAT, "IgnoreFallDamage", InputIgnoreFallDamage ), - DEFINE_INPUTFUNC( FIELD_VOID, "OnSquadMemberKilled", OnSquadMemberKilled ), - DEFINE_INPUTFUNC( FIELD_VOID, "DisableFlashlight", InputDisableFlashlight ), - DEFINE_INPUTFUNC( FIELD_VOID, "EnableFlashlight", InputEnableFlashlight ), - DEFINE_INPUTFUNC( FIELD_VOID, "ForceDropPhysObjects", InputForceDropPhysObjects ), - - DEFINE_SOUNDPATCH( m_sndLeeches ), - DEFINE_SOUNDPATCH( m_sndWaterSplashes ), - - DEFINE_FIELD( m_flArmorReductionTime, FIELD_TIME ), - DEFINE_FIELD( m_iArmorReductionFrom, FIELD_INTEGER ), - - DEFINE_FIELD( m_flTimeUseSuspended, FIELD_TIME ), - - //DEFINE_FIELD( m_hPlayerProxy, FIELD_EHANDLE ), //Shut up class check! - -END_DATADESC() - -CHL2_Player::CHL2_Player() -{ - m_nNumMissPositions = 0; - m_pPlayerAISquad = 0; - m_bSprintEnabled = true; - - m_flArmorReductionTime = 0.0f; - m_iArmorReductionFrom = 0; -} - -// -// SUIT POWER DEVICES -// -#define SUITPOWER_CHARGE_RATE 12.5 // 100 units in 8 seconds - -#ifdef HL2MP - CSuitPowerDevice SuitDeviceSprint( bits_SUIT_DEVICE_SPRINT, 25.0f ); // 100 units in 4 seconds -#else - CSuitPowerDevice SuitDeviceSprint( bits_SUIT_DEVICE_SPRINT, 12.5f ); // 100 units in 8 seconds -#endif - -#ifdef HL2_EPISODIC - CSuitPowerDevice SuitDeviceFlashlight( bits_SUIT_DEVICE_FLASHLIGHT, 1.111 ); // 100 units in 90 second -#else - CSuitPowerDevice SuitDeviceFlashlight( bits_SUIT_DEVICE_FLASHLIGHT, 2.222 ); // 100 units in 45 second -#endif -CSuitPowerDevice SuitDeviceBreather( bits_SUIT_DEVICE_BREATHER, 6.7f ); // 100 units in 15 seconds (plus three padded seconds) - - -IMPLEMENT_SERVERCLASS_ST(CHL2_Player, DT_HL2_Player) - SendPropDataTable(SENDINFO_DT(m_HL2Local), &REFERENCE_SEND_TABLE(DT_HL2Local), SendProxy_SendLocalDataTable), - SendPropBool( SENDINFO(m_fIsSprinting) ), -END_SEND_TABLE() - - -void CHL2_Player::Precache( void ) -{ - BaseClass::Precache(); - - PrecacheScriptSound( "HL2Player.SprintNoPower" ); - PrecacheScriptSound( "HL2Player.SprintStart" ); - PrecacheScriptSound( "HL2Player.UseDeny" ); - PrecacheScriptSound( "HL2Player.FlashLightOn" ); - PrecacheScriptSound( "HL2Player.FlashLightOff" ); - PrecacheScriptSound( "HL2Player.PickupWeapon" ); - PrecacheScriptSound( "HL2Player.TrainUse" ); - PrecacheScriptSound( "HL2Player.Use" ); - PrecacheScriptSound( "HL2Player.BurnPain" ); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CHL2_Player::CheckSuitZoom( void ) -{ -//#ifndef _XBOX - //Adrian - No zooming without a suit! - if ( IsSuitEquipped() ) - { - if ( m_afButtonReleased & IN_ZOOM ) - { - StopZooming(); - } - else if ( m_afButtonPressed & IN_ZOOM ) - { - StartZooming(); - } - } -//#endif//_XBOX -} - -void CHL2_Player::EquipSuit( bool bPlayEffects ) -{ - MDLCACHE_CRITICAL_SECTION(); - BaseClass::EquipSuit(); - - m_HL2Local.m_bDisplayReticle = true; - - if ( bPlayEffects == true ) - { - StartAdmireGlovesAnimation(); - } -} - -void CHL2_Player::RemoveSuit( void ) -{ - BaseClass::RemoveSuit(); - - m_HL2Local.m_bDisplayReticle = false; -} - -void CHL2_Player::HandleSpeedChanges( void ) -{ - int buttonsChanged = m_afButtonPressed | m_afButtonReleased; - - if( buttonsChanged & IN_SPEED ) - { - // The state of the sprint/run button has changed. - if ( IsSuitEquipped() ) - { - if ( !(m_afButtonPressed & IN_SPEED) && IsSprinting() ) - { -#ifndef _XBOX - StopSprinting(); -#endif - } - else if ( (m_afButtonPressed & IN_SPEED) && !IsSprinting() ) - { - if ( CanSprint() ) - { -#ifdef _XBOX - StartAutoSprint(); -#else - StartSprinting(); -#endif - } - else - { - // Reset key, so it will be activated post whatever is suppressing it. - m_nButtons &= ~IN_SPEED; - } - } -#ifdef _XBOX - else if( (m_afButtonPressed & IN_SPEED) && IsSprinting() ) - { - StopSprinting(); - } -#endif//_XBOX - } - else - { - // ROBIN: We're trialling Sprint not affecting movement speed without the HEV suit - /* - // The state of the WALK button has changed. - if( !IsWalking() && !(m_afButtonPressed & IN_SPEED) ) - { - StartWalking(); - } - else if( IsWalking() && !IsSprinting() && (m_afButtonPressed & IN_SPEED) && !(m_nButtons & IN_DUCK) ) - { - StopWalking(); - } - */ - } - } - else if( buttonsChanged & IN_WALK ) - { - if ( IsSuitEquipped() ) - { - // The state of the WALK button has changed. - if( IsWalking() && !(m_afButtonPressed & IN_WALK) ) - { - StopWalking(); - } - else if( !IsWalking() && !IsSprinting() && (m_afButtonPressed & IN_WALK) && !(m_nButtons & IN_DUCK) ) - { - StartWalking(); - } - } - } - - if ( IsSuitEquipped() && m_fIsWalking && !(m_nButtons & IN_WALK) ) - StopWalking(); -} - -//----------------------------------------------------------------------------- -// This happens when we powerdown from the mega physcannon to the regular one -//----------------------------------------------------------------------------- -void CHL2_Player::HandleArmorReduction( void ) -{ - if ( m_flArmorReductionTime < gpGlobals->curtime ) - return; - - if ( ArmorValue() <= 0 ) - return; - - float flPercent = 1.0f - (( m_flArmorReductionTime - gpGlobals->curtime ) / ARMOR_DECAY_TIME ); - - int iArmor = Lerp( flPercent, m_iArmorReductionFrom, 0 ); - - SetArmorValue( iArmor ); -} - -//----------------------------------------------------------------------------- -// Purpose: Allow pre-frame adjustments on the player -//----------------------------------------------------------------------------- -void CHL2_Player::PreThink(void) -{ - if ( player_showpredictedposition.GetBool() ) - { - Vector predPos; - - UTIL_PredictedPosition( this, player_showpredictedposition_timestep.GetFloat(), &predPos ); - - NDebugOverlay::Box( predPos, NAI_Hull::Mins( GetHullType() ), NAI_Hull::Maxs( GetHullType() ), 0, 255, 0, 0, 0.01f ); - NDebugOverlay::Line( GetAbsOrigin(), predPos, 0, 255, 0, 0, 0.01f ); - } - - // Riding a vehicle? - if ( IsInAVehicle() ) - { - VPROF( "CHL2_Player::PreThink-Vehicle" ); - // make sure we update the client, check for timed damage and update suit even if we are in a vehicle - UpdateClientData(); - CheckTimeBasedDamage(); - - // Allow the suit to recharge when in the vehicle. - SuitPower_Update(); - CheckSuitUpdate(); - CheckSuitZoom(); - - WaterMove(); - return; - } - - // This is an experiment of mine- autojumping! - // only affects you if sv_autojump is nonzero. - if( (GetFlags() & FL_ONGROUND) && sv_autojump.GetFloat() != 0 ) - { - VPROF( "CHL2_Player::PreThink-Autojump" ); - // check autojump - Vector vecCheckDir; - - vecCheckDir = GetAbsVelocity(); - - float flVelocity = VectorNormalize( vecCheckDir ); - - if( flVelocity > 200 ) - { - // Going fast enough to autojump - vecCheckDir = WorldSpaceCenter() + vecCheckDir * 34 - Vector( 0, 0, 16 ); - - trace_t tr; - - UTIL_TraceHull( WorldSpaceCenter() - Vector( 0, 0, 16 ), vecCheckDir, NAI_Hull::Mins(HULL_TINY_CENTERED),NAI_Hull::Maxs(HULL_TINY_CENTERED), MASK_PLAYERSOLID, this, COLLISION_GROUP_PLAYER, &tr ); - - //NDebugOverlay::Line( tr.startpos, tr.endpos, 0,255,0, true, 10 ); - - if( tr.fraction == 1.0 && !tr.startsolid ) - { - // Now trace down! - UTIL_TraceLine( vecCheckDir, vecCheckDir - Vector( 0, 0, 64 ), MASK_PLAYERSOLID, this, COLLISION_GROUP_NONE, &tr ); - - //NDebugOverlay::Line( tr.startpos, tr.endpos, 0,255,0, true, 10 ); - - if( tr.fraction == 1.0 && !tr.startsolid ) - { - // !!!HACKHACK - // I KNOW, I KNOW, this is definitely not the right way to do this, - // but I'm prototyping! (sjb) - Vector vecNewVelocity = GetAbsVelocity(); - vecNewVelocity.z += 250; - SetAbsVelocity( vecNewVelocity ); - } - } - } - } - - VPROF_SCOPE_BEGIN( "CHL2_Player::PreThink-Speed" ); - HandleSpeedChanges(); -#ifdef HL2_EPISODIC - HandleArmorReduction(); -#endif - -#ifdef _XBOX - if( m_bIsAutoSprinting ) - { - // Stop sprinting if the player lets off the stick for a moment. - if( GetStickDist() == 0.0f ) - { - if( gpGlobals->curtime > m_fAutoSprintMinTime ) - { - StopSprinting(); - } - } - else - { - // Stop sprinting one half second after the player stops inputting with the move stick. - m_fAutoSprintMinTime = gpGlobals->curtime + 0.5f; - } - } -#endif//_XBOX - VPROF_SCOPE_END(); - - if ( g_fGameOver || IsPlayerLockedInPlace() ) - return; // finale - - VPROF_SCOPE_BEGIN( "CHL2_Player::PreThink-ItemPreFrame" ); - ItemPreFrame( ); - VPROF_SCOPE_END(); - - VPROF_SCOPE_BEGIN( "CHL2_Player::PreThink-WaterMove" ); - WaterMove(); - VPROF_SCOPE_END(); - - if ( g_pGameRules && g_pGameRules->FAllowFlashlight() ) - m_Local.m_iHideHUD &= ~HIDEHUD_FLASHLIGHT; - else - m_Local.m_iHideHUD |= HIDEHUD_FLASHLIGHT; - - - VPROF_SCOPE_BEGIN( "CHL2_Player::PreThink-CommanderUpdate" ); - CommanderUpdate(); - VPROF_SCOPE_END(); - - // Operate suit accessories and manage power consumption/charge - VPROF_SCOPE_BEGIN( "CHL2_Player::PreThink-SuitPower_Update" ); - SuitPower_Update(); - VPROF_SCOPE_END(); - - // checks if new client data (for HUD and view control) needs to be sent to the client - VPROF_SCOPE_BEGIN( "CHL2_Player::PreThink-UpdateClientData" ); - UpdateClientData(); - VPROF_SCOPE_END(); - - VPROF_SCOPE_BEGIN( "CHL2_Player::PreThink-CheckTimeBasedDamage" ); - CheckTimeBasedDamage(); - VPROF_SCOPE_END(); - - VPROF_SCOPE_BEGIN( "CHL2_Player::PreThink-CheckSuitUpdate" ); - CheckSuitUpdate(); - VPROF_SCOPE_END(); - - VPROF_SCOPE_BEGIN( "CHL2_Player::PreThink-CheckSuitZoom" ); - CheckSuitZoom(); - VPROF_SCOPE_END(); - - if (m_lifeState >= LIFE_DYING) - { - PlayerDeathThink(); - return; - } - -#ifdef HL2_EPISODIC - CheckFlashlight(); -#endif - - // So the correct flags get sent to client asap. - // - if ( m_afPhysicsFlags & PFLAG_DIROVERRIDE ) - AddFlag( FL_ONTRAIN ); - else - RemoveFlag( FL_ONTRAIN ); - - // Train speed control - if ( m_afPhysicsFlags & PFLAG_DIROVERRIDE ) - { - CBaseEntity *pTrain = GetGroundEntity(); - float vel; - - if ( pTrain ) - { - if ( !(pTrain->ObjectCaps() & FCAP_DIRECTIONAL_USE) ) - pTrain = NULL; - } - - if ( !pTrain ) - { - if ( GetActiveWeapon() && (GetActiveWeapon()->ObjectCaps() & FCAP_DIRECTIONAL_USE) ) - { - m_iTrain = TRAIN_ACTIVE | TRAIN_NEW; - - if ( m_nButtons & IN_FORWARD ) - { - m_iTrain |= TRAIN_FAST; - } - else if ( m_nButtons & IN_BACK ) - { - m_iTrain |= TRAIN_BACK; - } - else - { - m_iTrain |= TRAIN_NEUTRAL; - } - return; - } - else - { - trace_t trainTrace; - // Maybe this is on the other side of a level transition - UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() + Vector(0,0,-38), - MASK_PLAYERSOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &trainTrace ); - - if ( trainTrace.fraction != 1.0 && trainTrace.m_pEnt ) - pTrain = trainTrace.m_pEnt; - - - if ( !pTrain || !(pTrain->ObjectCaps() & FCAP_DIRECTIONAL_USE) || !pTrain->OnControls(this) ) - { -// Warning( "In train mode with no train!\n" ); - m_afPhysicsFlags &= ~PFLAG_DIROVERRIDE; - m_iTrain = TRAIN_NEW|TRAIN_OFF; - return; - } - } - } - else if ( !( GetFlags() & FL_ONGROUND ) || pTrain->HasSpawnFlags( SF_TRACKTRAIN_NOCONTROL ) || (m_nButtons & (IN_MOVELEFT|IN_MOVERIGHT) ) ) - { - // Turn off the train if you jump, strafe, or the train controls go dead - m_afPhysicsFlags &= ~PFLAG_DIROVERRIDE; - m_iTrain = TRAIN_NEW|TRAIN_OFF; - return; - } - - SetAbsVelocity( vec3_origin ); - vel = 0; - if ( m_afButtonPressed & IN_FORWARD ) - { - vel = 1; - pTrain->Use( this, this, USE_SET, (float)vel ); - } - else if ( m_afButtonPressed & IN_BACK ) - { - vel = -1; - pTrain->Use( this, this, USE_SET, (float)vel ); - } - - if (vel) - { - m_iTrain = TrainSpeed(pTrain->m_flSpeed, ((CFuncTrackTrain*)pTrain)->GetMaxSpeed()); - m_iTrain |= TRAIN_ACTIVE|TRAIN_NEW; - } - } - else if (m_iTrain & TRAIN_ACTIVE) - { - m_iTrain = TRAIN_NEW; // turn off train - } - - - // - // If we're not on the ground, we're falling. Update our falling velocity. - // - if ( !( GetFlags() & FL_ONGROUND ) ) - { - m_Local.m_flFallVelocity = -GetAbsVelocity().z; - } - - if ( m_afPhysicsFlags & PFLAG_ONBARNACLE ) - { - bool bOnBarnacle = false; - CNPC_Barnacle *pBarnacle = NULL; - do - { - // FIXME: Not a good or fast solution, but maybe it will catch the bug! - pBarnacle = (CNPC_Barnacle*)gEntList.FindEntityByClassname( pBarnacle, "npc_barnacle" ); - if ( pBarnacle ) - { - if ( pBarnacle->GetEnemy() == this ) - { - bOnBarnacle = true; - } - } - } while ( pBarnacle ); - - if ( !bOnBarnacle ) - { - Warning( "Attached to barnacle?\n" ); - Assert( 0 ); - m_afPhysicsFlags &= ~PFLAG_ONBARNACLE; - } - else - { - SetAbsVelocity( vec3_origin ); - } - } - // StudioFrameAdvance( );//!!!HACKHACK!!! Can't be hit by traceline when not animating? - - // Update weapon's ready status - UpdateWeaponPosture(); - - // Disallow shooting while zooming -#ifdef _XBOX - if ( IsZooming() ) - { - if( !GetActiveWeapon() || !GetActiveWeapon()->IsWeaponZoomed() ) - { - // If not zoomed because of the weapon itself, do not attack. - m_nButtons &= ~(IN_ATTACK|IN_ATTACK2); - } - } -#else - if ( m_nButtons & IN_ZOOM ) - { - //FIXME: Held weapons like the grenade get sad when this happens -#ifdef HL2_EPISODIC - // Episodic allows players to zoom while using a func_tank - if ( !m_hUseEntity || GetActiveWeapon()->IsWeaponVisible() ) -#endif - m_nButtons &= ~(IN_ATTACK|IN_ATTACK2); - } -#endif//_XBOX -} - -void CHL2_Player::PostThink( void ) -{ - BaseClass::PostThink(); - - if ( !g_fGameOver && !IsPlayerLockedInPlace() && IsAlive() ) - { - HandleAdmireGlovesAnimation(); - } -} - -void CHL2_Player::StartAdmireGlovesAnimation( void ) -{ - CBaseViewModel *vm = GetViewModel( 0 ); - - if ( vm && !GetActiveWeapon() ) - { - vm->SetWeaponModel( "models/weapons/v_hands.mdl", NULL ); - ShowViewModel( true ); - - int idealSequence = vm->SelectWeightedSequence( ACT_VM_IDLE ); - - if ( idealSequence >= 0 ) - { - vm->SendViewModelMatchingSequence( idealSequence ); - m_flAdmireGlovesAnimTime = gpGlobals->curtime + vm->SequenceDuration( idealSequence ); - } - } -} - -void CHL2_Player::HandleAdmireGlovesAnimation( void ) -{ - CBaseViewModel *pVM = GetViewModel(); - - if ( pVM && pVM->GetOwningWeapon() == NULL ) - { - if ( m_flAdmireGlovesAnimTime != 0.0 ) - { - if ( m_flAdmireGlovesAnimTime > gpGlobals->curtime ) - { - pVM->m_flPlaybackRate = 1.0f; - pVM->StudioFrameAdvance( ); - } - else if ( m_flAdmireGlovesAnimTime < gpGlobals->curtime ) - { - m_flAdmireGlovesAnimTime = 0.0f; - pVM->SetWeaponModel( NULL, NULL ); - } - } - } - else - m_flAdmireGlovesAnimTime = 0.0f; -} - -#define HL2PLAYER_RELOADGAME_ATTACK_DELAY 1.0f - -void CHL2_Player::Activate( void ) -{ - BaseClass::Activate(); - InitSprinting(); - -#ifdef HL2_EPISODIC - - // Delay attacks by 1 second after loading a game. - if ( GetActiveWeapon() ) - { - float flRemaining = GetActiveWeapon()->m_flNextPrimaryAttack - gpGlobals->curtime; - - if ( flRemaining < HL2PLAYER_RELOADGAME_ATTACK_DELAY ) - { - GetActiveWeapon()->m_flNextPrimaryAttack = gpGlobals->curtime + HL2PLAYER_RELOADGAME_ATTACK_DELAY; - } - - flRemaining = GetActiveWeapon()->m_flNextSecondaryAttack - gpGlobals->curtime; - - if ( flRemaining < HL2PLAYER_RELOADGAME_ATTACK_DELAY ) - { - GetActiveWeapon()->m_flNextSecondaryAttack = gpGlobals->curtime + HL2PLAYER_RELOADGAME_ATTACK_DELAY; - } - } - -#endif -} - -//------------------------------------------------------------------------------ -// Purpose : -// Input : -// Output : -//------------------------------------------------------------------------------ -Class_T CHL2_Player::Classify ( void ) -{ - // If player controlling another entity? If so, return this class - if (m_nControlClass != CLASS_NONE) - { - return m_nControlClass; - } - else - { - if(IsInAVehicle()) - { - IServerVehicle *pVehicle = GetVehicle(); - return pVehicle->ClassifyPassenger( this, CLASS_PLAYER ); - } - else - { - return CLASS_PLAYER; - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: This is a generic function (to be implemented by sub-classes) to -// handle specific interactions between different types of characters -// (For example the barnacle grabbing an NPC) -// Input : Constant for the type of interaction -// Output : true - if sub-class has a response for the interaction -// false - if sub-class has no response -//----------------------------------------------------------------------------- -bool CHL2_Player::HandleInteraction(int interactionType, void *data, CBaseCombatCharacter* sourceEnt) -{ - if ( interactionType == g_interactionBarnacleVictimDangle ) - return false; - - if (interactionType == g_interactionBarnacleVictimReleased) - { - m_afPhysicsFlags &= ~PFLAG_ONBARNACLE; - SetMoveType( MOVETYPE_WALK ); - return true; - } - else if (interactionType == g_interactionBarnacleVictimGrab) - { -#ifdef HL2_EPISODIC - CNPC_Alyx *pAlyx = CNPC_Alyx::GetAlyx(); - if ( pAlyx ) - { - // Make Alyx totally hate this barnacle so that she saves the player. - int priority; - - priority = pAlyx->IRelationPriority(sourceEnt); - pAlyx->AddEntityRelationship( sourceEnt, D_HT, priority + 5 ); - } -#endif//HL2_EPISODIC - - m_afPhysicsFlags |= PFLAG_ONBARNACLE; - ClearUseEntity(); - return true; - } - return false; -} - - -void CHL2_Player::PlayerRunCommand(CUserCmd *ucmd, IMoveHelper *moveHelper) -{ - // Handle FL_FROZEN. - if ( m_afPhysicsFlags & PFLAG_ONBARNACLE ) - { - ucmd->forwardmove = 0; - ucmd->sidemove = 0; - ucmd->upmove = 0; - ucmd->buttons &= ~IN_USE; - } - - // Can't use stuff while dead - if ( IsDead() ) - { - ucmd->buttons &= ~IN_USE; - } - - //Update our movement information - if ( ( ucmd->forwardmove != 0 ) || ( ucmd->sidemove != 0 ) || ( ucmd->upmove != 0 ) ) - { - m_flIdleTime -= TICK_INTERVAL * 2.0f; - - if ( m_flIdleTime < 0.0f ) - { - m_flIdleTime = 0.0f; - } - - m_flMoveTime += TICK_INTERVAL; - - if ( m_flMoveTime > 4.0f ) - { - m_flMoveTime = 4.0f; - } - } - else - { - m_flIdleTime += TICK_INTERVAL; - - if ( m_flIdleTime > 4.0f ) - { - m_flIdleTime = 4.0f; - } - - m_flMoveTime -= TICK_INTERVAL * 2.0f; - - if ( m_flMoveTime < 0.0f ) - { - m_flMoveTime = 0.0f; - } - } - - //Msg("Player time: [ACTIVE: %f]\t[IDLE: %f]\n", m_flMoveTime, m_flIdleTime ); - - BaseClass::PlayerRunCommand( ucmd, moveHelper ); -} - -//----------------------------------------------------------------------------- -// Purpose: Sets HL2 specific defaults. -//----------------------------------------------------------------------------- -void CHL2_Player::Spawn(void) -{ - -#ifndef HL2MP - SetModel( "models/player.mdl" ); -#endif - - BaseClass::Spawn(); - - // - // Our player movement speed is set once here. This will override the cl_xxxx - // cvars unless they are set to be lower than this. - // - //m_flMaxspeed = 320; - - if ( !IsSuitEquipped() ) - StartWalking(); - - SuitPower_SetCharge( 100 ); - - m_Local.m_iHideHUD |= HIDEHUD_CHAT; - - m_pPlayerAISquad = g_AI_SquadManager.FindCreateSquad(AllocPooledString(PLAYER_SQUADNAME)); - - InitSprinting(); - - GetPlayerProxy(); - - SetFlashlightPowerDrainScale( 1.0f ); -} - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -void CHL2_Player::InitSprinting( void ) -{ - StopSprinting(); -} - - -//----------------------------------------------------------------------------- -// Purpose: Returns whether or not we are allowed to sprint now. -//----------------------------------------------------------------------------- -bool CHL2_Player::CanSprint() -{ - return ( m_bSprintEnabled && // Only if sprint is enabled - !IsWalking() && // Not if we're walking - !( m_Local.m_bDucked && !m_Local.m_bDucking ) && // Nor if we're ducking - (GetWaterLevel() != 3) && // Certainly not underwater - (GlobalEntity_GetState("suit_no_sprint") != GLOBAL_ON) ); // Out of the question without the sprint module -} - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -#ifdef _XBOX -void CHL2_Player::StartAutoSprint() -{ - if( IsSprinting() ) - { - StopSprinting(); - } - else - { - StartSprinting(); - m_bIsAutoSprinting = true; - m_fAutoSprintMinTime = gpGlobals->curtime + 1.5f; - } -} -#endif// _XBOX - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -void CHL2_Player::StartSprinting( void ) -{ - if( m_HL2Local.m_flSuitPower < 10 ) - { - // Don't sprint unless there's a reasonable - // amount of suit power. - CPASAttenuationFilter filter( this ); - filter.UsePredictionRules(); - EmitSound( filter, entindex(), "HL2Player.SprintNoPower" ); - return; - } - - if( !SuitPower_AddDevice( SuitDeviceSprint ) ) - return; - - CPASAttenuationFilter filter( this ); - filter.UsePredictionRules(); - EmitSound( filter, entindex(), "HL2Player.SprintStart" ); - - SetMaxSpeed( HL2_SPRINT_SPEED ); - m_fIsSprinting = true; -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -void CHL2_Player::StopSprinting( void ) -{ - if ( m_HL2Local.m_bitsActiveDevices & SuitDeviceSprint.GetDeviceID() ) - { - SuitPower_RemoveDevice( SuitDeviceSprint ); - } - SetMaxSpeed( HL2_NORM_SPEED ); - m_fIsSprinting = false; - -#ifdef _XBOX - m_bIsAutoSprinting = false; - m_fAutoSprintMinTime = 0.0f; -#endif//_XBOX -} - - -//----------------------------------------------------------------------------- -// Purpose: Called to disable and enable sprint due to temporary circumstances: -// - Carrying a heavy object with the physcannon -//----------------------------------------------------------------------------- -void CHL2_Player::EnableSprint( bool bEnable ) -{ - if ( !bEnable && IsSprinting() ) - { - StopSprinting(); - } - - m_bSprintEnabled = bEnable; -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -void CHL2_Player::StartWalking( void ) -{ - SetMaxSpeed( HL2_WALK_SPEED ); - m_fIsWalking = true; -} - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -void CHL2_Player::StopWalking( void ) -{ - SetMaxSpeed( HL2_NORM_SPEED ); - m_fIsWalking = false; -} - -//----------------------------------------------------------------------------- -// Purpose: -// Output : Returns true on success, false on failure. -//----------------------------------------------------------------------------- -bool CHL2_Player::CanZoom( CBaseEntity *pRequester ) -{ - if ( IsZooming() ) - return false; - - //Check our weapon - - return true; -} - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -void CHL2_Player::ToggleZoom(void) -{ - if( IsZooming() ) - { - StopZooming(); - } - else - { - StartZooming(); - } -} - -//----------------------------------------------------------------------------- -// Purpose: +zoom suit zoom -//----------------------------------------------------------------------------- -void CHL2_Player::StartZooming( void ) -{ - int iFOV = 25; - if ( SetFOV( this, iFOV, 0.4f ) ) - { - m_HL2Local.m_bZooming = true; - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CHL2_Player::StopZooming( void ) -{ - int iFOV = GetZoomOwnerDesiredFOV( m_hZoomOwner ); - - if ( SetFOV( this, iFOV, 0.2f ) ) - { - m_HL2Local.m_bZooming = false; - } -} - -//----------------------------------------------------------------------------- -// Purpose: -// Output : Returns true on success, false on failure. -//----------------------------------------------------------------------------- -bool CHL2_Player::IsZooming( void ) -{ - if ( m_hZoomOwner != NULL ) - return true; - - return false; -} - -class CPhysicsPlayerCallback : public IPhysicsPlayerControllerEvent -{ -public: - int ShouldMoveTo( IPhysicsObject *pObject, const Vector &position ) - { - CHL2_Player *pPlayer = (CHL2_Player *)pObject->GetGameData(); - if ( pPlayer ) - { - if ( pPlayer->TouchedPhysics() ) - { - return 0; - } - } - return 1; - } -}; - -static CPhysicsPlayerCallback playerCallback; - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CHL2_Player::InitVCollision( void ) -{ - BaseClass::InitVCollision(); - - // Setup the HL2 specific callback. - IPhysicsPlayerController *pPlayerController = GetPhysicsController(); - if ( pPlayerController ) - { - pPlayerController->SetEventHandler( &playerCallback ); - } -} - - -CHL2_Player::~CHL2_Player( void ) -{ -} - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- - -bool CHL2_Player::CommanderFindGoal( commandgoal_t *pGoal ) -{ - CAI_BaseNPC *pAllyNpc; - trace_t tr; - Vector vecTarget; - Vector forward; - - EyeVectors( &forward ); - - //--------------------------------- - // MASK_SHOT on purpose! So that you don't hit the invisible hulls of the NPCs. - CTraceFilterSkipTwoEntities filter( this, PhysCannonGetHeldEntity( GetActiveWeapon() ), COLLISION_GROUP_INTERACTIVE_DEBRIS ); - - UTIL_TraceLine( EyePosition(), EyePosition() + forward * MAX_COORD_RANGE, MASK_SHOT, &filter, &tr ); - - if( !tr.DidHitWorld() ) - { - CUtlVector Allies; - AISquadIter_t iter; - for ( pAllyNpc = m_pPlayerAISquad->GetFirstMember(&iter); pAllyNpc; pAllyNpc = m_pPlayerAISquad->GetNextMember(&iter) ) - { - if ( pAllyNpc->IsCommandable() ) - Allies.AddToTail( pAllyNpc ); - } - - for( int i = 0 ; i < Allies.Count() ; i++ ) - { - if( Allies[ i ]->IsValidCommandTarget( tr.m_pEnt ) ) - { - pGoal->m_pGoalEntity = tr.m_pEnt; - return true; - } - } - } - - if( tr.fraction == 1.0 || (tr.surface.flags & SURF_SKY) ) - { - // Move commands invalid against skybox. - pGoal->m_vecGoalLocation = tr.endpos; - return false; - } - - if ( tr.m_pEnt->IsNPC() && ((CAI_BaseNPC *)(tr.m_pEnt))->IsCommandable() ) - { - pGoal->m_vecGoalLocation = tr.m_pEnt->GetAbsOrigin(); - } - else - { - vecTarget = tr.endpos; - - Vector mins( -16, -16, 0 ); - Vector maxs( 16, 16, 0 ); - - // Back up from whatever we hit so that there's enough space at the - // target location for a bounding box. - // Now trace down. - //UTIL_TraceLine( vecTarget, vecTarget - Vector( 0, 0, 8192 ), MASK_SOLID, this, COLLISION_GROUP_NONE, &tr ); - UTIL_TraceHull( vecTarget + tr.plane.normal * 24, - vecTarget - Vector( 0, 0, 8192 ), - mins, - maxs, - MASK_SOLID_BRUSHONLY, - this, - COLLISION_GROUP_NONE, - &tr ); - - - if ( !tr.startsolid ) - pGoal->m_vecGoalLocation = tr.endpos; - else - pGoal->m_vecGoalLocation = vecTarget; - } - - pAllyNpc = GetSquadCommandRepresentative(); - if ( !pAllyNpc ) - return false; - - vecTarget = pGoal->m_vecGoalLocation; - if ( !pAllyNpc->FindNearestValidGoalPos( vecTarget, &pGoal->m_vecGoalLocation ) ) - return false; - - return ( ( vecTarget - pGoal->m_vecGoalLocation ).LengthSqr() < Square( 15*12 ) ); -} - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -CAI_BaseNPC *CHL2_Player::GetSquadCommandRepresentative() -{ - if ( m_pPlayerAISquad != NULL ) - { - CAI_BaseNPC *pAllyNpc = m_pPlayerAISquad->GetFirstMember(); - - if ( pAllyNpc ) - { - return pAllyNpc->GetSquadCommandRepresentative(); - } - } - - return NULL; -} - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -int CHL2_Player::GetNumSquadCommandables() -{ - AISquadIter_t iter; - int c = 0; - for ( CAI_BaseNPC *pAllyNpc = m_pPlayerAISquad->GetFirstMember(&iter); pAllyNpc; pAllyNpc = m_pPlayerAISquad->GetNextMember(&iter) ) - { - if ( pAllyNpc->IsCommandable() ) - c++; - } - return c; -} - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -int CHL2_Player::GetNumSquadCommandableMedics() -{ - AISquadIter_t iter; - int c = 0; - for ( CAI_BaseNPC *pAllyNpc = m_pPlayerAISquad->GetFirstMember(&iter); pAllyNpc; pAllyNpc = m_pPlayerAISquad->GetNextMember(&iter) ) - { - if ( pAllyNpc->IsCommandable() && pAllyNpc->IsMedic() ) - c++; - } - return c; -} - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -void CHL2_Player::CommanderUpdate() -{ - CAI_BaseNPC *pCommandRepresentative = GetSquadCommandRepresentative(); - bool bFollowMode = false; - if ( pCommandRepresentative ) - { - bFollowMode = ( pCommandRepresentative->GetCommandGoal() == vec3_invalid ); - - // set the variables for network transmission (to show on the hud) - m_HL2Local.m_iSquadMemberCount = GetNumSquadCommandables(); - m_HL2Local.m_iSquadMedicCount = GetNumSquadCommandableMedics(); - m_HL2Local.m_fSquadInFollowMode = bFollowMode; - - // debugging code for displaying extra squad indicators - /* - char *pszMoving = ""; - AISquadIter_t iter; - for ( CAI_BaseNPC *pAllyNpc = m_pPlayerAISquad->GetFirstMember(&iter); pAllyNpc; pAllyNpc = m_pPlayerAISquad->GetNextMember(&iter) ) - { - if ( pAllyNpc->IsCommandMoving() ) - { - pszMoving = "<-"; - break; - } - } - - NDebugOverlay::ScreenText( - 0.932, 0.919, - CFmtStr( "%d|%c%s", GetNumSquadCommandables(), ( bFollowMode ) ? 'F' : 'S', pszMoving ), - 255, 128, 0, 128, - 0 ); - */ - - } - else - { - m_HL2Local.m_iSquadMemberCount = 0; - m_HL2Local.m_iSquadMedicCount = 0; - m_HL2Local.m_fSquadInFollowMode = true; - } - - if ( m_QueuedCommand != CC_NONE && ( m_QueuedCommand == CC_FOLLOW || gpGlobals->realtime - m_RealTimeLastSquadCommand >= player_squad_double_tap_time.GetFloat() ) ) - { - CommanderExecute( m_QueuedCommand ); - m_QueuedCommand = CC_NONE; - } - else if ( !bFollowMode && pCommandRepresentative && m_CommanderUpdateTimer.Expired() && player_squad_transient_commands.GetBool() ) - { - m_CommanderUpdateTimer.Set(2.5); - - if ( pCommandRepresentative->ShouldAutoSummon() ) - CommanderExecute( CC_FOLLOW ); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -// -// bHandled - indicates whether to continue delivering this order to -// all allies. Allows us to stop delivering certain types of orders once we find -// a suitable candidate. (like picking up a single weapon. We don't wish for -// all allies to respond and try to pick up one weapon). -//----------------------------------------------------------------------------- -bool CHL2_Player::CommanderExecuteOne( CAI_BaseNPC *pNpc, const commandgoal_t &goal, CAI_BaseNPC **Allies, int numAllies ) -{ - if ( goal.m_pGoalEntity ) - { - return pNpc->TargetOrder( goal.m_pGoalEntity, Allies, numAllies ); - } - else if ( pNpc->IsInPlayerSquad() ) - { - pNpc->MoveOrder( goal.m_vecGoalLocation, Allies, numAllies ); - } - - return true; -} - -//--------------------------------------------------------- -//--------------------------------------------------------- -void CHL2_Player::CommanderExecute( CommanderCommand_t command ) -{ - CAI_BaseNPC *pPlayerSquadLeader = GetSquadCommandRepresentative(); - - if ( !pPlayerSquadLeader ) - { - EmitSound( "HL2Player.UseDeny" ); - return; - } - - int i; - CUtlVector Allies; - commandgoal_t goal; - - if ( command == CC_TOGGLE ) - { - if ( pPlayerSquadLeader->GetCommandGoal() != vec3_invalid ) - command = CC_FOLLOW; - else - command = CC_SEND; - } - else - { - if ( command == CC_FOLLOW && pPlayerSquadLeader->GetCommandGoal() == vec3_invalid ) - return; - } - - if ( command == CC_FOLLOW ) - { - goal.m_pGoalEntity = this; - goal.m_vecGoalLocation = vec3_invalid; - } - else - { - goal.m_pGoalEntity = NULL; - goal.m_vecGoalLocation = vec3_invalid; - - // Find a goal for ourselves. - if( !CommanderFindGoal( &goal ) ) - { - EmitSound( "HL2Player.UseDeny" ); - return; // just keep following - } - } - -#ifdef _DEBUG - if( goal.m_pGoalEntity == NULL && goal.m_vecGoalLocation == vec3_invalid ) - { - DevMsg( 1, "**ERROR: Someone sent an invalid goal to CommanderExecute!\n" ); - } -#endif // _DEBUG - - AISquadIter_t iter; - for ( CAI_BaseNPC *pAllyNpc = m_pPlayerAISquad->GetFirstMember(&iter); pAllyNpc; pAllyNpc = m_pPlayerAISquad->GetNextMember(&iter) ) - { - if ( pAllyNpc->IsCommandable() ) - Allies.AddToTail( pAllyNpc ); - } - - //--------------------------------- - // If the trace hits an NPC, send all ally NPCs a "target" order. Always - // goes to targeted one first -#ifdef DEBUG - int nAIs = g_AI_Manager.NumAIs(); -#endif - CAI_BaseNPC * pTargetNpc = (goal.m_pGoalEntity) ? goal.m_pGoalEntity->MyNPCPointer() : NULL; - - bool bHandled = false; - if( pTargetNpc ) - { - bHandled = !CommanderExecuteOne( pTargetNpc, goal, Allies.Base(), Allies.Count() ); - } - - for ( i = 0; !bHandled && i < Allies.Count(); i++ ) - { - if ( Allies[i] != pTargetNpc && Allies[i]->IsPlayerAlly() ) - { - bHandled = !CommanderExecuteOne( Allies[i], goal, Allies.Base(), Allies.Count() ); - } - Assert( nAIs == g_AI_Manager.NumAIs() ); // not coded to support mutating set of NPCs - } -} - -//----------------------------------------------------------------------------- -// Enter/exit commander mode, manage ally selection. -//----------------------------------------------------------------------------- -void CHL2_Player::CommanderMode() -{ - float commandInterval = gpGlobals->realtime - m_RealTimeLastSquadCommand; - m_RealTimeLastSquadCommand = gpGlobals->realtime; - if ( commandInterval < player_squad_double_tap_time.GetFloat() ) - { - m_QueuedCommand = CC_FOLLOW; - } - else - { - m_QueuedCommand = (player_squad_transient_commands.GetBool()) ? CC_SEND : CC_TOGGLE; - } -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : iImpulse - -//----------------------------------------------------------------------------- -void CHL2_Player::CheatImpulseCommands( int iImpulse ) -{ - switch( iImpulse ) - { - case 50: - { - CommanderMode(); - break; - } - - case 51: - { - // Cheat to create a dynamic resupply item - Vector vecForward; - AngleVectors( EyeAngles(), &vecForward ); - CBaseEntity *pItem = (CBaseEntity *)CreateEntityByName( "item_dynamic_resupply" ); - if ( pItem ) - { - Vector vecOrigin = GetAbsOrigin() + vecForward * 256 + Vector(0,0,64); - QAngle vecAngles( 0, GetAbsAngles().y - 90, 0 ); - pItem->SetAbsOrigin( vecOrigin ); - pItem->SetAbsAngles( vecAngles ); - pItem->KeyValue( "targetname", "resupply" ); - pItem->Spawn(); - pItem->Activate(); - } - break; - } - - case 52: - { - // Rangefinder - trace_t tr; - UTIL_TraceLine( EyePosition(), EyePosition() + EyeDirection3D() * MAX_COORD_RANGE, MASK_SHOT, this, COLLISION_GROUP_NONE, &tr ); - - if( tr.fraction != 1.0 ) - { - float flDist = (tr.startpos - tr.endpos).Length(); - float flDist2D = (tr.startpos - tr.endpos).Length2D(); - DevMsg( 1,"\nStartPos: %.4f %.4f %.4f --- EndPos: %.4f %.4f %.4f\n", tr.startpos.x,tr.startpos.y,tr.startpos.z,tr.endpos.x,tr.endpos.y,tr.endpos.z ); - DevMsg( 1,"3D Distance: %.4f units (%.2f feet) --- 2D Distance: %.4f units (%.2f feet)\n", flDist, flDist / 12.0, flDist2D, flDist2D / 12.0 ); - } - - break; - } - - default: - BaseClass::CheatImpulseCommands( iImpulse ); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CHL2_Player::SetupVisibility( CBaseEntity *pViewEntity, unsigned char *pvs, int pvssize ) -{ - BaseClass::SetupVisibility( pViewEntity, pvs, pvssize ); - - int area = pViewEntity ? pViewEntity->NetworkProp()->AreaNum() : NetworkProp()->AreaNum(); - PointCameraSetupVisibility( this, area, pvs, pvssize ); - - // If the intro script is playing, we want to get it's visibility points - if ( g_hIntroScript ) - { - Vector vecOrigin; - CBaseEntity *pCamera; - if ( g_hIntroScript->GetIncludedPVSOrigin( &vecOrigin, &pCamera ) ) - { - // If it's a point camera, turn it on - CPointCamera *pPointCamera = dynamic_cast< CPointCamera* >(pCamera); - if ( pPointCamera ) - { - pPointCamera->SetActive( true ); - } - engine->AddOriginToPVS( vecOrigin ); - } - } -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -void CHL2_Player::SuitPower_Update( void ) -{ - if( SuitPower_ShouldRecharge() ) - { - SuitPower_Charge( SUITPOWER_CHARGE_RATE * gpGlobals->frametime ); - } - else if( m_HL2Local.m_bitsActiveDevices ) - { - float flPowerLoad = m_flSuitPowerLoad; - -#ifndef _XBOX - //Since XBox quickly shuts off sprint if it isn't being used, this isn't an issue. - if( SuitPower_IsDeviceActive(SuitDeviceSprint) ) - { - if( !fabs(GetAbsVelocity().x) && !fabs(GetAbsVelocity().y) ) - { - // If player's not moving, don't drain sprint juice. - flPowerLoad -= SuitDeviceSprint.GetDeviceDrainRate(); - } - } -#endif//_XBOX - - if( SuitPower_IsDeviceActive(SuitDeviceFlashlight) ) - { - float factor; - - factor = 1.0f / m_flFlashlightPowerDrainScale; - - flPowerLoad -= ( SuitDeviceFlashlight.GetDeviceDrainRate() * (1.0f - factor) ); - } - - if( !SuitPower_Drain( flPowerLoad * gpGlobals->frametime ) ) - { - // TURN OFF ALL DEVICES!! - if( IsSprinting() ) - { - StopSprinting(); - } - - if( FlashlightIsOn() ) - { -#ifndef HL2MP - FlashlightTurnOff(); -#endif - } - } - - // turn off flashlight a little bit after it hits below one aux power notch (5%) - if( m_HL2Local.m_flSuitPower < 4.8f && FlashlightIsOn() ) - { -#ifndef HL2MP - FlashlightTurnOff(); -#endif - } - } -} - - -//----------------------------------------------------------------------------- -// Charge battery fully, turn off all devices. -//----------------------------------------------------------------------------- -void CHL2_Player::SuitPower_Initialize( void ) -{ - m_HL2Local.m_bitsActiveDevices = 0x00000000; - m_HL2Local.m_flSuitPower = 100.0; - m_flSuitPowerLoad = 0.0; -} - - -//----------------------------------------------------------------------------- -// Purpose: Interface to drain power from the suit's power supply. -// Input: Amount of charge to remove (expressed as percentage of full charge) -// Output: Returns TRUE if successful, FALSE if not enough power available. -//----------------------------------------------------------------------------- -bool CHL2_Player::SuitPower_Drain( float flPower ) -{ - // Suitpower cheat on? - if ( sv_infinite_aux_power.GetBool() ) - return true; - - m_HL2Local.m_flSuitPower -= flPower; - - if( m_HL2Local.m_flSuitPower < 0.0 ) - { - // Power is depleted! - // Clamp and fail - m_HL2Local.m_flSuitPower = 0.0; - return false; - } - - return true; -} - -//----------------------------------------------------------------------------- -// Purpose: Interface to add power to the suit's power supply -// Input: Amount of charge to add -//----------------------------------------------------------------------------- -void CHL2_Player::SuitPower_Charge( float flPower ) -{ - m_HL2Local.m_flSuitPower += flPower; - - if( m_HL2Local.m_flSuitPower > 100.0 ) - { - // Full charge, clamp. - m_HL2Local.m_flSuitPower = 100.0; - } -} - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -bool CHL2_Player::SuitPower_IsDeviceActive( const CSuitPowerDevice &device ) -{ - return (m_HL2Local.m_bitsActiveDevices & device.GetDeviceID()) != 0; -} - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -bool CHL2_Player::SuitPower_AddDevice( const CSuitPowerDevice &device ) -{ - // Make sure this device is NOT active!! - if( m_HL2Local.m_bitsActiveDevices & device.GetDeviceID() ) - return false; - - if( !IsSuitEquipped() ) - return false; - - m_HL2Local.m_bitsActiveDevices |= device.GetDeviceID(); - m_flSuitPowerLoad += device.GetDeviceDrainRate(); - return true; -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -bool CHL2_Player::SuitPower_RemoveDevice( const CSuitPowerDevice &device ) -{ - // Make sure this device is active!! - if( ! (m_HL2Local.m_bitsActiveDevices & device.GetDeviceID()) ) - return false; - - if( !IsSuitEquipped() ) - return false; - - // Take a little bit of suit power when you disable a device. If the device is shutting off - // because the battery is drained, no harm done, the battery charge cannot go below 0. - // This code in combination with the delay before the suit can start recharging are a defense - // against exploits where the player could rapidly tap sprint and never run out of power. - SuitPower_Drain( device.GetDeviceDrainRate() * 0.1f ); - - m_HL2Local.m_bitsActiveDevices &= ~device.GetDeviceID(); - m_flSuitPowerLoad -= device.GetDeviceDrainRate(); - - if( m_HL2Local.m_bitsActiveDevices == 0x00000000 ) - { - // With this device turned off, we can set this timer which tells us when the - // suit power system entered a no-load state. - m_flTimeAllSuitDevicesOff = gpGlobals->curtime; - } - - return true; -} - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -#define SUITPOWER_BEGIN_RECHARGE_DELAY 0.5f -bool CHL2_Player::SuitPower_ShouldRecharge( void ) -{ - // Make sure all devices are off. - if( m_HL2Local.m_bitsActiveDevices != 0x00000000 ) - return false; - - // Is the system fully charged? - if( m_HL2Local.m_flSuitPower >= 100.0f ) - return false; - - // Has the system been in a no-load state for long enough - // to begin recharging? - if( gpGlobals->curtime < m_flTimeAllSuitDevicesOff + SUITPOWER_BEGIN_RECHARGE_DELAY ) - return false; - - return true; -} - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -ConVar sk_battery( "sk_battery","0" ); - -bool CHL2_Player::ApplyBattery( float powerMultiplier ) -{ - const float MAX_NORMAL_BATTERY = 100; - if ((ArmorValue() < MAX_NORMAL_BATTERY) && IsSuitEquipped()) - { - int pct; - char szcharge[64]; - - IncrementArmorValue( sk_battery.GetFloat() * powerMultiplier, MAX_NORMAL_BATTERY ); - - CPASAttenuationFilter filter( this, "ItemBattery.Touch" ); - EmitSound( filter, entindex(), "ItemBattery.Touch" ); - - CSingleUserRecipientFilter user( this ); - user.MakeReliable(); - - UserMessageBegin( user, "ItemPickup" ); - WRITE_STRING( "item_battery" ); - MessageEnd(); - - - // Suit reports new power level - // For some reason this wasn't working in release build -- round it. - pct = (int)( (float)(ArmorValue() * 100.0) * (1.0/MAX_NORMAL_BATTERY) + 0.5); - pct = (pct / 5); - if (pct > 0) - pct--; - - Q_snprintf( szcharge,sizeof(szcharge),"!HEV_%1dP", pct ); - - //UTIL_EmitSoundSuit(edict(), szcharge); - //SetSuitUpdate(szcharge, FALSE, SUIT_NEXT_IN_30SEC); - return true; - } - return false; -} - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -int CHL2_Player::FlashlightIsOn( void ) -{ - return IsEffectActive( EF_DIMLIGHT ); -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -void CHL2_Player::FlashlightTurnOn( void ) -{ - if( m_bFlashlightDisabled ) - return; - - if( !SuitPower_AddDevice( SuitDeviceFlashlight ) ) - return; - - AddEffects( EF_DIMLIGHT ); - EmitSound( "HL2Player.FlashLightOn" ); - - variant_t flashlighton; - flashlighton.SetFloat( m_HL2Local.m_flSuitPower / 100.0f ); - - FirePlayerProxyOutput( "OnFlashlightOn", flashlighton, this, this ); -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -void CHL2_Player::FlashlightTurnOff( void ) -{ - if( !SuitPower_RemoveDevice( SuitDeviceFlashlight ) ) - return; - - RemoveEffects( EF_DIMLIGHT ); - EmitSound( "HL2Player.FlashLightOff" ); - - variant_t flashlightoff; - flashlightoff.SetFloat( m_HL2Local.m_flSuitPower / 100.0f ); - FirePlayerProxyOutput( "OnFlashlightOff", flashlightoff, this, this ); -} - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -#define FLASHLIGHT_RANGE Square(600) -bool CHL2_Player::IsIlluminatedByFlashlight( CBaseEntity *pEntity, float *flReturnDot ) -{ - if( !FlashlightIsOn() ) - return false; - - if( pEntity->Classify() == CLASS_BARNACLE && pEntity->GetEnemy() == this ) - { - // As long as my flashlight is on, the barnacle that's pulling me in is considered illuminated. - // This is because players often shine their flashlights at Alyx when they are in a barnacle's - // grasp, and wonder why Alyx isn't helping. Alyx isn't helping because the light isn't pointed - // at the barnacle. This will allow Alyx to see the barnacle no matter which way the light is pointed. - return true; - } - - // Within 50 feet? - float flDistSqr = GetAbsOrigin().DistToSqr(pEntity->GetAbsOrigin()); - if( flDistSqr > FLASHLIGHT_RANGE ) - return false; - - // Within 45 degrees? - Vector vecSpot = pEntity->WorldSpaceCenter(); - Vector los; - - // If the eyeposition is too close, move it back. Solves problems - // caused by the player being too close the target. - if ( flDistSqr < (128 * 128) ) - { - Vector vecForward; - EyeVectors( &vecForward ); - Vector vecMovedEyePos = EyePosition() - (vecForward * 128); - los = ( vecSpot - vecMovedEyePos ); - } - else - { - los = ( vecSpot - EyePosition() ); - } - - VectorNormalize( los ); - Vector facingDir = EyeDirection3D( ); - float flDot = DotProduct( los, facingDir ); - - if ( flReturnDot ) - { - *flReturnDot = flDot; - } - - if ( flDot < 0.92387f ) - return false; - - if( !FVisible(pEntity) ) - return false; - - return true; -} - -//----------------------------------------------------------------------------- -// Purpose: Let NPCs know when the flashlight is trained on them -//----------------------------------------------------------------------------- -void CHL2_Player::CheckFlashlight( void ) -{ - if ( !FlashlightIsOn() ) - return; - - if ( m_flNextFlashlightCheckTime > gpGlobals->curtime ) - return; - m_flNextFlashlightCheckTime = gpGlobals->curtime + FLASHLIGHT_NPC_CHECK_INTERVAL; - - // Loop through NPCs looking for illuminated ones - for ( int i = 0; i < g_AI_Manager.NumAIs(); i++ ) - { - CAI_BaseNPC *pNPC = g_AI_Manager.AccessAIs()[i]; - - float flDot; - - if ( IsIlluminatedByFlashlight( pNPC, &flDot ) ) - { - pNPC->PlayerHasIlluminatedNPC( this, flDot ); - } - } -} - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -void CHL2_Player::SetPlayerUnderwater( bool state ) -{ - if ( state ) - { - SuitPower_AddDevice( SuitDeviceBreather ); - } - else - { - SuitPower_RemoveDevice( SuitDeviceBreather ); - } - - BaseClass::SetPlayerUnderwater( state ); -} - -//----------------------------------------------------------------------------- -bool CHL2_Player::PassesDamageFilter( const CTakeDamageInfo &info ) -{ - if( info.GetAttacker()->MyNPCPointer() && info.GetAttacker()->MyNPCPointer()->IsPlayerAlly() ) - { - return false; - } - - return BaseClass::PassesDamageFilter( info ); -} - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -void CHL2_Player::InputDisableFlashlight( inputdata_t &inputdata ) -{ - if( FlashlightIsOn() ) - FlashlightTurnOff(); - - m_bFlashlightDisabled = true; -} - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -void CHL2_Player::InputEnableFlashlight( inputdata_t &inputdata ) -{ - m_bFlashlightDisabled = false; -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -void CHL2_Player::InputIgnoreFallDamage( inputdata_t &inputdata ) -{ - float timeToIgnore = inputdata.value.Float(); - - if ( timeToIgnore <= 0.0 ) - timeToIgnore = TIME_IGNORE_FALL_DAMAGE; - - m_flTimeIgnoreFallDamage = gpGlobals->curtime + timeToIgnore; -} - -//----------------------------------------------------------------------------- -// Purpose: Notification of a player's npc ally in the players squad being killed -//----------------------------------------------------------------------------- -void CHL2_Player::OnSquadMemberKilled( inputdata_t &data ) -{ - // send a message to the client, to notify the hud of the loss - CSingleUserRecipientFilter user( this ); - user.MakeReliable(); - UserMessageBegin( user, "SquadMemberDied" ); - MessageEnd(); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CHL2_Player::NotifyFriendsOfDamage( CBaseEntity *pAttackerEntity ) -{ - CAI_BaseNPC *pAttacker = pAttackerEntity->MyNPCPointer(); - if ( pAttacker ) - { - const Vector &origin = GetAbsOrigin(); - for ( int i = 0; i < g_AI_Manager.NumAIs(); i++ ) - { - const float NEAR_Z = 12*12; - const float NEAR_XY_SQ = Square( 50*12 ); - CAI_BaseNPC *pNpc = g_AI_Manager.AccessAIs()[i]; - if ( pNpc->IsPlayerAlly() ) - { - const Vector &originNpc = pNpc->GetAbsOrigin(); - if ( fabsf( originNpc.z - origin.z ) < NEAR_Z ) - { - if ( (originNpc.AsVector2D() - origin.AsVector2D()).LengthSqr() < NEAR_XY_SQ ) - { - pNpc->OnFriendDamaged( this, pAttacker ); - } - } - } - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -int CHL2_Player::OnTakeDamage( const CTakeDamageInfo &info ) -{ - if ( GlobalEntity_GetState( "gordon_invulnerable" ) == GLOBAL_ON ) - return 0; - - if ( ( info.GetDamageType() & DMG_FALL ) && m_flTimeIgnoreFallDamage > gpGlobals->curtime ) - { - m_flTimeIgnoreFallDamage = 0; - return 0; - } - - if( info.GetDamageType() & DMG_BLAST_SURFACE ) - { - if( GetWaterLevel() > 2 ) - { - // Don't take blast damage from anything above the surface. - if( info.GetInflictor()->GetWaterLevel() == 0 ) - { - return 0; - } - } - } - - if ( info.GetDamage() > 0.0f ) - { - m_flLastDamageTime = gpGlobals->curtime; - - if ( info.GetAttacker() ) - NotifyFriendsOfDamage( info.GetAttacker() ); - } - - // Modify the amount of damage the player takes, based on skill. - CTakeDamageInfo playerDamage = info; - - // Should we run this damage through the skill level adjustment? - bool bAdjustForSkillLevel = true; - - if( info.GetDamageType() == DMG_GENERIC && info.GetAttacker() == this && info.GetInflictor() == this ) - { - // Only do a skill level adjustment if the player isn't his own attacker AND inflictor. - // This prevents damage from SetHealth() inputs from being adjusted for skill level. - bAdjustForSkillLevel = false; - } - - if( bAdjustForSkillLevel ) - { - playerDamage.AdjustPlayerDamageTakenForSkillLevel(); - } - - return BaseClass::OnTakeDamage( playerDamage ); -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : &info - -//----------------------------------------------------------------------------- -int CHL2_Player::OnTakeDamage_Alive( const CTakeDamageInfo &info ) -{ - // Drown - if( info.GetDamageType() & DMG_DROWN ) - { - if( m_idrowndmg == m_idrownrestored ) - { - EmitSound( "Player.DrownStart" ); - } - else - { - EmitSound( "Player.DrownContinue" ); - } - } - - // Burnt - if ( info.GetDamageType() & DMG_BURN ) - { - EmitSound( "HL2Player.BurnPain" ); - } - - - if( (info.GetDamageType() & DMG_SLASH) && hl2_episodic.GetBool() ) - { - if( m_afPhysicsFlags & PFLAG_USING ) - { - // Stop the player using a rotating button for a short time if hit by a creature's melee attack. - // This is for the antlion burrow-corking training in EP1 (sjb). - SuspendUse( 0.5f ); - } - } - - - // Call the base class implementation - return BaseClass::OnTakeDamage_Alive( info ); -} - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -void CHL2_Player::OnDamagedByExplosion( const CTakeDamageInfo &info ) -{ - if ( info.GetInflictor() && info.GetInflictor()->ClassMatches( "mortarshell" ) ) - { - // No ear ringing for mortar - UTIL_ScreenShake( info.GetInflictor()->GetAbsOrigin(), 4.0, 1.0, 0.5, 1000, SHAKE_START, false ); - return; - } - BaseClass::OnDamagedByExplosion( info ); -} - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -bool CHL2_Player::ShouldShootMissTarget( CBaseCombatCharacter *pAttacker ) -{ - if( gpGlobals->curtime > m_flTargetFindTime ) - { - // Put this off into the future again. - m_flTargetFindTime = gpGlobals->curtime + random->RandomFloat( 3, 5 ); - return true; - } - - return false; -} - -//----------------------------------------------------------------------------- -// Purpose: Notifies Alyx that player has put a combine ball into a socket so she can comment on it. -// Input : pCombineBall - ball the was socketed -//----------------------------------------------------------------------------- -void CHL2_Player::CombineBallSocketed( CPropCombineBall *pCombineBall ) -{ -#ifdef HL2_EPISODIC - CNPC_Alyx *pAlyx = CNPC_Alyx::GetAlyx(); - if ( pAlyx ) - { - pAlyx->CombineBallSocketed( pCombineBall->NumBounces() ); - } -#endif -} - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -void CHL2_Player::Event_KilledOther( CBaseEntity *pVictim, const CTakeDamageInfo &info ) -{ - BaseClass::Event_KilledOther( pVictim, info ); - -#ifdef HL2_EPISODIC - CNPC_Alyx *pAlyx = CNPC_Alyx::GetAlyx(); - if ( pAlyx ) - { - pAlyx->PlayerKilledOther( pVictim, info ); - } -#endif -} - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -void CHL2_Player::Event_Killed( const CTakeDamageInfo &info ) -{ - BaseClass::Event_Killed( info ); - - NotifyScriptsOfDeath(); -} - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -void CHL2_Player::NotifyScriptsOfDeath( void ) -{ - CBaseEntity *pEnt = gEntList.FindEntityByClassname( NULL, "scripted_sequence" ); - - while( pEnt ) - { - variant_t emptyVariant; - pEnt->AcceptInput( "ScriptPlayerDeath", NULL, NULL, emptyVariant, 0 ); - - pEnt = gEntList.FindEntityByClassname( pEnt, "scripted_sequence" ); - } - - pEnt = gEntList.FindEntityByClassname( NULL, "logic_choreographed_scene" ); - - while( pEnt ) - { - variant_t emptyVariant; - pEnt->AcceptInput( "ScriptPlayerDeath", NULL, NULL, emptyVariant, 0 ); - - pEnt = gEntList.FindEntityByClassname( pEnt, "logic_choreographed_scene" ); - } -} - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -void CHL2_Player::GetAutoaimVector( autoaim_params_t ¶ms ) -{ - BaseClass::GetAutoaimVector( params ); - - if ( IsXbox() ) - { - if( IsInAVehicle() ) - { - if( m_hLockedAutoAimEntity && m_hLockedAutoAimEntity->IsAlive() && ShouldKeepLockedAutoaimTarget(m_hLockedAutoAimEntity) ) - { - if( params.m_hAutoAimEntity && params.m_hAutoAimEntity != m_hLockedAutoAimEntity ) - { - // Autoaim has picked a new target. Switch. - m_hLockedAutoAimEntity = params.m_hAutoAimEntity; - } - - // Ignore autoaim and just keep aiming at this target. - params.m_hAutoAimEntity = m_hLockedAutoAimEntity; - Vector vecTarget = m_hLockedAutoAimEntity->BodyTarget( EyePosition(), false ); - Vector vecDir = vecTarget - EyePosition(); - VectorNormalize( vecDir ); - - params.m_vecAutoAimDir = vecDir; - params.m_vecAutoAimPoint = vecTarget; - return; - } - else - { - m_hLockedAutoAimEntity = NULL; - } - } - - // If the player manually gets his crosshair onto a target, make that target sticky - if( params.m_fScale != AUTOAIM_SCALE_DIRECT_ONLY ) - { - // Only affect this for 'real' queries - //if( params.m_hAutoAimEntity && params.m_bOnTargetNatural ) - if( params.m_hAutoAimEntity ) - { - // Turn on sticky. - m_HL2Local.m_bStickyAutoAim = true; - - if( IsInAVehicle() ) - { - m_hLockedAutoAimEntity = params.m_hAutoAimEntity; - } - } - else if( !params.m_hAutoAimEntity ) - { - // Turn off sticky only if there's no target at all. - m_HL2Local.m_bStickyAutoAim = false; - - m_hLockedAutoAimEntity = NULL; - } - } - } -} - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -bool CHL2_Player::ShouldKeepLockedAutoaimTarget( EHANDLE hLockedTarget ) -{ - Vector vecLooking; - Vector vecToTarget; - - vecToTarget = hLockedTarget->WorldSpaceCenter() - EyePosition(); - float flDist = vecToTarget.Length2D(); - VectorNormalize( vecToTarget ); - - if( flDist > autoaim_max_dist.GetFloat() ) - return false; - - float flDot; - - vecLooking = EyeDirection3D(); - flDot = DotProduct( vecLooking, vecToTarget ); - - if( flDot < autoaim_unlock_target.GetFloat() ) - return false; - - return true; -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : iCount - -// iAmmoIndex - -// bSuppressSound - -// Output : int -//----------------------------------------------------------------------------- -int CHL2_Player::GiveAmmo( int nCount, int nAmmoIndex, bool bSuppressSound) -{ - // Don't try to give the player invalid ammo indices. - if (nAmmoIndex < 0) - return 0; - - bool bCheckAutoSwitch = false; - if (!HasAnyAmmoOfType(nAmmoIndex)) - { - bCheckAutoSwitch = true; - } - - int nAdd = BaseClass::GiveAmmo(nCount, nAmmoIndex, bSuppressSound); - - if ( nCount > 0 && nAdd == 0 ) - { - // we've been denied the pickup, display a hud icon to show that - CSingleUserRecipientFilter user( this ); - user.MakeReliable(); - UserMessageBegin( user, "AmmoDenied" ); - WRITE_SHORT( nAmmoIndex ); - MessageEnd(); - } - - // - // If I was dry on ammo for my best weapon and justed picked up ammo for it, - // autoswitch to my best weapon now. - // - if (bCheckAutoSwitch) - { - CBaseCombatWeapon *pWeapon = g_pGameRules->GetNextBestWeapon(this, GetActiveWeapon()); - - if ( pWeapon && pWeapon->GetPrimaryAmmoType() == nAmmoIndex ) - { - SwitchToNextBestWeapon(GetActiveWeapon()); - } - } - - return nAdd; -} - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -bool CHL2_Player::Weapon_CanUse( CBaseCombatWeapon *pWeapon ) -{ -#ifndef HL2MP - if ( pWeapon->ClassMatches( "weapon_stunstick" ) ) - { - if ( ApplyBattery( 0.5 ) ) - UTIL_Remove( pWeapon ); - return false; - } -#endif - - return BaseClass::Weapon_CanUse( pWeapon ); -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *pWeapon - -//----------------------------------------------------------------------------- -void CHL2_Player::Weapon_Equip( CBaseCombatWeapon *pWeapon ) -{ -#if HL2_SINGLE_PRIMARY_WEAPON_MODE - - if ( pWeapon->GetSlot() == WEAPON_PRIMARY_SLOT ) - { - Weapon_DropSlot( WEAPON_PRIMARY_SLOT ); - } - -#endif - - BaseClass::Weapon_Equip( pWeapon ); -} - -//----------------------------------------------------------------------------- -// Purpose: Player reacts to bumping a weapon. -// Input : pWeapon - the weapon that the player bumped into. -// Output : Returns true if player picked up the weapon -//----------------------------------------------------------------------------- -bool CHL2_Player::BumpWeapon( CBaseCombatWeapon *pWeapon ) -{ - -#if HL2_SINGLE_PRIMARY_WEAPON_MODE - - CBaseCombatCharacter *pOwner = pWeapon->GetOwner(); - - // Can I have this weapon type? - if ( pOwner || !Weapon_CanUse( pWeapon ) || !g_pGameRules->CanHavePlayerItem( this, pWeapon ) ) - { - if ( gEvilImpulse101 ) - { - UTIL_Remove( pWeapon ); - } - return false; - } - - // ---------------------------------------- - // If I already have it just take the ammo - // ---------------------------------------- - if (Weapon_OwnsThisType( pWeapon->GetClassname(), pWeapon->GetSubType())) - { - //Only remove the weapon if we attained ammo from it - if ( Weapon_EquipAmmoOnly( pWeapon ) == false ) - return false; - - // Only remove me if I have no ammo left - // Can't just check HasAnyAmmo because if I don't use clips, I want to be removed, - if ( pWeapon->UsesClipsForAmmo1() && pWeapon->HasPrimaryAmmo() ) - return false; - - UTIL_Remove( pWeapon ); - return false; - } - // ------------------------- - // Otherwise take the weapon - // ------------------------- - else - { - //Make sure we're not trying to take a new weapon type we already have - if ( Weapon_SlotOccupied( pWeapon ) ) - { - CBaseCombatWeapon *pActiveWeapon = Weapon_GetSlot( WEAPON_PRIMARY_SLOT ); - - if ( pActiveWeapon != NULL && pActiveWeapon->HasAnyAmmo() == false && Weapon_CanSwitchTo( pWeapon ) ) - { - Weapon_Equip( pWeapon ); - return true; - } - - //Attempt to take ammo if this is the gun we're holding already - if ( Weapon_OwnsThisType( pWeapon->GetClassname(), pWeapon->GetSubType() ) ) - { - Weapon_EquipAmmoOnly( pWeapon ); - } - - return false; - } - - pWeapon->CheckRespawn(); - - pWeapon->AddSolidFlags( FSOLID_NOT_SOLID ); - pWeapon->AddEffects( EF_NODRAW ); - - Weapon_Equip( pWeapon ); - - EmitSound( "HL2Player.PickupWeapon" ); - - return true; - } -#else - - return BaseClass::BumpWeapon( pWeapon ); - -#endif - -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *cmd - -// Output : Returns true on success, false on failure. -//----------------------------------------------------------------------------- -bool CHL2_Player::ClientCommand(const char *cmd) -{ -#if HL2_SINGLE_PRIMARY_WEAPON_MODE - - //Drop primary weapon - if( stricmp( cmd, "DropPrimary" ) == 0 ) - { - Weapon_DropSlot( WEAPON_PRIMARY_SLOT ); - return true; - } - -#endif - - if ( !stricmp( cmd, "emit" ) ) - { - CSingleUserRecipientFilter filter( this ); - if ( engine->Cmd_Argc() > 1 ) - { - EmitSound( filter, entindex(), engine->Cmd_Argv( 1 ) ); - } - else - { - EmitSound( filter, entindex(), "Test.Sound" ); - } - return true; - } - - return BaseClass::ClientCommand( cmd ); -} - -//----------------------------------------------------------------------------- -// Purpose: -// Output : void CBasePlayer::PlayerUse -//----------------------------------------------------------------------------- -void CHL2_Player::PlayerUse ( void ) -{ - // Was use pressed or released? - if ( ! ((m_nButtons | m_afButtonPressed | m_afButtonReleased) & IN_USE) ) - return; - - if ( m_afButtonPressed & IN_USE ) - { - // Currently using a latched entity? - if ( ClearUseEntity() ) - { - return; - } - else - { - if ( m_afPhysicsFlags & PFLAG_DIROVERRIDE ) - { - m_afPhysicsFlags &= ~PFLAG_DIROVERRIDE; - m_iTrain = TRAIN_NEW|TRAIN_OFF; - return; - } - else - { // Start controlling the train! - CBaseEntity *pTrain = GetGroundEntity(); - if ( pTrain && !(m_nButtons & IN_JUMP) && (GetFlags() & FL_ONGROUND) && (pTrain->ObjectCaps() & FCAP_DIRECTIONAL_USE) && pTrain->OnControls(this) ) - { - m_afPhysicsFlags |= PFLAG_DIROVERRIDE; - m_iTrain = TrainSpeed(pTrain->m_flSpeed, ((CFuncTrackTrain*)pTrain)->GetMaxSpeed()); - m_iTrain |= TRAIN_NEW; - EmitSound( "HL2Player.TrainUse" ); - return; - } - } - } - - // Tracker 3926: We can't +USE something if we're climbing a ladder - if ( GetMoveType() == MOVETYPE_LADDER ) - { - return; - } - } - - if( m_flTimeUseSuspended > gpGlobals->curtime ) - { - // Something has temporarily stopped us being able to USE things. - // Obviously, this should be used very carefully.(sjb) - return; - } - - CBaseEntity *pUseEntity = FindUseEntity(); - - bool usedSomething = false; - - // Found an object - if ( pUseEntity ) - { - //!!!UNDONE: traceline here to prevent +USEing buttons through walls - int caps = pUseEntity->ObjectCaps(); - variant_t emptyVariant; - - if ( m_afButtonPressed & IN_USE ) - { - // Robin: Don't play sounds for NPCs, because NPCs will allow respond with speech. - if ( !pUseEntity->MyNPCPointer() ) - { - EmitSound( "HL2Player.Use" ); - } - } - - if ( ( (m_nButtons & IN_USE) && (caps & FCAP_CONTINUOUS_USE) ) || - ( (m_afButtonPressed & IN_USE) && (caps & (FCAP_IMPULSE_USE|FCAP_ONOFF_USE)) ) ) - { - if ( caps & FCAP_CONTINUOUS_USE ) - m_afPhysicsFlags |= PFLAG_USING; - - pUseEntity->AcceptInput( "Use", this, this, emptyVariant, USE_TOGGLE ); - - usedSomething = true; - } - // UNDONE: Send different USE codes for ON/OFF. Cache last ONOFF_USE object to send 'off' if you turn away - else if ( (m_afButtonReleased & IN_USE) && (pUseEntity->ObjectCaps() & FCAP_ONOFF_USE) ) // BUGBUG This is an "off" use - { - pUseEntity->AcceptInput( "Use", this, this, emptyVariant, USE_TOGGLE ); - - usedSomething = true; - } - -#if HL2_SINGLE_PRIMARY_WEAPON_MODE - - //Check for weapon pick-up - if ( m_afButtonPressed & IN_USE ) - { - CBaseCombatWeapon *pWeapon = dynamic_cast(pUseEntity); - - if ( ( pWeapon != NULL ) && ( Weapon_CanSwitchTo( pWeapon ) ) ) - { - //Try to take ammo or swap the weapon - if ( Weapon_OwnsThisType( pWeapon->GetClassname(), pWeapon->GetSubType() ) ) - { - Weapon_EquipAmmoOnly( pWeapon ); - } - else - { - Weapon_DropSlot( pWeapon->GetSlot() ); - Weapon_Equip( pWeapon ); - } - - usedSomething = true; - } - } -#endif - } - else if ( m_afButtonPressed & IN_USE ) - { - // Signal that we want to play the deny sound, unless the user is +USEing on a ladder! - // The sound is emitted in ItemPostFrame, since that occurs after GameMovement::ProcessMove which - // lets the ladder code unset this flag. - m_bPlayUseDenySound = true; - } - - // Debounce the use key - if ( usedSomething && pUseEntity ) - { - m_Local.m_nOldButtons |= IN_USE; - m_afButtonPressed &= ~IN_USE; - } -} - -ConVar sv_show_crosshair_target( "sv_show_crosshair_target", "0" ); - -//----------------------------------------------------------------------------- -// Purpose: Updates the posture of the weapon from lowered to ready -//----------------------------------------------------------------------------- -void CHL2_Player::UpdateWeaponPosture( void ) -{ - CBaseHLCombatWeapon *pWeapon = dynamic_cast(GetActiveWeapon()); - - if ( pWeapon && m_LowerWeaponTimer.Expired() && pWeapon->CanLower() ) - { - m_LowerWeaponTimer.Set( .3 ); - VPROF( "CHL2_Player::UpdateWeaponPosture-CheckLower" ); - Vector vecAim = BaseClass::GetAutoaimVector( AUTOAIM_SCALE_DIRECT_ONLY ); - - const float CHECK_FRIENDLY_RANGE = 50 * 12; - trace_t tr; - UTIL_TraceLine( EyePosition(), EyePosition() + vecAim * CHECK_FRIENDLY_RANGE, MASK_SHOT, this, COLLISION_GROUP_NONE, &tr ); - - CBaseEntity *aimTarget = tr.m_pEnt; - - //If we're over something - if ( aimTarget && !tr.DidHitWorld() ) - { - if ( !aimTarget->IsNPC() || aimTarget->MyNPCPointer()->GetState() != NPC_STATE_COMBAT ) - { - Disposition_t dis = IRelationType( aimTarget ); - - //Debug info for seeing what an object "cons" as - if ( sv_show_crosshair_target.GetBool() ) - { - int text_offset = BaseClass::DrawDebugTextOverlays(); - - char tempstr[255]; - - switch ( dis ) - { - case D_LI: - Q_snprintf( tempstr, sizeof(tempstr), "Disposition: Like" ); - break; - - case D_HT: - Q_snprintf( tempstr, sizeof(tempstr), "Disposition: Hate" ); - break; - - case D_FR: - Q_snprintf( tempstr, sizeof(tempstr), "Disposition: Fear" ); - break; - - case D_NU: - Q_snprintf( tempstr, sizeof(tempstr), "Disposition: Neutral" ); - break; - - default: - case D_ER: - Q_snprintf( tempstr, sizeof(tempstr), "Disposition: !!!ERROR!!!" ); - break; - } - - //Draw the text - NDebugOverlay::EntityText( aimTarget->entindex(), text_offset, tempstr, 0 ); - } - - //See if we hates it - if ( dis == D_LI ) - { - //We're over a friendly, drop our weapon - if ( Weapon_Lower() == false ) - { - //FIXME: We couldn't lower our weapon! - } - - return; - } - } - } - - if ( Weapon_Ready() == false ) - { - //FIXME: We couldn't raise our weapon! - } - } - - if( hl2_xbox_aiming.GetBool() ) - { - if( !pWeapon || !m_AutoaimTimer.Expired() ) - { - return; - } - - m_AutoaimTimer.Set( .1 ); - - VPROF( "hl2_xbox_aiming" ); - - // Call the autoaim code to update the local player data, which allows the client to update. - autoaim_params_t params; - params.m_fScale = AUTOAIM_SCALE_DEFAULT; - params.m_fMaxDist = autoaim_max_dist.GetFloat(); - GetAutoaimVector( params ); - m_HL2Local.m_hAutoAimTarget.Set( params.m_hAutoAimEntity ); - m_HL2Local.m_vecAutoAimPoint.Set( params.m_vecAutoAimPoint ); - return; - } - else - { - // Make sure there's no residual autoaim target if the user changes the xbox_aiming convar on the fly. - m_HL2Local.m_hAutoAimTarget.Set(NULL); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Lowers the weapon posture (for hovering over friendlies) -// Output : Returns true on success, false on failure. -//----------------------------------------------------------------------------- -bool CHL2_Player::Weapon_Lower( void ) -{ - VPROF( "CHL2_Player::Weapon_Lower" ); - // Already lowered? - if ( m_HL2Local.m_bWeaponLowered ) - return true; - - m_HL2Local.m_bWeaponLowered = true; - - CBaseHLCombatWeapon *pWeapon = dynamic_cast(GetActiveWeapon()); - - if ( pWeapon == NULL ) - return false; - - return pWeapon->Lower(); -} - -//----------------------------------------------------------------------------- -// Purpose: Returns the weapon posture to normal -// Output : Returns true on success, false on failure. -//----------------------------------------------------------------------------- -bool CHL2_Player::Weapon_Ready( void ) -{ - VPROF( "CHL2_Player::Weapon_Ready" ); - - // Already ready? - if ( m_HL2Local.m_bWeaponLowered == false ) - return true; - - m_HL2Local.m_bWeaponLowered = false; - - CBaseHLCombatWeapon *pWeapon = dynamic_cast(GetActiveWeapon()); - - if ( pWeapon == NULL ) - return false; - - return pWeapon->Ready(); -} - -//----------------------------------------------------------------------------- -// Purpose: Returns whether or not we can switch to the given weapon. -// Input : pWeapon - -//----------------------------------------------------------------------------- -bool CHL2_Player::Weapon_CanSwitchTo( CBaseCombatWeapon *pWeapon ) -{ - CBasePlayer *pPlayer = (CBasePlayer *)this; -#if !defined( CLIENT_DLL ) - IServerVehicle *pVehicle = pPlayer->GetVehicle(); -#else - IClientVehicle *pVehicle = pPlayer->GetVehicle(); -#endif - if (pVehicle && !pPlayer->UsingStandardWeaponsInVehicle()) - return false; - - if ( !pWeapon->HasAnyAmmo() && !GetAmmoCount( pWeapon->m_iPrimaryAmmoType ) ) - return false; - - if ( !pWeapon->CanDeploy() ) - return false; - - if ( GetActiveWeapon() ) - { - if ( PhysCannonGetHeldEntity( GetActiveWeapon() ) == pWeapon && - Weapon_OwnsThisType( pWeapon->GetClassname(), pWeapon->GetSubType()) ) - { - return true; - } - - if ( !GetActiveWeapon()->CanHolster() ) - return false; - } - - return true; -} - -void CHL2_Player::PickupObject( CBaseEntity *pObject, bool bLimitMassAndSize ) -{ - // can't pick up what you're standing on - if ( GetGroundEntity() == pObject ) - return; - - if ( bLimitMassAndSize == true ) - { - if ( CBasePlayer::CanPickupObject( pObject, 35, 128 ) == false ) - return; - } - - // Can't be picked up if NPCs are on me - if ( pObject->HasNPCsOnIt() ) - return; - - PlayerPickupObject( this, pObject ); -} - -//----------------------------------------------------------------------------- -// Purpose: -// Output : CBaseEntity -//----------------------------------------------------------------------------- -bool CHL2_Player::IsHoldingEntity( CBaseEntity *pEnt ) -{ - return PlayerPickupControllerIsHoldingEntity( m_hUseEntity, pEnt ); -} - -float CHL2_Player::GetHeldObjectMass( IPhysicsObject *pHeldObject ) -{ - float mass = PlayerPickupGetHeldObjectMass( m_hUseEntity, pHeldObject ); - if ( mass == 0.0f ) - { - mass = PhysCannonGetHeldObjectMass( GetActiveWeapon(), pHeldObject ); - } - return mass; -} - -//----------------------------------------------------------------------------- -// Purpose: Force the player to drop any physics objects he's carrying -//----------------------------------------------------------------------------- -void CHL2_Player::ForceDropOfCarriedPhysObjects( CBaseEntity *pOnlyIfHoldingThis ) -{ - if ( PhysIsInCallback() ) - { - variant_t value; - g_EventQueue.AddEvent( this, "ForceDropPhysObjects", value, 0.01f, pOnlyIfHoldingThis, this ); - return; - } - // Drop any objects being handheld. - ClearUseEntity(); - - // Then force the physcannon to drop anything it's holding, if it's our active weapon - PhysCannonForceDrop( GetActiveWeapon(), pOnlyIfHoldingThis ); -} - -void CHL2_Player::InputForceDropPhysObjects( inputdata_t &data ) -{ - ForceDropOfCarriedPhysObjects( data.pActivator ); -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CHL2_Player::UpdateClientData( void ) -{ - if (m_DmgTake || m_DmgSave || m_bitsHUDDamage != m_bitsDamageType) - { - // Comes from inside me if not set - Vector damageOrigin = GetLocalOrigin(); - // send "damage" message - // causes screen to flash, and pain compass to show direction of damage - damageOrigin = m_DmgOrigin; - - // only send down damage type that have hud art - int visibleDamageBits = m_bitsDamageType & DMG_SHOWNHUD; - - m_DmgTake = clamp( m_DmgTake, 0, 255 ); - m_DmgSave = clamp( m_DmgSave, 0, 255 ); - - // If we're poisoned, but it wasn't this frame, don't send the indicator - // Without this check, any damage that occured to the player while they were - // recovering from a poison bite would register as poisonous as well and flash - // the whole screen! -- jdw - if ( visibleDamageBits & DMG_POISON ) - { - float flLastPoisonedDelta = gpGlobals->curtime - m_tbdPrev; - if ( flLastPoisonedDelta > 0.1f ) - { - visibleDamageBits &= ~DMG_POISON; - } - } - - CSingleUserRecipientFilter user( this ); - user.MakeReliable(); - UserMessageBegin( user, "Damage" ); - WRITE_BYTE( m_DmgSave ); - WRITE_BYTE( m_DmgTake ); - WRITE_LONG( visibleDamageBits ); - WRITE_FLOAT( damageOrigin.x ); //BUG: Should be fixed point (to hud) not floats - WRITE_FLOAT( damageOrigin.y ); //BUG: However, the HUD does _not_ implement bitfield messages (yet) - WRITE_FLOAT( damageOrigin.z ); //BUG: We use WRITE_VEC3COORD for everything else - MessageEnd(); - - m_DmgTake = 0; - m_DmgSave = 0; - m_bitsHUDDamage = m_bitsDamageType; - - // Clear off non-time-based damage indicators - m_bitsDamageType &= DMG_TIMEBASED; - } - - BaseClass::UpdateClientData(); -} - -//--------------------------------------------------------- -//--------------------------------------------------------- -void CHL2_Player::OnRestore() -{ - BaseClass::OnRestore(); - m_pPlayerAISquad = g_AI_SquadManager.FindCreateSquad(AllocPooledString(PLAYER_SQUADNAME)); -} - -//--------------------------------------------------------- -//--------------------------------------------------------- -Vector CHL2_Player::EyeDirection2D( void ) -{ - Vector vecReturn = EyeDirection3D(); - vecReturn.z = 0; - vecReturn.AsVector2D().NormalizeInPlace(); - - return vecReturn; -} - -//--------------------------------------------------------- -//--------------------------------------------------------- -Vector CHL2_Player::EyeDirection3D( void ) -{ - Vector vecForward; - IServerVehicle *pVehicle = GetVehicle(); - if ( pVehicle ) - { - Assert( pVehicle ); - int nRole = pVehicle->GetPassengerRole( this ); - - Vector vecEyeOrigin; - QAngle angEyeAngles; - pVehicle->GetVehicleViewPosition( nRole, &vecEyeOrigin, &angEyeAngles ); - AngleVectors( angEyeAngles, &vecForward ); - } - else - { - AngleVectors( EyeAngles(), &vecForward ); - } - return vecForward; -} - - -//--------------------------------------------------------- -//--------------------------------------------------------- -bool CHL2_Player::Weapon_Switch( CBaseCombatWeapon *pWeapon, int viewmodelindex ) -{ - // Recalculate proficiency! - SetCurrentWeaponProficiency( CalcWeaponProficiency( pWeapon ) ); - - // Come out of suit zoom mode - if ( IsZooming() ) - { - StopZooming(); - } - - return BaseClass::Weapon_Switch( pWeapon, viewmodelindex ); -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -WeaponProficiency_t CHL2_Player::CalcWeaponProficiency( CBaseCombatWeapon *pWeapon ) -{ - WeaponProficiency_t proficiency; - - proficiency = WEAPON_PROFICIENCY_PERFECT; - - if( weapon_showproficiency.GetBool() != 0 ) - { - Msg("Player switched to %s, proficiency is %s\n", pWeapon->GetClassname(), GetWeaponProficiencyName( proficiency ) ); - } - - return proficiency; -} - -//----------------------------------------------------------------------------- -// Purpose: override how single player rays hit the player -//----------------------------------------------------------------------------- - -bool LineCircleIntersection( - const Vector2D ¢er, - const float radius, - const Vector2D &vLinePt, - const Vector2D &vLineDir, - float *fIntersection1, - float *fIntersection2) -{ - // Line = P + Vt - // Sphere = r (assume we've translated to origin) - // (P + Vt)^2 = r^2 - // VVt^2 + 2PVt + (PP - r^2) - // Solve as quadratic: (-b +/- sqrt(b^2 - 4ac)) / 2a - // If (b^2 - 4ac) is < 0 there is no solution. - // If (b^2 - 4ac) is = 0 there is one solution (a case this function doesn't support). - // If (b^2 - 4ac) is > 0 there are two solutions. - Vector2D P; - float a, b, c, sqr, insideSqr; - - - // Translate circle to origin. - P[0] = vLinePt[0] - center[0]; - P[1] = vLinePt[1] - center[1]; - - a = vLineDir.Dot(vLineDir); - b = 2.0f * P.Dot(vLineDir); - c = P.Dot(P) - (radius * radius); - - insideSqr = b*b - 4*a*c; - if(insideSqr <= 0.000001f) - return false; - - // Ok, two solutions. - sqr = (float)FastSqrt(insideSqr); - - float denom = 1.0 / (2.0f * a); - - *fIntersection1 = (-b - sqr) * denom; - *fIntersection2 = (-b + sqr) * denom; - - return true; -} - -static void Collision_ClearTrace( const Vector &vecRayStart, const Vector &vecRayDelta, CBaseTrace *pTrace ) -{ - pTrace->startpos = vecRayStart; - pTrace->endpos = vecRayStart; - pTrace->endpos += vecRayDelta; - pTrace->startsolid = false; - pTrace->allsolid = false; - pTrace->fraction = 1.0f; - pTrace->contents = 0; -} - - -bool IntersectRayWithAACylinder( const Ray_t &ray, - const Vector ¢er, float radius, float height, CBaseTrace *pTrace ) -{ - Assert( ray.m_IsRay ); - Collision_ClearTrace( ray.m_Start, ray.m_Delta, pTrace ); - - // First intersect the ray with the top + bottom planes - float halfHeight = height * 0.5; - - // Handle parallel case - Vector vStart = ray.m_Start - center; - Vector vEnd = vStart + ray.m_Delta; - - float flEnterFrac, flLeaveFrac; - if (FloatMakePositive(ray.m_Delta.z) < 1e-8) - { - if ( (vStart.z < -halfHeight) || (vStart.z > halfHeight) ) - { - return false; // no hit - } - flEnterFrac = 0.0f; flLeaveFrac = 1.0f; - } - else - { - // Clip the ray to the top and bottom of box - flEnterFrac = IntersectRayWithAAPlane( vStart, vEnd, 2, 1, halfHeight); - flLeaveFrac = IntersectRayWithAAPlane( vStart, vEnd, 2, 1, -halfHeight); - - if ( flLeaveFrac < flEnterFrac ) - { - float temp = flLeaveFrac; - flLeaveFrac = flEnterFrac; - flEnterFrac = temp; - } - - if ( flLeaveFrac < 0 || flEnterFrac > 1) - { - return false; - } - } - - // Intersect with circle - float flCircleEnterFrac, flCircleLeaveFrac; - if ( !LineCircleIntersection( vec3_origin.AsVector2D(), radius, - vStart.AsVector2D(), ray.m_Delta.AsVector2D(), &flCircleEnterFrac, &flCircleLeaveFrac ) ) - { - return false; // no hit - } - - Assert( flCircleEnterFrac <= flCircleLeaveFrac ); - if ( flCircleLeaveFrac < 0 || flCircleEnterFrac > 1) - { - return false; - } - - if ( flEnterFrac < flCircleEnterFrac ) - flEnterFrac = flCircleEnterFrac; - if ( flLeaveFrac > flCircleLeaveFrac ) - flLeaveFrac = flCircleLeaveFrac; - - if ( flLeaveFrac < flEnterFrac ) - return false; - - VectorMA( ray.m_Start, flEnterFrac , ray.m_Delta, pTrace->endpos ); - pTrace->fraction = flEnterFrac; - pTrace->contents = CONTENTS_SOLID; - - // Calculate the point on our center line where we're nearest the intersection point - Vector collisionCenter; - CalcClosestPointOnLineSegment( pTrace->endpos, center + Vector( 0, 0, halfHeight ), center - Vector( 0, 0, halfHeight ), collisionCenter ); - - // Our normal is the direction from that center point to the intersection point - pTrace->plane.normal = pTrace->endpos - collisionCenter; - VectorNormalize( pTrace->plane.normal ); - - return true; -} - - -bool CHL2_Player::TestHitboxes( const Ray_t &ray, unsigned int fContentsMask, trace_t& tr ) -{ - if( g_pGameRules->IsMultiplayer() ) - { - return BaseClass::TestHitboxes( ray, fContentsMask, tr ); - } - else - { - Assert( ray.m_IsRay ); - - Vector mins, maxs; - - mins = WorldAlignMins(); - maxs = WorldAlignMaxs(); - - if ( IntersectRayWithAACylinder( ray, WorldSpaceCenter(), maxs.x * PLAYER_HULL_REDUCTION, maxs.z - mins.z, &tr ) ) - { - CStudioHdr *pStudioHdr = GetModelPtr( ); - if (!pStudioHdr) - return false; - - mstudiohitboxset_t *set = pStudioHdr->pHitboxSet( m_nHitboxSet ); - if ( !set || !set->numhitboxes ) - return false; - - mstudiobbox_t *pbox = set->pHitbox( tr.hitbox ); - mstudiobone_t *pBone = pStudioHdr->pBone(pbox->bone); - tr.surface.name = "**studio**"; - tr.surface.flags = SURF_HITBOX; - tr.surface.surfaceProps = physprops->GetSurfaceIndex( pBone->pszSurfaceProp() ); - } - - return true; - } -} - -//--------------------------------------------------------- -// Show the player's scaled down bbox that we use for -// bullet impacts. -//--------------------------------------------------------- -void CHL2_Player::DrawDebugGeometryOverlays(void) -{ - BaseClass::DrawDebugGeometryOverlays(); - - if (m_debugOverlays & OVERLAY_BBOX_BIT) - { - Vector mins, maxs; - - mins = WorldAlignMins(); - maxs = WorldAlignMaxs(); - - mins.x *= PLAYER_HULL_REDUCTION; - mins.y *= PLAYER_HULL_REDUCTION; - - maxs.x *= PLAYER_HULL_REDUCTION; - maxs.y *= PLAYER_HULL_REDUCTION; - - NDebugOverlay::Box( GetAbsOrigin(), mins, maxs, 255, 0, 0, 100, 0 ); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Helper to remove from ladder -//----------------------------------------------------------------------------- -void CHL2_Player::ExitLadder() -{ - if ( MOVETYPE_LADDER != GetMoveType() ) - return; - - SetMoveType( MOVETYPE_WALK ); - SetMoveCollide( MOVECOLLIDE_DEFAULT ); - // Remove from ladder - m_HL2Local.m_hLadder.Set( NULL ); -} - - -//----------------------------------------------------------------------------- -// Purpose: Queues up a use deny sound, played in ItemPostFrame. -//----------------------------------------------------------------------------- -void CHL2_Player::PlayUseDenySound() -{ - m_bPlayUseDenySound = true; -} - - -void CHL2_Player::ItemPostFrame() -{ - BaseClass::ItemPostFrame(); - - if ( m_bPlayUseDenySound ) - { - m_bPlayUseDenySound = false; - EmitSound( "HL2Player.UseDeny" ); - } -} - - -void CHL2_Player::StartWaterDeathSounds( void ) -{ - CPASAttenuationFilter filter( this ); - - if ( m_sndLeeches == NULL ) - { - m_sndLeeches = (CSoundEnvelopeController::GetController()).SoundCreate( filter, entindex(), CHAN_STATIC, "coast.leech_bites_loop" , ATTN_NORM ); - } - - if ( m_sndLeeches ) - { - (CSoundEnvelopeController::GetController()).Play( m_sndLeeches, 1.0f, 100 ); - } - - if ( m_sndWaterSplashes == NULL ) - { - m_sndWaterSplashes = (CSoundEnvelopeController::GetController()).SoundCreate( filter, entindex(), CHAN_STATIC, "coast.leech_water_churn_loop" , ATTN_NORM ); - } - - if ( m_sndWaterSplashes ) - { - (CSoundEnvelopeController::GetController()).Play( m_sndWaterSplashes, 1.0f, 100 ); - } -} - -void CHL2_Player::StopWaterDeathSounds( void ) -{ - if ( m_sndLeeches ) - { - (CSoundEnvelopeController::GetController()).SoundFadeOut( m_sndLeeches, 0.5f, true ); - m_sndLeeches = NULL; - } - - if ( m_sndWaterSplashes ) - { - (CSoundEnvelopeController::GetController()).SoundFadeOut( m_sndWaterSplashes, 0.5f, true ); - m_sndWaterSplashes = NULL; - } -} - -//----------------------------------------------------------------------------- -// Shuts down sounds -//----------------------------------------------------------------------------- -void CHL2_Player::StopLoopingSounds( void ) -{ - if ( m_sndLeeches != NULL ) - { - (CSoundEnvelopeController::GetController()).SoundDestroy( m_sndLeeches ); - m_sndLeeches = NULL; - } - - if ( m_sndWaterSplashes != NULL ) - { - (CSoundEnvelopeController::GetController()).SoundDestroy( m_sndWaterSplashes ); - m_sndWaterSplashes = NULL; - } - - BaseClass::StopLoopingSounds(); -} - -//----------------------------------------------------------------------------- -void CHL2_Player::ModifyOrAppendPlayerCriteria( AI_CriteriaSet& set ) -{ - BaseClass::ModifyOrAppendPlayerCriteria( set ); - - if ( GlobalEntity_GetIndex( "gordon_precriminal" ) == -1 ) - { - set.AppendCriteria( "gordon_precriminal", "0" ); - } -} - -CLogicPlayerProxy *CHL2_Player::GetPlayerProxy( void ) -{ - CLogicPlayerProxy *pProxy = dynamic_cast< CLogicPlayerProxy* > ( m_hPlayerProxy.Get() ); - - if ( pProxy == NULL ) - { - pProxy = (CLogicPlayerProxy*)gEntList.FindEntityByClassname(NULL, "logic_playerproxy" ); - - if ( pProxy == NULL ) - return NULL; - - pProxy->m_hPlayer = this; - m_hPlayerProxy = pProxy; - } - - return pProxy; -} - -void CHL2_Player::FirePlayerProxyOutput( const char *pszOutputName, variant_t variant, CBaseEntity *pActivator, CBaseEntity *pCaller ) -{ - if ( GetPlayerProxy() == NULL ) - return; - - GetPlayerProxy()->FireNamedOutput( pszOutputName, variant, pActivator, pCaller ); -} - -LINK_ENTITY_TO_CLASS( logic_playerproxy, CLogicPlayerProxy); - -BEGIN_DATADESC( CLogicPlayerProxy ) - DEFINE_OUTPUT( m_OnFlashlightOn, "OnFlashlightOn" ), - DEFINE_OUTPUT( m_OnFlashlightOff, "OnFlashlightOff" ), - DEFINE_OUTPUT( m_RequestedPlayerHealth, "PlayerHealth" ), - DEFINE_OUTPUT( m_PlayerHasAmmo, "PlayerHasAmmo" ), - DEFINE_OUTPUT( m_PlayerHasNoAmmo, "PlayerHasNoAmmo" ), - DEFINE_INPUTFUNC( FIELD_VOID, "RequestPlayerHealth", InputRequestPlayerHealth ), - DEFINE_INPUTFUNC( FIELD_VOID, "SetFlashlightSlowDrain", InputSetFlashlightSlowDrain ), - DEFINE_INPUTFUNC( FIELD_VOID, "SetFlashlightNormalDrain", InputSetFlashlightNormalDrain ), - DEFINE_INPUTFUNC( FIELD_INTEGER, "SetPlayerHealth", InputSetPlayerHealth ), - DEFINE_INPUTFUNC( FIELD_VOID, "RequestAmmoState", InputRequestAmmoState ), - DEFINE_FIELD( m_hPlayer, FIELD_EHANDLE ), -END_DATADESC() - -void CLogicPlayerProxy::Activate( void ) -{ - BaseClass::Activate(); - - if ( m_hPlayer == NULL ) - { - m_hPlayer = AI_GetSinglePlayer(); - } -} - -void CLogicPlayerProxy::InputSetPlayerHealth( inputdata_t &inputdata ) -{ - if ( m_hPlayer == NULL ) - return; - - m_hPlayer->SetHealth( inputdata.value.Int() ); - -} - -void CLogicPlayerProxy::InputRequestPlayerHealth( inputdata_t &inputdata ) -{ - if ( m_hPlayer == NULL ) - return; - - m_RequestedPlayerHealth.Set( m_hPlayer->GetHealth(), inputdata.pActivator, inputdata.pCaller ); -} - -void CLogicPlayerProxy::InputSetFlashlightSlowDrain( inputdata_t &inputdata ) -{ - if( m_hPlayer == NULL ) - return; - - CHL2_Player *pPlayer = dynamic_cast(m_hPlayer.Get()); - - if( pPlayer ) - pPlayer->SetFlashlightPowerDrainScale( hl2_darkness_flashlight_factor.GetFloat() ); -} - -void CLogicPlayerProxy::InputSetFlashlightNormalDrain( inputdata_t &inputdata ) -{ - if( m_hPlayer == NULL ) - return; - - CHL2_Player *pPlayer = dynamic_cast(m_hPlayer.Get()); - - if( pPlayer ) - pPlayer->SetFlashlightPowerDrainScale( 1.0f ); -} - -void CLogicPlayerProxy::InputRequestAmmoState( inputdata_t &inputdata ) -{ - if( m_hPlayer == NULL ) - return; - - CHL2_Player *pPlayer = dynamic_cast(m_hPlayer.Get()); - - for ( int i = 0 ; i < pPlayer->WeaponCount(); ++i ) - { - CBaseCombatWeapon* pCheck = pPlayer->GetWeapon( i ); - - if ( pCheck ) - { - if ( pCheck->HasAnyAmmo() && (pCheck->UsesPrimaryAmmo() || pCheck->UsesSecondaryAmmo())) - { - m_PlayerHasAmmo.FireOutput( this, this, 0 ); - return; - } - } - } - - m_PlayerHasNoAmmo.FireOutput( this, this, 0 ); -} \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: Player for HL2. +// +//=============================================================================// + +#include "cbase.h" +#include "hl2_player.h" +#include "globalstate.h" +#include "game.h" +#include "gamerules.h" +#include "trains.h" +#include "basehlcombatweapon_shared.h" +#include "vcollide_parse.h" +#include "in_buttons.h" +#include "ai_interactions.h" +#include "ai_squad.h" +#include "igamemovement.h" +#include "ai_hull.h" +#include "hl2_shareddefs.h" +#include "info_camera_link.h" +#include "point_camera.h" +#include "engine/IEngineSound.h" +#include "ndebugoverlay.h" +#include "iservervehicle.h" +#include "IVehicle.h" +#include "globals.h" +#include "collisionutils.h" +#include "coordsize.h" +#include "effect_color_tables.h" +#include "vphysics/player_controller.h" +#include "player_pickup.h" +#include "weapon_physcannon.h" +#include "script_intro.h" +#include "effect_dispatch_data.h" +#include "te_effect_dispatch.h" +#include "ai_basenpc.h" +#include "AI_Criteria.h" +#include "npc_barnacle.h" +#include "entitylist.h" +#include "env_zoom.h" +#include "hl2_gamerules.h" +#include "prop_combine_ball.h" +#include "datacache/imdlcache.h" +#include "eventqueue.h" + +#ifdef HL2_EPISODIC +#include "npc_alyx_episodic.h" +#endif + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +extern ConVar weapon_showproficiency; +extern ConVar autoaim_max_dist; + +// Do not touch with without seeing me, please! (sjb) +// For consistency's sake, enemy gunfire is traced against a scaled down +// version of the player's hull, not the hitboxes for the player's model +// because the player isn't aware of his model, and can't do anything about +// preventing headshots and other such things. Also, game difficulty will +// not change if the model changes. This is the value by which to scale +// the X/Y of the player's hull to get the volume to trace bullets against. +#define PLAYER_HULL_REDUCTION 0.70 + +// This switches between the single primary weapon, and multiple weapons with buckets approach (jdw) +#define HL2_SINGLE_PRIMARY_WEAPON_MODE 0 + +#define TIME_IGNORE_FALL_DAMAGE 10.0 + +extern int gEvilImpulse101; + +ConVar sv_autojump( "sv_autojump", "0" ); + +ConVar hl2_walkspeed( "hl2_walkspeed", "150" ); +ConVar hl2_normspeed( "hl2_normspeed", "190" ); +ConVar hl2_sprintspeed( "hl2_sprintspeed", "320" ); + +ConVar hl2_xbox_aiming( "hl2_xbox_aiming", "1" ); + +ConVar hl2_darkness_flashlight_factor ( "hl2_darkness_flashlight_factor", "1" ); + +#ifdef HL2MP + #define HL2_WALK_SPEED 150 + #define HL2_NORM_SPEED 190 + #define HL2_SPRINT_SPEED 320 +#else + #define HL2_WALK_SPEED hl2_walkspeed.GetFloat() + #define HL2_NORM_SPEED hl2_normspeed.GetFloat() + #define HL2_SPRINT_SPEED hl2_sprintspeed.GetFloat() +#endif + +#ifdef _XBOX + #define AUTOSPRINT_MIN_DURATION 1.0f +#endif//_XBOX + +ConVar player_showpredictedposition( "player_showpredictedposition", "0" ); +ConVar player_showpredictedposition_timestep( "player_showpredictedposition_timestep", "1.0" ); + +ConVar player_squad_transient_commands( "player_squad_transient_commands", "1", FCVAR_REPLICATED ); +ConVar player_squad_double_tap_time( "player_squad_double_tap_time", "0.25" ); + +ConVar sv_infinite_aux_power( "sv_infinite_aux_power", "0", FCVAR_CHEAT ); + +ConVar autoaim_unlock_target( "autoaim_unlock_target", "0.8666" ); + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +void CC_ToggleZoom( void ) +{ + CBasePlayer* pPlayer = UTIL_GetCommandClient(); + + if( pPlayer ) + { + CHL2_Player *pHL2Player = dynamic_cast(pPlayer); + + if( pHL2Player && pHL2Player->IsSuitEquipped() ) + { + pHL2Player->ToggleZoom(); + } + } +} + +static ConCommand toggle_zoom("toggle_zoom", CC_ToggleZoom, "Toggles zoom display" ); + +// ConVar cl_forwardspeed( "cl_forwardspeed", "400", FCVAR_CHEAT ); // Links us to the client's version +ConVar xc_crouch_range( "xc_crouch_range", "0.85", FCVAR_ARCHIVE, "Percentarge [1..0] of joystick range to allow ducking within" ); // Only 1/2 of the range is used +ConVar xc_use_crouch_limiter( "xc_use_crouch_limiter", "0", FCVAR_ARCHIVE, "Use the crouch limiting logic on the controller" ); + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +void CC_ToggleDuck( void ) +{ + CBasePlayer* pPlayer = UTIL_GetCommandClient(); + if ( pPlayer == NULL ) + return; + + // Cannot be frozen + if ( pPlayer->GetFlags() & FL_FROZEN ) + return; + + static bool bChecked = false; + static ConVar *pCVcl_forwardspeed = NULL; + if ( !bChecked ) + { + bChecked = true; + pCVcl_forwardspeed = ( ConVar * )cvar->FindVar( "cl_forwardspeed" ); + } + + + // If we're not ducked, do extra checking + if ( xc_use_crouch_limiter.GetBool() ) + { + if ( pPlayer->GetToggledDuckState() == false ) + { + float flForwardSpeed = 400.0f; + if ( pCVcl_forwardspeed ) + { + flForwardSpeed = pCVcl_forwardspeed->GetFloat(); + } + + flForwardSpeed = max( 1.0f, flForwardSpeed ); + + // Make sure we're not in the blindspot on the crouch detection + float flStickDistPerc = ( pPlayer->GetStickDist() / flForwardSpeed ); // Speed is the magnitude + if ( flStickDistPerc > xc_crouch_range.GetFloat() ) + return; + } + } + + // Toggle the duck + pPlayer->ToggleDuck(); +} + +static ConCommand toggle_duck("toggle_duck", CC_ToggleDuck, "Toggles duck" ); + +#ifndef HL2MP +LINK_ENTITY_TO_CLASS( player, CHL2_Player ); +#endif + +PRECACHE_REGISTER(player); + +CBaseEntity *FindEntityForward( CBasePlayer *pMe, bool fHull ); + +BEGIN_SIMPLE_DATADESC( LadderMove_t ) + DEFINE_FIELD( m_bForceLadderMove, FIELD_BOOLEAN ), + DEFINE_FIELD( m_bForceMount, FIELD_BOOLEAN ), + DEFINE_FIELD( m_flStartTime, FIELD_TIME ), + DEFINE_FIELD( m_flArrivalTime, FIELD_TIME ), + DEFINE_FIELD( m_vecGoalPosition, FIELD_POSITION_VECTOR ), + DEFINE_FIELD( m_vecStartPosition, FIELD_POSITION_VECTOR ), + DEFINE_FIELD( m_hForceLadder, FIELD_EHANDLE ), + DEFINE_FIELD( m_hReservedSpot, FIELD_EHANDLE ), +END_DATADESC() + +// Global Savedata for HL2 player +BEGIN_DATADESC( CHL2_Player ) + + DEFINE_FIELD( m_nControlClass, FIELD_INTEGER ), + DEFINE_EMBEDDED( m_HL2Local ), + + DEFINE_FIELD( m_bSprintEnabled, FIELD_BOOLEAN ), + DEFINE_FIELD( m_flTimeAllSuitDevicesOff, FIELD_TIME ), + DEFINE_FIELD( m_fIsSprinting, FIELD_BOOLEAN ), + DEFINE_FIELD( m_fIsWalking, FIELD_BOOLEAN ), + + /* + // These are initialized every time the player calls Activate() + DEFINE_FIELD( m_bIsAutoSprinting, FIELD_BOOLEAN ), + DEFINE_FIELD( m_fAutoSprintMinTime, FIELD_TIME ), + */ + + // Field is used within a single tick, no need to save restore + // DEFINE_FIELD( m_bPlayUseDenySound, FIELD_BOOLEAN ), + // m_pPlayerAISquad reacquired on load + + DEFINE_AUTO_ARRAY( m_vecMissPositions, FIELD_POSITION_VECTOR ), + DEFINE_FIELD( m_nNumMissPositions, FIELD_INTEGER ), + + // m_pPlayerAISquad + DEFINE_EMBEDDED( m_CommanderUpdateTimer ), + // m_RealTimeLastSquadCommand + DEFINE_FIELD( m_QueuedCommand, FIELD_INTEGER ), + + DEFINE_FIELD( m_flTimeIgnoreFallDamage, FIELD_TIME ), + + // Suit power fields + DEFINE_FIELD( m_flSuitPowerLoad, FIELD_FLOAT ), + + DEFINE_FIELD( m_flIdleTime, FIELD_TIME ), + DEFINE_FIELD( m_flMoveTime, FIELD_TIME ), + DEFINE_FIELD( m_flLastDamageTime, FIELD_TIME ), + DEFINE_FIELD( m_flTargetFindTime, FIELD_TIME ), + + DEFINE_FIELD( m_flAdmireGlovesAnimTime, FIELD_TIME ), + DEFINE_FIELD( m_flNextFlashlightCheckTime, FIELD_TIME ), + DEFINE_FIELD( m_flFlashlightPowerDrainScale, FIELD_FLOAT ), + DEFINE_FIELD( m_bFlashlightDisabled, FIELD_BOOLEAN ), + + DEFINE_FIELD( m_hLockedAutoAimEntity, FIELD_EHANDLE ), + + DEFINE_EMBEDDED( m_LowerWeaponTimer ), + DEFINE_EMBEDDED( m_AutoaimTimer ), + + DEFINE_INPUTFUNC( FIELD_FLOAT, "IgnoreFallDamage", InputIgnoreFallDamage ), + DEFINE_INPUTFUNC( FIELD_VOID, "OnSquadMemberKilled", OnSquadMemberKilled ), + DEFINE_INPUTFUNC( FIELD_VOID, "DisableFlashlight", InputDisableFlashlight ), + DEFINE_INPUTFUNC( FIELD_VOID, "EnableFlashlight", InputEnableFlashlight ), + DEFINE_INPUTFUNC( FIELD_VOID, "ForceDropPhysObjects", InputForceDropPhysObjects ), + + DEFINE_SOUNDPATCH( m_sndLeeches ), + DEFINE_SOUNDPATCH( m_sndWaterSplashes ), + + DEFINE_FIELD( m_flArmorReductionTime, FIELD_TIME ), + DEFINE_FIELD( m_iArmorReductionFrom, FIELD_INTEGER ), + + DEFINE_FIELD( m_flTimeUseSuspended, FIELD_TIME ), + + //DEFINE_FIELD( m_hPlayerProxy, FIELD_EHANDLE ), //Shut up class check! + +END_DATADESC() + +CHL2_Player::CHL2_Player() +{ + m_nNumMissPositions = 0; + m_pPlayerAISquad = 0; + m_bSprintEnabled = true; + + m_flArmorReductionTime = 0.0f; + m_iArmorReductionFrom = 0; +} + +// +// SUIT POWER DEVICES +// +#define SUITPOWER_CHARGE_RATE 12.5 // 100 units in 8 seconds + +#ifdef HL2MP + CSuitPowerDevice SuitDeviceSprint( bits_SUIT_DEVICE_SPRINT, 25.0f ); // 100 units in 4 seconds +#else + CSuitPowerDevice SuitDeviceSprint( bits_SUIT_DEVICE_SPRINT, 12.5f ); // 100 units in 8 seconds +#endif + +#ifdef HL2_EPISODIC + CSuitPowerDevice SuitDeviceFlashlight( bits_SUIT_DEVICE_FLASHLIGHT, 1.111 ); // 100 units in 90 second +#else + CSuitPowerDevice SuitDeviceFlashlight( bits_SUIT_DEVICE_FLASHLIGHT, 2.222 ); // 100 units in 45 second +#endif +CSuitPowerDevice SuitDeviceBreather( bits_SUIT_DEVICE_BREATHER, 6.7f ); // 100 units in 15 seconds (plus three padded seconds) + + +IMPLEMENT_SERVERCLASS_ST(CHL2_Player, DT_HL2_Player) + SendPropDataTable(SENDINFO_DT(m_HL2Local), &REFERENCE_SEND_TABLE(DT_HL2Local), SendProxy_SendLocalDataTable), + SendPropBool( SENDINFO(m_fIsSprinting) ), +END_SEND_TABLE() + + +void CHL2_Player::Precache( void ) +{ + BaseClass::Precache(); + + PrecacheScriptSound( "HL2Player.SprintNoPower" ); + PrecacheScriptSound( "HL2Player.SprintStart" ); + PrecacheScriptSound( "HL2Player.UseDeny" ); + PrecacheScriptSound( "HL2Player.FlashLightOn" ); + PrecacheScriptSound( "HL2Player.FlashLightOff" ); + PrecacheScriptSound( "HL2Player.PickupWeapon" ); + PrecacheScriptSound( "HL2Player.TrainUse" ); + PrecacheScriptSound( "HL2Player.Use" ); + PrecacheScriptSound( "HL2Player.BurnPain" ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CHL2_Player::CheckSuitZoom( void ) +{ +//#ifndef _XBOX + //Adrian - No zooming without a suit! + if ( IsSuitEquipped() ) + { + if ( m_afButtonReleased & IN_ZOOM ) + { + StopZooming(); + } + else if ( m_afButtonPressed & IN_ZOOM ) + { + StartZooming(); + } + } +//#endif//_XBOX +} + +void CHL2_Player::EquipSuit( bool bPlayEffects ) +{ + MDLCACHE_CRITICAL_SECTION(); + BaseClass::EquipSuit(); + + m_HL2Local.m_bDisplayReticle = true; + + if ( bPlayEffects == true ) + { + StartAdmireGlovesAnimation(); + } +} + +void CHL2_Player::RemoveSuit( void ) +{ + BaseClass::RemoveSuit(); + + m_HL2Local.m_bDisplayReticle = false; +} + +void CHL2_Player::HandleSpeedChanges( void ) +{ + int buttonsChanged = m_afButtonPressed | m_afButtonReleased; + + if( buttonsChanged & IN_SPEED ) + { + // The state of the sprint/run button has changed. + if ( IsSuitEquipped() ) + { + if ( !(m_afButtonPressed & IN_SPEED) && IsSprinting() ) + { +#ifndef _XBOX + StopSprinting(); +#endif + } + else if ( (m_afButtonPressed & IN_SPEED) && !IsSprinting() ) + { + if ( CanSprint() ) + { +#ifdef _XBOX + StartAutoSprint(); +#else + StartSprinting(); +#endif + } + else + { + // Reset key, so it will be activated post whatever is suppressing it. + m_nButtons &= ~IN_SPEED; + } + } +#ifdef _XBOX + else if( (m_afButtonPressed & IN_SPEED) && IsSprinting() ) + { + StopSprinting(); + } +#endif//_XBOX + } + else + { + // ROBIN: We're trialling Sprint not affecting movement speed without the HEV suit + /* + // The state of the WALK button has changed. + if( !IsWalking() && !(m_afButtonPressed & IN_SPEED) ) + { + StartWalking(); + } + else if( IsWalking() && !IsSprinting() && (m_afButtonPressed & IN_SPEED) && !(m_nButtons & IN_DUCK) ) + { + StopWalking(); + } + */ + } + } + else if( buttonsChanged & IN_WALK ) + { + if ( IsSuitEquipped() ) + { + // The state of the WALK button has changed. + if( IsWalking() && !(m_afButtonPressed & IN_WALK) ) + { + StopWalking(); + } + else if( !IsWalking() && !IsSprinting() && (m_afButtonPressed & IN_WALK) && !(m_nButtons & IN_DUCK) ) + { + StartWalking(); + } + } + } + + if ( IsSuitEquipped() && m_fIsWalking && !(m_nButtons & IN_WALK) ) + StopWalking(); +} + +//----------------------------------------------------------------------------- +// This happens when we powerdown from the mega physcannon to the regular one +//----------------------------------------------------------------------------- +void CHL2_Player::HandleArmorReduction( void ) +{ + if ( m_flArmorReductionTime < gpGlobals->curtime ) + return; + + if ( ArmorValue() <= 0 ) + return; + + float flPercent = 1.0f - (( m_flArmorReductionTime - gpGlobals->curtime ) / ARMOR_DECAY_TIME ); + + int iArmor = Lerp( flPercent, m_iArmorReductionFrom, 0 ); + + SetArmorValue( iArmor ); +} + +//----------------------------------------------------------------------------- +// Purpose: Allow pre-frame adjustments on the player +//----------------------------------------------------------------------------- +void CHL2_Player::PreThink(void) +{ + if ( player_showpredictedposition.GetBool() ) + { + Vector predPos; + + UTIL_PredictedPosition( this, player_showpredictedposition_timestep.GetFloat(), &predPos ); + + NDebugOverlay::Box( predPos, NAI_Hull::Mins( GetHullType() ), NAI_Hull::Maxs( GetHullType() ), 0, 255, 0, 0, 0.01f ); + NDebugOverlay::Line( GetAbsOrigin(), predPos, 0, 255, 0, 0, 0.01f ); + } + + // Riding a vehicle? + if ( IsInAVehicle() ) + { + VPROF( "CHL2_Player::PreThink-Vehicle" ); + // make sure we update the client, check for timed damage and update suit even if we are in a vehicle + UpdateClientData(); + CheckTimeBasedDamage(); + + // Allow the suit to recharge when in the vehicle. + SuitPower_Update(); + CheckSuitUpdate(); + CheckSuitZoom(); + + WaterMove(); + return; + } + + // This is an experiment of mine- autojumping! + // only affects you if sv_autojump is nonzero. + if( (GetFlags() & FL_ONGROUND) && sv_autojump.GetFloat() != 0 ) + { + VPROF( "CHL2_Player::PreThink-Autojump" ); + // check autojump + Vector vecCheckDir; + + vecCheckDir = GetAbsVelocity(); + + float flVelocity = VectorNormalize( vecCheckDir ); + + if( flVelocity > 200 ) + { + // Going fast enough to autojump + vecCheckDir = WorldSpaceCenter() + vecCheckDir * 34 - Vector( 0, 0, 16 ); + + trace_t tr; + + UTIL_TraceHull( WorldSpaceCenter() - Vector( 0, 0, 16 ), vecCheckDir, NAI_Hull::Mins(HULL_TINY_CENTERED),NAI_Hull::Maxs(HULL_TINY_CENTERED), MASK_PLAYERSOLID, this, COLLISION_GROUP_PLAYER, &tr ); + + //NDebugOverlay::Line( tr.startpos, tr.endpos, 0,255,0, true, 10 ); + + if( tr.fraction == 1.0 && !tr.startsolid ) + { + // Now trace down! + UTIL_TraceLine( vecCheckDir, vecCheckDir - Vector( 0, 0, 64 ), MASK_PLAYERSOLID, this, COLLISION_GROUP_NONE, &tr ); + + //NDebugOverlay::Line( tr.startpos, tr.endpos, 0,255,0, true, 10 ); + + if( tr.fraction == 1.0 && !tr.startsolid ) + { + // !!!HACKHACK + // I KNOW, I KNOW, this is definitely not the right way to do this, + // but I'm prototyping! (sjb) + Vector vecNewVelocity = GetAbsVelocity(); + vecNewVelocity.z += 250; + SetAbsVelocity( vecNewVelocity ); + } + } + } + } + + VPROF_SCOPE_BEGIN( "CHL2_Player::PreThink-Speed" ); + HandleSpeedChanges(); +#ifdef HL2_EPISODIC + HandleArmorReduction(); +#endif + +#ifdef _XBOX + if( m_bIsAutoSprinting ) + { + // Stop sprinting if the player lets off the stick for a moment. + if( GetStickDist() == 0.0f ) + { + if( gpGlobals->curtime > m_fAutoSprintMinTime ) + { + StopSprinting(); + } + } + else + { + // Stop sprinting one half second after the player stops inputting with the move stick. + m_fAutoSprintMinTime = gpGlobals->curtime + 0.5f; + } + } +#endif//_XBOX + VPROF_SCOPE_END(); + + if ( g_fGameOver || IsPlayerLockedInPlace() ) + return; // finale + + VPROF_SCOPE_BEGIN( "CHL2_Player::PreThink-ItemPreFrame" ); + ItemPreFrame( ); + VPROF_SCOPE_END(); + + VPROF_SCOPE_BEGIN( "CHL2_Player::PreThink-WaterMove" ); + WaterMove(); + VPROF_SCOPE_END(); + + if ( g_pGameRules && g_pGameRules->FAllowFlashlight() ) + m_Local.m_iHideHUD &= ~HIDEHUD_FLASHLIGHT; + else + m_Local.m_iHideHUD |= HIDEHUD_FLASHLIGHT; + + + VPROF_SCOPE_BEGIN( "CHL2_Player::PreThink-CommanderUpdate" ); + CommanderUpdate(); + VPROF_SCOPE_END(); + + // Operate suit accessories and manage power consumption/charge + VPROF_SCOPE_BEGIN( "CHL2_Player::PreThink-SuitPower_Update" ); + SuitPower_Update(); + VPROF_SCOPE_END(); + + // checks if new client data (for HUD and view control) needs to be sent to the client + VPROF_SCOPE_BEGIN( "CHL2_Player::PreThink-UpdateClientData" ); + UpdateClientData(); + VPROF_SCOPE_END(); + + VPROF_SCOPE_BEGIN( "CHL2_Player::PreThink-CheckTimeBasedDamage" ); + CheckTimeBasedDamage(); + VPROF_SCOPE_END(); + + VPROF_SCOPE_BEGIN( "CHL2_Player::PreThink-CheckSuitUpdate" ); + CheckSuitUpdate(); + VPROF_SCOPE_END(); + + VPROF_SCOPE_BEGIN( "CHL2_Player::PreThink-CheckSuitZoom" ); + CheckSuitZoom(); + VPROF_SCOPE_END(); + + if (m_lifeState >= LIFE_DYING) + { + PlayerDeathThink(); + return; + } + +#ifdef HL2_EPISODIC + CheckFlashlight(); +#endif + + // So the correct flags get sent to client asap. + // + if ( m_afPhysicsFlags & PFLAG_DIROVERRIDE ) + AddFlag( FL_ONTRAIN ); + else + RemoveFlag( FL_ONTRAIN ); + + // Train speed control + if ( m_afPhysicsFlags & PFLAG_DIROVERRIDE ) + { + CBaseEntity *pTrain = GetGroundEntity(); + float vel; + + if ( pTrain ) + { + if ( !(pTrain->ObjectCaps() & FCAP_DIRECTIONAL_USE) ) + pTrain = NULL; + } + + if ( !pTrain ) + { + if ( GetActiveWeapon() && (GetActiveWeapon()->ObjectCaps() & FCAP_DIRECTIONAL_USE) ) + { + m_iTrain = TRAIN_ACTIVE | TRAIN_NEW; + + if ( m_nButtons & IN_FORWARD ) + { + m_iTrain |= TRAIN_FAST; + } + else if ( m_nButtons & IN_BACK ) + { + m_iTrain |= TRAIN_BACK; + } + else + { + m_iTrain |= TRAIN_NEUTRAL; + } + return; + } + else + { + trace_t trainTrace; + // Maybe this is on the other side of a level transition + UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() + Vector(0,0,-38), + MASK_PLAYERSOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &trainTrace ); + + if ( trainTrace.fraction != 1.0 && trainTrace.m_pEnt ) + pTrain = trainTrace.m_pEnt; + + + if ( !pTrain || !(pTrain->ObjectCaps() & FCAP_DIRECTIONAL_USE) || !pTrain->OnControls(this) ) + { +// Warning( "In train mode with no train!\n" ); + m_afPhysicsFlags &= ~PFLAG_DIROVERRIDE; + m_iTrain = TRAIN_NEW|TRAIN_OFF; + return; + } + } + } + else if ( !( GetFlags() & FL_ONGROUND ) || pTrain->HasSpawnFlags( SF_TRACKTRAIN_NOCONTROL ) || (m_nButtons & (IN_MOVELEFT|IN_MOVERIGHT) ) ) + { + // Turn off the train if you jump, strafe, or the train controls go dead + m_afPhysicsFlags &= ~PFLAG_DIROVERRIDE; + m_iTrain = TRAIN_NEW|TRAIN_OFF; + return; + } + + SetAbsVelocity( vec3_origin ); + vel = 0; + if ( m_afButtonPressed & IN_FORWARD ) + { + vel = 1; + pTrain->Use( this, this, USE_SET, (float)vel ); + } + else if ( m_afButtonPressed & IN_BACK ) + { + vel = -1; + pTrain->Use( this, this, USE_SET, (float)vel ); + } + + if (vel) + { + m_iTrain = TrainSpeed(static_cast(pTrain->m_flSpeed), static_cast(((CFuncTrackTrain*)pTrain)->GetMaxSpeed())); + m_iTrain |= TRAIN_ACTIVE|TRAIN_NEW; + } + } + else if (m_iTrain & TRAIN_ACTIVE) + { + m_iTrain = TRAIN_NEW; // turn off train + } + + + // + // If we're not on the ground, we're falling. Update our falling velocity. + // + if ( !( GetFlags() & FL_ONGROUND ) ) + { + m_Local.m_flFallVelocity = -GetAbsVelocity().z; + } + + if ( m_afPhysicsFlags & PFLAG_ONBARNACLE ) + { + bool bOnBarnacle = false; + CNPC_Barnacle *pBarnacle = NULL; + do + { + // FIXME: Not a good or fast solution, but maybe it will catch the bug! + pBarnacle = (CNPC_Barnacle*)gEntList.FindEntityByClassname( pBarnacle, "npc_barnacle" ); + if ( pBarnacle ) + { + if ( pBarnacle->GetEnemy() == this ) + { + bOnBarnacle = true; + } + } + } while ( pBarnacle ); + + if ( !bOnBarnacle ) + { + Warning( "Attached to barnacle?\n" ); + Assert( 0 ); + m_afPhysicsFlags &= ~PFLAG_ONBARNACLE; + } + else + { + SetAbsVelocity( vec3_origin ); + } + } + // StudioFrameAdvance( );//!!!HACKHACK!!! Can't be hit by traceline when not animating? + + // Update weapon's ready status + UpdateWeaponPosture(); + + // Disallow shooting while zooming +#ifdef _XBOX + if ( IsZooming() ) + { + if( !GetActiveWeapon() || !GetActiveWeapon()->IsWeaponZoomed() ) + { + // If not zoomed because of the weapon itself, do not attack. + m_nButtons &= ~(IN_ATTACK|IN_ATTACK2); + } + } +#else + if ( m_nButtons & IN_ZOOM ) + { + //FIXME: Held weapons like the grenade get sad when this happens +#ifdef HL2_EPISODIC + // Episodic allows players to zoom while using a func_tank + if ( !m_hUseEntity || GetActiveWeapon()->IsWeaponVisible() ) +#endif + m_nButtons &= ~(IN_ATTACK|IN_ATTACK2); + } +#endif//_XBOX +} + +void CHL2_Player::PostThink( void ) +{ + BaseClass::PostThink(); + + if ( !g_fGameOver && !IsPlayerLockedInPlace() && IsAlive() ) + { + HandleAdmireGlovesAnimation(); + } +} + +void CHL2_Player::StartAdmireGlovesAnimation( void ) +{ + CBaseViewModel *vm = GetViewModel( 0 ); + + if ( vm && !GetActiveWeapon() ) + { + vm->SetWeaponModel( "models/weapons/v_hands.mdl", NULL ); + ShowViewModel( true ); + + int idealSequence = vm->SelectWeightedSequence( ACT_VM_IDLE ); + + if ( idealSequence >= 0 ) + { + vm->SendViewModelMatchingSequence( idealSequence ); + m_flAdmireGlovesAnimTime = gpGlobals->curtime + vm->SequenceDuration( idealSequence ); + } + } +} + +void CHL2_Player::HandleAdmireGlovesAnimation( void ) +{ + CBaseViewModel *pVM = GetViewModel(); + + if ( pVM && pVM->GetOwningWeapon() == NULL ) + { + if ( m_flAdmireGlovesAnimTime != 0.0 ) + { + if ( m_flAdmireGlovesAnimTime > gpGlobals->curtime ) + { + pVM->m_flPlaybackRate = 1.0f; + pVM->StudioFrameAdvance( ); + } + else if ( m_flAdmireGlovesAnimTime < gpGlobals->curtime ) + { + m_flAdmireGlovesAnimTime = 0.0f; + pVM->SetWeaponModel( NULL, NULL ); + } + } + } + else + m_flAdmireGlovesAnimTime = 0.0f; +} + +#define HL2PLAYER_RELOADGAME_ATTACK_DELAY 1.0f + +void CHL2_Player::Activate( void ) +{ + BaseClass::Activate(); + InitSprinting(); + +#ifdef HL2_EPISODIC + + // Delay attacks by 1 second after loading a game. + if ( GetActiveWeapon() ) + { + float flRemaining = GetActiveWeapon()->m_flNextPrimaryAttack - gpGlobals->curtime; + + if ( flRemaining < HL2PLAYER_RELOADGAME_ATTACK_DELAY ) + { + GetActiveWeapon()->m_flNextPrimaryAttack = gpGlobals->curtime + HL2PLAYER_RELOADGAME_ATTACK_DELAY; + } + + flRemaining = GetActiveWeapon()->m_flNextSecondaryAttack - gpGlobals->curtime; + + if ( flRemaining < HL2PLAYER_RELOADGAME_ATTACK_DELAY ) + { + GetActiveWeapon()->m_flNextSecondaryAttack = gpGlobals->curtime + HL2PLAYER_RELOADGAME_ATTACK_DELAY; + } + } + +#endif +} + +//------------------------------------------------------------------------------ +// Purpose : +// Input : +// Output : +//------------------------------------------------------------------------------ +Class_T CHL2_Player::Classify ( void ) +{ + // If player controlling another entity? If so, return this class + if (m_nControlClass != CLASS_NONE) + { + return m_nControlClass; + } + else + { + if(IsInAVehicle()) + { + IServerVehicle *pVehicle = GetVehicle(); + return pVehicle->ClassifyPassenger( this, CLASS_PLAYER ); + } + else + { + return CLASS_PLAYER; + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: This is a generic function (to be implemented by sub-classes) to +// handle specific interactions between different types of characters +// (For example the barnacle grabbing an NPC) +// Input : Constant for the type of interaction +// Output : true - if sub-class has a response for the interaction +// false - if sub-class has no response +//----------------------------------------------------------------------------- +bool CHL2_Player::HandleInteraction(int interactionType, void *data, CBaseCombatCharacter* sourceEnt) +{ + if ( interactionType == g_interactionBarnacleVictimDangle ) + return false; + + if (interactionType == g_interactionBarnacleVictimReleased) + { + m_afPhysicsFlags &= ~PFLAG_ONBARNACLE; + SetMoveType( MOVETYPE_WALK ); + return true; + } + else if (interactionType == g_interactionBarnacleVictimGrab) + { +#ifdef HL2_EPISODIC + CNPC_Alyx *pAlyx = CNPC_Alyx::GetAlyx(); + if ( pAlyx ) + { + // Make Alyx totally hate this barnacle so that she saves the player. + int priority; + + priority = pAlyx->IRelationPriority(sourceEnt); + pAlyx->AddEntityRelationship( sourceEnt, D_HT, priority + 5 ); + } +#endif//HL2_EPISODIC + + m_afPhysicsFlags |= PFLAG_ONBARNACLE; + ClearUseEntity(); + return true; + } + return false; +} + + +void CHL2_Player::PlayerRunCommand(CUserCmd *ucmd, IMoveHelper *moveHelper) +{ + // Handle FL_FROZEN. + if ( m_afPhysicsFlags & PFLAG_ONBARNACLE ) + { + ucmd->forwardmove = 0; + ucmd->sidemove = 0; + ucmd->upmove = 0; + ucmd->buttons &= ~IN_USE; + } + + // Can't use stuff while dead + if ( IsDead() ) + { + ucmd->buttons &= ~IN_USE; + } + + //Update our movement information + if ( ( ucmd->forwardmove != 0 ) || ( ucmd->sidemove != 0 ) || ( ucmd->upmove != 0 ) ) + { + m_flIdleTime -= TICK_INTERVAL * 2.0f; + + if ( m_flIdleTime < 0.0f ) + { + m_flIdleTime = 0.0f; + } + + m_flMoveTime += TICK_INTERVAL; + + if ( m_flMoveTime > 4.0f ) + { + m_flMoveTime = 4.0f; + } + } + else + { + m_flIdleTime += TICK_INTERVAL; + + if ( m_flIdleTime > 4.0f ) + { + m_flIdleTime = 4.0f; + } + + m_flMoveTime -= TICK_INTERVAL * 2.0f; + + if ( m_flMoveTime < 0.0f ) + { + m_flMoveTime = 0.0f; + } + } + + //Msg("Player time: [ACTIVE: %f]\t[IDLE: %f]\n", m_flMoveTime, m_flIdleTime ); + + BaseClass::PlayerRunCommand( ucmd, moveHelper ); +} + +//----------------------------------------------------------------------------- +// Purpose: Sets HL2 specific defaults. +//----------------------------------------------------------------------------- +void CHL2_Player::Spawn(void) +{ + +#ifndef HL2MP + SetModel( "models/player.mdl" ); +#endif + + BaseClass::Spawn(); + + // + // Our player movement speed is set once here. This will override the cl_xxxx + // cvars unless they are set to be lower than this. + // + //m_flMaxspeed = 320; + + if ( !IsSuitEquipped() ) + StartWalking(); + + SuitPower_SetCharge( 100 ); + + m_Local.m_iHideHUD |= HIDEHUD_CHAT; + + m_pPlayerAISquad = g_AI_SquadManager.FindCreateSquad(AllocPooledString(PLAYER_SQUADNAME)); + + InitSprinting(); + + GetPlayerProxy(); + + SetFlashlightPowerDrainScale( 1.0f ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CHL2_Player::InitSprinting( void ) +{ + StopSprinting(); +} + + +//----------------------------------------------------------------------------- +// Purpose: Returns whether or not we are allowed to sprint now. +//----------------------------------------------------------------------------- +bool CHL2_Player::CanSprint() +{ + return ( m_bSprintEnabled && // Only if sprint is enabled + !IsWalking() && // Not if we're walking + !( m_Local.m_bDucked && !m_Local.m_bDucking ) && // Nor if we're ducking + (GetWaterLevel() != 3) && // Certainly not underwater + (GlobalEntity_GetState("suit_no_sprint") != GLOBAL_ON) ); // Out of the question without the sprint module +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +#ifdef _XBOX +void CHL2_Player::StartAutoSprint() +{ + if( IsSprinting() ) + { + StopSprinting(); + } + else + { + StartSprinting(); + m_bIsAutoSprinting = true; + m_fAutoSprintMinTime = gpGlobals->curtime + 1.5f; + } +} +#endif// _XBOX + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CHL2_Player::StartSprinting( void ) +{ + if( m_HL2Local.m_flSuitPower < 10 ) + { + // Don't sprint unless there's a reasonable + // amount of suit power. + CPASAttenuationFilter filter( this ); + filter.UsePredictionRules(); + EmitSound( filter, entindex(), "HL2Player.SprintNoPower" ); + return; + } + + if( !SuitPower_AddDevice( SuitDeviceSprint ) ) + return; + + CPASAttenuationFilter filter( this ); + filter.UsePredictionRules(); + EmitSound( filter, entindex(), "HL2Player.SprintStart" ); + + SetMaxSpeed( HL2_SPRINT_SPEED ); + m_fIsSprinting = true; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CHL2_Player::StopSprinting( void ) +{ + if ( m_HL2Local.m_bitsActiveDevices & SuitDeviceSprint.GetDeviceID() ) + { + SuitPower_RemoveDevice( SuitDeviceSprint ); + } + SetMaxSpeed( HL2_NORM_SPEED ); + m_fIsSprinting = false; + +#ifdef _XBOX + m_bIsAutoSprinting = false; + m_fAutoSprintMinTime = 0.0f; +#endif//_XBOX +} + + +//----------------------------------------------------------------------------- +// Purpose: Called to disable and enable sprint due to temporary circumstances: +// - Carrying a heavy object with the physcannon +//----------------------------------------------------------------------------- +void CHL2_Player::EnableSprint( bool bEnable ) +{ + if ( !bEnable && IsSprinting() ) + { + StopSprinting(); + } + + m_bSprintEnabled = bEnable; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CHL2_Player::StartWalking( void ) +{ + SetMaxSpeed( HL2_WALK_SPEED ); + m_fIsWalking = true; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CHL2_Player::StopWalking( void ) +{ + SetMaxSpeed( HL2_NORM_SPEED ); + m_fIsWalking = false; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CHL2_Player::CanZoom( CBaseEntity *pRequester ) +{ + if ( IsZooming() ) + return false; + + //Check our weapon + + return true; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CHL2_Player::ToggleZoom(void) +{ + if( IsZooming() ) + { + StopZooming(); + } + else + { + StartZooming(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +zoom suit zoom +//----------------------------------------------------------------------------- +void CHL2_Player::StartZooming( void ) +{ + int iFOV = 25; + if ( SetFOV( this, iFOV, 0.4f ) ) + { + m_HL2Local.m_bZooming = true; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CHL2_Player::StopZooming( void ) +{ + int iFOV = static_cast(GetZoomOwnerDesiredFOV( m_hZoomOwner )); + + if ( SetFOV( this, iFOV, 0.2f ) ) + { + m_HL2Local.m_bZooming = false; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CHL2_Player::IsZooming( void ) +{ + if ( m_hZoomOwner != NULL ) + return true; + + return false; +} + +class CPhysicsPlayerCallback : public IPhysicsPlayerControllerEvent +{ +public: + int ShouldMoveTo( IPhysicsObject *pObject, const Vector &position ) + { + CHL2_Player *pPlayer = (CHL2_Player *)pObject->GetGameData(); + if ( pPlayer ) + { + if ( pPlayer->TouchedPhysics() ) + { + return 0; + } + } + return 1; + } +}; + +static CPhysicsPlayerCallback playerCallback; + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CHL2_Player::InitVCollision( void ) +{ + BaseClass::InitVCollision(); + + // Setup the HL2 specific callback. + IPhysicsPlayerController *pPlayerController = GetPhysicsController(); + if ( pPlayerController ) + { + pPlayerController->SetEventHandler( &playerCallback ); + } +} + + +CHL2_Player::~CHL2_Player( void ) +{ +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +bool CHL2_Player::CommanderFindGoal( commandgoal_t *pGoal ) +{ + CAI_BaseNPC *pAllyNpc; + trace_t tr; + Vector vecTarget; + Vector forward; + + EyeVectors( &forward ); + + //--------------------------------- + // MASK_SHOT on purpose! So that you don't hit the invisible hulls of the NPCs. + CTraceFilterSkipTwoEntities filter( this, PhysCannonGetHeldEntity( GetActiveWeapon() ), COLLISION_GROUP_INTERACTIVE_DEBRIS ); + + UTIL_TraceLine( EyePosition(), EyePosition() + forward * MAX_COORD_RANGE, MASK_SHOT, &filter, &tr ); + + if( !tr.DidHitWorld() ) + { + CUtlVector Allies; + AISquadIter_t iter; + for ( pAllyNpc = m_pPlayerAISquad->GetFirstMember(&iter); pAllyNpc; pAllyNpc = m_pPlayerAISquad->GetNextMember(&iter) ) + { + if ( pAllyNpc->IsCommandable() ) + Allies.AddToTail( pAllyNpc ); + } + + for( int i = 0 ; i < Allies.Count() ; i++ ) + { + if( Allies[ i ]->IsValidCommandTarget( tr.m_pEnt ) ) + { + pGoal->m_pGoalEntity = tr.m_pEnt; + return true; + } + } + } + + if( tr.fraction == 1.0 || (tr.surface.flags & SURF_SKY) ) + { + // Move commands invalid against skybox. + pGoal->m_vecGoalLocation = tr.endpos; + return false; + } + + if ( tr.m_pEnt->IsNPC() && ((CAI_BaseNPC *)(tr.m_pEnt))->IsCommandable() ) + { + pGoal->m_vecGoalLocation = tr.m_pEnt->GetAbsOrigin(); + } + else + { + vecTarget = tr.endpos; + + Vector mins( -16, -16, 0 ); + Vector maxs( 16, 16, 0 ); + + // Back up from whatever we hit so that there's enough space at the + // target location for a bounding box. + // Now trace down. + //UTIL_TraceLine( vecTarget, vecTarget - Vector( 0, 0, 8192 ), MASK_SOLID, this, COLLISION_GROUP_NONE, &tr ); + UTIL_TraceHull( vecTarget + tr.plane.normal * 24, + vecTarget - Vector( 0, 0, 8192 ), + mins, + maxs, + MASK_SOLID_BRUSHONLY, + this, + COLLISION_GROUP_NONE, + &tr ); + + + if ( !tr.startsolid ) + pGoal->m_vecGoalLocation = tr.endpos; + else + pGoal->m_vecGoalLocation = vecTarget; + } + + pAllyNpc = GetSquadCommandRepresentative(); + if ( !pAllyNpc ) + return false; + + vecTarget = pGoal->m_vecGoalLocation; + if ( !pAllyNpc->FindNearestValidGoalPos( vecTarget, &pGoal->m_vecGoalLocation ) ) + return false; + + return ( ( vecTarget - pGoal->m_vecGoalLocation ).LengthSqr() < Square( 15*12 ) ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +CAI_BaseNPC *CHL2_Player::GetSquadCommandRepresentative() +{ + if ( m_pPlayerAISquad != NULL ) + { + CAI_BaseNPC *pAllyNpc = m_pPlayerAISquad->GetFirstMember(); + + if ( pAllyNpc ) + { + return pAllyNpc->GetSquadCommandRepresentative(); + } + } + + return NULL; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +int CHL2_Player::GetNumSquadCommandables() +{ + AISquadIter_t iter; + int c = 0; + for ( CAI_BaseNPC *pAllyNpc = m_pPlayerAISquad->GetFirstMember(&iter); pAllyNpc; pAllyNpc = m_pPlayerAISquad->GetNextMember(&iter) ) + { + if ( pAllyNpc->IsCommandable() ) + c++; + } + return c; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +int CHL2_Player::GetNumSquadCommandableMedics() +{ + AISquadIter_t iter; + int c = 0; + for ( CAI_BaseNPC *pAllyNpc = m_pPlayerAISquad->GetFirstMember(&iter); pAllyNpc; pAllyNpc = m_pPlayerAISquad->GetNextMember(&iter) ) + { + if ( pAllyNpc->IsCommandable() && pAllyNpc->IsMedic() ) + c++; + } + return c; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CHL2_Player::CommanderUpdate() +{ + CAI_BaseNPC *pCommandRepresentative = GetSquadCommandRepresentative(); + bool bFollowMode = false; + if ( pCommandRepresentative ) + { + bFollowMode = ( pCommandRepresentative->GetCommandGoal() == vec3_invalid ); + + // set the variables for network transmission (to show on the hud) + m_HL2Local.m_iSquadMemberCount = GetNumSquadCommandables(); + m_HL2Local.m_iSquadMedicCount = GetNumSquadCommandableMedics(); + m_HL2Local.m_fSquadInFollowMode = bFollowMode; + + // debugging code for displaying extra squad indicators + /* + char *pszMoving = ""; + AISquadIter_t iter; + for ( CAI_BaseNPC *pAllyNpc = m_pPlayerAISquad->GetFirstMember(&iter); pAllyNpc; pAllyNpc = m_pPlayerAISquad->GetNextMember(&iter) ) + { + if ( pAllyNpc->IsCommandMoving() ) + { + pszMoving = "<-"; + break; + } + } + + NDebugOverlay::ScreenText( + 0.932, 0.919, + CFmtStr( "%d|%c%s", GetNumSquadCommandables(), ( bFollowMode ) ? 'F' : 'S', pszMoving ), + 255, 128, 0, 128, + 0 ); + */ + + } + else + { + m_HL2Local.m_iSquadMemberCount = 0; + m_HL2Local.m_iSquadMedicCount = 0; + m_HL2Local.m_fSquadInFollowMode = true; + } + + if ( m_QueuedCommand != CC_NONE && ( m_QueuedCommand == CC_FOLLOW || gpGlobals->realtime - m_RealTimeLastSquadCommand >= player_squad_double_tap_time.GetFloat() ) ) + { + CommanderExecute( m_QueuedCommand ); + m_QueuedCommand = CC_NONE; + } + else if ( !bFollowMode && pCommandRepresentative && m_CommanderUpdateTimer.Expired() && player_squad_transient_commands.GetBool() ) + { + m_CommanderUpdateTimer.Set(2.5); + + if ( pCommandRepresentative->ShouldAutoSummon() ) + CommanderExecute( CC_FOLLOW ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// +// bHandled - indicates whether to continue delivering this order to +// all allies. Allows us to stop delivering certain types of orders once we find +// a suitable candidate. (like picking up a single weapon. We don't wish for +// all allies to respond and try to pick up one weapon). +//----------------------------------------------------------------------------- +bool CHL2_Player::CommanderExecuteOne( CAI_BaseNPC *pNpc, const commandgoal_t &goal, CAI_BaseNPC **Allies, int numAllies ) +{ + if ( goal.m_pGoalEntity ) + { + return pNpc->TargetOrder( goal.m_pGoalEntity, Allies, numAllies ); + } + else if ( pNpc->IsInPlayerSquad() ) + { + pNpc->MoveOrder( goal.m_vecGoalLocation, Allies, numAllies ); + } + + return true; +} + +//--------------------------------------------------------- +//--------------------------------------------------------- +void CHL2_Player::CommanderExecute( CommanderCommand_t command ) +{ + CAI_BaseNPC *pPlayerSquadLeader = GetSquadCommandRepresentative(); + + if ( !pPlayerSquadLeader ) + { + EmitSound( "HL2Player.UseDeny" ); + return; + } + + int i; + CUtlVector Allies; + commandgoal_t goal; + + if ( command == CC_TOGGLE ) + { + if ( pPlayerSquadLeader->GetCommandGoal() != vec3_invalid ) + command = CC_FOLLOW; + else + command = CC_SEND; + } + else + { + if ( command == CC_FOLLOW && pPlayerSquadLeader->GetCommandGoal() == vec3_invalid ) + return; + } + + if ( command == CC_FOLLOW ) + { + goal.m_pGoalEntity = this; + goal.m_vecGoalLocation = vec3_invalid; + } + else + { + goal.m_pGoalEntity = NULL; + goal.m_vecGoalLocation = vec3_invalid; + + // Find a goal for ourselves. + if( !CommanderFindGoal( &goal ) ) + { + EmitSound( "HL2Player.UseDeny" ); + return; // just keep following + } + } + +#ifdef _DEBUG + if( goal.m_pGoalEntity == NULL && goal.m_vecGoalLocation == vec3_invalid ) + { + DevMsg( 1, "**ERROR: Someone sent an invalid goal to CommanderExecute!\n" ); + } +#endif // _DEBUG + + AISquadIter_t iter; + for ( CAI_BaseNPC *pAllyNpc = m_pPlayerAISquad->GetFirstMember(&iter); pAllyNpc; pAllyNpc = m_pPlayerAISquad->GetNextMember(&iter) ) + { + if ( pAllyNpc->IsCommandable() ) + Allies.AddToTail( pAllyNpc ); + } + + //--------------------------------- + // If the trace hits an NPC, send all ally NPCs a "target" order. Always + // goes to targeted one first +#ifdef DEBUG + int nAIs = g_AI_Manager.NumAIs(); +#endif + CAI_BaseNPC * pTargetNpc = (goal.m_pGoalEntity) ? goal.m_pGoalEntity->MyNPCPointer() : NULL; + + bool bHandled = false; + if( pTargetNpc ) + { + bHandled = !CommanderExecuteOne( pTargetNpc, goal, Allies.Base(), Allies.Count() ); + } + + for ( i = 0; !bHandled && i < Allies.Count(); i++ ) + { + if ( Allies[i] != pTargetNpc && Allies[i]->IsPlayerAlly() ) + { + bHandled = !CommanderExecuteOne( Allies[i], goal, Allies.Base(), Allies.Count() ); + } + Assert( nAIs == g_AI_Manager.NumAIs() ); // not coded to support mutating set of NPCs + } +} + +//----------------------------------------------------------------------------- +// Enter/exit commander mode, manage ally selection. +//----------------------------------------------------------------------------- +void CHL2_Player::CommanderMode() +{ + float commandInterval = gpGlobals->realtime - m_RealTimeLastSquadCommand; + m_RealTimeLastSquadCommand = gpGlobals->realtime; + if ( commandInterval < player_squad_double_tap_time.GetFloat() ) + { + m_QueuedCommand = CC_FOLLOW; + } + else + { + m_QueuedCommand = (player_squad_transient_commands.GetBool()) ? CC_SEND : CC_TOGGLE; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : iImpulse - +//----------------------------------------------------------------------------- +void CHL2_Player::CheatImpulseCommands( int iImpulse ) +{ + switch( iImpulse ) + { + case 50: + { + CommanderMode(); + break; + } + + case 51: + { + // Cheat to create a dynamic resupply item + Vector vecForward; + AngleVectors( EyeAngles(), &vecForward ); + CBaseEntity *pItem = (CBaseEntity *)CreateEntityByName( "item_dynamic_resupply" ); + if ( pItem ) + { + Vector vecOrigin = GetAbsOrigin() + vecForward * 256 + Vector(0,0,64); + QAngle vecAngles( 0, GetAbsAngles().y - 90, 0 ); + pItem->SetAbsOrigin( vecOrigin ); + pItem->SetAbsAngles( vecAngles ); + pItem->KeyValue( "targetname", "resupply" ); + pItem->Spawn(); + pItem->Activate(); + } + break; + } + + case 52: + { + // Rangefinder + trace_t tr; + UTIL_TraceLine( EyePosition(), EyePosition() + EyeDirection3D() * MAX_COORD_RANGE, MASK_SHOT, this, COLLISION_GROUP_NONE, &tr ); + + if( tr.fraction != 1.0 ) + { + float flDist = (tr.startpos - tr.endpos).Length(); + float flDist2D = (tr.startpos - tr.endpos).Length2D(); + DevMsg( 1,"\nStartPos: %.4f %.4f %.4f --- EndPos: %.4f %.4f %.4f\n", tr.startpos.x,tr.startpos.y,tr.startpos.z,tr.endpos.x,tr.endpos.y,tr.endpos.z ); + DevMsg( 1,"3D Distance: %.4f units (%.2f feet) --- 2D Distance: %.4f units (%.2f feet)\n", flDist, flDist / 12.0, flDist2D, flDist2D / 12.0 ); + } + + break; + } + + default: + BaseClass::CheatImpulseCommands( iImpulse ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CHL2_Player::SetupVisibility( CBaseEntity *pViewEntity, unsigned char *pvs, int pvssize ) +{ + BaseClass::SetupVisibility( pViewEntity, pvs, pvssize ); + + int area = pViewEntity ? pViewEntity->NetworkProp()->AreaNum() : NetworkProp()->AreaNum(); + PointCameraSetupVisibility( this, area, pvs, pvssize ); + + // If the intro script is playing, we want to get it's visibility points + if ( g_hIntroScript ) + { + Vector vecOrigin; + CBaseEntity *pCamera; + if ( g_hIntroScript->GetIncludedPVSOrigin( &vecOrigin, &pCamera ) ) + { + // If it's a point camera, turn it on + CPointCamera *pPointCamera = dynamic_cast< CPointCamera* >(pCamera); + if ( pPointCamera ) + { + pPointCamera->SetActive( true ); + } + engine->AddOriginToPVS( vecOrigin ); + } + } +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CHL2_Player::SuitPower_Update( void ) +{ + if( SuitPower_ShouldRecharge() ) + { + SuitPower_Charge( SUITPOWER_CHARGE_RATE * gpGlobals->frametime ); + } + else if( m_HL2Local.m_bitsActiveDevices ) + { + float flPowerLoad = m_flSuitPowerLoad; + +#ifndef _XBOX + //Since XBox quickly shuts off sprint if it isn't being used, this isn't an issue. + if( SuitPower_IsDeviceActive(SuitDeviceSprint) ) + { + if( !fabs(GetAbsVelocity().x) && !fabs(GetAbsVelocity().y) ) + { + // If player's not moving, don't drain sprint juice. + flPowerLoad -= SuitDeviceSprint.GetDeviceDrainRate(); + } + } +#endif//_XBOX + + if( SuitPower_IsDeviceActive(SuitDeviceFlashlight) ) + { + float factor; + + factor = 1.0f / m_flFlashlightPowerDrainScale; + + flPowerLoad -= ( SuitDeviceFlashlight.GetDeviceDrainRate() * (1.0f - factor) ); + } + + if( !SuitPower_Drain( flPowerLoad * gpGlobals->frametime ) ) + { + // TURN OFF ALL DEVICES!! + if( IsSprinting() ) + { + StopSprinting(); + } + + if( FlashlightIsOn() ) + { +#ifndef HL2MP + FlashlightTurnOff(); +#endif + } + } + + // turn off flashlight a little bit after it hits below one aux power notch (5%) + if( m_HL2Local.m_flSuitPower < 4.8f && FlashlightIsOn() ) + { +#ifndef HL2MP + FlashlightTurnOff(); +#endif + } + } +} + + +//----------------------------------------------------------------------------- +// Charge battery fully, turn off all devices. +//----------------------------------------------------------------------------- +void CHL2_Player::SuitPower_Initialize( void ) +{ + m_HL2Local.m_bitsActiveDevices = 0x00000000; + m_HL2Local.m_flSuitPower = 100.0; + m_flSuitPowerLoad = 0.0; +} + + +//----------------------------------------------------------------------------- +// Purpose: Interface to drain power from the suit's power supply. +// Input: Amount of charge to remove (expressed as percentage of full charge) +// Output: Returns TRUE if successful, FALSE if not enough power available. +//----------------------------------------------------------------------------- +bool CHL2_Player::SuitPower_Drain( float flPower ) +{ + // Suitpower cheat on? + if ( sv_infinite_aux_power.GetBool() ) + return true; + + m_HL2Local.m_flSuitPower -= flPower; + + if( m_HL2Local.m_flSuitPower < 0.0 ) + { + // Power is depleted! + // Clamp and fail + m_HL2Local.m_flSuitPower = 0.0; + return false; + } + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: Interface to add power to the suit's power supply +// Input: Amount of charge to add +//----------------------------------------------------------------------------- +void CHL2_Player::SuitPower_Charge( float flPower ) +{ + m_HL2Local.m_flSuitPower += flPower; + + if( m_HL2Local.m_flSuitPower > 100.0 ) + { + // Full charge, clamp. + m_HL2Local.m_flSuitPower = 100.0; + } +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +bool CHL2_Player::SuitPower_IsDeviceActive( const CSuitPowerDevice &device ) +{ + return (m_HL2Local.m_bitsActiveDevices & device.GetDeviceID()) != 0; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +bool CHL2_Player::SuitPower_AddDevice( const CSuitPowerDevice &device ) +{ + // Make sure this device is NOT active!! + if( m_HL2Local.m_bitsActiveDevices & device.GetDeviceID() ) + return false; + + if( !IsSuitEquipped() ) + return false; + + m_HL2Local.m_bitsActiveDevices |= device.GetDeviceID(); + m_flSuitPowerLoad += device.GetDeviceDrainRate(); + return true; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +bool CHL2_Player::SuitPower_RemoveDevice( const CSuitPowerDevice &device ) +{ + // Make sure this device is active!! + if( ! (m_HL2Local.m_bitsActiveDevices & device.GetDeviceID()) ) + return false; + + if( !IsSuitEquipped() ) + return false; + + // Take a little bit of suit power when you disable a device. If the device is shutting off + // because the battery is drained, no harm done, the battery charge cannot go below 0. + // This code in combination with the delay before the suit can start recharging are a defense + // against exploits where the player could rapidly tap sprint and never run out of power. + SuitPower_Drain( device.GetDeviceDrainRate() * 0.1f ); + + m_HL2Local.m_bitsActiveDevices &= ~device.GetDeviceID(); + m_flSuitPowerLoad -= device.GetDeviceDrainRate(); + + if( m_HL2Local.m_bitsActiveDevices == 0x00000000 ) + { + // With this device turned off, we can set this timer which tells us when the + // suit power system entered a no-load state. + m_flTimeAllSuitDevicesOff = gpGlobals->curtime; + } + + return true; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +#define SUITPOWER_BEGIN_RECHARGE_DELAY 0.5f +bool CHL2_Player::SuitPower_ShouldRecharge( void ) +{ + // Make sure all devices are off. + if( m_HL2Local.m_bitsActiveDevices != 0x00000000 ) + return false; + + // Is the system fully charged? + if( m_HL2Local.m_flSuitPower >= 100.0f ) + return false; + + // Has the system been in a no-load state for long enough + // to begin recharging? + if( gpGlobals->curtime < m_flTimeAllSuitDevicesOff + SUITPOWER_BEGIN_RECHARGE_DELAY ) + return false; + + return true; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +ConVar sk_battery( "sk_battery","0" ); + +bool CHL2_Player::ApplyBattery( float powerMultiplier ) +{ + const float MAX_NORMAL_BATTERY = 100; + if ((ArmorValue() < MAX_NORMAL_BATTERY) && IsSuitEquipped()) + { + int pct; + char szcharge[64]; + + IncrementArmorValue( static_cast(sk_battery.GetFloat() * powerMultiplier), static_cast(MAX_NORMAL_BATTERY) ); + + CPASAttenuationFilter filter( this, "ItemBattery.Touch" ); + EmitSound( filter, entindex(), "ItemBattery.Touch" ); + + CSingleUserRecipientFilter user( this ); + user.MakeReliable(); + + UserMessageBegin( user, "ItemPickup" ); + WRITE_STRING( "item_battery" ); + MessageEnd(); + + + // Suit reports new power level + // For some reason this wasn't working in release build -- round it. + pct = (int)( (float)(ArmorValue() * 100.0) * (1.0/MAX_NORMAL_BATTERY) + 0.5); + pct = (pct / 5); + if (pct > 0) + pct--; + + Q_snprintf( szcharge,sizeof(szcharge),"!HEV_%1dP", pct ); + + //UTIL_EmitSoundSuit(edict(), szcharge); + //SetSuitUpdate(szcharge, FALSE, SUIT_NEXT_IN_30SEC); + return true; + } + return false; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +int CHL2_Player::FlashlightIsOn( void ) +{ + return IsEffectActive( EF_DIMLIGHT ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CHL2_Player::FlashlightTurnOn( void ) +{ + if( m_bFlashlightDisabled ) + return; + + if( !SuitPower_AddDevice( SuitDeviceFlashlight ) ) + return; + + AddEffects( EF_DIMLIGHT ); + EmitSound( "HL2Player.FlashLightOn" ); + + variant_t flashlighton; + flashlighton.SetFloat( m_HL2Local.m_flSuitPower / 100.0f ); + + FirePlayerProxyOutput( "OnFlashlightOn", flashlighton, this, this ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CHL2_Player::FlashlightTurnOff( void ) +{ + if( !SuitPower_RemoveDevice( SuitDeviceFlashlight ) ) + return; + + RemoveEffects( EF_DIMLIGHT ); + EmitSound( "HL2Player.FlashLightOff" ); + + variant_t flashlightoff; + flashlightoff.SetFloat( m_HL2Local.m_flSuitPower / 100.0f ); + FirePlayerProxyOutput( "OnFlashlightOff", flashlightoff, this, this ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +#define FLASHLIGHT_RANGE Square(600) +bool CHL2_Player::IsIlluminatedByFlashlight( CBaseEntity *pEntity, float *flReturnDot ) +{ + if( !FlashlightIsOn() ) + return false; + + if( pEntity->Classify() == CLASS_BARNACLE && pEntity->GetEnemy() == this ) + { + // As long as my flashlight is on, the barnacle that's pulling me in is considered illuminated. + // This is because players often shine their flashlights at Alyx when they are in a barnacle's + // grasp, and wonder why Alyx isn't helping. Alyx isn't helping because the light isn't pointed + // at the barnacle. This will allow Alyx to see the barnacle no matter which way the light is pointed. + return true; + } + + // Within 50 feet? + float flDistSqr = GetAbsOrigin().DistToSqr(pEntity->GetAbsOrigin()); + if( flDistSqr > FLASHLIGHT_RANGE ) + return false; + + // Within 45 degrees? + Vector vecSpot = pEntity->WorldSpaceCenter(); + Vector los; + + // If the eyeposition is too close, move it back. Solves problems + // caused by the player being too close the target. + if ( flDistSqr < (128 * 128) ) + { + Vector vecForward; + EyeVectors( &vecForward ); + Vector vecMovedEyePos = EyePosition() - (vecForward * 128); + los = ( vecSpot - vecMovedEyePos ); + } + else + { + los = ( vecSpot - EyePosition() ); + } + + VectorNormalize( los ); + Vector facingDir = EyeDirection3D( ); + float flDot = DotProduct( los, facingDir ); + + if ( flReturnDot ) + { + *flReturnDot = flDot; + } + + if ( flDot < 0.92387f ) + return false; + + if( !FVisible(pEntity) ) + return false; + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: Let NPCs know when the flashlight is trained on them +//----------------------------------------------------------------------------- +void CHL2_Player::CheckFlashlight( void ) +{ + if ( !FlashlightIsOn() ) + return; + + if ( m_flNextFlashlightCheckTime > gpGlobals->curtime ) + return; + m_flNextFlashlightCheckTime = gpGlobals->curtime + FLASHLIGHT_NPC_CHECK_INTERVAL; + + // Loop through NPCs looking for illuminated ones + for ( int i = 0; i < g_AI_Manager.NumAIs(); i++ ) + { + CAI_BaseNPC *pNPC = g_AI_Manager.AccessAIs()[i]; + + float flDot; + + if ( IsIlluminatedByFlashlight( pNPC, &flDot ) ) + { + pNPC->PlayerHasIlluminatedNPC( this, flDot ); + } + } +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CHL2_Player::SetPlayerUnderwater( bool state ) +{ + if ( state ) + { + SuitPower_AddDevice( SuitDeviceBreather ); + } + else + { + SuitPower_RemoveDevice( SuitDeviceBreather ); + } + + BaseClass::SetPlayerUnderwater( state ); +} + +//----------------------------------------------------------------------------- +bool CHL2_Player::PassesDamageFilter( const CTakeDamageInfo &info ) +{ + if( info.GetAttacker()->MyNPCPointer() && info.GetAttacker()->MyNPCPointer()->IsPlayerAlly() ) + { + return false; + } + + return BaseClass::PassesDamageFilter( info ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CHL2_Player::InputDisableFlashlight( inputdata_t &inputdata ) +{ + if( FlashlightIsOn() ) + FlashlightTurnOff(); + + m_bFlashlightDisabled = true; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CHL2_Player::InputEnableFlashlight( inputdata_t &inputdata ) +{ + m_bFlashlightDisabled = false; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CHL2_Player::InputIgnoreFallDamage( inputdata_t &inputdata ) +{ + float timeToIgnore = inputdata.value.Float(); + + if ( timeToIgnore <= 0.0 ) + timeToIgnore = TIME_IGNORE_FALL_DAMAGE; + + m_flTimeIgnoreFallDamage = gpGlobals->curtime + timeToIgnore; +} + +//----------------------------------------------------------------------------- +// Purpose: Notification of a player's npc ally in the players squad being killed +//----------------------------------------------------------------------------- +void CHL2_Player::OnSquadMemberKilled( inputdata_t &data ) +{ + // send a message to the client, to notify the hud of the loss + CSingleUserRecipientFilter user( this ); + user.MakeReliable(); + UserMessageBegin( user, "SquadMemberDied" ); + MessageEnd(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CHL2_Player::NotifyFriendsOfDamage( CBaseEntity *pAttackerEntity ) +{ + CAI_BaseNPC *pAttacker = pAttackerEntity->MyNPCPointer(); + if ( pAttacker ) + { + const Vector &origin = GetAbsOrigin(); + for ( int i = 0; i < g_AI_Manager.NumAIs(); i++ ) + { + const float NEAR_Z = 12*12; + const float NEAR_XY_SQ = Square( 50*12 ); + CAI_BaseNPC *pNpc = g_AI_Manager.AccessAIs()[i]; + if ( pNpc->IsPlayerAlly() ) + { + const Vector &originNpc = pNpc->GetAbsOrigin(); + if ( fabsf( originNpc.z - origin.z ) < NEAR_Z ) + { + if ( (originNpc.AsVector2D() - origin.AsVector2D()).LengthSqr() < NEAR_XY_SQ ) + { + pNpc->OnFriendDamaged( this, pAttacker ); + } + } + } + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +int CHL2_Player::OnTakeDamage( const CTakeDamageInfo &info ) +{ + if ( GlobalEntity_GetState( "gordon_invulnerable" ) == GLOBAL_ON ) + return 0; + + if ( ( info.GetDamageType() & DMG_FALL ) && m_flTimeIgnoreFallDamage > gpGlobals->curtime ) + { + m_flTimeIgnoreFallDamage = 0; + return 0; + } + + if( info.GetDamageType() & DMG_BLAST_SURFACE ) + { + if( GetWaterLevel() > 2 ) + { + // Don't take blast damage from anything above the surface. + if( info.GetInflictor()->GetWaterLevel() == 0 ) + { + return 0; + } + } + } + + if ( info.GetDamage() > 0.0f ) + { + m_flLastDamageTime = gpGlobals->curtime; + + if ( info.GetAttacker() ) + NotifyFriendsOfDamage( info.GetAttacker() ); + } + + // Modify the amount of damage the player takes, based on skill. + CTakeDamageInfo playerDamage = info; + + // Should we run this damage through the skill level adjustment? + bool bAdjustForSkillLevel = true; + + if( info.GetDamageType() == DMG_GENERIC && info.GetAttacker() == this && info.GetInflictor() == this ) + { + // Only do a skill level adjustment if the player isn't his own attacker AND inflictor. + // This prevents damage from SetHealth() inputs from being adjusted for skill level. + bAdjustForSkillLevel = false; + } + + if( bAdjustForSkillLevel ) + { + playerDamage.AdjustPlayerDamageTakenForSkillLevel(); + } + + return BaseClass::OnTakeDamage( playerDamage ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : &info - +//----------------------------------------------------------------------------- +int CHL2_Player::OnTakeDamage_Alive( const CTakeDamageInfo &info ) +{ + // Drown + if( info.GetDamageType() & DMG_DROWN ) + { + if( m_idrowndmg == m_idrownrestored ) + { + EmitSound( "Player.DrownStart" ); + } + else + { + EmitSound( "Player.DrownContinue" ); + } + } + + // Burnt + if ( info.GetDamageType() & DMG_BURN ) + { + EmitSound( "HL2Player.BurnPain" ); + } + + + if( (info.GetDamageType() & DMG_SLASH) && hl2_episodic.GetBool() ) + { + if( m_afPhysicsFlags & PFLAG_USING ) + { + // Stop the player using a rotating button for a short time if hit by a creature's melee attack. + // This is for the antlion burrow-corking training in EP1 (sjb). + SuspendUse( 0.5f ); + } + } + + + // Call the base class implementation + return BaseClass::OnTakeDamage_Alive( info ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CHL2_Player::OnDamagedByExplosion( const CTakeDamageInfo &info ) +{ + if ( info.GetInflictor() && info.GetInflictor()->ClassMatches( "mortarshell" ) ) + { + // No ear ringing for mortar + UTIL_ScreenShake( info.GetInflictor()->GetAbsOrigin(), 4.0, 1.0, 0.5, 1000, SHAKE_START, false ); + return; + } + BaseClass::OnDamagedByExplosion( info ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +bool CHL2_Player::ShouldShootMissTarget( CBaseCombatCharacter *pAttacker ) +{ + if( gpGlobals->curtime > m_flTargetFindTime ) + { + // Put this off into the future again. + m_flTargetFindTime = gpGlobals->curtime + random->RandomFloat( 3, 5 ); + return true; + } + + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: Notifies Alyx that player has put a combine ball into a socket so she can comment on it. +// Input : pCombineBall - ball the was socketed +//----------------------------------------------------------------------------- +void CHL2_Player::CombineBallSocketed( CPropCombineBall *pCombineBall ) +{ +#ifdef HL2_EPISODIC + CNPC_Alyx *pAlyx = CNPC_Alyx::GetAlyx(); + if ( pAlyx ) + { + pAlyx->CombineBallSocketed( pCombineBall->NumBounces() ); + } +#endif +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CHL2_Player::Event_KilledOther( CBaseEntity *pVictim, const CTakeDamageInfo &info ) +{ + BaseClass::Event_KilledOther( pVictim, info ); + +#ifdef HL2_EPISODIC + CNPC_Alyx *pAlyx = CNPC_Alyx::GetAlyx(); + if ( pAlyx ) + { + pAlyx->PlayerKilledOther( pVictim, info ); + } +#endif +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CHL2_Player::Event_Killed( const CTakeDamageInfo &info ) +{ + BaseClass::Event_Killed( info ); + + NotifyScriptsOfDeath(); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CHL2_Player::NotifyScriptsOfDeath( void ) +{ + CBaseEntity *pEnt = gEntList.FindEntityByClassname( NULL, "scripted_sequence" ); + + while( pEnt ) + { + variant_t emptyVariant; + pEnt->AcceptInput( "ScriptPlayerDeath", NULL, NULL, emptyVariant, 0 ); + + pEnt = gEntList.FindEntityByClassname( pEnt, "scripted_sequence" ); + } + + pEnt = gEntList.FindEntityByClassname( NULL, "logic_choreographed_scene" ); + + while( pEnt ) + { + variant_t emptyVariant; + pEnt->AcceptInput( "ScriptPlayerDeath", NULL, NULL, emptyVariant, 0 ); + + pEnt = gEntList.FindEntityByClassname( pEnt, "logic_choreographed_scene" ); + } +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CHL2_Player::GetAutoaimVector( autoaim_params_t ¶ms ) +{ + BaseClass::GetAutoaimVector( params ); + + if ( IsXbox() ) + { + if( IsInAVehicle() ) + { + if( m_hLockedAutoAimEntity && m_hLockedAutoAimEntity->IsAlive() && ShouldKeepLockedAutoaimTarget(m_hLockedAutoAimEntity) ) + { + if( params.m_hAutoAimEntity && params.m_hAutoAimEntity != m_hLockedAutoAimEntity ) + { + // Autoaim has picked a new target. Switch. + m_hLockedAutoAimEntity = params.m_hAutoAimEntity; + } + + // Ignore autoaim and just keep aiming at this target. + params.m_hAutoAimEntity = m_hLockedAutoAimEntity; + Vector vecTarget = m_hLockedAutoAimEntity->BodyTarget( EyePosition(), false ); + Vector vecDir = vecTarget - EyePosition(); + VectorNormalize( vecDir ); + + params.m_vecAutoAimDir = vecDir; + params.m_vecAutoAimPoint = vecTarget; + return; + } + else + { + m_hLockedAutoAimEntity = NULL; + } + } + + // If the player manually gets his crosshair onto a target, make that target sticky + if( params.m_fScale != AUTOAIM_SCALE_DIRECT_ONLY ) + { + // Only affect this for 'real' queries + //if( params.m_hAutoAimEntity && params.m_bOnTargetNatural ) + if( params.m_hAutoAimEntity ) + { + // Turn on sticky. + m_HL2Local.m_bStickyAutoAim = true; + + if( IsInAVehicle() ) + { + m_hLockedAutoAimEntity = params.m_hAutoAimEntity; + } + } + else if( !params.m_hAutoAimEntity ) + { + // Turn off sticky only if there's no target at all. + m_HL2Local.m_bStickyAutoAim = false; + + m_hLockedAutoAimEntity = NULL; + } + } + } +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +bool CHL2_Player::ShouldKeepLockedAutoaimTarget( EHANDLE hLockedTarget ) +{ + Vector vecLooking; + Vector vecToTarget; + + vecToTarget = hLockedTarget->WorldSpaceCenter() - EyePosition(); + float flDist = vecToTarget.Length2D(); + VectorNormalize( vecToTarget ); + + if( flDist > autoaim_max_dist.GetFloat() ) + return false; + + float flDot; + + vecLooking = EyeDirection3D(); + flDot = DotProduct( vecLooking, vecToTarget ); + + if( flDot < autoaim_unlock_target.GetFloat() ) + return false; + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : iCount - +// iAmmoIndex - +// bSuppressSound - +// Output : int +//----------------------------------------------------------------------------- +int CHL2_Player::GiveAmmo( int nCount, int nAmmoIndex, bool bSuppressSound) +{ + // Don't try to give the player invalid ammo indices. + if (nAmmoIndex < 0) + return 0; + + bool bCheckAutoSwitch = false; + if (!HasAnyAmmoOfType(nAmmoIndex)) + { + bCheckAutoSwitch = true; + } + + int nAdd = BaseClass::GiveAmmo(nCount, nAmmoIndex, bSuppressSound); + + if ( nCount > 0 && nAdd == 0 ) + { + // we've been denied the pickup, display a hud icon to show that + CSingleUserRecipientFilter user( this ); + user.MakeReliable(); + UserMessageBegin( user, "AmmoDenied" ); + WRITE_SHORT( nAmmoIndex ); + MessageEnd(); + } + + // + // If I was dry on ammo for my best weapon and justed picked up ammo for it, + // autoswitch to my best weapon now. + // + if (bCheckAutoSwitch) + { + CBaseCombatWeapon *pWeapon = g_pGameRules->GetNextBestWeapon(this, GetActiveWeapon()); + + if ( pWeapon && pWeapon->GetPrimaryAmmoType() == nAmmoIndex ) + { + SwitchToNextBestWeapon(GetActiveWeapon()); + } + } + + return nAdd; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +bool CHL2_Player::Weapon_CanUse( CBaseCombatWeapon *pWeapon ) +{ +#ifndef HL2MP + if ( pWeapon->ClassMatches( "weapon_stunstick" ) ) + { + if ( ApplyBattery( 0.5 ) ) + UTIL_Remove( pWeapon ); + return false; + } +#endif + + return BaseClass::Weapon_CanUse( pWeapon ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *pWeapon - +//----------------------------------------------------------------------------- +void CHL2_Player::Weapon_Equip( CBaseCombatWeapon *pWeapon ) +{ +#if HL2_SINGLE_PRIMARY_WEAPON_MODE + + if ( pWeapon->GetSlot() == WEAPON_PRIMARY_SLOT ) + { + Weapon_DropSlot( WEAPON_PRIMARY_SLOT ); + } + +#endif + + BaseClass::Weapon_Equip( pWeapon ); +} + +//----------------------------------------------------------------------------- +// Purpose: Player reacts to bumping a weapon. +// Input : pWeapon - the weapon that the player bumped into. +// Output : Returns true if player picked up the weapon +//----------------------------------------------------------------------------- +bool CHL2_Player::BumpWeapon( CBaseCombatWeapon *pWeapon ) +{ + +#if HL2_SINGLE_PRIMARY_WEAPON_MODE + + CBaseCombatCharacter *pOwner = pWeapon->GetOwner(); + + // Can I have this weapon type? + if ( pOwner || !Weapon_CanUse( pWeapon ) || !g_pGameRules->CanHavePlayerItem( this, pWeapon ) ) + { + if ( gEvilImpulse101 ) + { + UTIL_Remove( pWeapon ); + } + return false; + } + + // ---------------------------------------- + // If I already have it just take the ammo + // ---------------------------------------- + if (Weapon_OwnsThisType( pWeapon->GetClassname(), pWeapon->GetSubType())) + { + //Only remove the weapon if we attained ammo from it + if ( Weapon_EquipAmmoOnly( pWeapon ) == false ) + return false; + + // Only remove me if I have no ammo left + // Can't just check HasAnyAmmo because if I don't use clips, I want to be removed, + if ( pWeapon->UsesClipsForAmmo1() && pWeapon->HasPrimaryAmmo() ) + return false; + + UTIL_Remove( pWeapon ); + return false; + } + // ------------------------- + // Otherwise take the weapon + // ------------------------- + else + { + //Make sure we're not trying to take a new weapon type we already have + if ( Weapon_SlotOccupied( pWeapon ) ) + { + CBaseCombatWeapon *pActiveWeapon = Weapon_GetSlot( WEAPON_PRIMARY_SLOT ); + + if ( pActiveWeapon != NULL && pActiveWeapon->HasAnyAmmo() == false && Weapon_CanSwitchTo( pWeapon ) ) + { + Weapon_Equip( pWeapon ); + return true; + } + + //Attempt to take ammo if this is the gun we're holding already + if ( Weapon_OwnsThisType( pWeapon->GetClassname(), pWeapon->GetSubType() ) ) + { + Weapon_EquipAmmoOnly( pWeapon ); + } + + return false; + } + + pWeapon->CheckRespawn(); + + pWeapon->AddSolidFlags( FSOLID_NOT_SOLID ); + pWeapon->AddEffects( EF_NODRAW ); + + Weapon_Equip( pWeapon ); + + EmitSound( "HL2Player.PickupWeapon" ); + + return true; + } +#else + + return BaseClass::BumpWeapon( pWeapon ); + +#endif + +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *cmd - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CHL2_Player::ClientCommand(const char *cmd) +{ +#if HL2_SINGLE_PRIMARY_WEAPON_MODE + + //Drop primary weapon + if( stricmp( cmd, "DropPrimary" ) == 0 ) + { + Weapon_DropSlot( WEAPON_PRIMARY_SLOT ); + return true; + } + +#endif + + if ( !stricmp( cmd, "emit" ) ) + { + CSingleUserRecipientFilter filter( this ); + if ( engine->Cmd_Argc() > 1 ) + { + EmitSound( filter, entindex(), engine->Cmd_Argv( 1 ) ); + } + else + { + EmitSound( filter, entindex(), "Test.Sound" ); + } + return true; + } + + return BaseClass::ClientCommand( cmd ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : void CBasePlayer::PlayerUse +//----------------------------------------------------------------------------- +void CHL2_Player::PlayerUse ( void ) +{ + // Was use pressed or released? + if ( ! ((m_nButtons | m_afButtonPressed | m_afButtonReleased) & IN_USE) ) + return; + + if ( m_afButtonPressed & IN_USE ) + { + // Currently using a latched entity? + if ( ClearUseEntity() ) + { + return; + } + else + { + if ( m_afPhysicsFlags & PFLAG_DIROVERRIDE ) + { + m_afPhysicsFlags &= ~PFLAG_DIROVERRIDE; + m_iTrain = TRAIN_NEW|TRAIN_OFF; + return; + } + else + { // Start controlling the train! + CBaseEntity *pTrain = GetGroundEntity(); + if ( pTrain && !(m_nButtons & IN_JUMP) && (GetFlags() & FL_ONGROUND) && (pTrain->ObjectCaps() & FCAP_DIRECTIONAL_USE) && pTrain->OnControls(this) ) + { + m_afPhysicsFlags |= PFLAG_DIROVERRIDE; + m_iTrain = TrainSpeed(static_cast(pTrain->m_flSpeed), static_cast(((CFuncTrackTrain*)pTrain)->GetMaxSpeed())); + m_iTrain |= TRAIN_NEW; + EmitSound( "HL2Player.TrainUse" ); + return; + } + } + } + + // Tracker 3926: We can't +USE something if we're climbing a ladder + if ( GetMoveType() == MOVETYPE_LADDER ) + { + return; + } + } + + if( m_flTimeUseSuspended > gpGlobals->curtime ) + { + // Something has temporarily stopped us being able to USE things. + // Obviously, this should be used very carefully.(sjb) + return; + } + + CBaseEntity *pUseEntity = FindUseEntity(); + + bool usedSomething = false; + + // Found an object + if ( pUseEntity ) + { + //!!!UNDONE: traceline here to prevent +USEing buttons through walls + int caps = pUseEntity->ObjectCaps(); + variant_t emptyVariant; + + if ( m_afButtonPressed & IN_USE ) + { + // Robin: Don't play sounds for NPCs, because NPCs will allow respond with speech. + if ( !pUseEntity->MyNPCPointer() ) + { + EmitSound( "HL2Player.Use" ); + } + } + + if ( ( (m_nButtons & IN_USE) && (caps & FCAP_CONTINUOUS_USE) ) || + ( (m_afButtonPressed & IN_USE) && (caps & (FCAP_IMPULSE_USE|FCAP_ONOFF_USE)) ) ) + { + if ( caps & FCAP_CONTINUOUS_USE ) + m_afPhysicsFlags |= PFLAG_USING; + + pUseEntity->AcceptInput( "Use", this, this, emptyVariant, USE_TOGGLE ); + + usedSomething = true; + } + // UNDONE: Send different USE codes for ON/OFF. Cache last ONOFF_USE object to send 'off' if you turn away + else if ( (m_afButtonReleased & IN_USE) && (pUseEntity->ObjectCaps() & FCAP_ONOFF_USE) ) // BUGBUG This is an "off" use + { + pUseEntity->AcceptInput( "Use", this, this, emptyVariant, USE_TOGGLE ); + + usedSomething = true; + } + +#if HL2_SINGLE_PRIMARY_WEAPON_MODE + + //Check for weapon pick-up + if ( m_afButtonPressed & IN_USE ) + { + CBaseCombatWeapon *pWeapon = dynamic_cast(pUseEntity); + + if ( ( pWeapon != NULL ) && ( Weapon_CanSwitchTo( pWeapon ) ) ) + { + //Try to take ammo or swap the weapon + if ( Weapon_OwnsThisType( pWeapon->GetClassname(), pWeapon->GetSubType() ) ) + { + Weapon_EquipAmmoOnly( pWeapon ); + } + else + { + Weapon_DropSlot( pWeapon->GetSlot() ); + Weapon_Equip( pWeapon ); + } + + usedSomething = true; + } + } +#endif + } + else if ( m_afButtonPressed & IN_USE ) + { + // Signal that we want to play the deny sound, unless the user is +USEing on a ladder! + // The sound is emitted in ItemPostFrame, since that occurs after GameMovement::ProcessMove which + // lets the ladder code unset this flag. + m_bPlayUseDenySound = true; + } + + // Debounce the use key + if ( usedSomething && pUseEntity ) + { + m_Local.m_nOldButtons |= IN_USE; + m_afButtonPressed &= ~IN_USE; + } +} + +ConVar sv_show_crosshair_target( "sv_show_crosshair_target", "0" ); + +//----------------------------------------------------------------------------- +// Purpose: Updates the posture of the weapon from lowered to ready +//----------------------------------------------------------------------------- +void CHL2_Player::UpdateWeaponPosture( void ) +{ + CBaseHLCombatWeapon *pWeapon = dynamic_cast(GetActiveWeapon()); + + if ( pWeapon && m_LowerWeaponTimer.Expired() && pWeapon->CanLower() ) + { + m_LowerWeaponTimer.Set( .3 ); + VPROF( "CHL2_Player::UpdateWeaponPosture-CheckLower" ); + Vector vecAim = BaseClass::GetAutoaimVector( AUTOAIM_SCALE_DIRECT_ONLY ); + + const float CHECK_FRIENDLY_RANGE = 50 * 12; + trace_t tr; + UTIL_TraceLine( EyePosition(), EyePosition() + vecAim * CHECK_FRIENDLY_RANGE, MASK_SHOT, this, COLLISION_GROUP_NONE, &tr ); + + CBaseEntity *aimTarget = tr.m_pEnt; + + //If we're over something + if ( aimTarget && !tr.DidHitWorld() ) + { + if ( !aimTarget->IsNPC() || aimTarget->MyNPCPointer()->GetState() != NPC_STATE_COMBAT ) + { + Disposition_t dis = IRelationType( aimTarget ); + + //Debug info for seeing what an object "cons" as + if ( sv_show_crosshair_target.GetBool() ) + { + int text_offset = BaseClass::DrawDebugTextOverlays(); + + char tempstr[255]; + + switch ( dis ) + { + case D_LI: + Q_snprintf( tempstr, sizeof(tempstr), "Disposition: Like" ); + break; + + case D_HT: + Q_snprintf( tempstr, sizeof(tempstr), "Disposition: Hate" ); + break; + + case D_FR: + Q_snprintf( tempstr, sizeof(tempstr), "Disposition: Fear" ); + break; + + case D_NU: + Q_snprintf( tempstr, sizeof(tempstr), "Disposition: Neutral" ); + break; + + default: + case D_ER: + Q_snprintf( tempstr, sizeof(tempstr), "Disposition: !!!ERROR!!!" ); + break; + } + + //Draw the text + NDebugOverlay::EntityText( aimTarget->entindex(), text_offset, tempstr, 0 ); + } + + //See if we hates it + if ( dis == D_LI ) + { + //We're over a friendly, drop our weapon + if ( Weapon_Lower() == false ) + { + //FIXME: We couldn't lower our weapon! + } + + return; + } + } + } + + if ( Weapon_Ready() == false ) + { + //FIXME: We couldn't raise our weapon! + } + } + + if( hl2_xbox_aiming.GetBool() ) + { + if( !pWeapon || !m_AutoaimTimer.Expired() ) + { + return; + } + + m_AutoaimTimer.Set( .1 ); + + VPROF( "hl2_xbox_aiming" ); + + // Call the autoaim code to update the local player data, which allows the client to update. + autoaim_params_t params; + params.m_fScale = AUTOAIM_SCALE_DEFAULT; + params.m_fMaxDist = autoaim_max_dist.GetFloat(); + GetAutoaimVector( params ); + m_HL2Local.m_hAutoAimTarget.Set( params.m_hAutoAimEntity ); + m_HL2Local.m_vecAutoAimPoint.Set( params.m_vecAutoAimPoint ); + return; + } + else + { + // Make sure there's no residual autoaim target if the user changes the xbox_aiming convar on the fly. + m_HL2Local.m_hAutoAimTarget.Set(NULL); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Lowers the weapon posture (for hovering over friendlies) +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CHL2_Player::Weapon_Lower( void ) +{ + VPROF( "CHL2_Player::Weapon_Lower" ); + // Already lowered? + if ( m_HL2Local.m_bWeaponLowered ) + return true; + + m_HL2Local.m_bWeaponLowered = true; + + CBaseHLCombatWeapon *pWeapon = dynamic_cast(GetActiveWeapon()); + + if ( pWeapon == NULL ) + return false; + + return pWeapon->Lower(); +} + +//----------------------------------------------------------------------------- +// Purpose: Returns the weapon posture to normal +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CHL2_Player::Weapon_Ready( void ) +{ + VPROF( "CHL2_Player::Weapon_Ready" ); + + // Already ready? + if ( m_HL2Local.m_bWeaponLowered == false ) + return true; + + m_HL2Local.m_bWeaponLowered = false; + + CBaseHLCombatWeapon *pWeapon = dynamic_cast(GetActiveWeapon()); + + if ( pWeapon == NULL ) + return false; + + return pWeapon->Ready(); +} + +//----------------------------------------------------------------------------- +// Purpose: Returns whether or not we can switch to the given weapon. +// Input : pWeapon - +//----------------------------------------------------------------------------- +bool CHL2_Player::Weapon_CanSwitchTo( CBaseCombatWeapon *pWeapon ) +{ + CBasePlayer *pPlayer = (CBasePlayer *)this; +#if !defined( CLIENT_DLL ) + IServerVehicle *pVehicle = pPlayer->GetVehicle(); +#else + IClientVehicle *pVehicle = pPlayer->GetVehicle(); +#endif + if (pVehicle && !pPlayer->UsingStandardWeaponsInVehicle()) + return false; + + if ( !pWeapon->HasAnyAmmo() && !GetAmmoCount( pWeapon->m_iPrimaryAmmoType ) ) + return false; + + if ( !pWeapon->CanDeploy() ) + return false; + + if ( GetActiveWeapon() ) + { + if ( PhysCannonGetHeldEntity( GetActiveWeapon() ) == pWeapon && + Weapon_OwnsThisType( pWeapon->GetClassname(), pWeapon->GetSubType()) ) + { + return true; + } + + if ( !GetActiveWeapon()->CanHolster() ) + return false; + } + + return true; +} + +void CHL2_Player::PickupObject( CBaseEntity *pObject, bool bLimitMassAndSize ) +{ + // can't pick up what you're standing on + if ( GetGroundEntity() == pObject ) + return; + + if ( bLimitMassAndSize == true ) + { + if ( CBasePlayer::CanPickupObject( pObject, 35, 128 ) == false ) + return; + } + + // Can't be picked up if NPCs are on me + if ( pObject->HasNPCsOnIt() ) + return; + + PlayerPickupObject( this, pObject ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : CBaseEntity +//----------------------------------------------------------------------------- +bool CHL2_Player::IsHoldingEntity( CBaseEntity *pEnt ) +{ + return PlayerPickupControllerIsHoldingEntity( m_hUseEntity, pEnt ); +} + +float CHL2_Player::GetHeldObjectMass( IPhysicsObject *pHeldObject ) +{ + float mass = PlayerPickupGetHeldObjectMass( m_hUseEntity, pHeldObject ); + if ( mass == 0.0f ) + { + mass = PhysCannonGetHeldObjectMass( GetActiveWeapon(), pHeldObject ); + } + return mass; +} + +//----------------------------------------------------------------------------- +// Purpose: Force the player to drop any physics objects he's carrying +//----------------------------------------------------------------------------- +void CHL2_Player::ForceDropOfCarriedPhysObjects( CBaseEntity *pOnlyIfHoldingThis ) +{ + if ( PhysIsInCallback() ) + { + variant_t value; + g_EventQueue.AddEvent( this, "ForceDropPhysObjects", value, 0.01f, pOnlyIfHoldingThis, this ); + return; + } + // Drop any objects being handheld. + ClearUseEntity(); + + // Then force the physcannon to drop anything it's holding, if it's our active weapon + PhysCannonForceDrop( GetActiveWeapon(), pOnlyIfHoldingThis ); +} + +void CHL2_Player::InputForceDropPhysObjects( inputdata_t &data ) +{ + ForceDropOfCarriedPhysObjects( data.pActivator ); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CHL2_Player::UpdateClientData( void ) +{ + if (m_DmgTake || m_DmgSave || m_bitsHUDDamage != m_bitsDamageType) + { + // Comes from inside me if not set + Vector damageOrigin = GetLocalOrigin(); + // send "damage" message + // causes screen to flash, and pain compass to show direction of damage + damageOrigin = m_DmgOrigin; + + // only send down damage type that have hud art + int visibleDamageBits = m_bitsDamageType & DMG_SHOWNHUD; + + m_DmgTake = clamp( m_DmgTake, 0, 255 ); + m_DmgSave = clamp( m_DmgSave, 0, 255 ); + + // If we're poisoned, but it wasn't this frame, don't send the indicator + // Without this check, any damage that occured to the player while they were + // recovering from a poison bite would register as poisonous as well and flash + // the whole screen! -- jdw + if ( visibleDamageBits & DMG_POISON ) + { + float flLastPoisonedDelta = gpGlobals->curtime - m_tbdPrev; + if ( flLastPoisonedDelta > 0.1f ) + { + visibleDamageBits &= ~DMG_POISON; + } + } + + CSingleUserRecipientFilter user( this ); + user.MakeReliable(); + UserMessageBegin( user, "Damage" ); + WRITE_BYTE( static_cast(m_DmgSave) ); + WRITE_BYTE( static_cast(m_DmgTake) ); + WRITE_LONG( visibleDamageBits ); + WRITE_FLOAT( damageOrigin.x ); //BUG: Should be fixed point (to hud) not floats + WRITE_FLOAT( damageOrigin.y ); //BUG: However, the HUD does _not_ implement bitfield messages (yet) + WRITE_FLOAT( damageOrigin.z ); //BUG: We use WRITE_VEC3COORD for everything else + MessageEnd(); + + m_DmgTake = 0; + m_DmgSave = 0; + m_bitsHUDDamage = m_bitsDamageType; + + // Clear off non-time-based damage indicators + m_bitsDamageType &= DMG_TIMEBASED; + } + + BaseClass::UpdateClientData(); +} + +//--------------------------------------------------------- +//--------------------------------------------------------- +void CHL2_Player::OnRestore() +{ + BaseClass::OnRestore(); + m_pPlayerAISquad = g_AI_SquadManager.FindCreateSquad(AllocPooledString(PLAYER_SQUADNAME)); +} + +//--------------------------------------------------------- +//--------------------------------------------------------- +Vector CHL2_Player::EyeDirection2D( void ) +{ + Vector vecReturn = EyeDirection3D(); + vecReturn.z = 0; + vecReturn.AsVector2D().NormalizeInPlace(); + + return vecReturn; +} + +//--------------------------------------------------------- +//--------------------------------------------------------- +Vector CHL2_Player::EyeDirection3D( void ) +{ + Vector vecForward; + IServerVehicle *pVehicle = GetVehicle(); + if ( pVehicle ) + { + Assert( pVehicle ); + int nRole = pVehicle->GetPassengerRole( this ); + + Vector vecEyeOrigin; + QAngle angEyeAngles; + pVehicle->GetVehicleViewPosition( nRole, &vecEyeOrigin, &angEyeAngles ); + AngleVectors( angEyeAngles, &vecForward ); + } + else + { + AngleVectors( EyeAngles(), &vecForward ); + } + return vecForward; +} + + +//--------------------------------------------------------- +//--------------------------------------------------------- +bool CHL2_Player::Weapon_Switch( CBaseCombatWeapon *pWeapon, int viewmodelindex ) +{ + // Recalculate proficiency! + SetCurrentWeaponProficiency( CalcWeaponProficiency( pWeapon ) ); + + // Come out of suit zoom mode + if ( IsZooming() ) + { + StopZooming(); + } + + return BaseClass::Weapon_Switch( pWeapon, viewmodelindex ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +WeaponProficiency_t CHL2_Player::CalcWeaponProficiency( CBaseCombatWeapon *pWeapon ) +{ + WeaponProficiency_t proficiency; + + proficiency = WEAPON_PROFICIENCY_PERFECT; + + if( weapon_showproficiency.GetBool() != 0 ) + { + Msg("Player switched to %s, proficiency is %s\n", pWeapon->GetClassname(), GetWeaponProficiencyName( proficiency ) ); + } + + return proficiency; +} + +//----------------------------------------------------------------------------- +// Purpose: override how single player rays hit the player +//----------------------------------------------------------------------------- + +bool LineCircleIntersection( + const Vector2D ¢er, + const float radius, + const Vector2D &vLinePt, + const Vector2D &vLineDir, + float *fIntersection1, + float *fIntersection2) +{ + // Line = P + Vt + // Sphere = r (assume we've translated to origin) + // (P + Vt)^2 = r^2 + // VVt^2 + 2PVt + (PP - r^2) + // Solve as quadratic: (-b +/- sqrt(b^2 - 4ac)) / 2a + // If (b^2 - 4ac) is < 0 there is no solution. + // If (b^2 - 4ac) is = 0 there is one solution (a case this function doesn't support). + // If (b^2 - 4ac) is > 0 there are two solutions. + Vector2D P; + float a, b, c, sqr, insideSqr; + + + // Translate circle to origin. + P[0] = vLinePt[0] - center[0]; + P[1] = vLinePt[1] - center[1]; + + a = vLineDir.Dot(vLineDir); + b = 2.0f * P.Dot(vLineDir); + c = P.Dot(P) - (radius * radius); + + insideSqr = b*b - 4*a*c; + if(insideSqr <= 0.000001f) + return false; + + // Ok, two solutions. + sqr = (float)FastSqrt(insideSqr); + + float denom = 1.0 / (2.0f * a); + + *fIntersection1 = (-b - sqr) * denom; + *fIntersection2 = (-b + sqr) * denom; + + return true; +} + +static void Collision_ClearTrace( const Vector &vecRayStart, const Vector &vecRayDelta, CBaseTrace *pTrace ) +{ + pTrace->startpos = vecRayStart; + pTrace->endpos = vecRayStart; + pTrace->endpos += vecRayDelta; + pTrace->startsolid = false; + pTrace->allsolid = false; + pTrace->fraction = 1.0f; + pTrace->contents = 0; +} + + +bool IntersectRayWithAACylinder( const Ray_t &ray, + const Vector ¢er, float radius, float height, CBaseTrace *pTrace ) +{ + Assert( ray.m_IsRay ); + Collision_ClearTrace( ray.m_Start, ray.m_Delta, pTrace ); + + // First intersect the ray with the top + bottom planes + float halfHeight = height * 0.5; + + // Handle parallel case + Vector vStart = ray.m_Start - center; + Vector vEnd = vStart + ray.m_Delta; + + float flEnterFrac, flLeaveFrac; + if (FloatMakePositive(ray.m_Delta.z) < 1e-8) + { + if ( (vStart.z < -halfHeight) || (vStart.z > halfHeight) ) + { + return false; // no hit + } + flEnterFrac = 0.0f; flLeaveFrac = 1.0f; + } + else + { + // Clip the ray to the top and bottom of box + flEnterFrac = IntersectRayWithAAPlane( vStart, vEnd, 2, 1, halfHeight); + flLeaveFrac = IntersectRayWithAAPlane( vStart, vEnd, 2, 1, -halfHeight); + + if ( flLeaveFrac < flEnterFrac ) + { + float temp = flLeaveFrac; + flLeaveFrac = flEnterFrac; + flEnterFrac = temp; + } + + if ( flLeaveFrac < 0 || flEnterFrac > 1) + { + return false; + } + } + + // Intersect with circle + float flCircleEnterFrac, flCircleLeaveFrac; + if ( !LineCircleIntersection( vec3_origin.AsVector2D(), radius, + vStart.AsVector2D(), ray.m_Delta.AsVector2D(), &flCircleEnterFrac, &flCircleLeaveFrac ) ) + { + return false; // no hit + } + + Assert( flCircleEnterFrac <= flCircleLeaveFrac ); + if ( flCircleLeaveFrac < 0 || flCircleEnterFrac > 1) + { + return false; + } + + if ( flEnterFrac < flCircleEnterFrac ) + flEnterFrac = flCircleEnterFrac; + if ( flLeaveFrac > flCircleLeaveFrac ) + flLeaveFrac = flCircleLeaveFrac; + + if ( flLeaveFrac < flEnterFrac ) + return false; + + VectorMA( ray.m_Start, flEnterFrac , ray.m_Delta, pTrace->endpos ); + pTrace->fraction = flEnterFrac; + pTrace->contents = CONTENTS_SOLID; + + // Calculate the point on our center line where we're nearest the intersection point + Vector collisionCenter; + CalcClosestPointOnLineSegment( pTrace->endpos, center + Vector( 0, 0, halfHeight ), center - Vector( 0, 0, halfHeight ), collisionCenter ); + + // Our normal is the direction from that center point to the intersection point + pTrace->plane.normal = pTrace->endpos - collisionCenter; + VectorNormalize( pTrace->plane.normal ); + + return true; +} + + +bool CHL2_Player::TestHitboxes( const Ray_t &ray, unsigned int fContentsMask, trace_t& tr ) +{ + if( g_pGameRules->IsMultiplayer() ) + { + return BaseClass::TestHitboxes( ray, fContentsMask, tr ); + } + else + { + Assert( ray.m_IsRay ); + + Vector mins, maxs; + + mins = WorldAlignMins(); + maxs = WorldAlignMaxs(); + + if ( IntersectRayWithAACylinder( ray, WorldSpaceCenter(), maxs.x * PLAYER_HULL_REDUCTION, maxs.z - mins.z, &tr ) ) + { + CStudioHdr *pStudioHdr = GetModelPtr( ); + if (!pStudioHdr) + return false; + + mstudiohitboxset_t *set = pStudioHdr->pHitboxSet( m_nHitboxSet ); + if ( !set || !set->numhitboxes ) + return false; + + mstudiobbox_t *pbox = set->pHitbox( tr.hitbox ); + mstudiobone_t *pBone = pStudioHdr->pBone(pbox->bone); + tr.surface.name = "**studio**"; + tr.surface.flags = SURF_HITBOX; + tr.surface.surfaceProps = physprops->GetSurfaceIndex( pBone->pszSurfaceProp() ); + } + + return true; + } +} + +//--------------------------------------------------------- +// Show the player's scaled down bbox that we use for +// bullet impacts. +//--------------------------------------------------------- +void CHL2_Player::DrawDebugGeometryOverlays(void) +{ + BaseClass::DrawDebugGeometryOverlays(); + + if (m_debugOverlays & OVERLAY_BBOX_BIT) + { + Vector mins, maxs; + + mins = WorldAlignMins(); + maxs = WorldAlignMaxs(); + + mins.x *= PLAYER_HULL_REDUCTION; + mins.y *= PLAYER_HULL_REDUCTION; + + maxs.x *= PLAYER_HULL_REDUCTION; + maxs.y *= PLAYER_HULL_REDUCTION; + + NDebugOverlay::Box( GetAbsOrigin(), mins, maxs, 255, 0, 0, 100, 0 ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Helper to remove from ladder +//----------------------------------------------------------------------------- +void CHL2_Player::ExitLadder() +{ + if ( MOVETYPE_LADDER != GetMoveType() ) + return; + + SetMoveType( MOVETYPE_WALK ); + SetMoveCollide( MOVECOLLIDE_DEFAULT ); + // Remove from ladder + m_HL2Local.m_hLadder.Set( NULL ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Queues up a use deny sound, played in ItemPostFrame. +//----------------------------------------------------------------------------- +void CHL2_Player::PlayUseDenySound() +{ + m_bPlayUseDenySound = true; +} + + +void CHL2_Player::ItemPostFrame() +{ + BaseClass::ItemPostFrame(); + + if ( m_bPlayUseDenySound ) + { + m_bPlayUseDenySound = false; + EmitSound( "HL2Player.UseDeny" ); + } +} + + +void CHL2_Player::StartWaterDeathSounds( void ) +{ + CPASAttenuationFilter filter( this ); + + if ( m_sndLeeches == NULL ) + { + m_sndLeeches = (CSoundEnvelopeController::GetController()).SoundCreate( filter, entindex(), CHAN_STATIC, "coast.leech_bites_loop" , ATTN_NORM ); + } + + if ( m_sndLeeches ) + { + (CSoundEnvelopeController::GetController()).Play( m_sndLeeches, 1.0f, 100 ); + } + + if ( m_sndWaterSplashes == NULL ) + { + m_sndWaterSplashes = (CSoundEnvelopeController::GetController()).SoundCreate( filter, entindex(), CHAN_STATIC, "coast.leech_water_churn_loop" , ATTN_NORM ); + } + + if ( m_sndWaterSplashes ) + { + (CSoundEnvelopeController::GetController()).Play( m_sndWaterSplashes, 1.0f, 100 ); + } +} + +void CHL2_Player::StopWaterDeathSounds( void ) +{ + if ( m_sndLeeches ) + { + (CSoundEnvelopeController::GetController()).SoundFadeOut( m_sndLeeches, 0.5f, true ); + m_sndLeeches = NULL; + } + + if ( m_sndWaterSplashes ) + { + (CSoundEnvelopeController::GetController()).SoundFadeOut( m_sndWaterSplashes, 0.5f, true ); + m_sndWaterSplashes = NULL; + } +} + +//----------------------------------------------------------------------------- +// Shuts down sounds +//----------------------------------------------------------------------------- +void CHL2_Player::StopLoopingSounds( void ) +{ + if ( m_sndLeeches != NULL ) + { + (CSoundEnvelopeController::GetController()).SoundDestroy( m_sndLeeches ); + m_sndLeeches = NULL; + } + + if ( m_sndWaterSplashes != NULL ) + { + (CSoundEnvelopeController::GetController()).SoundDestroy( m_sndWaterSplashes ); + m_sndWaterSplashes = NULL; + } + + BaseClass::StopLoopingSounds(); +} + +//----------------------------------------------------------------------------- +void CHL2_Player::ModifyOrAppendPlayerCriteria( AI_CriteriaSet& set ) +{ + BaseClass::ModifyOrAppendPlayerCriteria( set ); + + if ( GlobalEntity_GetIndex( "gordon_precriminal" ) == -1 ) + { + set.AppendCriteria( "gordon_precriminal", "0" ); + } +} + +CLogicPlayerProxy *CHL2_Player::GetPlayerProxy( void ) +{ + CLogicPlayerProxy *pProxy = dynamic_cast< CLogicPlayerProxy* > ( m_hPlayerProxy.Get() ); + + if ( pProxy == NULL ) + { + pProxy = (CLogicPlayerProxy*)gEntList.FindEntityByClassname(NULL, "logic_playerproxy" ); + + if ( pProxy == NULL ) + return NULL; + + pProxy->m_hPlayer = this; + m_hPlayerProxy = pProxy; + } + + return pProxy; +} + +void CHL2_Player::FirePlayerProxyOutput( const char *pszOutputName, variant_t variant, CBaseEntity *pActivator, CBaseEntity *pCaller ) +{ + if ( GetPlayerProxy() == NULL ) + return; + + GetPlayerProxy()->FireNamedOutput( pszOutputName, variant, pActivator, pCaller ); +} + +LINK_ENTITY_TO_CLASS( logic_playerproxy, CLogicPlayerProxy); + +BEGIN_DATADESC( CLogicPlayerProxy ) + DEFINE_OUTPUT( m_OnFlashlightOn, "OnFlashlightOn" ), + DEFINE_OUTPUT( m_OnFlashlightOff, "OnFlashlightOff" ), + DEFINE_OUTPUT( m_RequestedPlayerHealth, "PlayerHealth" ), + DEFINE_OUTPUT( m_PlayerHasAmmo, "PlayerHasAmmo" ), + DEFINE_OUTPUT( m_PlayerHasNoAmmo, "PlayerHasNoAmmo" ), + DEFINE_INPUTFUNC( FIELD_VOID, "RequestPlayerHealth", InputRequestPlayerHealth ), + DEFINE_INPUTFUNC( FIELD_VOID, "SetFlashlightSlowDrain", InputSetFlashlightSlowDrain ), + DEFINE_INPUTFUNC( FIELD_VOID, "SetFlashlightNormalDrain", InputSetFlashlightNormalDrain ), + DEFINE_INPUTFUNC( FIELD_INTEGER, "SetPlayerHealth", InputSetPlayerHealth ), + DEFINE_INPUTFUNC( FIELD_VOID, "RequestAmmoState", InputRequestAmmoState ), + DEFINE_FIELD( m_hPlayer, FIELD_EHANDLE ), +END_DATADESC() + +void CLogicPlayerProxy::Activate( void ) +{ + BaseClass::Activate(); + + if ( m_hPlayer == NULL ) + { + m_hPlayer = AI_GetSinglePlayer(); + } +} + +void CLogicPlayerProxy::InputSetPlayerHealth( inputdata_t &inputdata ) +{ + if ( m_hPlayer == NULL ) + return; + + m_hPlayer->SetHealth( inputdata.value.Int() ); + +} + +void CLogicPlayerProxy::InputRequestPlayerHealth( inputdata_t &inputdata ) +{ + if ( m_hPlayer == NULL ) + return; + + m_RequestedPlayerHealth.Set( m_hPlayer->GetHealth(), inputdata.pActivator, inputdata.pCaller ); +} + +void CLogicPlayerProxy::InputSetFlashlightSlowDrain( inputdata_t &inputdata ) +{ + if( m_hPlayer == NULL ) + return; + + CHL2_Player *pPlayer = dynamic_cast(m_hPlayer.Get()); + + if( pPlayer ) + pPlayer->SetFlashlightPowerDrainScale( hl2_darkness_flashlight_factor.GetFloat() ); +} + +void CLogicPlayerProxy::InputSetFlashlightNormalDrain( inputdata_t &inputdata ) +{ + if( m_hPlayer == NULL ) + return; + + CHL2_Player *pPlayer = dynamic_cast(m_hPlayer.Get()); + + if( pPlayer ) + pPlayer->SetFlashlightPowerDrainScale( 1.0f ); +} + +void CLogicPlayerProxy::InputRequestAmmoState( inputdata_t &inputdata ) +{ + if( m_hPlayer == NULL ) + return; + + CHL2_Player *pPlayer = dynamic_cast(m_hPlayer.Get()); + + for ( int i = 0 ; i < pPlayer->WeaponCount(); ++i ) + { + CBaseCombatWeapon* pCheck = pPlayer->GetWeapon( i ); + + if ( pCheck ) + { + if ( pCheck->HasAnyAmmo() && (pCheck->UsesPrimaryAmmo() || pCheck->UsesSecondaryAmmo())) + { + m_PlayerHasAmmo.FireOutput( this, this, 0 ); + return; + } + } + } + + m_PlayerHasNoAmmo.FireOutput( this, this, 0 ); +} diff --git a/dlls/hl2_dll/hl2_triggers.cpp b/dlls/hl2_dll/hl2_triggers.cpp index 68ec4bf0..22a9e905 100644 --- a/dlls/hl2_dll/hl2_triggers.cpp +++ b/dlls/hl2_dll/hl2_triggers.cpp @@ -542,12 +542,12 @@ void CWateryDeathLeech::LeechThink( void ) dt = 0.1f; } m_nRenderMode = kRenderTransTexture; - int speed = max(1,256*dt); // fade out over 1 second + int speed = static_cast(max(1,256*dt)); // fade out over 1 second if ( m_iFadeState == -1 ) - SetRenderColorA( UTIL_Approach( 0, m_clrRender->a, speed ) ); + SetRenderColorA( static_cast(UTIL_Approach( 0, m_clrRender->a, speed )) ); else - SetRenderColorA( UTIL_Approach( 255, m_clrRender->a, speed ) ); + SetRenderColorA( static_cast(UTIL_Approach( 255, m_clrRender->a, speed )) ); if ( m_clrRender->a == 0 ) { diff --git a/dlls/hl2_dll/item_ammo.cpp b/dlls/hl2_dll/item_ammo.cpp index 71fc934d..6c8f36de 100644 --- a/dlls/hl2_dll/item_ammo.cpp +++ b/dlls/hl2_dll/item_ammo.cpp @@ -32,7 +32,7 @@ int ITEM_GiveAmmo( CBasePlayer *pPlayer, float flCount, const char *pszAmmoName, // Don't give out less than 1 of anything. flCount = max( 1.0f, flCount ); - return pPlayer->GiveAmmo( flCount, iAmmoType, bSuppressSound ); + return pPlayer->GiveAmmo( static_cast(flCount), iAmmoType, bSuppressSound ); } // ======================================================================== diff --git a/dlls/hl2_dll/item_dynamic_resupply.cpp b/dlls/hl2_dll/item_dynamic_resupply.cpp index 5ace966b..969a2552 100644 --- a/dlls/hl2_dll/item_dynamic_resupply.cpp +++ b/dlls/hl2_dll/item_dynamic_resupply.cpp @@ -218,12 +218,12 @@ void CItem_DynamicResupply::Precache( void ) { // Precache all the items potentially spawned int i; - for ( i = 0; i < NUM_HEALTH_ITEMS; i++ ) + for ( i = 0; i < static_cast(NUM_HEALTH_ITEMS); i++ ) { UTIL_PrecacheOther( g_DynamicResupplyHealthItems[i].sEntityName ); } - for ( i = 0; i < NUM_AMMO_ITEMS; i++ ) + for ( i = 0; i < static_cast(NUM_AMMO_ITEMS); i++ ) { UTIL_PrecacheOther( g_DynamicResupplyAmmoItems[i].sEntityName ); } @@ -293,7 +293,7 @@ void CItem_DynamicResupply::SpawnFullItem( CItem_DynamicResupply *pMaster, CBase float flRatio[NUM_AMMO_ITEMS]; int i; float flTotalProb = 0.0f; - for ( i = 0; i < NUM_AMMO_ITEMS; ++i ) + for ( i = 0; i < static_cast(NUM_AMMO_ITEMS); ++i ) { int iAmmoType = GetAmmoDef()->Index( g_DynamicResupplyAmmoItems[i].sAmmoDef ); bool bCanSpawn = pPlayer->Weapon_GetWpnForAmmo( iAmmoType ) != NULL; @@ -329,7 +329,7 @@ void CItem_DynamicResupply::SpawnFullItem( CItem_DynamicResupply *pMaster, CBase } float flChoice = random->RandomFloat( 0.0f, flTotalProb ); - for ( i = 0; i < NUM_AMMO_ITEMS; ++i ) + for ( i = 0; i < static_cast(NUM_AMMO_ITEMS); ++i ) { if ( flChoice <= flRatio[i] ) { @@ -399,7 +399,7 @@ void CItem_DynamicResupply::FindPotentialItems( int nCount, DynamicResupplyItems //----------------------------------------------------------------------------- void CItem_DynamicResupply::ComputeHealthRatios( CItem_DynamicResupply* pMaster, CBasePlayer *pPlayer, int iDebug, SpawnInfo_t *pSpawnInfo ) { - for ( int i = 0; i < NUM_HEALTH_ITEMS; i++ ) + for ( int i = 0; i < static_cast(NUM_HEALTH_ITEMS); i++ ) { // Figure out the current level of this resupply type float flMax; @@ -435,7 +435,7 @@ void CItem_DynamicResupply::ComputeHealthRatios( CItem_DynamicResupply* pMaster, if ( iDebug ) { Msg("Calculating desired health ratios & deltas:\n"); - for ( int i = 0; i < NUM_HEALTH_ITEMS; i++ ) + for ( int i = 0; i < static_cast(NUM_HEALTH_ITEMS); i++ ) { Msg(" %s Desired Ratio: %.2f, Current Ratio: %.2f = Delta of %.2f\n", g_DynamicResupplyHealthItems[i].sEntityName, pSpawnInfo[i].m_flDesiredRatio, pSpawnInfo[i].m_flCurrentRatio, pSpawnInfo[i].m_flDelta ); @@ -449,7 +449,7 @@ void CItem_DynamicResupply::ComputeHealthRatios( CItem_DynamicResupply* pMaster, //----------------------------------------------------------------------------- void CItem_DynamicResupply::ComputeAmmoRatios( CItem_DynamicResupply* pMaster, CBasePlayer *pPlayer, int iDebug, SpawnInfo_t *pSpawnInfo ) { - for ( int i = 0; i < NUM_AMMO_ITEMS; i++ ) + for ( int i = 0; i < static_cast(NUM_AMMO_ITEMS); i++ ) { // Get the ammodef's int iAmmoType = GetAmmoDef()->Index( g_DynamicResupplyAmmoItems[i].sAmmoDef ); @@ -477,7 +477,7 @@ void CItem_DynamicResupply::ComputeAmmoRatios( CItem_DynamicResupply* pMaster, C if ( iDebug ) { Msg("Calculating desired ammo ratios & deltas:\n"); - for ( int i = 0; i < NUM_AMMO_ITEMS; i++ ) + for ( int i = 0; i < static_cast(NUM_AMMO_ITEMS); i++ ) { Msg(" %s Desired Ratio: %.2f, Current Ratio: %.2f = Delta of %.2f\n", g_DynamicResupplyAmmoItems[i].sEntityName, pSpawnInfo[i].m_flDesiredRatio, pSpawnInfo[i].m_flCurrentRatio, pSpawnInfo[i].m_flDelta ); diff --git a/dlls/hl2_dll/item_healthkit.cpp b/dlls/hl2_dll/item_healthkit.cpp index 041e81db..c8233b6c 100644 --- a/dlls/hl2_dll/item_healthkit.cpp +++ b/dlls/hl2_dll/item_healthkit.cpp @@ -243,7 +243,7 @@ void CWallHealth::Spawn(void) SetModel( STRING( GetModelName() ) ); - m_iJuice = sk_healthcharger.GetFloat(); + m_iJuice = sk_healthcharger.GetInt(); m_nState = 0; @@ -388,7 +388,7 @@ void CWallHealth::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE u void CWallHealth::Recharge(void) { EmitSound( "WallHealth.Recharge" ); - m_iJuice = sk_healthcharger.GetFloat(); + m_iJuice = sk_healthcharger.GetInt(); m_nState = 0; SetThink( NULL ); } @@ -405,7 +405,7 @@ void CWallHealth::Off(void) m_iOn = 0; - if ((!m_iJuice) && ( ( m_iReactivate = g_pGameRules->FlHealthChargerRechargeTime() ) > 0) ) + if ((!m_iJuice) && ( ( m_iReactivate = static_cast(g_pGameRules->FlHealthChargerRechargeTime()) ) > 0) ) { SetNextThink( gpGlobals->curtime + m_iReactivate ); SetThink(&CWallHealth::Recharge); @@ -520,7 +520,7 @@ void CNewWallHealth::Spawn(void) ResetSequence( LookupSequence( "idle" ) ); - m_iJuice = sk_healthcharger.GetFloat(); + m_iJuice = sk_healthcharger.GetInt(); m_nState = 0; @@ -700,7 +700,7 @@ void CNewWallHealth::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYP void CNewWallHealth::Recharge(void) { EmitSound( "WallHealth.Recharge" ); - m_flJuice = m_iJuice = sk_healthcharger.GetFloat(); + m_flJuice = m_iJuice = sk_healthcharger.GetInt(); m_nState = 0; ResetSequence( LookupSequence( "idle" ) ); @@ -733,7 +733,7 @@ void CNewWallHealth::Off(void) { if ((!m_iJuice) && g_pGameRules->FlHealthChargerRechargeTime() > 0 ) { - m_iReactivate = g_pGameRules->FlHealthChargerRechargeTime(); + m_iReactivate = static_cast(g_pGameRules->FlHealthChargerRechargeTime()); SetNextThink( gpGlobals->curtime + m_iReactivate ); SetThink(&CNewWallHealth::Recharge); } diff --git a/dlls/hl2_dll/npc_BaseZombie.cpp b/dlls/hl2_dll/npc_BaseZombie.cpp index 218e2314..b783cb44 100644 --- a/dlls/hl2_dll/npc_BaseZombie.cpp +++ b/dlls/hl2_dll/npc_BaseZombie.cpp @@ -890,6 +890,9 @@ int CNPC_BaseZombie::OnTakeDamage_Alive( const CTakeDamageInfo &inputInfo ) case RELEASE_SCHEDULED: SetCondition( COND_ZOMBIE_RELEASECRAB ); break; + + default: + break; } if( ShouldBecomeTorso( info, flDamageThreshold ) ) @@ -945,7 +948,7 @@ void CNPC_BaseZombie::MakeAISpookySound( float volume, float duration ) { if ( HL2GameRules()->IsAlyxInDarknessMode() ) { - CSoundEnt::InsertSound( SOUND_COMBAT, EyePosition(), volume, duration, this, SOUNDENT_CHANNEL_SPOOKY_NOISE ); + CSoundEnt::InsertSound( SOUND_COMBAT, EyePosition(), static_cast(volume), duration, this, SOUNDENT_CHANNEL_SPOOKY_NOISE ); } } @@ -1010,7 +1013,7 @@ void CNPC_BaseZombie::MoanSound( envelopePoint_t *pEnvelope, int iEnvelopeSize ) float duration = ENVELOPE_CONTROLLER.SoundPlayEnvelope( m_pMoanSound, SOUNDCTRL_CHANGE_VOLUME, pEnvelope, iEnvelopeSize ); - float flPitch = random->RandomInt( m_flMoanPitch + zombie_changemin.GetInt(), m_flMoanPitch + zombie_changemax.GetInt() ); + float flPitch = random->RandomFloat( m_flMoanPitch + zombie_changemin.GetFloat(), m_flMoanPitch + zombie_changemax.GetFloat() ); ENVELOPE_CONTROLLER.SoundChangePitch( m_pMoanSound, flPitch, 0.3 ); m_flNextMoanSound = gpGlobals->curtime + duration + 9999; @@ -1221,7 +1224,7 @@ void CNPC_BaseZombie::Ignite( float flFlameLifetime, bool bNPCOnly, float flSize #endif // HL2_EPISODIC // Set the zombie up to burn to death in about ten seconds. - SetHealth( min( m_iHealth, FLAME_DIRECT_DAMAGE_PER_SEC * (ZOMBIE_BURN_TIME + random->RandomFloat( -ZOMBIE_BURN_TIME_NOISE, ZOMBIE_BURN_TIME_NOISE)) ) ); + SetHealth( static_cast(min( m_iHealth, FLAME_DIRECT_DAMAGE_PER_SEC * (ZOMBIE_BURN_TIME + random->RandomFloat( -ZOMBIE_BURN_TIME_NOISE, ZOMBIE_BURN_TIME_NOISE))) ) ); // FIXME: use overlays when they come online //AddOverlay( ACT_ZOM_WALK_ON_FIRE, false ); @@ -1537,7 +1540,7 @@ void CNPC_BaseZombie::HandleAnimEvent( animevent_t *pEvent ) right = right * 100; forward = forward * 200; - ClawAttack( GetClawAttackRange(), sk_zombie_dmg_one_slash.GetFloat(), QAngle( -15, -20, -10 ), right + forward, ZOMBIE_BLOOD_RIGHT_HAND ); + ClawAttack( GetClawAttackRange(), sk_zombie_dmg_one_slash.GetInt(), QAngle( -15, -20, -10 ), right + forward, ZOMBIE_BLOOD_RIGHT_HAND ); return; } @@ -1549,7 +1552,7 @@ void CNPC_BaseZombie::HandleAnimEvent( animevent_t *pEvent ) right = right * -100; forward = forward * 200; - ClawAttack( GetClawAttackRange(), sk_zombie_dmg_one_slash.GetFloat(), QAngle( -15, 20, -10 ), right + forward, ZOMBIE_BLOOD_LEFT_HAND ); + ClawAttack( GetClawAttackRange(), sk_zombie_dmg_one_slash.GetInt(), QAngle( -15, 20, -10 ), right + forward, ZOMBIE_BLOOD_LEFT_HAND ); return; } @@ -1559,7 +1562,7 @@ void CNPC_BaseZombie::HandleAnimEvent( animevent_t *pEvent ) QAngle qaPunch( 45, random->RandomInt(-5,5), random->RandomInt(-5,5) ); AngleVectors( GetLocalAngles(), &forward ); forward = forward * 200; - ClawAttack( GetClawAttackRange(), sk_zombie_dmg_one_slash.GetFloat(), qaPunch, forward, ZOMBIE_BLOOD_BOTH_HANDS ); + ClawAttack( GetClawAttackRange(), sk_zombie_dmg_one_slash.GetInt(), qaPunch, forward, ZOMBIE_BLOOD_BOTH_HANDS ); return; } @@ -1860,13 +1863,15 @@ int CNPC_BaseZombie::SelectSchedule ( void ) #ifdef DEBUG_ZOMBIES DevMsg("Wandering\n"); -#endif+ +#endif // Just lost track of our enemy. // Wander around a bit so we don't look like a dingus. return SCHED_ZOMBIE_WANDER_MEDIUM; } break; + default: + break; } return BaseClass::SelectSchedule(); @@ -2567,6 +2572,9 @@ Activity CNPC_BaseZombie::NPC_TranslateActivity( Activity baseAct ) // I'm on fire. Put ME out. return ( Activity )ACT_IDLE_ON_FIRE; } + + default: + break; } } diff --git a/dlls/hl2_dll/npc_BaseZombie.h b/dlls/hl2_dll/npc_BaseZombie.h index dcd75a19..be961047 100644 --- a/dlls/hl2_dll/npc_BaseZombie.h +++ b/dlls/hl2_dll/npc_BaseZombie.h @@ -185,7 +185,7 @@ public: virtual void SetZombieModel( void ) { }; virtual void BecomeTorso( const Vector &vecTorsoForce, const Vector &vecLegsForce ); virtual bool CanBecomeLiveTorso() { return false; } - virtual bool CNPC_BaseZombie::HeadcrabFits( CBaseAnimating *pCrab ); + virtual bool HeadcrabFits( CBaseAnimating *pCrab ); void ReleaseHeadcrab( const Vector &vecOrigin, const Vector &vecVelocity, bool fRemoveHead, bool fRagdollBody, bool fRagdollCrab = false ); void SetHeadcrabSpawnLocation( int iCrabAttachment, CBaseAnimating *pCrab ); diff --git a/dlls/hl2_dll/npc_PoisonZombie.cpp b/dlls/hl2_dll/npc_PoisonZombie.cpp index 20d69f07..ba1bf00a 100644 --- a/dlls/hl2_dll/npc_PoisonZombie.cpp +++ b/dlls/hl2_dll/npc_PoisonZombie.cpp @@ -712,7 +712,7 @@ void CNPC_PoisonZombie::HandleAnimEvent( animevent_t *pEvent ) QAngle qaPunch( 45, random->RandomInt(-5, 5), random->RandomInt(-5, 5) ); AngleVectors( GetLocalAngles(), &forward ); forward = forward * 200; - ClawAttack( GetClawAttackRange(), sk_zombie_poison_dmg_spit.GetFloat(), qaPunch, forward, ZOMBIE_BLOOD_BITE ); + ClawAttack( GetClawAttackRange(), sk_zombie_poison_dmg_spit.GetInt(), qaPunch, forward, ZOMBIE_BLOOD_BITE ); return; } diff --git a/dlls/hl2_dll/npc_alyx_episodic.cpp b/dlls/hl2_dll/npc_alyx_episodic.cpp index 1a9e7837..2463490b 100644 --- a/dlls/hl2_dll/npc_alyx_episodic.cpp +++ b/dlls/hl2_dll/npc_alyx_episodic.cpp @@ -158,6 +158,8 @@ bool CNPC_Alyx::FValidateHintType( CAI_Hint *pHint ) case HINT_WORLD_VISUALLY_INTERESTING_STEALTH: return true; break; + default: + break; } return BaseClass::FValidateHintType( pHint ); @@ -485,7 +487,7 @@ void CNPC_Alyx::ReadinessLevelChanged( int iPriorLevel ) { AIRL_STIMULATED, AIRL_RELAXED, ACT_IDLE, ACT_READINESS_STIMULATED_TO_RELAXED, } }; - for ( int i = 0; i < ARRAYSIZE( readinessTransitions ); i++ ) + for ( int i = 0; i < static_cast(ARRAYSIZE( readinessTransitions )); i++ ) { if ( GetIdealActivity() != readinessTransitions[i].requiredActivity ) continue; @@ -1678,6 +1680,9 @@ void CNPC_Alyx::BuildScheduleTestBits() SetCustomInterruptCondition( COND_ALYX_HAS_INTERACT_TARGET ); SetCustomInterruptCondition( COND_ALYX_CAN_INTERACT_WITH_TARGET ); break; + + default: + break; } } @@ -3175,6 +3180,9 @@ bool CNPC_Alyx::IsCrouchedActivity( Activity activity ) case ACT_RANGE_AIM_PISTOL_LOW: case ACT_RANGE_AIM_AR2_LOW: return true; + + default: + break; } return false; } diff --git a/dlls/hl2_dll/npc_antlion.cpp b/dlls/hl2_dll/npc_antlion.cpp index 8481efcf..b4a21213 100644 --- a/dlls/hl2_dll/npc_antlion.cpp +++ b/dlls/hl2_dll/npc_antlion.cpp @@ -498,7 +498,7 @@ void CNPC_Antlion::MeleeAttack( float distance, float damage, QAngle &viewPunch, return; } - CBaseEntity *pHurt = CheckTraceHullAttack( distance, -Vector(16,16,32), Vector(16,16,32), damage, DMG_SLASH, 5.0f ); + CBaseEntity *pHurt = CheckTraceHullAttack( distance, -Vector(16,16,32), Vector(16,16,32), static_cast(damage), DMG_SLASH, 5.0f ); if ( pHurt ) { @@ -1949,7 +1949,7 @@ void CNPC_Antlion::TraceAttack( const CTakeDamageInfo &info, const Vector &vecDi if ( newInfo.GetDamageType() & (DMG_CRUSH|DMG_PHYSGUN) ) { PainSound( newInfo ); - ApplyAbsVelocityImpulse( ( vecShoveDir * random->RandomInt( 500.0f, 1000.0f ) ) + Vector(0,0,64.0f) ); + ApplyAbsVelocityImpulse( ( vecShoveDir * random->RandomFloat( 500.0f, 1000.0f ) ) + Vector(0,0,64.0f) ); SetGroundEntity( NULL ); } @@ -1974,7 +1974,7 @@ void CNPC_Antlion::TraceAttack( const CTakeDamageInfo &info, const Vector &vecDi SetCondition( COND_ANTLION_FLIPPED ); } - ApplyAbsVelocityImpulse( ( vecShoveDir * random->RandomInt( 500.0f, 1000.0f ) ) + Vector(0,0,64.0f) ); + ApplyAbsVelocityImpulse( ( vecShoveDir * random->RandomFloat( 500.0f, 1000.0f ) ) + Vector(0,0,64.0f) ); SetGroundEntity( NULL ); } else @@ -1987,7 +1987,7 @@ void CNPC_Antlion::TraceAttack( const CTakeDamageInfo &info, const Vector &vecDi SetCondition( COND_ANTLION_FLIPPED ); //Get tossed! - ApplyAbsVelocityImpulse( ( vecShoveDir * random->RandomInt( 500.0f, 1000.0f ) ) + Vector(0,0,64.0f) ); + ApplyAbsVelocityImpulse( ( vecShoveDir * random->RandomFloat( 500.0f, 1000.0f ) ) + Vector(0,0,64.0f) ); SetGroundEntity( NULL ); } } diff --git a/dlls/hl2_dll/npc_antlionguard.cpp b/dlls/hl2_dll/npc_antlionguard.cpp index f6269c87..43166406 100644 --- a/dlls/hl2_dll/npc_antlionguard.cpp +++ b/dlls/hl2_dll/npc_antlionguard.cpp @@ -2561,7 +2561,7 @@ void CNPC_AntlionGuard::SummonAntlions( void ) // Only spawn up to our max count int iSpawnPoint = 0; - for ( int i = 0; (m_iNumLiveAntlions < ANTLIONGUARD_SUMMON_COUNT) && (iSpawnPoint < ARRAYSIZE(sAntlionSpawnPositions)); i++ ) + for ( int i = 0; (m_iNumLiveAntlions < ANTLIONGUARD_SUMMON_COUNT) && (iSpawnPoint < static_cast(ARRAYSIZE(sAntlionSpawnPositions))); i++ ) { // Determine spawn position for the antlion Vector vecSpawn = GetAbsOrigin() + ( sAntlionSpawnPositions[iSpawnPoint].flForward * vecForward ) + ( sAntlionSpawnPositions[iSpawnPoint].flRight * vecRight ); diff --git a/dlls/hl2_dll/npc_attackchopper.cpp b/dlls/hl2_dll/npc_attackchopper.cpp index 1abf2986..d6fcb8ac 100644 --- a/dlls/hl2_dll/npc_attackchopper.cpp +++ b/dlls/hl2_dll/npc_attackchopper.cpp @@ -1429,7 +1429,7 @@ void CNPC_AttackHelicopter::InputSetHealthFraction( inputdata_t &inputdata ) // Sets the health fraction, no damage effects if ( inputdata.value.Float() > 0 ) { - SetHealth( GetMaxHealth() * inputdata.value.Float() * 0.01f ); + SetHealth( static_cast(GetMaxHealth() * inputdata.value.Float() * 0.01f) ); } } @@ -2131,7 +2131,7 @@ bool CNPC_AttackHelicopter::DoGunCharging( ) case SHOOT_MODE_DEFAULT: { int nBurstCount = sk_helicopter_burstcount.GetInt(); - m_nRemainingBursts = random->RandomInt( nBurstCount, 2.0 * nBurstCount ); + m_nRemainingBursts = random->RandomInt( nBurstCount, 2 * nBurstCount ); m_flIdleTimeDelay = 0.1f * ( m_nRemainingBursts - nBurstCount ); } break; @@ -3262,7 +3262,7 @@ int CNPC_AttackHelicopter::OnTakeDamage_Alive( const CTakeDamageInfo &info ) { if ( nPrevHealth != GetMaxHealth() ) { - DropCorpse( info.GetDamage() ); + DropCorpse( static_cast(info.GetDamage()) ); } } @@ -4703,8 +4703,8 @@ int CGrenadeHelicopter::OnTakeDamage( const CTakeDamageInfo &info ) //------------------------------------------------------------------------------ void CGrenadeHelicopter::DoExplosion( const Vector &vecOrigin, const Vector &vecVelocity ) { - ExplosionCreate( GetAbsOrigin(), GetAbsAngles(), this, sk_helicopter_grenadedamage.GetFloat(), - sk_helicopter_grenaderadius.GetFloat(), (SF_ENVEXPLOSION_NOSPARKS|SF_ENVEXPLOSION_NODLIGHTS|SF_ENVEXPLOSION_NODECAL|SF_ENVEXPLOSION_NOFIREBALL|SF_ENVEXPLOSION_NOPARTICLES), + ExplosionCreate( GetAbsOrigin(), GetAbsAngles(), this, sk_helicopter_grenadedamage.GetInt(), + sk_helicopter_grenaderadius.GetInt(), (SF_ENVEXPLOSION_NOSPARKS|SF_ENVEXPLOSION_NODLIGHTS|SF_ENVEXPLOSION_NODECAL|SF_ENVEXPLOSION_NOFIREBALL|SF_ENVEXPLOSION_NOPARTICLES), sk_helicopter_grenadeforce.GetFloat() ); if ( GetShakeAmplitude() ) diff --git a/dlls/hl2_dll/npc_barnacle.cpp b/dlls/hl2_dll/npc_barnacle.cpp index d81d5705..86bbb5a5 100644 --- a/dlls/hl2_dll/npc_barnacle.cpp +++ b/dlls/hl2_dll/npc_barnacle.cpp @@ -360,7 +360,7 @@ void CNPC_Barnacle::PlayerHasIlluminatedNPC( CBasePlayer *pPlayer, float flDot ) // Create a sound to scare friendly allies away from the base on the barnacle if( IsAlive() ) { - CSoundEnt::InsertSound( SOUND_MOVE_AWAY | SOUND_CONTEXT_ALLIES_ONLY, m_vecTip, 60.0f, FLASHLIGHT_NPC_CHECK_INTERVAL ); + CSoundEnt::InsertSound( SOUND_MOVE_AWAY | SOUND_CONTEXT_ALLIES_ONLY, m_vecTip, 60, FLASHLIGHT_NPC_CHECK_INTERVAL ); } } @@ -1733,7 +1733,7 @@ void CNPC_Barnacle::SpawnDeathGibs( void ) bool bDroppedAny = false; // Drop a random number of gibs - for ( int i=0; i < ARRAYSIZE(m_szGibNames); i++ ) + for ( int i=0; i < static_cast(ARRAYSIZE(m_szGibNames)); i++ ) { if ( random->RandomInt( 0, 1 ) ) { @@ -1941,7 +1941,7 @@ void CNPC_Barnacle::Precache() PrecacheModel("models/barnacle.mdl"); // Precache all gibs - for ( int i=0; i < ARRAYSIZE(m_szGibNames); i++ ) + for ( int i=0; i < static_cast(ARRAYSIZE(m_szGibNames)); i++ ) { PrecacheModel( m_szGibNames[i] ); } diff --git a/dlls/hl2_dll/npc_breen.cpp b/dlls/hl2_dll/npc_breen.cpp index 738d2c82..df4daa55 100644 --- a/dlls/hl2_dll/npc_breen.cpp +++ b/dlls/hl2_dll/npc_breen.cpp @@ -69,7 +69,7 @@ void CNPC_Breen::HandleAnimEvent( animevent_t *pEvent ) //----------------------------------------------------------------------------- int CNPC_Breen::GetSoundInterests ( void ) { - return NULL; + return 0; } //----------------------------------------------------------------------------- diff --git a/dlls/hl2_dll/npc_citizen17.cpp b/dlls/hl2_dll/npc_citizen17.cpp index 524530f9..ff69775d 100644 --- a/dlls/hl2_dll/npc_citizen17.cpp +++ b/dlls/hl2_dll/npc_citizen17.cpp @@ -116,23 +116,23 @@ struct citizen_expression_list_t // Scared citizen_expression_list_t ScaredExpressions[STATES_WITH_EXPRESSIONS] = { - { "scenes/Expressions/citizen_scared_idle_01.vcd" }, - { "scenes/Expressions/citizen_scared_alert_01.vcd" }, - { "scenes/Expressions/citizen_scared_combat_01.vcd" }, + {{ "scenes/Expressions/citizen_scared_idle_01.vcd" }}, + {{ "scenes/Expressions/citizen_scared_alert_01.vcd" }}, + {{ "scenes/Expressions/citizen_scared_combat_01.vcd" }}, }; // Normal citizen_expression_list_t NormalExpressions[STATES_WITH_EXPRESSIONS] = { - { "scenes/Expressions/citizen_normal_idle_01.vcd" }, - { "scenes/Expressions/citizen_normal_alert_01.vcd" }, - { "scenes/Expressions/citizen_normal_combat_01.vcd" }, + {{ "scenes/Expressions/citizen_normal_idle_01.vcd" }}, + {{ "scenes/Expressions/citizen_normal_alert_01.vcd" }}, + {{ "scenes/Expressions/citizen_normal_combat_01.vcd" }}, }; // Angry citizen_expression_list_t AngryExpressions[STATES_WITH_EXPRESSIONS] = { - { "scenes/Expressions/citizen_angry_idle_01.vcd" }, - { "scenes/Expressions/citizen_angry_alert_01.vcd" }, - { "scenes/Expressions/citizen_angry_combat_01.vcd" }, + {{ "scenes/Expressions/citizen_angry_idle_01.vcd" }}, + {{ "scenes/Expressions/citizen_angry_alert_01.vcd" }}, + {{ "scenes/Expressions/citizen_angry_combat_01.vcd" }}, }; //----------------------------------------------------------------------------- @@ -374,15 +374,15 @@ void CNPC_Citizen::Precache() for ( int i = 0; i < STATES_WITH_EXPRESSIONS; i++ ) { - for ( int j = 0; j < ARRAYSIZE(ScaredExpressions[i].szExpressions); j++ ) + for ( int j = 0; j < static_cast(ARRAYSIZE(ScaredExpressions[i].szExpressions)); j++ ) { PrecacheInstancedScene( ScaredExpressions[i].szExpressions[j] ); } - for ( int j = 0; j < ARRAYSIZE(NormalExpressions[i].szExpressions); j++ ) + for ( int j = 0; j < static_cast(ARRAYSIZE(NormalExpressions[i].szExpressions)); j++ ) { PrecacheInstancedScene( NormalExpressions[i].szExpressions[j] ); } - for ( int j = 0; j < ARRAYSIZE(AngryExpressions[i].szExpressions); j++ ) + for ( int j = 0; j < static_cast(ARRAYSIZE(AngryExpressions[i].szExpressions)); j++ ) { PrecacheInstancedScene( AngryExpressions[i].szExpressions[j] ); } @@ -579,7 +579,7 @@ void CNPC_Citizen::SelectModel() Q_strncpy(szMapName, STRING(gpGlobals->mapname), sizeof(szMapName) ); Q_strlower(szMapName); - for ( int i = 0; i < ARRAYSIZE(CitizenTypeMappings); i++ ) + for ( int i = 0; i < static_cast(ARRAYSIZE(CitizenTypeMappings)); i++ ) { if ( Q_stristr( szMapName, CitizenTypeMappings[i].pszMapTag ) ) { @@ -613,7 +613,7 @@ void CNPC_Citizen::SelectModel() for ( i = 0; i < g_AI_Manager.NumAIs(); i++ ) { CNPC_Citizen *pCitizen = dynamic_cast(g_AI_Manager.AccessAIs()[i]); - if ( pCitizen && pCitizen != this && pCitizen->m_iHead >= 0 && pCitizen->m_iHead < ARRAYSIZE(g_ppszRandomHeads) ) + if ( pCitizen && pCitizen != this && pCitizen->m_iHead >= 0 && pCitizen->m_iHead < static_cast(ARRAYSIZE(g_ppszRandomHeads)) ) { headCounts[pCitizen->m_iHead]++; } @@ -622,7 +622,7 @@ void CNPC_Citizen::SelectModel() // Find all candidates CUtlVectorFixed candidates; - for ( i = 0; i < ARRAYSIZE(g_ppszRandomHeads); i++ ) + for ( i = 0; i < static_cast(ARRAYSIZE(g_ppszRandomHeads)); i++ ) { if ( !gender || g_ppszRandomHeads[i][0] == gender ) { @@ -666,7 +666,7 @@ void CNPC_Citizen::SelectModel() pszModelName++; if ( m_iHead == -1 ) { - for ( int i = 0; i < ARRAYSIZE(g_ppszRandomHeads); i++ ) + for ( int i = 0; i < static_cast(ARRAYSIZE(g_ppszRandomHeads)); i++ ) { if ( Q_stricmp( g_ppszRandomHeads[i], pszModelName ) == 0 ) { @@ -991,8 +991,8 @@ void CNPC_Citizen::PrescheduleThink() float b = bMin + ( bMax - bMin ) * fade; // THIS IS A PLACEHOLDER UNTIL WE HAVE A REAL DESIGN & ART -- DO NOT REMOVE - NDebugOverlay::Line( Vector( mins.x, GetAbsOrigin().y, GetAbsOrigin().z+1 ), Vector( maxs.x, GetAbsOrigin().y, GetAbsOrigin().z+1 ), r, g, b, false, .11 ); - NDebugOverlay::Line( Vector( GetAbsOrigin().x, mins.y, GetAbsOrigin().z+1 ), Vector( GetAbsOrigin().x, maxs.y, GetAbsOrigin().z+1 ), r, g, b, false, .11 ); + NDebugOverlay::Line( Vector( mins.x, GetAbsOrigin().y, GetAbsOrigin().z+1 ), Vector( maxs.x, GetAbsOrigin().y, GetAbsOrigin().z+1 ), static_cast(r), static_cast(g), static_cast(b), false, .11 ); + NDebugOverlay::Line( Vector( GetAbsOrigin().x, mins.y, GetAbsOrigin().z+1 ), Vector( GetAbsOrigin().x, maxs.y, GetAbsOrigin().z+1 ), static_cast(r), static_cast(g), static_cast(b), false, .11 ); } if( GetEnemy() && g_ai_citizen_show_enemy.GetBool() ) { @@ -1708,7 +1708,7 @@ void CNPC_Citizen::HandleAnimEvent( animevent_t *pEvent ) // If I have a name, make my weapon match it with "_weapon" appended if ( GetEntityName() != NULL_STRING ) { - pWeapon->SetName( AllocPooledString(UTIL_VarArgs("%s_weapon", GetEntityName())) ); + pWeapon->SetName( AllocPooledString(UTIL_VarArgs("%s_weapon", STRING(GetEntityName()))) ); } Weapon_Equip( pWeapon ); } @@ -1943,7 +1943,7 @@ Vector CNPC_Citizen::GetActualShootPosition( const Vector &shootOrigin ) 128, -128 }; - for ( int i = 0; i < ARRAYSIZE(flShotOffsets); i++ ) + for ( int i = 0; i < static_cast(ARRAYSIZE(flShotOffsets)); i++ ) { Vector vecTest = vecTarget + (vecRight * flShotOffsets[i]); // Add some random height to it @@ -3044,7 +3044,7 @@ int __cdecl SquadSortFunc( const SquadMemberInfo_t *pLeft, const SquadMemberInfo return 1; } - return ( pLeft->distSq - pRight->distSq ); + return static_cast(pLeft->distSq - pRight->distSq); } CAI_BaseNPC *CNPC_Citizen::GetSquadCommandRepresentative() @@ -3281,9 +3281,9 @@ bool CNPC_Citizen::ShouldHealTarget( CBaseEntity *pTarget, bool bActiveUse ) int requiredHealth; if ( bTargetIsPlayer ) - requiredHealth = pTarget->GetMaxHealth() - sk_citizen_heal_player.GetFloat(); + requiredHealth = pTarget->GetMaxHealth() - sk_citizen_heal_player.GetInt(); else - requiredHealth = pTarget->GetMaxHealth() * sk_citizen_heal_player_min_pct.GetFloat(); + requiredHealth = pTarget->GetMaxHealth() * sk_citizen_heal_player_min_pct.GetInt(); if ( ( pTarget->m_iHealth <= requiredHealth ) && IRelationType( pTarget ) == D_LI ) return true; diff --git a/dlls/hl2_dll/npc_combine_episodic.cpp b/dlls/hl2_dll/npc_combine_episodic.cpp index beebc664..9c3248e0 100644 --- a/dlls/hl2_dll/npc_combine_episodic.cpp +++ b/dlls/hl2_dll/npc_combine_episodic.cpp @@ -991,7 +991,7 @@ void CNPC_Combine::StartTask( const Task_t *pTask ) info.SetAttacker( this ); info.SetInflictor( this ); info.SetDamage( m_iHealth ); - info.SetDamageType( pTask->flTaskData ); + info.SetDamageType( static_cast(pTask->flTaskData) ); info.SetDamageForce( Vector( 0.1, 0.1, 0.1 ) ); TakeDamage( info ); @@ -1319,6 +1319,9 @@ Activity CNPC_Combine::NPC_TranslateActivity( Activity eNewActivity ) case ACT_RUN: eNewActivity = ACT_RUN_AIM; break; + + default: + break; } } @@ -1419,6 +1422,9 @@ void CNPC_Combine::AnnounceEnemyType( CBaseEntity *pEnemy ) case CLASS_BARNACLE: pSentenceName = "COMBINE_MONST_PARASITES"; break; + + default: + break; } m_Sentences.Speak( pSentenceName, SENTENCE_PRIORITY_HIGH ); @@ -1455,6 +1461,9 @@ void CNPC_Combine::AnnounceEnemyKill( CBaseEntity *pEnemy ) case CLASS_HEADCRAB: case CLASS_BARNACLE: break; + + default: + break; } m_Sentences.Speak( pSentenceName, SENTENCE_PRIORITY_HIGH ); @@ -1820,6 +1829,9 @@ int CNPC_Combine::SelectSchedule( void ) return nSched; } break; + + default: + break; } // no special cases here, call the base class diff --git a/dlls/hl2_dll/npc_combinedropship.cpp b/dlls/hl2_dll/npc_combinedropship.cpp index 775da5c3..eade4081 100644 --- a/dlls/hl2_dll/npc_combinedropship.cpp +++ b/dlls/hl2_dll/npc_combinedropship.cpp @@ -612,7 +612,7 @@ int CCombineDropshipContainer::OnTakeDamage( const CTakeDamageInfo &info ) if ( ShouldTriggerDamageEffect( nPrevHealth, MAX_EXPLOSIONS ) ) { - ExplosionCreate( dmgInfo.GetDamagePosition(), vec3_angle, this, 1000, 500.0f, + ExplosionCreate( dmgInfo.GetDamagePosition(), vec3_angle, this, 1000, 500, SF_ENVEXPLOSION_NODAMAGE | SF_ENVEXPLOSION_NOSPARKS | SF_ENVEXPLOSION_NODLIGHTS | SF_ENVEXPLOSION_NOSMOKE, 0 ); UTIL_ScreenShake( dmgInfo.GetDamagePosition(), 25.0, 150.0, 1.0, 750.0f, SHAKE_START ); @@ -630,7 +630,7 @@ int CCombineDropshipContainer::OnTakeDamage( const CTakeDamageInfo &info ) void CCombineDropshipContainer::AddSmokeTrail( const Vector &vecPos ) { // Start this trail out with a bang! - ExplosionCreate( vecPos, vec3_angle, this, 1000, 500.0f, SF_ENVEXPLOSION_NODAMAGE | + ExplosionCreate( vecPos, vec3_angle, this, 1000, 500, SF_ENVEXPLOSION_NODAMAGE | SF_ENVEXPLOSION_NOSPARKS | SF_ENVEXPLOSION_NODLIGHTS | SF_ENVEXPLOSION_NOSMOKE, 0 ); UTIL_ScreenShake( vecPos, 25.0, 150.0, 1.0, 750.0f, SHAKE_START ); @@ -2220,6 +2220,9 @@ void CNPC_CombineDropship::PrescheduleThink( void ) DoRotorWash(); } break; + + default: + break; } if ( !(CAI_BaseNPC::m_nDebugBits & bits_debugDisableAI) ) @@ -2289,7 +2292,7 @@ void CNPC_CombineDropship::SpawnTroop( void ) QAngle vecDeployEndAngles; m_hContainer->GetAttachment( m_iAttachmentTroopDeploy, vecDeployEndPoint, vecDeployEndAngles ); vecDeployEndPoint = GetDropoffFinishPosition( vecDeployEndPoint, NULL, vecNPCMins, vecNPCMaxs ); - CSoundEnt::InsertSound( SOUND_DANGER, vecDeployEndPoint, 120.0f, 2.0f, this ); + CSoundEnt::InsertSound( SOUND_DANGER, vecDeployEndPoint, 120, 2.0f, this ); // Make sure there are no NPCs on the spot trace_t tr; @@ -2794,7 +2797,7 @@ bool CNPC_CombineDropship::FireCannonRound( void ) //------------------------------------------------------------------------------ void CNPC_CombineDropship::DoImpactEffect( trace_t &tr, int nDamageType ) { - CSoundEnt::InsertSound( SOUND_DANGER | SOUND_CONTEXT_REACT_TO_SOURCE, tr.endpos, 120.0f, 0.3f, this ); + CSoundEnt::InsertSound( SOUND_DANGER | SOUND_CONTEXT_REACT_TO_SOURCE, tr.endpos, 120, 0.3f, this ); BaseClass::DoImpactEffect( tr, nDamageType ); } diff --git a/dlls/hl2_dll/npc_combinegunship.cpp b/dlls/hl2_dll/npc_combinegunship.cpp index 3f21032b..8f332060 100644 --- a/dlls/hl2_dll/npc_combinegunship.cpp +++ b/dlls/hl2_dll/npc_combinegunship.cpp @@ -957,7 +957,7 @@ void CNPC_CombineGunship::ManageWarningBeam( void ) g_pEffects->EnergySplash( tr2.endpos + vDir * 8, tr2.plane.normal, true ); } - g_pEffects->Sparks( tr2.endpos, 3.0f - (m_flGroundAttackTime-gpGlobals->curtime), 3.5f - (m_flGroundAttackTime-gpGlobals->curtime), &tr2.plane.normal ); + g_pEffects->Sparks( tr2.endpos, static_cast(3.0f - (m_flGroundAttackTime-gpGlobals->curtime)), static_cast(3.5f - (m_flGroundAttackTime-gpGlobals->curtime)), &tr2.plane.normal ); } } @@ -1958,7 +1958,7 @@ void CNPC_CombineGunship::BeginDestruct( void ) return; } - m_hRagdoll->SetName( AllocPooledString( UTIL_VarArgs("%s_ragdoll", GetEntityName()) ) ); + m_hRagdoll->SetName( AllocPooledString( UTIL_VarArgs("%s_ragdoll", STRING(GetEntityName())) ) ); // Tell the smoke trail to follow the ragdoll CreateSmokeTrail(); @@ -2899,16 +2899,16 @@ int CNPC_CombineGunship::OnTakeDamage_Alive( const CTakeDamageInfo &inputInfo ) int iHealthIncrements = sk_gunship_health_increments.GetInt(); if ( g_pGameRules->IsSkillLevel( SKILL_EASY ) ) { - iHealthIncrements = ceil( iHealthIncrements * 0.5 ); + iHealthIncrements = static_cast(ceil( iHealthIncrements * 0.5 )); } else if ( g_pGameRules->IsSkillLevel( SKILL_HARD ) ) { - iHealthIncrements = floor( iHealthIncrements * 1.5 ); + iHealthIncrements = static_cast(floor( iHealthIncrements * 1.5 )); } info.SetDamage( ( GetMaxHealth() / (float)iHealthIncrements ) + 1 ); // Find out which "stage" we're at in our health - int healthIncrement = iHealthIncrements - ( GetHealth() / (float)(( GetMaxHealth() / (float)iHealthIncrements )) ); + int healthIncrement = iHealthIncrements - ( GetHealth() / ( GetMaxHealth() / iHealthIncrements ) ); switch ( healthIncrement ) { case 1: diff --git a/dlls/hl2_dll/npc_combines.cpp b/dlls/hl2_dll/npc_combines.cpp index d50e3e0e..1cbde8e0 100644 --- a/dlls/hl2_dll/npc_combines.cpp +++ b/dlls/hl2_dll/npc_combines.cpp @@ -58,15 +58,15 @@ void CNPC_CombineS::Spawn( void ) if( IsElite() ) { // Stronger, tougher. - SetHealth( sk_combine_guard_health.GetFloat() ); - SetMaxHealth( sk_combine_guard_health.GetFloat() ); - SetKickDamage( sk_combine_guard_kick.GetFloat() ); + SetHealth( sk_combine_guard_health.GetInt() ); + SetMaxHealth( sk_combine_guard_health.GetInt() ); + SetKickDamage( sk_combine_guard_kick.GetInt() ); } else { - SetHealth( sk_combine_s_health.GetFloat() ); - SetMaxHealth( sk_combine_s_health.GetFloat() ); - SetKickDamage( sk_combine_s_kick.GetFloat() ); + SetHealth( sk_combine_s_health.GetInt() ); + SetMaxHealth( sk_combine_s_health.GetInt() ); + SetKickDamage( sk_combine_s_kick.GetInt() ); } CapabilitiesAdd( bits_CAP_ANIMATEDFACE ); @@ -356,7 +356,7 @@ bool CNPC_CombineS::IsHeavyDamage( const CTakeDamageInfo &info ) // Shotgun blasts where at least half the pellets hit me are heavy damage if ( info.GetDamageType() & DMG_BUCKSHOT ) { - int iHalfMax = sk_plr_dmg_buckshot.GetFloat() * sk_plr_num_shotgun_pellets.GetInt() * 0.5; + int iHalfMax = static_cast(sk_plr_dmg_buckshot.GetInt() * sk_plr_num_shotgun_pellets.GetInt() * 0.5); if ( info.GetDamage() >= iHalfMax ) return true; } diff --git a/dlls/hl2_dll/npc_cranedriver.cpp b/dlls/hl2_dll/npc_cranedriver.cpp index 24059c89..a76826e0 100644 --- a/dlls/hl2_dll/npc_cranedriver.cpp +++ b/dlls/hl2_dll/npc_cranedriver.cpp @@ -259,6 +259,9 @@ int CNPC_CraneDriver::SelectSchedule( void ) // We can't attack him, so if we don't have anything on the crane, grab something if ( m_hCrane->GetTotalMassOnCrane() == 0 ) return SCHED_CRANE_FIND_LARGE_OBJECT; + + default: + break; } return BaseClass::SelectSchedule(); diff --git a/dlls/hl2_dll/npc_crow.cpp b/dlls/hl2_dll/npc_crow.cpp index 7893a3be..337ec115 100644 --- a/dlls/hl2_dll/npc_crow.cpp +++ b/dlls/hl2_dll/npc_crow.cpp @@ -1223,6 +1223,9 @@ int CNPC_Crow::SelectSchedule( void ) // TODO: need idle flying behaviors! } + + default: + break; } return BaseClass::SelectSchedule(); diff --git a/dlls/hl2_dll/npc_eli.cpp b/dlls/hl2_dll/npc_eli.cpp index edef31f2..c1cf81c2 100644 --- a/dlls/hl2_dll/npc_eli.cpp +++ b/dlls/hl2_dll/npc_eli.cpp @@ -72,7 +72,7 @@ void CNPC_Eli::HandleAnimEvent( animevent_t *pEvent ) //----------------------------------------------------------------------------- int CNPC_Eli::GetSoundInterests ( void ) { - return NULL; + return 0; } //----------------------------------------------------------------------------- diff --git a/dlls/hl2_dll/npc_fastzombie.cpp b/dlls/hl2_dll/npc_fastzombie.cpp index d4ca9125..f468ec06 100644 --- a/dlls/hl2_dll/npc_fastzombie.cpp +++ b/dlls/hl2_dll/npc_fastzombie.cpp @@ -500,6 +500,9 @@ int CFastZombie::SelectSchedule ( void ) return SCHED_ZOMBIE_WANDER_MEDIUM; } break; + + default: + break; } return BaseClass::SelectSchedule(); @@ -553,7 +556,7 @@ void CFastZombie::PrescheduleThink( void ) int iPitch; m_flDistFactor = min( 1.0, 1 - flDistNoBBox / FASTZOMBIE_EXCITE_DIST ); - iPitch = FASTZOMBIE_MIN_PITCH + ( ( FASTZOMBIE_MAX_PITCH - FASTZOMBIE_MIN_PITCH ) * m_flDistFactor); + iPitch = static_cast(FASTZOMBIE_MIN_PITCH + ( ( FASTZOMBIE_MAX_PITCH - FASTZOMBIE_MIN_PITCH ) * m_flDistFactor)); ENVELOPE_CONTROLLER.SoundChangePitch( m_pMoanSound, iPitch, FASTZOMBIE_SOUND_UPDATE_FREQ ); } diff --git a/dlls/hl2_dll/npc_gman.cpp b/dlls/hl2_dll/npc_gman.cpp index 75b64354..36f21dad 100644 --- a/dlls/hl2_dll/npc_gman.cpp +++ b/dlls/hl2_dll/npc_gman.cpp @@ -84,7 +84,7 @@ void CNPC_GMan::HandleAnimEvent( animevent_t *pEvent ) //----------------------------------------------------------------------------- int CNPC_GMan::GetSoundInterests ( void ) { - return NULL; + return 0; } diff --git a/dlls/hl2_dll/npc_headcrab.cpp b/dlls/hl2_dll/npc_headcrab.cpp index 31c65ea8..d2d15c6c 100644 --- a/dlls/hl2_dll/npc_headcrab.cpp +++ b/dlls/hl2_dll/npc_headcrab.cpp @@ -971,7 +971,7 @@ int CBaseHeadcrab::CalcDamageInfo( CTakeDamageInfo *pInfo ) { pInfo->Set( this, this, sk_headcrab_melee_dmg.GetFloat(), DMG_SLASH ); CalculateMeleeDamageForce( pInfo, GetAbsVelocity(), GetAbsOrigin() ); - return pInfo->GetDamage(); + return static_cast(pInfo->GetDamage()); } //----------------------------------------------------------------------------- @@ -1842,7 +1842,7 @@ int CBaseHeadcrab::SelectSchedule( void ) return SCHED_HEADCRAB_UNHIDE; } - return m_bBurrowed ? SCHED_HEADCRAB_BURROW_WAIT : SCHED_IDLE_STAND; + return m_bBurrowed ? static_cast(SCHED_HEADCRAB_BURROW_WAIT) : static_cast(SCHED_IDLE_STAND); } if ( GetSpawnFlags() & SF_HEADCRAB_START_HANGING && IsHangingFromCeiling() == false ) @@ -1916,6 +1916,9 @@ int CBaseHeadcrab::SelectSchedule( void ) } break; } + + default: + break; } if ( HasCondition( COND_FLOATING_OFF_GROUND ) ) diff --git a/dlls/hl2_dll/npc_kleiner.cpp b/dlls/hl2_dll/npc_kleiner.cpp index db790d54..3b3cbfcc 100644 --- a/dlls/hl2_dll/npc_kleiner.cpp +++ b/dlls/hl2_dll/npc_kleiner.cpp @@ -70,7 +70,7 @@ void CNPC_Kleiner::HandleAnimEvent( animevent_t *pEvent ) //----------------------------------------------------------------------------- int CNPC_Kleiner::GetSoundInterests ( void ) { - return NULL; + return 0; } //----------------------------------------------------------------------------- diff --git a/dlls/hl2_dll/npc_launcher.cpp b/dlls/hl2_dll/npc_launcher.cpp index c247c5e0..c6eef1e6 100644 --- a/dlls/hl2_dll/npc_launcher.cpp +++ b/dlls/hl2_dll/npc_launcher.cpp @@ -376,7 +376,7 @@ void CNPC_Launcher::LauncherThink( void ) // Otherwise look for enemy to fire at else { - GetSenses()->Look(m_flMaxAttackDist); + GetSenses()->Look(static_cast(m_flMaxAttackDist)); CBaseEntity* pBestEnemy = BestEnemy(); if (pBestEnemy) diff --git a/dlls/hl2_dll/npc_manhack.cpp b/dlls/hl2_dll/npc_manhack.cpp index d1ddf8f4..c4dc5726 100644 --- a/dlls/hl2_dll/npc_manhack.cpp +++ b/dlls/hl2_dll/npc_manhack.cpp @@ -1589,7 +1589,7 @@ void CNPC_Manhack::Bump( CBaseEntity *pHitEntity, float flInterval, trace_t &tr if (moveVec.z < 0) { float floorZ = GetFloorZ(GetAbsOrigin()); - if (abs(GetAbsOrigin().z - floorZ) < 36) + if (abs(static_cast(GetAbsOrigin().z - floorZ)) < 36) { moveVec.z = 0; } @@ -1811,11 +1811,11 @@ void CNPC_Manhack::PlayFlySound(void) float flDistFactor; flDistFactor = min( 1.0, 1 - flEnemyDist / MANHACK_PITCH_DIST1 ); - iPitch1 = MANHACK_MIN_PITCH1 + ( ( MANHACK_MAX_PITCH1 - MANHACK_MIN_PITCH1 ) * flDistFactor); + iPitch1 = static_cast(MANHACK_MIN_PITCH1 + ( ( MANHACK_MAX_PITCH1 - MANHACK_MIN_PITCH1 ) * flDistFactor)); // NOTE: MANHACK_PITCH_DIST2 must be < MANHACK_PITCH_DIST1 flDistFactor = min( 1.0, 1 - flEnemyDist / MANHACK_PITCH_DIST2 ); - iPitch2 = MANHACK_MIN_PITCH2 + ( ( MANHACK_MAX_PITCH2 - MANHACK_MIN_PITCH2 ) * flDistFactor); + iPitch2 = static_cast(MANHACK_MIN_PITCH2 + ( ( MANHACK_MAX_PITCH2 - MANHACK_MIN_PITCH2 ) * flDistFactor)); m_nEnginePitch1 = iPitch1; m_flEnginePitch1Time = gpGlobals->curtime + 0.1f; diff --git a/dlls/hl2_dll/npc_metropolice.cpp b/dlls/hl2_dll/npc_metropolice.cpp index 0b61527e..55c64008 100644 --- a/dlls/hl2_dll/npc_metropolice.cpp +++ b/dlls/hl2_dll/npc_metropolice.cpp @@ -995,6 +995,9 @@ void CNPC_MetroPolice::AnnounceEnemyType( CBaseEntity *pEnemy ) case CLASS_BARNACLE: pSentenceName = "METROPOLICE_MONST_PARASITES"; break; + + default: + break; } m_Sentences.Speak( pSentenceName, SENTENCE_PRIORITY_HIGH ); @@ -1050,6 +1053,9 @@ void CNPC_MetroPolice::AnnounceEnemyKill( CBaseEntity *pEnemy ) case CLASS_BARNACLE: pSentenceName = "METROPOLICE_KILL_PARASITES"; break; + + default: + break; } m_Sentences.Speak( pSentenceName, SENTENCE_PRIORITY_HIGH ); @@ -1210,8 +1216,8 @@ void CNPC_MetroPolice::OnUpdateShotRegulator( ) float factor = (dist - MIN_PISTOL_MODIFY_DIST) / (MAX_PISTOL_MODIFY_DIST - MIN_PISTOL_MODIFY_DIST); - int nMinBurst = MIN_MIN_PISTOL_BURST + ( MAX_MIN_PISTOL_BURST - MIN_MIN_PISTOL_BURST ) * (1.0 - factor); - int nMaxBurst = MIN_MAX_PISTOL_BURST + ( MAX_MAX_PISTOL_BURST - MIN_MAX_PISTOL_BURST ) * (1.0 - factor); + int nMinBurst = static_cast(MIN_MIN_PISTOL_BURST + ( MAX_MIN_PISTOL_BURST - MIN_MIN_PISTOL_BURST ) * (1.0 - factor)); + int nMaxBurst = static_cast(MIN_MAX_PISTOL_BURST + ( MAX_MAX_PISTOL_BURST - MIN_MAX_PISTOL_BURST ) * (1.0 - factor)); float flMinRestInterval = MIN_MIN_PISTOL_REST_INTERVAL + ( MAX_MIN_PISTOL_REST_INTERVAL - MIN_MIN_PISTOL_REST_INTERVAL ) * factor; float flMaxRestInterval = MIN_MAX_PISTOL_REST_INTERVAL + ( MAX_MAX_PISTOL_REST_INTERVAL - MIN_MAX_PISTOL_REST_INTERVAL ) * factor; @@ -1675,7 +1681,7 @@ float CNPC_MetroPolice::AimBurstAtReactionTime( float flReactionTime, float flDi #define AIM_AT_SHOT_SPEED_COUNT 6 #define AIM_AT_SHOT_DIST_COUNT 6 -static int s_pShotCountFraction[AIM_AT_TIME_DIST_COUNT][AIM_AT_TIME_SPEED_COUNT] = +static float s_pShotCountFraction[AIM_AT_TIME_DIST_COUNT][AIM_AT_TIME_SPEED_COUNT] = { { 3.0f, 3.0f, 2.5f, 1.5f, 1.0f, 0.0f }, { 3.0f, 3.0f, 2.5f, 1.25f, 0.5f, 0.0f }, @@ -1706,7 +1712,7 @@ int CNPC_MetroPolice::AimBurstAtSetupHitCount( float flDistToTarget, float flCur flShotFactor += s_pShotCountFraction[nv][nu+1] * fu * (1.0f - fv); flShotFactor += s_pShotCountFraction[nv+1][nu+1] * fu * fv; - int nExtraShots = nHitCount * flShotFactor; + int nExtraShots = static_cast(nHitCount * flShotFactor); m_nMaxBurstHits += random->RandomInt( nExtraShots, nExtraShots + 1 ); return nExtraShots; } @@ -4169,6 +4175,9 @@ int CNPC_MetroPolice::SelectSchedule( void ) return nResult; } break; + + default: + break; } } @@ -4355,7 +4364,7 @@ void CNPC_MetroPolice::StartTask( const Task_t *pTask ) { case TASK_METROPOLICE_WAIT_FOR_SENTENCE: { - if ( FOkToMakeSound( pTask->flTaskData ) ) + if ( FOkToMakeSound( static_cast(pTask->flTaskData) ) ) { TaskComplete(); } @@ -4429,7 +4438,7 @@ void CNPC_MetroPolice::StartTask( const Task_t *pTask ) info.SetAttacker( this ); info.SetInflictor( this ); info.SetDamage( m_iHealth ); - info.SetDamageType( pTask->flTaskData ); + info.SetDamageType( static_cast(pTask->flTaskData) ); info.SetDamageForce( Vector( 0.1, 0.1, 0.1 ) ); TakeDamage( info ); @@ -4666,7 +4675,7 @@ void CNPC_MetroPolice::RunTask( const Task_t *pTask ) case TASK_METROPOLICE_WAIT_FOR_SENTENCE: { - if ( FOkToMakeSound( pTask->flTaskData ) ) + if ( FOkToMakeSound( static_cast(pTask->flTaskData) ) ) { TaskComplete(); } @@ -4853,7 +4862,7 @@ int CNPC_MetroPolice::OnTakeDamage_Alive( const CTakeDamageInfo &inputInfo ) { // Keep track of recent damage by my attacker. If it seems like we're // being killed, consider running off and hiding. - m_nRecentDamage += info.GetDamage(); + m_nRecentDamage += static_cast(info.GetDamage()); m_flRecentDamageTime = gpGlobals->curtime; } diff --git a/dlls/hl2_dll/npc_mossman.cpp b/dlls/hl2_dll/npc_mossman.cpp index 3bfcb8bd..ef186004 100644 --- a/dlls/hl2_dll/npc_mossman.cpp +++ b/dlls/hl2_dll/npc_mossman.cpp @@ -82,7 +82,7 @@ void CNPC_Mossman::HandleAnimEvent( animevent_t *pEvent ) //----------------------------------------------------------------------------- int CNPC_Mossman::GetSoundInterests ( void ) { - return NULL; + return 0; } //----------------------------------------------------------------------------- diff --git a/dlls/hl2_dll/npc_playercompanion.cpp b/dlls/hl2_dll/npc_playercompanion.cpp index d935c60f..e76aa57e 100644 --- a/dlls/hl2_dll/npc_playercompanion.cpp +++ b/dlls/hl2_dll/npc_playercompanion.cpp @@ -2702,7 +2702,7 @@ bool CNPC_PlayerCompanion::OverrideMove( float flInterval ) } } - for ( int i = 0; i < ARRAYSIZE(classNames); i++ ) + for ( int i = 0; i < static_cast(ARRAYSIZE(classNames)); i++ ) { if ( classNames[i] != NULL_STRING ) { diff --git a/dlls/hl2_dll/npc_rollermine.cpp b/dlls/hl2_dll/npc_rollermine.cpp index cfd4989a..f380d275 100644 --- a/dlls/hl2_dll/npc_rollermine.cpp +++ b/dlls/hl2_dll/npc_rollermine.cpp @@ -753,6 +753,8 @@ NPC_STATE CNPC_RollerMine::SelectIdealState( void ) return NPC_STATE_ALERT; } } + default: + break; } return BaseClass::SelectIdealState(); @@ -840,12 +842,12 @@ void CNPC_RollerMine::RunAI() if( m_bHackedByAlyx ) { // Scare combine - CSoundEnt::InsertSound( (SOUND_DANGER | SOUND_CONTEXT_COMBINE_ONLY | SOUND_CONTEXT_REACT_TO_SOURCE | SOUND_CONTEXT_DANGER_APPROACH), WorldSpaceCenter() + Vector( 0, 0, 32 ) + vecVelocity * 0.5f, 120.0f, 0.2f, this, SOUNDENT_CHANNEL_REPEATED_DANGER ); + CSoundEnt::InsertSound( (SOUND_DANGER | SOUND_CONTEXT_COMBINE_ONLY | SOUND_CONTEXT_REACT_TO_SOURCE | SOUND_CONTEXT_DANGER_APPROACH), WorldSpaceCenter() + Vector( 0, 0, 32 ) + vecVelocity * 0.5f, 120, 0.2f, this, SOUNDENT_CHANNEL_REPEATED_DANGER ); } else { // Scare player allies - CSoundEnt::InsertSound( (SOUND_DANGER | SOUND_CONTEXT_EXCLUDE_COMBINE | SOUND_CONTEXT_REACT_TO_SOURCE | SOUND_CONTEXT_DANGER_APPROACH), WorldSpaceCenter() + Vector( 0, 0, 32 ) + vecVelocity * 0.5f, 120.0f, 0.2f, this, SOUNDENT_CHANNEL_REPEATED_DANGER ); + CSoundEnt::InsertSound( (SOUND_DANGER | SOUND_CONTEXT_EXCLUDE_COMBINE | SOUND_CONTEXT_REACT_TO_SOURCE | SOUND_CONTEXT_DANGER_APPROACH), WorldSpaceCenter() + Vector( 0, 0, 32 ) + vecVelocity * 0.5f, 120, 0.2f, this, SOUNDENT_CHANNEL_REPEATED_DANGER ); } } @@ -2530,7 +2532,7 @@ void CNPC_RollerMine::Explode( void ) } else { - ExplosionCreate( WorldSpaceCenter(), GetLocalAngles(), this, expDamage, 128, true ); + ExplosionCreate( WorldSpaceCenter(), GetLocalAngles(), this, static_cast(expDamage), 128, true ); } CTakeDamageInfo info( this, this, 1, DMG_GENERIC ); @@ -2637,6 +2639,9 @@ void CNPC_RollerMine::UpdateRollingSound() case ROLL_SOUND_OFF: // no sound break; + + default: + break; } // start the new sound playing if necessary diff --git a/dlls/hl2_dll/npc_scanner.cpp b/dlls/hl2_dll/npc_scanner.cpp index 197da2a3..a5fb2797 100644 --- a/dlls/hl2_dll/npc_scanner.cpp +++ b/dlls/hl2_dll/npc_scanner.cpp @@ -1168,7 +1168,7 @@ void CNPC_CScanner::AttackDivebombCollide(float flInterval) if (vBounceVel.z < 0) { float floorZ = GetFloorZ(GetAbsOrigin()); - if (abs(GetAbsOrigin().z - floorZ) < 36) + if (abs(static_cast(GetAbsOrigin().z - floorZ)) < 36) { vBounceVel.z = 0; } @@ -1359,7 +1359,7 @@ void CNPC_CScanner::PlayFlySound(void) float speed = GetCurrentVelocity().Length(); float flVolume = 0.25f + (0.75f*(speed/GetMaxSpeed())); - int iPitch = min( 255, 80 + (20*(speed/GetMaxSpeed())) ); + int iPitch = static_cast(min( 255, 80 + (20*(speed/GetMaxSpeed())) )); //Update our pitch and volume based on our speed controller.SoundChangePitch( m_pEngineSound, iPitch, 0.1f ); @@ -2761,12 +2761,12 @@ void CNPC_CScanner::SpotlightUpdate(void) } else if (m_flSpotlightCurLength > m_flSpotlightMaxLength) { - m_hSpotlightTarget->SetRenderColorA( (1-((m_flSpotlightCurLength-m_flSpotlightMaxLength)/m_flSpotlightMaxLength)) ); + m_hSpotlightTarget->SetRenderColorA( static_cast((1-((m_flSpotlightCurLength-m_flSpotlightMaxLength)/m_flSpotlightMaxLength))) ); m_hSpotlight->SetFadeLength(m_flSpotlightMaxLength); } else { - m_hSpotlightTarget->SetRenderColorA( 1.0 ); + m_hSpotlightTarget->SetRenderColorA( 1 ); m_hSpotlight->SetFadeLength(m_flSpotlightCurLength); } @@ -2920,7 +2920,7 @@ void CNPC_CScanner::BlindFlashTarget( CBaseEntity *pTarget ) if ( tr.startsolid == false && tr.fraction == 1.0) { - color32 white = { 255, 255, 255, SCANNER_FLASH_MAX_VALUE * dotPr }; + color32 white = { 255, 255, 255, static_cast(SCANNER_FLASH_MAX_VALUE * dotPr) }; UTIL_ScreenFade( pTarget, white, 3.0, 0.5, FFADE_IN ); } } @@ -3891,6 +3891,9 @@ float CNPC_CScanner::GetGoalDistance( void ) case SCANNER_FLY_FOLLOW: return ( SCANNER_FOLLOW_DIST ); break; + + default: + break; } return 128.0f; diff --git a/dlls/hl2_dll/npc_stalker.cpp b/dlls/hl2_dll/npc_stalker.cpp index cd368a8f..6e65c31f 100644 --- a/dlls/hl2_dll/npc_stalker.cpp +++ b/dlls/hl2_dll/npc_stalker.cpp @@ -757,6 +757,9 @@ int CNPC_Stalker::SelectSchedule( void ) break; } + + default: + break; } // no special cases here, call the base class @@ -1261,7 +1264,7 @@ void CNPC_Stalker::HandleAnimEvent( animevent_t *pEvent ) { CBaseEntity *pHurt; - pHurt = CheckTraceHullAttack( 32, Vector(-16,-16,-16), Vector(16,16,16), sk_stalker_melee_dmg.GetFloat(), DMG_SLASH ); + pHurt = CheckTraceHullAttack( 32, Vector(-16,-16,-16), Vector(16,16,16), sk_stalker_melee_dmg.GetInt(), DMG_SLASH ); if ( pHurt ) { diff --git a/dlls/hl2_dll/npc_strider.cpp b/dlls/hl2_dll/npc_strider.cpp index 9f5f900d..dc2a8243 100644 --- a/dlls/hl2_dll/npc_strider.cpp +++ b/dlls/hl2_dll/npc_strider.cpp @@ -2704,15 +2704,15 @@ int CNPC_Strider::OnTakeDamage_Alive( const CTakeDamageInfo &info ) { if( g_pGameRules->IsSkillLevel(SKILL_EASY) ) { - damage = GetMaxHealth() / sk_strider_num_missiles1.GetFloat(); + damage = GetMaxHealth() / sk_strider_num_missiles1.GetInt(); } else if( g_pGameRules->IsSkillLevel(SKILL_HARD) ) { - damage = GetMaxHealth() / sk_strider_num_missiles3.GetFloat(); + damage = GetMaxHealth() / sk_strider_num_missiles3.GetInt(); } else // Medium, or unspecified { - damage = GetMaxHealth() / sk_strider_num_missiles2.GetFloat(); + damage = GetMaxHealth() / sk_strider_num_missiles2.GetInt(); } } @@ -2815,7 +2815,7 @@ int CNPC_Strider::TakeDamageFromCombineBall( const CTakeDamageInfo &info ) m_iHealth -= damage; - return damage; + return static_cast(damage); } //--------------------------------------------------------- diff --git a/dlls/hl2_dll/npc_turret_ceiling.cpp b/dlls/hl2_dll/npc_turret_ceiling.cpp index 5c14ba62..1c097aed 100644 --- a/dlls/hl2_dll/npc_turret_ceiling.cpp +++ b/dlls/hl2_dll/npc_turret_ceiling.cpp @@ -1,1127 +1,1127 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -//=============================================================================// - -#include "cbase.h" -#include "ai_basenpc.h" -#include "ai_senses.h" -#include "ai_memory.h" -#include "engine/IEngineSound.h" -#include "ammodef.h" -#include "Sprite.h" -#include "hl2_dll/hl2_player.h" -#include "soundenvelope.h" -#include "explode.h" -#include "IEffects.h" -#include "animation.h" -#include "basehlcombatweapon_shared.h" -#include "iservervehicle.h" - -// memdbgon must be the last include file in a .cpp file!!! -#include "tier0/memdbgon.h" - -//Debug visualization -ConVar g_debug_turret_ceiling( "g_debug_turret_ceiling", "0" ); - -#define CEILING_TURRET_MODEL "models/combine_turrets/ceiling_turret.mdl" -#define CEILING_TURRET_GLOW_SPRITE "sprites/glow1.vmt" -#define CEILING_TURRET_BC_YAW "aim_yaw" -#define CEILING_TURRET_BC_PITCH "aim_pitch" -#define CEILING_TURRET_RANGE 1500 -#define CEILING_TURRET_SPREAD VECTOR_CONE_2DEGREES -#define CEILING_TURRET_MAX_WAIT 5 -#define CEILING_TURRET_PING_TIME 1.0f //LPB!! - -#define CEILING_TURRET_VOICE_PITCH_LOW 45 -#define CEILING_TURRET_VOICE_PITCH_HIGH 100 - -//Aiming variables -#define CEILING_TURRET_MAX_NOHARM_PERIOD 0.0f -#define CEILING_TURRET_MAX_GRACE_PERIOD 3.0f - -//Spawnflags -#define SF_CEILING_TURRET_AUTOACTIVATE 0x00000020 -#define SF_CEILING_TURRET_STARTINACTIVE 0x00000040 -#define SF_CEILING_TURRET_NEVERRETIRE 0x00000080 -#define SF_CEILING_TURRET_OUT_OF_AMMO 0x00000100 - -//Heights -#define CEILING_TURRET_RETRACT_HEIGHT 24 -#define CEILING_TURRET_DEPLOY_HEIGHT 64 - -//Activities -int ACT_CEILING_TURRET_OPEN; -int ACT_CEILING_TURRET_CLOSE; -int ACT_CEILING_TURRET_OPEN_IDLE; -int ACT_CEILING_TURRET_CLOSED_IDLE; -int ACT_CEILING_TURRET_FIRE; -int ACT_CEILING_TURRET_DRYFIRE; - -//Turret states -enum turretState_e -{ - TURRET_SEARCHING, - TURRET_AUTO_SEARCHING, - TURRET_ACTIVE, - TURRET_DEPLOYING, - TURRET_RETIRING, - TURRET_DEAD, -}; - -//Eye states -enum eyeState_t -{ - TURRET_EYE_SEE_TARGET, //Sees the target, bright and big - TURRET_EYE_SEEKING_TARGET, //Looking for a target, blinking (bright) - TURRET_EYE_DORMANT, //Not active - TURRET_EYE_DEAD, //Completely invisible - TURRET_EYE_DISABLED, //Turned off, must be reactivated before it'll deploy again (completely invisible) -}; - -// -// Ceiling Turret -// - -class CNPC_CeilingTurret : public CAI_BaseNPC -{ - DECLARE_CLASS( CNPC_CeilingTurret, CAI_BaseNPC ); -public: - - CNPC_CeilingTurret( void ); - ~CNPC_CeilingTurret( void ); - - void Precache( void ); - void Spawn( void ); - - // Think functions - void Retire( void ); - void Deploy( void ); - void ActiveThink( void ); - void SearchThink( void ); - void AutoSearchThink( void ); - void DeathThink( void ); - - // Inputs - void InputToggle( inputdata_t &inputdata ); - void InputEnable( inputdata_t &inputdata ); - void InputDisable( inputdata_t &inputdata ); - - void SetLastSightTime(); - - float MaxYawSpeed( void ); - - int OnTakeDamage( const CTakeDamageInfo &inputInfo ); - - virtual bool CanBeAnEnemyOf( CBaseEntity *pEnemy ); - - Class_T Classify( void ) - { - if( m_bEnabled ) - return CLASS_COMBINE; - - return CLASS_NONE; - } - - bool FVisible( CBaseEntity *pEntity, int traceMask = MASK_OPAQUE, CBaseEntity **ppBlocker = NULL ); - - Vector EyeOffset( Activity nActivity ) - { - Vector vecEyeOffset(0,0,-64); - GetEyePosition( GetModelPtr(), vecEyeOffset ); - return vecEyeOffset; - } - - Vector EyePosition( void ) - { - return GetAbsOrigin() + EyeOffset(GetActivity()); - } - - Vector GetAttackSpread( CBaseCombatWeapon *pWeapon, CBaseEntity *pTarget ) - { - return VECTOR_CONE_5DEGREES * ((CBaseHLCombatWeapon::GetDefaultProficiencyValues())[ WEAPON_PROFICIENCY_PERFECT ].spreadscale); - } - -protected: - - bool PreThink( turretState_e state ); - void Shoot( const Vector &vecSrc, const Vector &vecDirToEnemy ); - void SetEyeState( eyeState_t state ); - void Ping( void ); - void Toggle( void ); - void Enable( void ); - void Disable( void ); - void SpinUp( void ); - void SpinDown( void ); - void SetHeight( float height ); - - bool UpdateFacing( void ); - - int m_iAmmoType; - int m_iMinHealthDmg; - - bool m_bAutoStart; - bool m_bActive; //Denotes the turret is deployed and looking for targets - bool m_bBlinkState; - bool m_bEnabled; //Denotes whether the turret is able to deploy or not - - float m_flShotTime; - float m_flLastSight; - float m_flPingTime; - - QAngle m_vecGoalAngles; - - CSprite *m_pEyeGlow; - - COutputEvent m_OnDeploy; - COutputEvent m_OnRetire; - COutputEvent m_OnTipped; - - DECLARE_DATADESC(); -}; - -//Datatable -BEGIN_DATADESC( CNPC_CeilingTurret ) - - DEFINE_FIELD( m_iAmmoType, FIELD_INTEGER ), - DEFINE_KEYFIELD( m_iMinHealthDmg, FIELD_INTEGER, "minhealthdmg" ), - DEFINE_FIELD( m_bAutoStart, FIELD_BOOLEAN ), - DEFINE_FIELD( m_bActive, FIELD_BOOLEAN ), - DEFINE_FIELD( m_bBlinkState, FIELD_BOOLEAN ), - DEFINE_FIELD( m_bEnabled, FIELD_BOOLEAN ), - DEFINE_FIELD( m_flShotTime, FIELD_TIME ), - DEFINE_FIELD( m_flLastSight, FIELD_TIME ), - DEFINE_FIELD( m_flPingTime, FIELD_TIME ), - DEFINE_FIELD( m_vecGoalAngles, FIELD_VECTOR ), - DEFINE_FIELD( m_pEyeGlow, FIELD_CLASSPTR ), - - DEFINE_THINKFUNC( Retire ), - DEFINE_THINKFUNC( Deploy ), - DEFINE_THINKFUNC( ActiveThink ), - DEFINE_THINKFUNC( SearchThink ), - DEFINE_THINKFUNC( AutoSearchThink ), - DEFINE_THINKFUNC( DeathThink ), - - // Inputs - DEFINE_INPUTFUNC( FIELD_VOID, "Toggle", InputToggle ), - DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ), - DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ), - - DEFINE_OUTPUT( m_OnDeploy, "OnDeploy" ), - DEFINE_OUTPUT( m_OnRetire, "OnRetire" ), - DEFINE_OUTPUT( m_OnTipped, "OnTipped" ), - -END_DATADESC() - -LINK_ENTITY_TO_CLASS( npc_turret_ceiling, CNPC_CeilingTurret ); - -//----------------------------------------------------------------------------- -// Constructor -//----------------------------------------------------------------------------- -CNPC_CeilingTurret::CNPC_CeilingTurret( void ) -{ - m_bActive = false; - m_pEyeGlow = NULL; - m_iAmmoType = -1; - m_iMinHealthDmg = 0; - m_bAutoStart = false; - m_flPingTime = 0; - m_flShotTime = 0; - m_flLastSight = 0; - m_bBlinkState = false; - m_bEnabled = false; - - m_vecGoalAngles.Init(); -} - -CNPC_CeilingTurret::~CNPC_CeilingTurret( void ) -{ -} - -//----------------------------------------------------------------------------- -// Purpose: Precache -//----------------------------------------------------------------------------- -void CNPC_CeilingTurret::Precache( void ) -{ - PrecacheModel( CEILING_TURRET_MODEL ); - PrecacheModel( CEILING_TURRET_GLOW_SPRITE ); - - // Activities - ADD_CUSTOM_ACTIVITY( CNPC_CeilingTurret, ACT_CEILING_TURRET_OPEN ); - ADD_CUSTOM_ACTIVITY( CNPC_CeilingTurret, ACT_CEILING_TURRET_CLOSE ); - ADD_CUSTOM_ACTIVITY( CNPC_CeilingTurret, ACT_CEILING_TURRET_CLOSED_IDLE ); - ADD_CUSTOM_ACTIVITY( CNPC_CeilingTurret, ACT_CEILING_TURRET_OPEN_IDLE ); - ADD_CUSTOM_ACTIVITY( CNPC_CeilingTurret, ACT_CEILING_TURRET_FIRE ); - ADD_CUSTOM_ACTIVITY( CNPC_CeilingTurret, ACT_CEILING_TURRET_DRYFIRE ); - - PrecacheScriptSound( "NPC_CeilingTurret.Retire" ); - PrecacheScriptSound( "NPC_CeilingTurret.Deploy" ); - PrecacheScriptSound( "NPC_CeilingTurret.Move" ); - PrecacheScriptSound( "NPC_CeilingTurret.Active" ); - PrecacheScriptSound( "NPC_CeilingTurret.Alert" ); - PrecacheScriptSound( "NPC_CeilingTurret.ShotSounds" ); - PrecacheScriptSound( "NPC_CeilingTurret.Ping" ); - PrecacheScriptSound( "NPC_CeilingTurret.Die" ); - - PrecacheScriptSound( "NPC_FloorTurret.DryFire" ); - - BaseClass::Precache(); -} - -//----------------------------------------------------------------------------- -// Purpose: Spawn the entity -//----------------------------------------------------------------------------- -void CNPC_CeilingTurret::Spawn( void ) -{ - Precache(); - - SetModel( CEILING_TURRET_MODEL ); - - BaseClass::Spawn(); - - m_HackedGunPos = Vector( 0, 0, 12.75 ); - SetViewOffset( EyeOffset( ACT_IDLE ) ); - m_flFieldOfView = 0.0f; - m_takedamage = DAMAGE_YES; - m_iHealth = 1000; - m_bloodColor = BLOOD_COLOR_MECH; - - SetSolid( SOLID_BBOX ); - AddSolidFlags( FSOLID_NOT_STANDABLE ); - - SetHeight( CEILING_TURRET_RETRACT_HEIGHT ); - - AddFlag( FL_AIMTARGET ); - AddEFlags( EFL_NO_DISSOLVE ); - - SetPoseParameter( CEILING_TURRET_BC_YAW, 0 ); - SetPoseParameter( CEILING_TURRET_BC_PITCH, 0 ); - - m_iAmmoType = GetAmmoDef()->Index( "AR2" ); - - //Create our eye sprite - m_pEyeGlow = CSprite::SpriteCreate( CEILING_TURRET_GLOW_SPRITE, GetLocalOrigin(), false ); - m_pEyeGlow->SetTransparency( kRenderTransAdd, 255, 0, 0, 128, kRenderFxNoDissipation ); - m_pEyeGlow->SetAttachment( this, 2 ); - - //Set our autostart state - m_bAutoStart = !!( m_spawnflags & SF_CEILING_TURRET_AUTOACTIVATE ); - m_bEnabled = ( ( m_spawnflags & SF_CEILING_TURRET_STARTINACTIVE ) == false ); - - //Do we start active? - if ( m_bAutoStart && m_bEnabled ) - { - SetThink( &CNPC_CeilingTurret::AutoSearchThink ); - SetEyeState( TURRET_EYE_DORMANT ); - } - else - { - SetEyeState( TURRET_EYE_DISABLED ); - } - - //Stagger our starting times - SetNextThink( gpGlobals->curtime + random->RandomFloat( 0.1f, 0.3f ) ); - - // Don't allow us to skip animation setup because our attachments are critical to us! - SetBoneCacheFlags( BCF_NO_ANIMATION_SKIP ); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -int CNPC_CeilingTurret::OnTakeDamage( const CTakeDamageInfo &inputInfo ) -{ - if ( !m_takedamage ) - return 0; - - CTakeDamageInfo info = inputInfo; - - if ( m_bActive == false ) - info.ScaleDamage( 0.1f ); - - // If attacker can't do at least the min required damage to us, don't take any damage from them - if ( info.GetDamage() < m_iMinHealthDmg ) - return 0; - - m_iHealth -= info.GetDamage(); - - if ( m_iHealth <= 0 ) - { - m_iHealth = 0; - m_takedamage = DAMAGE_NO; - - RemoveFlag( FL_NPC ); // why are they set in the first place??? - - //FIXME: This needs to throw a ragdoll gib or something other than animating the retraction -- jdw - - ExplosionCreate( GetAbsOrigin(), GetLocalAngles(), this, 100, 100, false ); - SetThink( &CNPC_CeilingTurret::DeathThink ); - - StopSound( "NPC_CeilingTurret.Alert" ); - - m_OnDamaged.FireOutput( info.GetInflictor(), this ); - - SetNextThink( gpGlobals->curtime + 0.1f ); - - return 0; - } - - return 1; -} - -//----------------------------------------------------------------------------- -// Purpose: Retract and stop attacking -//----------------------------------------------------------------------------- -void CNPC_CeilingTurret::Retire( void ) -{ - if ( PreThink( TURRET_RETIRING ) ) - return; - - //Level out the turret - m_vecGoalAngles = GetAbsAngles(); - SetNextThink( gpGlobals->curtime ); - - //Set ourselves to close - if ( GetActivity() != ACT_CEILING_TURRET_CLOSE ) - { - //Set our visible state to dormant - SetEyeState( TURRET_EYE_DORMANT ); - - SetActivity( (Activity) ACT_CEILING_TURRET_OPEN_IDLE ); - - //If we're done moving to our desired facing, close up - if ( UpdateFacing() == false ) - { - SetActivity( (Activity) ACT_CEILING_TURRET_CLOSE ); - EmitSound( "NPC_CeilingTurret.Retire" ); - - //Notify of the retraction - m_OnRetire.FireOutput( NULL, this ); - } - } - else if ( IsActivityFinished() ) - { - SetHeight( CEILING_TURRET_RETRACT_HEIGHT ); - - m_bActive = false; - m_flLastSight = 0; - - SetActivity( (Activity) ACT_CEILING_TURRET_CLOSED_IDLE ); - - //Go back to auto searching - if ( m_bAutoStart ) - { - SetThink( &CNPC_CeilingTurret::AutoSearchThink ); - SetNextThink( gpGlobals->curtime + 0.05f ); - } - else - { - //Set our visible state to dormant - SetEyeState( TURRET_EYE_DISABLED ); - SetThink( &CNPC_CeilingTurret::SUB_DoNothing ); - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: Deploy and start attacking -//----------------------------------------------------------------------------- -void CNPC_CeilingTurret::Deploy( void ) -{ - if ( PreThink( TURRET_DEPLOYING ) ) - return; - - m_vecGoalAngles = GetAbsAngles(); - - SetNextThink( gpGlobals->curtime ); - - //Show we've seen a target - SetEyeState( TURRET_EYE_SEE_TARGET ); - - //Open if we're not already - if ( GetActivity() != ACT_CEILING_TURRET_OPEN ) - { - m_bActive = true; - SetActivity( (Activity) ACT_CEILING_TURRET_OPEN ); - EmitSound( "NPC_CeilingTurret.Deploy" ); - - //Notify we're deploying - m_OnDeploy.FireOutput( NULL, this ); - } - - //If we're done, then start searching - if ( IsActivityFinished() ) - { - SetHeight( CEILING_TURRET_DEPLOY_HEIGHT ); - - SetActivity( (Activity) ACT_CEILING_TURRET_OPEN_IDLE ); - - m_flShotTime = gpGlobals->curtime + 1.0f; - - m_flPlaybackRate = 0; - SetThink( &CNPC_CeilingTurret::SearchThink ); - - EmitSound( "NPC_CeilingTurret.Move" ); - } - - SetLastSightTime(); -} - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -void CNPC_CeilingTurret::SetLastSightTime() -{ - if( HasSpawnFlags( SF_CEILING_TURRET_NEVERRETIRE ) ) - { - m_flLastSight = FLT_MAX; - } - else - { - m_flLastSight = gpGlobals->curtime + CEILING_TURRET_MAX_WAIT; - } -} - -//----------------------------------------------------------------------------- -// Purpose: Returns the speed at which the turret can face a target -//----------------------------------------------------------------------------- -float CNPC_CeilingTurret::MaxYawSpeed( void ) -{ - //TODO: Scale by difficulty? - return 360.0f; -} - -//----------------------------------------------------------------------------- -// Purpose: Causes the turret to face its desired angles -//----------------------------------------------------------------------------- -bool CNPC_CeilingTurret::UpdateFacing( void ) -{ - bool bMoved = false; - matrix3x4_t localToWorld; - - GetAttachment( LookupAttachment( "eyes" ), localToWorld ); - - Vector vecGoalDir; - AngleVectors( m_vecGoalAngles, &vecGoalDir ); - - Vector vecGoalLocalDir; - VectorIRotate( vecGoalDir, localToWorld, vecGoalLocalDir ); - - if ( g_debug_turret_ceiling.GetBool() ) - { - Vector vecMuzzle, vecMuzzleDir; - QAngle vecMuzzleAng; - - GetAttachment( "eyes", vecMuzzle, vecMuzzleAng ); - AngleVectors( vecMuzzleAng, &vecMuzzleDir ); - - NDebugOverlay::Cross3D( vecMuzzle, -Vector(2,2,2), Vector(2,2,2), 255, 255, 0, false, 0.05 ); - NDebugOverlay::Cross3D( vecMuzzle+(vecMuzzleDir*256), -Vector(2,2,2), Vector(2,2,2), 255, 255, 0, false, 0.05 ); - NDebugOverlay::Line( vecMuzzle, vecMuzzle+(vecMuzzleDir*256), 255, 255, 0, false, 0.05 ); - - NDebugOverlay::Cross3D( vecMuzzle, -Vector(2,2,2), Vector(2,2,2), 255, 0, 0, false, 0.05 ); - NDebugOverlay::Cross3D( vecMuzzle+(vecGoalDir*256), -Vector(2,2,2), Vector(2,2,2), 255, 0, 0, false, 0.05 ); - NDebugOverlay::Line( vecMuzzle, vecMuzzle+(vecGoalDir*256), 255, 0, 0, false, 0.05 ); - } - - QAngle vecGoalLocalAngles; - VectorAngles( vecGoalLocalDir, vecGoalLocalAngles ); - - // Update pitch - float flDiff = AngleNormalize( UTIL_ApproachAngle( vecGoalLocalAngles.x, 0.0, 0.1f * MaxYawSpeed() ) ); - - int iPose = LookupPoseParameter( CEILING_TURRET_BC_PITCH ); - SetPoseParameter( iPose, GetPoseParameter( iPose ) + ( flDiff / 1.5f ) ); - - if ( fabs( flDiff ) > 0.1f ) - { - bMoved = true; - } - - // Update yaw - flDiff = AngleNormalize( UTIL_ApproachAngle( vecGoalLocalAngles.y, 0.0, 0.1f * MaxYawSpeed() ) ); - - iPose = LookupPoseParameter( CEILING_TURRET_BC_YAW ); - SetPoseParameter( iPose, GetPoseParameter( iPose ) + ( flDiff / 1.5f ) ); - - if ( fabs( flDiff ) > 0.1f ) - { - bMoved = true; - } - - InvalidateBoneCache(); - - return bMoved; -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *pEntity - -// Output : Returns true on success, false on failure. -//----------------------------------------------------------------------------- -bool CNPC_CeilingTurret::FVisible( CBaseEntity *pEntity, int traceMask, CBaseEntity **ppBlocker ) -{ - CBaseEntity *pHitEntity = NULL; - if ( BaseClass::FVisible( pEntity, traceMask, &pHitEntity ) ) - return true; - - // If we hit something that's okay to hit anyway, still fire - if ( pHitEntity && pHitEntity->MyCombatCharacterPointer() ) - { - if (IRelationType(pHitEntity) == D_HT) - return true; - } - - if (ppBlocker) - { - *ppBlocker = pHitEntity; - } - - return false; -} - -//----------------------------------------------------------------------------- -// Purpose: Allows the turret to fire on targets if they're visible -//----------------------------------------------------------------------------- -void CNPC_CeilingTurret::ActiveThink( void ) -{ - //Allow descended classes a chance to do something before the think function - if ( PreThink( TURRET_ACTIVE ) ) - return; - - //Update our think time - SetNextThink( gpGlobals->curtime + 0.1f ); - - //If we've become inactive, go back to searching - if ( ( m_bActive == false ) || ( GetEnemy() == NULL ) ) - { - SetEnemy( NULL ); - SetLastSightTime(); - SetThink( &CNPC_CeilingTurret::SearchThink ); - m_vecGoalAngles = GetAbsAngles(); - return; - } - - //Get our shot positions - Vector vecMid = EyePosition(); - Vector vecMidEnemy = GetEnemy()->GetAbsOrigin(); - - //Store off our last seen location - UpdateEnemyMemory( GetEnemy(), vecMidEnemy ); - - //Look for our current enemy - bool bEnemyVisible = FInViewCone( GetEnemy() ) && FVisible( GetEnemy() ) && GetEnemy()->IsAlive(); - - //Calculate dir and dist to enemy - Vector vecDirToEnemy = vecMidEnemy - vecMid; - float flDistToEnemy = VectorNormalize( vecDirToEnemy ); - - //We want to look at the enemy's eyes so we don't jitter - Vector vecDirToEnemyEyes = GetEnemy()->WorldSpaceCenter() - vecMid; - VectorNormalize( vecDirToEnemyEyes ); - - QAngle vecAnglesToEnemy; - VectorAngles( vecDirToEnemyEyes, vecAnglesToEnemy ); - - //Draw debug info - if ( g_debug_turret_ceiling.GetBool() ) - { - NDebugOverlay::Cross3D( vecMid, -Vector(2,2,2), Vector(2,2,2), 0, 255, 0, false, 0.05 ); - NDebugOverlay::Cross3D( GetEnemy()->WorldSpaceCenter(), -Vector(2,2,2), Vector(2,2,2), 0, 255, 0, false, 0.05 ); - NDebugOverlay::Line( vecMid, GetEnemy()->WorldSpaceCenter(), 0, 255, 0, false, 0.05 ); - - NDebugOverlay::Cross3D( vecMid, -Vector(2,2,2), Vector(2,2,2), 0, 255, 0, false, 0.05 ); - NDebugOverlay::Cross3D( vecMidEnemy, -Vector(2,2,2), Vector(2,2,2), 0, 255, 0, false, 0.05 ); - NDebugOverlay::Line( vecMid, vecMidEnemy, 0, 255, 0, false, 0.05f ); - } - - //Current enemy is not visible - if ( ( bEnemyVisible == false ) || ( flDistToEnemy > CEILING_TURRET_RANGE )) - { - if ( m_flLastSight ) - { - m_flLastSight = gpGlobals->curtime + 0.5f; - } - else if ( gpGlobals->curtime > m_flLastSight ) - { - // Should we look for a new target? - ClearEnemyMemory(); - SetEnemy( NULL ); - SetLastSightTime(); - SetThink( &CNPC_CeilingTurret::SearchThink ); - m_vecGoalAngles = GetAbsAngles(); - - SpinDown(); - - return; - } - - bEnemyVisible = false; - } - - Vector vecMuzzle, vecMuzzleDir; - QAngle vecMuzzleAng; - - GetAttachment( "eyes", vecMuzzle, vecMuzzleAng ); - AngleVectors( vecMuzzleAng, &vecMuzzleDir ); - - if ( m_flShotTime < gpGlobals->curtime ) - { - //Fire the gun - if ( DotProduct( vecDirToEnemy, vecMuzzleDir ) >= 0.9848 ) // 10 degree slop - { - if ( m_spawnflags & SF_CEILING_TURRET_OUT_OF_AMMO ) - { - SetActivity( (Activity) ACT_CEILING_TURRET_DRYFIRE ); - } - else - { - SetActivity( (Activity) ACT_CEILING_TURRET_FIRE ); - } - - //Fire the weapon - Shoot( vecMuzzle, vecMuzzleDir ); - } - } - else - { - SetActivity( (Activity) ACT_CEILING_TURRET_OPEN_IDLE ); - } - - //If we can see our enemy, face it - if ( bEnemyVisible ) - { - m_vecGoalAngles.y = vecAnglesToEnemy.y; - m_vecGoalAngles.x = vecAnglesToEnemy.x; - } - - //Turn to face - UpdateFacing(); -} - -//----------------------------------------------------------------------------- -// Purpose: Target doesn't exist or has eluded us, so search for one -//----------------------------------------------------------------------------- -void CNPC_CeilingTurret::SearchThink( void ) -{ - //Allow descended classes a chance to do something before the think function - if ( PreThink( TURRET_SEARCHING ) ) - return; - - SetNextThink( gpGlobals->curtime + 0.05f ); - - SetActivity( (Activity) ACT_CEILING_TURRET_OPEN_IDLE ); - - //If our enemy has died, pick a new enemy - if ( ( GetEnemy() != NULL ) && ( GetEnemy()->IsAlive() == false ) ) - { - SetEnemy( NULL ); - } - - //Acquire the target - if ( GetEnemy() == NULL ) - { - GetSenses()->Look( CEILING_TURRET_RANGE ); - CBaseEntity *pEnemy = BestEnemy(); - if ( pEnemy ) - { - SetEnemy( pEnemy ); - } - } - - //If we've found a target, spin up the barrel and start to attack - if ( GetEnemy() != NULL ) - { - //Give players a grace period - if ( GetEnemy()->IsPlayer() ) - { - m_flShotTime = gpGlobals->curtime + 0.5f; - } - else - { - m_flShotTime = gpGlobals->curtime + 0.1f; - } - - m_flLastSight = 0; - SetThink( &CNPC_CeilingTurret::ActiveThink ); - SetEyeState( TURRET_EYE_SEE_TARGET ); - - SpinUp(); - EmitSound( "NPC_CeilingTurret.Active" ); - return; - } - - //Are we out of time and need to retract? - if ( gpGlobals->curtime > m_flLastSight ) - { - //Before we retrace, make sure that we are spun down. - m_flLastSight = 0; - SetThink( &CNPC_CeilingTurret::Retire ); - return; - } - - //Display that we're scanning - m_vecGoalAngles.x = 15.0f; - m_vecGoalAngles.y = GetAbsAngles().y + ( sin( gpGlobals->curtime * 2.0f ) * 45.0f ); - - //Turn and ping - UpdateFacing(); - Ping(); -} - -//----------------------------------------------------------------------------- -// Purpose: Watch for a target to wander into our view -//----------------------------------------------------------------------------- -void CNPC_CeilingTurret::AutoSearchThink( void ) -{ - //Allow descended classes a chance to do something before the think function - if ( PreThink( TURRET_AUTO_SEARCHING ) ) - return; - - //Spread out our thinking - SetNextThink( gpGlobals->curtime + random->RandomFloat( 0.2f, 0.4f ) ); - - //If the enemy is dead, find a new one - if ( ( GetEnemy() != NULL ) && ( GetEnemy()->IsAlive() == false ) ) - { - SetEnemy( NULL ); - } - - //Acquire Target - if ( GetEnemy() == NULL ) - { - GetSenses()->Look( CEILING_TURRET_RANGE ); - SetEnemy( BestEnemy() ); - } - - //Deploy if we've got an active target - if ( GetEnemy() != NULL ) - { - SetThink( &CNPC_CeilingTurret::Deploy ); - EmitSound( "NPC_CeilingTurret.Alert" ); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Fire! -//----------------------------------------------------------------------------- -void CNPC_CeilingTurret::Shoot( const Vector &vecSrc, const Vector &vecDirToEnemy ) -{ - if ( m_spawnflags & SF_CEILING_TURRET_OUT_OF_AMMO ) - { - EmitSound( "NPC_FloorTurret.DryFire"); - EmitSound( "NPC_CeilingTurret.Activate" ); - - if ( RandomFloat( 0, 1 ) > 0.7 ) - { - m_flShotTime = gpGlobals->curtime + random->RandomFloat( 0.5, 1.5 ); - } - else - { - m_flShotTime = gpGlobals->curtime; - } - return; - } - - FireBulletsInfo_t info; - - if ( GetEnemy() != NULL ) - { - Vector vecDir = GetActualShootTrajectory( vecSrc ); - - info.m_vecSrc = vecSrc; - info.m_vecDirShooting = vecDir; - info.m_iTracerFreq = 1; - info.m_iShots = 1; - info.m_pAttacker = this; - info.m_vecSpread = VECTOR_CONE_PRECALCULATED; - info.m_flDistance = MAX_COORD_RANGE; - info.m_iAmmoType = m_iAmmoType; - } - else - { - // Just shoot where you're facing! - Vector vecMuzzle, vecMuzzleDir; - QAngle vecMuzzleAng; - - info.m_vecSrc = vecSrc; - info.m_vecDirShooting = vecDirToEnemy; - info.m_iTracerFreq = 1; - info.m_iShots = 1; - info.m_pAttacker = this; - info.m_vecSpread = GetAttackSpread( NULL, NULL ); - info.m_flDistance = MAX_COORD_RANGE; - info.m_iAmmoType = m_iAmmoType; - } - - FireBullets( info ); - EmitSound( "NPC_CeilingTurret.ShotSounds" ); - DoMuzzleFlash(); -} - -//----------------------------------------------------------------------------- -// Purpose: Allows a generic think function before the others are called -// Input : state - which state the turret is currently in -//----------------------------------------------------------------------------- -bool CNPC_CeilingTurret::PreThink( turretState_e state ) -{ - CheckPVSCondition(); - - //Animate - StudioFrameAdvance(); - - //Do not interrupt current think function - return false; -} - -//----------------------------------------------------------------------------- -// Purpose: Sets the state of the glowing eye attached to the turret -// Input : state - state the eye should be in -//----------------------------------------------------------------------------- -void CNPC_CeilingTurret::SetEyeState( eyeState_t state ) -{ - //Must have a valid eye to affect - if ( m_pEyeGlow == NULL ) - return; - - //Set the state - switch( state ) - { - default: - case TURRET_EYE_SEE_TARGET: //Fade in and scale up - m_pEyeGlow->SetColor( 255, 0, 0 ); - m_pEyeGlow->SetBrightness( 164, 0.1f ); - m_pEyeGlow->SetScale( 0.4f, 0.1f ); - break; - - case TURRET_EYE_SEEKING_TARGET: //Ping-pongs - - //Toggle our state - m_bBlinkState = !m_bBlinkState; - m_pEyeGlow->SetColor( 255, 128, 0 ); - - if ( m_bBlinkState ) - { - //Fade up and scale up - m_pEyeGlow->SetScale( 0.25f, 0.1f ); - m_pEyeGlow->SetBrightness( 164, 0.1f ); - } - else - { - //Fade down and scale down - m_pEyeGlow->SetScale( 0.2f, 0.1f ); - m_pEyeGlow->SetBrightness( 64, 0.1f ); - } - - break; - - case TURRET_EYE_DORMANT: //Fade out and scale down - m_pEyeGlow->SetColor( 0, 255, 0 ); - m_pEyeGlow->SetScale( 0.1f, 0.5f ); - m_pEyeGlow->SetBrightness( 64, 0.5f ); - break; - - case TURRET_EYE_DEAD: //Fade out slowly - m_pEyeGlow->SetColor( 255, 0, 0 ); - m_pEyeGlow->SetScale( 0.1f, 3.0f ); - m_pEyeGlow->SetBrightness( 0, 3.0f ); - break; - - case TURRET_EYE_DISABLED: - m_pEyeGlow->SetColor( 0, 255, 0 ); - m_pEyeGlow->SetScale( 0.1f, 1.0f ); - m_pEyeGlow->SetBrightness( 0, 1.0f ); - break; - } -} - -//----------------------------------------------------------------------------- -// Purpose: Make a pinging noise so the player knows where we are -//----------------------------------------------------------------------------- -void CNPC_CeilingTurret::Ping( void ) -{ - //See if it's time to ping again - if ( m_flPingTime > gpGlobals->curtime ) - return; - - //Ping! - EmitSound( "NPC_CeilingTurret.Ping" ); - - SetEyeState( TURRET_EYE_SEEKING_TARGET ); - - m_flPingTime = gpGlobals->curtime + CEILING_TURRET_PING_TIME; -} - -//----------------------------------------------------------------------------- -// Purpose: Toggle the turret's state -//----------------------------------------------------------------------------- -void CNPC_CeilingTurret::Toggle( void ) -{ - //Toggle the state - if ( m_bEnabled ) - { - Disable(); - } - else - { - Enable(); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Enable the turret and deploy -//----------------------------------------------------------------------------- -void CNPC_CeilingTurret::Enable( void ) -{ - m_bEnabled = true; - - // if the turret is flagged as an autoactivate turret, re-enable its ability open self. - if ( m_spawnflags & SF_CEILING_TURRET_AUTOACTIVATE ) - { - m_bAutoStart = true; - } - - SetThink( &CNPC_CeilingTurret::Deploy ); - SetNextThink( gpGlobals->curtime + 0.05f ); -} - -//----------------------------------------------------------------------------- -// Purpose: Retire the turret until enabled again -//----------------------------------------------------------------------------- -void CNPC_CeilingTurret::Disable( void ) -{ - m_bEnabled = false; - m_bAutoStart = false; - - SetEnemy( NULL ); - SetThink( &CNPC_CeilingTurret::Retire ); - SetNextThink( gpGlobals->curtime + 0.1f ); -} - -//----------------------------------------------------------------------------- -// Purpose: Toggle the turret's state via input function -//----------------------------------------------------------------------------- -void CNPC_CeilingTurret::InputToggle( inputdata_t &inputdata ) -{ - Toggle(); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CNPC_CeilingTurret::InputEnable( inputdata_t &inputdata ) -{ - Enable(); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CNPC_CeilingTurret::InputDisable( inputdata_t &inputdata ) -{ - Disable(); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CNPC_CeilingTurret::SpinUp( void ) -{ -} - -#define CEILING_TURRET_MIN_SPIN_DOWN 1.0f - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CNPC_CeilingTurret::SpinDown( void ) -{ -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CNPC_CeilingTurret::DeathThink( void ) -{ - if ( PreThink( TURRET_DEAD ) ) - return; - - //Level out our angles - m_vecGoalAngles = GetAbsAngles(); - SetNextThink( gpGlobals->curtime ); - - if ( m_lifeState != LIFE_DEAD ) - { - m_lifeState = LIFE_DEAD; - - EmitSound( "NPC_CeilingTurret.Die" ); - - SetActivity( (Activity) ACT_CEILING_TURRET_CLOSE ); - } - - // lots of smoke - Vector pos; - CollisionProp()->RandomPointInBounds( vec3_origin, Vector( 1, 1, 1 ), &pos ); - - CBroadcastRecipientFilter filter; - - te->Smoke( filter, 0.0, &pos, g_sModelIndexSmoke, 2.5, 10 ); - - g_pEffects->Sparks( pos ); - - if ( IsActivityFinished() && ( UpdateFacing() == false ) ) - { - SetHeight( CEILING_TURRET_RETRACT_HEIGHT ); - - m_flPlaybackRate = 0; - SetThink( NULL ); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : height - -//----------------------------------------------------------------------------- -void CNPC_CeilingTurret::SetHeight( float height ) -{ - Vector forward, right, up; - AngleVectors( GetLocalAngles(), &forward, &right, &up ); - - Vector mins = ( forward * -16.0f ) + ( right * -16.0f ); - Vector maxs = ( forward * 16.0f ) + ( right * 16.0f ) + ( up * -height ); - - if ( mins.x > maxs.x ) - { - swap( mins.x, maxs.x ); - } - - if ( mins.y > maxs.y ) - { - swap( mins.y, maxs.y ); - } - - if ( mins.z > maxs.z ) - { - swap( mins.z, maxs.z ); - } - - SetCollisionBounds( mins, maxs ); -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *pEnemy - -// Output : Returns true on success, false on failure. -//----------------------------------------------------------------------------- -bool CNPC_CeilingTurret::CanBeAnEnemyOf( CBaseEntity *pEnemy ) -{ - // If we're out of ammo, make friendly companions ignore us - if ( m_spawnflags & SF_CEILING_TURRET_OUT_OF_AMMO ) - { - if ( pEnemy->Classify() == CLASS_PLAYER_ALLY_VITAL ) - return false; - } - - return BaseClass::CanBeAnEnemyOf( pEnemy ); -} \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include "cbase.h" +#include "ai_basenpc.h" +#include "ai_senses.h" +#include "ai_memory.h" +#include "engine/IEngineSound.h" +#include "ammodef.h" +#include "Sprite.h" +#include "hl2_dll/hl2_player.h" +#include "soundenvelope.h" +#include "explode.h" +#include "IEffects.h" +#include "animation.h" +#include "basehlcombatweapon_shared.h" +#include "iservervehicle.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +//Debug visualization +ConVar g_debug_turret_ceiling( "g_debug_turret_ceiling", "0" ); + +#define CEILING_TURRET_MODEL "models/combine_turrets/ceiling_turret.mdl" +#define CEILING_TURRET_GLOW_SPRITE "sprites/glow1.vmt" +#define CEILING_TURRET_BC_YAW "aim_yaw" +#define CEILING_TURRET_BC_PITCH "aim_pitch" +#define CEILING_TURRET_RANGE 1500 +#define CEILING_TURRET_SPREAD VECTOR_CONE_2DEGREES +#define CEILING_TURRET_MAX_WAIT 5 +#define CEILING_TURRET_PING_TIME 1.0f //LPB!! + +#define CEILING_TURRET_VOICE_PITCH_LOW 45 +#define CEILING_TURRET_VOICE_PITCH_HIGH 100 + +//Aiming variables +#define CEILING_TURRET_MAX_NOHARM_PERIOD 0.0f +#define CEILING_TURRET_MAX_GRACE_PERIOD 3.0f + +//Spawnflags +#define SF_CEILING_TURRET_AUTOACTIVATE 0x00000020 +#define SF_CEILING_TURRET_STARTINACTIVE 0x00000040 +#define SF_CEILING_TURRET_NEVERRETIRE 0x00000080 +#define SF_CEILING_TURRET_OUT_OF_AMMO 0x00000100 + +//Heights +#define CEILING_TURRET_RETRACT_HEIGHT 24 +#define CEILING_TURRET_DEPLOY_HEIGHT 64 + +//Activities +int ACT_CEILING_TURRET_OPEN; +int ACT_CEILING_TURRET_CLOSE; +int ACT_CEILING_TURRET_OPEN_IDLE; +int ACT_CEILING_TURRET_CLOSED_IDLE; +int ACT_CEILING_TURRET_FIRE; +int ACT_CEILING_TURRET_DRYFIRE; + +//Turret states +enum turretState_e +{ + TURRET_SEARCHING, + TURRET_AUTO_SEARCHING, + TURRET_ACTIVE, + TURRET_DEPLOYING, + TURRET_RETIRING, + TURRET_DEAD, +}; + +//Eye states +enum eyeState_t +{ + TURRET_EYE_SEE_TARGET, //Sees the target, bright and big + TURRET_EYE_SEEKING_TARGET, //Looking for a target, blinking (bright) + TURRET_EYE_DORMANT, //Not active + TURRET_EYE_DEAD, //Completely invisible + TURRET_EYE_DISABLED, //Turned off, must be reactivated before it'll deploy again (completely invisible) +}; + +// +// Ceiling Turret +// + +class CNPC_CeilingTurret : public CAI_BaseNPC +{ + DECLARE_CLASS( CNPC_CeilingTurret, CAI_BaseNPC ); +public: + + CNPC_CeilingTurret( void ); + ~CNPC_CeilingTurret( void ); + + void Precache( void ); + void Spawn( void ); + + // Think functions + void Retire( void ); + void Deploy( void ); + void ActiveThink( void ); + void SearchThink( void ); + void AutoSearchThink( void ); + void DeathThink( void ); + + // Inputs + void InputToggle( inputdata_t &inputdata ); + void InputEnable( inputdata_t &inputdata ); + void InputDisable( inputdata_t &inputdata ); + + void SetLastSightTime(); + + float MaxYawSpeed( void ); + + int OnTakeDamage( const CTakeDamageInfo &inputInfo ); + + virtual bool CanBeAnEnemyOf( CBaseEntity *pEnemy ); + + Class_T Classify( void ) + { + if( m_bEnabled ) + return CLASS_COMBINE; + + return CLASS_NONE; + } + + bool FVisible( CBaseEntity *pEntity, int traceMask = MASK_OPAQUE, CBaseEntity **ppBlocker = NULL ); + + Vector EyeOffset( Activity nActivity ) + { + Vector vecEyeOffset(0,0,-64); + GetEyePosition( GetModelPtr(), vecEyeOffset ); + return vecEyeOffset; + } + + Vector EyePosition( void ) + { + return GetAbsOrigin() + EyeOffset(GetActivity()); + } + + Vector GetAttackSpread( CBaseCombatWeapon *pWeapon, CBaseEntity *pTarget ) + { + return VECTOR_CONE_5DEGREES * ((CBaseHLCombatWeapon::GetDefaultProficiencyValues())[ WEAPON_PROFICIENCY_PERFECT ].spreadscale); + } + +protected: + + bool PreThink( turretState_e state ); + void Shoot( const Vector &vecSrc, const Vector &vecDirToEnemy ); + void SetEyeState( eyeState_t state ); + void Ping( void ); + void Toggle( void ); + void Enable( void ); + void Disable( void ); + void SpinUp( void ); + void SpinDown( void ); + void SetHeight( float height ); + + bool UpdateFacing( void ); + + int m_iAmmoType; + int m_iMinHealthDmg; + + bool m_bAutoStart; + bool m_bActive; //Denotes the turret is deployed and looking for targets + bool m_bBlinkState; + bool m_bEnabled; //Denotes whether the turret is able to deploy or not + + float m_flShotTime; + float m_flLastSight; + float m_flPingTime; + + QAngle m_vecGoalAngles; + + CSprite *m_pEyeGlow; + + COutputEvent m_OnDeploy; + COutputEvent m_OnRetire; + COutputEvent m_OnTipped; + + DECLARE_DATADESC(); +}; + +//Datatable +BEGIN_DATADESC( CNPC_CeilingTurret ) + + DEFINE_FIELD( m_iAmmoType, FIELD_INTEGER ), + DEFINE_KEYFIELD( m_iMinHealthDmg, FIELD_INTEGER, "minhealthdmg" ), + DEFINE_FIELD( m_bAutoStart, FIELD_BOOLEAN ), + DEFINE_FIELD( m_bActive, FIELD_BOOLEAN ), + DEFINE_FIELD( m_bBlinkState, FIELD_BOOLEAN ), + DEFINE_FIELD( m_bEnabled, FIELD_BOOLEAN ), + DEFINE_FIELD( m_flShotTime, FIELD_TIME ), + DEFINE_FIELD( m_flLastSight, FIELD_TIME ), + DEFINE_FIELD( m_flPingTime, FIELD_TIME ), + DEFINE_FIELD( m_vecGoalAngles, FIELD_VECTOR ), + DEFINE_FIELD( m_pEyeGlow, FIELD_CLASSPTR ), + + DEFINE_THINKFUNC( Retire ), + DEFINE_THINKFUNC( Deploy ), + DEFINE_THINKFUNC( ActiveThink ), + DEFINE_THINKFUNC( SearchThink ), + DEFINE_THINKFUNC( AutoSearchThink ), + DEFINE_THINKFUNC( DeathThink ), + + // Inputs + DEFINE_INPUTFUNC( FIELD_VOID, "Toggle", InputToggle ), + DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ), + DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ), + + DEFINE_OUTPUT( m_OnDeploy, "OnDeploy" ), + DEFINE_OUTPUT( m_OnRetire, "OnRetire" ), + DEFINE_OUTPUT( m_OnTipped, "OnTipped" ), + +END_DATADESC() + +LINK_ENTITY_TO_CLASS( npc_turret_ceiling, CNPC_CeilingTurret ); + +//----------------------------------------------------------------------------- +// Constructor +//----------------------------------------------------------------------------- +CNPC_CeilingTurret::CNPC_CeilingTurret( void ) +{ + m_bActive = false; + m_pEyeGlow = NULL; + m_iAmmoType = -1; + m_iMinHealthDmg = 0; + m_bAutoStart = false; + m_flPingTime = 0; + m_flShotTime = 0; + m_flLastSight = 0; + m_bBlinkState = false; + m_bEnabled = false; + + m_vecGoalAngles.Init(); +} + +CNPC_CeilingTurret::~CNPC_CeilingTurret( void ) +{ +} + +//----------------------------------------------------------------------------- +// Purpose: Precache +//----------------------------------------------------------------------------- +void CNPC_CeilingTurret::Precache( void ) +{ + PrecacheModel( CEILING_TURRET_MODEL ); + PrecacheModel( CEILING_TURRET_GLOW_SPRITE ); + + // Activities + ADD_CUSTOM_ACTIVITY( CNPC_CeilingTurret, ACT_CEILING_TURRET_OPEN ); + ADD_CUSTOM_ACTIVITY( CNPC_CeilingTurret, ACT_CEILING_TURRET_CLOSE ); + ADD_CUSTOM_ACTIVITY( CNPC_CeilingTurret, ACT_CEILING_TURRET_CLOSED_IDLE ); + ADD_CUSTOM_ACTIVITY( CNPC_CeilingTurret, ACT_CEILING_TURRET_OPEN_IDLE ); + ADD_CUSTOM_ACTIVITY( CNPC_CeilingTurret, ACT_CEILING_TURRET_FIRE ); + ADD_CUSTOM_ACTIVITY( CNPC_CeilingTurret, ACT_CEILING_TURRET_DRYFIRE ); + + PrecacheScriptSound( "NPC_CeilingTurret.Retire" ); + PrecacheScriptSound( "NPC_CeilingTurret.Deploy" ); + PrecacheScriptSound( "NPC_CeilingTurret.Move" ); + PrecacheScriptSound( "NPC_CeilingTurret.Active" ); + PrecacheScriptSound( "NPC_CeilingTurret.Alert" ); + PrecacheScriptSound( "NPC_CeilingTurret.ShotSounds" ); + PrecacheScriptSound( "NPC_CeilingTurret.Ping" ); + PrecacheScriptSound( "NPC_CeilingTurret.Die" ); + + PrecacheScriptSound( "NPC_FloorTurret.DryFire" ); + + BaseClass::Precache(); +} + +//----------------------------------------------------------------------------- +// Purpose: Spawn the entity +//----------------------------------------------------------------------------- +void CNPC_CeilingTurret::Spawn( void ) +{ + Precache(); + + SetModel( CEILING_TURRET_MODEL ); + + BaseClass::Spawn(); + + m_HackedGunPos = Vector( 0, 0, 12.75 ); + SetViewOffset( EyeOffset( ACT_IDLE ) ); + m_flFieldOfView = 0.0f; + m_takedamage = DAMAGE_YES; + m_iHealth = 1000; + m_bloodColor = BLOOD_COLOR_MECH; + + SetSolid( SOLID_BBOX ); + AddSolidFlags( FSOLID_NOT_STANDABLE ); + + SetHeight( CEILING_TURRET_RETRACT_HEIGHT ); + + AddFlag( FL_AIMTARGET ); + AddEFlags( EFL_NO_DISSOLVE ); + + SetPoseParameter( CEILING_TURRET_BC_YAW, 0 ); + SetPoseParameter( CEILING_TURRET_BC_PITCH, 0 ); + + m_iAmmoType = GetAmmoDef()->Index( "AR2" ); + + //Create our eye sprite + m_pEyeGlow = CSprite::SpriteCreate( CEILING_TURRET_GLOW_SPRITE, GetLocalOrigin(), false ); + m_pEyeGlow->SetTransparency( kRenderTransAdd, 255, 0, 0, 128, kRenderFxNoDissipation ); + m_pEyeGlow->SetAttachment( this, 2 ); + + //Set our autostart state + m_bAutoStart = !!( m_spawnflags & SF_CEILING_TURRET_AUTOACTIVATE ); + m_bEnabled = ( ( m_spawnflags & SF_CEILING_TURRET_STARTINACTIVE ) == false ); + + //Do we start active? + if ( m_bAutoStart && m_bEnabled ) + { + SetThink( &CNPC_CeilingTurret::AutoSearchThink ); + SetEyeState( TURRET_EYE_DORMANT ); + } + else + { + SetEyeState( TURRET_EYE_DISABLED ); + } + + //Stagger our starting times + SetNextThink( gpGlobals->curtime + random->RandomFloat( 0.1f, 0.3f ) ); + + // Don't allow us to skip animation setup because our attachments are critical to us! + SetBoneCacheFlags( BCF_NO_ANIMATION_SKIP ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +int CNPC_CeilingTurret::OnTakeDamage( const CTakeDamageInfo &inputInfo ) +{ + if ( !m_takedamage ) + return 0; + + CTakeDamageInfo info = inputInfo; + + if ( m_bActive == false ) + info.ScaleDamage( 0.1f ); + + // If attacker can't do at least the min required damage to us, don't take any damage from them + if ( info.GetDamage() < m_iMinHealthDmg ) + return 0; + + m_iHealth -= info.GetDamage(); + + if ( m_iHealth <= 0 ) + { + m_iHealth = 0; + m_takedamage = DAMAGE_NO; + + RemoveFlag( FL_NPC ); // why are they set in the first place??? + + //FIXME: This needs to throw a ragdoll gib or something other than animating the retraction -- jdw + + ExplosionCreate( GetAbsOrigin(), GetLocalAngles(), this, 100, 100, false ); + SetThink( &CNPC_CeilingTurret::DeathThink ); + + StopSound( "NPC_CeilingTurret.Alert" ); + + m_OnDamaged.FireOutput( info.GetInflictor(), this ); + + SetNextThink( gpGlobals->curtime + 0.1f ); + + return 0; + } + + return 1; +} + +//----------------------------------------------------------------------------- +// Purpose: Retract and stop attacking +//----------------------------------------------------------------------------- +void CNPC_CeilingTurret::Retire( void ) +{ + if ( PreThink( TURRET_RETIRING ) ) + return; + + //Level out the turret + m_vecGoalAngles = GetAbsAngles(); + SetNextThink( gpGlobals->curtime ); + + //Set ourselves to close + if ( GetActivity() != ACT_CEILING_TURRET_CLOSE ) + { + //Set our visible state to dormant + SetEyeState( TURRET_EYE_DORMANT ); + + SetActivity( (Activity) ACT_CEILING_TURRET_OPEN_IDLE ); + + //If we're done moving to our desired facing, close up + if ( UpdateFacing() == false ) + { + SetActivity( (Activity) ACT_CEILING_TURRET_CLOSE ); + EmitSound( "NPC_CeilingTurret.Retire" ); + + //Notify of the retraction + m_OnRetire.FireOutput( NULL, this ); + } + } + else if ( IsActivityFinished() ) + { + SetHeight( CEILING_TURRET_RETRACT_HEIGHT ); + + m_bActive = false; + m_flLastSight = 0; + + SetActivity( (Activity) ACT_CEILING_TURRET_CLOSED_IDLE ); + + //Go back to auto searching + if ( m_bAutoStart ) + { + SetThink( &CNPC_CeilingTurret::AutoSearchThink ); + SetNextThink( gpGlobals->curtime + 0.05f ); + } + else + { + //Set our visible state to dormant + SetEyeState( TURRET_EYE_DISABLED ); + SetThink( &CNPC_CeilingTurret::SUB_DoNothing ); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Deploy and start attacking +//----------------------------------------------------------------------------- +void CNPC_CeilingTurret::Deploy( void ) +{ + if ( PreThink( TURRET_DEPLOYING ) ) + return; + + m_vecGoalAngles = GetAbsAngles(); + + SetNextThink( gpGlobals->curtime ); + + //Show we've seen a target + SetEyeState( TURRET_EYE_SEE_TARGET ); + + //Open if we're not already + if ( GetActivity() != ACT_CEILING_TURRET_OPEN ) + { + m_bActive = true; + SetActivity( (Activity) ACT_CEILING_TURRET_OPEN ); + EmitSound( "NPC_CeilingTurret.Deploy" ); + + //Notify we're deploying + m_OnDeploy.FireOutput( NULL, this ); + } + + //If we're done, then start searching + if ( IsActivityFinished() ) + { + SetHeight( CEILING_TURRET_DEPLOY_HEIGHT ); + + SetActivity( (Activity) ACT_CEILING_TURRET_OPEN_IDLE ); + + m_flShotTime = gpGlobals->curtime + 1.0f; + + m_flPlaybackRate = 0; + SetThink( &CNPC_CeilingTurret::SearchThink ); + + EmitSound( "NPC_CeilingTurret.Move" ); + } + + SetLastSightTime(); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CNPC_CeilingTurret::SetLastSightTime() +{ + if( HasSpawnFlags( SF_CEILING_TURRET_NEVERRETIRE ) ) + { + m_flLastSight = FLT_MAX; + } + else + { + m_flLastSight = gpGlobals->curtime + CEILING_TURRET_MAX_WAIT; + } +} + +//----------------------------------------------------------------------------- +// Purpose: Returns the speed at which the turret can face a target +//----------------------------------------------------------------------------- +float CNPC_CeilingTurret::MaxYawSpeed( void ) +{ + //TODO: Scale by difficulty? + return 360.0f; +} + +//----------------------------------------------------------------------------- +// Purpose: Causes the turret to face its desired angles +//----------------------------------------------------------------------------- +bool CNPC_CeilingTurret::UpdateFacing( void ) +{ + bool bMoved = false; + matrix3x4_t localToWorld; + + GetAttachment( LookupAttachment( "eyes" ), localToWorld ); + + Vector vecGoalDir; + AngleVectors( m_vecGoalAngles, &vecGoalDir ); + + Vector vecGoalLocalDir; + VectorIRotate( vecGoalDir, localToWorld, vecGoalLocalDir ); + + if ( g_debug_turret_ceiling.GetBool() ) + { + Vector vecMuzzle, vecMuzzleDir; + QAngle vecMuzzleAng; + + GetAttachment( "eyes", vecMuzzle, vecMuzzleAng ); + AngleVectors( vecMuzzleAng, &vecMuzzleDir ); + + NDebugOverlay::Cross3D( vecMuzzle, -Vector(2,2,2), Vector(2,2,2), 255, 255, 0, false, 0.05 ); + NDebugOverlay::Cross3D( vecMuzzle+(vecMuzzleDir*256), -Vector(2,2,2), Vector(2,2,2), 255, 255, 0, false, 0.05 ); + NDebugOverlay::Line( vecMuzzle, vecMuzzle+(vecMuzzleDir*256), 255, 255, 0, false, 0.05 ); + + NDebugOverlay::Cross3D( vecMuzzle, -Vector(2,2,2), Vector(2,2,2), 255, 0, 0, false, 0.05 ); + NDebugOverlay::Cross3D( vecMuzzle+(vecGoalDir*256), -Vector(2,2,2), Vector(2,2,2), 255, 0, 0, false, 0.05 ); + NDebugOverlay::Line( vecMuzzle, vecMuzzle+(vecGoalDir*256), 255, 0, 0, false, 0.05 ); + } + + QAngle vecGoalLocalAngles; + VectorAngles( vecGoalLocalDir, vecGoalLocalAngles ); + + // Update pitch + float flDiff = AngleNormalize( UTIL_ApproachAngle( vecGoalLocalAngles.x, 0.0, 0.1f * MaxYawSpeed() ) ); + + int iPose = LookupPoseParameter( CEILING_TURRET_BC_PITCH ); + SetPoseParameter( iPose, GetPoseParameter( iPose ) + ( flDiff / 1.5f ) ); + + if ( fabs( flDiff ) > 0.1f ) + { + bMoved = true; + } + + // Update yaw + flDiff = AngleNormalize( UTIL_ApproachAngle( vecGoalLocalAngles.y, 0.0, 0.1f * MaxYawSpeed() ) ); + + iPose = LookupPoseParameter( CEILING_TURRET_BC_YAW ); + SetPoseParameter( iPose, GetPoseParameter( iPose ) + ( flDiff / 1.5f ) ); + + if ( fabs( flDiff ) > 0.1f ) + { + bMoved = true; + } + + InvalidateBoneCache(); + + return bMoved; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *pEntity - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CNPC_CeilingTurret::FVisible( CBaseEntity *pEntity, int traceMask, CBaseEntity **ppBlocker ) +{ + CBaseEntity *pHitEntity = NULL; + if ( BaseClass::FVisible( pEntity, traceMask, &pHitEntity ) ) + return true; + + // If we hit something that's okay to hit anyway, still fire + if ( pHitEntity && pHitEntity->MyCombatCharacterPointer() ) + { + if (IRelationType(pHitEntity) == D_HT) + return true; + } + + if (ppBlocker) + { + *ppBlocker = pHitEntity; + } + + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: Allows the turret to fire on targets if they're visible +//----------------------------------------------------------------------------- +void CNPC_CeilingTurret::ActiveThink( void ) +{ + //Allow descended classes a chance to do something before the think function + if ( PreThink( TURRET_ACTIVE ) ) + return; + + //Update our think time + SetNextThink( gpGlobals->curtime + 0.1f ); + + //If we've become inactive, go back to searching + if ( ( m_bActive == false ) || ( GetEnemy() == NULL ) ) + { + SetEnemy( NULL ); + SetLastSightTime(); + SetThink( &CNPC_CeilingTurret::SearchThink ); + m_vecGoalAngles = GetAbsAngles(); + return; + } + + //Get our shot positions + Vector vecMid = EyePosition(); + Vector vecMidEnemy = GetEnemy()->GetAbsOrigin(); + + //Store off our last seen location + UpdateEnemyMemory( GetEnemy(), vecMidEnemy ); + + //Look for our current enemy + bool bEnemyVisible = FInViewCone( GetEnemy() ) && FVisible( GetEnemy() ) && GetEnemy()->IsAlive(); + + //Calculate dir and dist to enemy + Vector vecDirToEnemy = vecMidEnemy - vecMid; + float flDistToEnemy = VectorNormalize( vecDirToEnemy ); + + //We want to look at the enemy's eyes so we don't jitter + Vector vecDirToEnemyEyes = GetEnemy()->WorldSpaceCenter() - vecMid; + VectorNormalize( vecDirToEnemyEyes ); + + QAngle vecAnglesToEnemy; + VectorAngles( vecDirToEnemyEyes, vecAnglesToEnemy ); + + //Draw debug info + if ( g_debug_turret_ceiling.GetBool() ) + { + NDebugOverlay::Cross3D( vecMid, -Vector(2,2,2), Vector(2,2,2), 0, 255, 0, false, 0.05 ); + NDebugOverlay::Cross3D( GetEnemy()->WorldSpaceCenter(), -Vector(2,2,2), Vector(2,2,2), 0, 255, 0, false, 0.05 ); + NDebugOverlay::Line( vecMid, GetEnemy()->WorldSpaceCenter(), 0, 255, 0, false, 0.05 ); + + NDebugOverlay::Cross3D( vecMid, -Vector(2,2,2), Vector(2,2,2), 0, 255, 0, false, 0.05 ); + NDebugOverlay::Cross3D( vecMidEnemy, -Vector(2,2,2), Vector(2,2,2), 0, 255, 0, false, 0.05 ); + NDebugOverlay::Line( vecMid, vecMidEnemy, 0, 255, 0, false, 0.05f ); + } + + //Current enemy is not visible + if ( ( bEnemyVisible == false ) || ( flDistToEnemy > CEILING_TURRET_RANGE )) + { + if ( m_flLastSight ) + { + m_flLastSight = gpGlobals->curtime + 0.5f; + } + else if ( gpGlobals->curtime > m_flLastSight ) + { + // Should we look for a new target? + ClearEnemyMemory(); + SetEnemy( NULL ); + SetLastSightTime(); + SetThink( &CNPC_CeilingTurret::SearchThink ); + m_vecGoalAngles = GetAbsAngles(); + + SpinDown(); + + return; + } + + bEnemyVisible = false; + } + + Vector vecMuzzle, vecMuzzleDir; + QAngle vecMuzzleAng; + + GetAttachment( "eyes", vecMuzzle, vecMuzzleAng ); + AngleVectors( vecMuzzleAng, &vecMuzzleDir ); + + if ( m_flShotTime < gpGlobals->curtime ) + { + //Fire the gun + if ( DotProduct( vecDirToEnemy, vecMuzzleDir ) >= 0.9848 ) // 10 degree slop + { + if ( m_spawnflags & SF_CEILING_TURRET_OUT_OF_AMMO ) + { + SetActivity( (Activity) ACT_CEILING_TURRET_DRYFIRE ); + } + else + { + SetActivity( (Activity) ACT_CEILING_TURRET_FIRE ); + } + + //Fire the weapon + Shoot( vecMuzzle, vecMuzzleDir ); + } + } + else + { + SetActivity( (Activity) ACT_CEILING_TURRET_OPEN_IDLE ); + } + + //If we can see our enemy, face it + if ( bEnemyVisible ) + { + m_vecGoalAngles.y = vecAnglesToEnemy.y; + m_vecGoalAngles.x = vecAnglesToEnemy.x; + } + + //Turn to face + UpdateFacing(); +} + +//----------------------------------------------------------------------------- +// Purpose: Target doesn't exist or has eluded us, so search for one +//----------------------------------------------------------------------------- +void CNPC_CeilingTurret::SearchThink( void ) +{ + //Allow descended classes a chance to do something before the think function + if ( PreThink( TURRET_SEARCHING ) ) + return; + + SetNextThink( gpGlobals->curtime + 0.05f ); + + SetActivity( (Activity) ACT_CEILING_TURRET_OPEN_IDLE ); + + //If our enemy has died, pick a new enemy + if ( ( GetEnemy() != NULL ) && ( GetEnemy()->IsAlive() == false ) ) + { + SetEnemy( NULL ); + } + + //Acquire the target + if ( GetEnemy() == NULL ) + { + GetSenses()->Look( CEILING_TURRET_RANGE ); + CBaseEntity *pEnemy = BestEnemy(); + if ( pEnemy ) + { + SetEnemy( pEnemy ); + } + } + + //If we've found a target, spin up the barrel and start to attack + if ( GetEnemy() != NULL ) + { + //Give players a grace period + if ( GetEnemy()->IsPlayer() ) + { + m_flShotTime = gpGlobals->curtime + 0.5f; + } + else + { + m_flShotTime = gpGlobals->curtime + 0.1f; + } + + m_flLastSight = 0; + SetThink( &CNPC_CeilingTurret::ActiveThink ); + SetEyeState( TURRET_EYE_SEE_TARGET ); + + SpinUp(); + EmitSound( "NPC_CeilingTurret.Active" ); + return; + } + + //Are we out of time and need to retract? + if ( gpGlobals->curtime > m_flLastSight ) + { + //Before we retrace, make sure that we are spun down. + m_flLastSight = 0; + SetThink( &CNPC_CeilingTurret::Retire ); + return; + } + + //Display that we're scanning + m_vecGoalAngles.x = 15.0f; + m_vecGoalAngles.y = GetAbsAngles().y + ( sin( gpGlobals->curtime * 2.0f ) * 45.0f ); + + //Turn and ping + UpdateFacing(); + Ping(); +} + +//----------------------------------------------------------------------------- +// Purpose: Watch for a target to wander into our view +//----------------------------------------------------------------------------- +void CNPC_CeilingTurret::AutoSearchThink( void ) +{ + //Allow descended classes a chance to do something before the think function + if ( PreThink( TURRET_AUTO_SEARCHING ) ) + return; + + //Spread out our thinking + SetNextThink( gpGlobals->curtime + random->RandomFloat( 0.2f, 0.4f ) ); + + //If the enemy is dead, find a new one + if ( ( GetEnemy() != NULL ) && ( GetEnemy()->IsAlive() == false ) ) + { + SetEnemy( NULL ); + } + + //Acquire Target + if ( GetEnemy() == NULL ) + { + GetSenses()->Look( CEILING_TURRET_RANGE ); + SetEnemy( BestEnemy() ); + } + + //Deploy if we've got an active target + if ( GetEnemy() != NULL ) + { + SetThink( &CNPC_CeilingTurret::Deploy ); + EmitSound( "NPC_CeilingTurret.Alert" ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Fire! +//----------------------------------------------------------------------------- +void CNPC_CeilingTurret::Shoot( const Vector &vecSrc, const Vector &vecDirToEnemy ) +{ + if ( m_spawnflags & SF_CEILING_TURRET_OUT_OF_AMMO ) + { + EmitSound( "NPC_FloorTurret.DryFire"); + EmitSound( "NPC_CeilingTurret.Activate" ); + + if ( RandomFloat( 0, 1 ) > 0.7 ) + { + m_flShotTime = gpGlobals->curtime + random->RandomFloat( 0.5, 1.5 ); + } + else + { + m_flShotTime = gpGlobals->curtime; + } + return; + } + + FireBulletsInfo_t info; + + if ( GetEnemy() != NULL ) + { + Vector vecDir = GetActualShootTrajectory( vecSrc ); + + info.m_vecSrc = vecSrc; + info.m_vecDirShooting = vecDir; + info.m_iTracerFreq = 1; + info.m_iShots = 1; + info.m_pAttacker = this; + info.m_vecSpread = VECTOR_CONE_PRECALCULATED; + info.m_flDistance = MAX_COORD_RANGE; + info.m_iAmmoType = m_iAmmoType; + } + else + { + // Just shoot where you're facing! + Vector vecMuzzle, vecMuzzleDir; + QAngle vecMuzzleAng; + + info.m_vecSrc = vecSrc; + info.m_vecDirShooting = vecDirToEnemy; + info.m_iTracerFreq = 1; + info.m_iShots = 1; + info.m_pAttacker = this; + info.m_vecSpread = GetAttackSpread( NULL, NULL ); + info.m_flDistance = MAX_COORD_RANGE; + info.m_iAmmoType = m_iAmmoType; + } + + FireBullets( info ); + EmitSound( "NPC_CeilingTurret.ShotSounds" ); + DoMuzzleFlash(); +} + +//----------------------------------------------------------------------------- +// Purpose: Allows a generic think function before the others are called +// Input : state - which state the turret is currently in +//----------------------------------------------------------------------------- +bool CNPC_CeilingTurret::PreThink( turretState_e state ) +{ + CheckPVSCondition(); + + //Animate + StudioFrameAdvance(); + + //Do not interrupt current think function + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: Sets the state of the glowing eye attached to the turret +// Input : state - state the eye should be in +//----------------------------------------------------------------------------- +void CNPC_CeilingTurret::SetEyeState( eyeState_t state ) +{ + //Must have a valid eye to affect + if ( m_pEyeGlow == NULL ) + return; + + //Set the state + switch( state ) + { + default: + case TURRET_EYE_SEE_TARGET: //Fade in and scale up + m_pEyeGlow->SetColor( 255, 0, 0 ); + m_pEyeGlow->SetBrightness( 164, 0.1f ); + m_pEyeGlow->SetScale( 0.4f, 0.1f ); + break; + + case TURRET_EYE_SEEKING_TARGET: //Ping-pongs + + //Toggle our state + m_bBlinkState = !m_bBlinkState; + m_pEyeGlow->SetColor( 255, 128, 0 ); + + if ( m_bBlinkState ) + { + //Fade up and scale up + m_pEyeGlow->SetScale( 0.25f, 0.1f ); + m_pEyeGlow->SetBrightness( 164, 0.1f ); + } + else + { + //Fade down and scale down + m_pEyeGlow->SetScale( 0.2f, 0.1f ); + m_pEyeGlow->SetBrightness( 64, 0.1f ); + } + + break; + + case TURRET_EYE_DORMANT: //Fade out and scale down + m_pEyeGlow->SetColor( 0, 255, 0 ); + m_pEyeGlow->SetScale( 0.1f, 0.5f ); + m_pEyeGlow->SetBrightness( 64, 0.5f ); + break; + + case TURRET_EYE_DEAD: //Fade out slowly + m_pEyeGlow->SetColor( 255, 0, 0 ); + m_pEyeGlow->SetScale( 0.1f, 3.0f ); + m_pEyeGlow->SetBrightness( 0, 3.0f ); + break; + + case TURRET_EYE_DISABLED: + m_pEyeGlow->SetColor( 0, 255, 0 ); + m_pEyeGlow->SetScale( 0.1f, 1.0f ); + m_pEyeGlow->SetBrightness( 0, 1.0f ); + break; + } +} + +//----------------------------------------------------------------------------- +// Purpose: Make a pinging noise so the player knows where we are +//----------------------------------------------------------------------------- +void CNPC_CeilingTurret::Ping( void ) +{ + //See if it's time to ping again + if ( m_flPingTime > gpGlobals->curtime ) + return; + + //Ping! + EmitSound( "NPC_CeilingTurret.Ping" ); + + SetEyeState( TURRET_EYE_SEEKING_TARGET ); + + m_flPingTime = gpGlobals->curtime + CEILING_TURRET_PING_TIME; +} + +//----------------------------------------------------------------------------- +// Purpose: Toggle the turret's state +//----------------------------------------------------------------------------- +void CNPC_CeilingTurret::Toggle( void ) +{ + //Toggle the state + if ( m_bEnabled ) + { + Disable(); + } + else + { + Enable(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Enable the turret and deploy +//----------------------------------------------------------------------------- +void CNPC_CeilingTurret::Enable( void ) +{ + m_bEnabled = true; + + // if the turret is flagged as an autoactivate turret, re-enable its ability open self. + if ( m_spawnflags & SF_CEILING_TURRET_AUTOACTIVATE ) + { + m_bAutoStart = true; + } + + SetThink( &CNPC_CeilingTurret::Deploy ); + SetNextThink( gpGlobals->curtime + 0.05f ); +} + +//----------------------------------------------------------------------------- +// Purpose: Retire the turret until enabled again +//----------------------------------------------------------------------------- +void CNPC_CeilingTurret::Disable( void ) +{ + m_bEnabled = false; + m_bAutoStart = false; + + SetEnemy( NULL ); + SetThink( &CNPC_CeilingTurret::Retire ); + SetNextThink( gpGlobals->curtime + 0.1f ); +} + +//----------------------------------------------------------------------------- +// Purpose: Toggle the turret's state via input function +//----------------------------------------------------------------------------- +void CNPC_CeilingTurret::InputToggle( inputdata_t &inputdata ) +{ + Toggle(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CNPC_CeilingTurret::InputEnable( inputdata_t &inputdata ) +{ + Enable(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CNPC_CeilingTurret::InputDisable( inputdata_t &inputdata ) +{ + Disable(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CNPC_CeilingTurret::SpinUp( void ) +{ +} + +#define CEILING_TURRET_MIN_SPIN_DOWN 1.0f + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CNPC_CeilingTurret::SpinDown( void ) +{ +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CNPC_CeilingTurret::DeathThink( void ) +{ + if ( PreThink( TURRET_DEAD ) ) + return; + + //Level out our angles + m_vecGoalAngles = GetAbsAngles(); + SetNextThink( gpGlobals->curtime ); + + if ( m_lifeState != LIFE_DEAD ) + { + m_lifeState = LIFE_DEAD; + + EmitSound( "NPC_CeilingTurret.Die" ); + + SetActivity( (Activity) ACT_CEILING_TURRET_CLOSE ); + } + + // lots of smoke + Vector pos; + CollisionProp()->RandomPointInBounds( vec3_origin, Vector( 1, 1, 1 ), &pos ); + + CBroadcastRecipientFilter filter; + + te->Smoke( filter, 0.0, &pos, g_sModelIndexSmoke, 2.5, 10 ); + + g_pEffects->Sparks( pos ); + + if ( IsActivityFinished() && ( UpdateFacing() == false ) ) + { + SetHeight( CEILING_TURRET_RETRACT_HEIGHT ); + + m_flPlaybackRate = 0; + SetThink( NULL ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : height - +//----------------------------------------------------------------------------- +void CNPC_CeilingTurret::SetHeight( float height ) +{ + Vector forward, right, up; + AngleVectors( GetLocalAngles(), &forward, &right, &up ); + + Vector mins = ( forward * -16.0f ) + ( right * -16.0f ); + Vector maxs = ( forward * 16.0f ) + ( right * 16.0f ) + ( up * -height ); + + if ( mins.x > maxs.x ) + { + swap( mins.x, maxs.x ); + } + + if ( mins.y > maxs.y ) + { + swap( mins.y, maxs.y ); + } + + if ( mins.z > maxs.z ) + { + swap( mins.z, maxs.z ); + } + + SetCollisionBounds( mins, maxs ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *pEnemy - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CNPC_CeilingTurret::CanBeAnEnemyOf( CBaseEntity *pEnemy ) +{ + // If we're out of ammo, make friendly companions ignore us + if ( m_spawnflags & SF_CEILING_TURRET_OUT_OF_AMMO ) + { + if ( pEnemy->Classify() == CLASS_PLAYER_ALLY_VITAL ) + return false; + } + + return BaseClass::CanBeAnEnemyOf( pEnemy ); +} diff --git a/dlls/hl2_dll/npc_turret_floor.cpp b/dlls/hl2_dll/npc_turret_floor.cpp index 258f608f..fed42de0 100644 --- a/dlls/hl2_dll/npc_turret_floor.cpp +++ b/dlls/hl2_dll/npc_turret_floor.cpp @@ -1955,3 +1955,4 @@ AI_BEGIN_CUSTOM_NPC( npc_turret_floor, CNPC_FloorTurret ) DECLARE_INTERACTION( g_interactionTurretStillStanding ); AI_END_CUSTOM_NPC() + diff --git a/dlls/hl2_dll/npc_vortigaunt.cpp b/dlls/hl2_dll/npc_vortigaunt.cpp index 0a79acba..e2c878bb 100644 --- a/dlls/hl2_dll/npc_vortigaunt.cpp +++ b/dlls/hl2_dll/npc_vortigaunt.cpp @@ -830,7 +830,7 @@ CBaseEntity* CNPC_Vortigaunt::Kick( void ) //------------------------------------------------------------------------------ void CNPC_Vortigaunt::Claw( int iAttachment) { - CBaseEntity *pHurt = CheckTraceHullAttack( 40, Vector(-10,-10,-10), Vector(10,10,10),sk_vortigaunt_dmg_claw.GetFloat(), DMG_SLASH ); + CBaseEntity *pHurt = CheckTraceHullAttack( 40, Vector(-10,-10,-10), Vector(10,10,10),sk_vortigaunt_dmg_claw.GetInt(), DMG_SLASH ); if ( pHurt ) { pHurt->ViewPunch( QAngle(5,0,-18) ); @@ -1744,6 +1744,9 @@ int CNPC_Vortigaunt::SelectSchedule( void ) } break; + + default: + break; } return BaseClass::SelectSchedule(); diff --git a/dlls/hl2_dll/prop_combine_ball.cpp b/dlls/hl2_dll/prop_combine_ball.cpp index 3855947b..1d3b02d3 100644 --- a/dlls/hl2_dll/prop_combine_ball.cpp +++ b/dlls/hl2_dll/prop_combine_ball.cpp @@ -1068,7 +1068,7 @@ void CPropCombineBall::DoExplosion( ) if( hl2_episodic.GetBool() ) { - CSoundEnt::InsertSound( SOUND_COMBAT | SOUND_CONTEXT_EXPLOSION, WorldSpaceCenter(), 180.0f, 0.25, this ); + CSoundEnt::InsertSound( SOUND_COMBAT | SOUND_CONTEXT_EXPLOSION, WorldSpaceCenter(), 180, 0.25, this ); } // Turn us off and wait because we need our trails to finish up properly diff --git a/dlls/hl2_dll/prop_thumper.cpp b/dlls/hl2_dll/prop_thumper.cpp index 13f9f4f9..1ae16b68 100644 --- a/dlls/hl2_dll/prop_thumper.cpp +++ b/dlls/hl2_dll/prop_thumper.cpp @@ -185,7 +185,7 @@ void CPropThumper::Thump ( void ) } EmitSound( "coast.thumper_dust" ); - CSoundEnt::InsertSound ( SOUND_THUMPER, GetAbsOrigin(), THUMPER_RADIUS * m_flPlaybackRate, THUMPER_SOUND_DURATION, this ); + CSoundEnt::InsertSound ( SOUND_THUMPER, GetAbsOrigin(), static_cast(THUMPER_RADIUS * m_flPlaybackRate), THUMPER_SOUND_DURATION, this ); if ( m_flPlaybackRate < 0.7f ) return; diff --git a/dlls/hl2_dll/proto_sniper.cpp b/dlls/hl2_dll/proto_sniper.cpp index 1dc0abe0..696de887 100644 --- a/dlls/hl2_dll/proto_sniper.cpp +++ b/dlls/hl2_dll/proto_sniper.cpp @@ -641,7 +641,7 @@ void CProtoSniper::LaserOn( const Vector &vecTarget, const Vector &vecDeviance ) m_pBeam->SetScrollRate( 0 ); m_pBeam->SetFadeLength( 0 ); m_pBeam->SetHaloTexture( sHaloSprite ); - m_pBeam->SetHaloScale( 4.0f ); + m_pBeam->SetHaloScale( 4 ); m_vecPaintStart = vecInitialAim; @@ -1100,7 +1100,7 @@ int CProtoSniper::IRelationPriority( CBaseEntity *pTarget ) if ( flDistance <= SNIPER_PROTECTION_MINDIST ) { float flBonus = (1.0 - (flDistance / SNIPER_PROTECTION_MINDIST)) * SNIPER_PROTECTION_PRIORITYCAP; - priority += flBonus; + priority += static_cast(flBonus); if ( m_debugOverlays & OVERLAY_NPC_SELECTED_BIT ) { diff --git a/dlls/hl2_dll/rotorwash.cpp b/dlls/hl2_dll/rotorwash.cpp index 561a331f..c8312b58 100644 --- a/dlls/hl2_dll/rotorwash.cpp +++ b/dlls/hl2_dll/rotorwash.cpp @@ -1,96 +1,96 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -//=============================================================================// - -#include "cbase.h" -#include "rotorwash.h" - -// memdbgon must be the last include file in a .cpp file!!! -#include "tier0/memdbgon.h" - -// ============================================== -// Rotorwash entity -// ============================================== - -class CRotorWashEmitter : public CBaseEntity -{ -public: - DECLARE_CLASS( CRotorWashEmitter, CBaseEntity ); - DECLARE_SERVERCLASS(); - DECLARE_DATADESC(); - - void SetAltitude( float flAltitude ) { m_flAltitude = flAltitude; } - void SetEmit( bool state ) { m_bEmit = state; } - void Spawn ( void ); - int ShouldTransmit( const CCheckTransmitInfo *pInfo ); - int UpdateTransmitState( void ); - -protected: - - CNetworkVar( bool, m_bEmit ); - CNetworkVar( float, m_flAltitude ); -}; - -IMPLEMENT_SERVERCLASS_ST( CRotorWashEmitter, DT_RotorWashEmitter ) - SendPropFloat(SENDINFO(m_flAltitude), -1, SPROP_NOSCALE ), -END_SEND_TABLE() - -LINK_ENTITY_TO_CLASS( env_rotorwash_emitter, CRotorWashEmitter ); - -BEGIN_DATADESC( CRotorWashEmitter ) - DEFINE_FIELD( m_bEmit, FIELD_BOOLEAN ), - DEFINE_KEYFIELD( m_flAltitude, FIELD_FLOAT, "altitude" ), -END_DATADESC() - -void CRotorWashEmitter::Spawn( void ) -{ - BaseClass::Spawn(); - SetEmit( false ); -} - -int CRotorWashEmitter::ShouldTransmit( const CCheckTransmitInfo *pInfo ) -{ - if ( GetParent() ) - { - return GetParent()->ShouldTransmit( pInfo ); - } - - return FL_EDICT_PVSCHECK; -} - -int CRotorWashEmitter::UpdateTransmitState( void ) -{ - if ( GetParent() ) - { - return SetTransmitState( FL_EDICT_FULLCHECK ); - } - - return SetTransmitState( FL_EDICT_PVSCHECK ); -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : &localOrigin - -// &localAngles - -// *pOwner - -// flAltitude - -// Output : CBaseEntity -//----------------------------------------------------------------------------- -CBaseEntity *CreateRotorWashEmitter( const Vector &localOrigin, const QAngle &localAngles, CBaseEntity *pOwner, float flAltitude ) -{ - CRotorWashEmitter *pEmitter = (CRotorWashEmitter *) CreateEntityByName( "env_rotorwash_emitter" ); - - if ( pEmitter == NULL ) - return NULL; - - pEmitter->SetAbsOrigin( localOrigin ); - pEmitter->SetAbsAngles( localAngles ); - pEmitter->FollowEntity( pOwner ); - - pEmitter->SetAltitude( flAltitude ); - pEmitter->SetEmit( false ); - - return pEmitter; -} \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "cbase.h" +#include "rotorwash.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +// ============================================== +// Rotorwash entity +// ============================================== + +class CRotorWashEmitter : public CBaseEntity +{ +public: + DECLARE_CLASS( CRotorWashEmitter, CBaseEntity ); + DECLARE_SERVERCLASS(); + DECLARE_DATADESC(); + + void SetAltitude( float flAltitude ) { m_flAltitude = flAltitude; } + void SetEmit( bool state ) { m_bEmit = state; } + void Spawn ( void ); + int ShouldTransmit( const CCheckTransmitInfo *pInfo ); + int UpdateTransmitState( void ); + +protected: + + CNetworkVar( bool, m_bEmit ); + CNetworkVar( float, m_flAltitude ); +}; + +IMPLEMENT_SERVERCLASS_ST( CRotorWashEmitter, DT_RotorWashEmitter ) + SendPropFloat(SENDINFO(m_flAltitude), -1, SPROP_NOSCALE ), +END_SEND_TABLE() + +LINK_ENTITY_TO_CLASS( env_rotorwash_emitter, CRotorWashEmitter ); + +BEGIN_DATADESC( CRotorWashEmitter ) + DEFINE_FIELD( m_bEmit, FIELD_BOOLEAN ), + DEFINE_KEYFIELD( m_flAltitude, FIELD_FLOAT, "altitude" ), +END_DATADESC() + +void CRotorWashEmitter::Spawn( void ) +{ + BaseClass::Spawn(); + SetEmit( false ); +} + +int CRotorWashEmitter::ShouldTransmit( const CCheckTransmitInfo *pInfo ) +{ + if ( GetParent() ) + { + return GetParent()->ShouldTransmit( pInfo ); + } + + return FL_EDICT_PVSCHECK; +} + +int CRotorWashEmitter::UpdateTransmitState( void ) +{ + if ( GetParent() ) + { + return SetTransmitState( FL_EDICT_FULLCHECK ); + } + + return SetTransmitState( FL_EDICT_PVSCHECK ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : &localOrigin - +// &localAngles - +// *pOwner - +// flAltitude - +// Output : CBaseEntity +//----------------------------------------------------------------------------- +CBaseEntity *CreateRotorWashEmitter( const Vector &localOrigin, const QAngle &localAngles, CBaseEntity *pOwner, float flAltitude ) +{ + CRotorWashEmitter *pEmitter = (CRotorWashEmitter *) CreateEntityByName( "env_rotorwash_emitter" ); + + if ( pEmitter == NULL ) + return NULL; + + pEmitter->SetAbsOrigin( localOrigin ); + pEmitter->SetAbsAngles( localAngles ); + pEmitter->FollowEntity( pOwner ); + + pEmitter->SetAltitude( flAltitude ); + pEmitter->SetEmit( false ); + + return pEmitter; +} diff --git a/dlls/hl2_dll/vehicle_airboat.cpp b/dlls/hl2_dll/vehicle_airboat.cpp index a6bae7a7..78a42a68 100644 --- a/dlls/hl2_dll/vehicle_airboat.cpp +++ b/dlls/hl2_dll/vehicle_airboat.cpp @@ -716,7 +716,7 @@ void CPropAirboat::ExitVehicle( int nRole ) ep.m_pSoundName = "Airboat_engine_stop"; ep.m_flVolume = controller.SoundGetVolume( m_pEngineSound ); ep.m_SoundLevel = SNDLVL_NORM; - ep.m_nPitch = controller.SoundGetPitch( m_pEngineSound ); + ep.m_nPitch = static_cast(controller.SoundGetPitch( m_pEngineSound )); EmitSound( filter, entindex(), ep ); @@ -910,7 +910,9 @@ void CPropAirboat::VPhysicsFriction( IPhysicsObject *pObject, float energy, int //----------------------------------------------------------------------------- // This fixes an optimizer bug that was causing targetYaw and targetPitch to // always be reported as clamped, thus disabling the gun. Ack! +#ifdef _MSC_VER #pragma optimize("", off) +#endif void CPropAirboat::AimGunAt( const Vector &aimPos, float flInterval ) { matrix3x4_t gunMatrix; @@ -960,8 +962,9 @@ void CPropAirboat::AimGunAt( const Vector &aimPos, float flInterval ) m_aimPitch = GetPoseParameter( AIRBOAT_GUN_PITCH ); m_aimYaw = GetPoseParameter( AIRBOAT_GUN_YAW ); } +#ifdef _MSC_VER #pragma optimize("", on) - +#endif //----------------------------------------------------------------------------- // Removes the ammo... @@ -1838,8 +1841,8 @@ void CPropAirboat::CreateDangerSounds( void ) // 0.7 seconds ahead vecSpot = vecStart + vecDir * (speed * 0.7f); - CSoundEnt::InsertSound( SOUND_DANGER, vecSpot, radius, soundDuration, this, SOUNDENT_CHANNEL_REPEATED_DANGER ); - CSoundEnt::InsertSound( SOUND_PHYSICS_DANGER, vecSpot, radius, soundDuration, this, SOUNDENT_CHANNEL_REPEATED_PHYSICS_DANGER ); + CSoundEnt::InsertSound( SOUND_DANGER, vecSpot, static_cast(radius), soundDuration, this, SOUNDENT_CHANNEL_REPEATED_DANGER ); + CSoundEnt::InsertSound( SOUND_PHYSICS_DANGER, vecSpot, static_cast(radius), soundDuration, this, SOUNDENT_CHANNEL_REPEATED_PHYSICS_DANGER ); //NDebugOverlay::Box(vecSpot, Vector(-radius,-radius,-radius),Vector(radius,radius,radius), 255, 0, 255, 0, soundDuration); #if 0 @@ -2057,7 +2060,7 @@ float CPropAirboat::CalculatePhysicsStressDamage( vphysics_objectstress_t *pStre //----------------------------------------------------------------------------- void CPropAirboat::ApplyStressDamage( IPhysicsObject *pPhysics ) { - vphysics_objectstress_t stressOut; + vphysics_objectstress_t stressOut = {0.0f, 0.0f, false, false}; float damage = CalculatePhysicsStressDamage( &stressOut, pPhysics ); if ( ( damage > 0 ) && ( m_hPlayer != NULL ) ) { diff --git a/dlls/hl2_dll/vehicle_apc.cpp b/dlls/hl2_dll/vehicle_apc.cpp index d553c8e8..4aadee66 100644 --- a/dlls/hl2_dll/vehicle_apc.cpp +++ b/dlls/hl2_dll/vehicle_apc.cpp @@ -293,7 +293,7 @@ Vector CPropAPC::BodyTarget( const Vector &posSrc, bool bNoisy ) void CPropAPC::AddSmokeTrail( const Vector &vecPos ) { // Start this trail out with a bang! - ExplosionCreate( vecPos, vec3_angle, this, 1000, 500.0f, SF_ENVEXPLOSION_NODAMAGE | + ExplosionCreate( vecPos, vec3_angle, this, 1000, 500, SF_ENVEXPLOSION_NODAMAGE | SF_ENVEXPLOSION_NOSPARKS | SF_ENVEXPLOSION_NODLIGHTS | SF_ENVEXPLOSION_NOSMOKE | SF_ENVEXPLOSION_NOFIREBALLSMOKE, 0 ); UTIL_ScreenShake( vecPos, 25.0, 150.0, 1.0, 750.0f, SHAKE_START ); @@ -351,7 +351,7 @@ void CPropAPC::AddSmokeTrail( const Vector &vecPos ) //------------------------------------------------------------------------------ void CPropAPC::ExplodeAndThrowChunk( const Vector &vecExplosionPos ) { - ExplosionCreate( vecExplosionPos, vec3_angle, this, 1000, 500.0f, + ExplosionCreate( vecExplosionPos, vec3_angle, this, 1000, 500, SF_ENVEXPLOSION_NODAMAGE | SF_ENVEXPLOSION_NOSPARKS | SF_ENVEXPLOSION_NODLIGHTS | SF_ENVEXPLOSION_NOSMOKE | SF_ENVEXPLOSION_NOFIREBALLSMOKE, 0 ); UTIL_ScreenShake( vecExplosionPos, 25.0, 150.0, 1.0, 750.0f, SHAKE_START ); diff --git a/dlls/hl2_dll/vehicle_jeep.cpp b/dlls/hl2_dll/vehicle_jeep.cpp index 26e8d7fb..ef40031f 100644 --- a/dlls/hl2_dll/vehicle_jeep.cpp +++ b/dlls/hl2_dll/vehicle_jeep.cpp @@ -1387,8 +1387,8 @@ void CPropJeep::CreateDangerSounds( void ) const float radius = speed * 0.4; // 0.3 seconds ahead of the jeep vecSpot = vecStart + vecDir * (speed * 0.3f); - CSoundEnt::InsertSound( SOUND_DANGER, vecSpot, radius, soundDuration, this, 0 ); - CSoundEnt::InsertSound( SOUND_PHYSICS_DANGER, vecSpot, radius, soundDuration, this, 1 ); + CSoundEnt::InsertSound( SOUND_DANGER, vecSpot, static_cast(radius), soundDuration, this, 0 ); + CSoundEnt::InsertSound( SOUND_PHYSICS_DANGER, vecSpot, static_cast(radius), soundDuration, this, 1 ); //NDebugOverlay::Box(vecSpot, Vector(-radius,-radius,-radius),Vector(radius,radius,radius), 255, 0, 255, 0, soundDuration); #if 0 diff --git a/dlls/hl2_dll/vehicle_prisoner_pod.cpp b/dlls/hl2_dll/vehicle_prisoner_pod.cpp index 44faaf40..d54b36f2 100644 --- a/dlls/hl2_dll/vehicle_prisoner_pod.cpp +++ b/dlls/hl2_dll/vehicle_prisoner_pod.cpp @@ -41,10 +41,10 @@ class CPropVehiclePrisonerPod; // Pod bones that have physics followers -static const char *pPodFollowerBoneNames[] = +/*static const char *pPodFollowerBoneNames[] = { "base", -}; +};*/ //----------------------------------------------------------------------------- diff --git a/dlls/hl2_dll/weapon_357.cpp b/dlls/hl2_dll/weapon_357.cpp index c74665ea..d2152995 100644 --- a/dlls/hl2_dll/weapon_357.cpp +++ b/dlls/hl2_dll/weapon_357.cpp @@ -6,10 +6,10 @@ //=============================================================================// #include "cbase.h" -#include "NPCEvent.h" +#include "npcevent.h" #include "basehlcombatweapon.h" #include "basecombatcharacter.h" -#include "AI_BaseNPC.h" +#include "ai_basenpc.h" #include "player.h" #include "gamerules.h" #include "in_buttons.h" diff --git a/dlls/hl2_dll/weapon_ar2.cpp b/dlls/hl2_dll/weapon_ar2.cpp index 1281649c..c749d9ba 100644 --- a/dlls/hl2_dll/weapon_ar2.cpp +++ b/dlls/hl2_dll/weapon_ar2.cpp @@ -7,16 +7,16 @@ #include "cbase.h" #include "basecombatweapon.h" -#include "NPCevent.h" +#include "npcevent.h" #include "basecombatcharacter.h" -#include "AI_BaseNPC.h" +#include "ai_basenpc.h" #include "player.h" #include "weapon_ar2.h" #include "grenade_ar2.h" #include "gamerules.h" #include "game.h" #include "in_buttons.h" -#include "AI_Memory.h" +#include "ai_memory.h" #include "soundent.h" #include "hl2_player.h" #include "EntityFlame.h" @@ -342,7 +342,7 @@ void CWeaponAR2::FireNPCPrimaryAttack( CBaseCombatCharacter *pOperator, bool bUs WeaponSoundRealtime( SINGLE_NPC ); - CSoundEnt::InsertSound( SOUND_COMBAT|SOUND_CONTEXT_GUNFIRE, pOperator->GetAbsOrigin(), SOUNDENT_VOLUME_MACHINEGUN, 0.2, pOperator, SOUNDENT_CHANNEL_WEAPON, pOperator->GetEnemy() ); + CSoundEnt::InsertSound( SOUND_COMBAT|SOUND_CONTEXT_GUNFIRE, pOperator->GetAbsOrigin(), static_cast(SOUNDENT_VOLUME_MACHINEGUN), 0.2, pOperator, SOUNDENT_CHANNEL_WEAPON, pOperator->GetEnemy() ); pOperator->FireBullets( 1, vecShootOrigin, vecShootDir, VECTOR_CONE_PRECALCULATED, MAX_TRACE_LENGTH, m_iPrimaryAmmoType, 2 ); diff --git a/dlls/hl2_dll/weapon_brickbat.cpp b/dlls/hl2_dll/weapon_brickbat.cpp index 88c01238..bd59a371 100644 --- a/dlls/hl2_dll/weapon_brickbat.cpp +++ b/dlls/hl2_dll/weapon_brickbat.cpp @@ -8,11 +8,11 @@ //=============================================================================// #include "cbase.h" -#include "NPCEvent.h" +#include "npcvent.h" #include "basehlcombatweapon.h" #include "basecombatcharacter.h" -#include "AI_BaseNPC.h" -#include "AI_Memory.h" +#include "ai_basenpc.h" +#include "ai_memory.h" #include "player.h" #include "gamerules.h" // For g_pGameRules #include "weapon_brickbat.h" diff --git a/dlls/hl2_dll/weapon_bugbait.cpp b/dlls/hl2_dll/weapon_bugbait.cpp index 4449b5ee..2b8d3ade 100644 --- a/dlls/hl2_dll/weapon_bugbait.cpp +++ b/dlls/hl2_dll/weapon_bugbait.cpp @@ -165,7 +165,7 @@ void CWeaponBugBait::Drop( const Vector &vecVelocity ) pSporeExplosion->m_flSpawnRate = 16.0f; pSporeExplosion->m_flParticleLifetime = 0.5f; - pSporeExplosion->SetRenderColor( 0.0f, 0.5f, 0.25f, 0.15f ); + pSporeExplosion->SetRenderColor( 0, 1, 0, 0 ); pSporeExplosion->m_flStartSize = 32; pSporeExplosion->m_flEndSize = 48; diff --git a/dlls/hl2_dll/weapon_cguard.cpp b/dlls/hl2_dll/weapon_cguard.cpp index bb7c4782..3e0d6fc0 100644 --- a/dlls/hl2_dll/weapon_cguard.cpp +++ b/dlls/hl2_dll/weapon_cguard.cpp @@ -123,8 +123,8 @@ public: TE_ConcussiveExplosion( filter, 0.0, &GetAbsOrigin(),//position 1.0f, //scale - 256*magnitude, //radius - 175*magnitude, //magnitude + static_cast(256 * magnitude), //radius + static_cast(175 * magnitude), //magnitude &vecForward ); //normal int colorRamp = random->RandomInt( 128, 255 ); diff --git a/dlls/hl2_dll/weapon_citizenpackage.cpp b/dlls/hl2_dll/weapon_citizenpackage.cpp index 1e05f5e6..3ee31df6 100644 --- a/dlls/hl2_dll/weapon_citizenpackage.cpp +++ b/dlls/hl2_dll/weapon_citizenpackage.cpp @@ -72,3 +72,4 @@ acttable_t CWeaponCitizenSuitcase::m_acttable[] = { ACT_WALK, ACT_WALK_SUITCASE, false }, }; IMPLEMENT_ACTTABLE(CWeaponCitizenSuitcase); + diff --git a/dlls/hl2_dll/weapon_citizenpackage.h b/dlls/hl2_dll/weapon_citizenpackage.h index 51a24418..44231a1f 100644 --- a/dlls/hl2_dll/weapon_citizenpackage.h +++ b/dlls/hl2_dll/weapon_citizenpackage.h @@ -1,31 +1,31 @@ -//====== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======= -// -// Purpose: -// -//============================================================================= -#ifndef WEAPON_CITIZENPACKAGE_H -#define WEAPON_CITIZENPACKAGE_H -#ifdef _WIN32 -#pragma once -#endif - -#include "basehlcombatweapon.h" - -//============================================================================= -// -// Weapon - Citizen Package Class -// -class CWeaponCitizenPackage : public CBaseHLCombatWeapon -{ - DECLARE_CLASS( CWeaponCitizenPackage, CBaseHLCombatWeapon ); -public: - - DECLARE_SERVERCLASS(); - DECLARE_DATADESC(); - DECLARE_ACTTABLE(); - - void ItemPostFrame( void ); - void Drop( const Vector &vecVelocity ); -}; - -#endif // WEAPON_CITIZENPACKAGE_H \ No newline at end of file +//====== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======= +// +// Purpose: +// +//============================================================================= +#ifndef WEAPON_CITIZENPACKAGE_H +#define WEAPON_CITIZENPACKAGE_H +#ifdef _WIN32 +#pragma once +#endif + +#include "basehlcombatweapon.h" + +//============================================================================= +// +// Weapon - Citizen Package Class +// +class CWeaponCitizenPackage : public CBaseHLCombatWeapon +{ + DECLARE_CLASS( CWeaponCitizenPackage, CBaseHLCombatWeapon ); +public: + + DECLARE_SERVERCLASS(); + DECLARE_DATADESC(); + DECLARE_ACTTABLE(); + + void ItemPostFrame( void ); + void Drop( const Vector &vecVelocity ); +}; + +#endif // WEAPON_CITIZENPACKAGE_H diff --git a/dlls/hl2_dll/weapon_crossbow.cpp b/dlls/hl2_dll/weapon_crossbow.cpp index 8d02608f..10ffb204 100644 --- a/dlls/hl2_dll/weapon_crossbow.cpp +++ b/dlls/hl2_dll/weapon_crossbow.cpp @@ -5,10 +5,10 @@ //=============================================================================// #include "cbase.h" -#include "NPCEvent.h" +#include "npcevent.h" #include "basehlcombatweapon_shared.h" #include "basecombatcharacter.h" -#include "AI_BaseNPC.h" +#include "ai_basenpc.h" #include "player.h" #include "gamerules.h" #include "in_buttons.h" @@ -18,8 +18,8 @@ #include "engine/IEngineSound.h" #include "IEffects.h" #include "te_effect_dispatch.h" -#include "sprite.h" -#include "spritetrail.h" +#include "Sprite.h" +#include "SpriteTrail.h" #include "beam_shared.h" #include "rumble_shared.h" @@ -370,7 +370,7 @@ void CCrossbowBolt::BubbleThink( void ) SetNextThink( gpGlobals->curtime + 0.1f ); // Make danger sounds out in front of me, to scare snipers back into their hole - CSoundEnt::InsertSound( SOUND_DANGER_SNIPERONLY, GetAbsOrigin() + GetAbsVelocity() * 0.2, 120.0f, 0.5f, this, SOUNDENT_CHANNEL_REPEATED_DANGER ); + CSoundEnt::InsertSound( SOUND_DANGER_SNIPERONLY, GetAbsOrigin() + GetAbsVelocity() * 0.2, 120, 0.5f, this, SOUNDENT_CHANNEL_REPEATED_DANGER ); if ( GetWaterLevel() == 0 ) return; diff --git a/dlls/hl2_dll/weapon_crowbar.cpp b/dlls/hl2_dll/weapon_crowbar.cpp index a999f2ce..7bd688b7 100644 --- a/dlls/hl2_dll/weapon_crowbar.cpp +++ b/dlls/hl2_dll/weapon_crowbar.cpp @@ -165,7 +165,7 @@ void CWeaponCrowbar::HandleAnimEventMeleeHit( animevent_t *pEvent, CBaseCombatCh Vector vecEnd; VectorMA( pOperator->Weapon_ShootPosition(), 50, vecDirection, vecEnd ); CBaseEntity *pHurt = pOperator->CheckTraceHullAttack( pOperator->Weapon_ShootPosition(), vecEnd, - Vector(-16,-16,-16), Vector(36,36,36), sk_npc_dmg_crowbar.GetFloat(), DMG_CLUB, 0.75 ); + Vector(-16,-16,-16), Vector(36,36,36), sk_npc_dmg_crowbar.GetInt(), DMG_CLUB, 0.75 ); // did I hit someone? if ( pHurt ) diff --git a/dlls/hl2_dll/weapon_physcannon.cpp b/dlls/hl2_dll/weapon_physcannon.cpp index ba9ad0e2..2f2f22e2 100644 --- a/dlls/hl2_dll/weapon_physcannon.cpp +++ b/dlls/hl2_dll/weapon_physcannon.cpp @@ -1,4468 +1,4468 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: Physics cannon -// -//=============================================================================// - -#include "cbase.h" -#include "player.h" -#include "gamerules.h" -#include "soundenvelope.h" -#include "engine/IEngineSound.h" -#include "physics.h" -#include "in_buttons.h" -#include "soundent.h" -#include "IEffects.h" -#include "ndebugoverlay.h" -#include "shake.h" -#include "hl2_player.h" -#include "beam_shared.h" -#include "sprite.h" -#include "util.h" -#include "weapon_physcannon.h" -#include "physics_saverestore.h" -#include "ai_basenpc.h" -#include "player_pickup.h" -#include "physics_prop_ragdoll.h" -#include "globalstate.h" -#include "props.h" -#include "movevars_shared.h" -#include "basehlcombatweapon.h" -#include "te_effect_dispatch.h" -#include "vphysics/friction.h" -#include "saverestore_utlvector.h" -#include "prop_combine_ball.h" -#include "physobj.h" -#include "hl2_gamerules.h" -#include "citadel_effects_shared.h" -#include "eventqueue.h" -#include "model_types.h" -#include "ai_interactions.h" -#include "rumble_shared.h" - -// memdbgon must be the last include file in a .cpp file!!! -#include "tier0/memdbgon.h" - -static const char *s_pWaitForUpgradeContext = "WaitForUpgrade"; - -ConVar g_debug_physcannon( "g_debug_physcannon", "0" ); - -ConVar physcannon_minforce( "physcannon_minforce", "700" ); -ConVar physcannon_maxforce( "physcannon_maxforce", "1500" ); -ConVar physcannon_maxmass( "physcannon_maxmass", "250" ); -ConVar physcannon_tracelength( "physcannon_tracelength", "250" ); -ConVar physcannon_mega_tracelength( "physcannon_mega_tracelength", "850" ); -ConVar physcannon_chargetime("physcannon_chargetime", "2" ); -ConVar physcannon_pullforce( "physcannon_pullforce", "4000" ); -ConVar physcannon_mega_pullforce( "physcannon_mega_pullforce", "8000" ); -ConVar physcannon_cone( "physcannon_cone", "0.97" ); -ConVar physcannon_ball_cone( "physcannon_ball_cone", "0.997" ); -ConVar physcannon_punt_cone( "physcannon_punt_cone", "0.997" ); -ConVar player_throwforce( "player_throwforce", "1000" ); - -extern ConVar hl2_normspeed; -extern ConVar hl2_walkspeed; - -#define PHYSCANNON_BEAM_SPRITE "sprites/orangelight1.vmt" -#define PHYSCANNON_GLOW_SPRITE "sprites/glow04_noz.vmt" -#define PHYSCANNON_ENDCAP_SPRITE "sprites/orangeflare1.vmt" -#define PHYSCANNON_CENTER_GLOW "sprites/orangecore1.vmt" -#define PHYSCANNON_BLAST_SPRITE "sprites/orangecore2.vmt" - -#define MEGACANNON_BEAM_SPRITE "sprites/lgtning_noz.vmt" -#define MEGACANNON_GLOW_SPRITE "sprites/blueflare1_noz.vmt" -#define MEGACANNON_ENDCAP_SPRITE "sprites/blueflare1_noz.vmt" -#define MEGACANNON_CENTER_GLOW "effects/fluttercore.vmt" -#define MEGACANNON_BLAST_SPRITE "effects/fluttercore.vmt" - -#define MEGACANNON_RAGDOLL_BOOGIE_SPRITE "sprites/lgtning_noz.vmt" - -#define MEGACANNON_MODEL "models/weapons/v_superphyscannon.mdl" -#define MEGACANNON_SKIN 1 - -// ------------------------------------------------------------------------- -// Physcannon trace filter to handle special cases -// ------------------------------------------------------------------------- - -class CTraceFilterPhyscannon : public CTraceFilterSimple -{ -public: - DECLARE_CLASS( CTraceFilterPhyscannon, CTraceFilterSimple ); - - CTraceFilterPhyscannon( const IHandleEntity *passentity, int collisionGroup ) - : CTraceFilterSimple( NULL, collisionGroup ), m_pTraceOwner( passentity ) { } - - // For this test, we only test against entities (then world brushes afterwards) - virtual TraceType_t GetTraceType() const { return TRACE_ENTITIES_ONLY; } - - bool HasContentsGrate( CBaseEntity *pEntity ) - { - // FIXME: Move this into the GetModelContents() function in base entity - - // Find the contents based on the model type - int nModelType = modelinfo->GetModelType( pEntity->GetModel() ); - if ( nModelType == mod_studio ) - { - CBaseAnimating *pAnim = dynamic_cast(pEntity); - if ( pAnim != NULL ) - { - CStudioHdr *pStudioHdr = pAnim->GetModelPtr(); - if ( pStudioHdr != NULL && (pStudioHdr->contents() & CONTENTS_GRATE) ) - return true; - } - } - else if ( nModelType == mod_brush ) - { - // Brushes poll their contents differently - int contents = modelinfo->GetModelContents( pEntity->GetModelIndex() ); - if ( contents & CONTENTS_GRATE ) - return true; - } - - return false; - } - - virtual bool ShouldHitEntity( IHandleEntity *pHandleEntity, int contentsMask ) - { - // Only skip ourselves (not things we own) - if ( pHandleEntity == m_pTraceOwner ) - return false; - - // Get the entity referenced by this handle - CBaseEntity *pEntity = EntityFromEntityHandle( pHandleEntity ); - if ( pEntity == NULL ) - return false; - - // Handle grate entities differently - if ( HasContentsGrate( pEntity ) ) - { - // See if it's a grabbable physics prop - CPhysicsProp *pPhysProp = dynamic_cast(pEntity); - if ( pPhysProp != NULL ) - return pPhysProp->CanBePickedUpByPhyscannon(); - - // See if it's a grabbable physics prop - if ( FClassnameIs( pEntity, "prop_physics" ) ) - { - CPhysicsProp *pPhysProp = dynamic_cast(pEntity); - if ( pPhysProp != NULL ) - return pPhysProp->CanBePickedUpByPhyscannon(); - - // Somehow had a classname that didn't match the class! - Assert(0); - } - else if ( FClassnameIs( pEntity, "func_physbox" ) ) - { - // Must be a moveable physbox - IPhysicsObject *pPhysObj = pEntity->VPhysicsGetObject(); - if ( pPhysObj != NULL && pPhysObj->IsMoveable() ) - return true; - } - - // Don't bother with any other sort of grated entity - return false; - } - - // Use the default rules - return BaseClass::ShouldHitEntity( pHandleEntity, contentsMask ); - } - -protected: - const IHandleEntity *m_pTraceOwner; -}; - -// We want to test against brushes alone -class CTraceFilterOnlyBrushes : public CTraceFilterSimple -{ -public: - DECLARE_CLASS( CTraceFilterOnlyBrushes, CTraceFilterSimple ); - CTraceFilterOnlyBrushes( int collisionGroup ) : CTraceFilterSimple( NULL, collisionGroup ) {} - virtual TraceType_t GetTraceType() const { return TRACE_WORLD_ONLY; } -}; - -//----------------------------------------------------------------------------- -// this will hit skip the pass entity, but not anything it owns -// (lets player grab own grenades) -class CTraceFilterNoOwnerTest : public CTraceFilterSimple -{ -public: - DECLARE_CLASS( CTraceFilterNoOwnerTest, CTraceFilterSimple ); - - CTraceFilterNoOwnerTest( const IHandleEntity *passentity, int collisionGroup ) - : CTraceFilterSimple( NULL, collisionGroup ), m_pPassNotOwner(passentity) - { - } - - virtual bool ShouldHitEntity( IHandleEntity *pHandleEntity, int contentsMask ) - { - if ( pHandleEntity != m_pPassNotOwner ) - return BaseClass::ShouldHitEntity( pHandleEntity, contentsMask ); - - return false; - } - -protected: - const IHandleEntity *m_pPassNotOwner; -}; - -//----------------------------------------------------------------------------- -// Purpose: Trace a line the special physcannon way! -//----------------------------------------------------------------------------- -void UTIL_PhyscannonTraceLine( const Vector &vecAbsStart, const Vector &vecAbsEnd, CBaseEntity *pTraceOwner, trace_t *pTrace ) -{ - // Default to HL2 vanilla - if ( hl2_episodic.GetBool() == false ) - { - CTraceFilterNoOwnerTest filter( pTraceOwner, COLLISION_GROUP_NONE ); - UTIL_TraceLine( vecAbsStart, vecAbsEnd, (MASK_SHOT|CONTENTS_GRATE), &filter, pTrace ); - return; - } - - // First, trace against entities - CTraceFilterPhyscannon filter( pTraceOwner, COLLISION_GROUP_NONE ); - UTIL_TraceLine( vecAbsStart, vecAbsEnd, (MASK_SHOT|CONTENTS_GRATE), &filter, pTrace ); - - // If we've hit something, test again to make sure no brushes block us - if ( pTrace->m_pEnt != NULL ) - { - trace_t testTrace; - CTraceFilterOnlyBrushes brushFilter( COLLISION_GROUP_NONE ); - UTIL_TraceLine( pTrace->startpos, pTrace->endpos, MASK_SHOT, &brushFilter, &testTrace ); - - // If we hit a brush, replace the trace with that result - if ( testTrace.fraction < 1.0f || testTrace.startsolid || testTrace.allsolid ) - { - *pTrace = testTrace; - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: Trace a hull for the physcannon -//----------------------------------------------------------------------------- -void UTIL_PhyscannonTraceHull( const Vector &vecAbsStart, const Vector &vecAbsEnd, const Vector &vecAbsMins, const Vector &vecAbsMaxs, CBaseEntity *pTraceOwner, trace_t *pTrace ) -{ - // Default to HL2 vanilla - if ( hl2_episodic.GetBool() == false ) - { - CTraceFilterNoOwnerTest filter( pTraceOwner, COLLISION_GROUP_NONE ); - UTIL_TraceHull( vecAbsStart, vecAbsEnd, vecAbsMins, vecAbsMaxs, (MASK_SHOT|CONTENTS_GRATE), &filter, pTrace ); - return; - } - - // First, trace against entities - CTraceFilterPhyscannon filter( pTraceOwner, COLLISION_GROUP_NONE ); - UTIL_TraceHull( vecAbsStart, vecAbsEnd, vecAbsMins, vecAbsMaxs, (MASK_SHOT|CONTENTS_GRATE), &filter, pTrace ); - - // If we've hit something, test again to make sure no brushes block us - if ( pTrace->m_pEnt != NULL ) - { - trace_t testTrace; - CTraceFilterOnlyBrushes brushFilter( COLLISION_GROUP_NONE ); - UTIL_TraceHull( pTrace->startpos, pTrace->endpos, vecAbsMins, vecAbsMaxs, MASK_SHOT, &brushFilter, &testTrace ); - - // If we hit a brush, replace the trace with that result - if ( testTrace.fraction < 1.0f || testTrace.startsolid || testTrace.allsolid ) - { - *pTrace = testTrace; - } - } -} - -static void MatrixOrthogonalize( matrix3x4_t &matrix, int column ) -{ - Vector columns[3]; - int i; - - for ( i = 0; i < 3; i++ ) - { - MatrixGetColumn( matrix, i, columns[i] ); - } - - int index0 = column; - int index1 = (column+1)%3; - int index2 = (column+2)%3; - - columns[index2] = CrossProduct( columns[index0], columns[index1] ); - columns[index1] = CrossProduct( columns[index2], columns[index0] ); - VectorNormalize( columns[index2] ); - VectorNormalize( columns[index1] ); - MatrixSetColumn( columns[index1], index1, matrix ); - MatrixSetColumn( columns[index2], index2, matrix ); -} - -#define SIGN(x) ( (x) < 0 ? -1 : 1 ) - -static QAngle AlignAngles( const QAngle &angles, float cosineAlignAngle ) -{ - matrix3x4_t alignMatrix; - AngleMatrix( angles, alignMatrix ); - - // NOTE: Must align z first - for ( int j = 3; --j >= 0; ) - { - Vector vec; - MatrixGetColumn( alignMatrix, j, vec ); - for ( int i = 0; i < 3; i++ ) - { - if ( fabs(vec[i]) > cosineAlignAngle ) - { - vec[i] = SIGN(vec[i]); - vec[(i+1)%3] = 0; - vec[(i+2)%3] = 0; - MatrixSetColumn( vec, j, alignMatrix ); - MatrixOrthogonalize( alignMatrix, j ); - break; - } - } - } - - QAngle out; - MatrixAngles( alignMatrix, out ); - return out; -} - - -static void TraceCollideAgainstBBox( const CPhysCollide *pCollide, const Vector &start, const Vector &end, const QAngle &angles, const Vector &boxOrigin, const Vector &mins, const Vector &maxs, trace_t *ptr ) -{ - physcollision->TraceBox( boxOrigin, boxOrigin + (start-end), mins, maxs, pCollide, start, angles, ptr ); - - if ( ptr->DidHit() ) - { - ptr->endpos = start * (1-ptr->fraction) + end * ptr->fraction; - ptr->startpos = start; - ptr->plane.dist = -ptr->plane.dist; - ptr->plane.normal *= -1; - } -} - -//----------------------------------------------------------------------------- -// Purpose: Finds the nearest ragdoll sub-piece to a location and returns it -// Input : *pTarget - entity that is the potential ragdoll -// &position - position we're testing against -// Output : IPhysicsObject - sub-object (if any) -//----------------------------------------------------------------------------- -IPhysicsObject *GetRagdollChildAtPosition( CBaseEntity *pTarget, const Vector &position ) -{ - // Check for a ragdoll - if ( dynamic_cast( pTarget ) == NULL ) - return NULL; - - // Get the root - IPhysicsObject *pList[32]; - int count = pTarget->VPhysicsGetObjectList( pList, ARRAYSIZE( pList ) ); - - IPhysicsObject *pBestChild = NULL; - float flBestDist = 99999999.0f; - float flDist; - Vector vPos; - - // Find the nearest child to where we're looking - for ( int i = 0; i < count; i++ ) - { - pList[i]->GetPosition( &vPos, NULL ); - - flDist = ( position - vPos ).LengthSqr(); - - if ( flDist < flBestDist ) - { - pBestChild = pList[i]; - flBestDist = flDist; - } - } - - // Make this our base now - pTarget->VPhysicsSwapObject( pBestChild ); - - return pTarget->VPhysicsGetObject(); -} - -//----------------------------------------------------------------------------- -// Purpose: Computes a local matrix for the player clamped to valid carry ranges -//----------------------------------------------------------------------------- -// when looking level, hold bottom of object 8 inches below eye level -#define PLAYER_HOLD_LEVEL_EYES -8 - -// when looking down, hold bottom of object 0 inches from feet -#define PLAYER_HOLD_DOWN_FEET 2 - -// when looking up, hold bottom of object 24 inches above eye level -#define PLAYER_HOLD_UP_EYES 24 - -// use a +/-30 degree range for the entire range of motion of pitch -#define PLAYER_LOOK_PITCH_RANGE 30 - -// player can reach down 2ft below his feet (otherwise he'll hold the object above the bottom) -#define PLAYER_REACH_DOWN_DISTANCE 24 - -static void ComputePlayerMatrix( CBasePlayer *pPlayer, matrix3x4_t &out ) -{ - if ( !pPlayer ) - return; - - QAngle angles = pPlayer->EyeAngles(); - Vector origin = pPlayer->EyePosition(); - - // 0-360 / -180-180 - //angles.x = init ? 0 : AngleDistance( angles.x, 0 ); - //angles.x = clamp( angles.x, -PLAYER_LOOK_PITCH_RANGE, PLAYER_LOOK_PITCH_RANGE ); - angles.x = 0; - - float feet = pPlayer->GetAbsOrigin().z + pPlayer->WorldAlignMins().z; - float eyes = origin.z; - float zoffset = 0; - // moving up (negative pitch is up) - if ( angles.x < 0 ) - { - zoffset = RemapVal( angles.x, 0, -PLAYER_LOOK_PITCH_RANGE, PLAYER_HOLD_LEVEL_EYES, PLAYER_HOLD_UP_EYES ); - } - else - { - zoffset = RemapVal( angles.x, 0, PLAYER_LOOK_PITCH_RANGE, PLAYER_HOLD_LEVEL_EYES, PLAYER_HOLD_DOWN_FEET + (feet - eyes) ); - } - origin.z += zoffset; - angles.x = 0; - AngleMatrix( angles, origin, out ); -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -// derive from this so we can add save/load data to it -struct game_shadowcontrol_params_t : public hlshadowcontrol_params_t -{ - DECLARE_SIMPLE_DATADESC(); -}; - -BEGIN_SIMPLE_DATADESC( game_shadowcontrol_params_t ) - - DEFINE_FIELD( targetPosition, FIELD_POSITION_VECTOR ), - DEFINE_FIELD( targetRotation, FIELD_VECTOR ), - DEFINE_FIELD( maxAngular, FIELD_FLOAT ), - DEFINE_FIELD( maxDampAngular, FIELD_FLOAT ), - DEFINE_FIELD( maxSpeed, FIELD_FLOAT ), - DEFINE_FIELD( maxDampSpeed, FIELD_FLOAT ), - DEFINE_FIELD( dampFactor, FIELD_FLOAT ), - DEFINE_FIELD( teleportDistance, FIELD_FLOAT ), - -END_DATADESC() - -//----------------------------------------------------------------------------- -class CGrabController : public IMotionEvent -{ - DECLARE_SIMPLE_DATADESC(); - -public: - - CGrabController( void ); - ~CGrabController( void ); - void AttachEntity( CBasePlayer *pPlayer, CBaseEntity *pEntity, IPhysicsObject *pPhys, bool bIsMegaPhysCannon, const Vector &vGrabPosition, bool bUseGrabPosition ); - void DetachEntity( bool bClearVelocity ); - void OnRestore(); - - bool UpdateObject( CBasePlayer *pPlayer, float flError ); - - void SetTargetPosition( const Vector &target, const QAngle &targetOrientation ); - float ComputeError(); - float GetLoadWeight( void ) const { return m_flLoadWeight; } - void SetAngleAlignment( float alignAngleCosine ) { m_angleAlignment = alignAngleCosine; } - void SetIgnorePitch( bool bIgnore ) { m_bIgnoreRelativePitch = bIgnore; } - QAngle TransformAnglesToPlayerSpace( const QAngle &anglesIn, CBasePlayer *pPlayer ); - QAngle TransformAnglesFromPlayerSpace( const QAngle &anglesIn, CBasePlayer *pPlayer ); - - CBaseEntity *GetAttached() { return (CBaseEntity *)m_attachedEntity; } - - IMotionEvent::simresult_e Simulate( IPhysicsMotionController *pController, IPhysicsObject *pObject, float deltaTime, Vector &linear, AngularImpulse &angular ); - float GetSavedMass( IPhysicsObject *pObject ); - -private: - // Compute the max speed for an attached object - void ComputeMaxSpeed( CBaseEntity *pEntity, IPhysicsObject *pPhysics ); - - game_shadowcontrol_params_t m_shadow; - float m_timeToArrive; - float m_errorTime; - float m_error; - float m_contactAmount; - float m_angleAlignment; - bool m_bCarriedEntityBlocksLOS; - bool m_bIgnoreRelativePitch; - - float m_flLoadWeight; - float m_savedRotDamping[VPHYSICS_MAX_OBJECT_LIST_COUNT]; - float m_savedMass[VPHYSICS_MAX_OBJECT_LIST_COUNT]; - EHANDLE m_attachedEntity; - QAngle m_vecPreferredCarryAngles; - bool m_bHasPreferredCarryAngles; - float m_flDistanceOffset; - - QAngle m_attachedAnglesPlayerSpace; - Vector m_attachedPositionObjectSpace; - - IPhysicsMotionController *m_controller; - - friend class CWeaponPhysCannon; -}; - -BEGIN_SIMPLE_DATADESC( CGrabController ) - - DEFINE_EMBEDDED( m_shadow ), - - DEFINE_FIELD( m_timeToArrive, FIELD_FLOAT ), - DEFINE_FIELD( m_errorTime, FIELD_FLOAT ), - DEFINE_FIELD( m_error, FIELD_FLOAT ), - DEFINE_FIELD( m_contactAmount, FIELD_FLOAT ), - DEFINE_AUTO_ARRAY( m_savedRotDamping, FIELD_FLOAT ), - DEFINE_AUTO_ARRAY( m_savedMass, FIELD_FLOAT ), - DEFINE_FIELD( m_flLoadWeight, FIELD_FLOAT ), - DEFINE_FIELD( m_bCarriedEntityBlocksLOS, FIELD_BOOLEAN ), - DEFINE_FIELD( m_bIgnoreRelativePitch, FIELD_BOOLEAN ), - DEFINE_FIELD( m_attachedEntity, FIELD_EHANDLE ), - DEFINE_FIELD( m_angleAlignment, FIELD_FLOAT ), - DEFINE_FIELD( m_vecPreferredCarryAngles, FIELD_VECTOR ), - DEFINE_FIELD( m_bHasPreferredCarryAngles, FIELD_BOOLEAN ), - DEFINE_FIELD( m_flDistanceOffset, FIELD_FLOAT ), - DEFINE_FIELD( m_attachedAnglesPlayerSpace, FIELD_VECTOR ), - DEFINE_FIELD( m_attachedPositionObjectSpace, FIELD_VECTOR ), - - // Physptrs can't be inside embedded classes - // DEFINE_PHYSPTR( m_controller ), - -END_DATADESC() - -const float DEFAULT_MAX_ANGULAR = 360.0f * 10.0f; -const float REDUCED_CARRY_MASS = 1.0f; - -CGrabController::CGrabController( void ) -{ - m_shadow.dampFactor = 1.0; - m_shadow.teleportDistance = 0; - m_errorTime = 0; - m_error = 0; - // make this controller really stiff! - m_shadow.maxSpeed = 1000; - m_shadow.maxAngular = DEFAULT_MAX_ANGULAR; - m_shadow.maxDampSpeed = m_shadow.maxSpeed*2; - m_shadow.maxDampAngular = m_shadow.maxAngular; - m_attachedEntity = NULL; - m_vecPreferredCarryAngles = vec3_angle; - m_bHasPreferredCarryAngles = false; - m_flDistanceOffset = 0; -} - -CGrabController::~CGrabController( void ) -{ - DetachEntity( false ); -} - -void CGrabController::OnRestore() -{ - if ( m_controller ) - { - m_controller->SetEventHandler( this ); - } -} - -void CGrabController::SetTargetPosition( const Vector &target, const QAngle &targetOrientation ) -{ - m_shadow.targetPosition = target; - m_shadow.targetRotation = targetOrientation; - - m_timeToArrive = gpGlobals->frametime; - - CBaseEntity *pAttached = GetAttached(); - if ( pAttached ) - { - IPhysicsObject *pObj = pAttached->VPhysicsGetObject(); - - if ( pObj != NULL ) - { - pObj->Wake(); - } - else - { - DetachEntity( false ); - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: -// Output : float -//----------------------------------------------------------------------------- -float CGrabController::ComputeError() -{ - if ( m_errorTime <= 0 ) - return 0; - - CBaseEntity *pAttached = GetAttached(); - if ( pAttached ) - { - Vector pos; - IPhysicsObject *pObj = pAttached->VPhysicsGetObject(); - - if ( pObj ) - { - pObj->GetShadowPosition( &pos, NULL ); - - float error = (m_shadow.targetPosition - pos).Length(); - if ( m_errorTime > 0 ) - { - if ( m_errorTime > 1 ) - { - m_errorTime = 1; - } - float speed = error / m_errorTime; - if ( speed > m_shadow.maxSpeed ) - { - error *= 0.5; - } - m_error = (1-m_errorTime) * m_error + error * m_errorTime; - } - } - else - { - DevMsg( "Object attached to Physcannon has no physics object\n" ); - DetachEntity( false ); - return 9999; // force detach - } - } - - if ( pAttached->IsEFlagSet( EFL_IS_BEING_LIFTED_BY_BARNACLE ) ) - { - m_error *= 3.0f; - } - - m_errorTime = 0; - - return m_error; -} - - -#define MASS_SPEED_SCALE 60 -#define MAX_MASS 40 - -void CGrabController::ComputeMaxSpeed( CBaseEntity *pEntity, IPhysicsObject *pPhysics ) -{ - m_shadow.maxSpeed = 1000; - m_shadow.maxAngular = DEFAULT_MAX_ANGULAR; - - // Compute total mass... - float flMass = PhysGetEntityMass( pEntity ); - float flMaxMass = physcannon_maxmass.GetFloat(); - if ( flMass <= flMaxMass ) - return; - - float flLerpFactor = clamp( flMass, flMaxMass, 500.0f ); - flLerpFactor = SimpleSplineRemapVal( flLerpFactor, flMaxMass, 500.0f, 0.0f, 1.0f ); - - float invMass = pPhysics->GetInvMass(); - float invInertia = pPhysics->GetInvInertia().Length(); - - float invMaxMass = 1.0f / MAX_MASS; - float ratio = invMaxMass / invMass; - invMass = invMaxMass; - invInertia *= ratio; - - float maxSpeed = invMass * MASS_SPEED_SCALE * 200; - float maxAngular = invInertia * MASS_SPEED_SCALE * 360; - - m_shadow.maxSpeed = Lerp( flLerpFactor, m_shadow.maxSpeed, maxSpeed ); - m_shadow.maxAngular = Lerp( flLerpFactor, m_shadow.maxAngular, maxAngular ); -} - - -QAngle CGrabController::TransformAnglesToPlayerSpace( const QAngle &anglesIn, CBasePlayer *pPlayer ) -{ - if ( m_bIgnoreRelativePitch ) - { - matrix3x4_t test; - QAngle angleTest = pPlayer->EyeAngles(); - angleTest.x = 0; - AngleMatrix( angleTest, test ); - return TransformAnglesToLocalSpace( anglesIn, test ); - } - return TransformAnglesToLocalSpace( anglesIn, pPlayer->EntityToWorldTransform() ); -} - -QAngle CGrabController::TransformAnglesFromPlayerSpace( const QAngle &anglesIn, CBasePlayer *pPlayer ) -{ - if ( m_bIgnoreRelativePitch ) - { - matrix3x4_t test; - QAngle angleTest = pPlayer->EyeAngles(); - angleTest.x = 0; - AngleMatrix( angleTest, test ); - return TransformAnglesToWorldSpace( anglesIn, test ); - } - return TransformAnglesToWorldSpace( anglesIn, pPlayer->EntityToWorldTransform() ); -} - - -void CGrabController::AttachEntity( CBasePlayer *pPlayer, CBaseEntity *pEntity, IPhysicsObject *pPhys, bool bIsMegaPhysCannon, const Vector &vGrabPosition, bool bUseGrabPosition ) -{ - // play the impact sound of the object hitting the player - // used as feedback to let the player know he picked up the object - int hitMaterial = pPhys->GetMaterialIndex(); - int playerMaterial = pPlayer->VPhysicsGetObject() ? pPlayer->VPhysicsGetObject()->GetMaterialIndex() : hitMaterial; - PhysicsImpactSound( pPlayer, pPhys, CHAN_STATIC, hitMaterial, playerMaterial, 1.0, 64 ); - Vector position; - QAngle angles; - pPhys->GetPosition( &position, &angles ); - // If it has a preferred orientation, use that instead. - Pickup_GetPreferredCarryAngles( pEntity, pPlayer, pPlayer->EntityToWorldTransform(), angles ); - -// ComputeMaxSpeed( pEntity, pPhys ); - - // If we haven't been killed by a grab, we allow the gun to grab the nearest part of a ragdoll - if ( bUseGrabPosition ) - { - IPhysicsObject *pChild = GetRagdollChildAtPosition( pEntity, vGrabPosition ); - - if ( pChild ) - { - pPhys = pChild; - } - } - - // Carried entities can never block LOS - m_bCarriedEntityBlocksLOS = pEntity->BlocksLOS(); - pEntity->SetBlocksLOS( false ); - m_controller = physenv->CreateMotionController( this ); - m_controller->AttachObject( pPhys, true ); - // Don't do this, it's causing trouble with constraint solvers. - //m_controller->SetPriority( IPhysicsMotionController::HIGH_PRIORITY ); - - pPhys->Wake(); - PhysSetGameFlags( pPhys, FVPHYSICS_PLAYER_HELD ); - SetTargetPosition( position, angles ); - m_attachedEntity = pEntity; - IPhysicsObject *pList[VPHYSICS_MAX_OBJECT_LIST_COUNT]; - int count = pEntity->VPhysicsGetObjectList( pList, ARRAYSIZE(pList) ); - m_flLoadWeight = 0; - float damping = 10; - float flFactor = count / 7.5f; - if ( flFactor < 1.0f ) - { - flFactor = 1.0f; - } - for ( int i = 0; i < count; i++ ) - { - float mass = pList[i]->GetMass(); - pList[i]->GetDamping( NULL, &m_savedRotDamping[i] ); - m_flLoadWeight += mass; - m_savedMass[i] = mass; - - // reduce the mass to prevent the player from adding crazy amounts of energy to the system - pList[i]->SetMass( REDUCED_CARRY_MASS / flFactor ); - pList[i]->SetDamping( NULL, &damping ); - } - - // Give extra mass to the phys object we're actually picking up - pPhys->SetMass( REDUCED_CARRY_MASS ); - pPhys->EnableDrag( false ); - - m_errorTime = bIsMegaPhysCannon ? -1.5f : -1.0f; // 1 seconds until error starts accumulating - m_error = 0; - m_contactAmount = 0; - - m_attachedAnglesPlayerSpace = TransformAnglesToPlayerSpace( angles, pPlayer ); - if ( m_angleAlignment != 0 ) - { - m_attachedAnglesPlayerSpace = AlignAngles( m_attachedAnglesPlayerSpace, m_angleAlignment ); - } - - // Ragdolls don't offset this way - if ( dynamic_cast(pEntity) ) - { - m_attachedPositionObjectSpace.Init(); - } - else - { - VectorITransform( pEntity->WorldSpaceCenter(), pEntity->EntityToWorldTransform(), m_attachedPositionObjectSpace ); - } - - // If it's a prop, see if it has desired carry angles - CPhysicsProp *pProp = dynamic_cast(pEntity); - if ( pProp ) - { - m_bHasPreferredCarryAngles = pProp->GetPropDataAngles( "preferred_carryangles", m_vecPreferredCarryAngles ); - m_flDistanceOffset = pProp->GetCarryDistanceOffset(); - } - else - { - m_bHasPreferredCarryAngles = false; - m_flDistanceOffset = 0; - } -} - -static void ClampPhysicsVelocity( IPhysicsObject *pPhys, float linearLimit, float angularLimit ) -{ - Vector vel; - AngularImpulse angVel; - pPhys->GetVelocity( &vel, &angVel ); - float speed = VectorNormalize(vel) - linearLimit; - float angSpeed = VectorNormalize(angVel) - angularLimit; - speed = speed < 0 ? 0 : -speed; - angSpeed = angSpeed < 0 ? 0 : -angSpeed; - vel *= speed; - angVel *= angSpeed; - pPhys->AddVelocity( &vel, &angVel ); -} - -void CGrabController::DetachEntity( bool bClearVelocity ) -{ - Assert(!PhysIsInCallback()); - CBaseEntity *pEntity = GetAttached(); - if ( pEntity ) - { - // Restore the LS blocking state - pEntity->SetBlocksLOS( m_bCarriedEntityBlocksLOS ); - IPhysicsObject *pList[VPHYSICS_MAX_OBJECT_LIST_COUNT]; - int count = pEntity->VPhysicsGetObjectList( pList, ARRAYSIZE(pList) ); - for ( int i = 0; i < count; i++ ) - { - IPhysicsObject *pPhys = pList[i]; - if ( !pPhys ) - continue; - - // on the odd chance that it's gone to sleep while under anti-gravity - pPhys->EnableDrag( true ); - pPhys->Wake(); - pPhys->SetMass( m_savedMass[i] ); - pPhys->SetDamping( NULL, &m_savedRotDamping[i] ); - PhysClearGameFlags( pPhys, FVPHYSICS_PLAYER_HELD ); - if ( bClearVelocity ) - { - PhysForceClearVelocity( pPhys ); - } - else - { - ClampPhysicsVelocity( pPhys, hl2_normspeed.GetFloat() * 1.5f, 2.0f * 360.0f ); - } - - } - } - - m_attachedEntity = NULL; - physenv->DestroyMotionController( m_controller ); - m_controller = NULL; -} - -static bool InContactWithHeavyObject( IPhysicsObject *pObject, float heavyMass ) -{ - bool contact = false; - IPhysicsFrictionSnapshot *pSnapshot = pObject->CreateFrictionSnapshot(); - while ( pSnapshot->IsValid() ) - { - IPhysicsObject *pOther = pSnapshot->GetObject( 1 ); - if ( !pOther->IsMoveable() || pOther->GetMass() > heavyMass ) - { - contact = true; - break; - } - pSnapshot->NextFrictionData(); - } - pObject->DestroyFrictionSnapshot( pSnapshot ); - return contact; -} - -IMotionEvent::simresult_e CGrabController::Simulate( IPhysicsMotionController *pController, IPhysicsObject *pObject, float deltaTime, Vector &linear, AngularImpulse &angular ) -{ - game_shadowcontrol_params_t shadowParams = m_shadow; - if ( InContactWithHeavyObject( pObject, GetLoadWeight() ) ) - { - m_contactAmount = Approach( 0.1f, m_contactAmount, deltaTime*2.0f ); - } - else - { - m_contactAmount = Approach( 1.0f, m_contactAmount, deltaTime*2.0f ); - } - shadowParams.maxAngular = m_shadow.maxAngular * m_contactAmount * m_contactAmount * m_contactAmount; - m_timeToArrive = pObject->ComputeShadowControl( shadowParams, m_timeToArrive, deltaTime ); - - // Slide along the current contact points to fix bouncing problems - Vector velocity; - AngularImpulse angVel; - pObject->GetVelocity( &velocity, &angVel ); - PhysComputeSlideDirection( pObject, velocity, angVel, &velocity, &angVel, GetLoadWeight() ); - pObject->SetVelocityInstantaneous( &velocity, NULL ); - - linear.Init(); - angular.Init(); - m_errorTime += deltaTime; - - return SIM_LOCAL_ACCELERATION; -} - -float CGrabController::GetSavedMass( IPhysicsObject *pObject ) -{ - CBaseEntity *pHeld = m_attachedEntity; - if ( pHeld ) - { - if ( pObject->GetGameData() == (void*)pHeld ) - { - IPhysicsObject *pList[VPHYSICS_MAX_OBJECT_LIST_COUNT]; - int count = pHeld->VPhysicsGetObjectList( pList, ARRAYSIZE(pList) ); - for ( int i = 0; i < count; i++ ) - { - if ( pList[i] == pObject ) - return m_savedMass[i]; - } - } - } - return 0.0f; -} - -//----------------------------------------------------------------------------- -// Player pickup controller -//----------------------------------------------------------------------------- -class CPlayerPickupController : public CBaseEntity -{ - DECLARE_DATADESC(); - DECLARE_CLASS( CPlayerPickupController, CBaseEntity ); -public: - void Init( CBasePlayer *pPlayer, CBaseEntity *pObject ); - void Shutdown( bool bThrown = false ); - bool OnControls( CBaseEntity *pControls ) { return true; } - void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); - void OnRestore() - { - m_grabController.OnRestore(); - } - void VPhysicsUpdate( IPhysicsObject *pPhysics ){} - void VPhysicsShadowUpdate( IPhysicsObject *pPhysics ) {} - - bool IsHoldingEntity( CBaseEntity *pEnt ); - CGrabController &GetGrabController() { return m_grabController; } - -private: - CGrabController m_grabController; - CBasePlayer *m_pPlayer; -}; - -LINK_ENTITY_TO_CLASS( player_pickup, CPlayerPickupController ); - -//--------------------------------------------------------- -// Save/Restore -//--------------------------------------------------------- -BEGIN_DATADESC( CPlayerPickupController ) - - DEFINE_EMBEDDED( m_grabController ), - - // Physptrs can't be inside embedded classes - DEFINE_PHYSPTR( m_grabController.m_controller ), - - DEFINE_FIELD( m_pPlayer, FIELD_CLASSPTR ), - -END_DATADESC() - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *pPlayer - -// *pObject - -//----------------------------------------------------------------------------- -void CPlayerPickupController::Init( CBasePlayer *pPlayer, CBaseEntity *pObject ) -{ - // Holster player's weapon - if ( pPlayer->GetActiveWeapon() ) - { - if ( !pPlayer->GetActiveWeapon()->Holster() ) - { - Shutdown(); - return; - } - } - - CHL2_Player *pOwner = (CHL2_Player *)ToBasePlayer( pPlayer ); - if ( pOwner ) - { - pOwner->EnableSprint( false ); - } - - // If the target is debris, convert it to non-debris - if ( pObject->GetCollisionGroup() == COLLISION_GROUP_DEBRIS ) - { - // Interactive debris converts back to debris when it comes to rest - pObject->SetCollisionGroup( COLLISION_GROUP_INTERACTIVE_DEBRIS ); - } - - // done so I'll go across level transitions with the player - SetParent( pPlayer ); - m_grabController.SetIgnorePitch( true ); - m_grabController.SetAngleAlignment( DOT_30DEGREE ); - m_pPlayer = pPlayer; - IPhysicsObject *pPhysics = pObject->VPhysicsGetObject(); - - Pickup_OnPhysGunPickup( pObject, m_pPlayer, PICKED_UP_BY_PLAYER ); - - m_grabController.AttachEntity( pPlayer, pObject, pPhysics, false, vec3_origin, false ); - - m_pPlayer->m_Local.m_iHideHUD |= HIDEHUD_WEAPONSELECTION; - m_pPlayer->SetUseEntity( this ); -} - - -//----------------------------------------------------------------------------- -// Purpose: -// Input : bool - -//----------------------------------------------------------------------------- -void CPlayerPickupController::Shutdown( bool bThrown ) -{ - CBaseEntity *pObject = m_grabController.GetAttached(); - - bool bClearVelocity = false; - if ( !bThrown && pObject && pObject->VPhysicsGetObject() && pObject->VPhysicsGetObject()->GetContactPoint(NULL,NULL) ) - { - bClearVelocity = true; - } - - m_grabController.DetachEntity( bClearVelocity ); - - if ( pObject != NULL ) - { - Pickup_OnPhysGunDrop( pObject, m_pPlayer, bThrown ? THROWN_BY_PLAYER : DROPPED_BY_PLAYER ); - } - - if ( m_pPlayer ) - { - CHL2_Player *pOwner = (CHL2_Player *)ToBasePlayer( m_pPlayer ); - if ( pOwner ) - { - pOwner->EnableSprint( true ); - } - - m_pPlayer->SetUseEntity( NULL ); - if ( m_pPlayer->GetActiveWeapon() ) - { - if ( !m_pPlayer->GetActiveWeapon()->Deploy() ) - { - // We tried to restore the player's weapon, but we couldn't. - // This usually happens when they're holding an empty weapon that doesn't - // autoswitch away when out of ammo. Switch to next best weapon. - m_pPlayer->SwitchToNextBestWeapon( NULL ); - } - } - - m_pPlayer->m_Local.m_iHideHUD &= ~HIDEHUD_WEAPONSELECTION; - } - Remove(); -} - - -void CPlayerPickupController::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) -{ - if ( ToBasePlayer(pActivator) == m_pPlayer ) - { - CBaseEntity *pAttached = m_grabController.GetAttached(); - - // UNDONE: Use vphysics stress to decide to drop objects - // UNDONE: Must fix case of forcing objects into the ground you're standing on (causes stress) before that will work - if ( !pAttached || useType == USE_OFF || (m_pPlayer->m_nButtons & IN_ATTACK2) || m_grabController.ComputeError() > 12 ) - { - Shutdown(); - return; - } - - //Adrian: Oops, our object became motion disabled, let go! - IPhysicsObject *pPhys = pAttached->VPhysicsGetObject(); - if ( pPhys && pPhys->IsMoveable() == false ) - { - Shutdown(); - return; - } - -#if STRESS_TEST - vphysics_objectstress_t stress; - CalculateObjectStress( pPhys, pAttached, &stress ); - if ( stress.exertedStress > 250 ) - { - Shutdown(); - return; - } -#endif - // +ATTACK will throw phys objects - if ( m_pPlayer->m_nButtons & IN_ATTACK ) - { - Shutdown( true ); - Vector vecLaunch; - m_pPlayer->EyeVectors( &vecLaunch ); - // JAY: Scale this with mass because some small objects really go flying - float massFactor = clamp( pPhys->GetMass(), 0.5, 15 ); - massFactor = RemapVal( massFactor, 0.5, 15, 0.5, 4 ); - vecLaunch *= player_throwforce.GetFloat() * massFactor; - - pPhys->ApplyForceCenter( vecLaunch ); - AngularImpulse aVel = RandomAngularImpulse( -10, 10 ) * massFactor; - pPhys->ApplyTorqueCenter( aVel ); - return; - } - - if ( useType == USE_SET ) - { - // update position - m_grabController.UpdateObject( m_pPlayer, 12 ); - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *pEnt - -// Output : Returns true on success, false on failure. -//----------------------------------------------------------------------------- -bool CPlayerPickupController::IsHoldingEntity( CBaseEntity *pEnt ) -{ - return ( m_grabController.GetAttached() == pEnt ); -} - -void PlayerPickupObject( CBasePlayer *pPlayer, CBaseEntity *pObject ) -{ - //Don't pick up if we don't have a phys object. - if ( pObject->VPhysicsGetObject() == NULL ) - return; - - CPlayerPickupController *pController = (CPlayerPickupController *)CBaseEntity::Create( "player_pickup", pObject->GetAbsOrigin(), vec3_angle, pPlayer ); - - if ( !pController ) - return; - - pController->Init( pPlayer, pObject ); -} - -//----------------------------------------------------------------------------- - -//----------------------------------------------------------------------------- -// Physcannon -//----------------------------------------------------------------------------- - -#define NUM_BEAMS 4 -#define NUM_SPRITES 6 - -struct thrown_objects_t -{ - float fTimeThrown; - EHANDLE hEntity; - - DECLARE_SIMPLE_DATADESC(); -}; - -BEGIN_SIMPLE_DATADESC( thrown_objects_t ) - DEFINE_FIELD( fTimeThrown, FIELD_TIME ), - DEFINE_FIELD( hEntity, FIELD_EHANDLE ), -END_DATADESC() - -class CWeaponPhysCannon : public CBaseHLCombatWeapon -{ -public: - DECLARE_CLASS( CWeaponPhysCannon, CBaseHLCombatWeapon ); - - DECLARE_SERVERCLASS(); - DECLARE_DATADESC(); - - CWeaponPhysCannon( void ); - - void Drop( const Vector &vecVelocity ); - void Precache(); - virtual void Spawn(); - virtual void OnRestore(); - virtual void StopLoopingSounds(); - virtual void UpdateOnRemove(void); - void PrimaryAttack(); - void SecondaryAttack(); - void WeaponIdle(); - void ItemPreFrame(); - void ItemPostFrame(); - void ItemBusyFrame(); - - virtual float GetMaxAutoAimDeflection() { return 0.90f; } - - void ForceDrop( void ); - bool DropIfEntityHeld( CBaseEntity *pTarget ); // Drops its held entity if it matches the entity passed in - CGrabController &GetGrabController() { return m_grabController; } - - bool CanHolster( void ); - bool Holster( CBaseCombatWeapon *pSwitchingTo = NULL ); - bool Deploy( void ); - - bool HasAnyAmmo( void ) { return true; } - - void InputBecomeMegaCannon( inputdata_t &inputdata ); - - void BeginUpgrade(); - - virtual void SetViewModel( void ); - virtual const char *GetShootSound( int iIndex ) const; - - void RecordThrownObject( CBaseEntity *pObject ); - void PurgeThrownObjects(); - bool IsAccountableForObject( CBaseEntity *pObject ); - - bool ShouldDisplayHUDHint() { return true; } - - - -protected: - enum FindObjectResult_t - { - OBJECT_FOUND = 0, - OBJECT_NOT_FOUND, - OBJECT_BEING_DETACHED, - }; - - void DoMegaEffect( int effectType, Vector *pos = NULL ); - void DoEffect( int effectType, Vector *pos = NULL ); - - void OpenElements( void ); - void CloseElements( void ); - - // Pickup and throw objects. - bool CanPickupObject( CBaseEntity *pTarget ); - bool IsFlesh( CBaseEntity *pTarget ); - void CheckForTarget( void ); - FindObjectResult_t FindObject( void ); - CBaseEntity *MegaPhysCannonFindObjectInCone( const Vector &vecOrigin, const Vector &vecDir, float flCone, float flCombineBallCone, bool bOnlyCombineBalls ); - CBaseEntity *FindObjectInCone( const Vector &vecOrigin, const Vector &vecDir, float flCone ); - bool AttachObject( CBaseEntity *pObject, const Vector &vPosition ); - void UpdateObject( void ); - void DetachObject( bool playSound = true, bool wasLaunched = false ); - void LaunchObject( const Vector &vecDir, float flForce ); - void StartEffects( void ); // Initialize all sprites and beams - void StopEffects( bool stopSound = true ); // Hide all effects temporarily - void DestroyEffects( void ); // Destroy all sprites and beams - - // Punt objects - this is pointing at an object in the world and applying a force to it. - void PuntNonVPhysics( CBaseEntity *pEntity, const Vector &forward, trace_t &tr ); - void PuntVPhysics( CBaseEntity *pEntity, const Vector &forward, trace_t &tr ); - void PuntRagdoll( CBaseEntity *pEntity, const Vector &forward, trace_t &tr ); - - // Velocity-based throw common to punt and launch code. - void ApplyVelocityBasedForce( CBaseEntity *pEntity, const Vector &forward ); - - // Physgun effects - void DoEffectClosed( void ); - void DoMegaEffectClosed( void ); - - void DoEffectReady( void ); - void DoMegaEffectReady( void ); - - void DoMegaEffectHolding( void ); - void DoEffectHolding( void ); - - void DoMegaEffectLaunch( Vector *pos ); - void DoEffectLaunch( Vector *pos ); - - void DoEffectNone( void ); - void DoEffectIdle( void ); - - // Trace length - float TraceLength(); - - // Do we have the super-phys gun? - inline bool IsMegaPhysCannon() - { - return PlayerHasMegaPhysCannon(); - } - - // Sprite scale factor - float SpriteScaleFactor(); - - float GetLoadPercentage(); - CSoundPatch *GetMotorSound( void ); - - void DryFire( void ); - void PrimaryFireEffect( void ); - - // What happens when the physgun picks up something - void Physgun_OnPhysGunPickup( CBaseEntity *pEntity, CBasePlayer *pOwner, PhysGunPickup_t reason ); - - // Wait until we're done upgrading - void WaitForUpgradeThink(); - - bool EntityAllowsPunts( CBaseEntity *pEntity ); - - bool m_bOpen; - bool m_bActive; - int m_nChangeState; //For delayed state change of elements - float m_flCheckSuppressTime; //Amount of time to suppress the checking for targets - bool m_flLastDenySoundPlayed; //Debounce for deny sound - int m_nAttack2Debounce; - - CNetworkVar( bool, m_bIsCurrentlyUpgrading ); - - float m_flElementDebounce; - float m_flElementPosition; - float m_flElementDestination; - - CHandle m_hBeams[NUM_BEAMS]; - CHandle m_hGlowSprites[NUM_SPRITES]; - CHandle m_hEndSprites[2]; - float m_flEndSpritesOverride[2]; - CHandle m_hCenterSprite; - CHandle m_hBlastSprite; - - CSoundPatch *m_sndMotor; // Whirring sound for the gun - - CGrabController m_grabController; - - int m_EffectState; // Current state of the effects on the gun - - bool m_bPhyscannonState; - - // A list of the objects thrown or punted recently, and the time done so. - CUtlVector< thrown_objects_t > m_ThrownEntities; - - float m_flTimeNextObjectPurge; -}; - -IMPLEMENT_SERVERCLASS_ST(CWeaponPhysCannon, DT_WeaponPhysCannon) - SendPropBool( SENDINFO( m_bIsCurrentlyUpgrading ) ), -END_SEND_TABLE() - -LINK_ENTITY_TO_CLASS( weapon_physcannon, CWeaponPhysCannon ); -PRECACHE_WEAPON_REGISTER( weapon_physcannon ); - -BEGIN_DATADESC( CWeaponPhysCannon ) - - DEFINE_FIELD( m_bOpen, FIELD_BOOLEAN ), - DEFINE_FIELD( m_bActive, FIELD_BOOLEAN ), - - DEFINE_FIELD( m_nChangeState, FIELD_INTEGER ), - DEFINE_FIELD( m_flCheckSuppressTime, FIELD_TIME ), - DEFINE_FIELD( m_flElementDebounce, FIELD_TIME ), - DEFINE_FIELD( m_flElementPosition, FIELD_FLOAT ), - DEFINE_FIELD( m_flElementDestination, FIELD_FLOAT ), - DEFINE_FIELD( m_nAttack2Debounce, FIELD_INTEGER ), - DEFINE_FIELD( m_bIsCurrentlyUpgrading, FIELD_BOOLEAN ), - DEFINE_FIELD( m_EffectState, FIELD_INTEGER ), - - DEFINE_AUTO_ARRAY( m_hBeams, FIELD_EHANDLE ), - DEFINE_AUTO_ARRAY( m_hGlowSprites, FIELD_EHANDLE ), - DEFINE_AUTO_ARRAY( m_hEndSprites, FIELD_EHANDLE ), - DEFINE_AUTO_ARRAY( m_flEndSpritesOverride, FIELD_TIME ), - DEFINE_FIELD( m_hCenterSprite, FIELD_EHANDLE ), - DEFINE_FIELD( m_hBlastSprite, FIELD_EHANDLE ), - DEFINE_FIELD( m_flLastDenySoundPlayed, FIELD_BOOLEAN ), - DEFINE_FIELD( m_bPhyscannonState, FIELD_BOOLEAN ), - DEFINE_SOUNDPATCH( m_sndMotor ), - - DEFINE_EMBEDDED( m_grabController ), - - // Physptrs can't be inside embedded classes - DEFINE_PHYSPTR( m_grabController.m_controller ), - - DEFINE_THINKFUNC( WaitForUpgradeThink ), - - DEFINE_UTLVECTOR( m_ThrownEntities, FIELD_EMBEDDED ), - - DEFINE_FIELD( m_flTimeNextObjectPurge, FIELD_TIME ), - -END_DATADESC() - - -enum -{ - ELEMENT_STATE_NONE = -1, - ELEMENT_STATE_OPEN, - ELEMENT_STATE_CLOSED, -}; - -enum -{ - EFFECT_NONE, - EFFECT_CLOSED, - EFFECT_READY, - EFFECT_HOLDING, - EFFECT_LAUNCH, -}; - - -//----------------------------------------------------------------------------- -// Do we have the super-phys gun? -//----------------------------------------------------------------------------- -bool PlayerHasMegaPhysCannon() -{ - return ( HL2GameRules()->MegaPhyscannonActive() == true ); -} - - -//----------------------------------------------------------------------------- -// Constructor -//----------------------------------------------------------------------------- -CWeaponPhysCannon::CWeaponPhysCannon( void ) -{ - m_flElementPosition = 0.0f; - m_flElementDestination = 0.0f; - m_bOpen = false; - m_nChangeState = ELEMENT_STATE_NONE; - m_flCheckSuppressTime = 0.0f; - m_EffectState = EFFECT_NONE; - m_flLastDenySoundPlayed = false; - - m_flEndSpritesOverride[0] = 0.0f; - m_flEndSpritesOverride[1] = 0.0f; - - m_bPhyscannonState = false; -} - -//----------------------------------------------------------------------------- -// Purpose: Precache -//----------------------------------------------------------------------------- -void CWeaponPhysCannon::Precache( void ) -{ - PrecacheModel( PHYSCANNON_BEAM_SPRITE ); - PrecacheModel( PHYSCANNON_GLOW_SPRITE ); - PrecacheModel( PHYSCANNON_ENDCAP_SPRITE ); - PrecacheModel( PHYSCANNON_CENTER_GLOW ); - PrecacheModel( PHYSCANNON_BLAST_SPRITE ); - - PrecacheModel( MEGACANNON_BEAM_SPRITE ); - PrecacheModel( MEGACANNON_GLOW_SPRITE ); - PrecacheModel( MEGACANNON_ENDCAP_SPRITE ); - PrecacheModel( MEGACANNON_CENTER_GLOW ); - PrecacheModel( MEGACANNON_BLAST_SPRITE ); - - PrecacheModel( MEGACANNON_RAGDOLL_BOOGIE_SPRITE ); - - // Precache our alternate model - PrecacheModel( MEGACANNON_MODEL ); - - PrecacheScriptSound( "Weapon_PhysCannon.HoldSound" ); - PrecacheScriptSound( "Weapon_Physgun.Off" ); - - PrecacheScriptSound( "Weapon_MegaPhysCannon.DryFire" ); - PrecacheScriptSound( "Weapon_MegaPhysCannon.Launch" ); - PrecacheScriptSound( "Weapon_MegaPhysCannon.Pickup"); - PrecacheScriptSound( "Weapon_MegaPhysCannon.Drop"); - PrecacheScriptSound( "Weapon_MegaPhysCannon.HoldSound"); - PrecacheScriptSound( "Weapon_MegaPhysCannon.ChargeZap"); - - BaseClass::Precache(); -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CWeaponPhysCannon::Spawn( void ) -{ - BaseClass::Spawn(); - - // Need to get close to pick it up - CollisionProp()->UseTriggerBounds( false ); - - m_bPhyscannonState = IsMegaPhysCannon(); - - // The megacannon uses a different skin - if ( IsMegaPhysCannon() ) - { - m_nSkin = MEGACANNON_SKIN; - } -} - - -//----------------------------------------------------------------------------- -// Purpose: Restore -//----------------------------------------------------------------------------- -void CWeaponPhysCannon::OnRestore() -{ - BaseClass::OnRestore(); - m_grabController.OnRestore(); - - m_bPhyscannonState = IsMegaPhysCannon(); - - // Tracker 8106: Physcannon effects disappear through level transition, so - // just recreate any effects here - if ( m_EffectState != EFFECT_NONE ) - { - DoEffect( m_EffectState, NULL ); - } -} - - -//----------------------------------------------------------------------------- -// On Remove -//----------------------------------------------------------------------------- -void CWeaponPhysCannon::UpdateOnRemove(void) -{ - DestroyEffects( ); - BaseClass::UpdateOnRemove(); -} - - -//----------------------------------------------------------------------------- -// Sprite scale factor -//----------------------------------------------------------------------------- -inline float CWeaponPhysCannon::SpriteScaleFactor() -{ - return IsMegaPhysCannon() ? 1.5f : 1.0f; -} - - -//----------------------------------------------------------------------------- -// Purpose: -// Output : Returns true on success, false on failure. -//----------------------------------------------------------------------------- -bool CWeaponPhysCannon::Deploy( void ) -{ - CloseElements(); - DoEffect( EFFECT_READY ); - - // Unbloat our bounds - if ( IsMegaPhysCannon() ) - { - CollisionProp()->UseTriggerBounds( false ); - } - - m_flTimeNextObjectPurge = gpGlobals->curtime; - - return BaseClass::Deploy(); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CWeaponPhysCannon::SetViewModel( void ) -{ - if ( !IsMegaPhysCannon() ) - { - BaseClass::SetViewModel(); - return; - } - - CBasePlayer *pOwner = ToBasePlayer( GetOwner() ); - if ( pOwner == NULL ) - return; - - CBaseViewModel *vm = pOwner->GetViewModel( m_nViewModelIndex ); - if ( vm == NULL ) - return; - - vm->SetWeaponModel( MEGACANNON_MODEL, this ); -} - -//----------------------------------------------------------------------------- -// Purpose: Force the cannon to drop anything it's carrying -//----------------------------------------------------------------------------- -void CWeaponPhysCannon::ForceDrop( void ) -{ - CloseElements(); - DetachObject(); - StopEffects(); -} - - -//----------------------------------------------------------------------------- -// Purpose: Drops its held entity if it matches the entity passed in -// Input : *pTarget - -// Output : Returns true on success, false on failure. -//----------------------------------------------------------------------------- -bool CWeaponPhysCannon::DropIfEntityHeld( CBaseEntity *pTarget ) -{ - if ( pTarget == NULL ) - return false; - - CBaseEntity *pHeld = m_grabController.GetAttached(); - - if ( pHeld == NULL ) - return false; - - if ( pHeld == pTarget ) - { - ForceDrop(); - return true; - } - - return false; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CWeaponPhysCannon::Drop( const Vector &vecVelocity ) -{ - ForceDrop(); - BaseClass::Drop( vecVelocity ); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -bool CWeaponPhysCannon::CanHolster( void ) -{ - //Don't holster this weapon if we're holding onto something - if ( m_bActive ) - return false; - - return BaseClass::CanHolster(); -}; - -//----------------------------------------------------------------------------- -// Purpose: -// Output : Returns true on success, false on failure. -//----------------------------------------------------------------------------- -bool CWeaponPhysCannon::Holster( CBaseCombatWeapon *pSwitchingTo ) -{ - //Don't holster this weapon if we're holding onto something - if ( m_bActive ) - { - if ( m_grabController.GetAttached() == pSwitchingTo && - GetOwner()->Weapon_OwnsThisType( pSwitchingTo->GetClassname(), pSwitchingTo->GetSubType()) ) - { - - } - else - { - return false; - } - } - - CBasePlayer *pOwner = ToBasePlayer( GetOwner() ); - if ( pOwner ) - { - pOwner->RumbleEffect( RUMBLE_PHYSCANNON_OPEN, 0, RUMBLE_FLAG_STOP ); - } - - ForceDrop(); - - return BaseClass::Holster( pSwitchingTo ); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CWeaponPhysCannon::DryFire( void ) -{ - SendWeaponAnim( ACT_VM_PRIMARYATTACK ); - WeaponSound( EMPTY ); - - CBasePlayer *pOwner = ToBasePlayer( GetOwner() ); - if ( pOwner ) - { - pOwner->RumbleEffect( RUMBLE_PISTOL, 0, RUMBLE_FLAG_RESTART ); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CWeaponPhysCannon::PrimaryFireEffect( void ) -{ - CBasePlayer *pOwner = ToBasePlayer( GetOwner() ); - - if ( pOwner == NULL ) - return; - - pOwner->ViewPunch( QAngle(-6, random->RandomInt(-2,2) ,0) ); - - color32 white = { 245, 245, 255, 32 }; - UTIL_ScreenFade( pOwner, white, 0.1f, 0.0f, FFADE_IN ); - - WeaponSound( SINGLE ); -} - -#define MAX_KNOCKBACK_FORCE 128 - -//----------------------------------------------------------------------------- -// Punt non-physics -//----------------------------------------------------------------------------- -void CWeaponPhysCannon::PuntNonVPhysics( CBaseEntity *pEntity, const Vector &forward, trace_t &tr ) -{ - CTakeDamageInfo info; - - info.SetAttacker( GetOwner() ); - info.SetInflictor( this ); - info.SetDamage( 1.0f ); - info.SetDamageType( DMG_CRUSH | DMG_PHYSGUN ); - info.SetDamageForce( forward ); // Scale? - info.SetDamagePosition( tr.endpos ); - - pEntity->DispatchTraceAttack( info, forward, &tr ); - - ApplyMultiDamage(); - - //Explosion effect - DoEffect( EFFECT_LAUNCH, &tr.endpos ); - - PrimaryFireEffect(); - SendWeaponAnim( ACT_VM_SECONDARYATTACK ); - - m_nChangeState = ELEMENT_STATE_CLOSED; - m_flElementDebounce = gpGlobals->curtime + 0.5f; - m_flCheckSuppressTime = gpGlobals->curtime + 0.25f; -} - - -//----------------------------------------------------------------------------- -// What happens when the physgun picks up something -//----------------------------------------------------------------------------- -void CWeaponPhysCannon::Physgun_OnPhysGunPickup( CBaseEntity *pEntity, CBasePlayer *pOwner, PhysGunPickup_t reason ) -{ - // If the target is debris, convert it to non-debris - if ( pEntity->GetCollisionGroup() == COLLISION_GROUP_DEBRIS ) - { - // Interactive debris converts back to debris when it comes to rest - pEntity->SetCollisionGroup( COLLISION_GROUP_INTERACTIVE_DEBRIS ); - } - - float mass = 0.0f; - if( pEntity->VPhysicsGetObject() ) - { - mass = pEntity->VPhysicsGetObject()->GetMass(); - } - - if( reason == PUNTED_BY_CANNON ) - { - pOwner->RumbleEffect( RUMBLE_357, 0, RUMBLE_FLAGS_NONE ); - RecordThrownObject( pEntity ); - } - - // Warn Alyx if the player is punting a car around. - if( hl2_episodic.GetBool() && mass > 250.0f ) - { - CAI_BaseNPC **ppAIs = g_AI_Manager.AccessAIs(); - int nAIs = g_AI_Manager.NumAIs(); - - for ( int i = 0; i < nAIs; i++ ) - { - if( ppAIs[ i ]->Classify() == CLASS_PLAYER_ALLY_VITAL ) - { - ppAIs[ i ]->DispatchInteraction( g_interactionPlayerPuntedHeavyObject, pEntity, pOwner ); - } - } - } - - Pickup_OnPhysGunPickup( pEntity, pOwner, reason ); -} - - -//----------------------------------------------------------------------------- -// Punt vphysics -//----------------------------------------------------------------------------- -void CWeaponPhysCannon::PuntVPhysics( CBaseEntity *pEntity, const Vector &vecForward, trace_t &tr ) -{ - CTakeDamageInfo info; - - Vector forward = vecForward; - - info.SetAttacker( GetOwner() ); - info.SetInflictor( this ); - info.SetDamage( 0.0f ); - info.SetDamageType( DMG_PHYSGUN ); - pEntity->DispatchTraceAttack( info, forward, &tr ); - ApplyMultiDamage(); - - CBasePlayer *pOwner = ToBasePlayer( GetOwner() ); - if ( Pickup_OnAttemptPhysGunPickup( pEntity, pOwner, PUNTED_BY_CANNON ) ) - { - IPhysicsObject *pList[64]; - int listCount = pEntity->VPhysicsGetObjectList( pList, ARRAYSIZE(pList) ); - if ( !listCount ) - { - //FIXME: Do we want to do this if there's no physics object? - Physgun_OnPhysGunPickup( pEntity, pOwner, PUNTED_BY_CANNON ); - DryFire(); - return; - } - - if( forward.z < 0 ) - { - //reflect, but flatten the trajectory out a bit so it's easier to hit standing targets - forward.z *= -0.65f; - } - - // NOTE: Do this first to enable motion (if disabled) - so forces will work - // Tell the object it's been punted - Physgun_OnPhysGunPickup( pEntity, pOwner, PUNTED_BY_CANNON ); - - // don't push vehicles that are attached to the world via fixed constraints - // they will just wiggle... - if ( (pList[0]->GetGameFlags() & FVPHYSICS_CONSTRAINT_STATIC) && pEntity->GetServerVehicle() ) - { - forward.Init(); - } - - if ( !IsMegaPhysCannon() && !Pickup_ShouldPuntUseLaunchForces( pEntity ) ) - { - int i; - - // limit mass to avoid punting REALLY huge things - float totalMass = 0; - for ( i = 0; i < listCount; i++ ) - { - totalMass += pList[i]->GetMass(); - } - float maxMass = 250; - IServerVehicle *pVehicle = pEntity->GetServerVehicle(); - if ( pVehicle ) - { - maxMass *= 2.5; // 625 for vehicles - } - float mass = min(totalMass, maxMass); // max 250kg of additional force - - // Put some spin on the object - for ( i = 0; i < listCount; i++ ) - { - const float hitObjectFactor = 0.5f; - const float otherObjectFactor = 1.0f - hitObjectFactor; - // Must be light enough - float ratio = pList[i]->GetMass() / totalMass; - if ( pList[i] == pEntity->VPhysicsGetObject() ) - { - ratio += hitObjectFactor; - ratio = min(ratio,1.0f); - } - else - { - ratio *= otherObjectFactor; - } - pList[i]->ApplyForceCenter( forward * 15000.0f * ratio ); - pList[i]->ApplyForceOffset( forward * mass * 600.0f * ratio, tr.endpos ); - } - } - else - { - ApplyVelocityBasedForce( pEntity, vecForward ); - } - } - - // Add recoil - QAngle recoil = QAngle( random->RandomFloat( 1.0f, 2.0f ), random->RandomFloat( -1.0f, 1.0f ), 0 ); - pOwner->ViewPunch( recoil ); - - //Explosion effect - DoEffect( EFFECT_LAUNCH, &tr.endpos ); - - PrimaryFireEffect(); - SendWeaponAnim( ACT_VM_SECONDARYATTACK ); - - m_nChangeState = ELEMENT_STATE_CLOSED; - m_flElementDebounce = gpGlobals->curtime + 0.5f; - m_flCheckSuppressTime = gpGlobals->curtime + 0.25f; - - // Don't allow the gun to regrab a thrown object!! - m_flNextSecondaryAttack = m_flNextPrimaryAttack = gpGlobals->curtime + 0.5f; -} - -//----------------------------------------------------------------------------- -// Purpose: Applies velocity-based forces to throw the entity. This code is -// called from both punt and launch carried code. -// ASSUMES: that pEntity is a vphysics entity. -// Input : - -//----------------------------------------------------------------------------- -void CWeaponPhysCannon::ApplyVelocityBasedForce( CBaseEntity *pEntity, const Vector &forward ) -{ - // Get the launch velocity - Vector vVel = Pickup_PhysGunLaunchVelocity( pEntity, forward ); - - // Get the launch angular impulse - AngularImpulse aVel = Pickup_PhysGunLaunchAngularImpulse( pEntity ); - - // Get the physics object (MUST have one) - IPhysicsObject *pPhysicsObject = pEntity->VPhysicsGetObject(); - if ( pPhysicsObject == NULL ) - { - Assert( 0 ); - return; - } - - // Affect the object - CRagdollProp *pRagdoll = dynamic_cast( pEntity ); - if ( pRagdoll == NULL ) - { - pPhysicsObject->AddVelocity( &vVel, &aVel ); - } - else - { - Vector vTempVel; - AngularImpulse vTempAVel; - - ragdoll_t *pRagdollPhys = pRagdoll->GetRagdoll( ); - for ( int j = 0; j < pRagdollPhys->listCount; ++j ) - { - pRagdollPhys->list[j].pObject->AddVelocity( &vVel, &aVel ); - } - } -} - - -//----------------------------------------------------------------------------- -// Punt non-physics -//----------------------------------------------------------------------------- -void CWeaponPhysCannon::PuntRagdoll( CBaseEntity *pEntity, const Vector &vecForward, trace_t &tr ) -{ - CBasePlayer *pOwner = ToBasePlayer( GetOwner() ); - Pickup_OnPhysGunDrop( pEntity, pOwner, LAUNCHED_BY_CANNON ); - - CTakeDamageInfo info; - - Vector forward = vecForward; - info.SetAttacker( GetOwner() ); - info.SetInflictor( this ); - info.SetDamage( 0.0f ); - info.SetDamageType( DMG_PHYSGUN ); - pEntity->DispatchTraceAttack( info, forward, &tr ); - ApplyMultiDamage(); - - if ( Pickup_OnAttemptPhysGunPickup( pEntity, pOwner, PUNTED_BY_CANNON ) ) - { - Physgun_OnPhysGunPickup( pEntity, pOwner, PUNTED_BY_CANNON ); - - if( forward.z < 0 ) - { - //reflect, but flatten the trajectory out a bit so it's easier to hit standing targets - forward.z *= -0.65f; - } - - Vector vVel = forward * 1500; - AngularImpulse aVel = Pickup_PhysGunLaunchAngularImpulse( pEntity ); - - CRagdollProp *pRagdoll = dynamic_cast( pEntity ); - ragdoll_t *pRagdollPhys = pRagdoll->GetRagdoll( ); - - int j; - for ( j = 0; j < pRagdollPhys->listCount; ++j ) - { - pRagdollPhys->list[j].pObject->AddVelocity( &vVel, NULL ); - } - } - - // Add recoil - QAngle recoil = QAngle( random->RandomFloat( 1.0f, 2.0f ), random->RandomFloat( -1.0f, 1.0f ), 0 ); - pOwner->ViewPunch( recoil ); - - //Explosion effect - DoEffect( EFFECT_LAUNCH, &tr.endpos ); - - PrimaryFireEffect(); - SendWeaponAnim( ACT_VM_SECONDARYATTACK ); - - m_nChangeState = ELEMENT_STATE_CLOSED; - m_flElementDebounce = gpGlobals->curtime + 0.5f; - m_flCheckSuppressTime = gpGlobals->curtime + 0.25f; - - // Don't allow the gun to regrab a thrown object!! - m_flNextSecondaryAttack = m_flNextPrimaryAttack = gpGlobals->curtime + 0.5f; -} - - -//----------------------------------------------------------------------------- -// Trace length -//----------------------------------------------------------------------------- -float CWeaponPhysCannon::TraceLength() -{ - if ( !IsMegaPhysCannon() ) - { - return physcannon_tracelength.GetFloat(); - } - - return physcannon_mega_tracelength.GetFloat(); -} - -//----------------------------------------------------------------------------- -// If there's any special rejection code you need to do per entity then do it here -// This is kinda nasty but I'd hate to move more physcannon related stuff into CBaseEntity -//----------------------------------------------------------------------------- -bool CWeaponPhysCannon::EntityAllowsPunts( CBaseEntity *pEntity ) -{ - if ( pEntity->HasSpawnFlags( SF_PHYSBOX_NEVER_PUNT ) ) - { - CPhysBox *pPhysBox = dynamic_cast(pEntity); - - if ( pPhysBox != NULL ) - { - if ( pPhysBox->HasSpawnFlags( SF_PHYSBOX_NEVER_PUNT ) ) - { - return false; - } - } - } - - if ( pEntity->HasSpawnFlags( SF_WEAPON_NO_PHYSCANNON_PUNT ) ) - { - CBaseCombatWeapon *pWeapon = dynamic_cast(pEntity); - - if ( pWeapon != NULL ) - { - if ( pWeapon->HasSpawnFlags( SF_WEAPON_NO_PHYSCANNON_PUNT ) ) - { - return false; - } - } - } - - return true; -} - - -//----------------------------------------------------------------------------- -// Purpose: -// -// This mode is a toggle. Primary fire one time to pick up a physics object. -// With an object held, click primary fire again to drop object. -//----------------------------------------------------------------------------- -void CWeaponPhysCannon::PrimaryAttack( void ) -{ - if( m_flNextPrimaryAttack > gpGlobals->curtime ) - return; - - CBasePlayer *pOwner = ToBasePlayer( GetOwner() ); - - if ( pOwner == NULL ) - return; - - if( m_bActive ) - { - // Punch the object being held!! - Vector forward; - pOwner->EyeVectors( &forward ); - - // Validate the item is within punt range - CBaseEntity *pHeld = m_grabController.GetAttached(); - Assert( pHeld != NULL ); - - if ( pHeld != NULL ) - { - float heldDist = ( pHeld->WorldSpaceCenter() - pOwner->WorldSpaceCenter() ).Length(); - - if ( heldDist > physcannon_tracelength.GetFloat() ) - { - // We can't punt this yet - DryFire(); - return; - } - } - - LaunchObject( forward, physcannon_maxforce.GetFloat() ); - - PrimaryFireEffect(); - SendWeaponAnim( ACT_VM_SECONDARYATTACK ); - return; - } - - // If not active, just issue a physics punch in the world. - m_flNextPrimaryAttack = gpGlobals->curtime + 0.5f; - - Vector forward; - pOwner->EyeVectors( &forward ); - - // NOTE: Notice we're *not* using the mega tracelength here - // when you have the mega cannon. Punting has shorter range. - Vector start, end; - start = pOwner->Weapon_ShootPosition(); - float flPuntDistance = physcannon_tracelength.GetFloat(); - VectorMA( start, flPuntDistance, forward, end ); - - trace_t tr; - UTIL_PhyscannonTraceHull( start, end, -Vector(8,8,8), Vector(8,8,8), pOwner, &tr ); - bool bValid = true; - CBaseEntity *pEntity = tr.m_pEnt; - if ( tr.fraction == 1 || !tr.m_pEnt || tr.m_pEnt->IsEFlagSet( EFL_NO_PHYSCANNON_INTERACTION ) ) - { - bValid = false; - } - else if ( (pEntity->GetMoveType() != MOVETYPE_VPHYSICS) && ( pEntity->m_takedamage == DAMAGE_NO ) ) - { - bValid = false; - } - - // If the entity we've hit is invalid, try a traceline instead - if ( !bValid ) - { - UTIL_PhyscannonTraceLine( start, end, pOwner, &tr ); - if ( tr.fraction == 1 || !tr.m_pEnt || tr.m_pEnt->IsEFlagSet( EFL_NO_PHYSCANNON_INTERACTION ) ) - { - if( hl2_episodic.GetBool() ) - { - // Try to find something in a very small cone. - CBaseEntity *pObject = FindObjectInCone( start, forward, physcannon_punt_cone.GetFloat() ); - - if( pObject ) - { - // Trace to the object. - UTIL_PhyscannonTraceLine( start, pObject->WorldSpaceCenter(), pOwner, &tr ); - - if( tr.m_pEnt && tr.m_pEnt == pObject && !(pObject->IsEFlagSet(EFL_NO_PHYSCANNON_INTERACTION)) ) - { - bValid = true; - pEntity = pObject; - } - } - } - } - else - { - bValid = true; - pEntity = tr.m_pEnt; - } - } - - if( !bValid ) - { - DryFire(); - return; - } - - // See if we hit something - if ( pEntity->GetMoveType() != MOVETYPE_VPHYSICS ) - { - if ( pEntity->m_takedamage == DAMAGE_NO ) - { - DryFire(); - return; - } - - if( GetOwner()->IsPlayer() && !IsMegaPhysCannon() ) - { - // Don't let the player zap any NPC's except regular antlions and headcrabs. - if( pEntity->IsNPC() && pEntity->Classify() != CLASS_HEADCRAB && !FClassnameIs(pEntity, "npc_antlion") ) - { - DryFire(); - return; - } - } - - if ( IsMegaPhysCannon() ) - { - if ( pEntity->IsNPC() && !pEntity->IsEFlagSet( EFL_NO_MEGAPHYSCANNON_RAGDOLL ) && pEntity->MyNPCPointer()->CanBecomeRagdoll() ) - { - CTakeDamageInfo info( pOwner, pOwner, 1.0f, DMG_GENERIC ); - CBaseEntity *pRagdoll = CreateServerRagdoll( pEntity->MyNPCPointer(), 0, info, COLLISION_GROUP_INTERACTIVE_DEBRIS, true ); - PhysSetEntityGameFlags( pRagdoll, FVPHYSICS_NO_SELF_COLLISIONS ); - pRagdoll->SetCollisionBounds( pEntity->CollisionProp()->OBBMins(), pEntity->CollisionProp()->OBBMaxs() ); - - // Necessary to cause it to do the appropriate death cleanup - CTakeDamageInfo ragdollInfo( pOwner, pOwner, 10000.0, DMG_PHYSGUN | DMG_REMOVENORAGDOLL ); - pEntity->TakeDamage( ragdollInfo ); - - PuntRagdoll( pRagdoll, forward, tr ); - return; - } - } - - PuntNonVPhysics( pEntity, forward, tr ); - } - else - { - if ( EntityAllowsPunts( pEntity) == false ) - { - DryFire(); - return; - } - - if ( !IsMegaPhysCannon() ) - { - if ( IsFlesh( pEntity ) ) - { - DryFire(); - return; - } - PuntVPhysics( pEntity, forward, tr ); - } - else - { - if ( dynamic_cast(pEntity) ) - { - PuntRagdoll( pEntity, forward, tr ); - } - else - { - PuntVPhysics( pEntity, forward, tr ); - } - } - } -} - - -//----------------------------------------------------------------------------- -// Purpose: Click secondary attack whilst holding an object to hurl it. -//----------------------------------------------------------------------------- -void CWeaponPhysCannon::SecondaryAttack( void ) -{ - if ( m_flNextSecondaryAttack > gpGlobals->curtime ) - return; - - CBasePlayer *pOwner = ToBasePlayer( GetOwner() ); - - if ( pOwner == NULL ) - return; - - // See if we should drop a held item - if ( ( m_bActive ) && ( pOwner->m_afButtonPressed & IN_ATTACK2 ) ) - { - // Drop the held object - m_flNextPrimaryAttack = gpGlobals->curtime + 0.5; - m_flNextSecondaryAttack = gpGlobals->curtime + 0.5; - - DetachObject(); - - DoEffect( EFFECT_READY ); - - SendWeaponAnim( ACT_VM_PRIMARYATTACK ); - } - else - { - // Otherwise pick it up - FindObjectResult_t result = FindObject(); - switch ( result ) - { - case OBJECT_FOUND: - WeaponSound( SPECIAL1 ); - SendWeaponAnim( ACT_VM_PRIMARYATTACK ); - m_flNextSecondaryAttack = gpGlobals->curtime + 0.5f; - - // We found an object. Debounce the button - m_nAttack2Debounce |= pOwner->m_nButtons; - break; - - case OBJECT_NOT_FOUND: - m_flNextSecondaryAttack = gpGlobals->curtime + 0.1f; - CloseElements(); - break; - - case OBJECT_BEING_DETACHED: - m_flNextSecondaryAttack = gpGlobals->curtime + 0.01f; - break; - } - - DoEffect( EFFECT_HOLDING ); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CWeaponPhysCannon::WeaponIdle( void ) -{ - if ( HasWeaponIdleTimeElapsed() ) - { - if ( m_bActive ) - { - //Shake when holding an item - SendWeaponAnim( ACT_VM_RELOAD ); - } - else - { - //Otherwise idle simply - SendWeaponAnim( ACT_VM_IDLE ); - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *pObject - -//----------------------------------------------------------------------------- -bool CWeaponPhysCannon::AttachObject( CBaseEntity *pObject, const Vector &vPosition ) -{ - if ( m_bActive ) - return false; - - if ( CanPickupObject( pObject ) == false ) - return false; - - m_grabController.SetIgnorePitch( false ); - m_grabController.SetAngleAlignment( 0 ); - - bool bKilledByGrab = false; - - bool bIsMegaPhysCannon = IsMegaPhysCannon(); - if ( bIsMegaPhysCannon ) - { - if ( pObject->IsNPC() && !pObject->IsEFlagSet( EFL_NO_MEGAPHYSCANNON_RAGDOLL ) ) - { - Assert( pObject->MyNPCPointer()->CanBecomeRagdoll() ); - CTakeDamageInfo info( GetOwner(), GetOwner(), 1.0f, DMG_GENERIC ); - CBaseEntity *pRagdoll = CreateServerRagdoll( pObject->MyNPCPointer(), 0, info, COLLISION_GROUP_INTERACTIVE_DEBRIS, true ); - PhysSetEntityGameFlags( pRagdoll, FVPHYSICS_NO_SELF_COLLISIONS ); - - pRagdoll->SetCollisionBounds( pObject->CollisionProp()->OBBMins(), pObject->CollisionProp()->OBBMaxs() ); - - // Necessary to cause it to do the appropriate death cleanup - CTakeDamageInfo ragdollInfo( GetOwner(), GetOwner(), 10000.0, DMG_PHYSGUN | DMG_REMOVENORAGDOLL ); - pObject->TakeDamage( ragdollInfo ); - - // Now we act on the ragdoll for the remainder of the time - pObject = pRagdoll; - bKilledByGrab = true; - } - } - - IPhysicsObject *pPhysics = pObject->VPhysicsGetObject(); - - // Must be valid - if ( !pPhysics ) - return false; - - CHL2_Player *pOwner = (CHL2_Player *)ToBasePlayer( GetOwner() ); - - m_bActive = true; - if( pOwner ) - { -#ifdef HL2_EPISODIC - CBreakableProp *pProp = dynamic_cast< CBreakableProp * >( pObject ); - - if ( pProp && pProp->HasInteraction( PROPINTER_PHYSGUN_CREATE_FLARE ) ) - { - pOwner->FlashlightTurnOff(); - } -#endif - - // NOTE: This can change the mass; so it must be done before max speed setting - Physgun_OnPhysGunPickup( pObject, pOwner, PICKED_UP_BY_CANNON ); - } - - // NOTE :This must happen after OnPhysGunPickup because that can change the mass - m_grabController.AttachEntity( pOwner, pObject, pPhysics, bIsMegaPhysCannon, vPosition, (!bKilledByGrab) ); - - if( pOwner ) - { - pOwner->EnableSprint( false ); - - float loadWeight = ( 1.0f - GetLoadPercentage() ); - float maxSpeed = hl2_walkspeed.GetFloat() + ( ( hl2_normspeed.GetFloat() - hl2_walkspeed.GetFloat() ) * loadWeight ); - - //Msg( "Load perc: %f -- Movement speed: %f/%f\n", loadWeight, maxSpeed, hl2_normspeed.GetFloat() ); - pOwner->SetMaxSpeed( maxSpeed ); - } - - // Don't drop again for a slight delay, in case they were pulling objects near them - m_flNextSecondaryAttack = gpGlobals->curtime + 0.4f; - - DoEffect( EFFECT_HOLDING ); - OpenElements(); - - if ( GetMotorSound() ) - { - (CSoundEnvelopeController::GetController()).Play( GetMotorSound(), 0.0f, 50 ); - (CSoundEnvelopeController::GetController()).SoundChangePitch( GetMotorSound(), 100, 0.5f ); - (CSoundEnvelopeController::GetController()).SoundChangeVolume( GetMotorSound(), 0.8f, 0.5f ); - } - - return true; -} - -CWeaponPhysCannon::FindObjectResult_t CWeaponPhysCannon::FindObject( void ) -{ - CBasePlayer *pPlayer = ToBasePlayer( GetOwner() ); - - Assert( pPlayer ); - if ( pPlayer == NULL ) - return OBJECT_NOT_FOUND; - - Vector forward; - pPlayer->EyeVectors( &forward ); - - // Setup our positions - Vector start = pPlayer->Weapon_ShootPosition(); - float testLength = TraceLength() * 4.0f; - Vector end = start + forward * testLength; - - if( IsMegaPhysCannon() && hl2_episodic.GetBool() ) - { - Vector vecAutoAimDir = pPlayer->GetAutoaimVector( 1.0f, testLength ); - end = start + vecAutoAimDir * testLength; - } - - // Try to find an object by looking straight ahead - trace_t tr; - UTIL_PhyscannonTraceLine( start, end, pPlayer, &tr ); - - // Try again with a hull trace - if ( ( tr.fraction == 1.0 ) || ( tr.m_pEnt == NULL ) || ( tr.m_pEnt->IsWorld() ) ) - { - UTIL_PhyscannonTraceHull( start, end, -Vector(4,4,4), Vector(4,4,4), pPlayer, &tr ); - } - - CBaseEntity *pEntity = tr.m_pEnt ? tr.m_pEnt->GetRootMoveParent() : NULL; - bool bAttach = false; - bool bPull = false; - - // If we hit something, pick it up or pull it - if ( ( tr.fraction != 1.0f ) && ( tr.m_pEnt ) && ( tr.m_pEnt->IsWorld() == false ) ) - { - // Attempt to attach if within range - if ( tr.fraction <= 0.25f ) - { - bAttach = true; - } - else if ( tr.fraction > 0.25f ) - { - bPull = true; - } - } - - // Find anything within a general cone in front - CBaseEntity *pConeEntity = NULL; - if ( !IsMegaPhysCannon() ) - { - if (!bAttach && !bPull) - { - pConeEntity = FindObjectInCone( start, forward, physcannon_cone.GetFloat() ); - } - } - else - { - pConeEntity = MegaPhysCannonFindObjectInCone( start, forward, - physcannon_cone.GetFloat(), physcannon_ball_cone.GetFloat(), bAttach || bPull ); - } - - if ( pConeEntity ) - { - pEntity = pConeEntity; - - // If the object is near, grab it. Else, pull it a bit. - if ( pEntity->WorldSpaceCenter().DistToSqr( start ) <= (testLength * testLength) ) - { - bAttach = true; - } - else - { - bPull = true; - } - } - - if ( CanPickupObject( pEntity ) == false ) - { - CBaseEntity *pNewObject = Pickup_OnFailedPhysGunPickup( pEntity, start ); - - if ( pNewObject && CanPickupObject( pNewObject ) ) - { - pEntity = pNewObject; - } - else - { - // Make a noise to signify we can't pick this up - if ( !m_flLastDenySoundPlayed ) - { - m_flLastDenySoundPlayed = true; - WeaponSound( SPECIAL3 ); - } - - return OBJECT_NOT_FOUND; - } - } - - // Check to see if the object is constrained + needs to be ripped off... - CBasePlayer *pOwner = ToBasePlayer( GetOwner() ); - if ( !Pickup_OnAttemptPhysGunPickup( pEntity, pOwner, PICKED_UP_BY_CANNON ) ) - return OBJECT_BEING_DETACHED; - - if ( bAttach ) - { - return AttachObject( pEntity, tr.endpos ) ? OBJECT_FOUND : OBJECT_NOT_FOUND; - } - - if ( !bPull ) - return OBJECT_NOT_FOUND; - - // FIXME: This needs to be run through the CanPickupObject logic - IPhysicsObject *pObj = pEntity->VPhysicsGetObject(); - if ( !pObj ) - return OBJECT_NOT_FOUND; - - // If we're too far, simply start to pull the object towards us - Vector pullDir = start - pEntity->WorldSpaceCenter(); - VectorNormalize( pullDir ); - pullDir *= IsMegaPhysCannon() ? physcannon_mega_pullforce.GetFloat() : physcannon_pullforce.GetFloat(); - - float mass = PhysGetEntityMass( pEntity ); - if ( mass < 50.0f ) - { - pullDir *= (mass + 0.5) * (1/50.0f); - } - - // Nudge it towards us - pObj->ApplyForceCenter( pullDir ); - return OBJECT_NOT_FOUND; -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -CBaseEntity *CWeaponPhysCannon::MegaPhysCannonFindObjectInCone( const Vector &vecOrigin, - const Vector &vecDir, float flCone, float flCombineBallCone, bool bOnlyCombineBalls ) -{ - // Find the nearest physics-based item in a cone in front of me. - CBaseEntity *list[1024]; - float flMaxDist = TraceLength() + 1.0; - float flNearestDist = flMaxDist; - bool bNearestIsCombineBall = bOnlyCombineBalls ? true : false; - Vector mins = vecOrigin - Vector( flNearestDist, flNearestDist, flNearestDist ); - Vector maxs = vecOrigin + Vector( flNearestDist, flNearestDist, flNearestDist ); - - CBaseEntity *pNearest = NULL; - - int count = UTIL_EntitiesInBox( list, 1024, mins, maxs, 0 ); - for( int i = 0 ; i < count ; i++ ) - { - if ( !list[ i ]->VPhysicsGetObject() ) - continue; - - bool bIsCombineBall = FClassnameIs( list[ i ], "prop_combine_ball" ); - if ( !bIsCombineBall && bNearestIsCombineBall ) - continue; - - // Closer than other objects - Vector los; - VectorSubtract( list[ i ]->WorldSpaceCenter(), vecOrigin, los ); - float flDist = VectorNormalize( los ); - - if ( !bIsCombineBall || bNearestIsCombineBall ) - { - // Closer than other objects - if( flDist >= flNearestDist ) - continue; - - // Cull to the cone - if ( DotProduct( los, vecDir ) <= flCone ) - continue; - } - else - { - // Close enough? - if ( flDist >= flMaxDist ) - continue; - - // Cull to the cone - if ( DotProduct( los, vecDir ) <= flCone ) - continue; - - // NOW: If it's either closer than nearest dist or within the ball cone, use it! - if ( (flDist > flNearestDist) && (DotProduct( los, vecDir ) <= flCombineBallCone) ) - continue; - } - - // Make sure it isn't occluded! - trace_t tr; - UTIL_PhyscannonTraceLine( vecOrigin, list[ i ]->WorldSpaceCenter(), GetOwner(), &tr ); - if( tr.m_pEnt == list[ i ] ) - { - flNearestDist = flDist; - pNearest = list[ i ]; - bNearestIsCombineBall = bIsCombineBall; - } - } - - return pNearest; -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -CBaseEntity *CWeaponPhysCannon::FindObjectInCone( const Vector &vecOrigin, const Vector &vecDir, float flCone ) -{ - // Find the nearest physics-based item in a cone in front of me. - CBaseEntity *list[256]; - float flNearestDist = physcannon_tracelength.GetFloat() + 1.0; //Use regular distance. - Vector mins = vecOrigin - Vector( flNearestDist, flNearestDist, flNearestDist ); - Vector maxs = vecOrigin + Vector( flNearestDist, flNearestDist, flNearestDist ); - - CBaseEntity *pNearest = NULL; - - int count = UTIL_EntitiesInBox( list, 256, mins, maxs, 0 ); - for( int i = 0 ; i < count ; i++ ) - { - if ( !list[ i ]->VPhysicsGetObject() ) - continue; - - // Closer than other objects - Vector los = ( list[ i ]->WorldSpaceCenter() - vecOrigin ); - float flDist = VectorNormalize( los ); - if( flDist >= flNearestDist ) - continue; - - // Cull to the cone - if ( DotProduct( los, vecDir ) <= flCone ) - continue; - - // Make sure it isn't occluded! - trace_t tr; - UTIL_PhyscannonTraceLine( vecOrigin, list[ i ]->WorldSpaceCenter(), GetOwner(), &tr ); - if( tr.m_pEnt == list[ i ] ) - { - flNearestDist = flDist; - pNearest = list[ i ]; - } - } - - return pNearest; -} - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -bool CGrabController::UpdateObject( CBasePlayer *pPlayer, float flError ) -{ - CBaseEntity *pEntity = GetAttached(); - if ( !pEntity || ComputeError() > flError || pPlayer->GetGroundEntity() == pEntity || !pEntity->VPhysicsGetObject() ) - { - return false; - } - - //Adrian: Oops, our object became motion disabled, let go! - IPhysicsObject *pPhys = pEntity->VPhysicsGetObject(); - if ( pPhys && pPhys->IsMoveable() == false ) - { - return false; - } - - Vector forward, right, up; - QAngle playerAngles = pPlayer->EyeAngles(); - AngleVectors( playerAngles, &forward, &right, &up ); - - if ( HL2GameRules()->MegaPhyscannonActive() ) - { - Vector los = ( pEntity->WorldSpaceCenter() - pPlayer->Weapon_ShootPosition() ); - VectorNormalize( los ); - - float flDot = DotProduct( los, forward ); - - //Let go of the item if we turn around too fast. - if ( flDot <= 0.35f ) - return false; - } - - // If this is set to true, the player will be allowed to lift the held object to - // a position directly over their own head. Otherwise, this behavior is prevented (default). - bool bAllowObjectOverhead = UTIL_IsCombineBallDefinite(pEntity); - - - - float pitch = AngleDistance(playerAngles.x,0); - - if( !bAllowObjectOverhead ) - { - playerAngles.x = clamp( pitch, -75, 75 ); - } - else - { - playerAngles.x = clamp( pitch, -90, 75 ); - } - - - - // Now clamp a sphere of object radius at end to the player's bbox - Vector radial = physcollision->CollideGetExtent( pPhys->GetCollide(), vec3_origin, pEntity->GetAbsAngles(), -forward ); - Vector player2d = pPlayer->CollisionProp()->OBBMaxs(); - float playerRadius = player2d.Length2D(); - float radius = playerRadius + fabs(DotProduct( forward, radial )); - - float distance = 24 + ( radius * 2.0f ); - - // Add the prop's distance offset - distance += m_flDistanceOffset; - - Vector start = pPlayer->Weapon_ShootPosition(); - Vector end = start + ( forward * distance ); - - trace_t tr; - CTraceFilterSkipTwoEntities traceFilter( pPlayer, pEntity, COLLISION_GROUP_NONE ); - Ray_t ray; - ray.Init( start, end ); - enginetrace->TraceRay( ray, MASK_SOLID_BRUSHONLY, &traceFilter, &tr ); - - if ( tr.fraction < 0.5 ) - { - end = start + forward * (radius*0.5f); - } - else if ( tr.fraction <= 1.0f ) - { - end = start + forward * ( distance - radius ); - } - Vector playerMins, playerMaxs, nearest; - pPlayer->CollisionProp()->WorldSpaceAABB( &playerMins, &playerMaxs ); - Vector playerLine = pPlayer->CollisionProp()->WorldSpaceCenter(); - CalcClosestPointOnLine( end, playerLine+Vector(0,0,playerMins.z), playerLine+Vector(0,0,playerMaxs.z), nearest, NULL ); - - if( !bAllowObjectOverhead ) - { - Vector delta = end - nearest; - float len = VectorNormalize(delta); - if ( len < radius ) - { - end = nearest + radius * delta; - } - } - - //Show overlays of radius - if ( g_debug_physcannon.GetBool() ) - { - NDebugOverlay::Box( end, -Vector( 2,2,2 ), Vector(2,2,2), 0, 255, 0, true, 0 ); - - NDebugOverlay::Box( GetAttached()->WorldSpaceCenter(), - -Vector( radius, radius, radius), - Vector( radius, radius, radius ), - 255, 0, 0, - true, - 0.0f ); - } - - QAngle angles = TransformAnglesFromPlayerSpace( m_attachedAnglesPlayerSpace, pPlayer ); - - // If it has a preferred orientation, update to ensure we're still oriented correctly. - Pickup_GetPreferredCarryAngles( pEntity, pPlayer, pPlayer->EntityToWorldTransform(), angles ); - - // We may be holding a prop that has preferred carry angles - if ( m_bHasPreferredCarryAngles ) - { - matrix3x4_t tmp; - ComputePlayerMatrix( pPlayer, tmp ); - angles = TransformAnglesToWorldSpace( m_vecPreferredCarryAngles, tmp ); - } - - matrix3x4_t attachedToWorld; - Vector offset; - AngleMatrix( angles, attachedToWorld ); - VectorRotate( m_attachedPositionObjectSpace, attachedToWorld, offset ); - - SetTargetPosition( end - offset, angles ); - - return true; -} - -void CWeaponPhysCannon::UpdateObject( void ) -{ - CBasePlayer *pPlayer = ToBasePlayer( GetOwner() ); - Assert( pPlayer ); - - float flError = IsMegaPhysCannon() ? 18 : 12; - if ( !m_grabController.UpdateObject( pPlayer, flError ) ) - { - DetachObject(); - return; - } -} - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -void CWeaponPhysCannon::DetachObject( bool playSound, bool wasLaunched ) -{ - if ( m_bActive == false ) - return; - - CHL2_Player *pOwner = (CHL2_Player *)ToBasePlayer( GetOwner() ); - if( pOwner != NULL ) - { - pOwner->EnableSprint( true ); - pOwner->SetMaxSpeed( hl2_normspeed.GetFloat() ); - - if( wasLaunched ) - { - pOwner->RumbleEffect( RUMBLE_357, 0, RUMBLE_FLAG_RESTART ); - } - } - - CBaseEntity *pObject = m_grabController.GetAttached(); - - m_grabController.DetachEntity( wasLaunched ); - - if ( pObject != NULL ) - { - Pickup_OnPhysGunDrop( pObject, pOwner, wasLaunched ? LAUNCHED_BY_CANNON : DROPPED_BY_CANNON ); - } - - // Stop our looping sound - if ( GetMotorSound() ) - { - (CSoundEnvelopeController::GetController()).SoundChangeVolume( GetMotorSound(), 0.0f, 1.0f ); - (CSoundEnvelopeController::GetController()).SoundChangePitch( GetMotorSound(), 50, 1.0f ); - } - - m_bActive = false; - - if ( playSound ) - { - //Play the detach sound - WeaponSound( MELEE_MISS ); - } - - RecordThrownObject( pObject ); -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -void CWeaponPhysCannon::ItemPreFrame() -{ - BaseClass::ItemPreFrame(); - - CBasePlayer *pOwner = ToBasePlayer( GetOwner() ); - - if ( pOwner == NULL ) - return; - - m_flElementPosition = UTIL_Approach( m_flElementDestination, m_flElementPosition, 0.1f ); - - CBaseViewModel *vm = pOwner->GetViewModel(); - - if ( vm != NULL ) - { - vm->SetPoseParameter( "active", m_flElementPosition ); - } - - // Update the object if the weapon is switched on. - if( m_bActive ) - { - UpdateObject(); - } - - if( gpGlobals->curtime >= m_flTimeNextObjectPurge ) - { - PurgeThrownObjects(); - m_flTimeNextObjectPurge = gpGlobals->curtime + 0.5f; - } -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CWeaponPhysCannon::CheckForTarget( void ) -{ - //See if we're suppressing this - if ( m_flCheckSuppressTime > gpGlobals->curtime ) - return; - - // holstered - if ( IsEffectActive( EF_NODRAW ) ) - return; - - CBasePlayer *pOwner = ToBasePlayer( GetOwner() ); - - if ( pOwner == NULL ) - return; - - if ( m_bActive ) - return; - - Vector aimDir; - pOwner->EyeVectors( &aimDir ); - - Vector startPos = pOwner->Weapon_ShootPosition(); - Vector endPos; - VectorMA( startPos, TraceLength(), aimDir, endPos ); - - trace_t tr; - UTIL_PhyscannonTraceHull( startPos, endPos, -Vector(4,4,4), Vector(4,4,4), GetOwner(), &tr ); - - if ( ( tr.fraction != 1.0f ) && ( tr.m_pEnt != NULL ) ) - { - // FIXME: Try just having the elements always open when pointed at a physics object - if ( CanPickupObject( tr.m_pEnt ) || Pickup_ForcePhysGunOpen( tr.m_pEnt, pOwner ) ) - // if ( ( tr.m_pEnt->VPhysicsGetObject() != NULL ) && ( tr.m_pEnt->GetMoveType() == MOVETYPE_VPHYSICS ) ) - { - m_nChangeState = ELEMENT_STATE_NONE; - OpenElements(); - return; - } - } - - // Close the elements after a delay to prevent overact state switching - if ( ( m_flElementDebounce < gpGlobals->curtime ) && ( m_nChangeState == ELEMENT_STATE_NONE ) ) - { - m_nChangeState = ELEMENT_STATE_CLOSED; - m_flElementDebounce = gpGlobals->curtime + 0.5f; - } -} - - -//----------------------------------------------------------------------------- -// Begin upgrading! -//----------------------------------------------------------------------------- -void CWeaponPhysCannon::BeginUpgrade() -{ - if ( IsMegaPhysCannon() ) - return; - - if ( m_bIsCurrentlyUpgrading ) - return; - - SetSequence( SelectWeightedSequence( ACT_PHYSCANNON_UPGRADE ) ); - ResetSequenceInfo(); - - m_bIsCurrentlyUpgrading = true; - - SetContextThink( &CWeaponPhysCannon::WaitForUpgradeThink, gpGlobals->curtime + 6.0f, s_pWaitForUpgradeContext ); - - EmitSound( "WeaponDissolve.Charge" ); - - // Bloat our bounds - CollisionProp()->UseTriggerBounds( true, 32.0f ); - - // Turn on the new skin - m_nSkin = MEGACANNON_SKIN; -} - - -//----------------------------------------------------------------------------- -// Wait until we're done upgrading -//----------------------------------------------------------------------------- -void CWeaponPhysCannon::WaitForUpgradeThink() -{ - Assert( m_bIsCurrentlyUpgrading ); - - StudioFrameAdvance(); - if ( !IsActivityFinished() ) - { - SetContextThink( &CWeaponPhysCannon::WaitForUpgradeThink, gpGlobals->curtime + 0.1f, s_pWaitForUpgradeContext ); - return; - } - - if ( !GlobalEntity_IsInTable( "super_phys_gun" ) ) - { - GlobalEntity_Add( MAKE_STRING("super_phys_gun"), gpGlobals->mapname, GLOBAL_ON ); - } - else - { - GlobalEntity_SetState( MAKE_STRING("super_phys_gun"), GLOBAL_ON ); - } - m_bIsCurrentlyUpgrading = false; - - // This is necessary to get the effects to look different - DestroyEffects(); - - // HACK: Hacky notification back to the level that we've finish upgrading - CBaseEntity *pEnt = gEntList.FindEntityByName( NULL, "script_physcannon_upgrade" ); - if ( pEnt ) - { - variant_t emptyVariant; - pEnt->AcceptInput( "Trigger", this, this, emptyVariant, 0 ); - } - - StopSound( "WeaponDissolve.Charge" ); - - // Re-enable weapon pickup - AddSolidFlags( FSOLID_TRIGGER ); - - SetContextThink( NULL, gpGlobals->curtime, s_pWaitForUpgradeContext ); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CWeaponPhysCannon::DoEffectIdle( void ) -{ - if ( IsEffectActive( EF_NODRAW ) ) - { - StopEffects(); - return; - } - - CBasePlayer *pOwner = ToBasePlayer( GetOwner() ); - - if ( pOwner == NULL ) - return; - - if ( m_bPhyscannonState != IsMegaPhysCannon() ) - { - DestroyEffects(); - StartEffects(); - - m_bPhyscannonState = IsMegaPhysCannon(); - - //This means we just switched to regular physcannon this frame. - if ( m_bPhyscannonState == false ) - { - EmitSound( "Weapon_Physgun.Off" ); - -#ifdef HL2_EPISODIC - ForceDrop(); - - CHL2_Player *pPlayer = dynamic_cast( pOwner ); - - if ( pPlayer ) - { - pPlayer->StartArmorReduction(); - } -#endif - - CCitadelEnergyCore *pCore = static_cast( CreateEntityByName( "env_citadel_energy_core" ) ); - - if ( pCore == NULL ) - return; - - CBaseAnimating *pBeamEnt = pOwner->GetViewModel(); - - if ( pBeamEnt ) - { - int iAttachment = pBeamEnt->LookupAttachment( "muzzle" ); - - Vector vOrigin; - QAngle vAngle; - - pBeamEnt->GetAttachment( iAttachment, vOrigin, vAngle ); - - pCore->SetAbsOrigin( vOrigin ); - pCore->SetAbsAngles( vAngle ); - - DispatchSpawn( pCore ); - pCore->Activate(); - - pCore->SetParent( pBeamEnt, iAttachment ); - pCore->SetScale( 2.5f ); - - variant_t variant; - variant.SetFloat( 1.0f ); - - g_EventQueue.AddEvent( pCore, "StartDischarge", 0, pOwner, pOwner ); - g_EventQueue.AddEvent( pCore, "Stop", variant, 1, pOwner, pOwner ); - - pCore->SetThink ( &CCitadelEnergyCore::SUB_Remove ); - pCore->SetNextThink( gpGlobals->curtime + 10.0f ); - - m_nSkin = 0; - } - } - } - - float flScaleFactor = SpriteScaleFactor(); - - // Flicker the end sprites - if ( ( m_hEndSprites[0] != NULL ) && ( m_hEndSprites[1] != NULL ) ) - { - //Make the end points flicker as fast as possible - //FIXME: Make this a property of the CSprite class! - for ( int i = 0; i < 2; i++ ) - { - m_hEndSprites[i]->SetBrightness( random->RandomInt( 200, 255 ) ); - m_hEndSprites[i]->SetScale( random->RandomFloat( 0.1, 0.15 ) * flScaleFactor ); - } - } - - // Flicker the glow sprites - for ( int i = 0; i < NUM_SPRITES; i++ ) - { - if ( m_hGlowSprites[i] == NULL ) - continue; - - if ( IsMegaPhysCannon() ) - { - m_hGlowSprites[i]->SetBrightness( random->RandomInt( 32, 48 ) ); - m_hGlowSprites[i]->SetScale( random->RandomFloat( 0.15, 0.2 ) * flScaleFactor ); - } - else - { - m_hGlowSprites[i]->SetBrightness( random->RandomInt( 16, 24 ) ); - m_hGlowSprites[i]->SetScale( random->RandomFloat( 0.3, 0.35 ) * flScaleFactor ); - } - } - - // Only do these effects on the mega-cannon - if ( IsMegaPhysCannon() ) - { - // Randomly arc between the elements and core - if ( random->RandomInt( 0, 100 ) == 0 && !engine->IsPaused() ) - { - CBeam *pBeam = CBeam::BeamCreate( MEGACANNON_BEAM_SPRITE, 1 ); - - CBaseEntity *pBeamEnt = pOwner->GetViewModel(); - pBeam->EntsInit( pBeamEnt, pBeamEnt ); - - int startAttachment; - int sprite; - - if ( random->RandomInt( 0, 1 ) ) - { - startAttachment = LookupAttachment( "fork1t" ); - sprite = 0; - } - else - { - startAttachment = LookupAttachment( "fork2t" ); - sprite = 1; - } - - int endAttachment = 1; - - pBeam->SetStartAttachment( startAttachment ); - pBeam->SetEndAttachment( endAttachment ); - pBeam->SetNoise( random->RandomFloat( 8.0f, 16.0f ) ); - pBeam->SetColor( 255, 255, 255 ); - pBeam->SetScrollRate( 25 ); - pBeam->SetBrightness( 128 ); - pBeam->SetWidth( 1 ); - pBeam->SetEndWidth( random->RandomFloat( 2, 8 ) ); - - float lifetime = random->RandomFloat( 0.2f, 0.4f ); - - pBeam->LiveForTime( lifetime ); - - if ( m_hEndSprites[sprite] != NULL ) - { - // Turn on the sprite for awhile - m_hEndSprites[sprite]->TurnOn(); - m_flEndSpritesOverride[sprite] = gpGlobals->curtime + lifetime; - EmitSound( "Weapon_MegaPhysCannon.ChargeZap" ); - } - } - - if ( m_hCenterSprite != NULL ) - { - if ( m_EffectState == EFFECT_HOLDING ) - { - m_hCenterSprite->SetBrightness( random->RandomInt( 32, 64 ) ); - m_hCenterSprite->SetScale( random->RandomFloat( 0.2, 0.25 ) * flScaleFactor ); - } - else - { - m_hCenterSprite->SetBrightness( random->RandomInt( 32, 64 ) ); - m_hCenterSprite->SetScale( random->RandomFloat( 0.125, 0.15 ) * flScaleFactor ); - } - } - - if ( m_hBlastSprite != NULL ) - { - if ( m_EffectState == EFFECT_HOLDING ) - { - m_hBlastSprite->SetBrightness( random->RandomInt( 125, 150 ) ); - m_hBlastSprite->SetScale( random->RandomFloat( 0.125, 0.15 ) * flScaleFactor ); - } - else - { - m_hBlastSprite->SetBrightness( random->RandomInt( 32, 64 ) ); - m_hBlastSprite->SetScale( random->RandomFloat( 0.075, 0.15 ) * flScaleFactor ); - } - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: Update our idle effects even when deploying -//----------------------------------------------------------------------------- -void CWeaponPhysCannon::ItemBusyFrame( void ) -{ - DoEffectIdle(); - - BaseClass::ItemBusyFrame(); -} - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -void CWeaponPhysCannon::ItemPostFrame() -{ - CBasePlayer *pOwner = ToBasePlayer( GetOwner() ); - if ( pOwner == NULL ) - { - // We found an object. Debounce the button - m_nAttack2Debounce = 0; - return; - } - - //Check for object in pickup range - if ( m_bActive == false ) - { - CheckForTarget(); - - if ( ( m_flElementDebounce < gpGlobals->curtime ) && ( m_nChangeState != ELEMENT_STATE_NONE ) ) - { - if ( m_nChangeState == ELEMENT_STATE_OPEN ) - { - OpenElements(); - } - else if ( m_nChangeState == ELEMENT_STATE_CLOSED ) - { - CloseElements(); - } - - m_nChangeState = ELEMENT_STATE_NONE; - } - } - - // NOTE: Attack2 will be considered to be pressed until the first item is picked up. - int nAttack2Mask = pOwner->m_nButtons & (~m_nAttack2Debounce); - if ( nAttack2Mask & IN_ATTACK2 ) - { - SecondaryAttack(); - } - else - { - // Reset our debouncer - m_flLastDenySoundPlayed = false; - - if ( m_bActive == false ) - { - DoEffect( EFFECT_READY ); - } - } - - if (( pOwner->m_nButtons & IN_ATTACK2 ) == 0 ) - { - m_nAttack2Debounce = 0; - } - - if ( pOwner->m_nButtons & IN_ATTACK ) - { - PrimaryAttack(); - } - else - { - WeaponIdle(); - } - - if ( hl2_episodic.GetBool() == true ) - { - if ( IsMegaPhysCannon() ) - { - if ( !( pOwner->m_nButtons & IN_ATTACK ) ) - { - m_flNextPrimaryAttack = gpGlobals->curtime; - } - } - } - - // Update our idle effects (flickers, etc) - DoEffectIdle(); -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -#define PHYSCANNON_DANGER_SOUND_RADIUS 128 - -void CWeaponPhysCannon::LaunchObject( const Vector &vecDir, float flForce ) -{ - // FIRE!!! - if( m_grabController.GetAttached() ) - { - CBaseEntity *pObject = m_grabController.GetAttached(); - - DetachObject( false, true ); - - // Trace ahead a bit and make a chain of danger sounds ahead of the phys object - // to scare potential targets - trace_t tr; - Vector vecStart = pObject->GetAbsOrigin(); - Vector vecSpot; - int iLength; - int i; - - UTIL_TraceLine( vecStart, vecStart + vecDir * flForce, MASK_SHOT, pObject, COLLISION_GROUP_NONE, &tr ); - iLength = ( tr.startpos - tr.endpos ).Length(); - vecSpot = vecStart + vecDir * PHYSCANNON_DANGER_SOUND_RADIUS; - - for( i = PHYSCANNON_DANGER_SOUND_RADIUS ; i < iLength ; i += PHYSCANNON_DANGER_SOUND_RADIUS ) - { - CSoundEnt::InsertSound( SOUND_PHYSICS_DANGER, vecSpot, PHYSCANNON_DANGER_SOUND_RADIUS, 0.5, pObject ); - vecSpot = vecSpot + ( vecDir * PHYSCANNON_DANGER_SOUND_RADIUS ); - } - - // Launch - ApplyVelocityBasedForce( pObject, vecDir ); - - // Don't allow the gun to regrab a thrown object!! - m_flNextSecondaryAttack = m_flNextPrimaryAttack = gpGlobals->curtime + 0.5; - - Vector center = pObject->WorldSpaceCenter(); - - //Do repulse effect - DoEffect( EFFECT_LAUNCH, ¢er ); - } - - // Stop our looping sound - if ( GetMotorSound() ) - { - (CSoundEnvelopeController::GetController()).SoundChangeVolume( GetMotorSound(), 0.0f, 1.0f ); - (CSoundEnvelopeController::GetController()).SoundChangePitch( GetMotorSound(), 50, 1.0f ); - } - - //Close the elements and suppress checking for a bit - m_nChangeState = ELEMENT_STATE_CLOSED; - m_flElementDebounce = gpGlobals->curtime + 0.1f; - m_flCheckSuppressTime = gpGlobals->curtime + 0.25f; -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -bool CWeaponPhysCannon::IsFlesh( CBaseEntity *pTarget ) -{ - Assert( pTarget ); - IPhysicsObject *pList[64]; - int count = pTarget->VPhysicsGetObjectList( pList, ARRAYSIZE(pList) ); - for ( int i = 0; i < count; i++ ) - { - int material = pList[i]->GetMaterialIndex(); - surfacedata_t *pSurfaceData = physprops->GetSurfaceData( material ); - // Is flesh ?, don't allow pickup - if ( pSurfaceData->game.material == 'A' || pSurfaceData->game.material == 'F' || pSurfaceData->game.material == 'B' || pSurfaceData->game.material == 'H' ) - return true; - } - return false; -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *pTarget - -// Output : Returns true on success, false on failure. -//----------------------------------------------------------------------------- -bool CWeaponPhysCannon::CanPickupObject( CBaseEntity *pTarget ) -{ - if ( pTarget == NULL ) - return false; - - if ( pTarget->GetBaseAnimating() && pTarget->GetBaseAnimating()->IsDissolving() ) - return false; - - if ( pTarget->HasSpawnFlags( SF_PHYSBOX_ALWAYS_PICK_UP ) || pTarget->HasSpawnFlags( SF_PHYSBOX_NEVER_PICK_UP ) ) - { - // It may seem strange to check this spawnflag before we know the class of this object, since the - // spawnflag only applies to func_physbox, but it can act as a filter of sorts to reduce the number - // of irrelevant entities that fall through to this next casting check, which is slower. - CPhysBox *pPhysBox = dynamic_cast(pTarget); - - if ( pPhysBox != NULL ) - { - if ( pTarget->HasSpawnFlags( SF_PHYSBOX_NEVER_PICK_UP ) ) - return false; - else - return true; - } - } - - if ( pTarget->HasSpawnFlags(SF_PHYSPROP_ALWAYS_PICK_UP) ) - { - // It may seem strange to check this spawnflag before we know the class of this object, since the - // spawnflag only applies to func_physbox, but it can act as a filter of sorts to reduce the number - // of irrelevant entities that fall through to this next casting check, which is slower. - CPhysicsProp *pPhysProp = dynamic_cast(pTarget); - if ( pPhysProp != NULL ) - return true; - } - - if ( pTarget->IsEFlagSet( EFL_NO_PHYSCANNON_INTERACTION ) ) - return false; - - CBasePlayer *pOwner = ToBasePlayer( GetOwner() ); - if ( pOwner && pOwner->GetGroundEntity() == pTarget ) - return false; - - if ( !IsMegaPhysCannon() ) - { - if ( IsFlesh( pTarget ) ) - return false; - return CBasePlayer::CanPickupObject( pTarget, physcannon_maxmass.GetFloat(), 0 ); - } - - if ( pTarget->IsNPC() && pTarget->MyNPCPointer()->CanBecomeRagdoll() ) - return true; - - if ( dynamic_cast(pTarget) ) - return true; - - return CBasePlayer::CanPickupObject( pTarget, 0, 0 ); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CWeaponPhysCannon::OpenElements( void ) -{ - if ( m_bOpen ) - return; - - WeaponSound( SPECIAL2 ); - - CBasePlayer *pOwner = ToBasePlayer( GetOwner() ); - - if ( pOwner == NULL ) - return; - - if( !IsMegaPhysCannon() ) - { - pOwner->RumbleEffect(RUMBLE_PHYSCANNON_OPEN, 0, RUMBLE_FLAG_RESTART|RUMBLE_FLAG_LOOP); - } - - if ( m_flElementPosition < 0.0f ) - m_flElementPosition = 0.0f; - - m_flElementDestination = 1.0f; - - SendWeaponAnim( ACT_VM_IDLE ); - - m_bOpen = true; - - DoEffect( EFFECT_READY ); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CWeaponPhysCannon::CloseElements( void ) -{ - // The mega cannon cannot be closed! - if ( IsMegaPhysCannon() ) - { - OpenElements(); - return; - } - - if ( m_bOpen == false ) - return; - - WeaponSound( MELEE_HIT ); - - CBasePlayer *pOwner = ToBasePlayer( GetOwner() ); - - if ( pOwner == NULL ) - return; - - pOwner->RumbleEffect(RUMBLE_PHYSCANNON_OPEN, 0, RUMBLE_FLAG_STOP); - - if ( m_flElementPosition > 1.0f ) - m_flElementPosition = 1.0f; - - m_flElementDestination = 0.0f; - - SendWeaponAnim( ACT_VM_IDLE ); - - m_bOpen = false; - - if ( GetMotorSound() ) - { - (CSoundEnvelopeController::GetController()).SoundChangeVolume( GetMotorSound(), 0.0f, 1.0f ); - (CSoundEnvelopeController::GetController()).SoundChangePitch( GetMotorSound(), 50, 1.0f ); - } - - DoEffect( EFFECT_CLOSED ); -} - -#define PHYSCANNON_MAX_MASS 500 - - -//----------------------------------------------------------------------------- -// Purpose: -// Output : float -//----------------------------------------------------------------------------- -float CWeaponPhysCannon::GetLoadPercentage( void ) -{ - float loadWeight = m_grabController.GetLoadWeight(); - loadWeight /= physcannon_maxmass.GetFloat(); - loadWeight = clamp( loadWeight, 0.0f, 1.0f ); - return loadWeight; -} - - -//----------------------------------------------------------------------------- -// Purpose: -// Output : CSoundPatch -//----------------------------------------------------------------------------- -CSoundPatch *CWeaponPhysCannon::GetMotorSound( void ) -{ - if ( m_sndMotor == NULL ) - { - CPASAttenuationFilter filter( this ); - - if ( IsMegaPhysCannon() ) - { - m_sndMotor = (CSoundEnvelopeController::GetController()).SoundCreate( filter, entindex(), CHAN_STATIC, "Weapon_MegaPhysCannon.HoldSound", ATTN_NORM ); - } - else - { - m_sndMotor = (CSoundEnvelopeController::GetController()).SoundCreate( filter, entindex(), CHAN_STATIC, "Weapon_PhysCannon.HoldSound", ATTN_NORM ); - } - } - - return m_sndMotor; -} - - -//----------------------------------------------------------------------------- -// Shuts down sounds -//----------------------------------------------------------------------------- -void CWeaponPhysCannon::StopLoopingSounds() -{ - if ( m_sndMotor != NULL ) - { - (CSoundEnvelopeController::GetController()).SoundDestroy( m_sndMotor ); - m_sndMotor = NULL; - } - - BaseClass::StopLoopingSounds(); -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CWeaponPhysCannon::DestroyEffects( void ) -{ - //Turn off main glow - if ( m_hCenterSprite != NULL ) - { - UTIL_Remove( m_hCenterSprite ); - m_hCenterSprite = NULL; - } - - if ( m_hBlastSprite != NULL ) - { - UTIL_Remove( m_hBlastSprite ); - m_hBlastSprite = NULL; - } - - // Turn off beams - for ( int i = 0; i < NUM_BEAMS; i++ ) - { - if ( m_hBeams[i] != NULL ) - { - UTIL_Remove( m_hBeams[i] ); - m_hBeams[i] = NULL; - } - } - - // Turn off sprites - for ( int i = 0; i < NUM_SPRITES; i++ ) - { - if ( m_hGlowSprites[i] != NULL ) - { - UTIL_Remove( m_hGlowSprites[i] ); - m_hGlowSprites[i] = NULL; - } - } - - for ( int i = 0; i < 2; i++ ) - { - if ( m_hEndSprites[i] != NULL ) - { - UTIL_Remove( m_hEndSprites[i] ); - m_hEndSprites[i] = NULL; - } - } -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CWeaponPhysCannon::StopEffects( bool stopSound ) -{ - // Turn off our effect state - DoEffect( EFFECT_NONE ); - - //Turn off main glow - if ( m_hCenterSprite != NULL ) - { - m_hCenterSprite->TurnOff(); - } - - if ( m_hBlastSprite != NULL ) - { - m_hBlastSprite->TurnOff(); - } - - //Turn off beams - for ( int i = 0; i < NUM_BEAMS; i++ ) - { - if ( m_hBeams[i] != NULL ) - { - m_hBeams[i]->SetBrightness( 0 ); - } - } - - //Turn off sprites - for ( int i = 0; i < NUM_SPRITES; i++ ) - { - if ( m_hGlowSprites[i] != NULL ) - { - m_hGlowSprites[i]->TurnOff(); - } - } - - for ( int i = 0; i < 2; i++ ) - { - if ( m_hEndSprites[i] != NULL ) - { - m_hEndSprites[i]->TurnOff(); - } - } - - //Shut off sounds - if ( stopSound && GetMotorSound() != NULL ) - { - (CSoundEnvelopeController::GetController()).SoundFadeOut( GetMotorSound(), 0.1f ); - } -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CWeaponPhysCannon::StartEffects( void ) -{ - CBasePlayer *pOwner = ToBasePlayer( GetOwner() ); - if ( pOwner == NULL ) - return; - - bool bIsMegaCannon = IsMegaPhysCannon(); - - int i; - float flScaleFactor = SpriteScaleFactor(); - CBaseEntity *pBeamEnt = pOwner->GetViewModel(); - - // Create the beams - for ( i = 0; i < NUM_BEAMS; i++ ) - { - if ( m_hBeams[i] ) - continue; - - const char *beamAttachNames[] = - { - "fork1t", - "fork2t", - "fork1t", - "fork2t", - "fork1t", - "fork2t", - }; - - m_hBeams[i] = CBeam::BeamCreate( - bIsMegaCannon ? MEGACANNON_BEAM_SPRITE : PHYSCANNON_BEAM_SPRITE, 1.0f ); - m_hBeams[i]->EntsInit( pBeamEnt, pBeamEnt ); - - int startAttachment = LookupAttachment( beamAttachNames[i] ); - int endAttachment = 1; - - m_hBeams[i]->FollowEntity( pBeamEnt ); - - m_hBeams[i]->AddSpawnFlags( SF_BEAM_TEMPORARY ); - m_hBeams[i]->SetStartAttachment( startAttachment ); - m_hBeams[i]->SetEndAttachment( endAttachment ); - m_hBeams[i]->SetNoise( random->RandomFloat( 8.0f, 16.0f ) ); - m_hBeams[i]->SetColor( 255, 255, 255 ); - m_hBeams[i]->SetScrollRate( 25 ); - m_hBeams[i]->SetBrightness( 128 ); - m_hBeams[i]->SetWidth( 0 ); - m_hBeams[i]->SetEndWidth( random->RandomFloat( 2, 4 ) ); - } - - //Create the glow sprites - for ( i = 0; i < NUM_SPRITES; i++ ) - { - if ( m_hGlowSprites[i] ) - continue; - - const char *attachNames[] = - { - "fork1b", - "fork1m", - "fork1t", - "fork2b", - "fork2m", - "fork2t" - }; - - m_hGlowSprites[i] = CSprite::SpriteCreate( - bIsMegaCannon ? MEGACANNON_GLOW_SPRITE : PHYSCANNON_GLOW_SPRITE, - GetAbsOrigin(), false ); - - m_hGlowSprites[i]->SetAsTemporary(); - - m_hGlowSprites[i]->SetAttachment( pOwner->GetViewModel(), LookupAttachment( attachNames[i] ) ); - - if ( bIsMegaCannon ) - { - m_hGlowSprites[i]->SetTransparency( kRenderTransAdd, 255, 255, 255, 128, kRenderFxNone ); - } - else - { - m_hGlowSprites[i]->SetTransparency( kRenderTransAdd, 255, 128, 0, 64, kRenderFxNoDissipation ); - } - - m_hGlowSprites[i]->SetBrightness( 255, 0.2f ); - m_hGlowSprites[i]->SetScale( 0.25f * flScaleFactor, 0.2f ); - } - - //Create the endcap sprites - for ( i = 0; i < 2; i++ ) - { - if ( m_hEndSprites[i] == NULL ) - { - const char *attachNames[] = - { - "fork1t", - "fork2t" - }; - - m_hEndSprites[i] = CSprite::SpriteCreate( - bIsMegaCannon ? MEGACANNON_ENDCAP_SPRITE : PHYSCANNON_ENDCAP_SPRITE, - GetAbsOrigin(), false ); - - m_hEndSprites[i]->SetAsTemporary(); - m_hEndSprites[i]->SetAttachment( pOwner->GetViewModel(), LookupAttachment( attachNames[i] ) ); - m_hEndSprites[i]->SetTransparency( kRenderTransAdd, 255, 255, 255, 255, kRenderFxNoDissipation ); - m_hEndSprites[i]->SetBrightness( 255, 0.2f ); - m_hEndSprites[i]->SetScale( 0.25f * flScaleFactor, 0.2f ); - m_hEndSprites[i]->TurnOff(); - } - } - - //Create the center glow - if ( m_hCenterSprite == NULL ) - { - m_hCenterSprite = CSprite::SpriteCreate( - bIsMegaCannon ? MEGACANNON_CENTER_GLOW : PHYSCANNON_CENTER_GLOW, - GetAbsOrigin(), false ); - - m_hCenterSprite->SetAsTemporary(); - m_hCenterSprite->SetAttachment( pOwner->GetViewModel(), 1 ); - m_hCenterSprite->SetTransparency( kRenderTransAdd, 255, 255, 255, 255, kRenderFxNone ); - m_hCenterSprite->SetBrightness( 255, 0.2f ); - m_hCenterSprite->SetScale( 0.1f, 0.2f ); - } - - //Create the blast sprite - if ( m_hBlastSprite == NULL ) - { - m_hBlastSprite = CSprite::SpriteCreate( - bIsMegaCannon ? MEGACANNON_BLAST_SPRITE : PHYSCANNON_BLAST_SPRITE, - GetAbsOrigin(), false ); - - m_hBlastSprite->SetAsTemporary(); - m_hBlastSprite->SetAttachment( pOwner->GetViewModel(), 1 ); - m_hBlastSprite->SetTransparency( kRenderTransAdd, 255, 255, 255, 255, kRenderFxNone ); - m_hBlastSprite->SetBrightness( 255, 0.2f ); - m_hBlastSprite->SetScale( 0.1f, 0.2f ); - m_hBlastSprite->TurnOff(); - } -} - -//----------------------------------------------------------------------------- -// Closing effects -//----------------------------------------------------------------------------- -void CWeaponPhysCannon::DoEffectClosed( void ) -{ - float flScaleFactor = SpriteScaleFactor(); - - // Turn off the center sprite - if ( m_hCenterSprite != NULL ) - { - m_hCenterSprite->SetBrightness( 0.0, 0.1f ); - m_hCenterSprite->SetScale( 0.0f, 0.1f ); - m_hCenterSprite->TurnOff(); - } - - // Turn off the end-caps - for ( int i = 0; i < 2; i++ ) - { - if ( m_hEndSprites[i] != NULL ) - { - m_hEndSprites[i]->TurnOff(); - } - } - - // Turn off the lightning - for ( int i = 0; i < NUM_BEAMS; i++ ) - { - if ( m_hBeams[i] != NULL ) - { - m_hBeams[i]->SetBrightness( 0 ); - } - } - - // Turn on the glow sprites - for ( int i = 0; i < NUM_SPRITES; i++ ) - { - if ( m_hGlowSprites[i] != NULL ) - { - m_hGlowSprites[i]->TurnOn(); - m_hGlowSprites[i]->SetBrightness( 16.0f, 0.2f ); - m_hGlowSprites[i]->SetScale( 0.3f * flScaleFactor, 0.2f ); - } - } - - // Prepare for scale down - if ( m_hBlastSprite != NULL ) - { - m_hBlastSprite->TurnOn(); - m_hBlastSprite->SetScale( 1.0f, 0.0f ); - m_hBlastSprite->SetBrightness( 0, 0.0f ); - } -} - -//----------------------------------------------------------------------------- -// Closing effects -//----------------------------------------------------------------------------- -void CWeaponPhysCannon::DoMegaEffectClosed( void ) -{ - float flScaleFactor = SpriteScaleFactor(); - - // Turn off the center sprite - if ( m_hCenterSprite != NULL ) - { - m_hCenterSprite->SetBrightness( 0.0, 0.1f ); - m_hCenterSprite->SetScale( 0.0f, 0.1f ); - m_hCenterSprite->TurnOff(); - } - - // Turn off the end-caps - for ( int i = 0; i < 2; i++ ) - { - if ( m_hEndSprites[i] != NULL ) - { - m_hEndSprites[i]->TurnOff(); - } - } - - // Turn off the lightning - for ( int i = 0; i < NUM_BEAMS; i++ ) - { - if ( m_hBeams[i] != NULL ) - { - m_hBeams[i]->SetBrightness( 0 ); - } - } - - // Turn on the glow sprites - for ( int i = 0; i < NUM_SPRITES; i++ ) - { - if ( m_hGlowSprites[i] != NULL ) - { - m_hGlowSprites[i]->TurnOn(); - m_hGlowSprites[i]->SetBrightness( 16.0f, 0.2f ); - m_hGlowSprites[i]->SetScale( 0.3f * flScaleFactor, 0.2f ); - } - } - - // Prepare for scale down - if ( m_hBlastSprite != NULL ) - { - m_hBlastSprite->TurnOn(); - m_hBlastSprite->SetScale( 1.0f, 0.0f ); - m_hBlastSprite->SetBrightness( 0, 0.0f ); - } -} - -//----------------------------------------------------------------------------- -// Ready effects -//----------------------------------------------------------------------------- -void CWeaponPhysCannon::DoEffectReady( ) -{ - float flScaleFactor = SpriteScaleFactor(); - - //Turn on the center sprite - if ( m_hCenterSprite != NULL ) - { - m_hCenterSprite->SetBrightness( 128, 0.2f ); - m_hCenterSprite->SetScale( 0.15f, 0.2f ); - m_hCenterSprite->TurnOn(); - } - - //Turn off the end-caps - for ( int i = 0; i < 2; i++ ) - { - if ( m_hEndSprites[i] != NULL ) - { - m_hEndSprites[i]->TurnOff(); - } - } - - //Turn off the lightning - for ( int i = 0; i < NUM_BEAMS; i++ ) - { - if ( m_hBeams[i] != NULL ) - { - m_hBeams[i]->SetBrightness( 0 ); - } - } - - //Turn on the glow sprites - for ( int i = 0; i < NUM_SPRITES; i++ ) - { - if ( m_hGlowSprites[i] != NULL ) - { - m_hGlowSprites[i]->TurnOn(); - m_hGlowSprites[i]->SetBrightness( 32.0f, 0.2f ); - m_hGlowSprites[i]->SetScale( 0.4f * flScaleFactor, 0.2f ); - } - } - - //Scale down - if ( m_hBlastSprite != NULL ) - { - m_hBlastSprite->TurnOn(); - m_hBlastSprite->SetScale( 0.1f, 0.2f ); - m_hBlastSprite->SetBrightness( 255, 0.1f ); - } -} - - -//----------------------------------------------------------------------------- -// Holding effects -//----------------------------------------------------------------------------- -void CWeaponPhysCannon::DoEffectHolding( ) -{ - float flScaleFactor = SpriteScaleFactor(); - - // Turn off the center sprite - if ( m_hCenterSprite != NULL ) - { - m_hCenterSprite->SetBrightness( 255, 0.1f ); - m_hCenterSprite->SetScale( 0.2f, 0.2f ); - m_hCenterSprite->TurnOn(); - } - - // Turn off the end-caps - for ( int i = 0; i < 2; i++ ) - { - if ( m_hEndSprites[i] != NULL ) - { - m_hEndSprites[i]->TurnOn(); - } - } - - // Turn off the lightning - for ( int i = 0; i < NUM_BEAMS; i++ ) - { - if ( m_hBeams[i] != NULL ) - { - m_hBeams[i]->SetBrightness( 128 ); - } - } - - // Turn on the glow sprites - for ( int i = 0; i < NUM_SPRITES; i++ ) - { - if ( m_hGlowSprites[i] != NULL ) - { - m_hGlowSprites[i]->TurnOn(); - m_hGlowSprites[i]->SetBrightness( 64.0f, 0.2f ); - m_hGlowSprites[i]->SetScale( 0.5f * flScaleFactor, 0.2f ); - } - } - - // Prepare for scale up - if ( m_hBlastSprite != NULL ) - { - m_hBlastSprite->TurnOff(); - m_hBlastSprite->SetScale( 0.1f, 0.0f ); - m_hBlastSprite->SetBrightness( 0, 0.0f ); - } -} - - -//----------------------------------------------------------------------------- -// Launch effects -//----------------------------------------------------------------------------- -void CWeaponPhysCannon::DoEffectLaunch( Vector *pos ) -{ - Assert( pos ); - if ( pos == NULL ) - return; - - CBasePlayer *pOwner = ToBasePlayer( GetOwner() ); - if ( pOwner == NULL ) - return; - - Vector endpos = *pos; - - // Check to store off our view model index - CBeam *pBeam = CBeam::BeamCreate( IsMegaPhysCannon() ? MEGACANNON_BEAM_SPRITE : PHYSCANNON_BEAM_SPRITE, 8 ); - - if ( pBeam != NULL ) - { - pBeam->PointEntInit( endpos, this ); - pBeam->SetEndAttachment( 1 ); - pBeam->SetWidth( 6.4 ); - pBeam->SetEndWidth( 12.8 ); - pBeam->SetBrightness( 255 ); - pBeam->SetColor( 255, 255, 255 ); - pBeam->LiveForTime( 0.1f ); - pBeam->RelinkBeam(); - pBeam->SetNoise( 2 ); - } - - Vector shotDir = ( endpos - pOwner->Weapon_ShootPosition() ); - VectorNormalize( shotDir ); - - //End hit - //FIXME: Probably too big - CPVSFilter filter( endpos ); - te->GaussExplosion( filter, 0.0f, endpos - ( shotDir * 4.0f ), RandomVector(-1.0f, 1.0f), 0 ); - - if ( m_hBlastSprite != NULL ) - { - m_hBlastSprite->TurnOn(); - m_hBlastSprite->SetScale( 2.0f, 0.1f ); - m_hBlastSprite->SetBrightness( 0.0f, 0.1f ); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *pos - -//----------------------------------------------------------------------------- -void CWeaponPhysCannon::DoMegaEffectLaunch( Vector *pos ) -{ - Assert( pos ); - if ( pos == NULL ) - return; - - CBasePlayer *pOwner = ToBasePlayer( GetOwner() ); - if ( pOwner == NULL ) - return; - - Vector endpos = *pos; - - // Check to store off our view model index - CBaseViewModel *vm = pOwner->GetViewModel(); - - int numBeams = random->RandomInt( 1, 2 ); - - CBeam *pBeam = CBeam::BeamCreate( IsMegaPhysCannon() ? MEGACANNON_BEAM_SPRITE : PHYSCANNON_BEAM_SPRITE, 0.8 ); - - if ( pBeam != NULL ) - { - pBeam->PointEntInit( endpos, vm ); - pBeam->SetEndAttachment( 1 ); - pBeam->SetWidth( 2 ); - pBeam->SetEndWidth( 12 ); - pBeam->SetBrightness( 255 ); - pBeam->SetColor( 255, 255, 255 ); - pBeam->LiveForTime( 0.1f ); - pBeam->RelinkBeam(); - pBeam->SetNoise( 0 ); - } - - for ( int i = 0; i < numBeams; i++ ) - { - pBeam = CBeam::BeamCreate( IsMegaPhysCannon() ? MEGACANNON_BEAM_SPRITE : PHYSCANNON_BEAM_SPRITE, 0.8 ); - - if ( pBeam != NULL ) - { - pBeam->PointEntInit( endpos, vm ); - pBeam->SetEndAttachment( 1 ); - pBeam->SetWidth( 2 ); - pBeam->SetEndWidth( random->RandomInt( 1, 2 ) ); - pBeam->SetBrightness( 255 ); - pBeam->SetColor( 255, 255, 255 ); - pBeam->LiveForTime( 0.1f ); - pBeam->RelinkBeam(); - pBeam->SetNoise( random->RandomInt( 8, 12 ) ); - } - } - - Vector shotDir = ( endpos - pOwner->Weapon_ShootPosition() ); - VectorNormalize( shotDir ); - - //End hit - //FIXME: Probably too big - CPVSFilter filter( endpos ); - te->GaussExplosion( filter, 0.0f, endpos - ( shotDir * 4.0f ), RandomVector(-1.0f, 1.0f), 0 ); -} - -//----------------------------------------------------------------------------- -// Holding effects -//----------------------------------------------------------------------------- -void CWeaponPhysCannon::DoMegaEffectHolding( void ) -{ - float flScaleFactor = SpriteScaleFactor(); - - // Turn off the center sprite - if ( m_hCenterSprite != NULL ) - { - m_hCenterSprite->SetBrightness( 255, 0.1f ); - m_hCenterSprite->SetScale( 0.2f, 0.2f ); - m_hCenterSprite->TurnOn(); - } - - // Turn off the end-caps - for ( int i = 0; i < 2; i++ ) - { - if ( m_hEndSprites[i] != NULL ) - { - m_hEndSprites[i]->TurnOn(); - } - } - - // Turn off the lightning - for ( int i = 0; i < NUM_BEAMS; i++ ) - { - if ( m_hBeams[i] != NULL ) - { - m_hBeams[i]->SetBrightness( 128 ); - } - } - - // Turn on the glow sprites - for ( int i = 0; i < NUM_SPRITES; i++ ) - { - if ( m_hGlowSprites[i] != NULL ) - { - m_hGlowSprites[i]->TurnOn(); - m_hGlowSprites[i]->SetBrightness( 32.0f, 0.2f ); - m_hGlowSprites[i]->SetScale( 0.25f * flScaleFactor, 0.2f ); - } - } -} - -//----------------------------------------------------------------------------- -// Ready effects -//----------------------------------------------------------------------------- -void CWeaponPhysCannon::DoMegaEffectReady( void ) -{ - float flScaleFactor = SpriteScaleFactor(); - - //Turn on the center sprite - if ( m_hCenterSprite != NULL ) - { - m_hCenterSprite->SetBrightness( 128, 0.2f ); - m_hCenterSprite->SetScale( 0.15f, 0.2f ); - m_hCenterSprite->TurnOn(); - } - - //Turn off the end-caps - for ( int i = 0; i < 2; i++ ) - { - if ( m_hEndSprites[i] != NULL ) - { - if ( m_flEndSpritesOverride[i] < gpGlobals->curtime ) - { - m_hEndSprites[i]->TurnOff(); - } - } - } - - //Turn off the lightning - for ( int i = 0; i < NUM_BEAMS; i++ ) - { - if ( m_hBeams[i] != NULL ) - { - m_hBeams[i]->SetBrightness( 0 ); - } - } - - //Turn on the glow sprites - for ( int i = 0; i < NUM_SPRITES; i++ ) - { - if ( m_hGlowSprites[i] != NULL ) - { - m_hGlowSprites[i]->TurnOn(); - m_hGlowSprites[i]->SetBrightness( 24.0f, 0.2f ); - m_hGlowSprites[i]->SetScale( 0.2f * flScaleFactor, 0.2f ); - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: Shutdown for the weapon when it's holstered -//----------------------------------------------------------------------------- -void CWeaponPhysCannon::DoEffectNone( void ) -{ - if ( m_hBlastSprite != NULL ) - { - // Become small - m_hBlastSprite->SetScale( 0.001f ); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : effectType - -// *pos - -//----------------------------------------------------------------------------- -void CWeaponPhysCannon::DoMegaEffect( int effectType, Vector *pos ) -{ - switch( effectType ) - { - case EFFECT_CLOSED: - DoMegaEffectClosed(); - break; - - case EFFECT_READY: - DoMegaEffectReady(); - break; - - case EFFECT_HOLDING: - DoMegaEffectHolding(); - break; - - case EFFECT_LAUNCH: - DoMegaEffectLaunch( pos ); - break; - - default: - case EFFECT_NONE: - break; - } -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : effectType - -//----------------------------------------------------------------------------- -void CWeaponPhysCannon::DoEffect( int effectType, Vector *pos ) -{ - // Make sure we're active - StartEffects(); - - m_EffectState = effectType; - - // Do different effects when upgraded - if ( IsMegaPhysCannon() ) - { - DoMegaEffect( effectType, pos ); - return; - } - - switch( effectType ) - { - case EFFECT_CLOSED: - DoEffectClosed( ); - break; - - case EFFECT_READY: - DoEffectReady( ); - break; - - case EFFECT_HOLDING: - DoEffectHolding(); - break; - - case EFFECT_LAUNCH: - DoEffectLaunch( pos ); - break; - - default: - case EFFECT_NONE: - DoEffectNone(); - break; - } -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : iIndex - -// Output : const char -//----------------------------------------------------------------------------- -const char *CWeaponPhysCannon::GetShootSound( int iIndex ) const -{ - // Just do this normally if we're a normal physcannon - if ( PlayerHasMegaPhysCannon() == false ) - return BaseClass::GetShootSound( iIndex ); - - // We override this if we're the charged up version - switch( iIndex ) - { - case EMPTY: - return "Weapon_MegaPhysCannon.DryFire"; - break; - - case SINGLE: - return "Weapon_MegaPhysCannon.Launch"; - break; - - case SPECIAL1: - return "Weapon_MegaPhysCannon.Pickup"; - break; - - case MELEE_MISS: - return "Weapon_MegaPhysCannon.Drop"; - break; - - default: - break; - } - - return BaseClass::GetShootSound( iIndex ); -} - -//----------------------------------------------------------------------------- -// Purpose: Adds the specified object to the list of objects that have been -// propelled by this physgun, along with a timestamp of when the -// object was added to the list. This list is checked when a physics -// object strikes another entity, to resolve whether the player is -// accountable for the impact. -// -// Input : pObject - pointer to the object being thrown by the physcannon. -//----------------------------------------------------------------------------- -void CWeaponPhysCannon::RecordThrownObject( CBaseEntity *pObject ) -{ - thrown_objects_t thrown; - thrown.hEntity = pObject; - thrown.fTimeThrown = gpGlobals->curtime; - - // Get rid of stale and dead objects in the list. - PurgeThrownObjects(); - - // See if this object is already in the list. - int count = m_ThrownEntities.Count(); - - for( int i = 0 ; i < count ; i++ ) - { - if( m_ThrownEntities[i].hEntity == pObject ) - { - // Just update the time. - //Msg("++UPDATING: %s (%d)\n", m_ThrownEntities[i].hEntity->GetClassname(), m_ThrownEntities[i].hEntity->entindex() ); - m_ThrownEntities[i] = thrown; - return; - } - } - - //Msg("++ADDING: %s (%d)\n", pObject->GetClassname(), pObject->entindex() ); - - m_ThrownEntities.AddToTail(thrown); -} - -//----------------------------------------------------------------------------- -// Purpose: Go through the objects in the thrown objects list and discard any -// objects that have gone 'stale'. (Were thrown several seconds ago), or -// have been destroyed or removed. -// -//----------------------------------------------------------------------------- -#define PHYSCANNON_THROWN_LIST_TIMEOUT 10.0f -void CWeaponPhysCannon::PurgeThrownObjects() -{ - bool bListChanged; - - // This is bubble-sorty, but the list is also very short. - do - { - bListChanged = false; - - int count = m_ThrownEntities.Count(); - for( int i = 0 ; i < count ; i++ ) - { - bool bRemove = false; - - if( !m_ThrownEntities[i].hEntity.Get() ) - { - bRemove = true; - } - else if( gpGlobals->curtime > (m_ThrownEntities[i].fTimeThrown + PHYSCANNON_THROWN_LIST_TIMEOUT) ) - { - bRemove = true; - } - else - { - IPhysicsObject *pObject = m_ThrownEntities[i].hEntity->VPhysicsGetObject(); - - if( pObject && pObject->IsAsleep() ) - { - bRemove = true; - } - } - - if( bRemove ) - { - //Msg("--REMOVING: %s (%d)\n", m_ThrownEntities[i].hEntity->GetClassname(), m_ThrownEntities[i].hEntity->entindex() ); - m_ThrownEntities.Remove(i); - bListChanged = true; - break; - } - } - } while( bListChanged ); -} - -bool CWeaponPhysCannon::IsAccountableForObject( CBaseEntity *pObject ) -{ - // Clean out the stale and dead items. - PurgeThrownObjects(); - - // Now if this object is in the list, the player is accountable for it striking something. - int count = m_ThrownEntities.Count(); - - for( int i = 0 ; i < count ; i++ ) - { - if( m_ThrownEntities[i].hEntity == pObject ) - { - return true; - } - } - - return false; -} - -//----------------------------------------------------------------------------- -// EXTERNAL API -//----------------------------------------------------------------------------- -void PhysCannonForceDrop( CBaseCombatWeapon *pActiveWeapon, CBaseEntity *pOnlyIfHoldingThis ) -{ - CWeaponPhysCannon *pCannon = dynamic_cast(pActiveWeapon); - if ( pCannon ) - { - if ( pOnlyIfHoldingThis ) - { - pCannon->DropIfEntityHeld( pOnlyIfHoldingThis ); - } - else - { - pCannon->ForceDrop(); - } - } -} - -void PhysCannonBeginUpgrade( CBaseAnimating *pAnim ) -{ - CWeaponPhysCannon *pWeaponPhyscannon = assert_cast< CWeaponPhysCannon* >( pAnim ); - pWeaponPhyscannon->BeginUpgrade(); -} - - -bool PlayerPickupControllerIsHoldingEntity( CBaseEntity *pPickupControllerEntity, CBaseEntity *pHeldEntity ) -{ - CPlayerPickupController *pController = dynamic_cast(pPickupControllerEntity); - - return pController ? pController->IsHoldingEntity( pHeldEntity ) : false; -} - - -float PhysCannonGetHeldObjectMass( CBaseCombatWeapon *pActiveWeapon, IPhysicsObject *pHeldObject ) -{ - float mass = 0.0f; - CWeaponPhysCannon *pCannon = dynamic_cast(pActiveWeapon); - if ( pCannon ) - { - CGrabController &grab = pCannon->GetGrabController(); - mass = grab.GetSavedMass( pHeldObject ); - } - - return mass; -} - -CBaseEntity *PhysCannonGetHeldEntity( CBaseCombatWeapon *pActiveWeapon ) -{ - CWeaponPhysCannon *pCannon = dynamic_cast(pActiveWeapon); - if ( pCannon ) - { - CGrabController &grab = pCannon->GetGrabController(); - return grab.GetAttached(); - } - - return NULL; -} - -CBaseEntity *GetPlayerHeldEntity( CBasePlayer *pPlayer ) -{ - CBaseEntity *pObject = NULL; - CPlayerPickupController *pPlayerPickupController = (CPlayerPickupController *)(pPlayer->GetUseEntity()); - - if ( pPlayerPickupController ) - { - pObject = pPlayerPickupController->GetGrabController().GetAttached(); - } - - return pObject; -} - -bool PhysCannonAccountableForObject( CBaseCombatWeapon *pPhysCannon, CBaseEntity *pObject ) -{ - CWeaponPhysCannon *pCannon = dynamic_cast(pPhysCannon); - if ( pCannon ) - { - return pCannon->IsAccountableForObject(pObject); - } - - return false; -} - -float PlayerPickupGetHeldObjectMass( CBaseEntity *pPickupControllerEntity, IPhysicsObject *pHeldObject ) -{ - float mass = 0.0f; - CPlayerPickupController *pController = dynamic_cast(pPickupControllerEntity); - if ( pController ) - { - CGrabController &grab = pController->GetGrabController(); - mass = grab.GetSavedMass( pHeldObject ); - } - return mass; -} +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: Physics cannon +// +//=============================================================================// + +#include "cbase.h" +#include "player.h" +#include "gamerules.h" +#include "soundenvelope.h" +#include "engine/IEngineSound.h" +#include "physics.h" +#include "in_buttons.h" +#include "soundent.h" +#include "IEffects.h" +#include "ndebugoverlay.h" +#include "shake.h" +#include "hl2_player.h" +#include "beam_shared.h" +#include "Sprite.h" +#include "util.h" +#include "weapon_physcannon.h" +#include "physics_saverestore.h" +#include "ai_basenpc.h" +#include "player_pickup.h" +#include "physics_prop_ragdoll.h" +#include "globalstate.h" +#include "props.h" +#include "movevars_shared.h" +#include "basehlcombatweapon.h" +#include "te_effect_dispatch.h" +#include "vphysics/friction.h" +#include "saverestore_utlvector.h" +#include "prop_combine_ball.h" +#include "physobj.h" +#include "hl2_gamerules.h" +#include "citadel_effects_shared.h" +#include "eventqueue.h" +#include "model_types.h" +#include "ai_interactions.h" +#include "rumble_shared.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +static const char *s_pWaitForUpgradeContext = "WaitForUpgrade"; + +ConVar g_debug_physcannon( "g_debug_physcannon", "0" ); + +ConVar physcannon_minforce( "physcannon_minforce", "700" ); +ConVar physcannon_maxforce( "physcannon_maxforce", "1500" ); +ConVar physcannon_maxmass( "physcannon_maxmass", "250" ); +ConVar physcannon_tracelength( "physcannon_tracelength", "250" ); +ConVar physcannon_mega_tracelength( "physcannon_mega_tracelength", "850" ); +ConVar physcannon_chargetime("physcannon_chargetime", "2" ); +ConVar physcannon_pullforce( "physcannon_pullforce", "4000" ); +ConVar physcannon_mega_pullforce( "physcannon_mega_pullforce", "8000" ); +ConVar physcannon_cone( "physcannon_cone", "0.97" ); +ConVar physcannon_ball_cone( "physcannon_ball_cone", "0.997" ); +ConVar physcannon_punt_cone( "physcannon_punt_cone", "0.997" ); +ConVar player_throwforce( "player_throwforce", "1000" ); + +extern ConVar hl2_normspeed; +extern ConVar hl2_walkspeed; + +#define PHYSCANNON_BEAM_SPRITE "sprites/orangelight1.vmt" +#define PHYSCANNON_GLOW_SPRITE "sprites/glow04_noz.vmt" +#define PHYSCANNON_ENDCAP_SPRITE "sprites/orangeflare1.vmt" +#define PHYSCANNON_CENTER_GLOW "sprites/orangecore1.vmt" +#define PHYSCANNON_BLAST_SPRITE "sprites/orangecore2.vmt" + +#define MEGACANNON_BEAM_SPRITE "sprites/lgtning_noz.vmt" +#define MEGACANNON_GLOW_SPRITE "sprites/blueflare1_noz.vmt" +#define MEGACANNON_ENDCAP_SPRITE "sprites/blueflare1_noz.vmt" +#define MEGACANNON_CENTER_GLOW "effects/fluttercore.vmt" +#define MEGACANNON_BLAST_SPRITE "effects/fluttercore.vmt" + +#define MEGACANNON_RAGDOLL_BOOGIE_SPRITE "sprites/lgtning_noz.vmt" + +#define MEGACANNON_MODEL "models/weapons/v_superphyscannon.mdl" +#define MEGACANNON_SKIN 1 + +// ------------------------------------------------------------------------- +// Physcannon trace filter to handle special cases +// ------------------------------------------------------------------------- + +class CTraceFilterPhyscannon : public CTraceFilterSimple +{ +public: + DECLARE_CLASS( CTraceFilterPhyscannon, CTraceFilterSimple ); + + CTraceFilterPhyscannon( const IHandleEntity *passentity, int collisionGroup ) + : CTraceFilterSimple( NULL, collisionGroup ), m_pTraceOwner( passentity ) { } + + // For this test, we only test against entities (then world brushes afterwards) + virtual TraceType_t GetTraceType() const { return TRACE_ENTITIES_ONLY; } + + bool HasContentsGrate( CBaseEntity *pEntity ) + { + // FIXME: Move this into the GetModelContents() function in base entity + + // Find the contents based on the model type + int nModelType = modelinfo->GetModelType( pEntity->GetModel() ); + if ( nModelType == mod_studio ) + { + CBaseAnimating *pAnim = dynamic_cast(pEntity); + if ( pAnim != NULL ) + { + CStudioHdr *pStudioHdr = pAnim->GetModelPtr(); + if ( pStudioHdr != NULL && (pStudioHdr->contents() & CONTENTS_GRATE) ) + return true; + } + } + else if ( nModelType == mod_brush ) + { + // Brushes poll their contents differently + int contents = modelinfo->GetModelContents( pEntity->GetModelIndex() ); + if ( contents & CONTENTS_GRATE ) + return true; + } + + return false; + } + + virtual bool ShouldHitEntity( IHandleEntity *pHandleEntity, int contentsMask ) + { + // Only skip ourselves (not things we own) + if ( pHandleEntity == m_pTraceOwner ) + return false; + + // Get the entity referenced by this handle + CBaseEntity *pEntity = EntityFromEntityHandle( pHandleEntity ); + if ( pEntity == NULL ) + return false; + + // Handle grate entities differently + if ( HasContentsGrate( pEntity ) ) + { + // See if it's a grabbable physics prop + CPhysicsProp *pPhysProp = dynamic_cast(pEntity); + if ( pPhysProp != NULL ) + return pPhysProp->CanBePickedUpByPhyscannon(); + + // See if it's a grabbable physics prop + if ( FClassnameIs( pEntity, "prop_physics" ) ) + { + CPhysicsProp *pPhysProp = dynamic_cast(pEntity); + if ( pPhysProp != NULL ) + return pPhysProp->CanBePickedUpByPhyscannon(); + + // Somehow had a classname that didn't match the class! + Assert(0); + } + else if ( FClassnameIs( pEntity, "func_physbox" ) ) + { + // Must be a moveable physbox + IPhysicsObject *pPhysObj = pEntity->VPhysicsGetObject(); + if ( pPhysObj != NULL && pPhysObj->IsMoveable() ) + return true; + } + + // Don't bother with any other sort of grated entity + return false; + } + + // Use the default rules + return BaseClass::ShouldHitEntity( pHandleEntity, contentsMask ); + } + +protected: + const IHandleEntity *m_pTraceOwner; +}; + +// We want to test against brushes alone +class CTraceFilterOnlyBrushes : public CTraceFilterSimple +{ +public: + DECLARE_CLASS( CTraceFilterOnlyBrushes, CTraceFilterSimple ); + CTraceFilterOnlyBrushes( int collisionGroup ) : CTraceFilterSimple( NULL, collisionGroup ) {} + virtual TraceType_t GetTraceType() const { return TRACE_WORLD_ONLY; } +}; + +//----------------------------------------------------------------------------- +// this will hit skip the pass entity, but not anything it owns +// (lets player grab own grenades) +class CTraceFilterNoOwnerTest : public CTraceFilterSimple +{ +public: + DECLARE_CLASS( CTraceFilterNoOwnerTest, CTraceFilterSimple ); + + CTraceFilterNoOwnerTest( const IHandleEntity *passentity, int collisionGroup ) + : CTraceFilterSimple( NULL, collisionGroup ), m_pPassNotOwner(passentity) + { + } + + virtual bool ShouldHitEntity( IHandleEntity *pHandleEntity, int contentsMask ) + { + if ( pHandleEntity != m_pPassNotOwner ) + return BaseClass::ShouldHitEntity( pHandleEntity, contentsMask ); + + return false; + } + +protected: + const IHandleEntity *m_pPassNotOwner; +}; + +//----------------------------------------------------------------------------- +// Purpose: Trace a line the special physcannon way! +//----------------------------------------------------------------------------- +void UTIL_PhyscannonTraceLine( const Vector &vecAbsStart, const Vector &vecAbsEnd, CBaseEntity *pTraceOwner, trace_t *pTrace ) +{ + // Default to HL2 vanilla + if ( hl2_episodic.GetBool() == false ) + { + CTraceFilterNoOwnerTest filter( pTraceOwner, COLLISION_GROUP_NONE ); + UTIL_TraceLine( vecAbsStart, vecAbsEnd, (MASK_SHOT|CONTENTS_GRATE), &filter, pTrace ); + return; + } + + // First, trace against entities + CTraceFilterPhyscannon filter( pTraceOwner, COLLISION_GROUP_NONE ); + UTIL_TraceLine( vecAbsStart, vecAbsEnd, (MASK_SHOT|CONTENTS_GRATE), &filter, pTrace ); + + // If we've hit something, test again to make sure no brushes block us + if ( pTrace->m_pEnt != NULL ) + { + trace_t testTrace; + CTraceFilterOnlyBrushes brushFilter( COLLISION_GROUP_NONE ); + UTIL_TraceLine( pTrace->startpos, pTrace->endpos, MASK_SHOT, &brushFilter, &testTrace ); + + // If we hit a brush, replace the trace with that result + if ( testTrace.fraction < 1.0f || testTrace.startsolid || testTrace.allsolid ) + { + *pTrace = testTrace; + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Trace a hull for the physcannon +//----------------------------------------------------------------------------- +void UTIL_PhyscannonTraceHull( const Vector &vecAbsStart, const Vector &vecAbsEnd, const Vector &vecAbsMins, const Vector &vecAbsMaxs, CBaseEntity *pTraceOwner, trace_t *pTrace ) +{ + // Default to HL2 vanilla + if ( hl2_episodic.GetBool() == false ) + { + CTraceFilterNoOwnerTest filter( pTraceOwner, COLLISION_GROUP_NONE ); + UTIL_TraceHull( vecAbsStart, vecAbsEnd, vecAbsMins, vecAbsMaxs, (MASK_SHOT|CONTENTS_GRATE), &filter, pTrace ); + return; + } + + // First, trace against entities + CTraceFilterPhyscannon filter( pTraceOwner, COLLISION_GROUP_NONE ); + UTIL_TraceHull( vecAbsStart, vecAbsEnd, vecAbsMins, vecAbsMaxs, (MASK_SHOT|CONTENTS_GRATE), &filter, pTrace ); + + // If we've hit something, test again to make sure no brushes block us + if ( pTrace->m_pEnt != NULL ) + { + trace_t testTrace; + CTraceFilterOnlyBrushes brushFilter( COLLISION_GROUP_NONE ); + UTIL_TraceHull( pTrace->startpos, pTrace->endpos, vecAbsMins, vecAbsMaxs, MASK_SHOT, &brushFilter, &testTrace ); + + // If we hit a brush, replace the trace with that result + if ( testTrace.fraction < 1.0f || testTrace.startsolid || testTrace.allsolid ) + { + *pTrace = testTrace; + } + } +} + +static void MatrixOrthogonalize( matrix3x4_t &matrix, int column ) +{ + Vector columns[3]; + int i; + + for ( i = 0; i < 3; i++ ) + { + MatrixGetColumn( matrix, i, columns[i] ); + } + + int index0 = column; + int index1 = (column+1)%3; + int index2 = (column+2)%3; + + columns[index2] = CrossProduct( columns[index0], columns[index1] ); + columns[index1] = CrossProduct( columns[index2], columns[index0] ); + VectorNormalize( columns[index2] ); + VectorNormalize( columns[index1] ); + MatrixSetColumn( columns[index1], index1, matrix ); + MatrixSetColumn( columns[index2], index2, matrix ); +} + +#define SIGN(x) ( (x) < 0 ? -1 : 1 ) + +static QAngle AlignAngles( const QAngle &angles, float cosineAlignAngle ) +{ + matrix3x4_t alignMatrix; + AngleMatrix( angles, alignMatrix ); + + // NOTE: Must align z first + for ( int j = 3; --j >= 0; ) + { + Vector vec; + MatrixGetColumn( alignMatrix, j, vec ); + for ( int i = 0; i < 3; i++ ) + { + if ( fabs(vec[i]) > cosineAlignAngle ) + { + vec[i] = SIGN(vec[i]); + vec[(i+1)%3] = 0; + vec[(i+2)%3] = 0; + MatrixSetColumn( vec, j, alignMatrix ); + MatrixOrthogonalize( alignMatrix, j ); + break; + } + } + } + + QAngle out; + MatrixAngles( alignMatrix, out ); + return out; +} + + +/*static void TraceCollideAgainstBBox( const CPhysCollide *pCollide, const Vector &start, const Vector &end, const QAngle &angles, const Vector &boxOrigin, const Vector &mins, const Vector &maxs, trace_t *ptr ) +{ + physcollision->TraceBox( boxOrigin, boxOrigin + (start-end), mins, maxs, pCollide, start, angles, ptr ); + + if ( ptr->DidHit() ) + { + ptr->endpos = start * (1-ptr->fraction) + end * ptr->fraction; + ptr->startpos = start; + ptr->plane.dist = -ptr->plane.dist; + ptr->plane.normal *= -1; + } +}*/ + +//----------------------------------------------------------------------------- +// Purpose: Finds the nearest ragdoll sub-piece to a location and returns it +// Input : *pTarget - entity that is the potential ragdoll +// &position - position we're testing against +// Output : IPhysicsObject - sub-object (if any) +//----------------------------------------------------------------------------- +IPhysicsObject *GetRagdollChildAtPosition( CBaseEntity *pTarget, const Vector &position ) +{ + // Check for a ragdoll + if ( dynamic_cast( pTarget ) == NULL ) + return NULL; + + // Get the root + IPhysicsObject *pList[32]; + int count = pTarget->VPhysicsGetObjectList( pList, ARRAYSIZE( pList ) ); + + IPhysicsObject *pBestChild = NULL; + float flBestDist = 99999999.0f; + float flDist; + Vector vPos; + + // Find the nearest child to where we're looking + for ( int i = 0; i < count; i++ ) + { + pList[i]->GetPosition( &vPos, NULL ); + + flDist = ( position - vPos ).LengthSqr(); + + if ( flDist < flBestDist ) + { + pBestChild = pList[i]; + flBestDist = flDist; + } + } + + // Make this our base now + pTarget->VPhysicsSwapObject( pBestChild ); + + return pTarget->VPhysicsGetObject(); +} + +//----------------------------------------------------------------------------- +// Purpose: Computes a local matrix for the player clamped to valid carry ranges +//----------------------------------------------------------------------------- +// when looking level, hold bottom of object 8 inches below eye level +#define PLAYER_HOLD_LEVEL_EYES -8 + +// when looking down, hold bottom of object 0 inches from feet +#define PLAYER_HOLD_DOWN_FEET 2 + +// when looking up, hold bottom of object 24 inches above eye level +#define PLAYER_HOLD_UP_EYES 24 + +// use a +/-30 degree range for the entire range of motion of pitch +#define PLAYER_LOOK_PITCH_RANGE 30 + +// player can reach down 2ft below his feet (otherwise he'll hold the object above the bottom) +#define PLAYER_REACH_DOWN_DISTANCE 24 + +static void ComputePlayerMatrix( CBasePlayer *pPlayer, matrix3x4_t &out ) +{ + if ( !pPlayer ) + return; + + QAngle angles = pPlayer->EyeAngles(); + Vector origin = pPlayer->EyePosition(); + + // 0-360 / -180-180 + //angles.x = init ? 0 : AngleDistance( angles.x, 0 ); + //angles.x = clamp( angles.x, -PLAYER_LOOK_PITCH_RANGE, PLAYER_LOOK_PITCH_RANGE ); + angles.x = 0; + + float feet = pPlayer->GetAbsOrigin().z + pPlayer->WorldAlignMins().z; + float eyes = origin.z; + float zoffset = 0; + // moving up (negative pitch is up) + if ( angles.x < 0 ) + { + zoffset = RemapVal( angles.x, 0, -PLAYER_LOOK_PITCH_RANGE, PLAYER_HOLD_LEVEL_EYES, PLAYER_HOLD_UP_EYES ); + } + else + { + zoffset = RemapVal( angles.x, 0, PLAYER_LOOK_PITCH_RANGE, PLAYER_HOLD_LEVEL_EYES, PLAYER_HOLD_DOWN_FEET + (feet - eyes) ); + } + origin.z += zoffset; + angles.x = 0; + AngleMatrix( angles, origin, out ); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +// derive from this so we can add save/load data to it +struct game_shadowcontrol_params_t : public hlshadowcontrol_params_t +{ + DECLARE_SIMPLE_DATADESC(); +}; + +BEGIN_SIMPLE_DATADESC( game_shadowcontrol_params_t ) + + DEFINE_FIELD( targetPosition, FIELD_POSITION_VECTOR ), + DEFINE_FIELD( targetRotation, FIELD_VECTOR ), + DEFINE_FIELD( maxAngular, FIELD_FLOAT ), + DEFINE_FIELD( maxDampAngular, FIELD_FLOAT ), + DEFINE_FIELD( maxSpeed, FIELD_FLOAT ), + DEFINE_FIELD( maxDampSpeed, FIELD_FLOAT ), + DEFINE_FIELD( dampFactor, FIELD_FLOAT ), + DEFINE_FIELD( teleportDistance, FIELD_FLOAT ), + +END_DATADESC() + +//----------------------------------------------------------------------------- +class CGrabController : public IMotionEvent +{ + DECLARE_SIMPLE_DATADESC(); + +public: + + CGrabController( void ); + ~CGrabController( void ); + void AttachEntity( CBasePlayer *pPlayer, CBaseEntity *pEntity, IPhysicsObject *pPhys, bool bIsMegaPhysCannon, const Vector &vGrabPosition, bool bUseGrabPosition ); + void DetachEntity( bool bClearVelocity ); + void OnRestore(); + + bool UpdateObject( CBasePlayer *pPlayer, float flError ); + + void SetTargetPosition( const Vector &target, const QAngle &targetOrientation ); + float ComputeError(); + float GetLoadWeight( void ) const { return m_flLoadWeight; } + void SetAngleAlignment( float alignAngleCosine ) { m_angleAlignment = alignAngleCosine; } + void SetIgnorePitch( bool bIgnore ) { m_bIgnoreRelativePitch = bIgnore; } + QAngle TransformAnglesToPlayerSpace( const QAngle &anglesIn, CBasePlayer *pPlayer ); + QAngle TransformAnglesFromPlayerSpace( const QAngle &anglesIn, CBasePlayer *pPlayer ); + + CBaseEntity *GetAttached() { return (CBaseEntity *)m_attachedEntity; } + + IMotionEvent::simresult_e Simulate( IPhysicsMotionController *pController, IPhysicsObject *pObject, float deltaTime, Vector &linear, AngularImpulse &angular ); + float GetSavedMass( IPhysicsObject *pObject ); + +private: + // Compute the max speed for an attached object + void ComputeMaxSpeed( CBaseEntity *pEntity, IPhysicsObject *pPhysics ); + + game_shadowcontrol_params_t m_shadow; + float m_timeToArrive; + float m_errorTime; + float m_error; + float m_contactAmount; + float m_angleAlignment; + bool m_bCarriedEntityBlocksLOS; + bool m_bIgnoreRelativePitch; + + float m_flLoadWeight; + float m_savedRotDamping[VPHYSICS_MAX_OBJECT_LIST_COUNT]; + float m_savedMass[VPHYSICS_MAX_OBJECT_LIST_COUNT]; + EHANDLE m_attachedEntity; + QAngle m_vecPreferredCarryAngles; + bool m_bHasPreferredCarryAngles; + float m_flDistanceOffset; + + QAngle m_attachedAnglesPlayerSpace; + Vector m_attachedPositionObjectSpace; + + IPhysicsMotionController *m_controller; + + friend class CWeaponPhysCannon; +}; + +BEGIN_SIMPLE_DATADESC( CGrabController ) + + DEFINE_EMBEDDED( m_shadow ), + + DEFINE_FIELD( m_timeToArrive, FIELD_FLOAT ), + DEFINE_FIELD( m_errorTime, FIELD_FLOAT ), + DEFINE_FIELD( m_error, FIELD_FLOAT ), + DEFINE_FIELD( m_contactAmount, FIELD_FLOAT ), + DEFINE_AUTO_ARRAY( m_savedRotDamping, FIELD_FLOAT ), + DEFINE_AUTO_ARRAY( m_savedMass, FIELD_FLOAT ), + DEFINE_FIELD( m_flLoadWeight, FIELD_FLOAT ), + DEFINE_FIELD( m_bCarriedEntityBlocksLOS, FIELD_BOOLEAN ), + DEFINE_FIELD( m_bIgnoreRelativePitch, FIELD_BOOLEAN ), + DEFINE_FIELD( m_attachedEntity, FIELD_EHANDLE ), + DEFINE_FIELD( m_angleAlignment, FIELD_FLOAT ), + DEFINE_FIELD( m_vecPreferredCarryAngles, FIELD_VECTOR ), + DEFINE_FIELD( m_bHasPreferredCarryAngles, FIELD_BOOLEAN ), + DEFINE_FIELD( m_flDistanceOffset, FIELD_FLOAT ), + DEFINE_FIELD( m_attachedAnglesPlayerSpace, FIELD_VECTOR ), + DEFINE_FIELD( m_attachedPositionObjectSpace, FIELD_VECTOR ), + + // Physptrs can't be inside embedded classes + // DEFINE_PHYSPTR( m_controller ), + +END_DATADESC() + +const float DEFAULT_MAX_ANGULAR = 360.0f * 10.0f; +const float REDUCED_CARRY_MASS = 1.0f; + +CGrabController::CGrabController( void ) +{ + m_shadow.dampFactor = 1.0; + m_shadow.teleportDistance = 0; + m_errorTime = 0; + m_error = 0; + // make this controller really stiff! + m_shadow.maxSpeed = 1000; + m_shadow.maxAngular = DEFAULT_MAX_ANGULAR; + m_shadow.maxDampSpeed = m_shadow.maxSpeed*2; + m_shadow.maxDampAngular = m_shadow.maxAngular; + m_attachedEntity = NULL; + m_vecPreferredCarryAngles = vec3_angle; + m_bHasPreferredCarryAngles = false; + m_flDistanceOffset = 0; +} + +CGrabController::~CGrabController( void ) +{ + DetachEntity( false ); +} + +void CGrabController::OnRestore() +{ + if ( m_controller ) + { + m_controller->SetEventHandler( this ); + } +} + +void CGrabController::SetTargetPosition( const Vector &target, const QAngle &targetOrientation ) +{ + m_shadow.targetPosition = target; + m_shadow.targetRotation = targetOrientation; + + m_timeToArrive = gpGlobals->frametime; + + CBaseEntity *pAttached = GetAttached(); + if ( pAttached ) + { + IPhysicsObject *pObj = pAttached->VPhysicsGetObject(); + + if ( pObj != NULL ) + { + pObj->Wake(); + } + else + { + DetachEntity( false ); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : float +//----------------------------------------------------------------------------- +float CGrabController::ComputeError() +{ + if ( m_errorTime <= 0 ) + return 0; + + CBaseEntity *pAttached = GetAttached(); + if ( pAttached ) + { + Vector pos; + IPhysicsObject *pObj = pAttached->VPhysicsGetObject(); + + if ( pObj ) + { + pObj->GetShadowPosition( &pos, NULL ); + + float error = (m_shadow.targetPosition - pos).Length(); + if ( m_errorTime > 0 ) + { + if ( m_errorTime > 1 ) + { + m_errorTime = 1; + } + float speed = error / m_errorTime; + if ( speed > m_shadow.maxSpeed ) + { + error *= 0.5; + } + m_error = (1-m_errorTime) * m_error + error * m_errorTime; + } + } + else + { + DevMsg( "Object attached to Physcannon has no physics object\n" ); + DetachEntity( false ); + return 9999; // force detach + } + } + + if ( pAttached->IsEFlagSet( EFL_IS_BEING_LIFTED_BY_BARNACLE ) ) + { + m_error *= 3.0f; + } + + m_errorTime = 0; + + return m_error; +} + + +#define MASS_SPEED_SCALE 60 +#define MAX_MASS 40 + +void CGrabController::ComputeMaxSpeed( CBaseEntity *pEntity, IPhysicsObject *pPhysics ) +{ + m_shadow.maxSpeed = 1000; + m_shadow.maxAngular = DEFAULT_MAX_ANGULAR; + + // Compute total mass... + float flMass = PhysGetEntityMass( pEntity ); + float flMaxMass = physcannon_maxmass.GetFloat(); + if ( flMass <= flMaxMass ) + return; + + float flLerpFactor = clamp( flMass, flMaxMass, 500.0f ); + flLerpFactor = SimpleSplineRemapVal( flLerpFactor, flMaxMass, 500.0f, 0.0f, 1.0f ); + + float invMass = pPhysics->GetInvMass(); + float invInertia = pPhysics->GetInvInertia().Length(); + + float invMaxMass = 1.0f / MAX_MASS; + float ratio = invMaxMass / invMass; + invMass = invMaxMass; + invInertia *= ratio; + + float maxSpeed = invMass * MASS_SPEED_SCALE * 200; + float maxAngular = invInertia * MASS_SPEED_SCALE * 360; + + m_shadow.maxSpeed = Lerp( flLerpFactor, m_shadow.maxSpeed, maxSpeed ); + m_shadow.maxAngular = Lerp( flLerpFactor, m_shadow.maxAngular, maxAngular ); +} + + +QAngle CGrabController::TransformAnglesToPlayerSpace( const QAngle &anglesIn, CBasePlayer *pPlayer ) +{ + if ( m_bIgnoreRelativePitch ) + { + matrix3x4_t test; + QAngle angleTest = pPlayer->EyeAngles(); + angleTest.x = 0; + AngleMatrix( angleTest, test ); + return TransformAnglesToLocalSpace( anglesIn, test ); + } + return TransformAnglesToLocalSpace( anglesIn, pPlayer->EntityToWorldTransform() ); +} + +QAngle CGrabController::TransformAnglesFromPlayerSpace( const QAngle &anglesIn, CBasePlayer *pPlayer ) +{ + if ( m_bIgnoreRelativePitch ) + { + matrix3x4_t test; + QAngle angleTest = pPlayer->EyeAngles(); + angleTest.x = 0; + AngleMatrix( angleTest, test ); + return TransformAnglesToWorldSpace( anglesIn, test ); + } + return TransformAnglesToWorldSpace( anglesIn, pPlayer->EntityToWorldTransform() ); +} + + +void CGrabController::AttachEntity( CBasePlayer *pPlayer, CBaseEntity *pEntity, IPhysicsObject *pPhys, bool bIsMegaPhysCannon, const Vector &vGrabPosition, bool bUseGrabPosition ) +{ + // play the impact sound of the object hitting the player + // used as feedback to let the player know he picked up the object + int hitMaterial = pPhys->GetMaterialIndex(); + int playerMaterial = pPlayer->VPhysicsGetObject() ? pPlayer->VPhysicsGetObject()->GetMaterialIndex() : hitMaterial; + PhysicsImpactSound( pPlayer, pPhys, CHAN_STATIC, hitMaterial, playerMaterial, 1.0, 64 ); + Vector position; + QAngle angles; + pPhys->GetPosition( &position, &angles ); + // If it has a preferred orientation, use that instead. + Pickup_GetPreferredCarryAngles( pEntity, pPlayer, pPlayer->EntityToWorldTransform(), angles ); + +// ComputeMaxSpeed( pEntity, pPhys ); + + // If we haven't been killed by a grab, we allow the gun to grab the nearest part of a ragdoll + if ( bUseGrabPosition ) + { + IPhysicsObject *pChild = GetRagdollChildAtPosition( pEntity, vGrabPosition ); + + if ( pChild ) + { + pPhys = pChild; + } + } + + // Carried entities can never block LOS + m_bCarriedEntityBlocksLOS = pEntity->BlocksLOS(); + pEntity->SetBlocksLOS( false ); + m_controller = physenv->CreateMotionController( this ); + m_controller->AttachObject( pPhys, true ); + // Don't do this, it's causing trouble with constraint solvers. + //m_controller->SetPriority( IPhysicsMotionController::HIGH_PRIORITY ); + + pPhys->Wake(); + PhysSetGameFlags( pPhys, FVPHYSICS_PLAYER_HELD ); + SetTargetPosition( position, angles ); + m_attachedEntity = pEntity; + IPhysicsObject *pList[VPHYSICS_MAX_OBJECT_LIST_COUNT]; + int count = pEntity->VPhysicsGetObjectList( pList, ARRAYSIZE(pList) ); + m_flLoadWeight = 0; + float damping = 10; + float flFactor = count / 7.5f; + if ( flFactor < 1.0f ) + { + flFactor = 1.0f; + } + for ( int i = 0; i < count; i++ ) + { + float mass = pList[i]->GetMass(); + pList[i]->GetDamping( NULL, &m_savedRotDamping[i] ); + m_flLoadWeight += mass; + m_savedMass[i] = mass; + + // reduce the mass to prevent the player from adding crazy amounts of energy to the system + pList[i]->SetMass( REDUCED_CARRY_MASS / flFactor ); + pList[i]->SetDamping( NULL, &damping ); + } + + // Give extra mass to the phys object we're actually picking up + pPhys->SetMass( REDUCED_CARRY_MASS ); + pPhys->EnableDrag( false ); + + m_errorTime = bIsMegaPhysCannon ? -1.5f : -1.0f; // 1 seconds until error starts accumulating + m_error = 0; + m_contactAmount = 0; + + m_attachedAnglesPlayerSpace = TransformAnglesToPlayerSpace( angles, pPlayer ); + if ( m_angleAlignment != 0 ) + { + m_attachedAnglesPlayerSpace = AlignAngles( m_attachedAnglesPlayerSpace, m_angleAlignment ); + } + + // Ragdolls don't offset this way + if ( dynamic_cast(pEntity) ) + { + m_attachedPositionObjectSpace.Init(); + } + else + { + VectorITransform( pEntity->WorldSpaceCenter(), pEntity->EntityToWorldTransform(), m_attachedPositionObjectSpace ); + } + + // If it's a prop, see if it has desired carry angles + CPhysicsProp *pProp = dynamic_cast(pEntity); + if ( pProp ) + { + m_bHasPreferredCarryAngles = pProp->GetPropDataAngles( "preferred_carryangles", m_vecPreferredCarryAngles ); + m_flDistanceOffset = pProp->GetCarryDistanceOffset(); + } + else + { + m_bHasPreferredCarryAngles = false; + m_flDistanceOffset = 0; + } +} + +static void ClampPhysicsVelocity( IPhysicsObject *pPhys, float linearLimit, float angularLimit ) +{ + Vector vel; + AngularImpulse angVel; + pPhys->GetVelocity( &vel, &angVel ); + float speed = VectorNormalize(vel) - linearLimit; + float angSpeed = VectorNormalize(angVel) - angularLimit; + speed = speed < 0 ? 0 : -speed; + angSpeed = angSpeed < 0 ? 0 : -angSpeed; + vel *= speed; + angVel *= angSpeed; + pPhys->AddVelocity( &vel, &angVel ); +} + +void CGrabController::DetachEntity( bool bClearVelocity ) +{ + Assert(!PhysIsInCallback()); + CBaseEntity *pEntity = GetAttached(); + if ( pEntity ) + { + // Restore the LS blocking state + pEntity->SetBlocksLOS( m_bCarriedEntityBlocksLOS ); + IPhysicsObject *pList[VPHYSICS_MAX_OBJECT_LIST_COUNT]; + int count = pEntity->VPhysicsGetObjectList( pList, ARRAYSIZE(pList) ); + for ( int i = 0; i < count; i++ ) + { + IPhysicsObject *pPhys = pList[i]; + if ( !pPhys ) + continue; + + // on the odd chance that it's gone to sleep while under anti-gravity + pPhys->EnableDrag( true ); + pPhys->Wake(); + pPhys->SetMass( m_savedMass[i] ); + pPhys->SetDamping( NULL, &m_savedRotDamping[i] ); + PhysClearGameFlags( pPhys, FVPHYSICS_PLAYER_HELD ); + if ( bClearVelocity ) + { + PhysForceClearVelocity( pPhys ); + } + else + { + ClampPhysicsVelocity( pPhys, hl2_normspeed.GetFloat() * 1.5f, 2.0f * 360.0f ); + } + + } + } + + m_attachedEntity = NULL; + physenv->DestroyMotionController( m_controller ); + m_controller = NULL; +} + +static bool InContactWithHeavyObject( IPhysicsObject *pObject, float heavyMass ) +{ + bool contact = false; + IPhysicsFrictionSnapshot *pSnapshot = pObject->CreateFrictionSnapshot(); + while ( pSnapshot->IsValid() ) + { + IPhysicsObject *pOther = pSnapshot->GetObject( 1 ); + if ( !pOther->IsMoveable() || pOther->GetMass() > heavyMass ) + { + contact = true; + break; + } + pSnapshot->NextFrictionData(); + } + pObject->DestroyFrictionSnapshot( pSnapshot ); + return contact; +} + +IMotionEvent::simresult_e CGrabController::Simulate( IPhysicsMotionController *pController, IPhysicsObject *pObject, float deltaTime, Vector &linear, AngularImpulse &angular ) +{ + game_shadowcontrol_params_t shadowParams = m_shadow; + if ( InContactWithHeavyObject( pObject, GetLoadWeight() ) ) + { + m_contactAmount = Approach( 0.1f, m_contactAmount, deltaTime*2.0f ); + } + else + { + m_contactAmount = Approach( 1.0f, m_contactAmount, deltaTime*2.0f ); + } + shadowParams.maxAngular = m_shadow.maxAngular * m_contactAmount * m_contactAmount * m_contactAmount; + m_timeToArrive = pObject->ComputeShadowControl( shadowParams, m_timeToArrive, deltaTime ); + + // Slide along the current contact points to fix bouncing problems + Vector velocity; + AngularImpulse angVel; + pObject->GetVelocity( &velocity, &angVel ); + PhysComputeSlideDirection( pObject, velocity, angVel, &velocity, &angVel, GetLoadWeight() ); + pObject->SetVelocityInstantaneous( &velocity, NULL ); + + linear.Init(); + angular.Init(); + m_errorTime += deltaTime; + + return SIM_LOCAL_ACCELERATION; +} + +float CGrabController::GetSavedMass( IPhysicsObject *pObject ) +{ + CBaseEntity *pHeld = m_attachedEntity; + if ( pHeld ) + { + if ( pObject->GetGameData() == (void*)pHeld ) + { + IPhysicsObject *pList[VPHYSICS_MAX_OBJECT_LIST_COUNT]; + int count = pHeld->VPhysicsGetObjectList( pList, ARRAYSIZE(pList) ); + for ( int i = 0; i < count; i++ ) + { + if ( pList[i] == pObject ) + return m_savedMass[i]; + } + } + } + return 0.0f; +} + +//----------------------------------------------------------------------------- +// Player pickup controller +//----------------------------------------------------------------------------- +class CPlayerPickupController : public CBaseEntity +{ + DECLARE_DATADESC(); + DECLARE_CLASS( CPlayerPickupController, CBaseEntity ); +public: + void Init( CBasePlayer *pPlayer, CBaseEntity *pObject ); + void Shutdown( bool bThrown = false ); + bool OnControls( CBaseEntity *pControls ) { return true; } + void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); + void OnRestore() + { + m_grabController.OnRestore(); + } + void VPhysicsUpdate( IPhysicsObject *pPhysics ){} + void VPhysicsShadowUpdate( IPhysicsObject *pPhysics ) {} + + bool IsHoldingEntity( CBaseEntity *pEnt ); + CGrabController &GetGrabController() { return m_grabController; } + +private: + CGrabController m_grabController; + CBasePlayer *m_pPlayer; +}; + +LINK_ENTITY_TO_CLASS( player_pickup, CPlayerPickupController ); + +//--------------------------------------------------------- +// Save/Restore +//--------------------------------------------------------- +BEGIN_DATADESC( CPlayerPickupController ) + + DEFINE_EMBEDDED( m_grabController ), + + // Physptrs can't be inside embedded classes + DEFINE_PHYSPTR( m_grabController.m_controller ), + + DEFINE_FIELD( m_pPlayer, FIELD_CLASSPTR ), + +END_DATADESC() + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *pPlayer - +// *pObject - +//----------------------------------------------------------------------------- +void CPlayerPickupController::Init( CBasePlayer *pPlayer, CBaseEntity *pObject ) +{ + // Holster player's weapon + if ( pPlayer->GetActiveWeapon() ) + { + if ( !pPlayer->GetActiveWeapon()->Holster() ) + { + Shutdown(); + return; + } + } + + CHL2_Player *pOwner = (CHL2_Player *)ToBasePlayer( pPlayer ); + if ( pOwner ) + { + pOwner->EnableSprint( false ); + } + + // If the target is debris, convert it to non-debris + if ( pObject->GetCollisionGroup() == COLLISION_GROUP_DEBRIS ) + { + // Interactive debris converts back to debris when it comes to rest + pObject->SetCollisionGroup( COLLISION_GROUP_INTERACTIVE_DEBRIS ); + } + + // done so I'll go across level transitions with the player + SetParent( pPlayer ); + m_grabController.SetIgnorePitch( true ); + m_grabController.SetAngleAlignment( DOT_30DEGREE ); + m_pPlayer = pPlayer; + IPhysicsObject *pPhysics = pObject->VPhysicsGetObject(); + + Pickup_OnPhysGunPickup( pObject, m_pPlayer, PICKED_UP_BY_PLAYER ); + + m_grabController.AttachEntity( pPlayer, pObject, pPhysics, false, vec3_origin, false ); + + m_pPlayer->m_Local.m_iHideHUD |= HIDEHUD_WEAPONSELECTION; + m_pPlayer->SetUseEntity( this ); +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : bool - +//----------------------------------------------------------------------------- +void CPlayerPickupController::Shutdown( bool bThrown ) +{ + CBaseEntity *pObject = m_grabController.GetAttached(); + + bool bClearVelocity = false; + if ( !bThrown && pObject && pObject->VPhysicsGetObject() && pObject->VPhysicsGetObject()->GetContactPoint(NULL,NULL) ) + { + bClearVelocity = true; + } + + m_grabController.DetachEntity( bClearVelocity ); + + if ( pObject != NULL ) + { + Pickup_OnPhysGunDrop( pObject, m_pPlayer, bThrown ? THROWN_BY_PLAYER : DROPPED_BY_PLAYER ); + } + + if ( m_pPlayer ) + { + CHL2_Player *pOwner = (CHL2_Player *)ToBasePlayer( m_pPlayer ); + if ( pOwner ) + { + pOwner->EnableSprint( true ); + } + + m_pPlayer->SetUseEntity( NULL ); + if ( m_pPlayer->GetActiveWeapon() ) + { + if ( !m_pPlayer->GetActiveWeapon()->Deploy() ) + { + // We tried to restore the player's weapon, but we couldn't. + // This usually happens when they're holding an empty weapon that doesn't + // autoswitch away when out of ammo. Switch to next best weapon. + m_pPlayer->SwitchToNextBestWeapon( NULL ); + } + } + + m_pPlayer->m_Local.m_iHideHUD &= ~HIDEHUD_WEAPONSELECTION; + } + Remove(); +} + + +void CPlayerPickupController::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) +{ + if ( ToBasePlayer(pActivator) == m_pPlayer ) + { + CBaseEntity *pAttached = m_grabController.GetAttached(); + + // UNDONE: Use vphysics stress to decide to drop objects + // UNDONE: Must fix case of forcing objects into the ground you're standing on (causes stress) before that will work + if ( !pAttached || useType == USE_OFF || (m_pPlayer->m_nButtons & IN_ATTACK2) || m_grabController.ComputeError() > 12 ) + { + Shutdown(); + return; + } + + //Adrian: Oops, our object became motion disabled, let go! + IPhysicsObject *pPhys = pAttached->VPhysicsGetObject(); + if ( pPhys && pPhys->IsMoveable() == false ) + { + Shutdown(); + return; + } + +#if STRESS_TEST + vphysics_objectstress_t stress; + CalculateObjectStress( pPhys, pAttached, &stress ); + if ( stress.exertedStress > 250 ) + { + Shutdown(); + return; + } +#endif + // +ATTACK will throw phys objects + if ( m_pPlayer->m_nButtons & IN_ATTACK ) + { + Shutdown( true ); + Vector vecLaunch; + m_pPlayer->EyeVectors( &vecLaunch ); + // JAY: Scale this with mass because some small objects really go flying + float massFactor = clamp( pPhys->GetMass(), 0.5, 15 ); + massFactor = RemapVal( massFactor, 0.5, 15, 0.5, 4 ); + vecLaunch *= player_throwforce.GetFloat() * massFactor; + + pPhys->ApplyForceCenter( vecLaunch ); + AngularImpulse aVel = RandomAngularImpulse( -10, 10 ) * massFactor; + pPhys->ApplyTorqueCenter( aVel ); + return; + } + + if ( useType == USE_SET ) + { + // update position + m_grabController.UpdateObject( m_pPlayer, 12 ); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *pEnt - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CPlayerPickupController::IsHoldingEntity( CBaseEntity *pEnt ) +{ + return ( m_grabController.GetAttached() == pEnt ); +} + +void PlayerPickupObject( CBasePlayer *pPlayer, CBaseEntity *pObject ) +{ + //Don't pick up if we don't have a phys object. + if ( pObject->VPhysicsGetObject() == NULL ) + return; + + CPlayerPickupController *pController = (CPlayerPickupController *)CBaseEntity::Create( "player_pickup", pObject->GetAbsOrigin(), vec3_angle, pPlayer ); + + if ( !pController ) + return; + + pController->Init( pPlayer, pObject ); +} + +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// Physcannon +//----------------------------------------------------------------------------- + +#define NUM_BEAMS 4 +#define NUM_SPRITES 6 + +struct thrown_objects_t +{ + float fTimeThrown; + EHANDLE hEntity; + + DECLARE_SIMPLE_DATADESC(); +}; + +BEGIN_SIMPLE_DATADESC( thrown_objects_t ) + DEFINE_FIELD( fTimeThrown, FIELD_TIME ), + DEFINE_FIELD( hEntity, FIELD_EHANDLE ), +END_DATADESC() + +class CWeaponPhysCannon : public CBaseHLCombatWeapon +{ +public: + DECLARE_CLASS( CWeaponPhysCannon, CBaseHLCombatWeapon ); + + DECLARE_SERVERCLASS(); + DECLARE_DATADESC(); + + CWeaponPhysCannon( void ); + + void Drop( const Vector &vecVelocity ); + void Precache(); + virtual void Spawn(); + virtual void OnRestore(); + virtual void StopLoopingSounds(); + virtual void UpdateOnRemove(void); + void PrimaryAttack(); + void SecondaryAttack(); + void WeaponIdle(); + void ItemPreFrame(); + void ItemPostFrame(); + void ItemBusyFrame(); + + virtual float GetMaxAutoAimDeflection() { return 0.90f; } + + void ForceDrop( void ); + bool DropIfEntityHeld( CBaseEntity *pTarget ); // Drops its held entity if it matches the entity passed in + CGrabController &GetGrabController() { return m_grabController; } + + bool CanHolster( void ); + bool Holster( CBaseCombatWeapon *pSwitchingTo = NULL ); + bool Deploy( void ); + + bool HasAnyAmmo( void ) { return true; } + + void InputBecomeMegaCannon( inputdata_t &inputdata ); + + void BeginUpgrade(); + + virtual void SetViewModel( void ); + virtual const char *GetShootSound( int iIndex ) const; + + void RecordThrownObject( CBaseEntity *pObject ); + void PurgeThrownObjects(); + bool IsAccountableForObject( CBaseEntity *pObject ); + + bool ShouldDisplayHUDHint() { return true; } + + + +protected: + enum FindObjectResult_t + { + OBJECT_FOUND = 0, + OBJECT_NOT_FOUND, + OBJECT_BEING_DETACHED, + }; + + void DoMegaEffect( int effectType, Vector *pos = NULL ); + void DoEffect( int effectType, Vector *pos = NULL ); + + void OpenElements( void ); + void CloseElements( void ); + + // Pickup and throw objects. + bool CanPickupObject( CBaseEntity *pTarget ); + bool IsFlesh( CBaseEntity *pTarget ); + void CheckForTarget( void ); + FindObjectResult_t FindObject( void ); + CBaseEntity *MegaPhysCannonFindObjectInCone( const Vector &vecOrigin, const Vector &vecDir, float flCone, float flCombineBallCone, bool bOnlyCombineBalls ); + CBaseEntity *FindObjectInCone( const Vector &vecOrigin, const Vector &vecDir, float flCone ); + bool AttachObject( CBaseEntity *pObject, const Vector &vPosition ); + void UpdateObject( void ); + void DetachObject( bool playSound = true, bool wasLaunched = false ); + void LaunchObject( const Vector &vecDir, float flForce ); + void StartEffects( void ); // Initialize all sprites and beams + void StopEffects( bool stopSound = true ); // Hide all effects temporarily + void DestroyEffects( void ); // Destroy all sprites and beams + + // Punt objects - this is pointing at an object in the world and applying a force to it. + void PuntNonVPhysics( CBaseEntity *pEntity, const Vector &forward, trace_t &tr ); + void PuntVPhysics( CBaseEntity *pEntity, const Vector &forward, trace_t &tr ); + void PuntRagdoll( CBaseEntity *pEntity, const Vector &forward, trace_t &tr ); + + // Velocity-based throw common to punt and launch code. + void ApplyVelocityBasedForce( CBaseEntity *pEntity, const Vector &forward ); + + // Physgun effects + void DoEffectClosed( void ); + void DoMegaEffectClosed( void ); + + void DoEffectReady( void ); + void DoMegaEffectReady( void ); + + void DoMegaEffectHolding( void ); + void DoEffectHolding( void ); + + void DoMegaEffectLaunch( Vector *pos ); + void DoEffectLaunch( Vector *pos ); + + void DoEffectNone( void ); + void DoEffectIdle( void ); + + // Trace length + float TraceLength(); + + // Do we have the super-phys gun? + inline bool IsMegaPhysCannon() + { + return PlayerHasMegaPhysCannon(); + } + + // Sprite scale factor + float SpriteScaleFactor(); + + float GetLoadPercentage(); + CSoundPatch *GetMotorSound( void ); + + void DryFire( void ); + void PrimaryFireEffect( void ); + + // What happens when the physgun picks up something + void Physgun_OnPhysGunPickup( CBaseEntity *pEntity, CBasePlayer *pOwner, PhysGunPickup_t reason ); + + // Wait until we're done upgrading + void WaitForUpgradeThink(); + + bool EntityAllowsPunts( CBaseEntity *pEntity ); + + bool m_bOpen; + bool m_bActive; + int m_nChangeState; //For delayed state change of elements + float m_flCheckSuppressTime; //Amount of time to suppress the checking for targets + bool m_flLastDenySoundPlayed; //Debounce for deny sound + int m_nAttack2Debounce; + + CNetworkVar( bool, m_bIsCurrentlyUpgrading ); + + float m_flElementDebounce; + float m_flElementPosition; + float m_flElementDestination; + + CHandle m_hBeams[NUM_BEAMS]; + CHandle m_hGlowSprites[NUM_SPRITES]; + CHandle m_hEndSprites[2]; + float m_flEndSpritesOverride[2]; + CHandle m_hCenterSprite; + CHandle m_hBlastSprite; + + CSoundPatch *m_sndMotor; // Whirring sound for the gun + + CGrabController m_grabController; + + int m_EffectState; // Current state of the effects on the gun + + bool m_bPhyscannonState; + + // A list of the objects thrown or punted recently, and the time done so. + CUtlVector< thrown_objects_t > m_ThrownEntities; + + float m_flTimeNextObjectPurge; +}; + +IMPLEMENT_SERVERCLASS_ST(CWeaponPhysCannon, DT_WeaponPhysCannon) + SendPropBool( SENDINFO( m_bIsCurrentlyUpgrading ) ), +END_SEND_TABLE() + +LINK_ENTITY_TO_CLASS( weapon_physcannon, CWeaponPhysCannon ); +PRECACHE_WEAPON_REGISTER( weapon_physcannon ); + +BEGIN_DATADESC( CWeaponPhysCannon ) + + DEFINE_FIELD( m_bOpen, FIELD_BOOLEAN ), + DEFINE_FIELD( m_bActive, FIELD_BOOLEAN ), + + DEFINE_FIELD( m_nChangeState, FIELD_INTEGER ), + DEFINE_FIELD( m_flCheckSuppressTime, FIELD_TIME ), + DEFINE_FIELD( m_flElementDebounce, FIELD_TIME ), + DEFINE_FIELD( m_flElementPosition, FIELD_FLOAT ), + DEFINE_FIELD( m_flElementDestination, FIELD_FLOAT ), + DEFINE_FIELD( m_nAttack2Debounce, FIELD_INTEGER ), + DEFINE_FIELD( m_bIsCurrentlyUpgrading, FIELD_BOOLEAN ), + DEFINE_FIELD( m_EffectState, FIELD_INTEGER ), + + DEFINE_AUTO_ARRAY( m_hBeams, FIELD_EHANDLE ), + DEFINE_AUTO_ARRAY( m_hGlowSprites, FIELD_EHANDLE ), + DEFINE_AUTO_ARRAY( m_hEndSprites, FIELD_EHANDLE ), + DEFINE_AUTO_ARRAY( m_flEndSpritesOverride, FIELD_TIME ), + DEFINE_FIELD( m_hCenterSprite, FIELD_EHANDLE ), + DEFINE_FIELD( m_hBlastSprite, FIELD_EHANDLE ), + DEFINE_FIELD( m_flLastDenySoundPlayed, FIELD_BOOLEAN ), + DEFINE_FIELD( m_bPhyscannonState, FIELD_BOOLEAN ), + DEFINE_SOUNDPATCH( m_sndMotor ), + + DEFINE_EMBEDDED( m_grabController ), + + // Physptrs can't be inside embedded classes + DEFINE_PHYSPTR( m_grabController.m_controller ), + + DEFINE_THINKFUNC( WaitForUpgradeThink ), + + DEFINE_UTLVECTOR( m_ThrownEntities, FIELD_EMBEDDED ), + + DEFINE_FIELD( m_flTimeNextObjectPurge, FIELD_TIME ), + +END_DATADESC() + + +enum +{ + ELEMENT_STATE_NONE = -1, + ELEMENT_STATE_OPEN, + ELEMENT_STATE_CLOSED, +}; + +enum +{ + EFFECT_NONE, + EFFECT_CLOSED, + EFFECT_READY, + EFFECT_HOLDING, + EFFECT_LAUNCH, +}; + + +//----------------------------------------------------------------------------- +// Do we have the super-phys gun? +//----------------------------------------------------------------------------- +bool PlayerHasMegaPhysCannon() +{ + return ( HL2GameRules()->MegaPhyscannonActive() == true ); +} + + +//----------------------------------------------------------------------------- +// Constructor +//----------------------------------------------------------------------------- +CWeaponPhysCannon::CWeaponPhysCannon( void ) +{ + m_flElementPosition = 0.0f; + m_flElementDestination = 0.0f; + m_bOpen = false; + m_nChangeState = ELEMENT_STATE_NONE; + m_flCheckSuppressTime = 0.0f; + m_EffectState = EFFECT_NONE; + m_flLastDenySoundPlayed = false; + + m_flEndSpritesOverride[0] = 0.0f; + m_flEndSpritesOverride[1] = 0.0f; + + m_bPhyscannonState = false; +} + +//----------------------------------------------------------------------------- +// Purpose: Precache +//----------------------------------------------------------------------------- +void CWeaponPhysCannon::Precache( void ) +{ + PrecacheModel( PHYSCANNON_BEAM_SPRITE ); + PrecacheModel( PHYSCANNON_GLOW_SPRITE ); + PrecacheModel( PHYSCANNON_ENDCAP_SPRITE ); + PrecacheModel( PHYSCANNON_CENTER_GLOW ); + PrecacheModel( PHYSCANNON_BLAST_SPRITE ); + + PrecacheModel( MEGACANNON_BEAM_SPRITE ); + PrecacheModel( MEGACANNON_GLOW_SPRITE ); + PrecacheModel( MEGACANNON_ENDCAP_SPRITE ); + PrecacheModel( MEGACANNON_CENTER_GLOW ); + PrecacheModel( MEGACANNON_BLAST_SPRITE ); + + PrecacheModel( MEGACANNON_RAGDOLL_BOOGIE_SPRITE ); + + // Precache our alternate model + PrecacheModel( MEGACANNON_MODEL ); + + PrecacheScriptSound( "Weapon_PhysCannon.HoldSound" ); + PrecacheScriptSound( "Weapon_Physgun.Off" ); + + PrecacheScriptSound( "Weapon_MegaPhysCannon.DryFire" ); + PrecacheScriptSound( "Weapon_MegaPhysCannon.Launch" ); + PrecacheScriptSound( "Weapon_MegaPhysCannon.Pickup"); + PrecacheScriptSound( "Weapon_MegaPhysCannon.Drop"); + PrecacheScriptSound( "Weapon_MegaPhysCannon.HoldSound"); + PrecacheScriptSound( "Weapon_MegaPhysCannon.ChargeZap"); + + BaseClass::Precache(); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CWeaponPhysCannon::Spawn( void ) +{ + BaseClass::Spawn(); + + // Need to get close to pick it up + CollisionProp()->UseTriggerBounds( false ); + + m_bPhyscannonState = IsMegaPhysCannon(); + + // The megacannon uses a different skin + if ( IsMegaPhysCannon() ) + { + m_nSkin = MEGACANNON_SKIN; + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Restore +//----------------------------------------------------------------------------- +void CWeaponPhysCannon::OnRestore() +{ + BaseClass::OnRestore(); + m_grabController.OnRestore(); + + m_bPhyscannonState = IsMegaPhysCannon(); + + // Tracker 8106: Physcannon effects disappear through level transition, so + // just recreate any effects here + if ( m_EffectState != EFFECT_NONE ) + { + DoEffect( m_EffectState, NULL ); + } +} + + +//----------------------------------------------------------------------------- +// On Remove +//----------------------------------------------------------------------------- +void CWeaponPhysCannon::UpdateOnRemove(void) +{ + DestroyEffects( ); + BaseClass::UpdateOnRemove(); +} + + +//----------------------------------------------------------------------------- +// Sprite scale factor +//----------------------------------------------------------------------------- +inline float CWeaponPhysCannon::SpriteScaleFactor() +{ + return IsMegaPhysCannon() ? 1.5f : 1.0f; +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CWeaponPhysCannon::Deploy( void ) +{ + CloseElements(); + DoEffect( EFFECT_READY ); + + // Unbloat our bounds + if ( IsMegaPhysCannon() ) + { + CollisionProp()->UseTriggerBounds( false ); + } + + m_flTimeNextObjectPurge = gpGlobals->curtime; + + return BaseClass::Deploy(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CWeaponPhysCannon::SetViewModel( void ) +{ + if ( !IsMegaPhysCannon() ) + { + BaseClass::SetViewModel(); + return; + } + + CBasePlayer *pOwner = ToBasePlayer( GetOwner() ); + if ( pOwner == NULL ) + return; + + CBaseViewModel *vm = pOwner->GetViewModel( m_nViewModelIndex ); + if ( vm == NULL ) + return; + + vm->SetWeaponModel( MEGACANNON_MODEL, this ); +} + +//----------------------------------------------------------------------------- +// Purpose: Force the cannon to drop anything it's carrying +//----------------------------------------------------------------------------- +void CWeaponPhysCannon::ForceDrop( void ) +{ + CloseElements(); + DetachObject(); + StopEffects(); +} + + +//----------------------------------------------------------------------------- +// Purpose: Drops its held entity if it matches the entity passed in +// Input : *pTarget - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CWeaponPhysCannon::DropIfEntityHeld( CBaseEntity *pTarget ) +{ + if ( pTarget == NULL ) + return false; + + CBaseEntity *pHeld = m_grabController.GetAttached(); + + if ( pHeld == NULL ) + return false; + + if ( pHeld == pTarget ) + { + ForceDrop(); + return true; + } + + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CWeaponPhysCannon::Drop( const Vector &vecVelocity ) +{ + ForceDrop(); + BaseClass::Drop( vecVelocity ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CWeaponPhysCannon::CanHolster( void ) +{ + //Don't holster this weapon if we're holding onto something + if ( m_bActive ) + return false; + + return BaseClass::CanHolster(); +}; + +//----------------------------------------------------------------------------- +// Purpose: +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CWeaponPhysCannon::Holster( CBaseCombatWeapon *pSwitchingTo ) +{ + //Don't holster this weapon if we're holding onto something + if ( m_bActive ) + { + if ( m_grabController.GetAttached() == pSwitchingTo && + GetOwner()->Weapon_OwnsThisType( pSwitchingTo->GetClassname(), pSwitchingTo->GetSubType()) ) + { + + } + else + { + return false; + } + } + + CBasePlayer *pOwner = ToBasePlayer( GetOwner() ); + if ( pOwner ) + { + pOwner->RumbleEffect( RUMBLE_PHYSCANNON_OPEN, 0, RUMBLE_FLAG_STOP ); + } + + ForceDrop(); + + return BaseClass::Holster( pSwitchingTo ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CWeaponPhysCannon::DryFire( void ) +{ + SendWeaponAnim( ACT_VM_PRIMARYATTACK ); + WeaponSound( EMPTY ); + + CBasePlayer *pOwner = ToBasePlayer( GetOwner() ); + if ( pOwner ) + { + pOwner->RumbleEffect( RUMBLE_PISTOL, 0, RUMBLE_FLAG_RESTART ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CWeaponPhysCannon::PrimaryFireEffect( void ) +{ + CBasePlayer *pOwner = ToBasePlayer( GetOwner() ); + + if ( pOwner == NULL ) + return; + + pOwner->ViewPunch( QAngle(-6, random->RandomInt(-2,2) ,0) ); + + color32 white = { 245, 245, 255, 32 }; + UTIL_ScreenFade( pOwner, white, 0.1f, 0.0f, FFADE_IN ); + + WeaponSound( SINGLE ); +} + +#define MAX_KNOCKBACK_FORCE 128 + +//----------------------------------------------------------------------------- +// Punt non-physics +//----------------------------------------------------------------------------- +void CWeaponPhysCannon::PuntNonVPhysics( CBaseEntity *pEntity, const Vector &forward, trace_t &tr ) +{ + CTakeDamageInfo info; + + info.SetAttacker( GetOwner() ); + info.SetInflictor( this ); + info.SetDamage( 1.0f ); + info.SetDamageType( DMG_CRUSH | DMG_PHYSGUN ); + info.SetDamageForce( forward ); // Scale? + info.SetDamagePosition( tr.endpos ); + + pEntity->DispatchTraceAttack( info, forward, &tr ); + + ApplyMultiDamage(); + + //Explosion effect + DoEffect( EFFECT_LAUNCH, &tr.endpos ); + + PrimaryFireEffect(); + SendWeaponAnim( ACT_VM_SECONDARYATTACK ); + + m_nChangeState = ELEMENT_STATE_CLOSED; + m_flElementDebounce = gpGlobals->curtime + 0.5f; + m_flCheckSuppressTime = gpGlobals->curtime + 0.25f; +} + + +//----------------------------------------------------------------------------- +// What happens when the physgun picks up something +//----------------------------------------------------------------------------- +void CWeaponPhysCannon::Physgun_OnPhysGunPickup( CBaseEntity *pEntity, CBasePlayer *pOwner, PhysGunPickup_t reason ) +{ + // If the target is debris, convert it to non-debris + if ( pEntity->GetCollisionGroup() == COLLISION_GROUP_DEBRIS ) + { + // Interactive debris converts back to debris when it comes to rest + pEntity->SetCollisionGroup( COLLISION_GROUP_INTERACTIVE_DEBRIS ); + } + + float mass = 0.0f; + if( pEntity->VPhysicsGetObject() ) + { + mass = pEntity->VPhysicsGetObject()->GetMass(); + } + + if( reason == PUNTED_BY_CANNON ) + { + pOwner->RumbleEffect( RUMBLE_357, 0, RUMBLE_FLAGS_NONE ); + RecordThrownObject( pEntity ); + } + + // Warn Alyx if the player is punting a car around. + if( hl2_episodic.GetBool() && mass > 250.0f ) + { + CAI_BaseNPC **ppAIs = g_AI_Manager.AccessAIs(); + int nAIs = g_AI_Manager.NumAIs(); + + for ( int i = 0; i < nAIs; i++ ) + { + if( ppAIs[ i ]->Classify() == CLASS_PLAYER_ALLY_VITAL ) + { + ppAIs[ i ]->DispatchInteraction( g_interactionPlayerPuntedHeavyObject, pEntity, pOwner ); + } + } + } + + Pickup_OnPhysGunPickup( pEntity, pOwner, reason ); +} + + +//----------------------------------------------------------------------------- +// Punt vphysics +//----------------------------------------------------------------------------- +void CWeaponPhysCannon::PuntVPhysics( CBaseEntity *pEntity, const Vector &vecForward, trace_t &tr ) +{ + CTakeDamageInfo info; + + Vector forward = vecForward; + + info.SetAttacker( GetOwner() ); + info.SetInflictor( this ); + info.SetDamage( 0.0f ); + info.SetDamageType( DMG_PHYSGUN ); + pEntity->DispatchTraceAttack( info, forward, &tr ); + ApplyMultiDamage(); + + CBasePlayer *pOwner = ToBasePlayer( GetOwner() ); + if ( Pickup_OnAttemptPhysGunPickup( pEntity, pOwner, PUNTED_BY_CANNON ) ) + { + IPhysicsObject *pList[64]; + int listCount = pEntity->VPhysicsGetObjectList( pList, ARRAYSIZE(pList) ); + if ( !listCount ) + { + //FIXME: Do we want to do this if there's no physics object? + Physgun_OnPhysGunPickup( pEntity, pOwner, PUNTED_BY_CANNON ); + DryFire(); + return; + } + + if( forward.z < 0 ) + { + //reflect, but flatten the trajectory out a bit so it's easier to hit standing targets + forward.z *= -0.65f; + } + + // NOTE: Do this first to enable motion (if disabled) - so forces will work + // Tell the object it's been punted + Physgun_OnPhysGunPickup( pEntity, pOwner, PUNTED_BY_CANNON ); + + // don't push vehicles that are attached to the world via fixed constraints + // they will just wiggle... + if ( (pList[0]->GetGameFlags() & FVPHYSICS_CONSTRAINT_STATIC) && pEntity->GetServerVehicle() ) + { + forward.Init(); + } + + if ( !IsMegaPhysCannon() && !Pickup_ShouldPuntUseLaunchForces( pEntity ) ) + { + int i; + + // limit mass to avoid punting REALLY huge things + float totalMass = 0; + for ( i = 0; i < listCount; i++ ) + { + totalMass += pList[i]->GetMass(); + } + float maxMass = 250; + IServerVehicle *pVehicle = pEntity->GetServerVehicle(); + if ( pVehicle ) + { + maxMass *= 2.5; // 625 for vehicles + } + float mass = min(totalMass, maxMass); // max 250kg of additional force + + // Put some spin on the object + for ( i = 0; i < listCount; i++ ) + { + const float hitObjectFactor = 0.5f; + const float otherObjectFactor = 1.0f - hitObjectFactor; + // Must be light enough + float ratio = pList[i]->GetMass() / totalMass; + if ( pList[i] == pEntity->VPhysicsGetObject() ) + { + ratio += hitObjectFactor; + ratio = min(ratio,1.0f); + } + else + { + ratio *= otherObjectFactor; + } + pList[i]->ApplyForceCenter( forward * 15000.0f * ratio ); + pList[i]->ApplyForceOffset( forward * mass * 600.0f * ratio, tr.endpos ); + } + } + else + { + ApplyVelocityBasedForce( pEntity, vecForward ); + } + } + + // Add recoil + QAngle recoil = QAngle( random->RandomFloat( 1.0f, 2.0f ), random->RandomFloat( -1.0f, 1.0f ), 0 ); + pOwner->ViewPunch( recoil ); + + //Explosion effect + DoEffect( EFFECT_LAUNCH, &tr.endpos ); + + PrimaryFireEffect(); + SendWeaponAnim( ACT_VM_SECONDARYATTACK ); + + m_nChangeState = ELEMENT_STATE_CLOSED; + m_flElementDebounce = gpGlobals->curtime + 0.5f; + m_flCheckSuppressTime = gpGlobals->curtime + 0.25f; + + // Don't allow the gun to regrab a thrown object!! + m_flNextSecondaryAttack = m_flNextPrimaryAttack = gpGlobals->curtime + 0.5f; +} + +//----------------------------------------------------------------------------- +// Purpose: Applies velocity-based forces to throw the entity. This code is +// called from both punt and launch carried code. +// ASSUMES: that pEntity is a vphysics entity. +// Input : - +//----------------------------------------------------------------------------- +void CWeaponPhysCannon::ApplyVelocityBasedForce( CBaseEntity *pEntity, const Vector &forward ) +{ + // Get the launch velocity + Vector vVel = Pickup_PhysGunLaunchVelocity( pEntity, forward ); + + // Get the launch angular impulse + AngularImpulse aVel = Pickup_PhysGunLaunchAngularImpulse( pEntity ); + + // Get the physics object (MUST have one) + IPhysicsObject *pPhysicsObject = pEntity->VPhysicsGetObject(); + if ( pPhysicsObject == NULL ) + { + Assert( 0 ); + return; + } + + // Affect the object + CRagdollProp *pRagdoll = dynamic_cast( pEntity ); + if ( pRagdoll == NULL ) + { + pPhysicsObject->AddVelocity( &vVel, &aVel ); + } + else + { + Vector vTempVel; + AngularImpulse vTempAVel; + + ragdoll_t *pRagdollPhys = pRagdoll->GetRagdoll( ); + for ( int j = 0; j < pRagdollPhys->listCount; ++j ) + { + pRagdollPhys->list[j].pObject->AddVelocity( &vVel, &aVel ); + } + } +} + + +//----------------------------------------------------------------------------- +// Punt non-physics +//----------------------------------------------------------------------------- +void CWeaponPhysCannon::PuntRagdoll( CBaseEntity *pEntity, const Vector &vecForward, trace_t &tr ) +{ + CBasePlayer *pOwner = ToBasePlayer( GetOwner() ); + Pickup_OnPhysGunDrop( pEntity, pOwner, LAUNCHED_BY_CANNON ); + + CTakeDamageInfo info; + + Vector forward = vecForward; + info.SetAttacker( GetOwner() ); + info.SetInflictor( this ); + info.SetDamage( 0.0f ); + info.SetDamageType( DMG_PHYSGUN ); + pEntity->DispatchTraceAttack( info, forward, &tr ); + ApplyMultiDamage(); + + if ( Pickup_OnAttemptPhysGunPickup( pEntity, pOwner, PUNTED_BY_CANNON ) ) + { + Physgun_OnPhysGunPickup( pEntity, pOwner, PUNTED_BY_CANNON ); + + if( forward.z < 0 ) + { + //reflect, but flatten the trajectory out a bit so it's easier to hit standing targets + forward.z *= -0.65f; + } + + Vector vVel = forward * 1500; + AngularImpulse aVel = Pickup_PhysGunLaunchAngularImpulse( pEntity ); + + CRagdollProp *pRagdoll = dynamic_cast( pEntity ); + ragdoll_t *pRagdollPhys = pRagdoll->GetRagdoll( ); + + int j; + for ( j = 0; j < pRagdollPhys->listCount; ++j ) + { + pRagdollPhys->list[j].pObject->AddVelocity( &vVel, NULL ); + } + } + + // Add recoil + QAngle recoil = QAngle( random->RandomFloat( 1.0f, 2.0f ), random->RandomFloat( -1.0f, 1.0f ), 0 ); + pOwner->ViewPunch( recoil ); + + //Explosion effect + DoEffect( EFFECT_LAUNCH, &tr.endpos ); + + PrimaryFireEffect(); + SendWeaponAnim( ACT_VM_SECONDARYATTACK ); + + m_nChangeState = ELEMENT_STATE_CLOSED; + m_flElementDebounce = gpGlobals->curtime + 0.5f; + m_flCheckSuppressTime = gpGlobals->curtime + 0.25f; + + // Don't allow the gun to regrab a thrown object!! + m_flNextSecondaryAttack = m_flNextPrimaryAttack = gpGlobals->curtime + 0.5f; +} + + +//----------------------------------------------------------------------------- +// Trace length +//----------------------------------------------------------------------------- +float CWeaponPhysCannon::TraceLength() +{ + if ( !IsMegaPhysCannon() ) + { + return physcannon_tracelength.GetFloat(); + } + + return physcannon_mega_tracelength.GetFloat(); +} + +//----------------------------------------------------------------------------- +// If there's any special rejection code you need to do per entity then do it here +// This is kinda nasty but I'd hate to move more physcannon related stuff into CBaseEntity +//----------------------------------------------------------------------------- +bool CWeaponPhysCannon::EntityAllowsPunts( CBaseEntity *pEntity ) +{ + if ( pEntity->HasSpawnFlags( SF_PHYSBOX_NEVER_PUNT ) ) + { + CPhysBox *pPhysBox = dynamic_cast(pEntity); + + if ( pPhysBox != NULL ) + { + if ( pPhysBox->HasSpawnFlags( SF_PHYSBOX_NEVER_PUNT ) ) + { + return false; + } + } + } + + if ( pEntity->HasSpawnFlags( SF_WEAPON_NO_PHYSCANNON_PUNT ) ) + { + CBaseCombatWeapon *pWeapon = dynamic_cast(pEntity); + + if ( pWeapon != NULL ) + { + if ( pWeapon->HasSpawnFlags( SF_WEAPON_NO_PHYSCANNON_PUNT ) ) + { + return false; + } + } + } + + return true; +} + + +//----------------------------------------------------------------------------- +// Purpose: +// +// This mode is a toggle. Primary fire one time to pick up a physics object. +// With an object held, click primary fire again to drop object. +//----------------------------------------------------------------------------- +void CWeaponPhysCannon::PrimaryAttack( void ) +{ + if( m_flNextPrimaryAttack > gpGlobals->curtime ) + return; + + CBasePlayer *pOwner = ToBasePlayer( GetOwner() ); + + if ( pOwner == NULL ) + return; + + if( m_bActive ) + { + // Punch the object being held!! + Vector forward; + pOwner->EyeVectors( &forward ); + + // Validate the item is within punt range + CBaseEntity *pHeld = m_grabController.GetAttached(); + Assert( pHeld != NULL ); + + if ( pHeld != NULL ) + { + float heldDist = ( pHeld->WorldSpaceCenter() - pOwner->WorldSpaceCenter() ).Length(); + + if ( heldDist > physcannon_tracelength.GetFloat() ) + { + // We can't punt this yet + DryFire(); + return; + } + } + + LaunchObject( forward, physcannon_maxforce.GetFloat() ); + + PrimaryFireEffect(); + SendWeaponAnim( ACT_VM_SECONDARYATTACK ); + return; + } + + // If not active, just issue a physics punch in the world. + m_flNextPrimaryAttack = gpGlobals->curtime + 0.5f; + + Vector forward; + pOwner->EyeVectors( &forward ); + + // NOTE: Notice we're *not* using the mega tracelength here + // when you have the mega cannon. Punting has shorter range. + Vector start, end; + start = pOwner->Weapon_ShootPosition(); + float flPuntDistance = physcannon_tracelength.GetFloat(); + VectorMA( start, flPuntDistance, forward, end ); + + trace_t tr; + UTIL_PhyscannonTraceHull( start, end, -Vector(8,8,8), Vector(8,8,8), pOwner, &tr ); + bool bValid = true; + CBaseEntity *pEntity = tr.m_pEnt; + if ( tr.fraction == 1 || !tr.m_pEnt || tr.m_pEnt->IsEFlagSet( EFL_NO_PHYSCANNON_INTERACTION ) ) + { + bValid = false; + } + else if ( (pEntity->GetMoveType() != MOVETYPE_VPHYSICS) && ( pEntity->m_takedamage == DAMAGE_NO ) ) + { + bValid = false; + } + + // If the entity we've hit is invalid, try a traceline instead + if ( !bValid ) + { + UTIL_PhyscannonTraceLine( start, end, pOwner, &tr ); + if ( tr.fraction == 1 || !tr.m_pEnt || tr.m_pEnt->IsEFlagSet( EFL_NO_PHYSCANNON_INTERACTION ) ) + { + if( hl2_episodic.GetBool() ) + { + // Try to find something in a very small cone. + CBaseEntity *pObject = FindObjectInCone( start, forward, physcannon_punt_cone.GetFloat() ); + + if( pObject ) + { + // Trace to the object. + UTIL_PhyscannonTraceLine( start, pObject->WorldSpaceCenter(), pOwner, &tr ); + + if( tr.m_pEnt && tr.m_pEnt == pObject && !(pObject->IsEFlagSet(EFL_NO_PHYSCANNON_INTERACTION)) ) + { + bValid = true; + pEntity = pObject; + } + } + } + } + else + { + bValid = true; + pEntity = tr.m_pEnt; + } + } + + if( !bValid ) + { + DryFire(); + return; + } + + // See if we hit something + if ( pEntity->GetMoveType() != MOVETYPE_VPHYSICS ) + { + if ( pEntity->m_takedamage == DAMAGE_NO ) + { + DryFire(); + return; + } + + if( GetOwner()->IsPlayer() && !IsMegaPhysCannon() ) + { + // Don't let the player zap any NPC's except regular antlions and headcrabs. + if( pEntity->IsNPC() && pEntity->Classify() != CLASS_HEADCRAB && !FClassnameIs(pEntity, "npc_antlion") ) + { + DryFire(); + return; + } + } + + if ( IsMegaPhysCannon() ) + { + if ( pEntity->IsNPC() && !pEntity->IsEFlagSet( EFL_NO_MEGAPHYSCANNON_RAGDOLL ) && pEntity->MyNPCPointer()->CanBecomeRagdoll() ) + { + CTakeDamageInfo info( pOwner, pOwner, 1.0f, DMG_GENERIC ); + CBaseEntity *pRagdoll = CreateServerRagdoll( pEntity->MyNPCPointer(), 0, info, COLLISION_GROUP_INTERACTIVE_DEBRIS, true ); + PhysSetEntityGameFlags( pRagdoll, FVPHYSICS_NO_SELF_COLLISIONS ); + pRagdoll->SetCollisionBounds( pEntity->CollisionProp()->OBBMins(), pEntity->CollisionProp()->OBBMaxs() ); + + // Necessary to cause it to do the appropriate death cleanup + CTakeDamageInfo ragdollInfo( pOwner, pOwner, 10000.0, DMG_PHYSGUN | DMG_REMOVENORAGDOLL ); + pEntity->TakeDamage( ragdollInfo ); + + PuntRagdoll( pRagdoll, forward, tr ); + return; + } + } + + PuntNonVPhysics( pEntity, forward, tr ); + } + else + { + if ( EntityAllowsPunts( pEntity) == false ) + { + DryFire(); + return; + } + + if ( !IsMegaPhysCannon() ) + { + if ( IsFlesh( pEntity ) ) + { + DryFire(); + return; + } + PuntVPhysics( pEntity, forward, tr ); + } + else + { + if ( dynamic_cast(pEntity) ) + { + PuntRagdoll( pEntity, forward, tr ); + } + else + { + PuntVPhysics( pEntity, forward, tr ); + } + } + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Click secondary attack whilst holding an object to hurl it. +//----------------------------------------------------------------------------- +void CWeaponPhysCannon::SecondaryAttack( void ) +{ + if ( m_flNextSecondaryAttack > gpGlobals->curtime ) + return; + + CBasePlayer *pOwner = ToBasePlayer( GetOwner() ); + + if ( pOwner == NULL ) + return; + + // See if we should drop a held item + if ( ( m_bActive ) && ( pOwner->m_afButtonPressed & IN_ATTACK2 ) ) + { + // Drop the held object + m_flNextPrimaryAttack = gpGlobals->curtime + 0.5; + m_flNextSecondaryAttack = gpGlobals->curtime + 0.5; + + DetachObject(); + + DoEffect( EFFECT_READY ); + + SendWeaponAnim( ACT_VM_PRIMARYATTACK ); + } + else + { + // Otherwise pick it up + FindObjectResult_t result = FindObject(); + switch ( result ) + { + case OBJECT_FOUND: + WeaponSound( SPECIAL1 ); + SendWeaponAnim( ACT_VM_PRIMARYATTACK ); + m_flNextSecondaryAttack = gpGlobals->curtime + 0.5f; + + // We found an object. Debounce the button + m_nAttack2Debounce |= pOwner->m_nButtons; + break; + + case OBJECT_NOT_FOUND: + m_flNextSecondaryAttack = gpGlobals->curtime + 0.1f; + CloseElements(); + break; + + case OBJECT_BEING_DETACHED: + m_flNextSecondaryAttack = gpGlobals->curtime + 0.01f; + break; + } + + DoEffect( EFFECT_HOLDING ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CWeaponPhysCannon::WeaponIdle( void ) +{ + if ( HasWeaponIdleTimeElapsed() ) + { + if ( m_bActive ) + { + //Shake when holding an item + SendWeaponAnim( ACT_VM_RELOAD ); + } + else + { + //Otherwise idle simply + SendWeaponAnim( ACT_VM_IDLE ); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *pObject - +//----------------------------------------------------------------------------- +bool CWeaponPhysCannon::AttachObject( CBaseEntity *pObject, const Vector &vPosition ) +{ + if ( m_bActive ) + return false; + + if ( CanPickupObject( pObject ) == false ) + return false; + + m_grabController.SetIgnorePitch( false ); + m_grabController.SetAngleAlignment( 0 ); + + bool bKilledByGrab = false; + + bool bIsMegaPhysCannon = IsMegaPhysCannon(); + if ( bIsMegaPhysCannon ) + { + if ( pObject->IsNPC() && !pObject->IsEFlagSet( EFL_NO_MEGAPHYSCANNON_RAGDOLL ) ) + { + Assert( pObject->MyNPCPointer()->CanBecomeRagdoll() ); + CTakeDamageInfo info( GetOwner(), GetOwner(), 1.0f, DMG_GENERIC ); + CBaseEntity *pRagdoll = CreateServerRagdoll( pObject->MyNPCPointer(), 0, info, COLLISION_GROUP_INTERACTIVE_DEBRIS, true ); + PhysSetEntityGameFlags( pRagdoll, FVPHYSICS_NO_SELF_COLLISIONS ); + + pRagdoll->SetCollisionBounds( pObject->CollisionProp()->OBBMins(), pObject->CollisionProp()->OBBMaxs() ); + + // Necessary to cause it to do the appropriate death cleanup + CTakeDamageInfo ragdollInfo( GetOwner(), GetOwner(), 10000.0, DMG_PHYSGUN | DMG_REMOVENORAGDOLL ); + pObject->TakeDamage( ragdollInfo ); + + // Now we act on the ragdoll for the remainder of the time + pObject = pRagdoll; + bKilledByGrab = true; + } + } + + IPhysicsObject *pPhysics = pObject->VPhysicsGetObject(); + + // Must be valid + if ( !pPhysics ) + return false; + + CHL2_Player *pOwner = (CHL2_Player *)ToBasePlayer( GetOwner() ); + + m_bActive = true; + if( pOwner ) + { +#ifdef HL2_EPISODIC + CBreakableProp *pProp = dynamic_cast< CBreakableProp * >( pObject ); + + if ( pProp && pProp->HasInteraction( PROPINTER_PHYSGUN_CREATE_FLARE ) ) + { + pOwner->FlashlightTurnOff(); + } +#endif + + // NOTE: This can change the mass; so it must be done before max speed setting + Physgun_OnPhysGunPickup( pObject, pOwner, PICKED_UP_BY_CANNON ); + } + + // NOTE :This must happen after OnPhysGunPickup because that can change the mass + m_grabController.AttachEntity( pOwner, pObject, pPhysics, bIsMegaPhysCannon, vPosition, (!bKilledByGrab) ); + + if( pOwner ) + { + pOwner->EnableSprint( false ); + + float loadWeight = ( 1.0f - GetLoadPercentage() ); + float maxSpeed = hl2_walkspeed.GetFloat() + ( ( hl2_normspeed.GetFloat() - hl2_walkspeed.GetFloat() ) * loadWeight ); + + //Msg( "Load perc: %f -- Movement speed: %f/%f\n", loadWeight, maxSpeed, hl2_normspeed.GetFloat() ); + pOwner->SetMaxSpeed( maxSpeed ); + } + + // Don't drop again for a slight delay, in case they were pulling objects near them + m_flNextSecondaryAttack = gpGlobals->curtime + 0.4f; + + DoEffect( EFFECT_HOLDING ); + OpenElements(); + + if ( GetMotorSound() ) + { + (CSoundEnvelopeController::GetController()).Play( GetMotorSound(), 0.0f, 50 ); + (CSoundEnvelopeController::GetController()).SoundChangePitch( GetMotorSound(), 100, 0.5f ); + (CSoundEnvelopeController::GetController()).SoundChangeVolume( GetMotorSound(), 0.8f, 0.5f ); + } + + return true; +} + +CWeaponPhysCannon::FindObjectResult_t CWeaponPhysCannon::FindObject( void ) +{ + CBasePlayer *pPlayer = ToBasePlayer( GetOwner() ); + + Assert( pPlayer ); + if ( pPlayer == NULL ) + return OBJECT_NOT_FOUND; + + Vector forward; + pPlayer->EyeVectors( &forward ); + + // Setup our positions + Vector start = pPlayer->Weapon_ShootPosition(); + float testLength = TraceLength() * 4.0f; + Vector end = start + forward * testLength; + + if( IsMegaPhysCannon() && hl2_episodic.GetBool() ) + { + Vector vecAutoAimDir = pPlayer->GetAutoaimVector( 1.0f, testLength ); + end = start + vecAutoAimDir * testLength; + } + + // Try to find an object by looking straight ahead + trace_t tr; + UTIL_PhyscannonTraceLine( start, end, pPlayer, &tr ); + + // Try again with a hull trace + if ( ( tr.fraction == 1.0 ) || ( tr.m_pEnt == NULL ) || ( tr.m_pEnt->IsWorld() ) ) + { + UTIL_PhyscannonTraceHull( start, end, -Vector(4,4,4), Vector(4,4,4), pPlayer, &tr ); + } + + CBaseEntity *pEntity = tr.m_pEnt ? tr.m_pEnt->GetRootMoveParent() : NULL; + bool bAttach = false; + bool bPull = false; + + // If we hit something, pick it up or pull it + if ( ( tr.fraction != 1.0f ) && ( tr.m_pEnt ) && ( tr.m_pEnt->IsWorld() == false ) ) + { + // Attempt to attach if within range + if ( tr.fraction <= 0.25f ) + { + bAttach = true; + } + else if ( tr.fraction > 0.25f ) + { + bPull = true; + } + } + + // Find anything within a general cone in front + CBaseEntity *pConeEntity = NULL; + if ( !IsMegaPhysCannon() ) + { + if (!bAttach && !bPull) + { + pConeEntity = FindObjectInCone( start, forward, physcannon_cone.GetFloat() ); + } + } + else + { + pConeEntity = MegaPhysCannonFindObjectInCone( start, forward, + physcannon_cone.GetFloat(), physcannon_ball_cone.GetFloat(), bAttach || bPull ); + } + + if ( pConeEntity ) + { + pEntity = pConeEntity; + + // If the object is near, grab it. Else, pull it a bit. + if ( pEntity->WorldSpaceCenter().DistToSqr( start ) <= (testLength * testLength) ) + { + bAttach = true; + } + else + { + bPull = true; + } + } + + if ( CanPickupObject( pEntity ) == false ) + { + CBaseEntity *pNewObject = Pickup_OnFailedPhysGunPickup( pEntity, start ); + + if ( pNewObject && CanPickupObject( pNewObject ) ) + { + pEntity = pNewObject; + } + else + { + // Make a noise to signify we can't pick this up + if ( !m_flLastDenySoundPlayed ) + { + m_flLastDenySoundPlayed = true; + WeaponSound( SPECIAL3 ); + } + + return OBJECT_NOT_FOUND; + } + } + + // Check to see if the object is constrained + needs to be ripped off... + CBasePlayer *pOwner = ToBasePlayer( GetOwner() ); + if ( !Pickup_OnAttemptPhysGunPickup( pEntity, pOwner, PICKED_UP_BY_CANNON ) ) + return OBJECT_BEING_DETACHED; + + if ( bAttach ) + { + return AttachObject( pEntity, tr.endpos ) ? OBJECT_FOUND : OBJECT_NOT_FOUND; + } + + if ( !bPull ) + return OBJECT_NOT_FOUND; + + // FIXME: This needs to be run through the CanPickupObject logic + IPhysicsObject *pObj = pEntity->VPhysicsGetObject(); + if ( !pObj ) + return OBJECT_NOT_FOUND; + + // If we're too far, simply start to pull the object towards us + Vector pullDir = start - pEntity->WorldSpaceCenter(); + VectorNormalize( pullDir ); + pullDir *= IsMegaPhysCannon() ? physcannon_mega_pullforce.GetFloat() : physcannon_pullforce.GetFloat(); + + float mass = PhysGetEntityMass( pEntity ); + if ( mass < 50.0f ) + { + pullDir *= (mass + 0.5) * (1/50.0f); + } + + // Nudge it towards us + pObj->ApplyForceCenter( pullDir ); + return OBJECT_NOT_FOUND; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +CBaseEntity *CWeaponPhysCannon::MegaPhysCannonFindObjectInCone( const Vector &vecOrigin, + const Vector &vecDir, float flCone, float flCombineBallCone, bool bOnlyCombineBalls ) +{ + // Find the nearest physics-based item in a cone in front of me. + CBaseEntity *list[1024]; + float flMaxDist = TraceLength() + 1.0; + float flNearestDist = flMaxDist; + bool bNearestIsCombineBall = bOnlyCombineBalls ? true : false; + Vector mins = vecOrigin - Vector( flNearestDist, flNearestDist, flNearestDist ); + Vector maxs = vecOrigin + Vector( flNearestDist, flNearestDist, flNearestDist ); + + CBaseEntity *pNearest = NULL; + + int count = UTIL_EntitiesInBox( list, 1024, mins, maxs, 0 ); + for( int i = 0 ; i < count ; i++ ) + { + if ( !list[ i ]->VPhysicsGetObject() ) + continue; + + bool bIsCombineBall = FClassnameIs( list[ i ], "prop_combine_ball" ); + if ( !bIsCombineBall && bNearestIsCombineBall ) + continue; + + // Closer than other objects + Vector los; + VectorSubtract( list[ i ]->WorldSpaceCenter(), vecOrigin, los ); + float flDist = VectorNormalize( los ); + + if ( !bIsCombineBall || bNearestIsCombineBall ) + { + // Closer than other objects + if( flDist >= flNearestDist ) + continue; + + // Cull to the cone + if ( DotProduct( los, vecDir ) <= flCone ) + continue; + } + else + { + // Close enough? + if ( flDist >= flMaxDist ) + continue; + + // Cull to the cone + if ( DotProduct( los, vecDir ) <= flCone ) + continue; + + // NOW: If it's either closer than nearest dist or within the ball cone, use it! + if ( (flDist > flNearestDist) && (DotProduct( los, vecDir ) <= flCombineBallCone) ) + continue; + } + + // Make sure it isn't occluded! + trace_t tr; + UTIL_PhyscannonTraceLine( vecOrigin, list[ i ]->WorldSpaceCenter(), GetOwner(), &tr ); + if( tr.m_pEnt == list[ i ] ) + { + flNearestDist = flDist; + pNearest = list[ i ]; + bNearestIsCombineBall = bIsCombineBall; + } + } + + return pNearest; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +CBaseEntity *CWeaponPhysCannon::FindObjectInCone( const Vector &vecOrigin, const Vector &vecDir, float flCone ) +{ + // Find the nearest physics-based item in a cone in front of me. + CBaseEntity *list[256]; + float flNearestDist = physcannon_tracelength.GetFloat() + 1.0; //Use regular distance. + Vector mins = vecOrigin - Vector( flNearestDist, flNearestDist, flNearestDist ); + Vector maxs = vecOrigin + Vector( flNearestDist, flNearestDist, flNearestDist ); + + CBaseEntity *pNearest = NULL; + + int count = UTIL_EntitiesInBox( list, 256, mins, maxs, 0 ); + for( int i = 0 ; i < count ; i++ ) + { + if ( !list[ i ]->VPhysicsGetObject() ) + continue; + + // Closer than other objects + Vector los = ( list[ i ]->WorldSpaceCenter() - vecOrigin ); + float flDist = VectorNormalize( los ); + if( flDist >= flNearestDist ) + continue; + + // Cull to the cone + if ( DotProduct( los, vecDir ) <= flCone ) + continue; + + // Make sure it isn't occluded! + trace_t tr; + UTIL_PhyscannonTraceLine( vecOrigin, list[ i ]->WorldSpaceCenter(), GetOwner(), &tr ); + if( tr.m_pEnt == list[ i ] ) + { + flNearestDist = flDist; + pNearest = list[ i ]; + } + } + + return pNearest; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +bool CGrabController::UpdateObject( CBasePlayer *pPlayer, float flError ) +{ + CBaseEntity *pEntity = GetAttached(); + if ( !pEntity || ComputeError() > flError || pPlayer->GetGroundEntity() == pEntity || !pEntity->VPhysicsGetObject() ) + { + return false; + } + + //Adrian: Oops, our object became motion disabled, let go! + IPhysicsObject *pPhys = pEntity->VPhysicsGetObject(); + if ( pPhys && pPhys->IsMoveable() == false ) + { + return false; + } + + Vector forward, right, up; + QAngle playerAngles = pPlayer->EyeAngles(); + AngleVectors( playerAngles, &forward, &right, &up ); + + if ( HL2GameRules()->MegaPhyscannonActive() ) + { + Vector los = ( pEntity->WorldSpaceCenter() - pPlayer->Weapon_ShootPosition() ); + VectorNormalize( los ); + + float flDot = DotProduct( los, forward ); + + //Let go of the item if we turn around too fast. + if ( flDot <= 0.35f ) + return false; + } + + // If this is set to true, the player will be allowed to lift the held object to + // a position directly over their own head. Otherwise, this behavior is prevented (default). + bool bAllowObjectOverhead = UTIL_IsCombineBallDefinite(pEntity); + + + + float pitch = AngleDistance(playerAngles.x,0); + + if( !bAllowObjectOverhead ) + { + playerAngles.x = clamp( pitch, -75, 75 ); + } + else + { + playerAngles.x = clamp( pitch, -90, 75 ); + } + + + + // Now clamp a sphere of object radius at end to the player's bbox + Vector radial = physcollision->CollideGetExtent( pPhys->GetCollide(), vec3_origin, pEntity->GetAbsAngles(), -forward ); + Vector player2d = pPlayer->CollisionProp()->OBBMaxs(); + float playerRadius = player2d.Length2D(); + float radius = playerRadius + fabs(DotProduct( forward, radial )); + + float distance = 24 + ( radius * 2.0f ); + + // Add the prop's distance offset + distance += m_flDistanceOffset; + + Vector start = pPlayer->Weapon_ShootPosition(); + Vector end = start + ( forward * distance ); + + trace_t tr; + CTraceFilterSkipTwoEntities traceFilter( pPlayer, pEntity, COLLISION_GROUP_NONE ); + Ray_t ray; + ray.Init( start, end ); + enginetrace->TraceRay( ray, MASK_SOLID_BRUSHONLY, &traceFilter, &tr ); + + if ( tr.fraction < 0.5 ) + { + end = start + forward * (radius*0.5f); + } + else if ( tr.fraction <= 1.0f ) + { + end = start + forward * ( distance - radius ); + } + Vector playerMins, playerMaxs, nearest; + pPlayer->CollisionProp()->WorldSpaceAABB( &playerMins, &playerMaxs ); + Vector playerLine = pPlayer->CollisionProp()->WorldSpaceCenter(); + CalcClosestPointOnLine( end, playerLine+Vector(0,0,playerMins.z), playerLine+Vector(0,0,playerMaxs.z), nearest, NULL ); + + if( !bAllowObjectOverhead ) + { + Vector delta = end - nearest; + float len = VectorNormalize(delta); + if ( len < radius ) + { + end = nearest + radius * delta; + } + } + + //Show overlays of radius + if ( g_debug_physcannon.GetBool() ) + { + NDebugOverlay::Box( end, -Vector( 2,2,2 ), Vector(2,2,2), 0, 255, 0, true, 0 ); + + NDebugOverlay::Box( GetAttached()->WorldSpaceCenter(), + -Vector( radius, radius, radius), + Vector( radius, radius, radius ), + 255, 0, 0, + true, + 0.0f ); + } + + QAngle angles = TransformAnglesFromPlayerSpace( m_attachedAnglesPlayerSpace, pPlayer ); + + // If it has a preferred orientation, update to ensure we're still oriented correctly. + Pickup_GetPreferredCarryAngles( pEntity, pPlayer, pPlayer->EntityToWorldTransform(), angles ); + + // We may be holding a prop that has preferred carry angles + if ( m_bHasPreferredCarryAngles ) + { + matrix3x4_t tmp; + ComputePlayerMatrix( pPlayer, tmp ); + angles = TransformAnglesToWorldSpace( m_vecPreferredCarryAngles, tmp ); + } + + matrix3x4_t attachedToWorld; + Vector offset; + AngleMatrix( angles, attachedToWorld ); + VectorRotate( m_attachedPositionObjectSpace, attachedToWorld, offset ); + + SetTargetPosition( end - offset, angles ); + + return true; +} + +void CWeaponPhysCannon::UpdateObject( void ) +{ + CBasePlayer *pPlayer = ToBasePlayer( GetOwner() ); + Assert( pPlayer ); + + float flError = IsMegaPhysCannon() ? 18 : 12; + if ( !m_grabController.UpdateObject( pPlayer, flError ) ) + { + DetachObject(); + return; + } +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CWeaponPhysCannon::DetachObject( bool playSound, bool wasLaunched ) +{ + if ( m_bActive == false ) + return; + + CHL2_Player *pOwner = (CHL2_Player *)ToBasePlayer( GetOwner() ); + if( pOwner != NULL ) + { + pOwner->EnableSprint( true ); + pOwner->SetMaxSpeed( hl2_normspeed.GetFloat() ); + + if( wasLaunched ) + { + pOwner->RumbleEffect( RUMBLE_357, 0, RUMBLE_FLAG_RESTART ); + } + } + + CBaseEntity *pObject = m_grabController.GetAttached(); + + m_grabController.DetachEntity( wasLaunched ); + + if ( pObject != NULL ) + { + Pickup_OnPhysGunDrop( pObject, pOwner, wasLaunched ? LAUNCHED_BY_CANNON : DROPPED_BY_CANNON ); + } + + // Stop our looping sound + if ( GetMotorSound() ) + { + (CSoundEnvelopeController::GetController()).SoundChangeVolume( GetMotorSound(), 0.0f, 1.0f ); + (CSoundEnvelopeController::GetController()).SoundChangePitch( GetMotorSound(), 50, 1.0f ); + } + + m_bActive = false; + + if ( playSound ) + { + //Play the detach sound + WeaponSound( MELEE_MISS ); + } + + RecordThrownObject( pObject ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CWeaponPhysCannon::ItemPreFrame() +{ + BaseClass::ItemPreFrame(); + + CBasePlayer *pOwner = ToBasePlayer( GetOwner() ); + + if ( pOwner == NULL ) + return; + + m_flElementPosition = UTIL_Approach( m_flElementDestination, m_flElementPosition, 0.1f ); + + CBaseViewModel *vm = pOwner->GetViewModel(); + + if ( vm != NULL ) + { + vm->SetPoseParameter( "active", m_flElementPosition ); + } + + // Update the object if the weapon is switched on. + if( m_bActive ) + { + UpdateObject(); + } + + if( gpGlobals->curtime >= m_flTimeNextObjectPurge ) + { + PurgeThrownObjects(); + m_flTimeNextObjectPurge = gpGlobals->curtime + 0.5f; + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CWeaponPhysCannon::CheckForTarget( void ) +{ + //See if we're suppressing this + if ( m_flCheckSuppressTime > gpGlobals->curtime ) + return; + + // holstered + if ( IsEffectActive( EF_NODRAW ) ) + return; + + CBasePlayer *pOwner = ToBasePlayer( GetOwner() ); + + if ( pOwner == NULL ) + return; + + if ( m_bActive ) + return; + + Vector aimDir; + pOwner->EyeVectors( &aimDir ); + + Vector startPos = pOwner->Weapon_ShootPosition(); + Vector endPos; + VectorMA( startPos, TraceLength(), aimDir, endPos ); + + trace_t tr; + UTIL_PhyscannonTraceHull( startPos, endPos, -Vector(4,4,4), Vector(4,4,4), GetOwner(), &tr ); + + if ( ( tr.fraction != 1.0f ) && ( tr.m_pEnt != NULL ) ) + { + // FIXME: Try just having the elements always open when pointed at a physics object + if ( CanPickupObject( tr.m_pEnt ) || Pickup_ForcePhysGunOpen( tr.m_pEnt, pOwner ) ) + // if ( ( tr.m_pEnt->VPhysicsGetObject() != NULL ) && ( tr.m_pEnt->GetMoveType() == MOVETYPE_VPHYSICS ) ) + { + m_nChangeState = ELEMENT_STATE_NONE; + OpenElements(); + return; + } + } + + // Close the elements after a delay to prevent overact state switching + if ( ( m_flElementDebounce < gpGlobals->curtime ) && ( m_nChangeState == ELEMENT_STATE_NONE ) ) + { + m_nChangeState = ELEMENT_STATE_CLOSED; + m_flElementDebounce = gpGlobals->curtime + 0.5f; + } +} + + +//----------------------------------------------------------------------------- +// Begin upgrading! +//----------------------------------------------------------------------------- +void CWeaponPhysCannon::BeginUpgrade() +{ + if ( IsMegaPhysCannon() ) + return; + + if ( m_bIsCurrentlyUpgrading ) + return; + + SetSequence( SelectWeightedSequence( ACT_PHYSCANNON_UPGRADE ) ); + ResetSequenceInfo(); + + m_bIsCurrentlyUpgrading = true; + + SetContextThink( &CWeaponPhysCannon::WaitForUpgradeThink, gpGlobals->curtime + 6.0f, s_pWaitForUpgradeContext ); + + EmitSound( "WeaponDissolve.Charge" ); + + // Bloat our bounds + CollisionProp()->UseTriggerBounds( true, 32.0f ); + + // Turn on the new skin + m_nSkin = MEGACANNON_SKIN; +} + + +//----------------------------------------------------------------------------- +// Wait until we're done upgrading +//----------------------------------------------------------------------------- +void CWeaponPhysCannon::WaitForUpgradeThink() +{ + Assert( m_bIsCurrentlyUpgrading ); + + StudioFrameAdvance(); + if ( !IsActivityFinished() ) + { + SetContextThink( &CWeaponPhysCannon::WaitForUpgradeThink, gpGlobals->curtime + 0.1f, s_pWaitForUpgradeContext ); + return; + } + + if ( !GlobalEntity_IsInTable( "super_phys_gun" ) ) + { + GlobalEntity_Add( MAKE_STRING("super_phys_gun"), gpGlobals->mapname, GLOBAL_ON ); + } + else + { + GlobalEntity_SetState( MAKE_STRING("super_phys_gun"), GLOBAL_ON ); + } + m_bIsCurrentlyUpgrading = false; + + // This is necessary to get the effects to look different + DestroyEffects(); + + // HACK: Hacky notification back to the level that we've finish upgrading + CBaseEntity *pEnt = gEntList.FindEntityByName( NULL, "script_physcannon_upgrade" ); + if ( pEnt ) + { + variant_t emptyVariant; + pEnt->AcceptInput( "Trigger", this, this, emptyVariant, 0 ); + } + + StopSound( "WeaponDissolve.Charge" ); + + // Re-enable weapon pickup + AddSolidFlags( FSOLID_TRIGGER ); + + SetContextThink( NULL, gpGlobals->curtime, s_pWaitForUpgradeContext ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CWeaponPhysCannon::DoEffectIdle( void ) +{ + if ( IsEffectActive( EF_NODRAW ) ) + { + StopEffects(); + return; + } + + CBasePlayer *pOwner = ToBasePlayer( GetOwner() ); + + if ( pOwner == NULL ) + return; + + if ( m_bPhyscannonState != IsMegaPhysCannon() ) + { + DestroyEffects(); + StartEffects(); + + m_bPhyscannonState = IsMegaPhysCannon(); + + //This means we just switched to regular physcannon this frame. + if ( m_bPhyscannonState == false ) + { + EmitSound( "Weapon_Physgun.Off" ); + +#ifdef HL2_EPISODIC + ForceDrop(); + + CHL2_Player *pPlayer = dynamic_cast( pOwner ); + + if ( pPlayer ) + { + pPlayer->StartArmorReduction(); + } +#endif + + CCitadelEnergyCore *pCore = static_cast( CreateEntityByName( "env_citadel_energy_core" ) ); + + if ( pCore == NULL ) + return; + + CBaseAnimating *pBeamEnt = pOwner->GetViewModel(); + + if ( pBeamEnt ) + { + int iAttachment = pBeamEnt->LookupAttachment( "muzzle" ); + + Vector vOrigin; + QAngle vAngle; + + pBeamEnt->GetAttachment( iAttachment, vOrigin, vAngle ); + + pCore->SetAbsOrigin( vOrigin ); + pCore->SetAbsAngles( vAngle ); + + DispatchSpawn( pCore ); + pCore->Activate(); + + pCore->SetParent( pBeamEnt, iAttachment ); + pCore->SetScale( 2.5f ); + + variant_t variant; + variant.SetFloat( 1.0f ); + + g_EventQueue.AddEvent( pCore, "StartDischarge", 0, pOwner, pOwner ); + g_EventQueue.AddEvent( pCore, "Stop", variant, 1, pOwner, pOwner ); + + pCore->SetThink ( &CCitadelEnergyCore::SUB_Remove ); + pCore->SetNextThink( gpGlobals->curtime + 10.0f ); + + m_nSkin = 0; + } + } + } + + float flScaleFactor = SpriteScaleFactor(); + + // Flicker the end sprites + if ( ( m_hEndSprites[0] != NULL ) && ( m_hEndSprites[1] != NULL ) ) + { + //Make the end points flicker as fast as possible + //FIXME: Make this a property of the CSprite class! + for ( int i = 0; i < 2; i++ ) + { + m_hEndSprites[i]->SetBrightness( random->RandomInt( 200, 255 ) ); + m_hEndSprites[i]->SetScale( random->RandomFloat( 0.1, 0.15 ) * flScaleFactor ); + } + } + + // Flicker the glow sprites + for ( int i = 0; i < NUM_SPRITES; i++ ) + { + if ( m_hGlowSprites[i] == NULL ) + continue; + + if ( IsMegaPhysCannon() ) + { + m_hGlowSprites[i]->SetBrightness( random->RandomInt( 32, 48 ) ); + m_hGlowSprites[i]->SetScale( random->RandomFloat( 0.15, 0.2 ) * flScaleFactor ); + } + else + { + m_hGlowSprites[i]->SetBrightness( random->RandomInt( 16, 24 ) ); + m_hGlowSprites[i]->SetScale( random->RandomFloat( 0.3, 0.35 ) * flScaleFactor ); + } + } + + // Only do these effects on the mega-cannon + if ( IsMegaPhysCannon() ) + { + // Randomly arc between the elements and core + if ( random->RandomInt( 0, 100 ) == 0 && !engine->IsPaused() ) + { + CBeam *pBeam = CBeam::BeamCreate( MEGACANNON_BEAM_SPRITE, 1 ); + + CBaseEntity *pBeamEnt = pOwner->GetViewModel(); + pBeam->EntsInit( pBeamEnt, pBeamEnt ); + + int startAttachment; + int sprite; + + if ( random->RandomInt( 0, 1 ) ) + { + startAttachment = LookupAttachment( "fork1t" ); + sprite = 0; + } + else + { + startAttachment = LookupAttachment( "fork2t" ); + sprite = 1; + } + + int endAttachment = 1; + + pBeam->SetStartAttachment( startAttachment ); + pBeam->SetEndAttachment( endAttachment ); + pBeam->SetNoise( random->RandomFloat( 8.0f, 16.0f ) ); + pBeam->SetColor( 255, 255, 255 ); + pBeam->SetScrollRate( 25 ); + pBeam->SetBrightness( 128 ); + pBeam->SetWidth( 1 ); + pBeam->SetEndWidth( random->RandomFloat( 2, 8 ) ); + + float lifetime = random->RandomFloat( 0.2f, 0.4f ); + + pBeam->LiveForTime( lifetime ); + + if ( m_hEndSprites[sprite] != NULL ) + { + // Turn on the sprite for awhile + m_hEndSprites[sprite]->TurnOn(); + m_flEndSpritesOverride[sprite] = gpGlobals->curtime + lifetime; + EmitSound( "Weapon_MegaPhysCannon.ChargeZap" ); + } + } + + if ( m_hCenterSprite != NULL ) + { + if ( m_EffectState == EFFECT_HOLDING ) + { + m_hCenterSprite->SetBrightness( random->RandomInt( 32, 64 ) ); + m_hCenterSprite->SetScale( random->RandomFloat( 0.2, 0.25 ) * flScaleFactor ); + } + else + { + m_hCenterSprite->SetBrightness( random->RandomInt( 32, 64 ) ); + m_hCenterSprite->SetScale( random->RandomFloat( 0.125, 0.15 ) * flScaleFactor ); + } + } + + if ( m_hBlastSprite != NULL ) + { + if ( m_EffectState == EFFECT_HOLDING ) + { + m_hBlastSprite->SetBrightness( random->RandomInt( 125, 150 ) ); + m_hBlastSprite->SetScale( random->RandomFloat( 0.125, 0.15 ) * flScaleFactor ); + } + else + { + m_hBlastSprite->SetBrightness( random->RandomInt( 32, 64 ) ); + m_hBlastSprite->SetScale( random->RandomFloat( 0.075, 0.15 ) * flScaleFactor ); + } + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Update our idle effects even when deploying +//----------------------------------------------------------------------------- +void CWeaponPhysCannon::ItemBusyFrame( void ) +{ + DoEffectIdle(); + + BaseClass::ItemBusyFrame(); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CWeaponPhysCannon::ItemPostFrame() +{ + CBasePlayer *pOwner = ToBasePlayer( GetOwner() ); + if ( pOwner == NULL ) + { + // We found an object. Debounce the button + m_nAttack2Debounce = 0; + return; + } + + //Check for object in pickup range + if ( m_bActive == false ) + { + CheckForTarget(); + + if ( ( m_flElementDebounce < gpGlobals->curtime ) && ( m_nChangeState != ELEMENT_STATE_NONE ) ) + { + if ( m_nChangeState == ELEMENT_STATE_OPEN ) + { + OpenElements(); + } + else if ( m_nChangeState == ELEMENT_STATE_CLOSED ) + { + CloseElements(); + } + + m_nChangeState = ELEMENT_STATE_NONE; + } + } + + // NOTE: Attack2 will be considered to be pressed until the first item is picked up. + int nAttack2Mask = pOwner->m_nButtons & (~m_nAttack2Debounce); + if ( nAttack2Mask & IN_ATTACK2 ) + { + SecondaryAttack(); + } + else + { + // Reset our debouncer + m_flLastDenySoundPlayed = false; + + if ( m_bActive == false ) + { + DoEffect( EFFECT_READY ); + } + } + + if (( pOwner->m_nButtons & IN_ATTACK2 ) == 0 ) + { + m_nAttack2Debounce = 0; + } + + if ( pOwner->m_nButtons & IN_ATTACK ) + { + PrimaryAttack(); + } + else + { + WeaponIdle(); + } + + if ( hl2_episodic.GetBool() == true ) + { + if ( IsMegaPhysCannon() ) + { + if ( !( pOwner->m_nButtons & IN_ATTACK ) ) + { + m_flNextPrimaryAttack = gpGlobals->curtime; + } + } + } + + // Update our idle effects (flickers, etc) + DoEffectIdle(); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +#define PHYSCANNON_DANGER_SOUND_RADIUS 128 + +void CWeaponPhysCannon::LaunchObject( const Vector &vecDir, float flForce ) +{ + // FIRE!!! + if( m_grabController.GetAttached() ) + { + CBaseEntity *pObject = m_grabController.GetAttached(); + + DetachObject( false, true ); + + // Trace ahead a bit and make a chain of danger sounds ahead of the phys object + // to scare potential targets + trace_t tr; + Vector vecStart = pObject->GetAbsOrigin(); + Vector vecSpot; + int iLength; + int i; + + UTIL_TraceLine( vecStart, vecStart + vecDir * flForce, MASK_SHOT, pObject, COLLISION_GROUP_NONE, &tr ); + iLength = static_cast(( tr.startpos - tr.endpos ).Length()); + vecSpot = vecStart + vecDir * PHYSCANNON_DANGER_SOUND_RADIUS; + + for( i = PHYSCANNON_DANGER_SOUND_RADIUS ; i < iLength ; i += PHYSCANNON_DANGER_SOUND_RADIUS ) + { + CSoundEnt::InsertSound( SOUND_PHYSICS_DANGER, vecSpot, PHYSCANNON_DANGER_SOUND_RADIUS, 0.5, pObject ); + vecSpot = vecSpot + ( vecDir * PHYSCANNON_DANGER_SOUND_RADIUS ); + } + + // Launch + ApplyVelocityBasedForce( pObject, vecDir ); + + // Don't allow the gun to regrab a thrown object!! + m_flNextSecondaryAttack = m_flNextPrimaryAttack = gpGlobals->curtime + 0.5; + + Vector center = pObject->WorldSpaceCenter(); + + //Do repulse effect + DoEffect( EFFECT_LAUNCH, ¢er ); + } + + // Stop our looping sound + if ( GetMotorSound() ) + { + (CSoundEnvelopeController::GetController()).SoundChangeVolume( GetMotorSound(), 0.0f, 1.0f ); + (CSoundEnvelopeController::GetController()).SoundChangePitch( GetMotorSound(), 50, 1.0f ); + } + + //Close the elements and suppress checking for a bit + m_nChangeState = ELEMENT_STATE_CLOSED; + m_flElementDebounce = gpGlobals->curtime + 0.1f; + m_flCheckSuppressTime = gpGlobals->curtime + 0.25f; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +bool CWeaponPhysCannon::IsFlesh( CBaseEntity *pTarget ) +{ + Assert( pTarget ); + IPhysicsObject *pList[64]; + int count = pTarget->VPhysicsGetObjectList( pList, ARRAYSIZE(pList) ); + for ( int i = 0; i < count; i++ ) + { + int material = pList[i]->GetMaterialIndex(); + surfacedata_t *pSurfaceData = physprops->GetSurfaceData( material ); + // Is flesh ?, don't allow pickup + if ( pSurfaceData->game.material == 'A' || pSurfaceData->game.material == 'F' || pSurfaceData->game.material == 'B' || pSurfaceData->game.material == 'H' ) + return true; + } + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *pTarget - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CWeaponPhysCannon::CanPickupObject( CBaseEntity *pTarget ) +{ + if ( pTarget == NULL ) + return false; + + if ( pTarget->GetBaseAnimating() && pTarget->GetBaseAnimating()->IsDissolving() ) + return false; + + if ( pTarget->HasSpawnFlags( SF_PHYSBOX_ALWAYS_PICK_UP ) || pTarget->HasSpawnFlags( SF_PHYSBOX_NEVER_PICK_UP ) ) + { + // It may seem strange to check this spawnflag before we know the class of this object, since the + // spawnflag only applies to func_physbox, but it can act as a filter of sorts to reduce the number + // of irrelevant entities that fall through to this next casting check, which is slower. + CPhysBox *pPhysBox = dynamic_cast(pTarget); + + if ( pPhysBox != NULL ) + { + if ( pTarget->HasSpawnFlags( SF_PHYSBOX_NEVER_PICK_UP ) ) + return false; + else + return true; + } + } + + if ( pTarget->HasSpawnFlags(SF_PHYSPROP_ALWAYS_PICK_UP) ) + { + // It may seem strange to check this spawnflag before we know the class of this object, since the + // spawnflag only applies to func_physbox, but it can act as a filter of sorts to reduce the number + // of irrelevant entities that fall through to this next casting check, which is slower. + CPhysicsProp *pPhysProp = dynamic_cast(pTarget); + if ( pPhysProp != NULL ) + return true; + } + + if ( pTarget->IsEFlagSet( EFL_NO_PHYSCANNON_INTERACTION ) ) + return false; + + CBasePlayer *pOwner = ToBasePlayer( GetOwner() ); + if ( pOwner && pOwner->GetGroundEntity() == pTarget ) + return false; + + if ( !IsMegaPhysCannon() ) + { + if ( IsFlesh( pTarget ) ) + return false; + return CBasePlayer::CanPickupObject( pTarget, physcannon_maxmass.GetFloat(), 0 ); + } + + if ( pTarget->IsNPC() && pTarget->MyNPCPointer()->CanBecomeRagdoll() ) + return true; + + if ( dynamic_cast(pTarget) ) + return true; + + return CBasePlayer::CanPickupObject( pTarget, 0, 0 ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CWeaponPhysCannon::OpenElements( void ) +{ + if ( m_bOpen ) + return; + + WeaponSound( SPECIAL2 ); + + CBasePlayer *pOwner = ToBasePlayer( GetOwner() ); + + if ( pOwner == NULL ) + return; + + if( !IsMegaPhysCannon() ) + { + pOwner->RumbleEffect(RUMBLE_PHYSCANNON_OPEN, 0, RUMBLE_FLAG_RESTART|RUMBLE_FLAG_LOOP); + } + + if ( m_flElementPosition < 0.0f ) + m_flElementPosition = 0.0f; + + m_flElementDestination = 1.0f; + + SendWeaponAnim( ACT_VM_IDLE ); + + m_bOpen = true; + + DoEffect( EFFECT_READY ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CWeaponPhysCannon::CloseElements( void ) +{ + // The mega cannon cannot be closed! + if ( IsMegaPhysCannon() ) + { + OpenElements(); + return; + } + + if ( m_bOpen == false ) + return; + + WeaponSound( MELEE_HIT ); + + CBasePlayer *pOwner = ToBasePlayer( GetOwner() ); + + if ( pOwner == NULL ) + return; + + pOwner->RumbleEffect(RUMBLE_PHYSCANNON_OPEN, 0, RUMBLE_FLAG_STOP); + + if ( m_flElementPosition > 1.0f ) + m_flElementPosition = 1.0f; + + m_flElementDestination = 0.0f; + + SendWeaponAnim( ACT_VM_IDLE ); + + m_bOpen = false; + + if ( GetMotorSound() ) + { + (CSoundEnvelopeController::GetController()).SoundChangeVolume( GetMotorSound(), 0.0f, 1.0f ); + (CSoundEnvelopeController::GetController()).SoundChangePitch( GetMotorSound(), 50, 1.0f ); + } + + DoEffect( EFFECT_CLOSED ); +} + +#define PHYSCANNON_MAX_MASS 500 + + +//----------------------------------------------------------------------------- +// Purpose: +// Output : float +//----------------------------------------------------------------------------- +float CWeaponPhysCannon::GetLoadPercentage( void ) +{ + float loadWeight = m_grabController.GetLoadWeight(); + loadWeight /= physcannon_maxmass.GetFloat(); + loadWeight = clamp( loadWeight, 0.0f, 1.0f ); + return loadWeight; +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Output : CSoundPatch +//----------------------------------------------------------------------------- +CSoundPatch *CWeaponPhysCannon::GetMotorSound( void ) +{ + if ( m_sndMotor == NULL ) + { + CPASAttenuationFilter filter( this ); + + if ( IsMegaPhysCannon() ) + { + m_sndMotor = (CSoundEnvelopeController::GetController()).SoundCreate( filter, entindex(), CHAN_STATIC, "Weapon_MegaPhysCannon.HoldSound", ATTN_NORM ); + } + else + { + m_sndMotor = (CSoundEnvelopeController::GetController()).SoundCreate( filter, entindex(), CHAN_STATIC, "Weapon_PhysCannon.HoldSound", ATTN_NORM ); + } + } + + return m_sndMotor; +} + + +//----------------------------------------------------------------------------- +// Shuts down sounds +//----------------------------------------------------------------------------- +void CWeaponPhysCannon::StopLoopingSounds() +{ + if ( m_sndMotor != NULL ) + { + (CSoundEnvelopeController::GetController()).SoundDestroy( m_sndMotor ); + m_sndMotor = NULL; + } + + BaseClass::StopLoopingSounds(); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CWeaponPhysCannon::DestroyEffects( void ) +{ + //Turn off main glow + if ( m_hCenterSprite != NULL ) + { + UTIL_Remove( m_hCenterSprite ); + m_hCenterSprite = NULL; + } + + if ( m_hBlastSprite != NULL ) + { + UTIL_Remove( m_hBlastSprite ); + m_hBlastSprite = NULL; + } + + // Turn off beams + for ( int i = 0; i < NUM_BEAMS; i++ ) + { + if ( m_hBeams[i] != NULL ) + { + UTIL_Remove( m_hBeams[i] ); + m_hBeams[i] = NULL; + } + } + + // Turn off sprites + for ( int i = 0; i < NUM_SPRITES; i++ ) + { + if ( m_hGlowSprites[i] != NULL ) + { + UTIL_Remove( m_hGlowSprites[i] ); + m_hGlowSprites[i] = NULL; + } + } + + for ( int i = 0; i < 2; i++ ) + { + if ( m_hEndSprites[i] != NULL ) + { + UTIL_Remove( m_hEndSprites[i] ); + m_hEndSprites[i] = NULL; + } + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CWeaponPhysCannon::StopEffects( bool stopSound ) +{ + // Turn off our effect state + DoEffect( EFFECT_NONE ); + + //Turn off main glow + if ( m_hCenterSprite != NULL ) + { + m_hCenterSprite->TurnOff(); + } + + if ( m_hBlastSprite != NULL ) + { + m_hBlastSprite->TurnOff(); + } + + //Turn off beams + for ( int i = 0; i < NUM_BEAMS; i++ ) + { + if ( m_hBeams[i] != NULL ) + { + m_hBeams[i]->SetBrightness( 0 ); + } + } + + //Turn off sprites + for ( int i = 0; i < NUM_SPRITES; i++ ) + { + if ( m_hGlowSprites[i] != NULL ) + { + m_hGlowSprites[i]->TurnOff(); + } + } + + for ( int i = 0; i < 2; i++ ) + { + if ( m_hEndSprites[i] != NULL ) + { + m_hEndSprites[i]->TurnOff(); + } + } + + //Shut off sounds + if ( stopSound && GetMotorSound() != NULL ) + { + (CSoundEnvelopeController::GetController()).SoundFadeOut( GetMotorSound(), 0.1f ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CWeaponPhysCannon::StartEffects( void ) +{ + CBasePlayer *pOwner = ToBasePlayer( GetOwner() ); + if ( pOwner == NULL ) + return; + + bool bIsMegaCannon = IsMegaPhysCannon(); + + int i; + float flScaleFactor = SpriteScaleFactor(); + CBaseEntity *pBeamEnt = pOwner->GetViewModel(); + + // Create the beams + for ( i = 0; i < NUM_BEAMS; i++ ) + { + if ( m_hBeams[i] ) + continue; + + const char *beamAttachNames[] = + { + "fork1t", + "fork2t", + "fork1t", + "fork2t", + "fork1t", + "fork2t", + }; + + m_hBeams[i] = CBeam::BeamCreate( + bIsMegaCannon ? MEGACANNON_BEAM_SPRITE : PHYSCANNON_BEAM_SPRITE, 1.0f ); + m_hBeams[i]->EntsInit( pBeamEnt, pBeamEnt ); + + int startAttachment = LookupAttachment( beamAttachNames[i] ); + int endAttachment = 1; + + m_hBeams[i]->FollowEntity( pBeamEnt ); + + m_hBeams[i]->AddSpawnFlags( SF_BEAM_TEMPORARY ); + m_hBeams[i]->SetStartAttachment( startAttachment ); + m_hBeams[i]->SetEndAttachment( endAttachment ); + m_hBeams[i]->SetNoise( random->RandomFloat( 8.0f, 16.0f ) ); + m_hBeams[i]->SetColor( 255, 255, 255 ); + m_hBeams[i]->SetScrollRate( 25 ); + m_hBeams[i]->SetBrightness( 128 ); + m_hBeams[i]->SetWidth( 0 ); + m_hBeams[i]->SetEndWidth( random->RandomFloat( 2, 4 ) ); + } + + //Create the glow sprites + for ( i = 0; i < NUM_SPRITES; i++ ) + { + if ( m_hGlowSprites[i] ) + continue; + + const char *attachNames[] = + { + "fork1b", + "fork1m", + "fork1t", + "fork2b", + "fork2m", + "fork2t" + }; + + m_hGlowSprites[i] = CSprite::SpriteCreate( + bIsMegaCannon ? MEGACANNON_GLOW_SPRITE : PHYSCANNON_GLOW_SPRITE, + GetAbsOrigin(), false ); + + m_hGlowSprites[i]->SetAsTemporary(); + + m_hGlowSprites[i]->SetAttachment( pOwner->GetViewModel(), LookupAttachment( attachNames[i] ) ); + + if ( bIsMegaCannon ) + { + m_hGlowSprites[i]->SetTransparency( kRenderTransAdd, 255, 255, 255, 128, kRenderFxNone ); + } + else + { + m_hGlowSprites[i]->SetTransparency( kRenderTransAdd, 255, 128, 0, 64, kRenderFxNoDissipation ); + } + + m_hGlowSprites[i]->SetBrightness( 255, 0.2f ); + m_hGlowSprites[i]->SetScale( 0.25f * flScaleFactor, 0.2f ); + } + + //Create the endcap sprites + for ( i = 0; i < 2; i++ ) + { + if ( m_hEndSprites[i] == NULL ) + { + const char *attachNames[] = + { + "fork1t", + "fork2t" + }; + + m_hEndSprites[i] = CSprite::SpriteCreate( + bIsMegaCannon ? MEGACANNON_ENDCAP_SPRITE : PHYSCANNON_ENDCAP_SPRITE, + GetAbsOrigin(), false ); + + m_hEndSprites[i]->SetAsTemporary(); + m_hEndSprites[i]->SetAttachment( pOwner->GetViewModel(), LookupAttachment( attachNames[i] ) ); + m_hEndSprites[i]->SetTransparency( kRenderTransAdd, 255, 255, 255, 255, kRenderFxNoDissipation ); + m_hEndSprites[i]->SetBrightness( 255, 0.2f ); + m_hEndSprites[i]->SetScale( 0.25f * flScaleFactor, 0.2f ); + m_hEndSprites[i]->TurnOff(); + } + } + + //Create the center glow + if ( m_hCenterSprite == NULL ) + { + m_hCenterSprite = CSprite::SpriteCreate( + bIsMegaCannon ? MEGACANNON_CENTER_GLOW : PHYSCANNON_CENTER_GLOW, + GetAbsOrigin(), false ); + + m_hCenterSprite->SetAsTemporary(); + m_hCenterSprite->SetAttachment( pOwner->GetViewModel(), 1 ); + m_hCenterSprite->SetTransparency( kRenderTransAdd, 255, 255, 255, 255, kRenderFxNone ); + m_hCenterSprite->SetBrightness( 255, 0.2f ); + m_hCenterSprite->SetScale( 0.1f, 0.2f ); + } + + //Create the blast sprite + if ( m_hBlastSprite == NULL ) + { + m_hBlastSprite = CSprite::SpriteCreate( + bIsMegaCannon ? MEGACANNON_BLAST_SPRITE : PHYSCANNON_BLAST_SPRITE, + GetAbsOrigin(), false ); + + m_hBlastSprite->SetAsTemporary(); + m_hBlastSprite->SetAttachment( pOwner->GetViewModel(), 1 ); + m_hBlastSprite->SetTransparency( kRenderTransAdd, 255, 255, 255, 255, kRenderFxNone ); + m_hBlastSprite->SetBrightness( 255, 0.2f ); + m_hBlastSprite->SetScale( 0.1f, 0.2f ); + m_hBlastSprite->TurnOff(); + } +} + +//----------------------------------------------------------------------------- +// Closing effects +//----------------------------------------------------------------------------- +void CWeaponPhysCannon::DoEffectClosed( void ) +{ + float flScaleFactor = SpriteScaleFactor(); + + // Turn off the center sprite + if ( m_hCenterSprite != NULL ) + { + m_hCenterSprite->SetBrightness( 0, 0.1f ); + m_hCenterSprite->SetScale( 0.0f, 0.1f ); + m_hCenterSprite->TurnOff(); + } + + // Turn off the end-caps + for ( int i = 0; i < 2; i++ ) + { + if ( m_hEndSprites[i] != NULL ) + { + m_hEndSprites[i]->TurnOff(); + } + } + + // Turn off the lightning + for ( int i = 0; i < NUM_BEAMS; i++ ) + { + if ( m_hBeams[i] != NULL ) + { + m_hBeams[i]->SetBrightness( 0 ); + } + } + + // Turn on the glow sprites + for ( int i = 0; i < NUM_SPRITES; i++ ) + { + if ( m_hGlowSprites[i] != NULL ) + { + m_hGlowSprites[i]->TurnOn(); + m_hGlowSprites[i]->SetBrightness( 16, 0.2f ); + m_hGlowSprites[i]->SetScale( 0.3f * flScaleFactor, 0.2f ); + } + } + + // Prepare for scale down + if ( m_hBlastSprite != NULL ) + { + m_hBlastSprite->TurnOn(); + m_hBlastSprite->SetScale( 1.0f, 0.0f ); + m_hBlastSprite->SetBrightness( 0, 0.0f ); + } +} + +//----------------------------------------------------------------------------- +// Closing effects +//----------------------------------------------------------------------------- +void CWeaponPhysCannon::DoMegaEffectClosed( void ) +{ + float flScaleFactor = SpriteScaleFactor(); + + // Turn off the center sprite + if ( m_hCenterSprite != NULL ) + { + m_hCenterSprite->SetBrightness( 0, 0.1f ); + m_hCenterSprite->SetScale( 0.0f, 0.1f ); + m_hCenterSprite->TurnOff(); + } + + // Turn off the end-caps + for ( int i = 0; i < 2; i++ ) + { + if ( m_hEndSprites[i] != NULL ) + { + m_hEndSprites[i]->TurnOff(); + } + } + + // Turn off the lightning + for ( int i = 0; i < NUM_BEAMS; i++ ) + { + if ( m_hBeams[i] != NULL ) + { + m_hBeams[i]->SetBrightness( 0 ); + } + } + + // Turn on the glow sprites + for ( int i = 0; i < NUM_SPRITES; i++ ) + { + if ( m_hGlowSprites[i] != NULL ) + { + m_hGlowSprites[i]->TurnOn(); + m_hGlowSprites[i]->SetBrightness( 16, 0.2f ); + m_hGlowSprites[i]->SetScale( 0.3f * flScaleFactor, 0.2f ); + } + } + + // Prepare for scale down + if ( m_hBlastSprite != NULL ) + { + m_hBlastSprite->TurnOn(); + m_hBlastSprite->SetScale( 1.0f, 0.0f ); + m_hBlastSprite->SetBrightness( 0, 0.0f ); + } +} + +//----------------------------------------------------------------------------- +// Ready effects +//----------------------------------------------------------------------------- +void CWeaponPhysCannon::DoEffectReady( ) +{ + float flScaleFactor = SpriteScaleFactor(); + + //Turn on the center sprite + if ( m_hCenterSprite != NULL ) + { + m_hCenterSprite->SetBrightness( 128, 0.2f ); + m_hCenterSprite->SetScale( 0.15f, 0.2f ); + m_hCenterSprite->TurnOn(); + } + + //Turn off the end-caps + for ( int i = 0; i < 2; i++ ) + { + if ( m_hEndSprites[i] != NULL ) + { + m_hEndSprites[i]->TurnOff(); + } + } + + //Turn off the lightning + for ( int i = 0; i < NUM_BEAMS; i++ ) + { + if ( m_hBeams[i] != NULL ) + { + m_hBeams[i]->SetBrightness( 0 ); + } + } + + //Turn on the glow sprites + for ( int i = 0; i < NUM_SPRITES; i++ ) + { + if ( m_hGlowSprites[i] != NULL ) + { + m_hGlowSprites[i]->TurnOn(); + m_hGlowSprites[i]->SetBrightness( 32, 0.2f ); + m_hGlowSprites[i]->SetScale( 0.4f * flScaleFactor, 0.2f ); + } + } + + //Scale down + if ( m_hBlastSprite != NULL ) + { + m_hBlastSprite->TurnOn(); + m_hBlastSprite->SetScale( 0.1f, 0.2f ); + m_hBlastSprite->SetBrightness( 255, 0.1f ); + } +} + + +//----------------------------------------------------------------------------- +// Holding effects +//----------------------------------------------------------------------------- +void CWeaponPhysCannon::DoEffectHolding( ) +{ + float flScaleFactor = SpriteScaleFactor(); + + // Turn off the center sprite + if ( m_hCenterSprite != NULL ) + { + m_hCenterSprite->SetBrightness( 255, 0.1f ); + m_hCenterSprite->SetScale( 0.2f, 0.2f ); + m_hCenterSprite->TurnOn(); + } + + // Turn off the end-caps + for ( int i = 0; i < 2; i++ ) + { + if ( m_hEndSprites[i] != NULL ) + { + m_hEndSprites[i]->TurnOn(); + } + } + + // Turn off the lightning + for ( int i = 0; i < NUM_BEAMS; i++ ) + { + if ( m_hBeams[i] != NULL ) + { + m_hBeams[i]->SetBrightness( 128 ); + } + } + + // Turn on the glow sprites + for ( int i = 0; i < NUM_SPRITES; i++ ) + { + if ( m_hGlowSprites[i] != NULL ) + { + m_hGlowSprites[i]->TurnOn(); + m_hGlowSprites[i]->SetBrightness( 64, 0.2f ); + m_hGlowSprites[i]->SetScale( 0.5f * flScaleFactor, 0.2f ); + } + } + + // Prepare for scale up + if ( m_hBlastSprite != NULL ) + { + m_hBlastSprite->TurnOff(); + m_hBlastSprite->SetScale( 0.1f, 0.0f ); + m_hBlastSprite->SetBrightness( 0, 0.0f ); + } +} + + +//----------------------------------------------------------------------------- +// Launch effects +//----------------------------------------------------------------------------- +void CWeaponPhysCannon::DoEffectLaunch( Vector *pos ) +{ + Assert( pos ); + if ( pos == NULL ) + return; + + CBasePlayer *pOwner = ToBasePlayer( GetOwner() ); + if ( pOwner == NULL ) + return; + + Vector endpos = *pos; + + // Check to store off our view model index + CBeam *pBeam = CBeam::BeamCreate( IsMegaPhysCannon() ? MEGACANNON_BEAM_SPRITE : PHYSCANNON_BEAM_SPRITE, 8 ); + + if ( pBeam != NULL ) + { + pBeam->PointEntInit( endpos, this ); + pBeam->SetEndAttachment( 1 ); + pBeam->SetWidth( 6.4 ); + pBeam->SetEndWidth( 12.8 ); + pBeam->SetBrightness( 255 ); + pBeam->SetColor( 255, 255, 255 ); + pBeam->LiveForTime( 0.1f ); + pBeam->RelinkBeam(); + pBeam->SetNoise( 2 ); + } + + Vector shotDir = ( endpos - pOwner->Weapon_ShootPosition() ); + VectorNormalize( shotDir ); + + //End hit + //FIXME: Probably too big + CPVSFilter filter( endpos ); + te->GaussExplosion( filter, 0.0f, endpos - ( shotDir * 4.0f ), RandomVector(-1.0f, 1.0f), 0 ); + + if ( m_hBlastSprite != NULL ) + { + m_hBlastSprite->TurnOn(); + m_hBlastSprite->SetScale( 2.0f, 0.1f ); + m_hBlastSprite->SetBrightness( 0, 0.1f ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *pos - +//----------------------------------------------------------------------------- +void CWeaponPhysCannon::DoMegaEffectLaunch( Vector *pos ) +{ + Assert( pos ); + if ( pos == NULL ) + return; + + CBasePlayer *pOwner = ToBasePlayer( GetOwner() ); + if ( pOwner == NULL ) + return; + + Vector endpos = *pos; + + // Check to store off our view model index + CBaseViewModel *vm = pOwner->GetViewModel(); + + int numBeams = random->RandomInt( 1, 2 ); + + CBeam *pBeam = CBeam::BeamCreate( IsMegaPhysCannon() ? MEGACANNON_BEAM_SPRITE : PHYSCANNON_BEAM_SPRITE, 0.8 ); + + if ( pBeam != NULL ) + { + pBeam->PointEntInit( endpos, vm ); + pBeam->SetEndAttachment( 1 ); + pBeam->SetWidth( 2 ); + pBeam->SetEndWidth( 12 ); + pBeam->SetBrightness( 255 ); + pBeam->SetColor( 255, 255, 255 ); + pBeam->LiveForTime( 0.1f ); + pBeam->RelinkBeam(); + pBeam->SetNoise( 0 ); + } + + for ( int i = 0; i < numBeams; i++ ) + { + pBeam = CBeam::BeamCreate( IsMegaPhysCannon() ? MEGACANNON_BEAM_SPRITE : PHYSCANNON_BEAM_SPRITE, 0.8 ); + + if ( pBeam != NULL ) + { + pBeam->PointEntInit( endpos, vm ); + pBeam->SetEndAttachment( 1 ); + pBeam->SetWidth( 2 ); + pBeam->SetEndWidth( random->RandomInt( 1, 2 ) ); + pBeam->SetBrightness( 255 ); + pBeam->SetColor( 255, 255, 255 ); + pBeam->LiveForTime( 0.1f ); + pBeam->RelinkBeam(); + pBeam->SetNoise( random->RandomInt( 8, 12 ) ); + } + } + + Vector shotDir = ( endpos - pOwner->Weapon_ShootPosition() ); + VectorNormalize( shotDir ); + + //End hit + //FIXME: Probably too big + CPVSFilter filter( endpos ); + te->GaussExplosion( filter, 0.0f, endpos - ( shotDir * 4.0f ), RandomVector(-1.0f, 1.0f), 0 ); +} + +//----------------------------------------------------------------------------- +// Holding effects +//----------------------------------------------------------------------------- +void CWeaponPhysCannon::DoMegaEffectHolding( void ) +{ + float flScaleFactor = SpriteScaleFactor(); + + // Turn off the center sprite + if ( m_hCenterSprite != NULL ) + { + m_hCenterSprite->SetBrightness( 255, 0.1f ); + m_hCenterSprite->SetScale( 0.2f, 0.2f ); + m_hCenterSprite->TurnOn(); + } + + // Turn off the end-caps + for ( int i = 0; i < 2; i++ ) + { + if ( m_hEndSprites[i] != NULL ) + { + m_hEndSprites[i]->TurnOn(); + } + } + + // Turn off the lightning + for ( int i = 0; i < NUM_BEAMS; i++ ) + { + if ( m_hBeams[i] != NULL ) + { + m_hBeams[i]->SetBrightness( 128 ); + } + } + + // Turn on the glow sprites + for ( int i = 0; i < NUM_SPRITES; i++ ) + { + if ( m_hGlowSprites[i] != NULL ) + { + m_hGlowSprites[i]->TurnOn(); + m_hGlowSprites[i]->SetBrightness( 32, 0.2f ); + m_hGlowSprites[i]->SetScale( 0.25f * flScaleFactor, 0.2f ); + } + } +} + +//----------------------------------------------------------------------------- +// Ready effects +//----------------------------------------------------------------------------- +void CWeaponPhysCannon::DoMegaEffectReady( void ) +{ + float flScaleFactor = SpriteScaleFactor(); + + //Turn on the center sprite + if ( m_hCenterSprite != NULL ) + { + m_hCenterSprite->SetBrightness( 128, 0.2f ); + m_hCenterSprite->SetScale( 0.15f, 0.2f ); + m_hCenterSprite->TurnOn(); + } + + //Turn off the end-caps + for ( int i = 0; i < 2; i++ ) + { + if ( m_hEndSprites[i] != NULL ) + { + if ( m_flEndSpritesOverride[i] < gpGlobals->curtime ) + { + m_hEndSprites[i]->TurnOff(); + } + } + } + + //Turn off the lightning + for ( int i = 0; i < NUM_BEAMS; i++ ) + { + if ( m_hBeams[i] != NULL ) + { + m_hBeams[i]->SetBrightness( 0 ); + } + } + + //Turn on the glow sprites + for ( int i = 0; i < NUM_SPRITES; i++ ) + { + if ( m_hGlowSprites[i] != NULL ) + { + m_hGlowSprites[i]->TurnOn(); + m_hGlowSprites[i]->SetBrightness( 24, 0.2f ); + m_hGlowSprites[i]->SetScale( 0.2f * flScaleFactor, 0.2f ); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Shutdown for the weapon when it's holstered +//----------------------------------------------------------------------------- +void CWeaponPhysCannon::DoEffectNone( void ) +{ + if ( m_hBlastSprite != NULL ) + { + // Become small + m_hBlastSprite->SetScale( 0.001f ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : effectType - +// *pos - +//----------------------------------------------------------------------------- +void CWeaponPhysCannon::DoMegaEffect( int effectType, Vector *pos ) +{ + switch( effectType ) + { + case EFFECT_CLOSED: + DoMegaEffectClosed(); + break; + + case EFFECT_READY: + DoMegaEffectReady(); + break; + + case EFFECT_HOLDING: + DoMegaEffectHolding(); + break; + + case EFFECT_LAUNCH: + DoMegaEffectLaunch( pos ); + break; + + default: + case EFFECT_NONE: + break; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : effectType - +//----------------------------------------------------------------------------- +void CWeaponPhysCannon::DoEffect( int effectType, Vector *pos ) +{ + // Make sure we're active + StartEffects(); + + m_EffectState = effectType; + + // Do different effects when upgraded + if ( IsMegaPhysCannon() ) + { + DoMegaEffect( effectType, pos ); + return; + } + + switch( effectType ) + { + case EFFECT_CLOSED: + DoEffectClosed( ); + break; + + case EFFECT_READY: + DoEffectReady( ); + break; + + case EFFECT_HOLDING: + DoEffectHolding(); + break; + + case EFFECT_LAUNCH: + DoEffectLaunch( pos ); + break; + + default: + case EFFECT_NONE: + DoEffectNone(); + break; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : iIndex - +// Output : const char +//----------------------------------------------------------------------------- +const char *CWeaponPhysCannon::GetShootSound( int iIndex ) const +{ + // Just do this normally if we're a normal physcannon + if ( PlayerHasMegaPhysCannon() == false ) + return BaseClass::GetShootSound( iIndex ); + + // We override this if we're the charged up version + switch( iIndex ) + { + case EMPTY: + return "Weapon_MegaPhysCannon.DryFire"; + break; + + case SINGLE: + return "Weapon_MegaPhysCannon.Launch"; + break; + + case SPECIAL1: + return "Weapon_MegaPhysCannon.Pickup"; + break; + + case MELEE_MISS: + return "Weapon_MegaPhysCannon.Drop"; + break; + + default: + break; + } + + return BaseClass::GetShootSound( iIndex ); +} + +//----------------------------------------------------------------------------- +// Purpose: Adds the specified object to the list of objects that have been +// propelled by this physgun, along with a timestamp of when the +// object was added to the list. This list is checked when a physics +// object strikes another entity, to resolve whether the player is +// accountable for the impact. +// +// Input : pObject - pointer to the object being thrown by the physcannon. +//----------------------------------------------------------------------------- +void CWeaponPhysCannon::RecordThrownObject( CBaseEntity *pObject ) +{ + thrown_objects_t thrown; + thrown.hEntity = pObject; + thrown.fTimeThrown = gpGlobals->curtime; + + // Get rid of stale and dead objects in the list. + PurgeThrownObjects(); + + // See if this object is already in the list. + int count = m_ThrownEntities.Count(); + + for( int i = 0 ; i < count ; i++ ) + { + if( m_ThrownEntities[i].hEntity == pObject ) + { + // Just update the time. + //Msg("++UPDATING: %s (%d)\n", m_ThrownEntities[i].hEntity->GetClassname(), m_ThrownEntities[i].hEntity->entindex() ); + m_ThrownEntities[i] = thrown; + return; + } + } + + //Msg("++ADDING: %s (%d)\n", pObject->GetClassname(), pObject->entindex() ); + + m_ThrownEntities.AddToTail(thrown); +} + +//----------------------------------------------------------------------------- +// Purpose: Go through the objects in the thrown objects list and discard any +// objects that have gone 'stale'. (Were thrown several seconds ago), or +// have been destroyed or removed. +// +//----------------------------------------------------------------------------- +#define PHYSCANNON_THROWN_LIST_TIMEOUT 10.0f +void CWeaponPhysCannon::PurgeThrownObjects() +{ + bool bListChanged; + + // This is bubble-sorty, but the list is also very short. + do + { + bListChanged = false; + + int count = m_ThrownEntities.Count(); + for( int i = 0 ; i < count ; i++ ) + { + bool bRemove = false; + + if( !m_ThrownEntities[i].hEntity.Get() ) + { + bRemove = true; + } + else if( gpGlobals->curtime > (m_ThrownEntities[i].fTimeThrown + PHYSCANNON_THROWN_LIST_TIMEOUT) ) + { + bRemove = true; + } + else + { + IPhysicsObject *pObject = m_ThrownEntities[i].hEntity->VPhysicsGetObject(); + + if( pObject && pObject->IsAsleep() ) + { + bRemove = true; + } + } + + if( bRemove ) + { + //Msg("--REMOVING: %s (%d)\n", m_ThrownEntities[i].hEntity->GetClassname(), m_ThrownEntities[i].hEntity->entindex() ); + m_ThrownEntities.Remove(i); + bListChanged = true; + break; + } + } + } while( bListChanged ); +} + +bool CWeaponPhysCannon::IsAccountableForObject( CBaseEntity *pObject ) +{ + // Clean out the stale and dead items. + PurgeThrownObjects(); + + // Now if this object is in the list, the player is accountable for it striking something. + int count = m_ThrownEntities.Count(); + + for( int i = 0 ; i < count ; i++ ) + { + if( m_ThrownEntities[i].hEntity == pObject ) + { + return true; + } + } + + return false; +} + +//----------------------------------------------------------------------------- +// EXTERNAL API +//----------------------------------------------------------------------------- +void PhysCannonForceDrop( CBaseCombatWeapon *pActiveWeapon, CBaseEntity *pOnlyIfHoldingThis ) +{ + CWeaponPhysCannon *pCannon = dynamic_cast(pActiveWeapon); + if ( pCannon ) + { + if ( pOnlyIfHoldingThis ) + { + pCannon->DropIfEntityHeld( pOnlyIfHoldingThis ); + } + else + { + pCannon->ForceDrop(); + } + } +} + +void PhysCannonBeginUpgrade( CBaseAnimating *pAnim ) +{ + CWeaponPhysCannon *pWeaponPhyscannon = assert_cast< CWeaponPhysCannon* >( pAnim ); + pWeaponPhyscannon->BeginUpgrade(); +} + + +bool PlayerPickupControllerIsHoldingEntity( CBaseEntity *pPickupControllerEntity, CBaseEntity *pHeldEntity ) +{ + CPlayerPickupController *pController = dynamic_cast(pPickupControllerEntity); + + return pController ? pController->IsHoldingEntity( pHeldEntity ) : false; +} + + +float PhysCannonGetHeldObjectMass( CBaseCombatWeapon *pActiveWeapon, IPhysicsObject *pHeldObject ) +{ + float mass = 0.0f; + CWeaponPhysCannon *pCannon = dynamic_cast(pActiveWeapon); + if ( pCannon ) + { + CGrabController &grab = pCannon->GetGrabController(); + mass = grab.GetSavedMass( pHeldObject ); + } + + return mass; +} + +CBaseEntity *PhysCannonGetHeldEntity( CBaseCombatWeapon *pActiveWeapon ) +{ + CWeaponPhysCannon *pCannon = dynamic_cast(pActiveWeapon); + if ( pCannon ) + { + CGrabController &grab = pCannon->GetGrabController(); + return grab.GetAttached(); + } + + return NULL; +} + +CBaseEntity *GetPlayerHeldEntity( CBasePlayer *pPlayer ) +{ + CBaseEntity *pObject = NULL; + CPlayerPickupController *pPlayerPickupController = (CPlayerPickupController *)(pPlayer->GetUseEntity()); + + if ( pPlayerPickupController ) + { + pObject = pPlayerPickupController->GetGrabController().GetAttached(); + } + + return pObject; +} + +bool PhysCannonAccountableForObject( CBaseCombatWeapon *pPhysCannon, CBaseEntity *pObject ) +{ + CWeaponPhysCannon *pCannon = dynamic_cast(pPhysCannon); + if ( pCannon ) + { + return pCannon->IsAccountableForObject(pObject); + } + + return false; +} + +float PlayerPickupGetHeldObjectMass( CBaseEntity *pPickupControllerEntity, IPhysicsObject *pHeldObject ) +{ + float mass = 0.0f; + CPlayerPickupController *pController = dynamic_cast(pPickupControllerEntity); + if ( pController ) + { + CGrabController &grab = pController->GetGrabController(); + mass = grab.GetSavedMass( pHeldObject ); + } + return mass; +} diff --git a/dlls/hl2_dll/weapon_pistol.cpp b/dlls/hl2_dll/weapon_pistol.cpp index 951dd065..cad414ca 100644 --- a/dlls/hl2_dll/weapon_pistol.cpp +++ b/dlls/hl2_dll/weapon_pistol.cpp @@ -6,10 +6,10 @@ //=============================================================================// #include "cbase.h" -#include "NPCEvent.h" +#include "npcevent.h" #include "basehlcombatweapon.h" #include "basecombatcharacter.h" -#include "AI_BaseNPC.h" +#include "ai_basenpc.h" #include "player.h" #include "gamerules.h" #include "in_buttons.h" @@ -190,7 +190,7 @@ void CWeaponPistol::Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatCh vecShootDir = npc->GetActualShootTrajectory( vecShootOrigin ); - CSoundEnt::InsertSound( SOUND_COMBAT|SOUND_CONTEXT_GUNFIRE, pOperator->GetAbsOrigin(), SOUNDENT_VOLUME_PISTOL, 0.2, pOperator, SOUNDENT_CHANNEL_WEAPON, pOperator->GetEnemy() ); + CSoundEnt::InsertSound( SOUND_COMBAT|SOUND_CONTEXT_GUNFIRE, pOperator->GetAbsOrigin(), static_cast(SOUNDENT_VOLUME_PISTOL), 0.2, pOperator, SOUNDENT_CHANNEL_WEAPON, pOperator->GetEnemy() ); WeaponSound( SINGLE_NPC ); pOperator->FireBullets( 1, vecShootOrigin, vecShootDir, VECTOR_CONE_PRECALCULATED, MAX_TRACE_LENGTH, m_iPrimaryAmmoType, 2 ); @@ -232,7 +232,7 @@ void CWeaponPistol::PrimaryAttack( void ) m_flLastAttackTime = gpGlobals->curtime; m_flSoonestPrimaryAttack = gpGlobals->curtime + PISTOL_FASTEST_REFIRE_TIME; - CSoundEnt::InsertSound( SOUND_COMBAT, GetAbsOrigin(), SOUNDENT_VOLUME_PISTOL, 0.2, GetOwner() ); + CSoundEnt::InsertSound( SOUND_COMBAT, GetAbsOrigin(), static_cast(SOUNDENT_VOLUME_PISTOL), 0.2, GetOwner() ); CBasePlayer *pOwner = ToBasePlayer( GetOwner() ); diff --git a/dlls/hl2_dll/weapon_rpg.cpp b/dlls/hl2_dll/weapon_rpg.cpp index d8480d85..3b9b14e4 100644 --- a/dlls/hl2_dll/weapon_rpg.cpp +++ b/dlls/hl2_dll/weapon_rpg.cpp @@ -18,8 +18,8 @@ #include "in_buttons.h" #include "weapon_rpg.h" #include "shake.h" -#include "AI_BaseNPC.h" -#include "AI_Squad.h" +#include "ai_basenpc.h" +#include "ai_squad.h" #include "te_effect_dispatch.h" #include "triggers.h" #include "smoke_trail.h" @@ -85,7 +85,7 @@ public: // a list of laser dots to search quickly CEntityClassList g_LaserDotList; -CLaserDot *CEntityClassList::m_pClassList = NULL; +template<> CLaserDot *CEntityClassList::m_pClassList = NULL; CLaserDot *GetLaserDotList() { return g_LaserDotList.m_pClassList; @@ -335,7 +335,7 @@ void CMissile::ShotDown( void ) void CMissile::DoExplosion( void ) { // Explode - ExplosionCreate( GetAbsOrigin(), GetAbsAngles(), GetOwnerEntity(), GetDamage(), CMissile::EXPLOSION_RADIUS, + ExplosionCreate( GetAbsOrigin(), GetAbsAngles(), GetOwnerEntity(), static_cast(GetDamage()), CMissile::EXPLOSION_RADIUS, SF_ENVEXPLOSION_NOSPARKS | SF_ENVEXPLOSION_NODLIGHTS | SF_ENVEXPLOSION_NOSMOKE, 0.0f, this); } @@ -819,7 +819,7 @@ CBaseEntity *CInfoAPCMissileHint::FindAimTarget( CBaseEntity *pMissile, const ch // a list of missiles to search quickly //----------------------------------------------------------------------------- CEntityClassList g_APCMissileList; -CAPCMissile *CEntityClassList::m_pClassList = NULL; +template <> CAPCMissile *CEntityClassList::m_pClassList = NULL; CAPCMissile *GetAPCMissileList() { return g_APCMissileList.m_pClassList; @@ -1083,9 +1083,9 @@ void CAPCMissile::DoExplosion( void ) else { #ifdef HL2_EPISODIC - ExplosionCreate( GetAbsOrigin(), GetAbsAngles(), this, APC_MISSILE_DAMAGE, 100, true, 20000 ); + ExplosionCreate( GetAbsOrigin(), GetAbsAngles(), this, static_cast(APC_MISSILE_DAMAGE), 100, true, 20000 ); #else - ExplosionCreate( GetAbsOrigin(), GetAbsAngles(), GetOwnerEntity(), APC_MISSILE_DAMAGE, 100, true, 20000 ); + ExplosionCreate( GetAbsOrigin(), GetAbsAngles(), GetOwnerEntity(), static_cast(APC_MISSILE_DAMAGE), 100, true, 20000 ); #endif } } @@ -1177,7 +1177,7 @@ void CAPCMissile::ComputeActualDotPosition( CLaserDot *pLaserDot, Vector *pActua m_hSpecificTarget = CInfoAPCMissileHint::FindAimTarget( this, STRING( m_strHint ), vecOrigin, vecVelocity ); } - CBaseEntity *pLaserTarget = m_hSpecificTarget ? m_hSpecificTarget : pLaserDot->GetTargetEntity(); + CBaseEntity *pLaserTarget = m_hSpecificTarget ? m_hSpecificTarget.Get() : pLaserDot->GetTargetEntity(); if ( !pLaserTarget ) { BaseClass::ComputeActualDotPosition( pLaserDot, pActualDotPosition, pHomingSpeed ); diff --git a/dlls/hl2_dll/weapon_shotgun.cpp b/dlls/hl2_dll/weapon_shotgun.cpp index 7674c8ce..2cd55088 100644 --- a/dlls/hl2_dll/weapon_shotgun.cpp +++ b/dlls/hl2_dll/weapon_shotgun.cpp @@ -8,14 +8,14 @@ //=============================================================================// #include "cbase.h" -#include "NPCEvent.h" -#include "basehlcombatweapon_shared.h" -#include "basecombatcharacter.h" -#include "AI_BaseNPC.h" -#include "player.h" -#include "gamerules.h" // For g_pGameRules -#include "in_buttons.h" -#include "soundent.h" +#include "npcevent.h" +#include "basehlcombatweapon_shared.h" +#include "basecombatcharacter.h" +#include "ai_basenpc.h" +#include "player.h" +#include "gamerules.h" // For g_pGameRules +#include "in_buttons.h" +#include "soundent.h" #include "vstdlib/random.h" // memdbgon must be the last include file in a .cpp file!!! @@ -420,7 +420,7 @@ void CWeaponShotgun::PrimaryAttack( void ) pPlayer->ViewPunch( QAngle( random->RandomFloat( -2, -1 ), random->RandomFloat( -2, 2 ), 0 ) ); - CSoundEnt::InsertSound( SOUND_COMBAT, GetAbsOrigin(), SOUNDENT_VOLUME_SHOTGUN, 0.2, GetOwner() ); + CSoundEnt::InsertSound( SOUND_COMBAT, GetAbsOrigin(), static_cast(SOUNDENT_VOLUME_SHOTGUN), 0.2, GetOwner() ); if (!m_iClip1 && pPlayer->GetAmmoCount(m_iPrimaryAmmoType) <= 0) { @@ -474,7 +474,7 @@ void CWeaponShotgun::SecondaryAttack( void ) pPlayer->SetMuzzleFlashTime( gpGlobals->curtime + 1.0 ); - CSoundEnt::InsertSound( SOUND_COMBAT, GetAbsOrigin(), SOUNDENT_VOLUME_SHOTGUN, 0.2 ); + CSoundEnt::InsertSound( SOUND_COMBAT, GetAbsOrigin(), static_cast(SOUNDENT_VOLUME_SHOTGUN), 0.2 ); if (!m_iClip1 && pPlayer->GetAmmoCount(m_iPrimaryAmmoType) <= 0) { diff --git a/dlls/hl2_dll/weapon_smg1.cpp b/dlls/hl2_dll/weapon_smg1.cpp index 43ac032a..78fe4246 100644 --- a/dlls/hl2_dll/weapon_smg1.cpp +++ b/dlls/hl2_dll/weapon_smg1.cpp @@ -6,14 +6,14 @@ #include "cbase.h" #include "basehlcombatweapon.h" -#include "NPCevent.h" +#include "npcevent.h" #include "basecombatcharacter.h" -#include "AI_BaseNPC.h" +#include "ai_basenpc.h" #include "player.h" #include "game.h" #include "in_buttons.h" #include "grenade_ar2.h" -#include "AI_Memory.h" +#include "ai_memory.h" #include "soundent.h" #include "rumble_shared.h" @@ -178,7 +178,7 @@ void CWeaponSMG1::FireNPCPrimaryAttack( CBaseCombatCharacter *pOperator, Vector // FIXME: use the returned number of bullets to account for >10hz firerate WeaponSoundRealtime( SINGLE_NPC ); - CSoundEnt::InsertSound( SOUND_COMBAT|SOUND_CONTEXT_GUNFIRE, pOperator->GetAbsOrigin(), SOUNDENT_VOLUME_MACHINEGUN, 0.2, pOperator, SOUNDENT_CHANNEL_WEAPON, pOperator->GetEnemy() ); + CSoundEnt::InsertSound( SOUND_COMBAT|SOUND_CONTEXT_GUNFIRE, pOperator->GetAbsOrigin(), static_cast(SOUNDENT_VOLUME_MACHINEGUN), 0.2, pOperator, SOUNDENT_CHANNEL_WEAPON, pOperator->GetEnemy() ); pOperator->FireBullets( 1, vecShootOrigin, vecShootDir, VECTOR_CONE_PRECALCULATED, MAX_TRACE_LENGTH, m_iPrimaryAmmoType, 2, entindex(), 0 ); diff --git a/dlls/hl2_dll/weapon_stunstick.cpp b/dlls/hl2_dll/weapon_stunstick.cpp index bd5b2ec4..1f1e54c2 100644 --- a/dlls/hl2_dll/weapon_stunstick.cpp +++ b/dlls/hl2_dll/weapon_stunstick.cpp @@ -211,7 +211,7 @@ void CWeaponStunStick::Operator_HandleAnimEvent( animevent_t *pEvent, CBaseComba VectorMA( pOperator->Weapon_ShootPosition(), 32, vecDirection, vecEnd ); // Stretch the swing box down to catch low level physics objects CBaseEntity *pHurt = pOperator->CheckTraceHullAttack( pOperator->Weapon_ShootPosition(), vecEnd, - Vector(-16,-16,-40), Vector(16,16,16), GetDamageForActivity( GetActivity() ), DMG_CLUB, 0.5f, false ); + Vector(-16,-16,-40), Vector(16,16,16), static_cast(GetDamageForActivity( GetActivity() )), DMG_CLUB, 0.5f, false ); // did I hit someone? if ( pHurt ) diff --git a/dlls/hl2_dll/weapon_thumper.cpp b/dlls/hl2_dll/weapon_thumper.cpp index 8825b372..bb9da9ba 100644 --- a/dlls/hl2_dll/weapon_thumper.cpp +++ b/dlls/hl2_dll/weapon_thumper.cpp @@ -8,9 +8,9 @@ #include "cbase.h" #include "basehlcombatweapon.h" -#include "NPCevent.h" +#include "npcevent.h" #include "basecombatcharacter.h" -#include "AI_BaseNPC.h" +#include "ai_basenpc.h" #include "player.h" #include "entitylist.h" #include "ndebugoverlay.h" diff --git a/dlls/hl2mp_dll/grenade_satchel.cpp b/dlls/hl2mp_dll/grenade_satchel.cpp index 31d016af..4405f26d 100644 --- a/dlls/hl2mp_dll/grenade_satchel.cpp +++ b/dlls/hl2mp_dll/grenade_satchel.cpp @@ -118,7 +118,7 @@ void CSatchelCharge::CreateEffects( void ) //----------------------------------------------------------------------------- void CSatchelCharge::InputExplode( inputdata_t &inputdata ) { - ExplosionCreate( GetAbsOrigin() + Vector( 0, 0, 16 ), GetAbsAngles(), GetThrower(), GetDamage(), 200, + ExplosionCreate( GetAbsOrigin() + Vector( 0, 0, 16 ), GetAbsAngles(), GetThrower(), static_cast(GetDamage()), 200, SF_ENVEXPLOSION_NOSPARKS | SF_ENVEXPLOSION_NODLIGHTS | SF_ENVEXPLOSION_NOSMOKE, 0.0f, this); UTIL_Remove( this ); diff --git a/dlls/hl2mp_dll/grenade_tripmine.cpp b/dlls/hl2mp_dll/grenade_tripmine.cpp index 6b4dd86d..03f5274c 100644 --- a/dlls/hl2mp_dll/grenade_tripmine.cpp +++ b/dlls/hl2mp_dll/grenade_tripmine.cpp @@ -176,7 +176,7 @@ void CTripmineGrenade::MakeBeam( void ) m_pBeam = CBeam::BeamCreate( g_pModelNameLaser, 0.35 ); m_pBeam->PointEntInit( vecTmpEnd, this ); m_pBeam->SetColor( 255, 55, 52 ); - m_pBeam->SetScrollRate( 25.6 ); + m_pBeam->SetScrollRate( static_cast(25.6) ); m_pBeam->SetBrightness( 64 ); int beamAttach = LookupAttachment("beam_attach"); @@ -266,7 +266,7 @@ void CTripmineGrenade::DelayDeathThink( void ) UTIL_TraceLine ( GetAbsOrigin() + m_vecDir * 8, GetAbsOrigin() - m_vecDir * 64, MASK_SOLID, this, COLLISION_GROUP_NONE, & tr); UTIL_ScreenShake( GetAbsOrigin(), 25.0, 150.0, 1.0, 750, SHAKE_START ); - ExplosionCreate( GetAbsOrigin() + m_vecDir * 8, GetAbsAngles(), m_hOwner, GetDamage(), 200, + ExplosionCreate( GetAbsOrigin() + m_vecDir * 8, GetAbsAngles(), m_hOwner, static_cast(GetDamage()), 200, SF_ENVEXPLOSION_NOSPARKS | SF_ENVEXPLOSION_NODLIGHTS | SF_ENVEXPLOSION_NOSMOKE, 0.0f, this); UTIL_Remove( this ); diff --git a/dlls/hl2mp_dll/hl2mp_bot_temp.cpp b/dlls/hl2mp_dll/hl2mp_bot_temp.cpp index 88419b80..3ba4fb6c 100644 --- a/dlls/hl2mp_dll/hl2mp_bot_temp.cpp +++ b/dlls/hl2mp_dll/hl2mp_bot_temp.cpp @@ -40,7 +40,6 @@ ConVar bot_crouch( "bot_crouch", "0", 0, "Bot crouches" ); static int BotNumber = 1; static int g_iNextBotTeam = -1; -static int g_iNextBotClass = -1; typedef struct { @@ -250,7 +249,7 @@ void Bot_Think( CHL2MP_Player *pBot ) QAngle angle; float angledelta = 15.0; - int maxtries = (int)360.0/angledelta; + int maxtries = static_cast(360.0 / angledelta); if ( botdata->lastturntoright ) { diff --git a/dlls/hl2mp_dll/hl2mp_bot_temp.h b/dlls/hl2mp_dll/hl2mp_bot_temp.h index 03b0bb75..562dade8 100644 --- a/dlls/hl2mp_dll/hl2mp_bot_temp.h +++ b/dlls/hl2mp_dll/hl2mp_bot_temp.h @@ -1,19 +1,18 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -//=============================================================================// -#ifndef BOT_BASE_H -#define BOT_BASE_H -#ifdef _WIN32 -#pragma once -#endif - - -// If iTeam or iClass is -1, then a team or class is randomly chosen. -CBasePlayer *BotPutInServer( bool bFrozen, int iTeam ); - - -#endif // BOT_BASE_H - +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// +#ifndef BOT_BASE_H +#define BOT_BASE_H +#ifdef _WIN32 +#pragma once +#endif + + +// If iTeam or iClass is -1, then a team or class is randomly chosen. +CBasePlayer *BotPutInServer( bool bFrozen, int iTeam ); + + +#endif // BOT_BASE_H diff --git a/dlls/hl2mp_dll/hl2mp_cvars.cpp b/dlls/hl2mp_dll/hl2mp_cvars.cpp index 21f0ac00..9b59389c 100644 --- a/dlls/hl2mp_dll/hl2mp_cvars.cpp +++ b/dlls/hl2mp_dll/hl2mp_cvars.cpp @@ -1,32 +1,32 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -//=============================================================================// - -#include "cbase.h" -#include "hl2mp_cvars.h" - - - -// Restart the round -ConVar mp_restartround( - "mp_restartround", - "0", - FCVAR_GAMEDLL, - "If non-zero, game will restart in the specified number of seconds" ); - -// Ready restart -ConVar mp_readyrestart( - "mp_readyrestart", - "0", - FCVAR_GAMEDLL, - "If non-zero, game will restart once each player gives the ready signal" ); - -// Ready signal -ConVar mp_ready_signal( - "mp_ready_signal", - "ready", - FCVAR_GAMEDLL, - "Text that each player must speak for the match to begin" ); \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include "cbase.h" +#include "hl2mp_cvars.h" + + + +// Restart the round +ConVar mp_restartround( + "mp_restartround", + "0", + FCVAR_GAMEDLL, + "If non-zero, game will restart in the specified number of seconds" ); + +// Ready restart +ConVar mp_readyrestart( + "mp_readyrestart", + "0", + FCVAR_GAMEDLL, + "If non-zero, game will restart once each player gives the ready signal" ); + +// Ready signal +ConVar mp_ready_signal( + "mp_ready_signal", + "ready", + FCVAR_GAMEDLL, + "Text that each player must speak for the match to begin" ); diff --git a/dlls/hl2mp_dll/hl2mp_cvars.h b/dlls/hl2mp_dll/hl2mp_cvars.h index 3602ac60..64d725d8 100644 --- a/dlls/hl2mp_dll/hl2mp_cvars.h +++ b/dlls/hl2mp_dll/hl2mp_cvars.h @@ -1,20 +1,20 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -//=============================================================================// - -#ifndef HL2MP_CVARS_H -#define HL2MP_CVARS_H -#ifdef _WIN32 -#pragma once -#endif - -#define MAX_INTERMISSION_TIME 120 - -extern ConVar mp_restartround; -extern ConVar mp_readyrestart; -extern ConVar mp_ready_signal; - -#endif //HL2MP_CVARS_H \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef HL2MP_CVARS_H +#define HL2MP_CVARS_H +#ifdef _WIN32 +#pragma once +#endif + +#define MAX_INTERMISSION_TIME 120 + +extern ConVar mp_restartround; +extern ConVar mp_readyrestart; +extern ConVar mp_ready_signal; + +#endif //HL2MP_CVARS_H diff --git a/dlls/hl2mp_dll/hl2mp_gameinterface.cpp b/dlls/hl2mp_dll/hl2mp_gameinterface.cpp index 42d12104..d511c5fb 100644 --- a/dlls/hl2mp_dll/hl2mp_gameinterface.cpp +++ b/dlls/hl2mp_dll/hl2mp_gameinterface.cpp @@ -1,64 +1,64 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -//=============================================================================// - -#include "cbase.h" -#include "gameinterface.h" -#include "mapentities.h" -#include "hl2mp_gameinterface.h" - -#include "utllinkedlist.h" - -// memdbgon must be the last include file in a .cpp file!!! -#include "tier0/memdbgon.h" - -CUtlLinkedList g_MapEntityRefs; - -void CServerGameClients::GetPlayerLimits( int& minplayers, int& maxplayers, int &defaultMaxPlayers ) const -{ - minplayers = defaultMaxPlayers = 2; - maxplayers = 16; -} - - -// -------------------------------------------------------------------------------------------- // -// Mod-specific CServerGameDLL implementation. -// -------------------------------------------------------------------------------------------- // - -class CHL2MPMapLoadEntityFilter : public IMapEntityFilter -{ -public: - virtual bool ShouldCreateEntity( const char *pClassname ) - { - // During map load, create all the entities. - return true; - } - - virtual CBaseEntity* CreateNextEntity( const char *pClassname ) - { - CBaseEntity *pRet = CreateEntityByName( pClassname ); - - CMapEntityRef ref; - ref.m_iEdict = -1; - ref.m_iSerialNumber = -1; - - if ( pRet ) - { - ref.m_iEdict = pRet->entindex(); - if ( pRet->edict() ) - ref.m_iSerialNumber = pRet->edict()->m_NetworkSerialNumber; - } - - g_MapEntityRefs.AddToTail( ref ); - return pRet; - } -}; - -void CServerGameDLL::LevelInit_ParseAllEntities( const char *pMapEntities ) -{ - g_MapEntityRefs.Purge(); - CHL2MPMapLoadEntityFilter filter; - MapEntity_ParseAllEntities( pMapEntities, &filter ); -} \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "cbase.h" +#include "gameinterface.h" +#include "mapentities.h" +#include "hl2mp_gameinterface.h" + +#include "utllinkedlist.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +CUtlLinkedList g_MapEntityRefs; + +void CServerGameClients::GetPlayerLimits( int& minplayers, int& maxplayers, int &defaultMaxPlayers ) const +{ + minplayers = defaultMaxPlayers = 2; + maxplayers = 16; +} + + +// -------------------------------------------------------------------------------------------- // +// Mod-specific CServerGameDLL implementation. +// -------------------------------------------------------------------------------------------- // + +class CHL2MPMapLoadEntityFilter : public IMapEntityFilter +{ +public: + virtual bool ShouldCreateEntity( const char *pClassname ) + { + // During map load, create all the entities. + return true; + } + + virtual CBaseEntity* CreateNextEntity( const char *pClassname ) + { + CBaseEntity *pRet = CreateEntityByName( pClassname ); + + CMapEntityRef ref; + ref.m_iEdict = -1; + ref.m_iSerialNumber = -1; + + if ( pRet ) + { + ref.m_iEdict = pRet->entindex(); + if ( pRet->edict() ) + ref.m_iSerialNumber = pRet->edict()->m_NetworkSerialNumber; + } + + g_MapEntityRefs.AddToTail( ref ); + return pRet; + } +}; + +void CServerGameDLL::LevelInit_ParseAllEntities( const char *pMapEntities ) +{ + g_MapEntityRefs.Purge(); + CHL2MPMapLoadEntityFilter filter; + MapEntity_ParseAllEntities( pMapEntities, &filter ); +} diff --git a/dlls/hl2mp_dll/hl2mp_gameinterface.h b/dlls/hl2mp_dll/hl2mp_gameinterface.h index 2648b132..2e1c0b18 100644 --- a/dlls/hl2mp_dll/hl2mp_gameinterface.h +++ b/dlls/hl2mp_dll/hl2mp_gameinterface.h @@ -1,20 +1,20 @@ -#ifndef HL2MP_GAMEINTERFACE_H -#define HL2MP_GAMEINTERFACE_H -#ifdef _WIN32 -#pragma once -#endif - -#include "utllinkedlist.h" - - -// These are created for map entities in order as the map entities are spawned. -class CMapEntityRef -{ -public: - int m_iEdict; // Which edict slot this entity got. -1 if CreateEntityByName failed. - int m_iSerialNumber; // The edict serial number. TODO used anywhere ? -}; - -extern CUtlLinkedList g_MapEntityRefs; - -#endif \ No newline at end of file +#ifndef HL2MP_GAMEINTERFACE_H +#define HL2MP_GAMEINTERFACE_H +#ifdef _WIN32 +#pragma once +#endif + +#include "utllinkedlist.h" + + +// These are created for map entities in order as the map entities are spawned. +class CMapEntityRef +{ +public: + int m_iEdict; // Which edict slot this entity got. -1 if CreateEntityByName failed. + int m_iSerialNumber; // The edict serial number. TODO used anywhere ? +}; + +extern CUtlLinkedList g_MapEntityRefs; + +#endif diff --git a/dlls/hl2mp_dll/hl2mp_player.cpp b/dlls/hl2mp_dll/hl2mp_player.cpp index 8b4fb66f..5b1ad534 100644 --- a/dlls/hl2mp_dll/hl2mp_player.cpp +++ b/dlls/hl2mp_dll/hl2mp_player.cpp @@ -1,1614 +1,1616 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: Player for HL2. -// -//=============================================================================// - -#include "weapon_hl2mpbasehlmpcombatweapon.h" - -#include "cbase.h" -#include "hl2mp_player.h" -#include "globalstate.h" -#include "game.h" -#include "gamerules.h" -#include "hl2mp_player_shared.h" -#include "predicted_viewmodel.h" -#include "in_buttons.h" -#include "hl2mp_gamerules.h" -#include "KeyValues.h" -#include "team.h" -#include "weapon_hl2mpbase.h" -#include "grenade_satchel.h" -#include "eventqueue.h" - -#include "engine/IEngineSound.h" -#include "SoundEmitterSystem/isoundemittersystembase.h" - -#include "ilagcompensationmanager.h" - -int g_iLastCitizenModel = 0; -int g_iLastCombineModel = 0; - -CBaseEntity *g_pLastCombineSpawn = NULL; -CBaseEntity *g_pLastRebelSpawn = NULL; -extern CBaseEntity *g_pLastSpawn; - -#define HL2MP_COMMAND_MAX_RATE 0.3 - -void ClientKill( edict_t *pEdict ); -void DropPrimedFragGrenade( CHL2MP_Player *pPlayer, CBaseCombatWeapon *pGrenade ); - -LINK_ENTITY_TO_CLASS( player, CHL2MP_Player ); - -LINK_ENTITY_TO_CLASS( info_player_combine, CPointEntity ); -LINK_ENTITY_TO_CLASS( info_player_rebel, CPointEntity ); - -IMPLEMENT_SERVERCLASS_ST(CHL2MP_Player, DT_HL2MP_Player) - SendPropAngle( SENDINFO_VECTORELEM(m_angEyeAngles, 0), 11, SPROP_CHANGES_OFTEN ), - SendPropAngle( SENDINFO_VECTORELEM(m_angEyeAngles, 1), 11, SPROP_CHANGES_OFTEN ), - SendPropEHandle( SENDINFO( m_hRagdoll ) ), - SendPropInt( SENDINFO( m_iSpawnInterpCounter), 4 ), - SendPropInt( SENDINFO( m_iPlayerSoundType), 3 ), - - SendPropExclude( "DT_BaseAnimating", "m_flPoseParameter" ), - SendPropExclude( "DT_BaseFlex", "m_viewtarget" ), - -// SendPropExclude( "DT_ServerAnimationData" , "m_flCycle" ), -// SendPropExclude( "DT_AnimTimeMustBeFirst" , "m_flAnimTime" ), - -END_SEND_TABLE() - -BEGIN_DATADESC( CHL2MP_Player ) -END_DATADESC() - -const char *g_ppszRandomCitizenModels[] = -{ - "models/humans/group03/male_01.mdl", - "models/humans/group03/male_02.mdl", - "models/humans/group03/female_01.mdl", - "models/humans/group03/male_03.mdl", - "models/humans/group03/female_02.mdl", - "models/humans/group03/male_04.mdl", - "models/humans/group03/female_03.mdl", - "models/humans/group03/male_05.mdl", - "models/humans/group03/female_04.mdl", - "models/humans/group03/male_06.mdl", - "models/humans/group03/female_06.mdl", - "models/humans/group03/male_07.mdl", - "models/humans/group03/female_07.mdl", - "models/humans/group03/male_08.mdl", - "models/humans/group03/male_09.mdl", -}; - -const char *g_ppszRandomCombineModels[] = -{ - "models/combine_soldier.mdl", - "models/combine_soldier_prisonguard.mdl", - "models/combine_super_soldier.mdl", - "models/police.mdl", -}; - - -#define MAX_COMBINE_MODELS 4 -#define MODEL_CHANGE_INTERVAL 5.0f -#define TEAM_CHANGE_INTERVAL 5.0f - -#define HL2MPPLAYER_PHYSDAMAGE_SCALE 4.0f - -#pragma warning( disable : 4355 ) - -CHL2MP_Player::CHL2MP_Player() : m_PlayerAnimState( this ) -{ - m_angEyeAngles.Init(); - - m_iLastWeaponFireUsercmd = 0; - - m_flNextModelChangeTime = 0.0f; - m_flNextTeamChangeTime = 0.0f; - - m_iSpawnInterpCounter = 0; - - m_bEnterObserver = false; - m_bReady = false; - - BaseClass::ChangeTeam( 0 ); - -// UseClientSideAnimation(); -} - -CHL2MP_Player::~CHL2MP_Player( void ) -{ - -} - -void CHL2MP_Player::UpdateOnRemove( void ) -{ - if ( m_hRagdoll ) - { - UTIL_RemoveImmediate( m_hRagdoll ); - m_hRagdoll = NULL; - } - - BaseClass::UpdateOnRemove(); -} - -void CHL2MP_Player::Precache( void ) -{ - BaseClass::Precache(); - - PrecacheModel ( "sprites/glow01.vmt" ); - - //Precache Citizen models - int nHeads = ARRAYSIZE( g_ppszRandomCitizenModels ); - int i; - - for ( i = 0; i < nHeads; ++i ) - PrecacheModel( g_ppszRandomCitizenModels[i] ); - - //Precache Combine Models - nHeads = ARRAYSIZE( g_ppszRandomCombineModels ); - - for ( i = 0; i < nHeads; ++i ) - PrecacheModel( g_ppszRandomCombineModels[i] ); - - PrecacheFootStepSounds(); - - PrecacheScriptSound( "NPC_MetroPolice.Die" ); - PrecacheScriptSound( "NPC_CombineS.Die" ); - PrecacheScriptSound( "NPC_Citizen.die" ); -} - -void CHL2MP_Player::GiveAllItems( void ) -{ - EquipSuit(); - - CBasePlayer::GiveAmmo( 255, "Pistol"); - CBasePlayer::GiveAmmo( 255, "AR2" ); - CBasePlayer::GiveAmmo( 5, "AR2AltFire" ); - CBasePlayer::GiveAmmo( 255, "SMG1"); - CBasePlayer::GiveAmmo( 1, "smg1_grenade"); - CBasePlayer::GiveAmmo( 255, "Buckshot"); - CBasePlayer::GiveAmmo( 32, "357" ); - CBasePlayer::GiveAmmo( 3, "rpg_round"); - - CBasePlayer::GiveAmmo( 1, "grenade" ); - CBasePlayer::GiveAmmo( 2, "slam" ); - - GiveNamedItem( "weapon_crowbar" ); - GiveNamedItem( "weapon_stunstick" ); - GiveNamedItem( "weapon_pistol" ); - GiveNamedItem( "weapon_357" ); - - GiveNamedItem( "weapon_smg1" ); - GiveNamedItem( "weapon_ar2" ); - - GiveNamedItem( "weapon_shotgun" ); - GiveNamedItem( "weapon_frag" ); - - GiveNamedItem( "weapon_crossbow" ); - - GiveNamedItem( "weapon_rpg" ); - - GiveNamedItem( "weapon_slam" ); - - GiveNamedItem( "weapon_physcannon" ); - -} - -void CHL2MP_Player::GiveDefaultItems( void ) -{ - EquipSuit(); - - CBasePlayer::GiveAmmo( 255, "Pistol"); - CBasePlayer::GiveAmmo( 45, "SMG1"); - CBasePlayer::GiveAmmo( 1, "grenade" ); - CBasePlayer::GiveAmmo( 6, "Buckshot"); - CBasePlayer::GiveAmmo( 6, "357" ); - - if ( GetPlayerModelType() == PLAYER_SOUNDS_METROPOLICE || GetPlayerModelType() == PLAYER_SOUNDS_COMBINESOLDIER ) - { - GiveNamedItem( "weapon_stunstick" ); - } - else if ( GetPlayerModelType() == PLAYER_SOUNDS_CITIZEN ) - { - GiveNamedItem( "weapon_crowbar" ); - } - - GiveNamedItem( "weapon_pistol" ); - GiveNamedItem( "weapon_smg1" ); - GiveNamedItem( "weapon_frag" ); - GiveNamedItem( "weapon_physcannon" ); - - const char *szDefaultWeaponName = engine->GetClientConVarValue( engine->IndexOfEdict( edict() ), "cl_defaultweapon" ); - - CBaseCombatWeapon *pDefaultWeapon = Weapon_OwnsThisType( szDefaultWeaponName ); - - if ( pDefaultWeapon ) - { - Weapon_Switch( pDefaultWeapon ); - } - else - { - Weapon_Switch( Weapon_OwnsThisType( "weapon_physcannon" ) ); - } -} - -void CHL2MP_Player::PickDefaultSpawnTeam( void ) -{ - if ( GetTeamNumber() == 0 ) - { - if ( HL2MPRules()->IsTeamplay() == false ) - { - if ( GetModelPtr() == NULL ) - { - const char *szModelName = NULL; - szModelName = engine->GetClientConVarValue( engine->IndexOfEdict( edict() ), "cl_playermodel" ); - - if ( ValidatePlayerModel( szModelName ) == false ) - { - char szReturnString[512]; - - Q_snprintf( szReturnString, sizeof (szReturnString ), "cl_playermodel models/combine_soldier.mdl\n" ); - engine->ClientCommand ( edict(), szReturnString ); - } - - ChangeTeam( TEAM_UNASSIGNED ); - } - } - else - { - CTeam *pCombine = g_Teams[TEAM_COMBINE]; - CTeam *pRebels = g_Teams[TEAM_REBELS]; - - if ( pCombine == NULL || pRebels == NULL ) - { - ChangeTeam( random->RandomInt( TEAM_COMBINE, TEAM_REBELS ) ); - } - else - { - if ( pCombine->GetNumPlayers() > pRebels->GetNumPlayers() ) - { - ChangeTeam( TEAM_REBELS ); - } - else if ( pCombine->GetNumPlayers() < pRebels->GetNumPlayers() ) - { - ChangeTeam( TEAM_COMBINE ); - } - else - { - ChangeTeam( random->RandomInt( TEAM_COMBINE, TEAM_REBELS ) ); - } - } - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: Sets HL2 specific defaults. -//----------------------------------------------------------------------------- -void CHL2MP_Player::Spawn(void) -{ - m_flNextModelChangeTime = 0.0f; - m_flNextTeamChangeTime = 0.0f; - - PickDefaultSpawnTeam(); - - BaseClass::Spawn(); - - if ( !IsObserver() ) - { - pl.deadflag = false; - RemoveSolidFlags( FSOLID_NOT_SOLID ); - - RemoveEffects( EF_NODRAW ); - - GiveDefaultItems(); - } - - RemoveEffects( EF_NOINTERP ); - - SetNumAnimOverlays( 3 ); - ResetAnimation(); - - m_nRenderFX = kRenderNormal; - - m_Local.m_iHideHUD = 0; - - AddFlag(FL_ONGROUND); // set the player on the ground at the start of the round. - - m_impactEnergyScale = HL2MPPLAYER_PHYSDAMAGE_SCALE; - - if ( HL2MPRules()->IsIntermission() ) - { - AddFlag( FL_FROZEN ); - } - else - { - RemoveFlag( FL_FROZEN ); - } - - m_iSpawnInterpCounter = (m_iSpawnInterpCounter + 1) % 8; - - m_Local.m_bDucked = false; - - SetPlayerUnderwater(false); - - m_bReady = false; -} - -void CHL2MP_Player::PickupObject( CBaseEntity *pObject, bool bLimitMassAndSize ) -{ - -} - -bool CHL2MP_Player::ValidatePlayerModel( const char *pModel ) -{ - int iModels = ARRAYSIZE( g_ppszRandomCitizenModels ); - int i; - - for ( i = 0; i < iModels; ++i ) - { - if ( !Q_stricmp( g_ppszRandomCitizenModels[i], pModel ) ) - { - return true; - } - } - - iModels = ARRAYSIZE( g_ppszRandomCombineModels ); - - for ( i = 0; i < iModels; ++i ) - { - if ( !Q_stricmp( g_ppszRandomCombineModels[i], pModel ) ) - { - return true; - } - } - - return false; -} - -void CHL2MP_Player::SetPlayerTeamModel( void ) -{ - const char *szModelName = NULL; - szModelName = engine->GetClientConVarValue( engine->IndexOfEdict( edict() ), "cl_playermodel" ); - - int modelIndex = modelinfo->GetModelIndex( szModelName ); - - if ( modelIndex == -1 || ValidatePlayerModel( szModelName ) == false ) - { - szModelName = "models/Combine_Soldier.mdl"; - m_iModelType = TEAM_COMBINE; - - char szReturnString[512]; - - Q_snprintf( szReturnString, sizeof (szReturnString ), "cl_playermodel %s\n", szModelName ); - engine->ClientCommand ( edict(), szReturnString ); - } - - if ( GetTeamNumber() == TEAM_COMBINE ) - { - if ( Q_stristr( szModelName, "models/human") ) - { - int nHeads = ARRAYSIZE( g_ppszRandomCombineModels ); - - g_iLastCombineModel = ( g_iLastCombineModel + 1 ) % nHeads; - szModelName = g_ppszRandomCombineModels[g_iLastCombineModel]; - } - - m_iModelType = TEAM_COMBINE; - } - else if ( GetTeamNumber() == TEAM_REBELS ) - { - if ( !Q_stristr( szModelName, "models/human") ) - { - int nHeads = ARRAYSIZE( g_ppszRandomCitizenModels ); - - g_iLastCitizenModel = ( g_iLastCitizenModel + 1 ) % nHeads; - szModelName = g_ppszRandomCitizenModels[g_iLastCitizenModel]; - } - - m_iModelType = TEAM_REBELS; - } - - SetModel( szModelName ); - SetupPlayerSoundsByModel( szModelName ); - - m_flNextModelChangeTime = gpGlobals->curtime + MODEL_CHANGE_INTERVAL; -} - -void CHL2MP_Player::SetPlayerModel( void ) -{ - const char *szModelName = NULL; - const char *pszCurrentModelName = modelinfo->GetModelName( GetModel()); - - szModelName = engine->GetClientConVarValue( engine->IndexOfEdict( edict() ), "cl_playermodel" ); - - if ( ValidatePlayerModel( szModelName ) == false ) - { - char szReturnString[512]; - - if ( ValidatePlayerModel( pszCurrentModelName ) == false ) - { - pszCurrentModelName = "models/Combine_Soldier.mdl"; - } - - Q_snprintf( szReturnString, sizeof (szReturnString ), "cl_playermodel %s\n", pszCurrentModelName ); - engine->ClientCommand ( edict(), szReturnString ); - - szModelName = pszCurrentModelName; - } - - if ( GetTeamNumber() == TEAM_COMBINE ) - { - int nHeads = ARRAYSIZE( g_ppszRandomCombineModels ); - - g_iLastCombineModel = ( g_iLastCombineModel + 1 ) % nHeads; - szModelName = g_ppszRandomCombineModels[g_iLastCombineModel]; - - m_iModelType = TEAM_COMBINE; - } - else if ( GetTeamNumber() == TEAM_REBELS ) - { - int nHeads = ARRAYSIZE( g_ppszRandomCitizenModels ); - - g_iLastCitizenModel = ( g_iLastCitizenModel + 1 ) % nHeads; - szModelName = g_ppszRandomCitizenModels[g_iLastCitizenModel]; - - m_iModelType = TEAM_REBELS; - } - else - { - if ( Q_strlen( szModelName ) == 0 ) - { - szModelName = g_ppszRandomCitizenModels[0]; - } - - if ( Q_stristr( szModelName, "models/human") ) - { - m_iModelType = TEAM_REBELS; - } - else - { - m_iModelType = TEAM_COMBINE; - } - } - - int modelIndex = modelinfo->GetModelIndex( szModelName ); - - if ( modelIndex == -1 ) - { - szModelName = "models/Combine_Soldier.mdl"; - m_iModelType = TEAM_COMBINE; - - char szReturnString[512]; - - Q_snprintf( szReturnString, sizeof (szReturnString ), "cl_playermodel %s\n", szModelName ); - engine->ClientCommand ( edict(), szReturnString ); - } - - SetModel( szModelName ); - SetupPlayerSoundsByModel( szModelName ); - - m_flNextModelChangeTime = gpGlobals->curtime + MODEL_CHANGE_INTERVAL; -} - -void CHL2MP_Player::SetupPlayerSoundsByModel( const char *pModelName ) -{ - if ( Q_stristr( pModelName, "models/human") ) - { - m_iPlayerSoundType = (int)PLAYER_SOUNDS_CITIZEN; - } - else if ( Q_stristr(pModelName, "police" ) ) - { - m_iPlayerSoundType = (int)PLAYER_SOUNDS_METROPOLICE; - } - else if ( Q_stristr(pModelName, "combine" ) ) - { - m_iPlayerSoundType = (int)PLAYER_SOUNDS_COMBINESOLDIER; - } -} - -void CHL2MP_Player::ResetAnimation( void ) -{ - if ( IsAlive() ) - { - SetSequence ( -1 ); - SetActivity( ACT_INVALID ); - - if (!GetAbsVelocity().x && !GetAbsVelocity().y) - SetAnimation( PLAYER_IDLE ); - else if ((GetAbsVelocity().x || GetAbsVelocity().y) && ( GetFlags() & FL_ONGROUND )) - SetAnimation( PLAYER_WALK ); - else if (GetWaterLevel() > 1) - SetAnimation( PLAYER_WALK ); - } -} - - -bool CHL2MP_Player::Weapon_Switch( CBaseCombatWeapon *pWeapon, int viewmodelindex ) -{ - bool bRet = BaseClass::Weapon_Switch( pWeapon, viewmodelindex ); - - if ( bRet == true ) - { - ResetAnimation(); - } - - return bRet; -} - -void CHL2MP_Player::PreThink( void ) -{ - QAngle vOldAngles = GetLocalAngles(); - QAngle vTempAngles = GetLocalAngles(); - - vTempAngles = EyeAngles(); - - if ( vTempAngles[PITCH] > 180.0f ) - { - vTempAngles[PITCH] -= 360.0f; - } - - SetLocalAngles( vTempAngles ); - - BaseClass::PreThink(); - State_PreThink(); - - //Reset bullet force accumulator, only lasts one frame - m_vecTotalBulletForce = vec3_origin; - SetLocalAngles( vOldAngles ); -} - -void CHL2MP_Player::PostThink( void ) -{ - BaseClass::PostThink(); - - if ( GetFlags() & FL_DUCKING ) - { - SetCollisionBounds( VEC_CROUCH_TRACE_MIN, VEC_CROUCH_TRACE_MAX ); - } - - m_PlayerAnimState.Update(); - - // Store the eye angles pitch so the client can compute its animation state correctly. - m_angEyeAngles = EyeAngles(); - - QAngle angles = GetLocalAngles(); - angles[PITCH] = 0; - SetLocalAngles( angles ); -} - -void CHL2MP_Player::PlayerDeathThink() -{ - if( !IsObserver() ) - { - BaseClass::PlayerDeathThink(); - } -} - -void CHL2MP_Player::FireBullets ( const FireBulletsInfo_t &info ) -{ - // Move other players back to history positions based on local player's lag - lagcompensation->StartLagCompensation( this, this->GetCurrentCommand() ); - - FireBulletsInfo_t modinfo = info; - - CWeaponHL2MPBase *pWeapon = dynamic_cast( GetActiveWeapon() ); - - if ( pWeapon ) - { - modinfo.m_iPlayerDamage = modinfo.m_iDamage = pWeapon->GetHL2MPWpnData().m_iPlayerDamage; - } - - NoteWeaponFired(); - - BaseClass::FireBullets( modinfo ); - - // Move other players back to history positions based on local player's lag - lagcompensation->FinishLagCompensation( this ); -} - -void CHL2MP_Player::NoteWeaponFired( void ) -{ - Assert( m_pCurrentCommand ); - if( m_pCurrentCommand ) - { - m_iLastWeaponFireUsercmd = m_pCurrentCommand->command_number; - } -} - -extern ConVar sv_maxunlag; - -bool CHL2MP_Player::WantsLagCompensationOnEntity( const CBasePlayer *pPlayer, const CUserCmd *pCmd, const CBitVec *pEntityTransmitBits ) const -{ - // No need to lag compensate at all if we're not attacking in this command and - // we haven't attacked recently. - if ( !( pCmd->buttons & IN_ATTACK ) && (pCmd->command_number - m_iLastWeaponFireUsercmd > 5) ) - return false; - - // If this entity hasn't been transmitted to us and acked, then don't bother lag compensating it. - if ( pEntityTransmitBits && !pEntityTransmitBits->Get( pPlayer->entindex() ) ) - return false; - - const Vector &vMyOrigin = GetAbsOrigin(); - const Vector &vHisOrigin = pPlayer->GetAbsOrigin(); - - // get max distance player could have moved within max lag compensation time, - // multiply by 1.5 to to avoid "dead zones" (sqrt(2) would be the exact value) - float maxDistance = 1.5 * pPlayer->MaxSpeed() * sv_maxunlag.GetFloat(); - - // If the player is within this distance, lag compensate them in case they're running past us. - if ( vHisOrigin.DistTo( vMyOrigin ) < maxDistance ) - return true; - - // If their origin is not within a 45 degree cone in front of us, no need to lag compensate. - Vector vForward; - AngleVectors( pCmd->viewangles, &vForward ); - - Vector vDiff = vHisOrigin - vMyOrigin; - VectorNormalize( vDiff ); - - float flCosAngle = 0.707107f; // 45 degree angle - if ( vForward.Dot( vDiff ) < flCosAngle ) - return false; - - return true; -} - -Activity CHL2MP_Player::TranslateTeamActivity( Activity ActToTranslate ) -{ - if ( m_iModelType == TEAM_COMBINE ) - return ActToTranslate; - - if ( ActToTranslate == ACT_RUN ) - return ACT_RUN_AIM_AGITATED; - - if ( ActToTranslate == ACT_IDLE ) - return ACT_IDLE_AIM_AGITATED; - - if ( ActToTranslate == ACT_WALK ) - return ACT_WALK_AIM_AGITATED; - - return ActToTranslate; -} - -extern ConVar hl2_normspeed; - -// Set the activity based on an event or current state -void CHL2MP_Player::SetAnimation( PLAYER_ANIM playerAnim ) -{ - int animDesired; - - float speed; - - speed = GetAbsVelocity().Length2D(); - - - // bool bRunning = true; - - //Revisit! -/* if ( ( m_nButtons & ( IN_FORWARD | IN_BACK | IN_MOVELEFT | IN_MOVERIGHT ) ) ) - { - if ( speed > 1.0f && speed < hl2_normspeed.GetFloat() - 20.0f ) - { - bRunning = false; - } - }*/ - - if ( GetFlags() & ( FL_FROZEN | FL_ATCONTROLS ) ) - { - speed = 0; - playerAnim = PLAYER_IDLE; - } - - Activity idealActivity = ACT_HL2MP_RUN; - - // This could stand to be redone. Why is playerAnim abstracted from activity? (sjb) - if ( playerAnim == PLAYER_JUMP ) - { - idealActivity = ACT_HL2MP_JUMP; - } - else if ( playerAnim == PLAYER_DIE ) - { - if ( m_lifeState == LIFE_ALIVE ) - { - return; - } - } - else if ( playerAnim == PLAYER_ATTACK1 ) - { - if ( GetActivity( ) == ACT_HOVER || - GetActivity( ) == ACT_SWIM || - GetActivity( ) == ACT_HOP || - GetActivity( ) == ACT_LEAP || - GetActivity( ) == ACT_DIESIMPLE ) - { - idealActivity = GetActivity( ); - } - else - { - idealActivity = ACT_HL2MP_GESTURE_RANGE_ATTACK; - } - } - else if ( playerAnim == PLAYER_RELOAD ) - { - idealActivity = ACT_HL2MP_GESTURE_RELOAD; - } - else if ( playerAnim == PLAYER_IDLE || playerAnim == PLAYER_WALK ) - { - if ( !( GetFlags() & FL_ONGROUND ) && GetActivity( ) == ACT_HL2MP_JUMP ) // Still jumping - { - idealActivity = GetActivity( ); - } - /* - else if ( GetWaterLevel() > 1 ) - { - if ( speed == 0 ) - idealActivity = ACT_HOVER; - else - idealActivity = ACT_SWIM; - } - */ - else - { - if ( GetFlags() & FL_DUCKING ) - { - if ( speed > 0 ) - { - idealActivity = ACT_HL2MP_WALK_CROUCH; - } - else - { - idealActivity = ACT_HL2MP_IDLE_CROUCH; - } - } - else - { - if ( speed > 0 ) - { - /* - if ( bRunning == false ) - { - idealActivity = ACT_WALK; - } - else - */ - { - idealActivity = ACT_HL2MP_RUN; - } - } - else - { - idealActivity = ACT_HL2MP_IDLE; - } - } - } - - idealActivity = TranslateTeamActivity( idealActivity ); - } - - if ( idealActivity == ACT_HL2MP_GESTURE_RANGE_ATTACK ) - { - RestartGesture( Weapon_TranslateActivity( idealActivity ) ); - - // FIXME: this seems a bit wacked - Weapon_SetActivity( Weapon_TranslateActivity( ACT_RANGE_ATTACK1 ), 0 ); - - return; - } - else if ( idealActivity == ACT_HL2MP_GESTURE_RELOAD ) - { - RestartGesture( Weapon_TranslateActivity( idealActivity ) ); - return; - } - else - { - SetActivity( idealActivity ); - - animDesired = SelectWeightedSequence( Weapon_TranslateActivity ( idealActivity ) ); - - if (animDesired == -1) - { - animDesired = SelectWeightedSequence( idealActivity ); - - if ( animDesired == -1 ) - { - animDesired = 0; - } - } - - // Already using the desired animation? - if ( GetSequence() == animDesired ) - return; - - m_flPlaybackRate = 1.0; - ResetSequence( animDesired ); - SetCycle( 0 ); - return; - } - - // Already using the desired animation? - if ( GetSequence() == animDesired ) - return; - - //Msg( "Set animation to %d\n", animDesired ); - // Reset to first frame of desired animation - ResetSequence( animDesired ); - SetCycle( 0 ); -} - - -extern int gEvilImpulse101; -//----------------------------------------------------------------------------- -// Purpose: Player reacts to bumping a weapon. -// Input : pWeapon - the weapon that the player bumped into. -// Output : Returns true if player picked up the weapon -//----------------------------------------------------------------------------- -bool CHL2MP_Player::BumpWeapon( CBaseCombatWeapon *pWeapon ) -{ - CBaseCombatCharacter *pOwner = pWeapon->GetOwner(); - - // Can I have this weapon type? - if ( IsEFlagSet( EFL_NO_WEAPON_PICKUP ) ) - return false; - - if ( pOwner || !Weapon_CanUse( pWeapon ) || !g_pGameRules->CanHavePlayerItem( this, pWeapon ) ) - { - if ( gEvilImpulse101 ) - { - UTIL_Remove( pWeapon ); - } - return false; - } - - // Don't let the player fetch weapons through walls (use MASK_SOLID so that you can't pickup through windows) - if( !pWeapon->FVisible( this, MASK_SOLID ) && !(GetFlags() & FL_NOTARGET) ) - { - return false; - } - - bool bOwnsWeaponAlready = !!Weapon_OwnsThisType( pWeapon->GetClassname(), pWeapon->GetSubType()); - - if ( bOwnsWeaponAlready == true ) - { - //If we have room for the ammo, then "take" the weapon too. - if ( Weapon_EquipAmmoOnly( pWeapon ) ) - { - pWeapon->CheckRespawn(); - - UTIL_Remove( pWeapon ); - return true; - } - else - { - return false; - } - } - - pWeapon->CheckRespawn(); - Weapon_Equip( pWeapon ); - - return true; -} - -void CHL2MP_Player::ChangeTeam( int iTeam ) -{ -/* if ( GetNextTeamChangeTime() >= gpGlobals->curtime ) - { - char szReturnString[128]; - Q_snprintf( szReturnString, sizeof( szReturnString ), "Please wait %d more seconds before trying to switch teams again.\n", (int)(GetNextTeamChangeTime() - gpGlobals->curtime) ); - - ClientPrint( this, HUD_PRINTTALK, szReturnString ); - return; - }*/ - - bool bKill = false; - - if ( HL2MPRules()->IsTeamplay() != true && iTeam != TEAM_SPECTATOR ) - { - //don't let them try to join combine or rebels during deathmatch. - iTeam = TEAM_UNASSIGNED; - } - - if ( HL2MPRules()->IsTeamplay() == true ) - { - if ( iTeam != GetTeamNumber() && GetTeamNumber() != TEAM_UNASSIGNED ) - { - bKill = true; - } - } - - BaseClass::ChangeTeam( iTeam ); - - m_flNextTeamChangeTime = gpGlobals->curtime + TEAM_CHANGE_INTERVAL; - - if ( HL2MPRules()->IsTeamplay() == true ) - { - SetPlayerTeamModel(); - } - else - { - SetPlayerModel(); - } - - if ( iTeam == TEAM_SPECTATOR ) - { - RemoveAllItems( true ); - - State_Transition( STATE_OBSERVER_MODE ); - } - - if ( bKill == true ) - { - ClientKill( edict() ); - } -} - -bool CHL2MP_Player::HandleCommand_JoinTeam( int team ) -{ - if ( !GetGlobalTeam( team ) ) - { - Warning( "HandleCommand_JoinTeam( %d ) - invalid team index.\n", team ); - return false; - } - - if ( team == TEAM_SPECTATOR ) - { - // Prevent this is the cvar is set - if ( !mp_allowspectators.GetInt() ) - { - ClientPrint( this, HUD_PRINTCENTER, "#Cannot_Be_Spectator" ); - return false; - } - - if ( GetTeamNumber() != TEAM_UNASSIGNED && !IsDead() ) - { - m_fNextSuicideTime = gpGlobals->curtime; // allow the suicide to work - - ClientKill( edict() ); - - // add 1 to frags to balance out the 1 subtracted for killing yourself - IncrementFragCount( 1 ); - } - - ChangeTeam( TEAM_SPECTATOR ); - - return true; - } - else - { - StopObserverMode(); - State_Transition(STATE_ACTIVE); - } - - // Switch their actual team... - ChangeTeam( team ); - - return true; -} - -bool CHL2MP_Player::ClientCommand( const char *pcmd ) -{ - if ( FStrEq( pcmd, "spectate" ) ) - { - if ( ShouldRunRateLimitedCommand( pcmd ) ) - { - // instantly join spectators - HandleCommand_JoinTeam( TEAM_SPECTATOR ); - } - return true; - } - else if ( FStrEq( pcmd, "jointeam" ) ) - { - if ( engine->Cmd_Argc() < 2 ) - { - Warning( "Player sent bad jointeam syntax\n" ); - } - - if ( ShouldRunRateLimitedCommand( pcmd ) ) - { - int iTeam = atoi( engine->Cmd_Argv(1) ); - HandleCommand_JoinTeam( iTeam ); - } - return true; - } - else if ( FStrEq( pcmd, "joingame" ) ) - { - - return true; - } - - return BaseClass::ClientCommand( pcmd ); -} - -void CHL2MP_Player::CheatImpulseCommands( int iImpulse ) -{ - switch ( iImpulse ) - { - case 101: - { - if( sv_cheats->GetBool() ) - { - GiveAllItems(); - } - } - break; - - default: - BaseClass::CheatImpulseCommands( iImpulse ); - } -} - -bool CHL2MP_Player::ShouldRunRateLimitedCommand( const char *pcmd ) -{ - int i = m_RateLimitLastCommandTimes.Find( pcmd ); - if ( i == m_RateLimitLastCommandTimes.InvalidIndex() ) - { - m_RateLimitLastCommandTimes.Insert( pcmd, gpGlobals->curtime ); - return true; - } - else if ( (gpGlobals->curtime - m_RateLimitLastCommandTimes[i]) < HL2MP_COMMAND_MAX_RATE ) - { - // Too fast. - return false; - } - else - { - m_RateLimitLastCommandTimes[i] = gpGlobals->curtime; - return true; - } -} - -void CHL2MP_Player::CreateViewModel( int index /*=0*/ ) -{ - Assert( index >= 0 && index < MAX_VIEWMODELS ); - - if ( GetViewModel( index ) ) - return; - - CPredictedViewModel *vm = ( CPredictedViewModel * )CreateEntityByName( "predicted_viewmodel" ); - if ( vm ) - { - vm->SetAbsOrigin( GetAbsOrigin() ); - vm->SetOwner( this ); - vm->SetIndex( index ); - DispatchSpawn( vm ); - vm->FollowEntity( this, false ); - m_hViewModel.Set( index, vm ); - } -} - -bool CHL2MP_Player::BecomeRagdollOnClient( const Vector &force ) -{ - return true; -} - -// -------------------------------------------------------------------------------- // -// Ragdoll entities. -// -------------------------------------------------------------------------------- // - -class CHL2MPRagdoll : public CBaseAnimatingOverlay -{ -public: - DECLARE_CLASS( CHL2MPRagdoll, CBaseAnimatingOverlay ); - DECLARE_SERVERCLASS(); - - // Transmit ragdolls to everyone. - virtual int UpdateTransmitState() - { - return SetTransmitState( FL_EDICT_ALWAYS ); - } - -public: - // In case the client has the player entity, we transmit the player index. - // In case the client doesn't have it, we transmit the player's model index, origin, and angles - // so they can create a ragdoll in the right place. - CNetworkHandle( CBaseEntity, m_hPlayer ); // networked entity handle - CNetworkVector( m_vecRagdollVelocity ); - CNetworkVector( m_vecRagdollOrigin ); -}; - -LINK_ENTITY_TO_CLASS( hl2mp_ragdoll, CHL2MPRagdoll ); - -IMPLEMENT_SERVERCLASS_ST_NOBASE( CHL2MPRagdoll, DT_HL2MPRagdoll ) - SendPropVector( SENDINFO(m_vecRagdollOrigin), -1, SPROP_COORD ), - SendPropEHandle( SENDINFO( m_hPlayer ) ), - SendPropModelIndex( SENDINFO( m_nModelIndex ) ), - SendPropInt ( SENDINFO(m_nForceBone), 8, 0 ), - SendPropVector ( SENDINFO(m_vecForce), -1, SPROP_NOSCALE ), - SendPropVector( SENDINFO( m_vecRagdollVelocity ) ) -END_SEND_TABLE() - - -void CHL2MP_Player::CreateRagdollEntity( void ) -{ - if ( m_hRagdoll ) - { - UTIL_RemoveImmediate( m_hRagdoll ); - m_hRagdoll = NULL; - } - - // If we already have a ragdoll, don't make another one. - CHL2MPRagdoll *pRagdoll = dynamic_cast< CHL2MPRagdoll* >( m_hRagdoll.Get() ); - - if ( !pRagdoll ) - { - // create a new one - pRagdoll = dynamic_cast< CHL2MPRagdoll* >( CreateEntityByName( "hl2mp_ragdoll" ) ); - } - - if ( pRagdoll ) - { - pRagdoll->m_hPlayer = this; - pRagdoll->m_vecRagdollOrigin = GetAbsOrigin(); - pRagdoll->m_vecRagdollVelocity = GetAbsVelocity(); - pRagdoll->m_nModelIndex = m_nModelIndex; - pRagdoll->m_nForceBone = m_nForceBone; - pRagdoll->m_vecForce = m_vecTotalBulletForce; - pRagdoll->SetAbsOrigin( GetAbsOrigin() ); - } - - // ragdolls will be removed on round restart automatically - m_hRagdoll = pRagdoll; -} - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -int CHL2MP_Player::FlashlightIsOn( void ) -{ - return IsEffectActive( EF_DIMLIGHT ); -} - -extern ConVar flashlight; - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -void CHL2MP_Player::FlashlightTurnOn( void ) -{ - if( flashlight.GetInt() > 0 && IsAlive() ) - { - AddEffects( EF_DIMLIGHT ); - EmitSound( "HL2Player.FlashlightOn" ); - } -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -void CHL2MP_Player::FlashlightTurnOff( void ) -{ - RemoveEffects( EF_DIMLIGHT ); - - if( IsAlive() ) - { - EmitSound( "HL2Player.FlashlightOff" ); - } -} - -void CHL2MP_Player::Weapon_Drop( CBaseCombatWeapon *pWeapon, const Vector *pvecTarget, const Vector *pVelocity ) -{ - //Drop a grenade if it's primed. - if ( GetActiveWeapon() ) - { - CBaseCombatWeapon *pGrenade = Weapon_OwnsThisType("weapon_frag"); - - if ( GetActiveWeapon() == pGrenade ) - { - if ( ( m_nButtons & IN_ATTACK ) || (m_nButtons & IN_ATTACK2) ) - { - DropPrimedFragGrenade( this, pGrenade ); - return; - } - } - } - - BaseClass::Weapon_Drop( pWeapon, pvecTarget, pVelocity ); -} - - -void CHL2MP_Player::DetonateTripmines( void ) -{ - CBaseEntity *pEntity = NULL; - - while ((pEntity = gEntList.FindEntityByClassname( pEntity, "npc_satchel" )) != NULL) - { - CSatchelCharge *pSatchel = dynamic_cast(pEntity); - if (pSatchel->m_bIsLive && pSatchel->GetThrower() == this ) - { - g_EventQueue.AddEvent( pSatchel, "Explode", 0.20, this, this ); - } - } - - // Play sound for pressing the detonator - EmitSound( "Weapon_SLAM.SatchelDetonate" ); -} - -void CHL2MP_Player::Event_Killed( const CTakeDamageInfo &info ) -{ - //update damage info with our accumulated physics force - CTakeDamageInfo subinfo = info; - subinfo.SetDamageForce( m_vecTotalBulletForce ); - - SetNumAnimOverlays( 0 ); - - // Note: since we're dead, it won't draw us on the client, but we don't set EF_NODRAW - // because we still want to transmit to the clients in our PVS. - CreateRagdollEntity(); - - DetonateTripmines(); - - BaseClass::Event_Killed( subinfo ); - - if ( info.GetDamageType() & DMG_DISSOLVE ) - { - if ( m_hRagdoll ) - { - m_hRagdoll->GetBaseAnimating()->Dissolve( NULL, gpGlobals->curtime, false, ENTITY_DISSOLVE_NORMAL ); - } - } - - CBaseEntity *pAttacker = info.GetAttacker(); - - if ( pAttacker ) - { - int iScoreToAdd = 1; - - if ( pAttacker == this ) - { - iScoreToAdd = -1; - } - - GetGlobalTeam( pAttacker->GetTeamNumber() )->AddScore( iScoreToAdd ); - } - - FlashlightTurnOff(); - - m_lifeState = LIFE_DEAD; - - RemoveEffects( EF_NODRAW ); // still draw player body - StopZooming(); -} - -int CHL2MP_Player::OnTakeDamage( const CTakeDamageInfo &inputInfo ) -{ - //return here if the player is in the respawn grace period vs. slams. - if ( gpGlobals->curtime < m_flSlamProtectTime && (inputInfo.GetDamageType() == DMG_BLAST ) ) - return 0; - - m_vecTotalBulletForce += inputInfo.GetDamageForce(); - - return BaseClass::OnTakeDamage( inputInfo ); -} - -void CHL2MP_Player::DeathSound( const CTakeDamageInfo &info ) -{ - if ( m_hRagdoll && m_hRagdoll->GetBaseAnimating()->IsDissolving() ) - return; - - char szStepSound[128]; - - Q_snprintf( szStepSound, sizeof( szStepSound ), "%s.Die", GetPlayerModelSoundPrefix() ); - - const char *pModelName = STRING( GetModelName() ); - - CSoundParameters params; - if ( GetParametersForSound( szStepSound, params, pModelName ) == false ) - return; - - Vector vecOrigin = GetAbsOrigin(); - - CRecipientFilter filter; - filter.AddRecipientsByPAS( vecOrigin ); - - EmitSound_t ep; - ep.m_nChannel = params.channel; - ep.m_pSoundName = params.soundname; - ep.m_flVolume = params.volume; - ep.m_SoundLevel = params.soundlevel; - ep.m_nFlags = 0; - ep.m_nPitch = params.pitch; - ep.m_pOrigin = &vecOrigin; - - EmitSound( filter, entindex(), ep ); -} - -CBaseEntity* CHL2MP_Player::EntSelectSpawnPoint( void ) -{ - CBaseEntity *pSpot = NULL; - CBaseEntity *pLastSpawnPoint = g_pLastSpawn; - edict_t *player = edict(); - const char *pSpawnpointName = "info_player_deathmatch"; - - if ( HL2MPRules()->IsTeamplay() == true ) - { - if ( GetTeamNumber() == TEAM_COMBINE ) - { - pSpawnpointName = "info_player_combine"; - pLastSpawnPoint = g_pLastCombineSpawn; - } - else if ( GetTeamNumber() == TEAM_REBELS ) - { - pSpawnpointName = "info_player_rebel"; - pLastSpawnPoint = g_pLastRebelSpawn; - } - - if ( gEntList.FindEntityByClassname( NULL, pSpawnpointName ) == NULL ) - { - pSpawnpointName = "info_player_deathmatch"; - pLastSpawnPoint = g_pLastSpawn; - } - } - - pSpot = pLastSpawnPoint; - // Randomize the start spot - for ( int i = random->RandomInt(1,5); i > 0; i-- ) - pSpot = gEntList.FindEntityByClassname( pSpot, pSpawnpointName ); - if ( !pSpot ) // skip over the null point - pSpot = gEntList.FindEntityByClassname( pSpot, pSpawnpointName ); - - CBaseEntity *pFirstSpot = pSpot; - - do - { - if ( pSpot ) - { - // check if pSpot is valid - if ( g_pGameRules->IsSpawnPointValid( pSpot, this ) ) - { - if ( pSpot->GetLocalOrigin() == vec3_origin ) - { - pSpot = gEntList.FindEntityByClassname( pSpot, pSpawnpointName ); - continue; - } - - // if so, go to pSpot - goto ReturnSpot; - } - } - // increment pSpot - pSpot = gEntList.FindEntityByClassname( pSpot, pSpawnpointName ); - } while ( pSpot != pFirstSpot ); // loop if we're not back to the start - - // we haven't found a place to spawn yet, so kill any guy at the first spawn point and spawn there - if ( pSpot ) - { - CBaseEntity *ent = NULL; - for ( CEntitySphereQuery sphere( pSpot->GetAbsOrigin(), 128 ); (ent = sphere.GetCurrentEntity()) != NULL; sphere.NextEntity() ) - { - // if ent is a client, kill em (unless they are ourselves) - if ( ent->IsPlayer() && !(ent->edict() == player) ) - ent->TakeDamage( CTakeDamageInfo( GetContainingEntity(INDEXENT(0)), GetContainingEntity(INDEXENT(0)), 300, DMG_GENERIC ) ); - } - goto ReturnSpot; - } - - if ( !pSpot ) - { - pSpot = gEntList.FindEntityByClassname( pSpot, "info_player_start" ); - - if ( pSpot ) - goto ReturnSpot; - } - -ReturnSpot: - - if ( HL2MPRules()->IsTeamplay() == true ) - { - if ( GetTeamNumber() == TEAM_COMBINE ) - { - g_pLastCombineSpawn = pSpot; - } - else if ( GetTeamNumber() == TEAM_REBELS ) - { - g_pLastRebelSpawn = pSpot; - } - } - - g_pLastSpawn = pSpot; - - m_flSlamProtectTime = gpGlobals->curtime + 0.5; - - return pSpot; -} - - -CON_COMMAND( timeleft, "prints the time remaining in the match" ) -{ - CHL2MP_Player *pPlayer = ToHL2MPPlayer( UTIL_GetCommandClient() ); - - int iTimeRemaining = (int)HL2MPRules()->GetMapRemainingTime(); - - if ( iTimeRemaining == 0 ) - { - if ( pPlayer ) - { - ClientPrint( pPlayer, HUD_PRINTTALK, "This game has no timelimit." ); - } - else - { - Msg( "* No Time Limit *\n" ); - } - } - else - { - int iMinutes, iSeconds; - iMinutes = iTimeRemaining / 60; - iSeconds = iTimeRemaining % 60; - - char minutes[8]; - char seconds[8]; - - Q_snprintf( minutes, sizeof(minutes), "%d", iMinutes ); - Q_snprintf( seconds, sizeof(seconds), "%2.2d", iSeconds ); - - if ( pPlayer ) - { - ClientPrint( pPlayer, HUD_PRINTTALK, "Time left in map: %s1:%s2", minutes, seconds ); - } - else - { - Msg( "Time Remaining: %s:%s\n", minutes, seconds ); - } - } -} - - -void CHL2MP_Player::Reset() -{ - ResetDeathCount(); - ResetFragCount(); -} - -bool CHL2MP_Player::IsReady() -{ - return m_bReady; -} - -void CHL2MP_Player::SetReady( bool bReady ) -{ - m_bReady = bReady; -} - -void CHL2MP_Player::CheckChatText( char *p, int bufsize ) -{ - //Look for escape sequences and replace - - char *buf = new char[bufsize]; - int pos = 0; - - // Parse say text for escape sequences - for ( char *pSrc = p; pSrc != NULL && *pSrc != 0 && pos < bufsize-1; pSrc++ ) - { - // copy each char across - buf[pos] = *pSrc; - pos++; - } - - buf[pos] = '\0'; - - // copy buf back into p - Q_strncpy( p, buf, bufsize ); - - delete[] buf; - - const char *pReadyCheck = p; - - HL2MPRules()->CheckChatForReadySignal( this, pReadyCheck ); -} - -void CHL2MP_Player::State_Transition( HL2MPPlayerState newState ) -{ - State_Leave(); - State_Enter( newState ); -} - - -void CHL2MP_Player::State_Enter( HL2MPPlayerState newState ) -{ - m_iPlayerState = newState; - m_pCurStateInfo = State_LookupInfo( newState ); - - // Initialize the new state. - if ( m_pCurStateInfo && m_pCurStateInfo->pfnEnterState ) - (this->*m_pCurStateInfo->pfnEnterState)(); -} - - -void CHL2MP_Player::State_Leave() -{ - if ( m_pCurStateInfo && m_pCurStateInfo->pfnLeaveState ) - { - (this->*m_pCurStateInfo->pfnLeaveState)(); - } -} - - -void CHL2MP_Player::State_PreThink() -{ - if ( m_pCurStateInfo && m_pCurStateInfo->pfnPreThink ) - { - (this->*m_pCurStateInfo->pfnPreThink)(); - } -} - - -CHL2MPPlayerStateInfo *CHL2MP_Player::State_LookupInfo( HL2MPPlayerState state ) -{ - // This table MUST match the - static CHL2MPPlayerStateInfo playerStateInfos[] = - { - { STATE_ACTIVE, "STATE_ACTIVE", &CHL2MP_Player::State_Enter_ACTIVE, NULL, &CHL2MP_Player::State_PreThink_ACTIVE }, - { STATE_OBSERVER_MODE, "STATE_OBSERVER_MODE", &CHL2MP_Player::State_Enter_OBSERVER_MODE, NULL, &CHL2MP_Player::State_PreThink_OBSERVER_MODE } - }; - - for ( int i=0; i < ARRAYSIZE( playerStateInfos ); i++ ) - { - if ( playerStateInfos[i].m_iPlayerState == state ) - return &playerStateInfos[i]; - } - - return NULL; -} - -bool CHL2MP_Player::StartObserverMode(int mode) -{ - //we only want to go into observer mode if the player asked to, not on a death timeout - if ( m_bEnterObserver == true ) - { - return BaseClass::StartObserverMode( mode ); - } - return false; -} - -void CHL2MP_Player::StopObserverMode() -{ - m_bEnterObserver = false; - BaseClass::StopObserverMode(); -} - -void CHL2MP_Player::State_Enter_OBSERVER_MODE() -{ - int observerMode = m_iObserverLastMode; - if ( IsNetClient() ) - { - const char *pIdealMode = engine->GetClientConVarValue( engine->IndexOfEdict( edict() ), "cl_spec_mode" ); - if ( pIdealMode ) - { - observerMode = atoi( pIdealMode ); - if ( observerMode <= OBS_MODE_FIXED || observerMode > OBS_MODE_ROAMING ) - { - observerMode = m_iObserverLastMode; - } - } - } - m_bEnterObserver = true; - StartObserverMode( observerMode ); -} - -void CHL2MP_Player::State_PreThink_OBSERVER_MODE() -{ - // Make sure nobody has changed any of our state. - // Assert( GetMoveType() == MOVETYPE_FLY ); - Assert( m_takedamage == DAMAGE_NO ); - Assert( IsSolidFlagSet( FSOLID_NOT_SOLID ) ); - // Assert( IsEffectActive( EF_NODRAW ) ); - - // Must be dead. - Assert( m_lifeState == LIFE_DEAD ); - Assert( pl.deadflag ); -} - - -void CHL2MP_Player::State_Enter_ACTIVE() -{ - SetMoveType( MOVETYPE_WALK ); - RemoveSolidFlags( FSOLID_NOT_SOLID ); - m_Local.m_iHideHUD = 0; -} - - -void CHL2MP_Player::State_PreThink_ACTIVE() -{ - //we don't really need to do anything here. - //This state_prethink structure came over from CS:S and was doing an assert check that fails the way hl2dm handles death -} +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: Player for HL2. +// +//=============================================================================// + +#include "weapon_hl2mpbasehlmpcombatweapon.h" + +#include "cbase.h" +#include "hl2mp_player.h" +#include "globalstate.h" +#include "game.h" +#include "gamerules.h" +#include "hl2mp_player_shared.h" +#include "predicted_viewmodel.h" +#include "in_buttons.h" +#include "hl2mp_gamerules.h" +#include "KeyValues.h" +#include "team.h" +#include "weapon_hl2mpbase.h" +#include "grenade_satchel.h" +#include "eventqueue.h" + +#include "engine/IEngineSound.h" +#include "SoundEmitterSystem/isoundemittersystembase.h" + +#include "ilagcompensationmanager.h" + +int g_iLastCitizenModel = 0; +int g_iLastCombineModel = 0; + +CBaseEntity *g_pLastCombineSpawn = NULL; +CBaseEntity *g_pLastRebelSpawn = NULL; +extern CBaseEntity *g_pLastSpawn; + +#define HL2MP_COMMAND_MAX_RATE 0.3 + +void ClientKill( edict_t *pEdict ); +void DropPrimedFragGrenade( CHL2MP_Player *pPlayer, CBaseCombatWeapon *pGrenade ); + +LINK_ENTITY_TO_CLASS( player, CHL2MP_Player ); + +LINK_ENTITY_TO_CLASS( info_player_combine, CPointEntity ); +LINK_ENTITY_TO_CLASS( info_player_rebel, CPointEntity ); + +IMPLEMENT_SERVERCLASS_ST(CHL2MP_Player, DT_HL2MP_Player) + SendPropAngle( SENDINFO_VECTORELEM(m_angEyeAngles, 0), 11, SPROP_CHANGES_OFTEN ), + SendPropAngle( SENDINFO_VECTORELEM(m_angEyeAngles, 1), 11, SPROP_CHANGES_OFTEN ), + SendPropEHandle( SENDINFO( m_hRagdoll ) ), + SendPropInt( SENDINFO( m_iSpawnInterpCounter), 4 ), + SendPropInt( SENDINFO( m_iPlayerSoundType), 3 ), + + SendPropExclude( "DT_BaseAnimating", "m_flPoseParameter" ), + SendPropExclude( "DT_BaseFlex", "m_viewtarget" ), + +// SendPropExclude( "DT_ServerAnimationData" , "m_flCycle" ), +// SendPropExclude( "DT_AnimTimeMustBeFirst" , "m_flAnimTime" ), + +END_SEND_TABLE() + +BEGIN_DATADESC( CHL2MP_Player ) +END_DATADESC() + +const char *g_ppszRandomCitizenModels[] = +{ + "models/humans/group03/male_01.mdl", + "models/humans/group03/male_02.mdl", + "models/humans/group03/female_01.mdl", + "models/humans/group03/male_03.mdl", + "models/humans/group03/female_02.mdl", + "models/humans/group03/male_04.mdl", + "models/humans/group03/female_03.mdl", + "models/humans/group03/male_05.mdl", + "models/humans/group03/female_04.mdl", + "models/humans/group03/male_06.mdl", + "models/humans/group03/female_06.mdl", + "models/humans/group03/male_07.mdl", + "models/humans/group03/female_07.mdl", + "models/humans/group03/male_08.mdl", + "models/humans/group03/male_09.mdl", +}; + +const char *g_ppszRandomCombineModels[] = +{ + "models/combine_soldier.mdl", + "models/combine_soldier_prisonguard.mdl", + "models/combine_super_soldier.mdl", + "models/police.mdl", +}; + + +#define MAX_COMBINE_MODELS 4 +#define MODEL_CHANGE_INTERVAL 5.0f +#define TEAM_CHANGE_INTERVAL 5.0f + +#define HL2MPPLAYER_PHYSDAMAGE_SCALE 4.0f + +#ifdef _MSC_VER +#pragma warning( disable : 4355 ) +#endif + +CHL2MP_Player::CHL2MP_Player() : m_PlayerAnimState( this ) +{ + m_angEyeAngles.Init(); + + m_iLastWeaponFireUsercmd = 0; + + m_flNextModelChangeTime = 0.0f; + m_flNextTeamChangeTime = 0.0f; + + m_iSpawnInterpCounter = 0; + + m_bEnterObserver = false; + m_bReady = false; + + BaseClass::ChangeTeam( 0 ); + +// UseClientSideAnimation(); +} + +CHL2MP_Player::~CHL2MP_Player( void ) +{ + +} + +void CHL2MP_Player::UpdateOnRemove( void ) +{ + if ( m_hRagdoll ) + { + UTIL_RemoveImmediate( m_hRagdoll ); + m_hRagdoll = NULL; + } + + BaseClass::UpdateOnRemove(); +} + +void CHL2MP_Player::Precache( void ) +{ + BaseClass::Precache(); + + PrecacheModel ( "sprites/glow01.vmt" ); + + //Precache Citizen models + int nHeads = ARRAYSIZE( g_ppszRandomCitizenModels ); + int i; + + for ( i = 0; i < nHeads; ++i ) + PrecacheModel( g_ppszRandomCitizenModels[i] ); + + //Precache Combine Models + nHeads = ARRAYSIZE( g_ppszRandomCombineModels ); + + for ( i = 0; i < nHeads; ++i ) + PrecacheModel( g_ppszRandomCombineModels[i] ); + + PrecacheFootStepSounds(); + + PrecacheScriptSound( "NPC_MetroPolice.Die" ); + PrecacheScriptSound( "NPC_CombineS.Die" ); + PrecacheScriptSound( "NPC_Citizen.die" ); +} + +void CHL2MP_Player::GiveAllItems( void ) +{ + EquipSuit(); + + CBasePlayer::GiveAmmo( 255, "Pistol"); + CBasePlayer::GiveAmmo( 255, "AR2" ); + CBasePlayer::GiveAmmo( 5, "AR2AltFire" ); + CBasePlayer::GiveAmmo( 255, "SMG1"); + CBasePlayer::GiveAmmo( 1, "smg1_grenade"); + CBasePlayer::GiveAmmo( 255, "Buckshot"); + CBasePlayer::GiveAmmo( 32, "357" ); + CBasePlayer::GiveAmmo( 3, "rpg_round"); + + CBasePlayer::GiveAmmo( 1, "grenade" ); + CBasePlayer::GiveAmmo( 2, "slam" ); + + GiveNamedItem( "weapon_crowbar" ); + GiveNamedItem( "weapon_stunstick" ); + GiveNamedItem( "weapon_pistol" ); + GiveNamedItem( "weapon_357" ); + + GiveNamedItem( "weapon_smg1" ); + GiveNamedItem( "weapon_ar2" ); + + GiveNamedItem( "weapon_shotgun" ); + GiveNamedItem( "weapon_frag" ); + + GiveNamedItem( "weapon_crossbow" ); + + GiveNamedItem( "weapon_rpg" ); + + GiveNamedItem( "weapon_slam" ); + + GiveNamedItem( "weapon_physcannon" ); + +} + +void CHL2MP_Player::GiveDefaultItems( void ) +{ + EquipSuit(); + + CBasePlayer::GiveAmmo( 255, "Pistol"); + CBasePlayer::GiveAmmo( 45, "SMG1"); + CBasePlayer::GiveAmmo( 1, "grenade" ); + CBasePlayer::GiveAmmo( 6, "Buckshot"); + CBasePlayer::GiveAmmo( 6, "357" ); + + if ( GetPlayerModelType() == PLAYER_SOUNDS_METROPOLICE || GetPlayerModelType() == PLAYER_SOUNDS_COMBINESOLDIER ) + { + GiveNamedItem( "weapon_stunstick" ); + } + else if ( GetPlayerModelType() == PLAYER_SOUNDS_CITIZEN ) + { + GiveNamedItem( "weapon_crowbar" ); + } + + GiveNamedItem( "weapon_pistol" ); + GiveNamedItem( "weapon_smg1" ); + GiveNamedItem( "weapon_frag" ); + GiveNamedItem( "weapon_physcannon" ); + + const char *szDefaultWeaponName = engine->GetClientConVarValue( engine->IndexOfEdict( edict() ), "cl_defaultweapon" ); + + CBaseCombatWeapon *pDefaultWeapon = Weapon_OwnsThisType( szDefaultWeaponName ); + + if ( pDefaultWeapon ) + { + Weapon_Switch( pDefaultWeapon ); + } + else + { + Weapon_Switch( Weapon_OwnsThisType( "weapon_physcannon" ) ); + } +} + +void CHL2MP_Player::PickDefaultSpawnTeam( void ) +{ + if ( GetTeamNumber() == 0 ) + { + if ( HL2MPRules()->IsTeamplay() == false ) + { + if ( GetModelPtr() == NULL ) + { + const char *szModelName = NULL; + szModelName = engine->GetClientConVarValue( engine->IndexOfEdict( edict() ), "cl_playermodel" ); + + if ( ValidatePlayerModel( szModelName ) == false ) + { + char szReturnString[512]; + + Q_snprintf( szReturnString, sizeof (szReturnString ), "cl_playermodel models/combine_soldier.mdl\n" ); + engine->ClientCommand ( edict(), szReturnString ); + } + + ChangeTeam( TEAM_UNASSIGNED ); + } + } + else + { + CTeam *pCombine = g_Teams[TEAM_COMBINE]; + CTeam *pRebels = g_Teams[TEAM_REBELS]; + + if ( pCombine == NULL || pRebels == NULL ) + { + ChangeTeam( random->RandomInt( TEAM_COMBINE, TEAM_REBELS ) ); + } + else + { + if ( pCombine->GetNumPlayers() > pRebels->GetNumPlayers() ) + { + ChangeTeam( TEAM_REBELS ); + } + else if ( pCombine->GetNumPlayers() < pRebels->GetNumPlayers() ) + { + ChangeTeam( TEAM_COMBINE ); + } + else + { + ChangeTeam( random->RandomInt( TEAM_COMBINE, TEAM_REBELS ) ); + } + } + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Sets HL2 specific defaults. +//----------------------------------------------------------------------------- +void CHL2MP_Player::Spawn(void) +{ + m_flNextModelChangeTime = 0.0f; + m_flNextTeamChangeTime = 0.0f; + + PickDefaultSpawnTeam(); + + BaseClass::Spawn(); + + if ( !IsObserver() ) + { + pl.deadflag = false; + RemoveSolidFlags( FSOLID_NOT_SOLID ); + + RemoveEffects( EF_NODRAW ); + + GiveDefaultItems(); + } + + RemoveEffects( EF_NOINTERP ); + + SetNumAnimOverlays( 3 ); + ResetAnimation(); + + m_nRenderFX = kRenderNormal; + + m_Local.m_iHideHUD = 0; + + AddFlag(FL_ONGROUND); // set the player on the ground at the start of the round. + + m_impactEnergyScale = HL2MPPLAYER_PHYSDAMAGE_SCALE; + + if ( HL2MPRules()->IsIntermission() ) + { + AddFlag( FL_FROZEN ); + } + else + { + RemoveFlag( FL_FROZEN ); + } + + m_iSpawnInterpCounter = (m_iSpawnInterpCounter + 1) % 8; + + m_Local.m_bDucked = false; + + SetPlayerUnderwater(false); + + m_bReady = false; +} + +void CHL2MP_Player::PickupObject( CBaseEntity *pObject, bool bLimitMassAndSize ) +{ + +} + +bool CHL2MP_Player::ValidatePlayerModel( const char *pModel ) +{ + int iModels = ARRAYSIZE( g_ppszRandomCitizenModels ); + int i; + + for ( i = 0; i < iModels; ++i ) + { + if ( !Q_stricmp( g_ppszRandomCitizenModels[i], pModel ) ) + { + return true; + } + } + + iModels = ARRAYSIZE( g_ppszRandomCombineModels ); + + for ( i = 0; i < iModels; ++i ) + { + if ( !Q_stricmp( g_ppszRandomCombineModels[i], pModel ) ) + { + return true; + } + } + + return false; +} + +void CHL2MP_Player::SetPlayerTeamModel( void ) +{ + const char *szModelName = NULL; + szModelName = engine->GetClientConVarValue( engine->IndexOfEdict( edict() ), "cl_playermodel" ); + + int modelIndex = modelinfo->GetModelIndex( szModelName ); + + if ( modelIndex == -1 || ValidatePlayerModel( szModelName ) == false ) + { + szModelName = "models/Combine_Soldier.mdl"; + m_iModelType = TEAM_COMBINE; + + char szReturnString[512]; + + Q_snprintf( szReturnString, sizeof (szReturnString ), "cl_playermodel %s\n", szModelName ); + engine->ClientCommand ( edict(), szReturnString ); + } + + if ( GetTeamNumber() == TEAM_COMBINE ) + { + if ( Q_stristr( szModelName, "models/human") ) + { + int nHeads = ARRAYSIZE( g_ppszRandomCombineModels ); + + g_iLastCombineModel = ( g_iLastCombineModel + 1 ) % nHeads; + szModelName = g_ppszRandomCombineModels[g_iLastCombineModel]; + } + + m_iModelType = TEAM_COMBINE; + } + else if ( GetTeamNumber() == TEAM_REBELS ) + { + if ( !Q_stristr( szModelName, "models/human") ) + { + int nHeads = ARRAYSIZE( g_ppszRandomCitizenModels ); + + g_iLastCitizenModel = ( g_iLastCitizenModel + 1 ) % nHeads; + szModelName = g_ppszRandomCitizenModels[g_iLastCitizenModel]; + } + + m_iModelType = TEAM_REBELS; + } + + SetModel( szModelName ); + SetupPlayerSoundsByModel( szModelName ); + + m_flNextModelChangeTime = gpGlobals->curtime + MODEL_CHANGE_INTERVAL; +} + +void CHL2MP_Player::SetPlayerModel( void ) +{ + const char *szModelName = NULL; + const char *pszCurrentModelName = modelinfo->GetModelName( GetModel()); + + szModelName = engine->GetClientConVarValue( engine->IndexOfEdict( edict() ), "cl_playermodel" ); + + if ( ValidatePlayerModel( szModelName ) == false ) + { + char szReturnString[512]; + + if ( ValidatePlayerModel( pszCurrentModelName ) == false ) + { + pszCurrentModelName = "models/Combine_Soldier.mdl"; + } + + Q_snprintf( szReturnString, sizeof (szReturnString ), "cl_playermodel %s\n", pszCurrentModelName ); + engine->ClientCommand ( edict(), szReturnString ); + + szModelName = pszCurrentModelName; + } + + if ( GetTeamNumber() == TEAM_COMBINE ) + { + int nHeads = ARRAYSIZE( g_ppszRandomCombineModels ); + + g_iLastCombineModel = ( g_iLastCombineModel + 1 ) % nHeads; + szModelName = g_ppszRandomCombineModels[g_iLastCombineModel]; + + m_iModelType = TEAM_COMBINE; + } + else if ( GetTeamNumber() == TEAM_REBELS ) + { + int nHeads = ARRAYSIZE( g_ppszRandomCitizenModels ); + + g_iLastCitizenModel = ( g_iLastCitizenModel + 1 ) % nHeads; + szModelName = g_ppszRandomCitizenModels[g_iLastCitizenModel]; + + m_iModelType = TEAM_REBELS; + } + else + { + if ( Q_strlen( szModelName ) == 0 ) + { + szModelName = g_ppszRandomCitizenModels[0]; + } + + if ( Q_stristr( szModelName, "models/human") ) + { + m_iModelType = TEAM_REBELS; + } + else + { + m_iModelType = TEAM_COMBINE; + } + } + + int modelIndex = modelinfo->GetModelIndex( szModelName ); + + if ( modelIndex == -1 ) + { + szModelName = "models/Combine_Soldier.mdl"; + m_iModelType = TEAM_COMBINE; + + char szReturnString[512]; + + Q_snprintf( szReturnString, sizeof (szReturnString ), "cl_playermodel %s\n", szModelName ); + engine->ClientCommand ( edict(), szReturnString ); + } + + SetModel( szModelName ); + SetupPlayerSoundsByModel( szModelName ); + + m_flNextModelChangeTime = gpGlobals->curtime + MODEL_CHANGE_INTERVAL; +} + +void CHL2MP_Player::SetupPlayerSoundsByModel( const char *pModelName ) +{ + if ( Q_stristr( pModelName, "models/human") ) + { + m_iPlayerSoundType = (int)PLAYER_SOUNDS_CITIZEN; + } + else if ( Q_stristr(pModelName, "police" ) ) + { + m_iPlayerSoundType = (int)PLAYER_SOUNDS_METROPOLICE; + } + else if ( Q_stristr(pModelName, "combine" ) ) + { + m_iPlayerSoundType = (int)PLAYER_SOUNDS_COMBINESOLDIER; + } +} + +void CHL2MP_Player::ResetAnimation( void ) +{ + if ( IsAlive() ) + { + SetSequence ( -1 ); + SetActivity( ACT_INVALID ); + + if (!GetAbsVelocity().x && !GetAbsVelocity().y) + SetAnimation( PLAYER_IDLE ); + else if ((GetAbsVelocity().x || GetAbsVelocity().y) && ( GetFlags() & FL_ONGROUND )) + SetAnimation( PLAYER_WALK ); + else if (GetWaterLevel() > 1) + SetAnimation( PLAYER_WALK ); + } +} + + +bool CHL2MP_Player::Weapon_Switch( CBaseCombatWeapon *pWeapon, int viewmodelindex ) +{ + bool bRet = BaseClass::Weapon_Switch( pWeapon, viewmodelindex ); + + if ( bRet == true ) + { + ResetAnimation(); + } + + return bRet; +} + +void CHL2MP_Player::PreThink( void ) +{ + QAngle vOldAngles = GetLocalAngles(); + QAngle vTempAngles = GetLocalAngles(); + + vTempAngles = EyeAngles(); + + if ( vTempAngles[PITCH] > 180.0f ) + { + vTempAngles[PITCH] -= 360.0f; + } + + SetLocalAngles( vTempAngles ); + + BaseClass::PreThink(); + State_PreThink(); + + //Reset bullet force accumulator, only lasts one frame + m_vecTotalBulletForce = vec3_origin; + SetLocalAngles( vOldAngles ); +} + +void CHL2MP_Player::PostThink( void ) +{ + BaseClass::PostThink(); + + if ( GetFlags() & FL_DUCKING ) + { + SetCollisionBounds( VEC_CROUCH_TRACE_MIN, VEC_CROUCH_TRACE_MAX ); + } + + m_PlayerAnimState.Update(); + + // Store the eye angles pitch so the client can compute its animation state correctly. + m_angEyeAngles = EyeAngles(); + + QAngle angles = GetLocalAngles(); + angles[PITCH] = 0; + SetLocalAngles( angles ); +} + +void CHL2MP_Player::PlayerDeathThink() +{ + if( !IsObserver() ) + { + BaseClass::PlayerDeathThink(); + } +} + +void CHL2MP_Player::FireBullets ( const FireBulletsInfo_t &info ) +{ + // Move other players back to history positions based on local player's lag + lagcompensation->StartLagCompensation( this, this->GetCurrentCommand() ); + + FireBulletsInfo_t modinfo = info; + + CWeaponHL2MPBase *pWeapon = dynamic_cast( GetActiveWeapon() ); + + if ( pWeapon ) + { + modinfo.m_iPlayerDamage = modinfo.m_iDamage = pWeapon->GetHL2MPWpnData().m_iPlayerDamage; + } + + NoteWeaponFired(); + + BaseClass::FireBullets( modinfo ); + + // Move other players back to history positions based on local player's lag + lagcompensation->FinishLagCompensation( this ); +} + +void CHL2MP_Player::NoteWeaponFired( void ) +{ + Assert( m_pCurrentCommand ); + if( m_pCurrentCommand ) + { + m_iLastWeaponFireUsercmd = m_pCurrentCommand->command_number; + } +} + +extern ConVar sv_maxunlag; + +bool CHL2MP_Player::WantsLagCompensationOnEntity( const CBasePlayer *pPlayer, const CUserCmd *pCmd, const CBitVec *pEntityTransmitBits ) const +{ + // No need to lag compensate at all if we're not attacking in this command and + // we haven't attacked recently. + if ( !( pCmd->buttons & IN_ATTACK ) && (pCmd->command_number - m_iLastWeaponFireUsercmd > 5) ) + return false; + + // If this entity hasn't been transmitted to us and acked, then don't bother lag compensating it. + if ( pEntityTransmitBits && !pEntityTransmitBits->Get( pPlayer->entindex() ) ) + return false; + + const Vector &vMyOrigin = GetAbsOrigin(); + const Vector &vHisOrigin = pPlayer->GetAbsOrigin(); + + // get max distance player could have moved within max lag compensation time, + // multiply by 1.5 to to avoid "dead zones" (sqrt(2) would be the exact value) + float maxDistance = 1.5 * pPlayer->MaxSpeed() * sv_maxunlag.GetFloat(); + + // If the player is within this distance, lag compensate them in case they're running past us. + if ( vHisOrigin.DistTo( vMyOrigin ) < maxDistance ) + return true; + + // If their origin is not within a 45 degree cone in front of us, no need to lag compensate. + Vector vForward; + AngleVectors( pCmd->viewangles, &vForward ); + + Vector vDiff = vHisOrigin - vMyOrigin; + VectorNormalize( vDiff ); + + float flCosAngle = 0.707107f; // 45 degree angle + if ( vForward.Dot( vDiff ) < flCosAngle ) + return false; + + return true; +} + +Activity CHL2MP_Player::TranslateTeamActivity( Activity ActToTranslate ) +{ + if ( m_iModelType == TEAM_COMBINE ) + return ActToTranslate; + + if ( ActToTranslate == ACT_RUN ) + return ACT_RUN_AIM_AGITATED; + + if ( ActToTranslate == ACT_IDLE ) + return ACT_IDLE_AIM_AGITATED; + + if ( ActToTranslate == ACT_WALK ) + return ACT_WALK_AIM_AGITATED; + + return ActToTranslate; +} + +extern ConVar hl2_normspeed; + +// Set the activity based on an event or current state +void CHL2MP_Player::SetAnimation( PLAYER_ANIM playerAnim ) +{ + int animDesired; + + float speed; + + speed = GetAbsVelocity().Length2D(); + + + // bool bRunning = true; + + //Revisit! +/* if ( ( m_nButtons & ( IN_FORWARD | IN_BACK | IN_MOVELEFT | IN_MOVERIGHT ) ) ) + { + if ( speed > 1.0f && speed < hl2_normspeed.GetFloat() - 20.0f ) + { + bRunning = false; + } + }*/ + + if ( GetFlags() & ( FL_FROZEN | FL_ATCONTROLS ) ) + { + speed = 0; + playerAnim = PLAYER_IDLE; + } + + Activity idealActivity = ACT_HL2MP_RUN; + + // This could stand to be redone. Why is playerAnim abstracted from activity? (sjb) + if ( playerAnim == PLAYER_JUMP ) + { + idealActivity = ACT_HL2MP_JUMP; + } + else if ( playerAnim == PLAYER_DIE ) + { + if ( m_lifeState == LIFE_ALIVE ) + { + return; + } + } + else if ( playerAnim == PLAYER_ATTACK1 ) + { + if ( GetActivity( ) == ACT_HOVER || + GetActivity( ) == ACT_SWIM || + GetActivity( ) == ACT_HOP || + GetActivity( ) == ACT_LEAP || + GetActivity( ) == ACT_DIESIMPLE ) + { + idealActivity = GetActivity( ); + } + else + { + idealActivity = ACT_HL2MP_GESTURE_RANGE_ATTACK; + } + } + else if ( playerAnim == PLAYER_RELOAD ) + { + idealActivity = ACT_HL2MP_GESTURE_RELOAD; + } + else if ( playerAnim == PLAYER_IDLE || playerAnim == PLAYER_WALK ) + { + if ( !( GetFlags() & FL_ONGROUND ) && GetActivity( ) == ACT_HL2MP_JUMP ) // Still jumping + { + idealActivity = GetActivity( ); + } + /* + else if ( GetWaterLevel() > 1 ) + { + if ( speed == 0 ) + idealActivity = ACT_HOVER; + else + idealActivity = ACT_SWIM; + } + */ + else + { + if ( GetFlags() & FL_DUCKING ) + { + if ( speed > 0 ) + { + idealActivity = ACT_HL2MP_WALK_CROUCH; + } + else + { + idealActivity = ACT_HL2MP_IDLE_CROUCH; + } + } + else + { + if ( speed > 0 ) + { + /* + if ( bRunning == false ) + { + idealActivity = ACT_WALK; + } + else + */ + { + idealActivity = ACT_HL2MP_RUN; + } + } + else + { + idealActivity = ACT_HL2MP_IDLE; + } + } + } + + idealActivity = TranslateTeamActivity( idealActivity ); + } + + if ( idealActivity == ACT_HL2MP_GESTURE_RANGE_ATTACK ) + { + RestartGesture( Weapon_TranslateActivity( idealActivity ) ); + + // FIXME: this seems a bit wacked + Weapon_SetActivity( Weapon_TranslateActivity( ACT_RANGE_ATTACK1 ), 0 ); + + return; + } + else if ( idealActivity == ACT_HL2MP_GESTURE_RELOAD ) + { + RestartGesture( Weapon_TranslateActivity( idealActivity ) ); + return; + } + else + { + SetActivity( idealActivity ); + + animDesired = SelectWeightedSequence( Weapon_TranslateActivity ( idealActivity ) ); + + if (animDesired == -1) + { + animDesired = SelectWeightedSequence( idealActivity ); + + if ( animDesired == -1 ) + { + animDesired = 0; + } + } + + // Already using the desired animation? + if ( GetSequence() == animDesired ) + return; + + m_flPlaybackRate = 1.0; + ResetSequence( animDesired ); + SetCycle( 0 ); + return; + } + + // Already using the desired animation? + if ( GetSequence() == animDesired ) + return; + + //Msg( "Set animation to %d\n", animDesired ); + // Reset to first frame of desired animation + ResetSequence( animDesired ); + SetCycle( 0 ); +} + + +extern int gEvilImpulse101; +//----------------------------------------------------------------------------- +// Purpose: Player reacts to bumping a weapon. +// Input : pWeapon - the weapon that the player bumped into. +// Output : Returns true if player picked up the weapon +//----------------------------------------------------------------------------- +bool CHL2MP_Player::BumpWeapon( CBaseCombatWeapon *pWeapon ) +{ + CBaseCombatCharacter *pOwner = pWeapon->GetOwner(); + + // Can I have this weapon type? + if ( IsEFlagSet( EFL_NO_WEAPON_PICKUP ) ) + return false; + + if ( pOwner || !Weapon_CanUse( pWeapon ) || !g_pGameRules->CanHavePlayerItem( this, pWeapon ) ) + { + if ( gEvilImpulse101 ) + { + UTIL_Remove( pWeapon ); + } + return false; + } + + // Don't let the player fetch weapons through walls (use MASK_SOLID so that you can't pickup through windows) + if( !pWeapon->FVisible( this, MASK_SOLID ) && !(GetFlags() & FL_NOTARGET) ) + { + return false; + } + + bool bOwnsWeaponAlready = !!Weapon_OwnsThisType( pWeapon->GetClassname(), pWeapon->GetSubType()); + + if ( bOwnsWeaponAlready == true ) + { + //If we have room for the ammo, then "take" the weapon too. + if ( Weapon_EquipAmmoOnly( pWeapon ) ) + { + pWeapon->CheckRespawn(); + + UTIL_Remove( pWeapon ); + return true; + } + else + { + return false; + } + } + + pWeapon->CheckRespawn(); + Weapon_Equip( pWeapon ); + + return true; +} + +void CHL2MP_Player::ChangeTeam( int iTeam ) +{ +/* if ( GetNextTeamChangeTime() >= gpGlobals->curtime ) + { + char szReturnString[128]; + Q_snprintf( szReturnString, sizeof( szReturnString ), "Please wait %d more seconds before trying to switch teams again.\n", (int)(GetNextTeamChangeTime() - gpGlobals->curtime) ); + + ClientPrint( this, HUD_PRINTTALK, szReturnString ); + return; + }*/ + + bool bKill = false; + + if ( HL2MPRules()->IsTeamplay() != true && iTeam != TEAM_SPECTATOR ) + { + //don't let them try to join combine or rebels during deathmatch. + iTeam = TEAM_UNASSIGNED; + } + + if ( HL2MPRules()->IsTeamplay() == true ) + { + if ( iTeam != GetTeamNumber() && GetTeamNumber() != TEAM_UNASSIGNED ) + { + bKill = true; + } + } + + BaseClass::ChangeTeam( iTeam ); + + m_flNextTeamChangeTime = gpGlobals->curtime + TEAM_CHANGE_INTERVAL; + + if ( HL2MPRules()->IsTeamplay() == true ) + { + SetPlayerTeamModel(); + } + else + { + SetPlayerModel(); + } + + if ( iTeam == TEAM_SPECTATOR ) + { + RemoveAllItems( true ); + + State_Transition( STATE_OBSERVER_MODE ); + } + + if ( bKill == true ) + { + ClientKill( edict() ); + } +} + +bool CHL2MP_Player::HandleCommand_JoinTeam( int team ) +{ + if ( !GetGlobalTeam( team ) ) + { + Warning( "HandleCommand_JoinTeam( %d ) - invalid team index.\n", team ); + return false; + } + + if ( team == TEAM_SPECTATOR ) + { + // Prevent this is the cvar is set + if ( !mp_allowspectators.GetInt() ) + { + ClientPrint( this, HUD_PRINTCENTER, "#Cannot_Be_Spectator" ); + return false; + } + + if ( GetTeamNumber() != TEAM_UNASSIGNED && !IsDead() ) + { + m_fNextSuicideTime = gpGlobals->curtime; // allow the suicide to work + + ClientKill( edict() ); + + // add 1 to frags to balance out the 1 subtracted for killing yourself + IncrementFragCount( 1 ); + } + + ChangeTeam( TEAM_SPECTATOR ); + + return true; + } + else + { + StopObserverMode(); + State_Transition(STATE_ACTIVE); + } + + // Switch their actual team... + ChangeTeam( team ); + + return true; +} + +bool CHL2MP_Player::ClientCommand( const char *pcmd ) +{ + if ( FStrEq( pcmd, "spectate" ) ) + { + if ( ShouldRunRateLimitedCommand( pcmd ) ) + { + // instantly join spectators + HandleCommand_JoinTeam( TEAM_SPECTATOR ); + } + return true; + } + else if ( FStrEq( pcmd, "jointeam" ) ) + { + if ( engine->Cmd_Argc() < 2 ) + { + Warning( "Player sent bad jointeam syntax\n" ); + } + + if ( ShouldRunRateLimitedCommand( pcmd ) ) + { + int iTeam = atoi( engine->Cmd_Argv(1) ); + HandleCommand_JoinTeam( iTeam ); + } + return true; + } + else if ( FStrEq( pcmd, "joingame" ) ) + { + + return true; + } + + return BaseClass::ClientCommand( pcmd ); +} + +void CHL2MP_Player::CheatImpulseCommands( int iImpulse ) +{ + switch ( iImpulse ) + { + case 101: + { + if( sv_cheats->GetBool() ) + { + GiveAllItems(); + } + } + break; + + default: + BaseClass::CheatImpulseCommands( iImpulse ); + } +} + +bool CHL2MP_Player::ShouldRunRateLimitedCommand( const char *pcmd ) +{ + int i = m_RateLimitLastCommandTimes.Find( pcmd ); + if ( i == m_RateLimitLastCommandTimes.InvalidIndex() ) + { + m_RateLimitLastCommandTimes.Insert( pcmd, gpGlobals->curtime ); + return true; + } + else if ( (gpGlobals->curtime - m_RateLimitLastCommandTimes[i]) < HL2MP_COMMAND_MAX_RATE ) + { + // Too fast. + return false; + } + else + { + m_RateLimitLastCommandTimes[i] = gpGlobals->curtime; + return true; + } +} + +void CHL2MP_Player::CreateViewModel( int index /*=0*/ ) +{ + Assert( index >= 0 && index < MAX_VIEWMODELS ); + + if ( GetViewModel( index ) ) + return; + + CPredictedViewModel *vm = ( CPredictedViewModel * )CreateEntityByName( "predicted_viewmodel" ); + if ( vm ) + { + vm->SetAbsOrigin( GetAbsOrigin() ); + vm->SetOwner( this ); + vm->SetIndex( index ); + DispatchSpawn( vm ); + vm->FollowEntity( this, false ); + m_hViewModel.Set( index, vm ); + } +} + +bool CHL2MP_Player::BecomeRagdollOnClient( const Vector &force ) +{ + return true; +} + +// -------------------------------------------------------------------------------- // +// Ragdoll entities. +// -------------------------------------------------------------------------------- // + +class CHL2MPRagdoll : public CBaseAnimatingOverlay +{ +public: + DECLARE_CLASS( CHL2MPRagdoll, CBaseAnimatingOverlay ); + DECLARE_SERVERCLASS(); + + // Transmit ragdolls to everyone. + virtual int UpdateTransmitState() + { + return SetTransmitState( FL_EDICT_ALWAYS ); + } + +public: + // In case the client has the player entity, we transmit the player index. + // In case the client doesn't have it, we transmit the player's model index, origin, and angles + // so they can create a ragdoll in the right place. + CNetworkHandle( CBaseEntity, m_hPlayer ); // networked entity handle + CNetworkVector( m_vecRagdollVelocity ); + CNetworkVector( m_vecRagdollOrigin ); +}; + +LINK_ENTITY_TO_CLASS( hl2mp_ragdoll, CHL2MPRagdoll ); + +IMPLEMENT_SERVERCLASS_ST_NOBASE( CHL2MPRagdoll, DT_HL2MPRagdoll ) + SendPropVector( SENDINFO(m_vecRagdollOrigin), -1, SPROP_COORD ), + SendPropEHandle( SENDINFO( m_hPlayer ) ), + SendPropModelIndex( SENDINFO( m_nModelIndex ) ), + SendPropInt ( SENDINFO(m_nForceBone), 8, 0 ), + SendPropVector ( SENDINFO(m_vecForce), -1, SPROP_NOSCALE ), + SendPropVector( SENDINFO( m_vecRagdollVelocity ) ) +END_SEND_TABLE() + + +void CHL2MP_Player::CreateRagdollEntity( void ) +{ + if ( m_hRagdoll ) + { + UTIL_RemoveImmediate( m_hRagdoll ); + m_hRagdoll = NULL; + } + + // If we already have a ragdoll, don't make another one. + CHL2MPRagdoll *pRagdoll = dynamic_cast< CHL2MPRagdoll* >( m_hRagdoll.Get() ); + + if ( !pRagdoll ) + { + // create a new one + pRagdoll = dynamic_cast< CHL2MPRagdoll* >( CreateEntityByName( "hl2mp_ragdoll" ) ); + } + + if ( pRagdoll ) + { + pRagdoll->m_hPlayer = this; + pRagdoll->m_vecRagdollOrigin = GetAbsOrigin(); + pRagdoll->m_vecRagdollVelocity = GetAbsVelocity(); + pRagdoll->m_nModelIndex = m_nModelIndex; + pRagdoll->m_nForceBone = m_nForceBone; + pRagdoll->m_vecForce = m_vecTotalBulletForce; + pRagdoll->SetAbsOrigin( GetAbsOrigin() ); + } + + // ragdolls will be removed on round restart automatically + m_hRagdoll = pRagdoll; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +int CHL2MP_Player::FlashlightIsOn( void ) +{ + return IsEffectActive( EF_DIMLIGHT ); +} + +extern ConVar flashlight; + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CHL2MP_Player::FlashlightTurnOn( void ) +{ + if( flashlight.GetInt() > 0 && IsAlive() ) + { + AddEffects( EF_DIMLIGHT ); + EmitSound( "HL2Player.FlashlightOn" ); + } +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CHL2MP_Player::FlashlightTurnOff( void ) +{ + RemoveEffects( EF_DIMLIGHT ); + + if( IsAlive() ) + { + EmitSound( "HL2Player.FlashlightOff" ); + } +} + +void CHL2MP_Player::Weapon_Drop( CBaseCombatWeapon *pWeapon, const Vector *pvecTarget, const Vector *pVelocity ) +{ + //Drop a grenade if it's primed. + if ( GetActiveWeapon() ) + { + CBaseCombatWeapon *pGrenade = Weapon_OwnsThisType("weapon_frag"); + + if ( GetActiveWeapon() == pGrenade ) + { + if ( ( m_nButtons & IN_ATTACK ) || (m_nButtons & IN_ATTACK2) ) + { + DropPrimedFragGrenade( this, pGrenade ); + return; + } + } + } + + BaseClass::Weapon_Drop( pWeapon, pvecTarget, pVelocity ); +} + + +void CHL2MP_Player::DetonateTripmines( void ) +{ + CBaseEntity *pEntity = NULL; + + while ((pEntity = gEntList.FindEntityByClassname( pEntity, "npc_satchel" )) != NULL) + { + CSatchelCharge *pSatchel = dynamic_cast(pEntity); + if (pSatchel->m_bIsLive && pSatchel->GetThrower() == this ) + { + g_EventQueue.AddEvent( pSatchel, "Explode", 0.20, this, this ); + } + } + + // Play sound for pressing the detonator + EmitSound( "Weapon_SLAM.SatchelDetonate" ); +} + +void CHL2MP_Player::Event_Killed( const CTakeDamageInfo &info ) +{ + //update damage info with our accumulated physics force + CTakeDamageInfo subinfo = info; + subinfo.SetDamageForce( m_vecTotalBulletForce ); + + SetNumAnimOverlays( 0 ); + + // Note: since we're dead, it won't draw us on the client, but we don't set EF_NODRAW + // because we still want to transmit to the clients in our PVS. + CreateRagdollEntity(); + + DetonateTripmines(); + + BaseClass::Event_Killed( subinfo ); + + if ( info.GetDamageType() & DMG_DISSOLVE ) + { + if ( m_hRagdoll ) + { + m_hRagdoll->GetBaseAnimating()->Dissolve( NULL, gpGlobals->curtime, false, ENTITY_DISSOLVE_NORMAL ); + } + } + + CBaseEntity *pAttacker = info.GetAttacker(); + + if ( pAttacker ) + { + int iScoreToAdd = 1; + + if ( pAttacker == this ) + { + iScoreToAdd = -1; + } + + GetGlobalTeam( pAttacker->GetTeamNumber() )->AddScore( iScoreToAdd ); + } + + FlashlightTurnOff(); + + m_lifeState = LIFE_DEAD; + + RemoveEffects( EF_NODRAW ); // still draw player body + StopZooming(); +} + +int CHL2MP_Player::OnTakeDamage( const CTakeDamageInfo &inputInfo ) +{ + //return here if the player is in the respawn grace period vs. slams. + if ( gpGlobals->curtime < m_flSlamProtectTime && (inputInfo.GetDamageType() == DMG_BLAST ) ) + return 0; + + m_vecTotalBulletForce += inputInfo.GetDamageForce(); + + return BaseClass::OnTakeDamage( inputInfo ); +} + +void CHL2MP_Player::DeathSound( const CTakeDamageInfo &info ) +{ + if ( m_hRagdoll && m_hRagdoll->GetBaseAnimating()->IsDissolving() ) + return; + + char szStepSound[128]; + + Q_snprintf( szStepSound, sizeof( szStepSound ), "%s.Die", GetPlayerModelSoundPrefix() ); + + const char *pModelName = STRING( GetModelName() ); + + CSoundParameters params; + if ( GetParametersForSound( szStepSound, params, pModelName ) == false ) + return; + + Vector vecOrigin = GetAbsOrigin(); + + CRecipientFilter filter; + filter.AddRecipientsByPAS( vecOrigin ); + + EmitSound_t ep; + ep.m_nChannel = params.channel; + ep.m_pSoundName = params.soundname; + ep.m_flVolume = params.volume; + ep.m_SoundLevel = params.soundlevel; + ep.m_nFlags = 0; + ep.m_nPitch = params.pitch; + ep.m_pOrigin = &vecOrigin; + + EmitSound( filter, entindex(), ep ); +} + +CBaseEntity* CHL2MP_Player::EntSelectSpawnPoint( void ) +{ + CBaseEntity *pSpot = NULL; + CBaseEntity *pLastSpawnPoint = g_pLastSpawn; + edict_t *player = edict(); + const char *pSpawnpointName = "info_player_deathmatch"; + + if ( HL2MPRules()->IsTeamplay() == true ) + { + if ( GetTeamNumber() == TEAM_COMBINE ) + { + pSpawnpointName = "info_player_combine"; + pLastSpawnPoint = g_pLastCombineSpawn; + } + else if ( GetTeamNumber() == TEAM_REBELS ) + { + pSpawnpointName = "info_player_rebel"; + pLastSpawnPoint = g_pLastRebelSpawn; + } + + if ( gEntList.FindEntityByClassname( NULL, pSpawnpointName ) == NULL ) + { + pSpawnpointName = "info_player_deathmatch"; + pLastSpawnPoint = g_pLastSpawn; + } + } + + pSpot = pLastSpawnPoint; + // Randomize the start spot + for ( int i = random->RandomInt(1,5); i > 0; i-- ) + pSpot = gEntList.FindEntityByClassname( pSpot, pSpawnpointName ); + if ( !pSpot ) // skip over the null point + pSpot = gEntList.FindEntityByClassname( pSpot, pSpawnpointName ); + + CBaseEntity *pFirstSpot = pSpot; + + do + { + if ( pSpot ) + { + // check if pSpot is valid + if ( g_pGameRules->IsSpawnPointValid( pSpot, this ) ) + { + if ( pSpot->GetLocalOrigin() == vec3_origin ) + { + pSpot = gEntList.FindEntityByClassname( pSpot, pSpawnpointName ); + continue; + } + + // if so, go to pSpot + goto ReturnSpot; + } + } + // increment pSpot + pSpot = gEntList.FindEntityByClassname( pSpot, pSpawnpointName ); + } while ( pSpot != pFirstSpot ); // loop if we're not back to the start + + // we haven't found a place to spawn yet, so kill any guy at the first spawn point and spawn there + if ( pSpot ) + { + CBaseEntity *ent = NULL; + for ( CEntitySphereQuery sphere( pSpot->GetAbsOrigin(), 128 ); (ent = sphere.GetCurrentEntity()) != NULL; sphere.NextEntity() ) + { + // if ent is a client, kill em (unless they are ourselves) + if ( ent->IsPlayer() && !(ent->edict() == player) ) + ent->TakeDamage( CTakeDamageInfo( GetContainingEntity(INDEXENT(0)), GetContainingEntity(INDEXENT(0)), 300, DMG_GENERIC ) ); + } + goto ReturnSpot; + } + + if ( !pSpot ) + { + pSpot = gEntList.FindEntityByClassname( pSpot, "info_player_start" ); + + if ( pSpot ) + goto ReturnSpot; + } + +ReturnSpot: + + if ( HL2MPRules()->IsTeamplay() == true ) + { + if ( GetTeamNumber() == TEAM_COMBINE ) + { + g_pLastCombineSpawn = pSpot; + } + else if ( GetTeamNumber() == TEAM_REBELS ) + { + g_pLastRebelSpawn = pSpot; + } + } + + g_pLastSpawn = pSpot; + + m_flSlamProtectTime = gpGlobals->curtime + 0.5; + + return pSpot; +} + + +CON_COMMAND( timeleft, "prints the time remaining in the match" ) +{ + CHL2MP_Player *pPlayer = ToHL2MPPlayer( UTIL_GetCommandClient() ); + + int iTimeRemaining = (int)HL2MPRules()->GetMapRemainingTime(); + + if ( iTimeRemaining == 0 ) + { + if ( pPlayer ) + { + ClientPrint( pPlayer, HUD_PRINTTALK, "This game has no timelimit." ); + } + else + { + Msg( "* No Time Limit *\n" ); + } + } + else + { + int iMinutes, iSeconds; + iMinutes = iTimeRemaining / 60; + iSeconds = iTimeRemaining % 60; + + char minutes[8]; + char seconds[8]; + + Q_snprintf( minutes, sizeof(minutes), "%d", iMinutes ); + Q_snprintf( seconds, sizeof(seconds), "%2.2d", iSeconds ); + + if ( pPlayer ) + { + ClientPrint( pPlayer, HUD_PRINTTALK, "Time left in map: %s1:%s2", minutes, seconds ); + } + else + { + Msg( "Time Remaining: %s:%s\n", minutes, seconds ); + } + } +} + + +void CHL2MP_Player::Reset() +{ + ResetDeathCount(); + ResetFragCount(); +} + +bool CHL2MP_Player::IsReady() +{ + return m_bReady; +} + +void CHL2MP_Player::SetReady( bool bReady ) +{ + m_bReady = bReady; +} + +void CHL2MP_Player::CheckChatText( char *p, int bufsize ) +{ + //Look for escape sequences and replace + + char *buf = new char[bufsize]; + int pos = 0; + + // Parse say text for escape sequences + for ( char *pSrc = p; pSrc != NULL && *pSrc != 0 && pos < bufsize-1; pSrc++ ) + { + // copy each char across + buf[pos] = *pSrc; + pos++; + } + + buf[pos] = '\0'; + + // copy buf back into p + Q_strncpy( p, buf, bufsize ); + + delete[] buf; + + const char *pReadyCheck = p; + + HL2MPRules()->CheckChatForReadySignal( this, pReadyCheck ); +} + +void CHL2MP_Player::State_Transition( HL2MPPlayerState newState ) +{ + State_Leave(); + State_Enter( newState ); +} + + +void CHL2MP_Player::State_Enter( HL2MPPlayerState newState ) +{ + m_iPlayerState = newState; + m_pCurStateInfo = State_LookupInfo( newState ); + + // Initialize the new state. + if ( m_pCurStateInfo && m_pCurStateInfo->pfnEnterState ) + (this->*m_pCurStateInfo->pfnEnterState)(); +} + + +void CHL2MP_Player::State_Leave() +{ + if ( m_pCurStateInfo && m_pCurStateInfo->pfnLeaveState ) + { + (this->*m_pCurStateInfo->pfnLeaveState)(); + } +} + + +void CHL2MP_Player::State_PreThink() +{ + if ( m_pCurStateInfo && m_pCurStateInfo->pfnPreThink ) + { + (this->*m_pCurStateInfo->pfnPreThink)(); + } +} + + +CHL2MPPlayerStateInfo *CHL2MP_Player::State_LookupInfo( HL2MPPlayerState state ) +{ + // This table MUST match the + static CHL2MPPlayerStateInfo playerStateInfos[] = + { + { STATE_ACTIVE, "STATE_ACTIVE", &CHL2MP_Player::State_Enter_ACTIVE, NULL, &CHL2MP_Player::State_PreThink_ACTIVE }, + { STATE_OBSERVER_MODE, "STATE_OBSERVER_MODE", &CHL2MP_Player::State_Enter_OBSERVER_MODE, NULL, &CHL2MP_Player::State_PreThink_OBSERVER_MODE } + }; + + for ( size_t i=0; i < ARRAYSIZE( playerStateInfos ); i++ ) + { + if ( playerStateInfos[i].m_iPlayerState == state ) + return &playerStateInfos[i]; + } + + return NULL; +} + +bool CHL2MP_Player::StartObserverMode(int mode) +{ + //we only want to go into observer mode if the player asked to, not on a death timeout + if ( m_bEnterObserver == true ) + { + return BaseClass::StartObserverMode( mode ); + } + return false; +} + +void CHL2MP_Player::StopObserverMode() +{ + m_bEnterObserver = false; + BaseClass::StopObserverMode(); +} + +void CHL2MP_Player::State_Enter_OBSERVER_MODE() +{ + int observerMode = m_iObserverLastMode; + if ( IsNetClient() ) + { + const char *pIdealMode = engine->GetClientConVarValue( engine->IndexOfEdict( edict() ), "cl_spec_mode" ); + if ( pIdealMode ) + { + observerMode = atoi( pIdealMode ); + if ( observerMode <= OBS_MODE_FIXED || observerMode > OBS_MODE_ROAMING ) + { + observerMode = m_iObserverLastMode; + } + } + } + m_bEnterObserver = true; + StartObserverMode( observerMode ); +} + +void CHL2MP_Player::State_PreThink_OBSERVER_MODE() +{ + // Make sure nobody has changed any of our state. + // Assert( GetMoveType() == MOVETYPE_FLY ); + Assert( m_takedamage == DAMAGE_NO ); + Assert( IsSolidFlagSet( FSOLID_NOT_SOLID ) ); + // Assert( IsEffectActive( EF_NODRAW ) ); + + // Must be dead. + Assert( m_lifeState == LIFE_DEAD ); + Assert( pl.deadflag ); +} + + +void CHL2MP_Player::State_Enter_ACTIVE() +{ + SetMoveType( MOVETYPE_WALK ); + RemoveSolidFlags( FSOLID_NOT_SOLID ); + m_Local.m_iHideHUD = 0; +} + + +void CHL2MP_Player::State_PreThink_ACTIVE() +{ + //we don't really need to do anything here. + //This state_prethink structure came over from CS:S and was doing an assert check that fails the way hl2dm handles death +} diff --git a/dlls/hltvdirector.cpp b/dlls/hltvdirector.cpp index 5e7847a9..14740bca 100644 --- a/dlls/hltvdirector.cpp +++ b/dlls/hltvdirector.cpp @@ -390,7 +390,7 @@ void CHLTVDirector::StartBestPlayerCameraShot() { shot->SetInt("target1", iBestCamera ); shot->SetInt("target2", iBestTarget ); - shot->SetInt("distance", 112.0f ); + shot->SetInt("distance", 112 ); shot->SetInt("phi", 20 ); // view over shoulder, randomly left or right @@ -431,11 +431,11 @@ void CHLTVDirector::StartFixedCameraShot(int iCamera, int iTarget) if ( shot ) { - shot->SetInt("posx", vCamPos.x ); - shot->SetInt("posy", vCamPos.y ); - shot->SetInt("posz", vCamPos.z ); - shot->SetInt("theta", aViewAngle.x ); - shot->SetInt("phi", aViewAngle.y ); + shot->SetInt("posx", static_cast(vCamPos.x) ); + shot->SetInt("posy", static_cast(vCamPos.y) ); + shot->SetInt("posz", static_cast(vCamPos.z) ); + shot->SetInt("theta", static_cast(aViewAngle.x) ); + shot->SetInt("phi", static_cast(aViewAngle.y) ); shot->SetInt("target", iTarget ); shot->SetFloat("fov", RandomFloat(50,110) ); diff --git a/dlls/item_world.cpp b/dlls/item_world.cpp index 8c603634..2b214846 100644 --- a/dlls/item_world.cpp +++ b/dlls/item_world.cpp @@ -218,6 +218,8 @@ void CItem::OnEntityEvent( EntityEvent_t event, void *pEventData ) SetNextThink( gpGlobals->curtime + 0.1f ); } break; + default: + break; } } diff --git a/dlls/lightglow.cpp b/dlls/lightglow.cpp index 20b30c9f..d5ffdb76 100644 --- a/dlls/lightglow.cpp +++ b/dlls/lightglow.cpp @@ -44,6 +44,7 @@ public: CNetworkVar( float, m_flHDRColorScale ); }; +void SendProxy_Angles( const SendProp *pProp, const void *pStruct, const void *pData, DVariant *pOut, int iElement, int objectID ); IMPLEMENT_SERVERCLASS_ST_NOBASE( CLightGlow, DT_LightGlow ) SendPropInt( SENDINFO(m_clrRender), 32, SPROP_UNSIGNED, SendProxy_Color32ToInt ), diff --git a/dlls/lights.cpp b/dlls/lights.cpp index a19da9c8..ec2be340 100644 --- a/dlls/lights.cpp +++ b/dlls/lights.cpp @@ -218,7 +218,7 @@ void CLight::FadeThink(void) { char sCurString[2]; sCurString[0] = m_iCurrentFade; - sCurString[1] = NULL; + sCurString[1] = 0; engine->LightStyle(m_iStyle, sCurString); // UNDONE: Consider making this settable war to control fade speed diff --git a/dlls/logicentities.cpp b/dlls/logicentities.cpp index d35edd2b..639d180d 100644 --- a/dlls/logicentities.cpp +++ b/dlls/logicentities.cpp @@ -711,10 +711,10 @@ void CMathColorBlend::InputValue( inputdata_t &inputdata ) // Remap the input value to the desired output color and update the output. // color32 Color; - Color.r = m_OutColor1.r + (((flClampValue - m_flInMin) * (m_OutColor2.r - m_OutColor1.r)) / (m_flInMax - m_flInMin)); - Color.g = m_OutColor1.g + (((flClampValue - m_flInMin) * (m_OutColor2.g - m_OutColor1.g)) / (m_flInMax - m_flInMin)); - Color.b = m_OutColor1.b + (((flClampValue - m_flInMin) * (m_OutColor2.b - m_OutColor1.b)) / (m_flInMax - m_flInMin)); - Color.a = m_OutColor1.a + (((flClampValue - m_flInMin) * (m_OutColor2.a - m_OutColor1.a)) / (m_flInMax - m_flInMin)); + Color.r = static_cast(m_OutColor1.r + (((flClampValue - m_flInMin) * (m_OutColor2.r - m_OutColor1.r)) / (m_flInMax - m_flInMin))); + Color.g = static_cast(m_OutColor1.g + (((flClampValue - m_flInMin) * (m_OutColor2.g - m_OutColor1.g)) / (m_flInMax - m_flInMin))); + Color.b = static_cast(m_OutColor1.b + (((flClampValue - m_flInMin) * (m_OutColor2.b - m_OutColor1.b)) / (m_flInMax - m_flInMin))); + Color.a = static_cast(m_OutColor1.a + (((flClampValue - m_flInMin) * (m_OutColor2.a - m_OutColor1.a)) / (m_flInMax - m_flInMin))); m_OutValue.Set(Color, inputdata.pActivator, this); } diff --git a/dlls/movement.cpp b/dlls/movement.cpp index 6dd4d94a..b7c8a7d8 100644 --- a/dlls/movement.cpp +++ b/dlls/movement.cpp @@ -146,9 +146,9 @@ void CPathKeyFrame::CalculateFrameDuration( void ) float x = 0; for ( int i = 0; i < 3; i++ ) { - if ( abs(ang[i]) > x ) + if ( abs(static_cast(ang[i])) > x ) { - x = abs(ang[i]); + x = abs(static_cast(ang[i])); } } diff --git a/dlls/nav.h b/dlls/nav.h index d4a42616..1bd82a31 100644 --- a/dlls/nav.h +++ b/dlls/nav.h @@ -174,6 +174,7 @@ inline NavDirType OppositeDirection( NavDirType dir ) case SOUTH: return NORTH; case EAST: return WEST; case WEST: return EAST; + default: break; } return NORTH; @@ -188,6 +189,7 @@ inline NavDirType DirectionLeft( NavDirType dir ) case SOUTH: return EAST; case EAST: return NORTH; case WEST: return SOUTH; + default: break; } return NORTH; @@ -202,6 +204,7 @@ inline NavDirType DirectionRight( NavDirType dir ) case SOUTH: return WEST; case EAST: return SOUTH; case WEST: return NORTH; + default: break; } return NORTH; @@ -216,6 +219,7 @@ inline void AddDirectionVector( Vector *v, NavDirType dir, float amount ) case SOUTH: v->y += amount; return; case EAST: v->x += amount; return; case WEST: v->x -= amount; return; + default: break; } } @@ -228,6 +232,7 @@ inline float DirectionToAngle( NavDirType dir ) case SOUTH: return 90.0f; case EAST: return 0.0f; case WEST: return 180.0f; + default: break; } return 0.0f; @@ -263,6 +268,7 @@ inline void DirectionToVector2D( NavDirType dir, Vector2D *v ) case SOUTH: v->x = 0.0f; v->y = 1.0f; break; case EAST: v->x = 1.0f; v->y = 0.0f; break; case WEST: v->x = -1.0f; v->y = 0.0f; break; + default: break; } } @@ -276,6 +282,7 @@ inline void CornerToVector2D( NavCornerType dir, Vector2D *v ) case NORTH_EAST: v->x = 1.0f; v->y = -1.0f; break; case SOUTH_EAST: v->x = 1.0f; v->y = 1.0f; break; case SOUTH_WEST: v->x = -1.0f; v->y = 1.0f; break; + default: break; } v->NormalizeInPlace(); diff --git a/dlls/nav_area.cpp b/dlls/nav_area.cpp index 5d3d17fa..df3f01d7 100644 --- a/dlls/nav_area.cpp +++ b/dlls/nav_area.cpp @@ -877,6 +877,8 @@ void CNavArea::FinishSplitEdit( CNavArea *newArea, NavDirType ignoreEdge ) corner[0] = NORTH_WEST; corner[1] = SOUTH_WEST; break; + default: + break; } while ( !newArea->IsOverlapping( *newArea->m_node[ corner[0] ]->GetPosition(), GenerationStepSize/2 ) ) @@ -2221,6 +2223,8 @@ void CNavArea::DrawConnectedAreas( void ) const from = hookPos + Vector( size, 0.0f, 0.0f ); to = hookPos + Vector( -size, 0.0f, 0.0f ); break; + default: + break; } from.z = GetZ( from ); @@ -2504,6 +2508,8 @@ static Vector FindPositionInArea( CNavArea *area, NavCornerType corner ) multX = -1; multY = -1; break; + default: + break; } const float offset = 12.5f; @@ -3027,6 +3033,8 @@ void CNavArea::RaiseCorner( NavCornerType corner, int amount, bool raiseAdjacent case SOUTH_EAST: m_extent.hi.z += amount; break; + default: + break; } // Recompute the center @@ -3083,7 +3091,7 @@ void CNavArea::RaiseCorner( NavCornerType corner, int amount, bool raiseAdjacent if ( areaPos.DistTo( cornerPos ) < tolerance ) { float heightDiff = (cornerPos.z + amount ) - areaPos.z; - area->RaiseCorner( NavCornerType(i), heightDiff, false ); + area->RaiseCorner( NavCornerType(i), static_cast(heightDiff), false ); } } } @@ -3200,25 +3208,25 @@ void CNavArea::PlaceOnGround( NavCornerType corner, float inset ) if ( corner == NORTH_WEST || corner == NUM_CORNERS ) { float newZ = FindGroundZ( nw, ne, sw ); - RaiseCorner( NORTH_WEST, newZ - nw.z ); + RaiseCorner( NORTH_WEST, static_cast(newZ - nw.z) ); } if ( corner == NORTH_EAST || corner == NUM_CORNERS ) { float newZ = FindGroundZ( ne, nw, se ); - RaiseCorner( NORTH_EAST, newZ - ne.z ); + RaiseCorner( NORTH_EAST, static_cast(newZ - ne.z) ); } if ( corner == SOUTH_WEST || corner == NUM_CORNERS ) { float newZ = FindGroundZ( sw, nw, se ); - RaiseCorner( SOUTH_WEST, newZ - sw.z ); + RaiseCorner( SOUTH_WEST, static_cast(newZ - sw.z) ); } if ( corner == SOUTH_EAST || corner == NUM_CORNERS ) { float newZ = FindGroundZ( se, ne, sw ); - RaiseCorner( SOUTH_EAST, newZ - se.z ); + RaiseCorner( SOUTH_EAST, static_cast(newZ - se.z) ); } } @@ -3351,7 +3359,7 @@ public: //----------------------------------------------------------------------------------------------------- -static int GetPushawayEntsInVolume( const Vector& origin, const Vector& mins, const Vector& maxs, CBaseEntity **ents, int nMaxEnts, int PartitionMask, CNavBlockerEnumerator *enumerator ) +/*static int GetPushawayEntsInVolume( const Vector& origin, const Vector& mins, const Vector& maxs, CBaseEntity **ents, int nMaxEnts, int PartitionMask, CNavBlockerEnumerator *enumerator ) { Ray_t ray; ray.Init( origin, origin, mins, maxs ); @@ -3371,7 +3379,7 @@ static int GetPushawayEntsInVolume( const Vector& origin, const Vector& mins, co delete physPropEnum; return numHit; -} +}*/ //-------------------------------------------------------------------------------------------------------------- diff --git a/dlls/nav_colors.h b/dlls/nav_colors.h index 8b0bd643..da1c8d49 100644 --- a/dlls/nav_colors.h +++ b/dlls/nav_colors.h @@ -1,69 +1,69 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -//=============================================================================// - -// Colors used for nav editing - -#ifndef NAV_COLORS_H -#define NAV_COLORS_H - -//-------------------------------------------------------------------------------------------------------------- -enum NavEditColor -{ - // Degenerate area colors - NavDegenerateFirstColor = 0, - NavDegenerateSecondColor, - - // Place painting color - NavSamePlaceColor, - NavDifferentPlaceColor, - NavNoPlaceColor, - - // Normal colors - NavSelectedColor, - NavMarkedColor, - NavNormalColor, - NavCornerColor, - NavBlockedColor, - - // Hiding spot colors - NavIdealSniperColor, - NavGoodSniperColor, - NavGoodCoverColor, - NavExposedColor, - NavApproachPointColor, - - // Connector colors - NavConnectedTwoWaysColor, - NavConnectedOneWayColor, - - // Editing colors - NavCursorColor, - NavSplitLineColor, - NavCreationColor, - NavGridColor, - - // Nav attribute colors - NavAttributeCrouchColor, - NavAttributeJumpColor, - NavAttributePreciseColor, - NavAttributeNoJumpColor, - NavAttributeStopColor, - NavAttributeRunColor, - NavAttributeWalkColor, - NavAttributeAvoidColor, -}; - -//-------------------------------------------------------------------------------------------------------------- - -void NavDrawLine( const Vector& from, const Vector& to, NavEditColor navColor ); -void NavDrawTriangle( const Vector& point1, const Vector& point2, const Vector& point3, NavEditColor navColor ); -void NavDrawHorizontalArrow( const Vector& from, const Vector& to, float width, NavEditColor navColor ); -void NavDrawDashedLine( const Vector& from, const Vector& to, NavEditColor navColor ); - -//-------------------------------------------------------------------------------------------------------------- - -#endif // NAV_COLORS_H \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +// Colors used for nav editing + +#ifndef NAV_COLORS_H +#define NAV_COLORS_H + +//-------------------------------------------------------------------------------------------------------------- +enum NavEditColor +{ + // Degenerate area colors + NavDegenerateFirstColor = 0, + NavDegenerateSecondColor, + + // Place painting color + NavSamePlaceColor, + NavDifferentPlaceColor, + NavNoPlaceColor, + + // Normal colors + NavSelectedColor, + NavMarkedColor, + NavNormalColor, + NavCornerColor, + NavBlockedColor, + + // Hiding spot colors + NavIdealSniperColor, + NavGoodSniperColor, + NavGoodCoverColor, + NavExposedColor, + NavApproachPointColor, + + // Connector colors + NavConnectedTwoWaysColor, + NavConnectedOneWayColor, + + // Editing colors + NavCursorColor, + NavSplitLineColor, + NavCreationColor, + NavGridColor, + + // Nav attribute colors + NavAttributeCrouchColor, + NavAttributeJumpColor, + NavAttributePreciseColor, + NavAttributeNoJumpColor, + NavAttributeStopColor, + NavAttributeRunColor, + NavAttributeWalkColor, + NavAttributeAvoidColor, +}; + +//-------------------------------------------------------------------------------------------------------------- + +void NavDrawLine( const Vector& from, const Vector& to, NavEditColor navColor ); +void NavDrawTriangle( const Vector& point1, const Vector& point2, const Vector& point3, NavEditColor navColor ); +void NavDrawHorizontalArrow( const Vector& from, const Vector& to, float width, NavEditColor navColor ); +void NavDrawDashedLine( const Vector& from, const Vector& to, NavEditColor navColor ); + +//-------------------------------------------------------------------------------------------------------------- + +#endif // NAV_COLORS_H diff --git a/dlls/nav_file.cpp b/dlls/nav_file.cpp index ce342175..75fa8519 100644 --- a/dlls/nav_file.cpp +++ b/dlls/nav_file.cpp @@ -1117,7 +1117,7 @@ static NavErrorType CheckNavFile( const char *bspFilename ) // read file version number unsigned int version; result = filesystem->Read( &version, sizeof(unsigned int), file ); - if (!result || version > NavCurrentVersion || version < 4) + if (!result || version > static_cast(NavCurrentVersion) || version < 4) { filesystem->Close( file ); return NAV_BAD_FILE_VERSION; @@ -1166,6 +1166,8 @@ void CommandNavCheckFileConsistency( void ) case NAV_OK: Msg( "The nav file for %s is up-to-date\n", bspFilename ); break; + default: + break; } bspFilename = filesystem->FindNext( findHandle ); @@ -1218,7 +1220,7 @@ NavErrorType CNavMesh::Load( void ) // read file version number unsigned int version; result = filesystem->Read( &version, sizeof(unsigned int), file ); - if (!result || version > NavCurrentVersion) + if (!result || version > static_cast(NavCurrentVersion)) { Msg( "Unknown navigation file version.\n" ); filesystem->Close( file ); diff --git a/dlls/nav_generate.cpp b/dlls/nav_generate.cpp index d86b03da..3ec7dff4 100644 --- a/dlls/nav_generate.cpp +++ b/dlls/nav_generate.cpp @@ -1895,6 +1895,9 @@ bool CNavMesh::UpdateGeneration( float maxTime ) return false; } + + default: + break; } return false; @@ -2073,16 +2076,16 @@ bool CNavMesh::SampleStep( void ) Vector pos = *m_currentNode->GetPosition(); // snap to grid - int cx = SnapToGrid( pos.x ); - int cy = SnapToGrid( pos.y ); + int cx = static_cast(SnapToGrid( pos.x )); + int cy = static_cast(SnapToGrid( pos.y )); // attempt to move to adjacent node switch( dir ) { - case NORTH: cy -= GenerationStepSize; break; - case SOUTH: cy += GenerationStepSize; break; - case EAST: cx += GenerationStepSize; break; - case WEST: cx -= GenerationStepSize; break; + case NORTH: cy -= static_cast(GenerationStepSize); break; + case SOUTH: cy += static_cast(GenerationStepSize); break; + case EAST: cx += static_cast(GenerationStepSize); break; + case WEST: cx -= static_cast(GenerationStepSize); break; } pos.x = cx; diff --git a/dlls/nav_ladder.cpp b/dlls/nav_ladder.cpp index 38f8d15a..25b90aac 100644 --- a/dlls/nav_ladder.cpp +++ b/dlls/nav_ladder.cpp @@ -93,6 +93,8 @@ CNavArea ** CNavLadder::GetConnection( LadderConnectionType dir ) return &m_topBehindArea; case LADDER_BOTTOM: return &m_bottomArea; + default: + break; } return NULL; diff --git a/dlls/nav_mesh.cpp b/dlls/nav_mesh.cpp index ca5d73e1..4d9ada43 100644 --- a/dlls/nav_mesh.cpp +++ b/dlls/nav_mesh.cpp @@ -502,7 +502,7 @@ CNavArea *CNavMesh::GetNearestNavArea( const Vector &pos, bool anyZ, float maxDi int originX = WorldToGridX( pos.x ); int originY = WorldToGridY( pos.y ); - int shiftLimit = maxDist / m_gridCellSize; + int shiftLimit = static_cast(maxDist / m_gridCellSize); // // Search in increasing rings out from origin, starting with cell diff --git a/dlls/npc_talker.cpp b/dlls/npc_talker.cpp index 5220fdcc..5992b2ad 100644 --- a/dlls/npc_talker.cpp +++ b/dlls/npc_talker.cpp @@ -765,7 +765,7 @@ int CNPCSimpleTalker::PlayScriptedSentence( const char *pszSentence, float delay m_useTime = gpGlobals->curtime + delay; // Stop all idle speech until after the sentence has completed - DeferAllIdleSpeech( delay + random->RandomInt( 3.0f, 5.0f ) ); + DeferAllIdleSpeech( delay + random->RandomFloat( 3.0f, 5.0f ) ); return sentenceIndex; } @@ -1126,7 +1126,7 @@ bool CNPCSimpleTalker::ShouldSpeakRandom( int iChance, float flModifier ) if ( !flModifier ) return false; - iChance = floor( (float)iChance / flModifier ); + iChance = static_cast(floor( (float)iChance / flModifier )); } return (random->RandomInt(0,iChance) == 0); diff --git a/dlls/npc_talker.h b/dlls/npc_talker.h index fc0f1db0..f1fb8524 100644 --- a/dlls/npc_talker.h +++ b/dlls/npc_talker.h @@ -15,7 +15,7 @@ #include #endif -#ifndef _XBOX +#if !defined _XBOX && defined _MSC_VER #pragma warning(push) #include #pragma warning(pop) diff --git a/dlls/npc_vehicledriver.cpp b/dlls/npc_vehicledriver.cpp index d5c02d6b..a6774cf0 100644 --- a/dlls/npc_vehicledriver.cpp +++ b/dlls/npc_vehicledriver.cpp @@ -297,6 +297,8 @@ int CNPC_VehicleDriver::SelectSchedule( void ) return SCHED_VEHICLEDRIVER_COMBAT_WAIT; } break; + default: + break; } return BaseClass::SelectSchedule(); diff --git a/dlls/physics.cpp b/dlls/physics.cpp index 7dc70510..ab293383 100644 --- a/dlls/physics.cpp +++ b/dlls/physics.cpp @@ -506,7 +506,7 @@ static void ReportPenetration( CBaseEntity *pEntity, float duration ) pEntity->m_debugOverlays |= OVERLAY_ABSBOX_BIT; } - pEntity->AddTimedOverlay( UTIL_VarArgs("VPhysics Penetration Error (%s)!", pEntity->GetDebugName()), duration ); + pEntity->AddTimedOverlay( UTIL_VarArgs("VPhysics Penetration Error (%s)!", pEntity->GetDebugName()), static_cast(duration) ); } } @@ -1461,7 +1461,7 @@ friction_t *CCollisionEvent::FindFriction( CBaseEntity *pObject ) { friction_t *pFree = NULL; - for ( int i = 0; i < ARRAYSIZE(m_current); i++ ) + for ( size_t i = 0; i < ARRAYSIZE(m_current); i++ ) { if ( !m_current[i].pObject && !pFree ) pFree = &m_current[i]; @@ -1541,7 +1541,7 @@ float CCollisionEvent::DeltaTimeSinceLastFluid( CBaseEntity *pEntity ) void CCollisionEvent::UpdateFrictionSounds( void ) { - for ( int i = 0; i < ARRAYSIZE(m_current); i++ ) + for ( size_t i = 0; i < ARRAYSIZE(m_current); i++ ) { if ( m_current[i].patch ) { @@ -1875,7 +1875,7 @@ int CCollisionEvent::AddDamageInflictor( IPhysicsObject *pInflictorPhysics, floa void CCollisionEvent::LevelShutdown( void ) { - for ( int i = 0; i < ARRAYSIZE(m_current); i++ ) + for ( size_t i = 0; i < ARRAYSIZE(m_current); i++ ) { if ( m_current[i].patch ) { diff --git a/dlls/physics_cannister.cpp b/dlls/physics_cannister.cpp index a766bae3..d83e6fb8 100644 --- a/dlls/physics_cannister.cpp +++ b/dlls/physics_cannister.cpp @@ -364,7 +364,7 @@ void CPhysicsCannister::Explode( CBaseEntity *pAttacker ) pPhysics->GetVelocity( &velocity, &angVelocity ); PropBreakableCreateAll( GetModelIndex(), pPhysics, GetAbsOrigin(), GetAbsAngles(), velocity, angVelocity, 1.0, 20, COLLISION_GROUP_DEBRIS ); - ExplosionCreate( GetAbsOrigin(), GetAbsAngles(), pAttacker, m_damage, 0, true ); + ExplosionCreate( GetAbsOrigin(), GetAbsAngles(), pAttacker, static_cast(m_damage), 0, true ); UTIL_Remove( this ); } diff --git a/dlls/physics_impact_damage.cpp b/dlls/physics_impact_damage.cpp index 12258d16..90f4a166 100644 --- a/dlls/physics_impact_damage.cpp +++ b/dlls/physics_impact_damage.cpp @@ -467,7 +467,7 @@ float CalculateDefaultPhysicsDamage( int index, gamevcollisionevent_t *pEvent, f // If we have a specified damage table, find it and use it instead if ( iszDamageTableName != NULL_STRING ) { - for ( int i = 0; i < ARRAYSIZE(gDamageTableRegistry); i++ ) + for ( size_t i = 0; i < ARRAYSIZE(gDamageTableRegistry); i++ ) { if ( !Q_strcmp( gDamageTableRegistry[i].pszTableName, STRING(iszDamageTableName) ) ) return CalculatePhysicsImpactDamage( index, pEvent, *(gDamageTableRegistry[i].pTable), energyScale, allowStaticDamage, damageType, bDamageFromHeldObjects ); diff --git a/dlls/physics_main.cpp b/dlls/physics_main.cpp index 5c1fdc1b..0f60d2ea 100644 --- a/dlls/physics_main.cpp +++ b/dlls/physics_main.cpp @@ -578,8 +578,8 @@ void CPhysicsPushedEntities::StoreMovedEntities( physicspushlist_t &list ) list.localOrigin = m_rootPusherStartLocalOrigin; list.localAngles = m_rootPusherStartLocalAngles; list.pushedCount = CountMovedEntities(); - Assert(list.pushedCount < ARRAYSIZE(list.pushedEnts)); - if ( list.pushedCount > ARRAYSIZE(list.pushedEnts) ) + Assert(static_cast(list.pushedCount) < ARRAYSIZE(list.pushedEnts)); + if ( list.pushedCount > static_cast(ARRAYSIZE(list.pushedEnts)) ) { list.pushedCount = ARRAYSIZE(list.pushedEnts); } @@ -1474,7 +1474,7 @@ void CBaseEntity::PerformPush( float movetime ) m_pBlocker = pBlocker; if (m_pBlocker.ToInt() != hPrevBlocker) { - if (hPrevBlocker != INVALID_EHANDLE_INDEX) + if (hPrevBlocker != static_cast(INVALID_EHANDLE_INDEX)) { EndBlocked(); } diff --git a/dlls/physics_prop_ragdoll.cpp b/dlls/physics_prop_ragdoll.cpp index 9b65bc9a..a775cfa5 100644 --- a/dlls/physics_prop_ragdoll.cpp +++ b/dlls/physics_prop_ragdoll.cpp @@ -343,7 +343,7 @@ void CRagdollProp::OnPhysGunPickup( CBasePlayer *pPhysGunUser, PhysGunPickup_t r } else { - CRagdollBoogie::Create( this, 150, gpGlobals->curtime, 2.0f, 0.0f ); + CRagdollBoogie::Create( this, 150, gpGlobals->curtime, 2.0f, 0 ); } } diff --git a/dlls/physobj.cpp b/dlls/physobj.cpp index c5f181ae..af685de7 100644 --- a/dlls/physobj.cpp +++ b/dlls/physobj.cpp @@ -1243,7 +1243,7 @@ void CPhysConvert::InputConvertTarget( inputdata_t &inputdata ) while ( (pEntity = gEntList.FindEntityByName( pEntity, m_target, NULL, inputdata.pActivator, inputdata.pCaller )) != NULL ) { entlist[count++] = pEntity; - if ( count >= ARRAYSIZE(entlist) ) + if ( count >= static_cast(ARRAYSIZE(entlist)) ) break; } diff --git a/dlls/player.cpp b/dlls/player.cpp index 04aaebd0..eaa9547c 100644 --- a/dlls/player.cpp +++ b/dlls/player.cpp @@ -1,8134 +1,8131 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: Functions dealing with the player. -// -//=============================================================================// - -#include "cbase.h" -#include "const.h" -#include "baseplayer_shared.h" -#include "trains.h" -#include "soundent.h" -#include "gib.h" -#include "shake.h" -#include "decals.h" -#include "gamerules.h" -#include "game.h" -#include "entityapi.h" -#include "entitylist.h" -#include "eventqueue.h" -#include "worldsize.h" -#include "isaverestore.h" -#include "globalstate.h" -#include "basecombatweapon.h" -#include "ai_basenpc.h" -#include "ai_network.h" -#include "ai_node.h" -#include "ai_networkmanager.h" -#include "ammodef.h" -#include "mathlib.h" -#include "ndebugoverlay.h" -#include "baseviewmodel.h" -#include "in_buttons.h" -#include "client.h" -#include "team.h" -#include "particle_smokegrenade.h" -#include "IEffects.h" -#include "vstdlib/random.h" -#include "engine/IEngineSound.h" -#include "movehelper_server.h" -#include "igamemovement.h" -#include "saverestoretypes.h" -#include "iservervehicle.h" -#include "movevars_shared.h" -#include "vcollide_parse.h" -#include "player_command.h" -#include "vehicle_base.h" -#include "AI_Criteria.h" -#include "globals.h" -#include "usermessages.h" -#include "gamevars_shared.h" -#include "world.h" -#include "physobj.h" -#include "KeyValues.h" -#include "coordsize.h" -#include "vphysics/player_controller.h" -#include "saverestore_utlvector.h" -#include "hltvdirector.h" -#include "nav_mesh.h" -#include "env_zoom.h" -#include "rumble_shared.h" - -#ifdef HL2_DLL -#include "combine_mine.h" -#include "weapon_physcannon.h" -#endif - -#ifdef HL2_DLL -extern ConVar hl2_xbox_aiming; -#define UseXboxAiming() hl2_xbox_aiming.GetBool() -#else -#define UseXboxAiming() 0 -#endif - -ConVar autoaim_max_dist( "autoaim_max_dist", "2160" ); // 2160 = 180 feet -ConVar autoaim_max_deflect( "autoaim_max_deflect", "0.99" ); - -// memdbgon must be the last include file in a .cpp file!!! -#include "tier0/memdbgon.h" - -static ConVar old_armor( "player_old_armor", "0" ); - -static ConVar physicsshadowupdate_render( "physicsshadowupdate_render", "0" ); -bool IsInCommentaryMode( void ); -bool IsListeningToCommentary( void ); - -// This is declared in the engine, too -ConVar sv_noclipduringpause( "sv_noclipduringpause", "0", FCVAR_REPLICATED | FCVAR_CHEAT, "If cheats are enabled, then you can noclip with the game paused (for doing screenshots, etc.)." ); - -extern ConVar sv_maxunlag; -extern ConVar sv_turbophysics; -extern ConVar *sv_maxreplay; - -// TIME BASED DAMAGE AMOUNT -// tweak these values based on gameplay feedback: -#define PARALYZE_DURATION 2 // number of 2 second intervals to take damage -#define PARALYZE_DAMAGE 1.0 // damage to take each 2 second interval - -#define NERVEGAS_DURATION 2 -#define NERVEGAS_DAMAGE 5.0 - -#define POISON_DURATION 5 -#define POISON_DAMAGE 2.0 - -#define RADIATION_DURATION 2 -#define RADIATION_DAMAGE 1.0 - -#define ACID_DURATION 2 -#define ACID_DAMAGE 5.0 - -#define SLOWBURN_DURATION 2 -#define SLOWBURN_DAMAGE 1.0 - -#define SLOWFREEZE_DURATION 2 -#define SLOWFREEZE_DAMAGE 1.0 - -//---------------------------------------------------- -// Player Physics Shadow -//---------------------------------------------------- -#define VPHYS_MAX_DISTANCE 2.0 -#define VPHYS_MAX_VEL 10 -#define VPHYS_MAX_DISTSQR (VPHYS_MAX_DISTANCE*VPHYS_MAX_DISTANCE) -#define VPHYS_MAX_VELSQR (VPHYS_MAX_VEL*VPHYS_MAX_VEL) - - -extern bool g_fDrawLines; -int gEvilImpulse101; - -bool gInitHUD = true; - -extern void respawn(CBaseEntity *pEdict, bool fCopyCorpse); -int MapTextureTypeStepType(char chTextureType); -extern void SpawnBlood(Vector vecSpot, const Vector &vecDir, int bloodColor, float flDamage); -extern void AddMultiDamage( const CTakeDamageInfo &info, CBaseEntity *pEntity ); - - -#define CMD_MOSTRECENT 0 - -//#define FLASH_DRAIN_TIME 1.2 //100 units/3 minutes -//#define FLASH_CHARGE_TIME 0.2 // 100 units/20 seconds (seconds per unit) - - -//#define PLAYER_MAX_SAFE_FALL_DIST 20// falling any farther than this many feet will inflict damage -//#define PLAYER_FATAL_FALL_DIST 60// 100% damage inflicted if player falls this many feet -//#define DAMAGE_PER_UNIT_FALLEN (float)( 100 ) / ( ( PLAYER_FATAL_FALL_DIST - PLAYER_MAX_SAFE_FALL_DIST ) * 12 ) -//#define MAX_SAFE_FALL_UNITS ( PLAYER_MAX_SAFE_FALL_DIST * 12 ) - -// player damage adjusters -ConVar sk_player_head( "sk_player_head","2" ); -ConVar sk_player_chest( "sk_player_chest","1" ); -ConVar sk_player_stomach( "sk_player_stomach","1" ); -ConVar sk_player_arm( "sk_player_arm","1" ); -ConVar sk_player_leg( "sk_player_leg","1" ); - -void CC_GiveCurrentAmmo( void ) -{ -#ifdef _XBOX - CBasePlayer *pPlayer = UTIL_PlayerByIndex(1); - - if( pPlayer ) - { - CBaseCombatWeapon *pWeapon = pPlayer->GetActiveWeapon(); - - if( pWeapon ) - { - if( pWeapon->UsesPrimaryAmmo() ) - { - int ammoIndex = pWeapon->GetPrimaryAmmoType(); - - if( ammoIndex != -1 ) - { - int giveAmount; - giveAmount = GetAmmoDef()->MaxCarry(ammoIndex); - pPlayer->GiveAmmo( giveAmount, GetAmmoDef()->GetAmmoOfIndex(ammoIndex)->pName ); - } - } - if( pWeapon->UsesSecondaryAmmo() && pWeapon->HasSecondaryAmmo() ) - { - // Give secondary ammo out, as long as the player already has some - // from a presumeably natural source. This prevents players on XBox - // having Combine Balls and so forth in areas of the game that - // were not tested with these items. - int ammoIndex = pWeapon->GetSecondaryAmmoType(); - - if( ammoIndex != -1 ) - { - int giveAmount; - giveAmount = GetAmmoDef()->MaxCarry(ammoIndex); - pPlayer->GiveAmmo( giveAmount, GetAmmoDef()->GetAmmoOfIndex(ammoIndex)->pName ); - } - } - } - } -#endif//_XBOX -} -static ConCommand givecurrentammo("givecurrentammo", CC_GiveCurrentAmmo, "Give a supply of ammo for current weapon..\n", FCVAR_CHEAT ); - - -// pl -BEGIN_SIMPLE_DATADESC( CPlayerState ) - // DEFINE_FIELD( netname, FIELD_STRING ), // Don't stomp player name with what's in save/restore - DEFINE_FIELD( v_angle, FIELD_VECTOR ), - DEFINE_FIELD( deadflag, FIELD_BOOLEAN ), - - // this is always set to true on restore, don't bother saving it. - // DEFINE_FIELD( fixangle, FIELD_INTEGER ), - // DEFINE_FIELD( anglechange, FIELD_FLOAT ), - // DEFINE_FIELD( hltv, FIELD_BOOLEAN ), - // DEFINE_FIELD( frags, FIELD_INTEGER ), - // DEFINE_FIELD( deaths, FIELD_INTEGER ), -END_DATADESC() - -// Global Savedata for player -BEGIN_DATADESC( CBasePlayer ) - - DEFINE_EMBEDDED( m_Local ), - DEFINE_UTLVECTOR( m_hTriggerSoundscapeList, FIELD_EHANDLE ), - DEFINE_EMBEDDED( pl ), - - DEFINE_FIELD( m_StuckLast, FIELD_INTEGER ), - - DEFINE_FIELD( m_nButtons, FIELD_INTEGER ), - DEFINE_FIELD( m_afButtonLast, FIELD_INTEGER ), - DEFINE_FIELD( m_afButtonPressed, FIELD_INTEGER ), - DEFINE_FIELD( m_afButtonReleased, FIELD_INTEGER ), - - DEFINE_FIELD( m_iFOV, FIELD_INTEGER ), - DEFINE_FIELD( m_iDefaultFOV, FIELD_INTEGER ), - - //DEFINE_FIELD( m_fOnTarget, FIELD_BOOLEAN ), // Don't need to restore - DEFINE_FIELD( m_iObserverMode, FIELD_INTEGER ), - DEFINE_FIELD( m_iObserverLastMode, FIELD_INTEGER ), - DEFINE_FIELD( m_hObserverTarget, FIELD_EHANDLE ), - DEFINE_FIELD( m_bForcedObserverMode, FIELD_BOOLEAN ), - DEFINE_AUTO_ARRAY( m_szAnimExtension, FIELD_CHARACTER ), -// DEFINE_CUSTOM_FIELD( m_Activity, ActivityDataOps() ), - - DEFINE_FIELD( m_nUpdateRate, FIELD_INTEGER ), - DEFINE_FIELD( m_fLerpTime, FIELD_FLOAT ), - DEFINE_FIELD( m_bLagCompensation, FIELD_BOOLEAN ), - DEFINE_FIELD( m_bPredictWeapons, FIELD_BOOLEAN ), - - DEFINE_FIELD( m_vecAdditionalPVSOrigin, FIELD_POSITION_VECTOR ), - DEFINE_FIELD( m_vecCameraPVSOrigin, FIELD_POSITION_VECTOR ), - - DEFINE_FIELD( m_hUseEntity, FIELD_EHANDLE ), - DEFINE_FIELD( m_iTrain, FIELD_INTEGER ), - DEFINE_FIELD( m_iRespawnFrames, FIELD_FLOAT ), - DEFINE_FIELD( m_afPhysicsFlags, FIELD_INTEGER ), - DEFINE_FIELD( m_hVehicle, FIELD_EHANDLE ), - - // recreate, don't restore - // DEFINE_FIELD( m_CommandContext, CUtlVector < CCommandContext > ), - //DEFINE_FIELD( m_pPhysicsController, FIELD_POINTER ), - //DEFINE_FIELD( m_pShadowStand, FIELD_POINTER ), - //DEFINE_FIELD( m_pShadowCrouch, FIELD_POINTER ), - //DEFINE_FIELD( m_vphysicsCollisionState, FIELD_INTEGER ), - // DEFINE_FIELD( m_lastNavArea, CNavArea ), - DEFINE_ARRAY( m_szNetworkIDString, FIELD_CHARACTER, MAX_NETWORKID_LENGTH ), - DEFINE_FIELD( m_oldOrigin, FIELD_POSITION_VECTOR ), - DEFINE_FIELD( m_vecSmoothedVelocity, FIELD_VECTOR ), - //DEFINE_FIELD( m_touchedPhysObject, FIELD_BOOLEAN ), - //DEFINE_FIELD( m_iPlayerSound, FIELD_INTEGER ), // Don't restore, set in Precache() - DEFINE_FIELD( m_iTargetVolume, FIELD_INTEGER ), - DEFINE_AUTO_ARRAY( m_rgItems, FIELD_INTEGER ), - //DEFINE_FIELD( m_fNextSuicideTime, FIELD_TIME ), - - DEFINE_FIELD( m_flTimeStepSound, FIELD_TIME ), - DEFINE_FIELD( m_flSwimTime, FIELD_TIME ), - DEFINE_FIELD( m_flDuckTime, FIELD_TIME ), - DEFINE_FIELD( m_flDuckJumpTime, FIELD_TIME ), - - DEFINE_FIELD( m_flSuitUpdate, FIELD_TIME ), - DEFINE_AUTO_ARRAY( m_rgSuitPlayList, FIELD_INTEGER ), - DEFINE_FIELD( m_iSuitPlayNext, FIELD_INTEGER ), - DEFINE_AUTO_ARRAY( m_rgiSuitNoRepeat, FIELD_INTEGER ), - DEFINE_AUTO_ARRAY( m_rgflSuitNoRepeatTime, FIELD_TIME ), - DEFINE_FIELD( m_lastDamageAmount, FIELD_INTEGER ), - DEFINE_FIELD( m_tbdPrev, FIELD_TIME ), - - //DEFINE_FIELD( m_flgeigerRange, FIELD_FLOAT ), // Don't restore, reset in Precache() - //DEFINE_FIELD( m_flgeigerDelay, FIELD_FLOAT ), // Don't restore, reset in Precache() - //DEFINE_FIELD( m_igeigerRangePrev, FIELD_FLOAT ), // Don't restore, reset in Precache() - //DEFINE_FIELD( m_iStepLeft, FIELD_INTEGER ), // Don't need to restore - //DEFINE_FIELD( m_chTextureType, FIELD_CHARACTER ), // Don't need to restore - - DEFINE_FIELD( m_idrowndmg, FIELD_INTEGER ), - DEFINE_FIELD( m_idrownrestored, FIELD_INTEGER ), - - DEFINE_FIELD( m_nPoisonDmg, FIELD_INTEGER ), - DEFINE_FIELD( m_nPoisonRestored, FIELD_INTEGER ), - - DEFINE_FIELD( m_bitsHUDDamage, FIELD_INTEGER ), - DEFINE_FIELD( m_fInitHUD, FIELD_BOOLEAN ), - DEFINE_FIELD( m_flDeathTime, FIELD_TIME ), - - //DEFINE_FIELD( m_fGameHUDInitialized, FIELD_BOOLEAN ), // only used in multiplayer games - //DEFINE_FIELD( m_fWeapon, FIELD_BOOLEAN ), // Don't restore, client needs reset - //DEFINE_FIELD( m_iUpdateTime, FIELD_INTEGER ), // Don't need to restore - //DEFINE_FIELD( m_iClientBattery, FIELD_INTEGER ), // Don't restore, client needs reset - //DEFINE_FIELD( m_iClientHideHUD, FIELD_INTEGER ), // Don't restore, client needs reset - //DEFINE_FIELD( m_vecAutoAim, FIELD_VECTOR ), // Don't save/restore - this is recomputed - //DEFINE_FIELD( m_lastx, FIELD_INTEGER ), - //DEFINE_FIELD( m_lasty, FIELD_INTEGER ), - - DEFINE_FIELD( m_iFrags, FIELD_INTEGER ), - DEFINE_FIELD( m_iDeaths, FIELD_INTEGER ), - DEFINE_FIELD( m_flNextDecalTime, FIELD_TIME ), - //DEFINE_AUTO_ARRAY( m_szTeamName, FIELD_STRING ), // mp - - //DEFINE_FIELD( m_iConnected, FIELD_INTEGER ), - // from edict_t - DEFINE_FIELD( m_ArmorValue, FIELD_INTEGER ), - DEFINE_FIELD( m_DmgOrigin, FIELD_VECTOR ), - DEFINE_FIELD( m_DmgTake, FIELD_FLOAT ), - DEFINE_FIELD( m_DmgSave, FIELD_FLOAT ), - DEFINE_FIELD( m_AirFinished, FIELD_TIME ), - DEFINE_FIELD( m_PainFinished, FIELD_TIME ), - - DEFINE_FIELD( m_iPlayerLocked, FIELD_INTEGER ), - - DEFINE_AUTO_ARRAY( m_hViewModel, FIELD_EHANDLE ), - - DEFINE_FIELD( m_flMaxspeed, FIELD_FLOAT ), - DEFINE_FIELD( m_flWaterJumpTime, FIELD_TIME ), - DEFINE_FIELD( m_vecWaterJumpVel, FIELD_VECTOR ), - DEFINE_FIELD( m_nImpulse, FIELD_INTEGER ), - DEFINE_FIELD( m_flStepSoundTime, FIELD_TIME ), - DEFINE_FIELD( m_flSwimSoundTime, FIELD_TIME ), - DEFINE_FIELD( m_vecLadderNormal, FIELD_VECTOR ), - - DEFINE_FIELD( m_flFlashTime, FIELD_TIME ), - DEFINE_FIELD( m_nDrownDmgRate, FIELD_INTEGER ), - - // NOT SAVED - //DEFINE_FIELD( m_vForcedOrigin, FIELD_VECTOR ), - //DEFINE_FIELD( m_bForceOrigin, FIELD_BOOLEAN ), - //DEFINE_FIELD( m_nTickBase, FIELD_INTEGER ), - //DEFINE_FIELD( m_LastCmd, FIELD_ ), - // DEFINE_FIELD( m_pCurrentCommand, CUserCmd ), - //DEFINE_FIELD( m_bGamePaused, FIELD_BOOLEAN ), - - DEFINE_FIELD( m_bitsDamageType, FIELD_INTEGER ), - DEFINE_AUTO_ARRAY( m_rgbTimeBasedDamage, FIELD_CHARACTER ), - DEFINE_FIELD( m_fLastPlayerTalkTime, FIELD_FLOAT ), - DEFINE_FIELD( m_hLastWeapon, FIELD_EHANDLE ), - -#if !defined( NO_ENTITY_PREDICTION ) - // DEFINE_FIELD( m_SimulatedByThisPlayer, CUtlVector < CHandle < CBaseEntity > > ), -#endif - - DEFINE_FIELD( m_flOldPlayerZ, FIELD_FLOAT ), - DEFINE_FIELD( m_flOldPlayerViewOffsetZ, FIELD_FLOAT ), - DEFINE_FIELD( m_bPlayerUnderwater, FIELD_BOOLEAN ), - DEFINE_FIELD( m_hViewEntity, FIELD_EHANDLE ), - - DEFINE_FIELD( m_hConstraintEntity, FIELD_EHANDLE ), - DEFINE_FIELD( m_vecConstraintCenter, FIELD_VECTOR ), - DEFINE_FIELD( m_flConstraintRadius, FIELD_FLOAT ), - DEFINE_FIELD( m_flConstraintWidth, FIELD_FLOAT ), - DEFINE_FIELD( m_flConstraintSpeedFactor, FIELD_FLOAT ), - DEFINE_FIELD( m_hZoomOwner, FIELD_EHANDLE ), - - DEFINE_FIELD( m_flLaggedMovementValue, FIELD_FLOAT ), - - DEFINE_FIELD( m_vNewVPhysicsPosition, FIELD_VECTOR ), - DEFINE_FIELD( m_vNewVPhysicsVelocity, FIELD_VECTOR ), - - DEFINE_FIELD( m_bSinglePlayerGameEnding, FIELD_BOOLEAN ), - - // Function Pointers - DEFINE_FUNCTION( PlayerDeathThink ), - - // Inputs - DEFINE_INPUTFUNC( FIELD_INTEGER, "SetHealth", InputSetHealth ), - DEFINE_INPUTFUNC( FIELD_BOOLEAN, "SetHUDVisibility", InputSetHUDVisibility ), - - DEFINE_FIELD( m_nNumCrouches, FIELD_INTEGER ), - DEFINE_FIELD( m_bDuckToggled, FIELD_BOOLEAN ), - - DEFINE_FIELD( m_nNumCrateHudHints, FIELD_INTEGER ), - -END_DATADESC() - -int giPrecacheGrunt = 0; - -edict_t *CBasePlayer::s_PlayerEdict = NULL; - - -inline bool ShouldRunCommandsInContext( const CCommandContext *ctx ) -{ - // TODO: This should be enabled at some point. If usercmds can run while paused, then - // they can create entities which will never die and it will fill up the entity list. -#ifdef NO_USERCMDS_DURING_PAUSE - return !ctx->paused || sv_noclipduringpause.GetInt(); -#else - return true; -#endif -} - - -//----------------------------------------------------------------------------- -// Purpose: -// Output : CBaseViewModel -//----------------------------------------------------------------------------- -CBaseViewModel *CBasePlayer::GetViewModel( int index /*= 0*/ ) -{ - Assert( index >= 0 && index < MAX_VIEWMODELS ); - return m_hViewModel[ index ].Get(); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CBasePlayer::CreateViewModel( int index /*=0*/ ) -{ - Assert( index >= 0 && index < MAX_VIEWMODELS ); - - if ( GetViewModel( index ) ) - return; - - CBaseViewModel *vm = ( CBaseViewModel * )CreateEntityByName( "viewmodel" ); - if ( vm ) - { - vm->SetAbsOrigin( GetAbsOrigin() ); - vm->SetOwner( this ); - vm->SetIndex( index ); - DispatchSpawn( vm ); - vm->FollowEntity( this ); - m_hViewModel.Set( index, vm ); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CBasePlayer::DestroyViewModels( void ) -{ - int i; - for ( i = MAX_VIEWMODELS - 1; i >= 0; i-- ) - { - CBaseViewModel *vm = GetViewModel( i ); - if ( !vm ) - continue; - - UTIL_Remove( vm ); - m_hViewModel.Set( i, NULL ); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Static member function to create a player of the specified class -// Input : *className - -// *ed - -// Output : CBasePlayer -//----------------------------------------------------------------------------- -CBasePlayer *CBasePlayer::CreatePlayer( const char *className, edict_t *ed ) -{ - CBasePlayer *player; - CBasePlayer::s_PlayerEdict = ed; - player = ( CBasePlayer * )CreateEntityByName( className ); - return player; -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : -// Output : -//----------------------------------------------------------------------------- -CBasePlayer::CBasePlayer( ) -{ - AddEFlags( EFL_NO_AUTO_EDICT_ATTACH ); - -#ifdef _DEBUG - m_vecAutoAim.Init(); - m_vecAdditionalPVSOrigin.Init(); - m_vecCameraPVSOrigin.Init(); - m_DmgOrigin.Init(); - m_vecLadderNormal.Init(); - - m_oldOrigin.Init(); - m_vecSmoothedVelocity.Init(); -#endif - - if ( s_PlayerEdict ) - { - // take the assigned edict_t and attach it - Assert( s_PlayerEdict != NULL ); - NetworkProp()->AttachEdict( s_PlayerEdict ); - s_PlayerEdict = NULL; - } - - m_flFlashTime = -1; - pl.fixangle = FIXANGLE_ABSOLUTE; - pl.hltv = false; - pl.frags = 0; - pl.deaths = 0; - - m_szNetname[0] = '\0'; - - m_iHealth = 0; - Weapon_SetLast( NULL ); - m_bitsDamageType = 0; - - m_bForceOrigin = false; - m_hVehicle = NULL; - m_pCurrentCommand = NULL; - - // Setup our default FOV - m_iDefaultFOV = g_pGameRules->DefaultFOV(); - - m_hZoomOwner = NULL; - - m_nUpdateRate = 20; // cl_updaterate defualt - m_fLerpTime = 0.1f; // cl_interp default - m_bPredictWeapons = true; - m_bLagCompensation = false; - m_flLaggedMovementValue = 1.0f; - m_StuckLast = 0; - m_impactEnergyScale = 1.0f; - m_fLastPlayerTalkTime = 0.0f; - m_PlayerInfo.SetParent( this ); - - ResetObserverMode(); - - m_surfaceProps = 0; - m_pSurfaceData = NULL; - m_surfaceFriction = 1.0f; - m_chTextureType = 0; - m_chPreviousTextureType = 0; - - m_fDelay = 0.0f; - m_fReplayEnd = -1; - m_iReplayEntity = 0; - - m_autoKickDisabled = false; - - m_nNumCrouches = 0; - m_bDuckToggled = false; -} - -CBasePlayer::~CBasePlayer( ) -{ - VPhysicsDestroyObject(); -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : -// Output : -//----------------------------------------------------------------------------- -void CBasePlayer::UpdateOnRemove( void ) -{ - VPhysicsDestroyObject(); - - // Remove him from his current team - if ( GetTeam() ) - { - GetTeam()->RemovePlayer( this ); - } - - // Chain at end to mimic destructor unwind order - BaseClass::UpdateOnRemove(); -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : **pvs - -// **pas - -//----------------------------------------------------------------------------- -void CBasePlayer::SetupVisibility( CBaseEntity *pViewEntity, unsigned char *pvs, int pvssize ) -{ - // If we have a viewentity, we don't add the player's origin. - if ( pViewEntity ) - return; - - Vector org; - org = EyePosition(); - - engine->AddOriginToPVS( org ); -} - -int CBasePlayer::UpdateTransmitState() -{ - // always call ShouldTransmit() for players - return SetTransmitState( FL_EDICT_FULLCHECK ); -} - -int CBasePlayer::ShouldTransmit( const CCheckTransmitInfo *pInfo ) -{ - // Allow me to introduce myself to, err, myself. - // I.e., always update the recipient player data even if it's nodraw (first person mode) - if ( pInfo->m_pClientEnt == edict() ) - { - return FL_EDICT_ALWAYS; - } - - // when HLTV is connected and spectators press +USE, they - // signal that they are recording a interesting scene - // so transmit these 'cameramans' to the HLTV client - if ( HLTVDirector()->GetCameraMan() == entindex() ) - { - CBaseEntity *pRecipientEntity = CBaseEntity::Instance( pInfo->m_pClientEnt ); - - Assert( pRecipientEntity->IsPlayer() ); - - CBasePlayer *pRecipientPlayer = static_cast( pRecipientEntity ); - if ( pRecipientPlayer->IsHLTV() ) - { - // HACK force calling RecomputePVSInformation to update PVS data - NetworkProp()->AreaNum(); - return FL_EDICT_ALWAYS; - } - } - - // Transmit for a short time after death so ragdolls can access reliable player data - if ( IsEffectActive( EF_NODRAW ) || ( IsObserver() && ( gpGlobals->curtime - m_flDeathTime > 0.5 ) ) ) - { - return FL_EDICT_DONTSEND; - } - - return BaseClass::ShouldTransmit( pInfo ); -} - - -bool CBasePlayer::WantsLagCompensationOnEntity( const CBasePlayer *pPlayer, const CUserCmd *pCmd, const CBitVec *pEntityTransmitBits ) const -{ - // Team members shouldn't be adjusted unless friendly fire is on. - if ( !friendlyfire.GetInt() && pPlayer->GetTeamNumber() == GetTeamNumber() ) - return false; - - // If this entity hasn't been transmitted to us and acked, then don't bother lag compensating it. - if ( pEntityTransmitBits && !pEntityTransmitBits->Get( pPlayer->entindex() ) ) - return false; - - const Vector &vMyOrigin = GetAbsOrigin(); - const Vector &vHisOrigin = pPlayer->GetAbsOrigin(); - - // get max distance player could have moved within max lag compensation time, - // multiply by 1.5 to to avoid "dead zones" (sqrt(2) would be the exact value) - float maxDistance = 1.5 * pPlayer->MaxSpeed() * sv_maxunlag.GetFloat(); - - // If the player is within this distance, lag compensate them in case they're running past us. - if ( vHisOrigin.DistTo( vMyOrigin ) < maxDistance ) - return true; - - // If their origin is not within a 45 degree cone in front of us, no need to lag compensate. - Vector vForward; - AngleVectors( pCmd->viewangles, &vForward ); - - Vector vDiff = vHisOrigin - vMyOrigin; - VectorNormalize( vDiff ); - - float flCosAngle = 0.707107f; // 45 degree angle - if ( vForward.Dot( vDiff ) < flCosAngle ) - return false; - - return true; -} - - -//----------------------------------------------------------------------------- -// Sets the view angles -//----------------------------------------------------------------------------- -void CBasePlayer::SnapEyeAngles( const QAngle &viewAngles ) -{ - pl.v_angle = viewAngles; - pl.fixangle = FIXANGLE_ABSOLUTE; -} - - -//----------------------------------------------------------------------------- -// Purpose: -// Input : iSpeed - -// iMax - -// Output : int -//----------------------------------------------------------------------------- -int TrainSpeed(int iSpeed, int iMax) -{ - float fSpeed, fMax; - int iRet = 0; - - fMax = (float)iMax; - fSpeed = iSpeed; - - fSpeed = fSpeed/fMax; - - if (iSpeed < 0) - iRet = TRAIN_BACK; - else if (iSpeed == 0) - iRet = TRAIN_NEUTRAL; - else if (fSpeed < 0.33) - iRet = TRAIN_SLOW; - else if (fSpeed < 0.66) - iRet = TRAIN_MEDIUM; - else - iRet = TRAIN_FAST; - - return iRet; -} - -void CBasePlayer::DeathSound( const CTakeDamageInfo &info ) -{ - // temporarily using pain sounds for death sounds - - // Did we die from falling? - if ( m_bitsDamageType & DMG_FALL ) - { - // They died in the fall. Play a splat sound. - EmitSound( "Player.FallGib" ); - } - else - { - EmitSound( "Player.Death" ); - } - - // play one of the suit death alarms - if ( IsSuitEquipped() ) - { - UTIL_EmitGroupnameSuit(edict(), "HEV_DEAD"); - } -} - -// override takehealth -// bitsDamageType indicates type of damage healed. - -int CBasePlayer::TakeHealth( float flHealth, int bitsDamageType ) -{ - // clear out any damage types we healed. - // UNDONE: generic health should not heal any - // UNDONE: time-based damage - if (m_takedamage) - { - m_bitsDamageType &= ~(bitsDamageType & ~DMG_TIMEBASED); - } - - return BaseClass::TakeHealth (flHealth, bitsDamageType); -} - - -//----------------------------------------------------------------------------- -// Purpose: Draw all overlays (should be implemented in cascade by subclass to add -// any additional non-text overlays) -// Input : -// Output : Current text offset from the top -//----------------------------------------------------------------------------- -void CBasePlayer::DrawDebugGeometryOverlays(void) -{ - // -------------------------------------------------------- - // If in buddha mode and dead draw lines to indicate death - // -------------------------------------------------------- - if ((m_debugOverlays & OVERLAY_BUDDHA_MODE) && m_iHealth == 1) - { - Vector vBodyDir = BodyDirection2D( ); - Vector eyePos = EyePosition() + vBodyDir*10.0; - Vector vUp = Vector(0,0,8); - Vector vSide; - CrossProduct( vBodyDir, vUp, vSide); - NDebugOverlay::Line(eyePos+vSide+vUp, eyePos-vSide-vUp, 255,0,0, false, 0); - NDebugOverlay::Line(eyePos+vSide-vUp, eyePos-vSide+vUp, 255,0,0, false, 0); - } - BaseClass::DrawDebugGeometryOverlays(); -} - -//========================================================= -// TraceAttack -//========================================================= -void CBasePlayer::TraceAttack( const CTakeDamageInfo &inputInfo, const Vector &vecDir, trace_t *ptr ) -{ - if ( m_takedamage ) - { - CTakeDamageInfo info = inputInfo; - - // -------------------------------------------------- - // If an NPC check if friendly fire is disallowed - // -------------------------------------------------- - CAI_BaseNPC *pNPC = info.GetAttacker()->MyNPCPointer(); - if ( pNPC && (pNPC->CapabilitiesGet() & bits_CAP_NO_HIT_PLAYER) && pNPC->IRelationType( this ) != D_HT ) - { - return; - } - - // Prevent team damage here so blood doesn't appear - if ( info.GetAttacker()->IsPlayer() ) - { - if ( !g_pGameRules->FPlayerCanTakeDamage( this, info.GetAttacker() ) ) - return; - } - - SetLastHitGroup( ptr->hitgroup ); - - - switch ( ptr->hitgroup ) - { - case HITGROUP_GENERIC: - break; - case HITGROUP_HEAD: - info.ScaleDamage( sk_player_head.GetFloat() ); - break; - case HITGROUP_CHEST: - info.ScaleDamage( sk_player_chest.GetFloat() ); - break; - case HITGROUP_STOMACH: - info.ScaleDamage( sk_player_stomach.GetFloat() ); - break; - case HITGROUP_LEFTARM: - case HITGROUP_RIGHTARM: - info.ScaleDamage( sk_player_arm.GetFloat() ); - break; - case HITGROUP_LEFTLEG: - case HITGROUP_RIGHTLEG: - info.ScaleDamage( sk_player_leg.GetFloat() ); - break; - default: - break; - } - - SpawnBlood(ptr->endpos, vecDir, BloodColor(), info.GetDamage());// a little surface blood. - TraceBleed( info.GetDamage(), vecDir, ptr, info.GetDamageType() ); - AddMultiDamage( info, this ); - } -} - -//------------------------------------------------------------------------------ -// Purpose : Do some kind of damage effect for the type of damage -// Input : -// Output : -//------------------------------------------------------------------------------ -void CBasePlayer::DamageEffect(float flDamage, int fDamageType) -{ - if (fDamageType & DMG_CRUSH) - { - //Red damage indicator - color32 red = {128,0,0,128}; - UTIL_ScreenFade( this, red, 1.0f, 0.1f, FFADE_IN ); - } - else if (fDamageType & DMG_DROWN) - { - //Red damage indicator - color32 blue = {0,0,128,128}; - UTIL_ScreenFade( this, blue, 1.0f, 0.1f, FFADE_IN ); - } - else if (fDamageType & DMG_SLASH) - { - // If slash damage shoot some blood - SpawnBlood(EyePosition(), g_vecAttackDir, BloodColor(), flDamage); - } - else if (fDamageType & DMG_PLASMA) - { - // Blue screen fade - color32 blue = {0,0,255,100}; - UTIL_ScreenFade( this, blue, 0.2, 0.4, FFADE_MODULATE ); - - // Very small screen shake - ViewPunch(QAngle(random->RandomInt(-0.1,0.1), random->RandomInt(-0.1,0.1), random->RandomInt(-0.1,0.1))); - - // Burn sound - EmitSound( "Player.PlasmaDamage" ); - } - else if (fDamageType & DMG_SONIC) - { - // Sonic damage sound - EmitSound( "Player.SonicDamage" ); - } - else if ( fDamageType & DMG_BULLET ) - { - EmitSound( "Flesh.BulletImpact" ); - } -} - -/* - Take some damage. - NOTE: each call to OnTakeDamage with bitsDamageType set to a time-based damage - type will cause the damage time countdown to be reset. Thus the ongoing effects of poison, radiation - etc are implemented with subsequent calls to OnTakeDamage using DMG_GENERIC. -*/ - -// Old values -#define OLD_ARMOR_RATIO 0.2 // Armor Takes 80% of the damage -#define OLD_ARMOR_BONUS 0.5 // Each Point of Armor is work 1/x points of health - -// New values -#define ARMOR_RATIO 0.2 -#define ARMOR_BONUS 1.0 - -//--------------------------------------------------------- -//--------------------------------------------------------- -bool CBasePlayer::ShouldTakeDamageInCommentaryMode( const CTakeDamageInfo &inputInfo ) -{ - // Only ignore damage when we're listening to a commentary node - if ( !IsListeningToCommentary() ) - return true; - - // Allow SetHealth inputs to kill player. - if ( inputInfo.GetInflictor() == this && inputInfo.GetAttacker() == this ) - return true; - - // In commentary, ignore all damage except for falling and leeches - if ( !(inputInfo.GetDamageType() & (DMG_BURN | DMG_PLASMA | DMG_FALL | DMG_CRUSH)) && inputInfo.GetDamageType() != DMG_GENERIC ) - return false; - - // We let DMG_CRUSH pass the check above so that we can check here for stress damage. Deny the CRUSH damage if there is no attacker, - // or if the attacker isn't a BSP model. Therefore, we're allowing any CRUSH damage done by a BSP model. - if ( (inputInfo.GetDamageType() & DMG_CRUSH) && ( inputInfo.GetAttacker() == NULL || !inputInfo.GetAttacker()->IsBSPModel() ) ) - return false; - - return true; -} - -int CBasePlayer::OnTakeDamage( const CTakeDamageInfo &inputInfo ) -{ - // have suit diagnose the problem - ie: report damage type - int bitsDamage = inputInfo.GetDamageType(); - int ffound = true; - int fmajor; - int fcritical; - int fTookDamage; - int ftrivial; - float flRatio; - float flBonus; - float flHealthPrev = m_iHealth; - - CTakeDamageInfo info = inputInfo; - - IServerVehicle *pVehicle = GetVehicle(); - if ( pVehicle ) - { - // Players don't take blast or radiation damage while in vehicles. - // The vehicle has to deal it to him - if ( info.GetDamageType() & (DMG_BLAST|DMG_RADIATION) ) - return 0; - - info.ScaleDamage(pVehicle->DamageModifier(info)); - } - - if ( IsInCommentaryMode() ) - { - if( !ShouldTakeDamageInCommentaryMode( info ) ) - return 0; - } - - if ( GetFlags() & FL_GODMODE ) - return 0; - - if ( m_debugOverlays & OVERLAY_BUDDHA_MODE ) - { - if ((m_iHealth - info.GetDamage()) <= 0) - { - m_iHealth = 1; - return 0; - } - } - - // Early out if there's no damage - if ( !info.GetDamage() ) - return 0; - - if( old_armor.GetBool() ) - { - flBonus = OLD_ARMOR_BONUS; - flRatio = OLD_ARMOR_RATIO; - } - else - { - flBonus = ARMOR_BONUS; - flRatio = ARMOR_RATIO; - } - - if ( ( info.GetDamageType() & DMG_BLAST ) && g_pGameRules->IsMultiplayer() ) - { - // blasts damage armor more. - flBonus *= 2; - } - - // Already dead - if ( !IsAlive() ) - return 0; - // go take the damage first - - - if ( !g_pGameRules->FPlayerCanTakeDamage( this, info.GetAttacker() ) ) - { - // Refuse the damage - return 0; - } - - // keep track of amount of damage last sustained - m_lastDamageAmount = info.GetDamage(); - - // Armor. - if (m_ArmorValue && !(info.GetDamageType() & (DMG_FALL | DMG_DROWN | DMG_POISON | DMG_RADIATION)) )// armor doesn't protect against fall or drown damage! - { - float flNew = info.GetDamage() * flRatio; - - float flArmor; - - flArmor = (info.GetDamage() - flNew) * flBonus; - - if( !old_armor.GetBool() ) - { - if( flArmor < 1.0 ) - { - flArmor = 1.0; - } - } - - // Does this use more armor than we have? - if (flArmor > m_ArmorValue) - { - flArmor = m_ArmorValue; - flArmor *= (1/flBonus); - flNew = info.GetDamage() - flArmor; - m_DmgSave = m_ArmorValue; - m_ArmorValue = 0; - } - else - { - m_DmgSave = flArmor; - m_ArmorValue -= flArmor; - } - - info.SetDamage( flNew ); - } - - // this cast to INT is critical!!! If a player ends up with 0.5 health, the engine will get that - // as an int (zero) and think the player is dead! (this will incite a clientside screentilt, etc) - - // NOTENOTE: jdw - We are now capable of retaining the matissa of this damage value and deferring its application - - // info.SetDamage( (int)info.GetDamage() ); - - // Call up to the base class - fTookDamage = BaseClass::OnTakeDamage( info ); - - // Early out if the base class took no damage - if ( !fTookDamage ) - return 0; - - // add to the damage total for clients, which will be sent as a single - // message at the end of the frame - // todo: remove after combining shotgun blasts? - if ( info.GetInflictor() && info.GetInflictor()->edict() ) - m_DmgOrigin = info.GetInflictor()->GetAbsOrigin(); - - m_DmgTake += (int)info.GetDamage(); - - // reset damage time countdown for each type of time based damage player just sustained - - for (int i = 0; i < CDMG_TIMEBASED; i++) - { - if (info.GetDamageType() & (DMG_PARALYZE << i)) - { - m_rgbTimeBasedDamage[i] = 0; - } - } - - // Display any effect associate with this damage type - DamageEffect(info.GetDamage(),bitsDamage); - - // how bad is it, doc? - ftrivial = (m_iHealth > 75 || m_lastDamageAmount < 5); - fmajor = (m_lastDamageAmount > 25); - fcritical = (m_iHealth < 30); - - // handle all bits set in this damage message, - // let the suit give player the diagnosis - - // UNDONE: add sounds for types of damage sustained (ie: burn, shock, slash ) - - // UNDONE: still need to record damage and heal messages for the following types - - // DMG_BURN - // DMG_FREEZE - // DMG_BLAST - // DMG_SHOCK - - m_bitsDamageType |= bitsDamage; // Save this so we can report it to the client - m_bitsHUDDamage = -1; // make sure the damage bits get resent - - while (fTookDamage && (!ftrivial || (bitsDamage & DMG_TIMEBASED)) && ffound && bitsDamage) - { - ffound = false; - - if (bitsDamage & DMG_CLUB) - { - if (fmajor) - SetSuitUpdate("!HEV_DMG4", false, SUIT_NEXT_IN_30SEC); // minor fracture - bitsDamage &= ~DMG_CLUB; - ffound = true; - } - if (bitsDamage & (DMG_FALL | DMG_CRUSH)) - { - if (fmajor) - SetSuitUpdate("!HEV_DMG5", false, SUIT_NEXT_IN_30SEC); // major fracture - else - SetSuitUpdate("!HEV_DMG4", false, SUIT_NEXT_IN_30SEC); // minor fracture - - bitsDamage &= ~(DMG_FALL | DMG_CRUSH); - ffound = true; - } - - if (bitsDamage & DMG_BULLET) - { - if (m_lastDamageAmount > 5) - SetSuitUpdate("!HEV_DMG6", false, SUIT_NEXT_IN_30SEC); // blood loss detected - //else - // SetSuitUpdate("!HEV_DMG0", false, SUIT_NEXT_IN_30SEC); // minor laceration - - bitsDamage &= ~DMG_BULLET; - ffound = true; - } - - if (bitsDamage & DMG_SLASH) - { - if (fmajor) - SetSuitUpdate("!HEV_DMG1", false, SUIT_NEXT_IN_30SEC); // major laceration - else - SetSuitUpdate("!HEV_DMG0", false, SUIT_NEXT_IN_30SEC); // minor laceration - - bitsDamage &= ~DMG_SLASH; - ffound = true; - } - - if (bitsDamage & DMG_SONIC) - { - if (fmajor) - SetSuitUpdate("!HEV_DMG2", false, SUIT_NEXT_IN_1MIN); // internal bleeding - bitsDamage &= ~DMG_SONIC; - ffound = true; - } - - if (bitsDamage & (DMG_POISON | DMG_PARALYZE)) - { - if (bitsDamage & DMG_POISON) - { - m_nPoisonDmg += info.GetDamage(); - m_tbdPrev = gpGlobals->curtime; - m_rgbTimeBasedDamage[itbd_PoisonRecover] = 0; - } - - SetSuitUpdate("!HEV_DMG3", false, SUIT_NEXT_IN_1MIN); // blood toxins detected - bitsDamage &= ~( DMG_POISON | DMG_PARALYZE ); - ffound = true; - } - - if (bitsDamage & DMG_ACID) - { - SetSuitUpdate("!HEV_DET1", false, SUIT_NEXT_IN_1MIN); // hazardous chemicals detected - bitsDamage &= ~DMG_ACID; - ffound = true; - } - - if (bitsDamage & DMG_NERVEGAS) - { - SetSuitUpdate("!HEV_DET0", false, SUIT_NEXT_IN_1MIN); // biohazard detected - bitsDamage &= ~DMG_NERVEGAS; - ffound = true; - } - - if (bitsDamage & DMG_RADIATION) - { - SetSuitUpdate("!HEV_DET2", false, SUIT_NEXT_IN_1MIN); // radiation detected - bitsDamage &= ~DMG_RADIATION; - ffound = true; - } - if (bitsDamage & DMG_SHOCK) - { - bitsDamage &= ~DMG_SHOCK; - ffound = true; - } - } - - m_Local.m_vecPunchAngle.SetX( -2 ); - - if (fTookDamage && !ftrivial && fmajor && flHealthPrev >= 75) - { - // first time we take major damage... - // turn automedic on if not on - SetSuitUpdate("!HEV_MED1", false, SUIT_NEXT_IN_30MIN); // automedic on - - // give morphine shot if not given recently - SetSuitUpdate("!HEV_HEAL7", false, SUIT_NEXT_IN_30MIN); // morphine shot - } - - if (fTookDamage && !ftrivial && fcritical && flHealthPrev < 75) - { - - // already took major damage, now it's critical... - if (m_iHealth < 6) - SetSuitUpdate("!HEV_HLTH3", false, SUIT_NEXT_IN_10MIN); // near death - else if (m_iHealth < 20) - SetSuitUpdate("!HEV_HLTH2", false, SUIT_NEXT_IN_10MIN); // health critical - - // give critical health warnings - if (!random->RandomInt(0,3) && flHealthPrev < 50) - SetSuitUpdate("!HEV_DMG7", false, SUIT_NEXT_IN_5MIN); //seek medical attention - } - - // if we're taking time based damage, warn about its continuing effects - if (fTookDamage && (info.GetDamageType() & DMG_TIMEBASED) && flHealthPrev < 75) - { - if (flHealthPrev < 50) - { - if (!random->RandomInt(0,3)) - SetSuitUpdate("!HEV_DMG7", false, SUIT_NEXT_IN_5MIN); //seek medical attention - } - else - SetSuitUpdate("!HEV_HLTH1", false, SUIT_NEXT_IN_10MIN); // health dropping - } - - // Do special explosion damage effect - if ( bitsDamage & DMG_BLAST ) - { - OnDamagedByExplosion( info ); - } - - return fTookDamage; -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : &info - -// damageAmount - -//----------------------------------------------------------------------------- -#define MIN_SHOCK_AND_CONFUSION_DAMAGE 30.0f -#define MIN_EAR_RINGING_DISTANCE 240.0f // 20 feet - -//----------------------------------------------------------------------------- -// Purpose: -// Input : &info - -//----------------------------------------------------------------------------- -void CBasePlayer::OnDamagedByExplosion( const CTakeDamageInfo &info ) -{ - float lastDamage = info.GetDamage(); - - float distanceFromPlayer = 9999.0f; - - CBaseEntity *inflictor = info.GetInflictor(); - if ( inflictor ) - { - Vector delta = GetAbsOrigin() - inflictor->GetAbsOrigin(); - distanceFromPlayer = delta.Length(); - } - - bool ear_ringing = distanceFromPlayer < MIN_EAR_RINGING_DISTANCE ? true : false; - bool shock = lastDamage >= MIN_SHOCK_AND_CONFUSION_DAMAGE; - - if ( !shock && !ear_ringing ) - return; - - int effect = shock ? - random->RandomInt( 35, 37 ) : - random->RandomInt( 32, 34 ); - - CSingleUserRecipientFilter user( this ); - enginesound->SetPlayerDSP( user, effect, false ); -} - -//========================================================= -// PackDeadPlayerItems - call this when a player dies to -// pack up the appropriate weapons and ammo items, and to -// destroy anything that shouldn't be packed. -// -// This is pretty brute force :( -//========================================================= -void CBasePlayer::PackDeadPlayerItems( void ) -{ - int iWeaponRules; - int iAmmoRules; - int i; - CBaseCombatWeapon *rgpPackWeapons[ 20 ];// 20 hardcoded for now. How to determine exactly how many weapons we have? - int iPackAmmo[ MAX_AMMO_SLOTS + 1]; - int iPW = 0;// index into packweapons array - int iPA = 0;// index into packammo array - - memset(rgpPackWeapons, NULL, sizeof(rgpPackWeapons) ); - memset(iPackAmmo, -1, sizeof(iPackAmmo) ); - - // get the game rules - iWeaponRules = g_pGameRules->DeadPlayerWeapons( this ); - iAmmoRules = g_pGameRules->DeadPlayerAmmo( this ); - - if ( iWeaponRules == GR_PLR_DROP_GUN_NO && iAmmoRules == GR_PLR_DROP_AMMO_NO ) - { - // nothing to pack. Remove the weapons and return. Don't call create on the box! - RemoveAllItems( true ); - return; - } - -// go through all of the weapons and make a list of the ones to pack - for ( i = 0 ; i < WeaponCount() ; i++ ) - { - // there's a weapon here. Should I pack it? - CBaseCombatWeapon *pPlayerItem = GetWeapon( i ); - if ( pPlayerItem ) - { - switch( iWeaponRules ) - { - case GR_PLR_DROP_GUN_ACTIVE: - if ( GetActiveWeapon() && pPlayerItem == GetActiveWeapon() ) - { - // this is the active item. Pack it. - rgpPackWeapons[ iPW++ ] = pPlayerItem; - } - break; - - case GR_PLR_DROP_GUN_ALL: - rgpPackWeapons[ iPW++ ] = pPlayerItem; - break; - - default: - break; - } - } - } - -// now go through ammo and make a list of which types to pack. - if ( iAmmoRules != GR_PLR_DROP_AMMO_NO ) - { - for ( i = 0 ; i < MAX_AMMO_SLOTS ; i++ ) - { - if ( GetAmmoCount( i ) > 0 ) - { - // player has some ammo of this type. - switch ( iAmmoRules ) - { - case GR_PLR_DROP_AMMO_ALL: - iPackAmmo[ iPA++ ] = i; - break; - - case GR_PLR_DROP_AMMO_ACTIVE: - // WEAPONTODO: Make this work - /* - if ( GetActiveWeapon() && i == GetActiveWeapon()->m_iPrimaryAmmoType ) - { - // this is the primary ammo type for the active weapon - iPackAmmo[ iPA++ ] = i; - } - else if ( GetActiveWeapon() && i == GetActiveWeapon()->m_iSecondaryAmmoType ) - { - // this is the secondary ammo type for the active weapon - iPackAmmo[ iPA++ ] = i; - } - */ - break; - - default: - break; - } - } - } - } - - RemoveAllItems( true );// now strip off everything that wasn't handled by the code above. -} - -void CBasePlayer::RemoveAllItems( bool removeSuit ) -{ - if (GetActiveWeapon()) - { - ResetAutoaim( ); - GetActiveWeapon()->Holster( ); - } - - Weapon_SetLast( NULL ); - RemoveAllWeapons(); - RemoveAllAmmo(); - - if ( removeSuit ) - { - RemoveSuit(); - } - - UpdateClientData(); -} - -bool CBasePlayer::IsDead() const -{ - return m_lifeState == LIFE_DEAD; -} - -static float DamageForce( const Vector &size, float damage ) -{ - float force = damage * ((32 * 32 * 72.0) / (size.x * size.y * size.z)) * 5; - - if ( force > 1000.0) - { - force = 1000.0; - } - - return force; -} - - -int CBasePlayer::OnTakeDamage_Alive( const CTakeDamageInfo &info ) -{ - // set damage type sustained - m_bitsDamageType |= info.GetDamageType(); - - if ( !BaseClass::OnTakeDamage_Alive( info ) ) - return 0; - - CBaseEntity * attacker = info.GetAttacker(); - - if ( !attacker ) - return 0; - - Vector vecDir = vec3_origin; - if ( info.GetInflictor() ) - { - vecDir = info.GetInflictor()->WorldSpaceCenter() - Vector ( 0, 0, 10 ) - WorldSpaceCenter(); - VectorNormalize( vecDir ); - } - - if ( info.GetInflictor() && (GetMoveType() == MOVETYPE_WALK) && - ( !attacker->IsSolidFlagSet(FSOLID_TRIGGER)) ) - { - Vector force = vecDir * -DamageForce( WorldAlignSize(), info.GetBaseDamage() ); - if ( force.z > 250.0f ) - { - force.z = 250.0f; - } - ApplyAbsVelocityImpulse( force ); - } - - // fire global game event - - IGameEvent * event = gameeventmanager->CreateEvent( "player_hurt" ); - if ( event ) - { - event->SetInt("userid", GetUserID() ); - event->SetInt("health", max(0, m_iHealth) ); - event->SetInt("priority", 5 ); // HLTV event priority, not transmitted - - if ( attacker->IsPlayer() ) - { - CBasePlayer *player = ToBasePlayer( attacker ); - event->SetInt("attacker", player->GetUserID() ); // hurt by other player - } - else - { - event->SetInt("attacker", 0 ); // hurt by "world" - } - - gameeventmanager->FireEvent( event ); - } - - // Insert a combat sound so that nearby NPCs hear battle - if ( attacker->IsNPC() ) - { - CSoundEnt::InsertSound( SOUND_COMBAT, GetAbsOrigin(), 512, 0.5, this );//<>//magic number - } - - return 1; -} - - -void CBasePlayer::Event_Killed( const CTakeDamageInfo &info ) -{ - CSound *pSound; - - g_pGameRules->PlayerKilled( this, info ); - - - RumbleEffect( RUMBLE_STOP_ALL, 0, RUMBLE_FLAGS_NONE ); - - ClearUseEntity(); - - // this client isn't going to be thinking for a while, so reset the sound until they respawn - pSound = CSoundEnt::SoundPointerForIndex( CSoundEnt::ClientSoundIndex( edict() ) ); - { - if ( pSound ) - { - pSound->Reset(); - } - } - - // don't let the status bar glitch for players with <0 health. - if (m_iHealth < -99) - { - m_iHealth = 0; - } - - // holster the current weapon - if ( GetActiveWeapon() ) - { - GetActiveWeapon()->Holster(); - } - - SetAnimation( PLAYER_DIE ); - - SetViewOffset( VEC_DEAD_VIEWHEIGHT ); - m_lifeState = LIFE_DYING; - - pl.deadflag = true; - AddSolidFlags( FSOLID_NOT_SOLID ); - // force contact points to get flushed if no longer valid - // UNDONE: Always do this on RecheckCollisionFilter() ? - IPhysicsObject *pObject = VPhysicsGetObject(); - if ( pObject ) - { - pObject->RecheckContactPoints(); - } - - SetMoveType( MOVETYPE_FLYGRAVITY ); - SetGroundEntity( NULL ); - - // clear out the suit message cache so we don't keep chattering - SetSuitUpdate(NULL, false, 0); - - // reset FOV - SetFOV( this, 0 ); - - if ( FlashlightIsOn() ) - { - FlashlightTurnOff(); - } - - m_flDeathTime = gpGlobals->curtime; - - // only count alive players - if (m_lastNavArea) - { - m_lastNavArea->DecrementPlayerCount( GetTeamNumber() ); - m_lastNavArea = NULL; - } - - BaseClass::Event_Killed( info ); -} - -void CBasePlayer::Event_Dying() -{ - // NOT GIBBED, RUN THIS CODE - - CTakeDamageInfo info; - DeathSound( info ); - - // The dead body rolls out of the vehicle. - if ( IsInAVehicle() ) - { - LeaveVehicle(); - } - - QAngle angles = GetLocalAngles(); - - angles.x = 0; - angles.z = 0; - - SetLocalAngles( angles ); - - SetThink(&CBasePlayer::PlayerDeathThink); - SetNextThink( gpGlobals->curtime + 0.1f ); - BaseClass::Event_Dying(); -} - - -// Set the activity based on an event or current state -void CBasePlayer::SetAnimation( PLAYER_ANIM playerAnim ) -{ - int animDesired; - char szAnim[64]; - - float speed; - - speed = GetAbsVelocity().Length2D(); - - if (GetFlags() & (FL_FROZEN|FL_ATCONTROLS)) - { - speed = 0; - playerAnim = PLAYER_IDLE; - } - - Activity idealActivity = ACT_WALK;// TEMP!!!!! - - // This could stand to be redone. Why is playerAnim abstracted from activity? (sjb) - if (playerAnim == PLAYER_JUMP) - { - idealActivity = ACT_HOP; - } - else if (playerAnim == PLAYER_SUPERJUMP) - { - idealActivity = ACT_LEAP; - } - else if (playerAnim == PLAYER_DIE) - { - if ( m_lifeState == LIFE_ALIVE ) - { - idealActivity = GetDeathActivity(); - } - } - else if (playerAnim == PLAYER_ATTACK1) - { - if ( m_Activity == ACT_HOVER || - m_Activity == ACT_SWIM || - m_Activity == ACT_HOP || - m_Activity == ACT_LEAP || - m_Activity == ACT_DIESIMPLE ) - { - idealActivity = m_Activity; - } - else - { - idealActivity = ACT_RANGE_ATTACK1; - } - } - else if (playerAnim == PLAYER_IDLE || playerAnim == PLAYER_WALK) - { - if ( !( GetFlags() & FL_ONGROUND ) && (m_Activity == ACT_HOP || m_Activity == ACT_LEAP) ) // Still jumping - { - idealActivity = m_Activity; - } - else if ( GetWaterLevel() > 1 ) - { - if ( speed == 0 ) - idealActivity = ACT_HOVER; - else - idealActivity = ACT_SWIM; - } - else - { - idealActivity = ACT_WALK; - } - } - - - if (idealActivity == ACT_RANGE_ATTACK1) - { - if ( GetFlags() & FL_DUCKING ) // crouching - { - Q_strncpy( szAnim, "crouch_shoot_" ,sizeof(szAnim)); - } - else - { - Q_strncpy( szAnim, "ref_shoot_" ,sizeof(szAnim)); - } - Q_strncat( szAnim, m_szAnimExtension ,sizeof(szAnim), COPY_ALL_CHARACTERS ); - animDesired = LookupSequence( szAnim ); - if (animDesired == -1) - animDesired = 0; - - if ( GetSequence() != animDesired || !SequenceLoops() ) - { - SetCycle( 0 ); - } - - // Tracker 24588: In single player when firing own weapon this causes eye and punchangle to jitter - //if (!SequenceLoops()) - //{ - // AddEffects( EF_NOINTERP ); - //} - - SetActivity( idealActivity ); - ResetSequence( animDesired ); - } - else if (idealActivity == ACT_WALK) - { - if (GetActivity() != ACT_RANGE_ATTACK1 || IsActivityFinished()) - { - if ( GetFlags() & FL_DUCKING ) // crouching - { - Q_strncpy( szAnim, "crouch_aim_" ,sizeof(szAnim)); - } - else - { - Q_strncpy( szAnim, "ref_aim_" ,sizeof(szAnim)); - } - Q_strncat( szAnim, m_szAnimExtension,sizeof(szAnim), COPY_ALL_CHARACTERS ); - animDesired = LookupSequence( szAnim ); - if (animDesired == -1) - animDesired = 0; - SetActivity( ACT_WALK ); - } - else - { - animDesired = GetSequence(); - } - } - else - { - if ( GetActivity() == idealActivity) - return; - - SetActivity( idealActivity ); - - animDesired = SelectWeightedSequence( m_Activity ); - - // Already using the desired animation? - if (GetSequence() == animDesired) - return; - - ResetSequence( animDesired ); - SetCycle( 0 ); - return; - } - - // Already using the desired animation? - if (GetSequence() == animDesired) - return; - - //Msg( "Set animation to %d\n", animDesired ); - // Reset to first frame of desired animation - ResetSequence( animDesired ); - SetCycle( 0 ); -} - -//----------------------------------------------------------------------------- -// Purpose: data accessor -// ensure that for every emitsound there is a matching stopsound -//----------------------------------------------------------------------------- -void CBasePlayer::SetPlayerUnderwater( bool state ) -{ - if ( m_bPlayerUnderwater != state ) - { - m_bPlayerUnderwater = state; - - if ( state ) - EmitSound( "Player.AmbientUnderWater" ); - else - StopSound( "Player.AmbientUnderWater" ); - } -} - -/* -=========== -WaterMove -============ -*/ -#ifdef HL2_DLL - -// test for HL2 drowning damage increase (aux power used instead) -#define AIRTIME 7 // lung full of air lasts this many seconds -#define DROWNING_DAMAGE_INITIAL 10 -#define DROWNING_DAMAGE_MAX 10 - -#else - -#define AIRTIME 12 // lung full of air lasts this many seconds -#define DROWNING_DAMAGE_INITIAL 2 -#define DROWNING_DAMAGE_MAX 5 - -#endif - -void CBasePlayer::WaterMove() -{ - int air; - - if ( ( GetMoveType() == MOVETYPE_NOCLIP ) && !GetMoveParent() ) - { - m_AirFinished = gpGlobals->curtime + AIRTIME; - return; - } - - if ( m_iHealth < 0 || !IsAlive() ) - { - if ( GetWaterLevel() < WL_Eyes ) - { - if ( IsPlayerUnderwater() ) - { - SetPlayerUnderwater( false ); - } - } - else if ( GetWaterLevel() < WL_Waist ) - { - if ( GetWaterLevel() == 0 ) - { - if ( GetFlags() & FL_INWATER ) - { - RemoveFlag( FL_INWATER ); - } - return; - } - } - else if ( GetWaterLevel() > WL_Waist ) - { - if ( IsPlayerUnderwater() == false ) - { - SetPlayerUnderwater( true ); - } - return; - } - return; - } - - // waterlevel 0 - not in water (WL_NotInWater) - // waterlevel 1 - feet in water (WL_Feet) - // waterlevel 2 - waist in water (WL_Waist) - // waterlevel 3 - head in water (WL_Eyes) - - if (GetWaterLevel() != WL_Eyes || CanBreatheUnderwater()) - { - // not underwater - - // play 'up for air' sound - - if (m_AirFinished < gpGlobals->curtime) - { - EmitSound( "Player.DrownStart" ); - } - - m_AirFinished = gpGlobals->curtime + AIRTIME; - m_nDrownDmgRate = DROWNING_DAMAGE_INITIAL; - - // if we took drowning damage, give it back slowly - if (m_idrowndmg > m_idrownrestored) - { - // set drowning damage bit. hack - dmg_drownrecover actually - // makes the time based damage code 'give back' health over time. - // make sure counter is cleared so we start count correctly. - - // NOTE: this actually causes the count to continue restarting - // until all drowning damage is healed. - - m_bitsDamageType |= DMG_DROWNRECOVER; - m_bitsDamageType &= ~DMG_DROWN; - m_rgbTimeBasedDamage[itbd_DrownRecover] = 0; - } - - } - else - { // fully under water - // stop restoring damage while underwater - m_bitsDamageType &= ~DMG_DROWNRECOVER; - m_rgbTimeBasedDamage[itbd_DrownRecover] = 0; - - if (m_AirFinished < gpGlobals->curtime && !(GetFlags() & FL_GODMODE) ) // drown! - { - if (m_PainFinished < gpGlobals->curtime) - { - // take drowning damage - m_nDrownDmgRate += 1; - if (m_nDrownDmgRate > DROWNING_DAMAGE_MAX) - { - m_nDrownDmgRate = DROWNING_DAMAGE_MAX; - } - - OnTakeDamage( CTakeDamageInfo( GetContainingEntity(INDEXENT(0)), GetContainingEntity(INDEXENT(0)), m_nDrownDmgRate, DMG_DROWN ) ); - m_PainFinished = gpGlobals->curtime + 1; - - // track drowning damage, give it back when - // player finally takes a breath - m_idrowndmg += m_nDrownDmgRate; - } - } - else - { - m_bitsDamageType &= ~DMG_DROWN; - } - } - - if ( GetWaterLevel() < WL_Eyes ) - { - if ( IsPlayerUnderwater() ) - { - SetPlayerUnderwater( false ); - } - } - else if ( GetWaterLevel() < WL_Waist ) - { - if ( GetWaterLevel() == 0 ) - { - if ( GetFlags() & FL_INWATER ) - { - EmitSound( "Player.Wade" ); - RemoveFlag( FL_INWATER ); - } - return; - } - } - else if ( GetWaterLevel() > WL_Waist ) - { - if ( IsPlayerUnderwater() == false ) - { - SetPlayerUnderwater( true ); - } - return; - } - - // make bubbles - - air = (int)( m_AirFinished - gpGlobals->curtime ); - -#if 0 - if (GetWaterType() == CONTENT_LAVA) // do damage - { - if (m_flDamageTime < gpGlobals->curtime) - { - OnTakeDamage( GetContainingEntity(INDEXENT(0)), GetContainingEntity(INDEXENT(0)), 10 * GetWaterLevel(), DMG_BURN); - } - } - else if (GetWaterType() == CONTENT_SLIME) // do damage - { - m_flDamageTime = gpGlobals->curtime + 1; - OnTakeDamage(GetContainingEntity(INDEXENT(0)), GetContainingEntity(INDEXENT(0)), 4 * GetWaterLevel(), DMG_ACID); - } -#endif - - if (!(GetFlags() & FL_INWATER)) - { - // player enter water sound - if (GetWaterType() == CONTENTS_WATER) - { - EmitSound( "Player.Wade" ); - } - - AddFlag( FL_INWATER ); - } -} - - -// true if the player is attached to a ladder -bool CBasePlayer::IsOnLadder( void ) -{ - return (GetMoveType() == MOVETYPE_LADDER); -} - - -float CBasePlayer::GetWaterJumpTime() const -{ - return m_flWaterJumpTime; -} - -void CBasePlayer::SetWaterJumpTime( float flWaterJumpTime ) -{ - m_flWaterJumpTime = flWaterJumpTime; -} - -float CBasePlayer::GetSwimSoundTime( void ) const -{ - return m_flSwimSoundTime; -} - -void CBasePlayer::SetSwimSoundTime( float flSwimSoundTime ) -{ - m_flSwimSoundTime = flSwimSoundTime; -} - -void CBasePlayer::ShowViewPortPanel( const char * name, bool bShow, KeyValues *data ) -{ - CSingleUserRecipientFilter filter( this ); - filter.MakeReliable(); - - int count = 0; - KeyValues *subkey = NULL; - - if ( data ) - { - subkey = data->GetFirstSubKey(); - while ( subkey ) - { - count++; subkey = subkey->GetNextKey(); - } - - subkey = data->GetFirstSubKey(); // reset - } - - UserMessageBegin( filter, "VGUIMenu" ); - WRITE_STRING( name ); // menu name - WRITE_BYTE( bShow?1:0 ); - WRITE_BYTE( count ); - - // write additional data (be carefull not more than 192 bytes!) - while ( subkey ) - { - WRITE_STRING( subkey->GetName() ); - WRITE_STRING( subkey->GetString() ); - subkey = subkey->GetNextKey(); - } - MessageEnd(); -} - - -void CBasePlayer::PlayerDeathThink(void) -{ - float flForward; - - SetNextThink( gpGlobals->curtime + 0.1f ); - - if (GetFlags() & FL_ONGROUND) - { - flForward = GetAbsVelocity().Length() - 20; - if (flForward <= 0) - { - SetAbsVelocity( vec3_origin ); - } - else - { - Vector vecNewVelocity = GetAbsVelocity(); - VectorNormalize( vecNewVelocity ); - vecNewVelocity *= flForward; - SetAbsVelocity( vecNewVelocity ); - } - } - - if ( HasWeapons() ) - { - // we drop the guns here because weapons that have an area effect and can kill their user - // will sometimes crash coming back from CBasePlayer::Killed() if they kill their owner because the - // player class sometimes is freed. It's safer to manipulate the weapons once we know - // we aren't calling into any of their code anymore through the player pointer. - PackDeadPlayerItems(); - } - - if (GetModelIndex() && (!IsSequenceFinished()) && (m_lifeState == LIFE_DYING)) - { - StudioFrameAdvance( ); - - m_iRespawnFrames++; - if ( m_iRespawnFrames < 60 ) // animations should be no longer than this - return; - } - - if (m_lifeState == LIFE_DYING) - m_lifeState = LIFE_DEAD; - - StopAnimation(); - - AddEffects( EF_NOINTERP ); - m_flPlaybackRate = 0.0; - - int fAnyButtonDown = (m_nButtons & ~IN_SCORE); - - // Strip out the duck key from this check if it's toggled - if ( (fAnyButtonDown & IN_DUCK) && GetToggledDuckState()) - { - fAnyButtonDown &= ~IN_DUCK; - } - - // wait for all buttons released - if (m_lifeState == LIFE_DEAD) - { - if (fAnyButtonDown) - return; - - if ( g_pGameRules->FPlayerCanRespawn( this ) ) - { - m_lifeState = LIFE_RESPAWNABLE; - } - - return; - } - -// if the player has been dead for one second longer than allowed by forcerespawn, -// forcerespawn isn't on. Send the player off to an intermission camera until they -// choose to respawn. - if ( g_pGameRules->IsMultiplayer() && ( gpGlobals->curtime > (m_flDeathTime + DEATH_ANIMATION_TIME) ) && !IsObserver() ) - { - // go to dead camera. - StartObserverMode( m_iObserverLastMode ); - } - -// wait for any button down, or mp_forcerespawn is set and the respawn time is up - if (!fAnyButtonDown - && !( g_pGameRules->IsMultiplayer() && forcerespawn.GetInt() > 0 && (gpGlobals->curtime > (m_flDeathTime + 5))) ) - return; - - m_nButtons = 0; - m_iRespawnFrames = 0; - - //Msg( "Respawn\n"); - - respawn( this, !IsObserver() );// don't copy a corpse if we're in deathcam. - SetNextThink( TICK_NEVER_THINK ); -} - -/* - -//========================================================= -// StartDeathCam - find an intermission spot and send the -// player off into observer mode -//========================================================= -void CBasePlayer::StartDeathCam( void ) -{ - CBaseEntity *pSpot, *pNewSpot; - int iRand; - - if ( GetViewOffset() == vec3_origin ) - { - // don't accept subsequent attempts to StartDeathCam() - return; - } - - pSpot = gEntList.FindEntityByClassname( NULL, "info_intermission"); - - if ( pSpot ) - { - // at least one intermission spot in the world. - iRand = random->RandomInt( 0, 3 ); - - while ( iRand > 0 ) - { - pNewSpot = gEntList.FindEntityByClassname( pSpot, "info_intermission"); - - if ( pNewSpot ) - { - pSpot = pNewSpot; - } - - iRand--; - } - - CreateCorpse(); - StartObserverMode( pSpot->GetAbsOrigin(), pSpot->GetAbsAngles() ); - } - else - { - // no intermission spot. Push them up in the air, looking down at their corpse - trace_t tr; - - CreateCorpse(); - - UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() + Vector( 0, 0, 128 ), - MASK_PLAYERSOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr ); - QAngle angles; - VectorAngles( GetAbsOrigin() - tr.endpos, angles ); - StartObserverMode( tr.endpos, angles ); - return; - } -} */ - -void CBasePlayer::StopObserverMode() -{ - m_bForcedObserverMode = false; - m_afPhysicsFlags &= ~PFLAG_OBSERVER; - - if ( m_iObserverMode == OBS_MODE_NONE ) - return; - - if ( m_iObserverMode > OBS_MODE_DEATHCAM ) - { - m_iObserverLastMode = m_iObserverMode; - } - - m_iObserverMode.Set( OBS_MODE_NONE ); - - ShowViewPortPanel( "specmenu", false ); - ShowViewPortPanel( "specgui", false ); - ShowViewPortPanel( "overview", false ); -} - -bool CBasePlayer::StartObserverMode(int mode) -{ - if ( !IsObserver() ) - { - // set position to last view offset - SetAbsOrigin( GetAbsOrigin() + GetViewOffset() ); - SetViewOffset( vec3_origin ); - } - - Assert( mode > OBS_MODE_NONE ); - - m_afPhysicsFlags |= PFLAG_OBSERVER; - - // Holster weapon immediately, to allow it to cleanup - if ( GetActiveWeapon() ) - GetActiveWeapon()->Holster(); - - // clear out the suit message cache so we don't keep chattering - SetSuitUpdate(NULL, FALSE, 0); - - SetGroundEntity( (CBaseEntity *)NULL ); - - RemoveFlag( FL_DUCKING ); - - AddSolidFlags( FSOLID_NOT_SOLID ); - - SetObserverMode( mode ); - - ShowViewPortPanel( "specgui" ); - - // Setup flags - m_Local.m_iHideHUD = HIDEHUD_HEALTH; - m_takedamage = DAMAGE_NO; - - //Don't set the player to EF_NODRAW - the client can determine - //whether to draw the player or not with ShouldDraw - //AddEffects( EF_NODRAW ); - - m_iHealth = 1; - m_lifeState = LIFE_DEAD; // Can't be dead, otherwise movement doesn't work right. - pl.deadflag = true; - - return true; -} - -bool CBasePlayer::SetObserverMode(int mode ) -{ - if ( mode < OBS_MODE_NONE || mode > OBS_MODE_ROAMING ) - return false; - - - // check mp_forcecamera settings for dead players - if ( mode > OBS_MODE_FIXED && GetTeamNumber() > TEAM_SPECTATOR ) - { - switch ( mp_forcecamera.GetInt() ) - { - case OBS_ALLOW_ALL : break; // no restrictions - case OBS_ALLOW_TEAM : mode = OBS_MODE_IN_EYE; break; - case OBS_ALLOW_NONE : mode = OBS_MODE_FIXED; break; // donw't allow anything - } - } - - if ( m_iObserverMode > OBS_MODE_DEATHCAM ) - { - // remember mode if we were really spectating before - m_iObserverLastMode = m_iObserverMode; - } - - m_iObserverMode = mode; - - switch ( mode ) - { - case OBS_MODE_NONE: - case OBS_MODE_FIXED : - case OBS_MODE_DEATHCAM : - SetFOV( this, 0 ); // Reset FOV - SetViewOffset( vec3_origin ); - SetMoveType( MOVETYPE_NONE ); - break; - - case OBS_MODE_CHASE : - case OBS_MODE_IN_EYE : - // udpate FOV and viewmodels - SetObserverTarget( m_hObserverTarget ); - SetMoveType( MOVETYPE_OBSERVER ); - break; - - case OBS_MODE_ROAMING : - SetFOV( this, 0 ); // Reset FOV - SetObserverTarget( m_hObserverTarget ); - SetViewOffset( vec3_origin ); - SetMoveType( MOVETYPE_OBSERVER ); - break; - - } - - CheckObserverSettings(); - - return true; -} - -int CBasePlayer::GetObserverMode() -{ - return m_iObserverMode; -} - -void CBasePlayer::ForceObserverMode(int mode) -{ - int tempMode = OBS_MODE_ROAMING; - - if ( m_iObserverMode == mode ) - return; - - // don't change last mode if already in forced mode - - if ( m_bForcedObserverMode ) - { - tempMode = m_iObserverLastMode; - } - - SetObserverMode( mode ); - - if ( m_bForcedObserverMode ) - { - m_iObserverLastMode = tempMode; - } - - m_bForcedObserverMode = true; -} - -void CBasePlayer::CheckObserverSettings() -{ - // check if we are in forced mode and may go back to old mode - if ( m_bForcedObserverMode ) - { - CBaseEntity * target = m_hObserverTarget; - - if ( !IsValidObserverTarget(target) ) - { - // if old target is still invalid, try to find valid one - target = FindNextObserverTarget( false ); - } - - if ( target ) - { - // we found a valid target - m_bForcedObserverMode = false; // disable force mode - SetObserverMode( m_iObserverLastMode ); // switch to last mode - SetObserverTarget( target ); // goto target - - // TODO check for HUD icons - return; - } - else - { - // else stay in forced mode, no changes - return; - } - } - - // make sure our last mode is valid - if ( m_iObserverLastMode < OBS_MODE_FIXED ) - { - m_iObserverLastMode = OBS_MODE_ROAMING; - } - - // check if our spectating target is still a valid one - - if ( m_iObserverMode == OBS_MODE_IN_EYE || m_iObserverMode == OBS_MODE_CHASE ) - { - if ( !IsValidObserverTarget( m_hObserverTarget.Get() ) ) - { - // our target is not valid, try to find new target - CBaseEntity * target = FindNextObserverTarget( false ); - if ( target ) - { - // switch to new valid target - SetObserverTarget( target ); - } - else - { - // couldn't find new target, switch to temporary mode - if ( mp_forcecamera.GetInt() == OBS_ALLOW_ALL ) - { - // let player roam around - ForceObserverMode( OBS_MODE_ROAMING ); - } - else - { - // fix player view right where it is - ForceObserverMode( OBS_MODE_FIXED ); - m_hObserverTarget.Set( NULL ); // no traget to follow - } - } - } - - CBasePlayer *target = ToBasePlayer( m_hObserverTarget.Get() ); - - // for ineye mode we have to copy several data to see exactly the same - - if ( target && m_iObserverMode == OBS_MODE_IN_EYE ) - { - int flagMask = FL_ONGROUND | FL_DUCKING ; - - int flags = target->GetFlags() & flagMask; - - if ( (GetFlags() & flagMask) != flags ) - { - flags |= GetFlags() & (~flagMask); // keep other flags - ClearFlags(); - AddFlag( flags ); - } - - if ( target->GetViewOffset() != GetViewOffset() ) - { - SetViewOffset( target->GetViewOffset() ); - } - } - } -} - -bool CBasePlayer::StartReplayMode( float fDelay, float fDuration, int iEntity ) -{ - if ( ( sv_maxreplay == NULL ) || ( sv_maxreplay->GetFloat() <= 0 ) ) - return false; - - m_fDelay = fDelay; - m_fReplayEnd = gpGlobals->curtime + fDuration; - m_iReplayEntity = iEntity; - - return true; -} - -void CBasePlayer::StopReplayMode() -{ - m_fDelay = 0.0f; - m_fReplayEnd = -1; - m_iReplayEntity = 0; -} - -int CBasePlayer::GetDelayTicks() -{ - if ( m_fReplayEnd > gpGlobals->curtime ) - { - return TIME_TO_TICKS( m_fDelay ); - } - else - { - if ( m_fDelay > 0.0f ) - StopReplayMode(); - - return 0; - } -} - -int CBasePlayer::GetReplayEntity() -{ - return m_iReplayEntity; -} - -CBaseEntity * CBasePlayer::GetObserverTarget() -{ - return m_hObserverTarget.Get(); -} - -void CBasePlayer::ObserverUse( bool bIsPressed ) -{ -#ifndef _XBOX - if ( !HLTVDirector()->IsActive() ) - return; - - if ( GetTeamNumber() != TEAM_SPECTATOR ) - return; // only pure spectators can play cameraman - - if ( !bIsPressed ) - return; - - int iCameraManIndex = HLTVDirector()->GetCameraMan(); - - if ( iCameraManIndex == 0 ) - { - // turn camera on - HLTVDirector()->SetCameraMan( entindex() ); - } - else if ( iCameraManIndex == entindex() ) - { - // turn camera off - HLTVDirector()->SetCameraMan( 0 ); - } - else - { - ClientPrint( this, HUD_PRINTTALK, "Camera in use by other player." ); - } - - /* UTIL_SayText( "Spectator can not USE anything", this ); - - Vector dir,end; - Vector start = GetAbsOrigin(); - - AngleVectors( GetAbsAngles(), &dir ); - VectorNormalize( dir ); - - VectorMA( start, 32.0f, dir, end ); - - trace_t tr; - UTIL_TraceLine( start, end, MASK_PLAYERSOLID, this, COLLISION_GROUP_PLAYER_MOVEMENT, &tr ); - - if ( tr.fraction == 1.0f ) - return; // no obstacles in spectators way - - VectorMA( start, 128.0f, dir, end ); - - Ray_t ray; - ray.Init( end, start, VEC_DUCK_HULL_MIN, VEC_DUCK_HULL_MAX ); - - UTIL_TraceRay( ray, MASK_PLAYERSOLID, this, COLLISION_GROUP_PLAYER_MOVEMENT, &tr ); - - if ( tr.startsolid || tr.allsolid ) - return; - - SetAbsOrigin( tr.endpos ); */ -#endif -} - -void CBasePlayer::JumptoPosition(const Vector &origin, const QAngle &angles) -{ - SetAbsOrigin( origin ); - SetAbsVelocity( vec3_origin ); // stop movement - SetLocalAngles( angles ); - SnapEyeAngles( angles ); -} - -bool CBasePlayer::SetObserverTarget(CBaseEntity *target) -{ - if ( !IsValidObserverTarget( target ) ) - return false; - - // set new target - m_hObserverTarget.Set( target ); - - // reset fov to default - SetFOV( this, 0 ); - - if ( m_iObserverMode == OBS_MODE_ROAMING ) - { - Vector dir, end; - Vector start = target->EyePosition(); - - AngleVectors( target->EyeAngles(), &dir ); - VectorNormalize( dir ); - VectorMA( start, -64.0f, dir, end ); - - Ray_t ray; - ray.Init( start, end, VEC_DUCK_HULL_MIN , VEC_DUCK_HULL_MAX ); - - trace_t tr; - UTIL_TraceRay( ray, MASK_PLAYERSOLID, target, COLLISION_GROUP_PLAYER_MOVEMENT, &tr ); - - JumptoPosition( tr.endpos, target->EyeAngles() ); - } - - return true; -} - -bool CBasePlayer::IsValidObserverTarget(CBaseEntity * target) -{ - if ( target == NULL ) - return false; - - // MOD AUTHORS: Add checks on target here or in derived methode - - if ( !target->IsPlayer() ) // only track players - return false; - - CBasePlayer * player = ToBasePlayer( target ); - - /* Don't spec observers or players who haven't picked a class yet - if ( player->IsObserver() ) - return false; */ - - if( player == this ) - return false; // We can't observe ourselves. - - if ( player->IsEffectActive( EF_NODRAW ) ) // don't watch invisible players - return false; - - if ( player->m_lifeState == LIFE_RESPAWNABLE ) // target is dead, waiting for respawn - return false; - - if ( player->m_lifeState == LIFE_DEAD || player->m_lifeState == LIFE_DYING ) - { - if ( (player->m_flDeathTime + DEATH_ANIMATION_TIME ) < gpGlobals->curtime ) - { - return false; // allow watching until 3 seconds after death to see death animation - } - } - - // check forcecamera settings for active players - if ( GetTeamNumber() != TEAM_SPECTATOR ) - { - switch ( mp_forcecamera.GetInt() ) - { - case OBS_ALLOW_ALL : break; - case OBS_ALLOW_TEAM : if ( GetTeamNumber() != target->GetTeamNumber() ) - return false; - break; - case OBS_ALLOW_NONE : return false; - } - } - - return true; // passed all test -} - -int CBasePlayer::GetNextObserverSearchStartPoint( bool bReverse ) -{ - int iDir = bReverse ? -1 : 1; - - int startIndex; - - if ( m_hObserverTarget ) - { - // start using last followed player - startIndex = m_hObserverTarget->entindex(); - } - else - { - // start using own player index - startIndex = this->entindex(); - } - - startIndex += iDir; - if (startIndex > gpGlobals->maxClients) - startIndex = 1; - else if (startIndex < 1) - startIndex = gpGlobals->maxClients; - - return startIndex; -} - -CBaseEntity * CBasePlayer::FindNextObserverTarget(bool bReverse) -{ - // MOD AUTHORS: Modify the logic of this function if you want to restrict the observer to watching - // only a subset of the players. e.g. Make it check the target's team. - -/* if ( m_flNextFollowTime && m_flNextFollowTime > gpGlobals->time ) - { - return; - } - - m_flNextFollowTime = gpGlobals->time + 0.25; - */ // TODO move outside this function - - int startIndex = GetNextObserverSearchStartPoint( bReverse ); - - int currentIndex = startIndex; - int iDir = bReverse ? -1 : 1; - - do - { - CBaseEntity * nextTarget = UTIL_PlayerByIndex( currentIndex ); - - if ( IsValidObserverTarget( nextTarget ) ) - { - return nextTarget; // found next valid player - } - - currentIndex += iDir; - - // Loop through the clients - if (currentIndex > gpGlobals->maxClients) - currentIndex = 1; - else if (currentIndex < 1) - currentIndex = gpGlobals->maxClients; - - } while ( currentIndex != startIndex ); - - return NULL; -} - -//----------------------------------------------------------------------------- -// Purpose: Return true if this object can be +used by the player -//----------------------------------------------------------------------------- -bool CBasePlayer::IsUseableEntity( CBaseEntity *pEntity, unsigned int requiredCaps ) -{ - if ( pEntity ) - { - int caps = pEntity->ObjectCaps(); - if ( caps & (FCAP_IMPULSE_USE|FCAP_CONTINUOUS_USE|FCAP_ONOFF_USE|FCAP_DIRECTIONAL_USE) ) - { - if ( (caps & requiredCaps) == requiredCaps ) - { - return true; - } - } - } - - return false; -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -bool CBasePlayer::CanPickupObject( CBaseEntity *pObject, float massLimit, float sizeLimit ) -{ - // UNDONE: Make this virtual and move to HL2 player -#ifdef HL2_DLL - //Must be valid - if ( pObject == NULL ) - return false; - - //Must move with physics - if ( pObject->GetMoveType() != MOVETYPE_VPHYSICS ) - return false; - - IPhysicsObject *pList[VPHYSICS_MAX_OBJECT_LIST_COUNT]; - int count = pObject->VPhysicsGetObjectList( pList, ARRAYSIZE(pList) ); - - //Must have a physics object - if (!count) - return false; - - float objectMass = 0; - bool checkEnable = false; - for ( int i = 0; i < count; i++ ) - { - objectMass += pList[i]->GetMass(); - if ( !pList[i]->IsMoveable() ) - { - checkEnable = true; - } - if ( pList[i]->GetGameFlags() & FVPHYSICS_NO_PLAYER_PICKUP ) - return false; - if ( pList[i]->IsHinged() ) - return false; - } - - - //Msg( "Target mass: %f\n", pPhys->GetMass() ); - - //Must be under our threshold weight - if ( massLimit > 0 && objectMass > massLimit ) - return false; - - if ( checkEnable ) - { - // Allowing picking up of bouncebombs. - CBounceBomb *pBomb = dynamic_cast(pObject); - if( pBomb ) - return true; - - // Allow pickup of phys props that are motion enabled on player pickup - CPhysicsProp *pProp = dynamic_cast(pObject); - CPhysBox *pBox = dynamic_cast(pObject); - if ( !pProp && !pBox ) - return false; - - if ( pProp && !(pProp->HasSpawnFlags( SF_PHYSPROP_ENABLE_ON_PHYSCANNON )) ) - return false; - - if ( pBox && !(pBox->HasSpawnFlags( SF_PHYSBOX_ENABLE_ON_PHYSCANNON )) ) - return false; - } - - if ( sizeLimit > 0 ) - { - const Vector &size = pObject->CollisionProp()->OBBSize(); - if ( size.x > sizeLimit || size.y > sizeLimit || size.z > sizeLimit ) - return false; - } - - return true; -#else - return false; -#endif -} - -float CBasePlayer::GetHeldObjectMass( IPhysicsObject *pHeldObject ) -{ - return 0; -} - - -//----------------------------------------------------------------------------- -// Purpose: Server side of jumping rules. Most jumping logic is already -// handled in shared gamemovement code. Put stuff here that should -// only be done server side. -//----------------------------------------------------------------------------- -void CBasePlayer::Jump() -{ -} - -void CBasePlayer::Duck( ) -{ - if (m_nButtons & IN_DUCK) - { - if ( m_Activity != ACT_LEAP ) - { - SetAnimation( PLAYER_WALK ); - } - } -} - -// -// ID's player as such. -// -Class_T CBasePlayer::Classify ( void ) -{ - return CLASS_PLAYER; -} - - -void CBasePlayer::ResetFragCount() -{ - m_iFrags = 0; - pl.frags = m_iFrags; -} - -void CBasePlayer::IncrementFragCount( int nCount ) -{ - m_iFrags += nCount; - pl.frags = m_iFrags; -} - -void CBasePlayer::ResetDeathCount() -{ - m_iDeaths = 0; - pl.deaths = m_iDeaths; -} - -void CBasePlayer::IncrementDeathCount( int nCount ) -{ - m_iDeaths += nCount; - pl.deaths = m_iDeaths; -} - -void CBasePlayer::AddPoints( int score, bool bAllowNegativeScore ) -{ - // Positive score always adds - if ( score < 0 ) - { - if ( !bAllowNegativeScore ) - { - if ( m_iFrags < 0 ) // Can't go more negative - return; - - if ( -score > m_iFrags ) // Will this go negative? - { - score = -m_iFrags; // Sum will be 0 - } - } - } - - m_iFrags += score; - pl.frags = m_iFrags; -} - -void CBasePlayer::AddPointsToTeam( int score, bool bAllowNegativeScore ) -{ - if ( GetTeam() ) - { - GetTeam()->AddScore( score ); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -// Output : int -//----------------------------------------------------------------------------- -int CBasePlayer::GetCommandContextCount( void ) const -{ - return m_CommandContext.Count(); -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : index - -// Output : CCommandContext -//----------------------------------------------------------------------------- -CCommandContext *CBasePlayer::GetCommandContext( int index ) -{ - if ( index < 0 || index >= m_CommandContext.Count() ) - return NULL; - - return &m_CommandContext[ index ]; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -CCommandContext *CBasePlayer::AllocCommandContext( void ) -{ - int idx = m_CommandContext.AddToTail(); - return &m_CommandContext[ idx ]; -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : index - -//----------------------------------------------------------------------------- -void CBasePlayer::RemoveCommandContext( int index ) -{ - m_CommandContext.Remove( index ); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CBasePlayer::RemoveAllCommandContexts() -{ - m_CommandContext.RemoveAll(); -} - -//----------------------------------------------------------------------------- -// Purpose: Determine how much time we will be running this frame -// Output : float -//----------------------------------------------------------------------------- -int CBasePlayer::DetermineSimulationTicks( void ) -{ - int command_context_count = GetCommandContextCount(); - - int context_number; - - int simulation_ticks = 0; - - // Determine how much time we will be running this frame and fixup player clock as needed - for ( context_number = 0; context_number < command_context_count; context_number++ ) - { - CCommandContext const *ctx = GetCommandContext( context_number ); - Assert( ctx ); - Assert( ctx->numcmds > 0 ); - Assert( ctx->dropped_packets >= 0 ); - - // Determine how long it will take to run those packets - simulation_ticks += ctx->numcmds + ctx->dropped_packets; - } - - return simulation_ticks; -} - -// 2 ticks ahead or behind current clock means we need to fix clock on client -#define TARGET_CLOCK_CORRECTION_TICKS (TIME_TO_TICKS(0.06f)) - - -extern ConVar skip; - -//----------------------------------------------------------------------------- -// Purpose: Based upon amount of time in simulation time, adjust m_nTickBase so that -// we just end at the end of the current frame (so the player is basically on clock -// with the server) -// Input : simulation_ticks - -//----------------------------------------------------------------------------- -void CBasePlayer::AdjustPlayerTimeBase( int simulation_ticks ) -{ - Assert( simulation_ticks >= 0 ); - if ( simulation_ticks < 0 ) - return; - - // Start in the past so that we get to the sv.time that we'll hit at the end of the - // frame, just as we process the final command - - if ( gpGlobals->maxClients == 1 ) - { - // set TickBase so that player simulation tick matches gpGlobals->tickcount after - // all commands have been executed - m_nTickBase = gpGlobals->tickcount - simulation_ticks + 1; - } - else // multiplayer - { - // set the target tick 2 ticks ahead in the future. this way the client can - // alternate around this targettick without getting smaller than gpGlobals->tickcount - // after running the commands simulation time should never be smaller than the - // current gpGlobals->tickcount, otherwise the simulation time drops out of the - // clientside view interpolation buffer. - - int end_of_frame_ticks = gpGlobals->tickcount + TARGET_CLOCK_CORRECTION_TICKS; - - int estimated_end_tick = m_nTickBase + simulation_ticks; - - // If client gets ahead of this, we'll need to correct - int too_fast_limit = end_of_frame_ticks + TARGET_CLOCK_CORRECTION_TICKS; - // If client falls behind this, we'll also need to correct - int too_slow_limit = end_of_frame_ticks - TARGET_CLOCK_CORRECTION_TICKS; - - // See if we are too fast - if ( estimated_end_tick > too_fast_limit ) - { - // DevMsg( "client too fast by %i ticks\n", estimated_end_tick - end_of_frame_ticks ); - m_nTickBase = end_of_frame_ticks - simulation_ticks + 1; - } - // Or to slow - else if ( estimated_end_tick < too_slow_limit ) - { - // DevMsg( "client too slow by %i ticks\n", end_of_frame_ticks - estimated_end_tick ); - m_nTickBase = end_of_frame_ticks - simulation_ticks + 1; - } - } -} - -void CBasePlayer::RunNullCommand( void ) -{ - CUserCmd cmd; // NULL command - - // Store off the globals.. they're gonna get whacked - float flOldFrametime = gpGlobals->frametime; - float flOldCurtime = gpGlobals->curtime; - - pl.fixangle = FIXANGLE_NONE; - cmd.viewangles = EyeAngles(); - - float flTimeBase = gpGlobals->curtime; - SetTimeBase( flTimeBase ); - - MoveHelperServer()->SetHost( this ); - PlayerRunCommand( &cmd, MoveHelperServer() ); - - // save off the last good usercmd - SetLastUserCommand( cmd ); - - // Restore the globals.. - gpGlobals->frametime = flOldFrametime; - gpGlobals->curtime = flOldCurtime; - - MoveHelperServer()->SetHost( NULL ); -} - -//----------------------------------------------------------------------------- -// Purpose: Note, don't chain to BaseClass::PhysicsSimulate -//----------------------------------------------------------------------------- -void CBasePlayer::PhysicsSimulate( void ) -{ - VPROF_BUDGET( "CBasePlayer::PhysicsSimulate", VPROF_BUDGETGROUP_PLAYER ); - // If we've got a moveparent, we must simulate that first. - CBaseEntity *pMoveParent = GetMoveParent(); - if (pMoveParent) - { - pMoveParent->PhysicsSimulate(); - } - - // Make sure not to simulate this guy twice per frame - if (m_nSimulationTick == gpGlobals->tickcount ) - { - return; - } - - m_nSimulationTick = gpGlobals->tickcount; - - // See how much time has queued up for running - int simulation_ticks = DetermineSimulationTicks(); - - // If some time will elapse, make sure our clock (m_nTickBase) starts at the correct time - if ( simulation_ticks > 0 ) - { - AdjustPlayerTimeBase( simulation_ticks ); - } - - if ( IsHLTV() ) - { - // just run a single, empty command to makke sure - // all preThink/Postthink functions are called as usual - Assert ( GetCommandContextCount() == 0 ); - RunNullCommand(); - RemoveAllCommandContexts(); - return; - } - - // Store off true server timestamps - float savetime = gpGlobals->curtime; - float saveframetime = gpGlobals->frametime; - - int command_context_count = GetCommandContextCount(); - for ( int context_number = 0; context_number < command_context_count; context_number++ ) - { - // Get oldest ( newer are added to tail ) - CCommandContext *ctx = GetCommandContext( context_number ); - Assert( ctx ); - - int i; - int numbackup = ctx->totalcmds - ctx->numcmds; - - // If the server is paused, zero out motion,buttons,view changes - if ( ctx->paused ) - { - bool clear_angles = true; - - // If no clipping and cheats enabled and noclipduring game enabled, then leave - // forwardmove and angles stuff in usercmd - if ( GetMoveType() == MOVETYPE_NOCLIP && - sv_cheats->GetBool() && - sv_noclipduringpause.GetBool() ) - { - clear_angles = false; - } - - for ( i = 0; i < ctx->numcmds; i++ ) - { - ctx->cmds[ i ].buttons = 0; - if ( clear_angles ) - { - ctx->cmds[ i ].forwardmove = 0; - ctx->cmds[ i ].sidemove = 0; - ctx->cmds[ i ].upmove = 0; - VectorCopy ( pl.v_angle, ctx->cmds[ i ].viewangles ); - } - } - - ctx->dropped_packets = 0; - } - - MoveHelperServer()->SetHost( this ); - - // Suppress predicted events, etc. - if ( IsPredictingWeapons() ) - { - IPredictionSystem::SuppressHostEvents( this ); - } - - // If we haven't dropped too many packets, then run some commands - if ( ctx->dropped_packets < 24 ) - { - int droppedcmds = ctx->dropped_packets; - - if ( droppedcmds > numbackup ) - { - // Msg( "lost %i cmds\n", droppedcmds ); - } - - // run the last known cmd for each dropped cmd we don't have a backup for - while ( droppedcmds > numbackup ) - { - m_LastCmd.tick_count++; - - if ( ShouldRunCommandsInContext( ctx ) ) - { - PlayerRunCommand( &m_LastCmd, MoveHelperServer() ); - } - droppedcmds--; - } - - // Now run the "history" commands if we still have dropped packets - while ( droppedcmds > 0 ) - { - int cmdnum = ctx->numcmds + droppedcmds - 1; - if ( ShouldRunCommandsInContext( ctx ) ) - { - PlayerRunCommand( &ctx->cmds[cmdnum], MoveHelperServer() ); - } - droppedcmds--; - } - } - - // Now run any new command(s). Go backward because the most recent command is at index 0. - for ( i = ctx->numcmds - 1; i >= 0; i-- ) - { - if ( ShouldRunCommandsInContext( ctx ) ) - { - PlayerRunCommand( &ctx->cmds[ i ], MoveHelperServer() ); - } - } - - // Save off the last good command in case we drop > numbackup packets and need to rerun them - // we'll use this to "guess" at what was in the missing packets - m_LastCmd = ctx->cmds[ CMD_MOSTRECENT ]; - - // Update our vphysics object. - if ( m_pPhysicsController ) - { - VPROF( "CBasePlayer::PhysicsSimulate-UpdateVPhysicsPosition" ); - // If simulating at 2 * TICK_INTERVAL, add an extra TICK_INTERVAL to position arrival computation - int additionalTick = CBaseEntity::IsSimulatingOnAlternateTicks() ? 1 : 0; - - float flSecondsToArrival = ( ctx->numcmds + ctx->dropped_packets + additionalTick ) * TICK_INTERVAL; - UpdateVPhysicsPosition( m_vNewVPhysicsPosition, m_vNewVPhysicsVelocity, flSecondsToArrival ); - } - - // Always reset after running commands - IPredictionSystem::SuppressHostEvents( NULL ); - - MoveHelperServer()->SetHost( NULL ); - } - - // Clear all contexts - RemoveAllCommandContexts(); - - // Restore the true server clock - // FIXME: Should this occur after simulation of children so - // that they are in the timespace of the player? - gpGlobals->curtime = savetime; - gpGlobals->frametime = saveframetime; -} - -unsigned int CBasePlayer::PhysicsSolidMaskForEntity() const -{ - return MASK_PLAYERSOLID; -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *buf - -// totalcmds - -// dropped_packets - -// ignore - -// paused - -// Output : float -- Time in seconds of last movement command -//----------------------------------------------------------------------------- -void CBasePlayer::ProcessUsercmds( CUserCmd *cmds, int numcmds, int totalcmds, - int dropped_packets, bool paused ) -{ - CCommandContext *ctx = AllocCommandContext(); - Assert( ctx ); - - int i; - for ( i = totalcmds - 1; i >= 0; i-- ) - { - ctx->cmds[ i ] = cmds[ i ]; - } - ctx->numcmds = numcmds; - ctx->totalcmds = totalcmds, - ctx->dropped_packets = dropped_packets; - ctx->paused = paused; - - // Set global pause state for this player - m_bGamePaused = paused; - - if ( paused ) - { - m_nSimulationTick = -1; - // Just run the commands right away if paused - PhysicsSimulate(); - } -} - -// Duck debouncing code to stop menu changes from disallowing crouch/uncrouch -ConVar xc_crouch_debounce( "xc_crouch_debounce", "0", FCVAR_NONE ); - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *ucmd - -// *moveHelper - -//----------------------------------------------------------------------------- -void CBasePlayer::PlayerRunCommand(CUserCmd *ucmd, IMoveHelper *moveHelper) -{ - m_touchedPhysObject = false; - - if ( pl.fixangle == FIXANGLE_NONE) - { - VectorCopy ( ucmd->viewangles, pl.v_angle ); - } - - // Handle FL_FROZEN. - // Prevent player moving for some seconds after New Game, so that they pick up everything - if( GetFlags() & FL_FROZEN || - (developer.GetInt() == 0 && gpGlobals->eLoadType == MapLoad_NewGame && gpGlobals->curtime < 3.0 ) ) - { - ucmd->forwardmove = 0; - ucmd->sidemove = 0; - ucmd->upmove = 0; - ucmd->buttons = 0; - ucmd->impulse = 0; - VectorCopy ( pl.v_angle, ucmd->viewangles ); - } -#ifdef _XBOX - else - { - // Force a duck if we're toggled - if ( GetToggledDuckState() ) - { - // If this is set, we've altered our menu options and need to debounce the duck - if ( xc_crouch_debounce.GetBool() ) - { - ToggleDuck(); - - // Mark it as handled - xc_crouch_debounce.SetValue( 0 ); - } - else - { - ucmd->buttons |= IN_DUCK; - } - } - } -#endif // _XBOX - - PlayerMove()->RunCommand(this, ucmd, moveHelper); -} - - -void CBasePlayer::HandleFuncTrain(void) -{ - if ( m_afPhysicsFlags & PFLAG_DIROVERRIDE ) - AddFlag( FL_ONTRAIN ); - else - RemoveFlag( FL_ONTRAIN ); - - // Train speed control - if (( m_afPhysicsFlags & PFLAG_DIROVERRIDE ) == 0) - { - if (m_iTrain & TRAIN_ACTIVE) - { - m_iTrain = TRAIN_NEW; // turn off train - } - return; - } - - CBaseEntity *pTrain = GetGroundEntity(); - float vel; - - if ( pTrain ) - { - if ( !(pTrain->ObjectCaps() & FCAP_DIRECTIONAL_USE) ) - pTrain = NULL; - } - - if ( !pTrain ) - { - if ( GetActiveWeapon()->ObjectCaps() & FCAP_DIRECTIONAL_USE ) - { - m_iTrain = TRAIN_ACTIVE | TRAIN_NEW; - - if ( m_nButtons & IN_FORWARD ) - { - m_iTrain |= TRAIN_FAST; - } - else if ( m_nButtons & IN_BACK ) - { - m_iTrain |= TRAIN_BACK; - } - else - { - m_iTrain |= TRAIN_NEUTRAL; - } - return; - } - else - { - trace_t trainTrace; - // Maybe this is on the other side of a level transition - UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() + Vector(0,0,-38), - MASK_PLAYERSOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &trainTrace ); - - if ( trainTrace.fraction != 1.0 && trainTrace.m_pEnt ) - pTrain = trainTrace.m_pEnt; - - - if ( !pTrain || !(pTrain->ObjectCaps() & FCAP_DIRECTIONAL_USE) || !pTrain->OnControls(this) ) - { - m_afPhysicsFlags &= ~PFLAG_DIROVERRIDE; - m_iTrain = TRAIN_NEW|TRAIN_OFF; - return; - } - } - } - else if ( !( GetFlags() & FL_ONGROUND ) || pTrain->HasSpawnFlags( SF_TRACKTRAIN_NOCONTROL ) || (m_nButtons & (IN_MOVELEFT|IN_MOVERIGHT) ) ) - { - // Turn off the train if you jump, strafe, or the train controls go dead - m_afPhysicsFlags &= ~PFLAG_DIROVERRIDE; - m_iTrain = TRAIN_NEW|TRAIN_OFF; - return; - } - - SetAbsVelocity( vec3_origin ); - vel = 0; - if ( m_afButtonPressed & IN_FORWARD ) - { - vel = 1; - pTrain->Use( this, this, USE_SET, (float)vel ); - } - else if ( m_afButtonPressed & IN_BACK ) - { - vel = -1; - pTrain->Use( this, this, USE_SET, (float)vel ); - } - - if (vel) - { - m_iTrain = TrainSpeed(pTrain->m_flSpeed, ((CFuncTrackTrain*)pTrain)->GetMaxSpeed()); - m_iTrain |= TRAIN_ACTIVE|TRAIN_NEW; - } -} - - -void CBasePlayer::PreThink(void) -{ - if ( g_fGameOver || m_iPlayerLocked ) - return; // intermission or finale - - ItemPreFrame( ); - WaterMove(); - - if ( g_pGameRules && g_pGameRules->FAllowFlashlight() ) - m_Local.m_iHideHUD &= ~HIDEHUD_FLASHLIGHT; - else - m_Local.m_iHideHUD |= HIDEHUD_FLASHLIGHT; - - // checks if new client data (for HUD and view control) needs to be sent to the client - UpdateClientData(); - - CheckTimeBasedDamage(); - - CheckSuitUpdate(); - - if ( GetObserverMode() > OBS_MODE_FIXED ) - { - CheckObserverSettings(); // do this each frame - } - - if (m_lifeState >= LIFE_DYING) - return; - - HandleFuncTrain(); - - if (m_nButtons & IN_JUMP) - { - // If on a ladder, jump off the ladder - // else Jump - Jump(); - } - - // If trying to duck, already ducked, or in the process of ducking - if ((m_nButtons & IN_DUCK) || (GetFlags() & FL_DUCKING) || (m_afPhysicsFlags & PFLAG_DUCKING) ) - Duck(); - - // - // If we're not on the ground, we're falling. Update our falling velocity. - // - if ( !( GetFlags() & FL_ONGROUND ) ) - { - m_Local.m_flFallVelocity = -GetAbsVelocity().z; - } - -#ifndef _XBOX - CNavArea *area = TheNavMesh->GetNavArea( GetAbsOrigin() ); - if (area && area != m_lastNavArea) - { - // player entered a new nav area - if (m_lastNavArea) - { - m_lastNavArea->DecrementPlayerCount( GetTeamNumber() ); - } - - area->IncrementPlayerCount( GetTeamNumber() ); - - m_lastNavArea = area; - if ( area->GetPlace() != UNDEFINED_PLACE ) - { - const char *placeName = TheNavMesh->PlaceToName( area->GetPlace() ); - if ( placeName && *placeName ) - { - Q_strncpy( m_szLastPlaceName.GetForModify(), placeName, MAX_PLACE_NAME_LENGTH ); - } - } - - // generate event - //KeyValues *event = new KeyValues( "player_entered_area" ); - //event->SetInt( "userid", GetUserID() ); - //event->SetInt( "areaid", area->GetID() ); - //gameeventmanager->FireEvent( event ); - } -#endif - - // StudioFrameAdvance( );//!!!HACKHACK!!! Can't be hit by traceline when not animating? -} - - -/* Time based Damage works as follows: - 1) There are several types of timebased damage: - - #define DMG_PARALYZE (1 << 14) // slows affected creature down - #define DMG_NERVEGAS (1 << 15) // nerve toxins, very bad - #define DMG_POISON (1 << 16) // blood poisioning - #define DMG_RADIATION (1 << 17) // radiation exposure - #define DMG_DROWNRECOVER (1 << 18) // drown recovery - #define DMG_ACID (1 << 19) // toxic chemicals or acid burns - #define DMG_SLOWBURN (1 << 20) // in an oven - - 2) A new hit inflicting tbd restarts the tbd counter - each NPC has an 8bit counter, - per damage type. The counter is decremented every second, so the maximum time - an effect will last is 255/60 = 4.25 minutes. Of course, staying within the radius - of a damaging effect like fire, nervegas, radiation will continually reset the counter to max. - - 3) Every second that a tbd counter is running, the player takes damage. The damage - is determined by the type of tdb. - Paralyze - 1/2 movement rate, 30 second duration. - Nervegas - 5 points per second, 16 second duration = 80 points max dose. - Poison - 2 points per second, 25 second duration = 50 points max dose. - Radiation - 1 point per second, 50 second duration = 50 points max dose. - Drown - 5 points per second, 2 second duration. - Acid/Chemical - 5 points per second, 10 second duration = 50 points max. - Burn - 10 points per second, 2 second duration. - Freeze - 3 points per second, 10 second duration = 30 points max. - - 4) Certain actions or countermeasures counteract the damaging effects of tbds: - - Armor/Heater/Cooler - Chemical(acid),burn, freeze all do damage to armor power, then to body - - recharged by suit recharger - Air In Lungs - drowning damage is done to air in lungs first, then to body - - recharged by poking head out of water - - 10 seconds if swiming fast - Air In SCUBA - drowning damage is done to air in tanks first, then to body - - 2 minutes in tanks. Need new tank once empty. - Radiation Syringe - Each syringe full provides protection vs one radiation dosage - Antitoxin Syringe - Each syringe full provides protection vs one poisoning (nervegas or poison). - Health kit - Immediate stop to acid/chemical, fire or freeze damage. - Radiation Shower - Immediate stop to radiation damage, acid/chemical or fire damage. - - -*/ - -// If player is taking time based damage, continue doing damage to player - -// this simulates the effect of being poisoned, gassed, dosed with radiation etc - -// anything that continues to do damage even after the initial contact stops. -// Update all time based damage counters, and shut off any that are done. - -// The m_bitsDamageType bit MUST be set if any damage is to be taken. -// This routine will detect the initial on value of the m_bitsDamageType -// and init the appropriate counter. Only processes damage every second. - -//#define PARALYZE_DURATION 30 // number of 2 second intervals to take damage -//#define PARALYZE_DAMAGE 0.0 // damage to take each 2 second interval - -//#define NERVEGAS_DURATION 16 -//#define NERVEGAS_DAMAGE 5.0 - -//#define POISON_DURATION 25 -//#define POISON_DAMAGE 2.0 - -//#define RADIATION_DURATION 50 -//#define RADIATION_DAMAGE 1.0 - -//#define ACID_DURATION 10 -//#define ACID_DAMAGE 5.0 - -//#define SLOWBURN_DURATION 2 -//#define SLOWBURN_DAMAGE 1.0 - -//#define SLOWFREEZE_DURATION 1.0 -//#define SLOWFREEZE_DAMAGE 3.0 - -/* */ - - -void CBasePlayer::CheckTimeBasedDamage() -{ - int i; - byte bDuration = 0; - - static float gtbdPrev = 0.0; - - if (!(m_bitsDamageType & DMG_TIMEBASED)) - return; - - // only check for time based damage approx. every 2 seconds - if (abs(gpGlobals->curtime - m_tbdPrev) < 2.0) - return; - - m_tbdPrev = gpGlobals->curtime; - - for (i = 0; i < CDMG_TIMEBASED; i++) - { - // make sure bit is set for damage type - if (m_bitsDamageType & (DMG_PARALYZE << i)) - { - switch (i) - { - case itbd_Paralyze: - // UNDONE - flag movement as half-speed - bDuration = PARALYZE_DURATION; - break; - case itbd_NerveGas: -// OnTakeDamage(pev, pev, NERVEGAS_DAMAGE, DMG_GENERIC); - bDuration = NERVEGAS_DURATION; - break; -// case itbd_Poison: -// OnTakeDamage( CTakeDamageInfo( this, this, POISON_DAMAGE, DMG_GENERIC ) ); -// bDuration = POISON_DURATION; -// break; - case itbd_Radiation: -// OnTakeDamage(pev, pev, RADIATION_DAMAGE, DMG_GENERIC); - bDuration = RADIATION_DURATION; - break; - case itbd_DrownRecover: - // NOTE: this hack is actually used to RESTORE health - // after the player has been drowning and finally takes a breath - if (m_idrowndmg > m_idrownrestored) - { - int idif = min(m_idrowndmg - m_idrownrestored, 10); - - TakeHealth(idif, DMG_GENERIC); - m_idrownrestored += idif; - } - bDuration = 4; // get up to 5*10 = 50 points back - break; - - case itbd_PoisonRecover: - { - // NOTE: this hack is actually used to RESTORE health - // after the player has been poisoned. - if (m_nPoisonDmg > m_nPoisonRestored) - { - int nDif = min(m_nPoisonDmg - m_nPoisonRestored, 10); - TakeHealth(nDif, DMG_GENERIC); - m_nPoisonRestored += nDif; - } - bDuration = 9; // get up to 10*10 = 100 points back - break; - } - - case itbd_Acid: -// OnTakeDamage(pev, pev, ACID_DAMAGE, DMG_GENERIC); - bDuration = ACID_DURATION; - break; - case itbd_SlowBurn: -// OnTakeDamage(pev, pev, SLOWBURN_DAMAGE, DMG_GENERIC); - bDuration = SLOWBURN_DURATION; - break; - case itbd_SlowFreeze: -// OnTakeDamage(pev, pev, SLOWFREEZE_DAMAGE, DMG_GENERIC); - bDuration = SLOWFREEZE_DURATION; - break; - default: - bDuration = 0; - } - - if (m_rgbTimeBasedDamage[i]) - { - // decrement damage duration, detect when done. - if (!m_rgbTimeBasedDamage[i] || --m_rgbTimeBasedDamage[i] == 0) - { - m_rgbTimeBasedDamage[i] = 0; - // if we're done, clear damage bits - m_bitsDamageType &= ~(DMG_PARALYZE << i); - } - } - else - // first time taking this damage type - init damage duration - m_rgbTimeBasedDamage[i] = bDuration; - } - } -} - -/* -THE POWER SUIT - -The Suit provides 3 main functions: Protection, Notification and Augmentation. -Some functions are automatic, some require power. -The player gets the suit shortly after getting off the train in C1A0 and it stays -with him for the entire game. - -Protection - - Heat/Cold - When the player enters a hot/cold area, the heating/cooling indicator on the suit - will come on and the battery will drain while the player stays in the area. - After the battery is dead, the player starts to take damage. - This feature is built into the suit and is automatically engaged. - Radiation Syringe - This will cause the player to be immune from the effects of radiation for N seconds. Single use item. - Anti-Toxin Syringe - This will cure the player from being poisoned. Single use item. - Health - Small (1st aid kits, food, etc.) - Large (boxes on walls) - Armor - The armor works using energy to create a protective field that deflects a - percentage of damage projectile and explosive attacks. After the armor has been deployed, - it will attempt to recharge itself to full capacity with the energy reserves from the battery. - It takes the armor N seconds to fully charge. - -Notification (via the HUD) - -x Health -x Ammo -x Automatic Health Care - Notifies the player when automatic healing has been engaged. -x Geiger counter - Classic Geiger counter sound and status bar at top of HUD - alerts player to dangerous levels of radiation. This is not visible when radiation levels are normal. -x Poison - Armor - Displays the current level of armor. - -Augmentation - - Reanimation (w/adrenaline) - Causes the player to come back to life after he has been dead for 3 seconds. - Will not work if player was gibbed. Single use. - Long Jump - Used by hitting the ??? key(s). Caused the player to further than normal. - SCUBA - Used automatically after picked up and after player enters the water. - Works for N seconds. Single use. - -Things powered by the battery - - Armor - Uses N watts for every M units of damage. - Heat/Cool - Uses N watts for every second in hot/cold area. - Long Jump - Uses N watts for every jump. - Alien Cloak - Uses N watts for each use. Each use lasts M seconds. - Alien Shield - Augments armor. Reduces Armor drain by one half - -*/ - -// if in range of radiation source, ping geiger counter - -#define GEIGERDELAY 0.25 - -void CBasePlayer::UpdateGeigerCounter( void ) -{ - byte range; - - // delay per update ie: don't flood net with these msgs - if (gpGlobals->curtime < m_flgeigerDelay) - return; - - m_flgeigerDelay = gpGlobals->curtime + GEIGERDELAY; - - // send range to radition source to client - range = (byte) clamp(m_flgeigerRange / 4, 0, 255); - - // This is to make sure you aren't driven crazy by geiger while in the airboat - if ( IsInAVehicle() ) - { - range = clamp( (int)range * 4, 0, 255 ); - } - - if (range != m_igeigerRangePrev) - { - m_igeigerRangePrev = range; - - CSingleUserRecipientFilter user( this ); - user.MakeReliable(); - UserMessageBegin( user, "Geiger" ); - WRITE_BYTE( range ); - MessageEnd(); - } - - // reset counter and semaphore - if (!random->RandomInt(0,3)) - { - m_flgeigerRange = 1000; - } -} - -/* -================ -CheckSuitUpdate - -Play suit update if it's time -================ -*/ - -#define SUITUPDATETIME 3.5 -#define SUITFIRSTUPDATETIME 0.1 - -void CBasePlayer::CheckSuitUpdate() -{ - int i; - int isentence = 0; - int isearch = m_iSuitPlayNext; - - // Ignore suit updates if no suit - if ( !IsSuitEquipped() ) - return; - - // if in range of radiation source, ping geiger counter - UpdateGeigerCounter(); - - if ( g_pGameRules->IsMultiplayer() ) - { - // don't bother updating HEV voice in multiplayer. - return; - } - - if ( gpGlobals->curtime >= m_flSuitUpdate && m_flSuitUpdate > 0) - { - // play a sentence off of the end of the queue - for (i = 0; i < CSUITPLAYLIST; i++) - { - if ((isentence = m_rgSuitPlayList[isearch]) != 0) - break; - - if (++isearch == CSUITPLAYLIST) - isearch = 0; - } - - if (isentence) - { - m_rgSuitPlayList[isearch] = 0; - if (isentence > 0) - { - // play sentence number - - char sentence[512]; - Q_snprintf( sentence, sizeof( sentence ), "!%s", engine->SentenceNameFromIndex( isentence ) ); - UTIL_EmitSoundSuit( edict(), sentence ); - } - else - { - // play sentence group - UTIL_EmitGroupIDSuit(edict(), -isentence); - } - m_flSuitUpdate = gpGlobals->curtime + SUITUPDATETIME; - } - else - // queue is empty, don't check - m_flSuitUpdate = 0; - } -} - -// add sentence to suit playlist queue. if fgroup is true, then -// name is a sentence group (HEV_AA), otherwise name is a specific -// sentence name ie: !HEV_AA0. If iNoRepeat is specified in -// seconds, then we won't repeat playback of this word or sentence -// for at least that number of seconds. - -void CBasePlayer::SetSuitUpdate(char *name, int fgroup, int iNoRepeatTime) -{ - int i; - int isentence; - int iempty = -1; - - - // Ignore suit updates if no suit - if ( !IsSuitEquipped() ) - return; - - if ( g_pGameRules->IsMultiplayer() ) - { - // due to static channel design, etc. We don't play HEV sounds in multiplayer right now. - return; - } - - // if name == NULL, then clear out the queue - - if (!name) - { - for (i = 0; i < CSUITPLAYLIST; i++) - m_rgSuitPlayList[i] = 0; - return; - } - // get sentence or group number - if (!fgroup) - { - isentence = SENTENCEG_Lookup(name); // Lookup sentence index (not group) by name - if (isentence < 0) - return; - } - else - // mark group number as negative - isentence = -SENTENCEG_GetIndex(name); // Lookup group index by name - - // check norepeat list - this list lets us cancel - // the playback of words or sentences that have already - // been played within a certain time. - - for (i = 0; i < CSUITNOREPEAT; i++) - { - if (isentence == m_rgiSuitNoRepeat[i]) - { - // this sentence or group is already in - // the norepeat list - - if (m_rgflSuitNoRepeatTime[i] < gpGlobals->curtime) - { - // norepeat time has expired, clear it out - m_rgiSuitNoRepeat[i] = 0; - m_rgflSuitNoRepeatTime[i] = 0.0; - iempty = i; - break; - } - else - { - // don't play, still marked as norepeat - return; - } - } - // keep track of empty slot - if (!m_rgiSuitNoRepeat[i]) - iempty = i; - } - - // sentence is not in norepeat list, save if norepeat time was given - - if (iNoRepeatTime) - { - if (iempty < 0) - iempty = random->RandomInt(0, CSUITNOREPEAT-1); // pick random slot to take over - m_rgiSuitNoRepeat[iempty] = isentence; - m_rgflSuitNoRepeatTime[iempty] = iNoRepeatTime + gpGlobals->curtime; - } - - // find empty spot in queue, or overwrite last spot - - m_rgSuitPlayList[m_iSuitPlayNext++] = isentence; - if (m_iSuitPlayNext == CSUITPLAYLIST) - m_iSuitPlayNext = 0; - - if (m_flSuitUpdate <= gpGlobals->curtime) - { - if (m_flSuitUpdate == 0) - // play queue is empty, don't delay too long before playback - m_flSuitUpdate = gpGlobals->curtime + SUITFIRSTUPDATETIME; - else - m_flSuitUpdate = gpGlobals->curtime + SUITUPDATETIME; - } - -} - -//========================================================= -// UpdatePlayerSound - updates the position of the player's -// reserved sound slot in the sound list. -//========================================================= -void CBasePlayer::UpdatePlayerSound ( void ) -{ - int iBodyVolume; - int iVolume; - CSound *pSound; - - pSound = CSoundEnt::SoundPointerForIndex( CSoundEnt::ClientSoundIndex( edict() ) ); - - if ( !pSound ) - { - Msg( "Client lost reserved sound!\n" ); - return; - } - - if (GetFlags() & FL_NOTARGET) - { - pSound->m_iVolume = 0; - return; - } - - // now figure out how loud the player's movement is. - if ( GetFlags() & FL_ONGROUND ) - { - iBodyVolume = GetAbsVelocity().Length(); - - // clamp the noise that can be made by the body, in case a push trigger, - // weapon recoil, or anything shoves the player abnormally fast. - // NOTE: 512 units is a pretty large radius for a sound made by the player's body. - // then again, I think some materials are pretty loud. - if ( iBodyVolume > 512 ) - { - iBodyVolume = 512; - } - } - else - { - iBodyVolume = 0; - } - - if ( m_nButtons & IN_JUMP ) - { - // Jumping is a little louder. - iBodyVolume += 100; - } - - m_iTargetVolume = iBodyVolume; - - // if target volume is greater than the player sound's current volume, we paste the new volume in - // immediately. If target is less than the current volume, current volume is not set immediately to the - // lower volume, rather works itself towards target volume over time. This gives NPCs a much better chance - // to hear a sound, especially if they don't listen every frame. - iVolume = pSound->Volume(); - - if ( m_iTargetVolume > iVolume ) - { - iVolume = m_iTargetVolume; - } - else if ( iVolume > m_iTargetVolume ) - { - iVolume -= 250 * gpGlobals->frametime; - - if ( iVolume < m_iTargetVolume ) - { - iVolume = 0; - } - } - - if ( pSound ) - { - pSound->SetSoundOrigin( GetAbsOrigin() ); - pSound->m_iType = SOUND_PLAYER; - pSound->m_iVolume = iVolume; - } - - // Below are a couple of useful little bits that make it easier to visualize just how much noise the - // player is making. - //Vector forward = UTIL_YawToVector( pl.v_angle.y ); - //UTIL_Sparks( GetAbsOrigin() + forward * iVolume ); - //Msg( "%d/%d\n", iVolume, m_iTargetVolume ); -} - -// This is a glorious hack to find free space when you've crouched into some solid space -// Our crouching collisions do not work correctly for some reason and this is easier -// than fixing the problem :( -void FixPlayerCrouchStuck( CBasePlayer *pPlayer ) -{ - trace_t trace; - - // Move up as many as 18 pixels if the player is stuck. - for ( int i = 0; i < 18; i++ ) - { - UTIL_TraceHull( pPlayer->GetAbsOrigin(), pPlayer->GetAbsOrigin(), - VEC_DUCK_HULL_MIN, VEC_DUCK_HULL_MAX, MASK_PLAYERSOLID, pPlayer, COLLISION_GROUP_NONE, &trace ); - if ( trace.startsolid ) - { - Vector origin = pPlayer->GetAbsOrigin(); - origin.z += 1.0f; - pPlayer->SetLocalOrigin( origin ); - } - else - break; - } -} - -#define SMOOTHING_FACTOR 0.9 -extern CMoveData *g_pMoveData; - -// UNDONE: Look and see if the ground entity is in hierarchy with a MOVETYPE_VPHYSICS? -// Behavior in that case is not as good currently when the parent is rideable -bool CBasePlayer::IsRideablePhysics( IPhysicsObject *pPhysics ) -{ - if ( pPhysics ) - { - if ( pPhysics->GetMass() > (VPhysicsGetObject()->GetMass()*2) ) - return true; - } - - return false; -} - -IPhysicsObject *CBasePlayer::GetGroundVPhysics() -{ - CBaseEntity *pGroundEntity = GetGroundEntity(); - if ( pGroundEntity && pGroundEntity->GetMoveType() == MOVETYPE_VPHYSICS ) - { - IPhysicsObject *pPhysGround = pGroundEntity->VPhysicsGetObject(); - if ( pPhysGround && pPhysGround->IsMoveable() ) - return pPhysGround; - } - return NULL; -} - - -//----------------------------------------------------------------------------- -// For debugging... -//----------------------------------------------------------------------------- -void CBasePlayer::ForceOrigin( const Vector &vecOrigin ) -{ - m_bForceOrigin = true; - m_vForcedOrigin = vecOrigin; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CBasePlayer::PostThink() -{ - m_vecSmoothedVelocity = m_vecSmoothedVelocity * SMOOTHING_FACTOR + GetAbsVelocity() * ( 1 - SMOOTHING_FACTOR ); - - if ( !g_fGameOver && !m_iPlayerLocked && IsAlive() ) - { - // set correct collision bounds (may have changed in player movement code) - VPROF_SCOPE_BEGIN( "CBasePlayer::PostThink-Bounds" ); - if ( GetFlags() & FL_DUCKING ) - { - SetCollisionBounds( VEC_DUCK_HULL_MIN, VEC_DUCK_HULL_MAX ); - } - else - { - SetCollisionBounds( VEC_HULL_MIN, VEC_HULL_MAX ); - } - VPROF_SCOPE_END(); - - VPROF_SCOPE_BEGIN( "CBasePlayer::PostThink-Use" ); - // Handle controlling an entity - if ( m_hUseEntity != NULL ) - { - // if they've moved too far from the gun, or deployed another weapon, unuse the gun - if ( m_hUseEntity->OnControls( this ) && - ( !GetActiveWeapon() || GetActiveWeapon()->IsEffectActive( EF_NODRAW ) || - ( GetActiveWeapon()->GetActivity() == ACT_VM_HOLSTER ) - ) ) - { - m_hUseEntity->Use( this, this, USE_SET, 2 ); // try fire the gun - } - else - { - // they've moved off the controls - ClearUseEntity(); - } - } - VPROF_SCOPE_END(); - - // do weapon stuff - VPROF_SCOPE_BEGIN( "CBasePlayer::PostThink-ItemPostFrame" ); - ItemPostFrame(); - VPROF_SCOPE_END(); - - if ( GetFlags() & FL_ONGROUND ) - { - if (m_Local.m_flFallVelocity > 64 && !g_pGameRules->IsMultiplayer()) - { - CSoundEnt::InsertSound ( SOUND_PLAYER, GetAbsOrigin(), m_Local.m_flFallVelocity, 0.2, this ); - // Msg( "fall %f\n", m_Local.m_flFallVelocity ); - } - m_Local.m_flFallVelocity = 0; - } - - // select the proper animation for the player character - if ( IsAlive() ) - { - VPROF( "CBasePlayer::PostThink-Animation" ); - // If he's in a vehicle, sit down - if ( IsInAVehicle() ) - SetAnimation( PLAYER_IN_VEHICLE ); - else if (!GetAbsVelocity().x && !GetAbsVelocity().y) - SetAnimation( PLAYER_IDLE ); - else if ((GetAbsVelocity().x || GetAbsVelocity().y) && ( GetFlags() & FL_ONGROUND )) - SetAnimation( PLAYER_WALK ); - else if (GetWaterLevel() > 1) - SetAnimation( PLAYER_WALK ); - } - - // Don't allow bogus sequence on player - if ( GetSequence() == -1 ) - { - SetSequence( 0 ); - } - - VPROF_SCOPE_BEGIN( "CBasePlayer::PostThink-StudioFrameAdvance" ); - StudioFrameAdvance(); - VPROF_SCOPE_END(); - - VPROF_SCOPE_BEGIN( "CBasePlayer::PostThink-DispatchAnimEvents" ); - DispatchAnimEvents( this ); - VPROF_SCOPE_END(); - - SetSimulationTime( gpGlobals->curtime ); - - //Let the weapon update as well - VPROF_SCOPE_BEGIN( "CBasePlayer::PostThink-Weapon_FrameUpdate" ); - Weapon_FrameUpdate(); - VPROF_SCOPE_END(); - - VPROF_SCOPE_BEGIN( "CBasePlayer::PostThink-UpdatePlayerSound" ); - UpdatePlayerSound(); - VPROF_SCOPE_END(); - - if ( m_bForceOrigin ) - { - SetLocalOrigin( m_vForcedOrigin ); - SetLocalAngles( m_Local.m_vecPunchAngle ); - m_Local.m_vecPunchAngle = RandomAngle( -25, 25 ); - m_Local.m_vecPunchAngleVel.Init(); - } - - VPROF_SCOPE_BEGIN( "CBasePlayer::PostThink-PostThinkVPhysics" ); - PostThinkVPhysics(); - VPROF_SCOPE_END(); - } - -#if !defined( NO_ENTITY_PREDICTION ) - // Even if dead simulate entities - SimulatePlayerSimulatedEntities(); -#endif - -} - -// handles touching physics objects -void CBasePlayer::Touch( CBaseEntity *pOther ) -{ - if ( pOther == GetGroundEntity() ) - return; - - if ( pOther->GetMoveType() != MOVETYPE_VPHYSICS || pOther->GetSolid() != SOLID_VPHYSICS || (pOther->GetSolidFlags() & FSOLID_TRIGGER) ) - return; - - IPhysicsObject *pPhys = pOther->VPhysicsGetObject(); - if ( !pPhys || !pPhys->IsMoveable() ) - return; - - SetTouchedPhysics( true ); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CBasePlayer::PostThinkVPhysics( void ) -{ - // Check to see if things are initialized! - if ( !m_pPhysicsController ) - return; - - Vector newPosition = GetAbsOrigin(); - float frametime = gpGlobals->frametime; - if ( frametime <= 0 || frametime > 0.1f ) - frametime = 0.1f; - - IPhysicsObject *pPhysGround = GetGroundVPhysics(); - - if ( !pPhysGround && m_touchedPhysObject && g_pMoveData->m_outStepHeight <= 0.f && (GetFlags() & FL_ONGROUND) ) - { - newPosition = m_oldOrigin + frametime * g_pMoveData->m_outWishVel; - newPosition = (GetAbsOrigin() * 0.5f) + (newPosition * 0.5f); - } - - int collisionState = VPHYS_WALK; - if ( GetMoveType() == MOVETYPE_NOCLIP || GetMoveType() == MOVETYPE_OBSERVER ) - { - collisionState = VPHYS_NOCLIP; - } - else if ( GetFlags() & FL_DUCKING ) - { - collisionState = VPHYS_CROUCH; - } - - if ( collisionState != m_vphysicsCollisionState ) - { - SetVCollisionState( collisionState ); - } - - if ( !(TouchedPhysics() || pPhysGround) ) - { - float maxSpeed = m_flMaxspeed > 0.0f ? m_flMaxspeed : sv_maxspeed.GetFloat(); - g_pMoveData->m_outWishVel.Init( maxSpeed, maxSpeed, maxSpeed ); - } - - // teleport the physics object up by stepheight (game code does this - reflect in the physics) - if ( g_pMoveData->m_outStepHeight > 0.1f ) - { - if ( g_pMoveData->m_outStepHeight > 4.0f ) - { - VPhysicsGetObject()->SetPosition( GetAbsOrigin(), vec3_angle, true ); - } - else - { - // don't ever teleport into solid - Vector position, end; - VPhysicsGetObject()->GetPosition( &position, NULL ); - end = position; - end.z += g_pMoveData->m_outStepHeight; - trace_t trace; - UTIL_TraceEntity( this, position, end, MASK_PLAYERSOLID, this, COLLISION_GROUP_PLAYER_MOVEMENT, &trace ); - if ( trace.DidHit() ) - { - g_pMoveData->m_outStepHeight = trace.endpos.z - position.z; - } - m_pPhysicsController->StepUp( g_pMoveData->m_outStepHeight ); - } - m_pPhysicsController->Jump(); - } - g_pMoveData->m_outStepHeight = 0.0f; - - // Store these off because after running the usercmds, it'll pass them - // to UpdateVPhysicsPosition. - m_vNewVPhysicsPosition = newPosition; - m_vNewVPhysicsVelocity = g_pMoveData->m_outWishVel; - - m_oldOrigin = GetAbsOrigin(); -} - -void CBasePlayer::UpdateVPhysicsPosition( const Vector &position, const Vector &velocity, float secondsToArrival ) -{ - bool onground = (GetFlags() & FL_ONGROUND) ? true : false; - IPhysicsObject *pPhysGround = GetGroundVPhysics(); - - // if the object is much heavier than the player, treat it as a local coordinate system - // the player controller will solve movement differently in this case. - if ( !IsRideablePhysics(pPhysGround) ) - { - pPhysGround = NULL; - } - - m_pPhysicsController->Update( position, velocity, secondsToArrival, onground, pPhysGround ); -} - -void CBasePlayer::UpdatePhysicsShadowToCurrentPosition() -{ - UpdateVPhysicsPosition( GetAbsOrigin(), vec3_origin, gpGlobals->frametime ); -} - -Vector CBasePlayer::GetSmoothedVelocity( void ) -{ - if ( IsInAVehicle() ) - { - return GetVehicle()->GetVehicleEnt()->GetSmoothedVelocity(); - } - return m_vecSmoothedVelocity; -} - - -CBaseEntity *g_pLastSpawn = NULL; - - -//----------------------------------------------------------------------------- -// Purpose: Finds a player start entity of the given classname. If any entity of -// of the given classname has the SF_PLAYER_START_MASTER flag set, that -// is the entity that will be returned. Otherwise, the first entity of -// the given classname is returned. -// Input : pszClassName - should be "info_player_start", "info_player_coop", or -// "info_player_deathmatch" -//----------------------------------------------------------------------------- -CBaseEntity *FindPlayerStart(const char *pszClassName) -{ - #define SF_PLAYER_START_MASTER 1 - - CBaseEntity *pStart = gEntList.FindEntityByClassname(NULL, pszClassName); - CBaseEntity *pStartFirst = pStart; - while (pStart != NULL) - { - if (pStart->HasSpawnFlags(SF_PLAYER_START_MASTER)) - { - return pStart; - } - - pStart = gEntList.FindEntityByClassname(pStart, pszClassName); - } - - return pStartFirst; -} - -/* -============ -EntSelectSpawnPoint - -Returns the entity to spawn at - -USES AND SETS GLOBAL g_pLastSpawn -============ -*/ -CBaseEntity *CBasePlayer::EntSelectSpawnPoint() -{ - CBaseEntity *pSpot; - edict_t *player; - - player = edict(); - -// choose a info_player_deathmatch point - if (g_pGameRules->IsCoOp()) - { - pSpot = gEntList.FindEntityByClassname( g_pLastSpawn, "info_player_coop"); - if ( pSpot ) - goto ReturnSpot; - pSpot = gEntList.FindEntityByClassname( g_pLastSpawn, "info_player_start"); - if ( pSpot ) - goto ReturnSpot; - } - else if ( g_pGameRules->IsDeathmatch() ) - { - pSpot = g_pLastSpawn; - // Randomize the start spot - for ( int i = random->RandomInt(1,5); i > 0; i-- ) - pSpot = gEntList.FindEntityByClassname( pSpot, "info_player_deathmatch" ); - if ( !pSpot ) // skip over the null point - pSpot = gEntList.FindEntityByClassname( pSpot, "info_player_deathmatch" ); - - CBaseEntity *pFirstSpot = pSpot; - - do - { - if ( pSpot ) - { - // check if pSpot is valid - if ( g_pGameRules->IsSpawnPointValid( pSpot, this ) ) - { - if ( pSpot->GetLocalOrigin() == vec3_origin ) - { - pSpot = gEntList.FindEntityByClassname( pSpot, "info_player_deathmatch" ); - continue; - } - - // if so, go to pSpot - goto ReturnSpot; - } - } - // increment pSpot - pSpot = gEntList.FindEntityByClassname( pSpot, "info_player_deathmatch" ); - } while ( pSpot != pFirstSpot ); // loop if we're not back to the start - - // we haven't found a place to spawn yet, so kill any guy at the first spawn point and spawn there - if ( pSpot ) - { - CBaseEntity *ent = NULL; - for ( CEntitySphereQuery sphere( pSpot->GetAbsOrigin(), 128 ); (ent = sphere.GetCurrentEntity()) != NULL; sphere.NextEntity() ) - { - // if ent is a client, kill em (unless they are ourselves) - if ( ent->IsPlayer() && !(ent->edict() == player) ) - ent->TakeDamage( CTakeDamageInfo( GetContainingEntity(INDEXENT(0)), GetContainingEntity(INDEXENT(0)), 300, DMG_GENERIC ) ); - } - goto ReturnSpot; - } - } - - // If startspot is set, (re)spawn there. - if ( !gpGlobals->startspot || !strlen(STRING(gpGlobals->startspot))) - { - pSpot = FindPlayerStart( "info_player_start" ); - if ( pSpot ) - goto ReturnSpot; - } - else - { - pSpot = gEntList.FindEntityByName( NULL, gpGlobals->startspot ); - if ( pSpot ) - goto ReturnSpot; - } - -ReturnSpot: - if ( !pSpot ) - { - Warning( "PutClientInServer: no info_player_start on level\n"); - return CBaseEntity::Instance( INDEXENT( 0 ) ); - } - - g_pLastSpawn = pSpot; - return pSpot; -} - -//----------------------------------------------------------------------------- -// Purpose: Called the first time the player's created -//----------------------------------------------------------------------------- -void CBasePlayer::InitialSpawn( void ) -{ - m_iConnected = PlayerConnected; -} - -//----------------------------------------------------------------------------- -// Purpose: Called everytime the player respawns -//----------------------------------------------------------------------------- -void CBasePlayer::Spawn( void ) -{ - SetClassname( "player" ); - - // Shared spawning code.. - SharedSpawn(); - - SetSimulatedEveryTick( true ); - SetAnimatedEveryTick( true ); - - m_ArmorValue = SpawnArmorValue(); - SetBlocksLOS( false ); - m_iMaxHealth = m_iHealth; - - // Clear all flags except for FL_FULLEDICT - if ( GetFlags() & FL_FAKECLIENT ) - { - ClearFlags(); - AddFlag( FL_CLIENT | FL_FAKECLIENT ); - } - else - { - ClearFlags(); - AddFlag( FL_CLIENT ); - } - - AddFlag( FL_AIMTARGET ); - - m_AirFinished = gpGlobals->curtime + AIRTIME; - m_nDrownDmgRate = DROWNING_DAMAGE_INITIAL; - - // only preserve the shadow flag - int effects = GetEffects() & EF_NOSHADOW; - SetEffects( effects ); - - m_DmgTake = 0; - m_DmgSave = 0; - m_bitsHUDDamage = -1; - m_bitsDamageType = 0; - m_afPhysicsFlags = 0; - - SetFOV( this, 0 ); - - m_flNextDecalTime = 0;// let this player decal as soon as he spawns. - - m_flgeigerDelay = gpGlobals->curtime + 2.0; // wait a few seconds until user-defined message registrations - // are recieved by all clients - - m_flTimeStepSound = 0; - m_flFieldOfView = 0.766;// some NPCs use this to determine whether or not the player is looking at them. - - m_vecAdditionalPVSOrigin = vec3_origin; - m_vecCameraPVSOrigin = vec3_origin; - - if ( !m_fGameHUDInitialized ) - g_pGameRules->SetDefaultPlayerTeam( this ); - - g_pGameRules->GetPlayerSpawnSpot( this ); - - m_Local.m_bDucked = false;// This will persist over round restart if you hold duck otherwise. - m_Local.m_bDucking = false; - SetViewOffset( VEC_VIEW ); - Precache(); - - m_bitsDamageType = 0; - m_bitsHUDDamage = -1; - SetPlayerUnderwater( false ); - - m_iTrain = TRAIN_NEW; - - m_HackedGunPos = Vector( 0, 32, 0 ); - - if ( m_iPlayerSound == SOUNDLIST_EMPTY ) - { - Msg( "Couldn't alloc player sound slot!\n" ); - } - - SetThink(NULL); - m_fInitHUD = true; - m_fWeapon = false; - m_iClientBattery = -1; - - m_lastx = m_lasty = 0; - - m_lastNavArea = NULL; - -#ifndef _XBOX - /// @todo Do this once per round instead of once per player - if (TheNavMesh) - { - TheNavMesh->ClearPlayerCounts(); - } -#endif - - Q_strncpy( m_szLastPlaceName.GetForModify(), "", MAX_PLACE_NAME_LENGTH ); - - CSingleUserRecipientFilter user( this ); - enginesound->SetPlayerDSP( user, 0, false ); - - CreateViewModel(); - - SetCollisionGroup( COLLISION_GROUP_PLAYER ); - - // if the player is locked, make sure he stays locked - if ( m_iPlayerLocked ) - { - m_iPlayerLocked = false; - LockPlayerInPlace(); - } - - if ( GetTeamNumber() != TEAM_SPECTATOR ) - { - StopObserverMode(); - } - else - { - StartObserverMode( m_iObserverLastMode ); - } - - StopReplayMode(); - - // Clear any screenfade - color32 nothing = {0,0,0,255}; - UTIL_ScreenFade( this, nothing, 0, 0, FFADE_IN | FFADE_PURGE ); - - g_pGameRules->PlayerSpawn( this ); - - m_flLaggedMovementValue = 1.0f; - m_vecSmoothedVelocity = vec3_origin; - InitVCollision(); - - IGameEvent *event = gameeventmanager->CreateEvent( "player_spawn" ); - - if ( event ) - { - event->SetInt("userid", GetUserID() ); - gameeventmanager->FireEvent( event ); - } - - RumbleEffect( RUMBLE_STOP_ALL, 0, RUMBLE_FLAGS_NONE ); -} - -void CBasePlayer::Activate( void ) -{ - BaseClass::Activate(); - - AimTarget_ForceRepopulateList(); - - RumbleEffect( RUMBLE_STOP_ALL, 0, RUMBLE_FLAGS_NONE ); - - // Reset the analog bias. If the player is in a vehicle when the game - // reloads, it will autosense and apply the correct bias. - m_iVehicleAnalogBias = VEHICLE_ANALOG_BIAS_NONE; -} - -void CBasePlayer::Precache( void ) -{ - BaseClass::Precache(); - - - PrecacheScriptSound( "Player.FallGib" ); - PrecacheScriptSound( "Player.Death" ); - PrecacheScriptSound( "Player.PlasmaDamage" ); - PrecacheScriptSound( "Player.SonicDamage" ); - PrecacheScriptSound( "Player.DrownStart" ); - PrecacheScriptSound( "Player.DrownContinue" ); - PrecacheScriptSound( "Player.Wade" ); - PrecacheScriptSound( "Player.AmbientUnderWater" ); - PrecacheScriptSound( "Player.Wade" ); - enginesound->PrecacheSentenceGroup( "HEV" ); - - // in the event that the player JUST spawned, and the level node graph - // was loaded, fix all of the node graph pointers before the game starts. - - // !!!BUGBUG - now that we have multiplayer, this needs to be moved! - /* todo - put in better spot and use new ainetowrk stuff - if ( WorldGraph.m_fGraphPresent && !WorldGraph.m_fGraphPointersSet ) - { - if ( !WorldGraph.FSetGraphPointers() ) - { - Msg( "**Graph pointers were not set!\n"); - } - else - { - Msg( "**Graph Pointers Set!\n" ); - } - } - */ - - // SOUNDS / MODELS ARE PRECACHED in ClientPrecache() (game specific) - // because they need to precache before any clients have connected - - // init geiger counter vars during spawn and each time - // we cross a level transition - m_flgeigerRange = 1000; - m_igeigerRangePrev = 1000; - -#if 0 - // @Note (toml 04-19-04): These are saved, used to be slammed here - m_bitsDamageType = 0; - m_bitsHUDDamage = -1; - SetPlayerUnderwter( false ); - - m_iTrain = TRAIN_NEW; -#endif - - m_iClientBattery = -1; - - m_iUpdateTime = 5; // won't update for 1/2 a second - - if ( gInitHUD ) - m_fInitHUD = true; - -} - - - -int CBasePlayer::Save( ISave &save ) -{ - if ( !BaseClass::Save(save) ) - return 0; - - return 1; -} - - -int CBasePlayer::Restore( IRestore &restore ) -{ - int status = BaseClass::Restore(restore); - if ( !status ) - return 0; - - CSaveRestoreData *pSaveData = gpGlobals->pSaveData; - // landmark isn't present. - if ( !pSaveData->levelInfo.fUseLandmark ) - { - Msg( "No Landmark:%s\n", pSaveData->levelInfo.szLandmarkName ); - - // default to normal spawn - CBaseEntity *pSpawnSpot = EntSelectSpawnPoint(); - SetLocalOrigin( pSpawnSpot->GetLocalOrigin() + Vector(0,0,1) ); - SetLocalAngles( pSpawnSpot->GetLocalAngles() ); - } - - QAngle newViewAngles = pl.v_angle; - newViewAngles.z = 0; // Clear out roll - SetLocalAngles( newViewAngles ); - SnapEyeAngles( newViewAngles ); - - // Copied from spawn() for now - SetBloodColor( BLOOD_COLOR_RED ); - - // clear this - it will get reset by touching the trigger again - m_afPhysicsFlags &= ~PFLAG_VPHYSICS_MOTIONCONTROLLER; - - if ( GetFlags() & FL_DUCKING ) - { - // Use the crouch HACK - FixPlayerCrouchStuck( this ); - UTIL_SetSize(this, VEC_DUCK_HULL_MIN, VEC_DUCK_HULL_MAX); - m_Local.m_bDucked = true; - } - else - { - m_Local.m_bDucked = false; - UTIL_SetSize(this, VEC_HULL_MIN, VEC_HULL_MAX); - } - - InitVCollision(); - - // success - return 1; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CBasePlayer::OnRestore( void ) -{ - BaseClass::OnRestore(); - - SetViewEntity( m_hViewEntity ); -} - -/* void CBasePlayer::SetTeamName( const char *pTeamName ) -{ - Q_strncpy( m_szTeamName, pTeamName, TEAM_NAME_LENGTH ); -} */ - -void CBasePlayer::SetArmorValue( int value ) -{ - m_ArmorValue = value; -} - -void CBasePlayer::IncrementArmorValue( int nCount, int nMaxValue ) -{ - m_ArmorValue += nCount; - if (nMaxValue > 0) - { - if (m_ArmorValue > nMaxValue) - m_ArmorValue = nMaxValue; - } -} - -// Only used by the physics gun... is there a better interface? -void CBasePlayer::SetPhysicsFlag( int nFlag, bool bSet ) -{ - if (bSet) - m_afPhysicsFlags |= nFlag; - else - m_afPhysicsFlags &= ~nFlag; -} - - -void CBasePlayer::NotifyNearbyRadiationSource( float flRange ) -{ - // if player's current geiger counter range is larger - // than range to this trigger hurt, reset player's - // geiger counter range - - if (m_flgeigerRange >= flRange) - m_flgeigerRange = flRange; -} - -void CBasePlayer::AllowImmediateDecalPainting() -{ - m_flNextDecalTime = gpGlobals->curtime; -} - -// Suicide... -void CBasePlayer::CommitSuicide() -{ - if( !IsAlive() ) - return; - - // prevent suiciding too often - if ( m_fNextSuicideTime > gpGlobals->curtime ) - return; - - // don't let them suicide for 5 seconds after suiciding - m_fNextSuicideTime = gpGlobals->curtime + 5; - - // have the player kill themself - m_iHealth = 0; - Event_Killed( CTakeDamageInfo( this, this, 0, DMG_NEVERGIB ) ); - Event_Dying(); -} - -//============================================== -// HasWeapons - do I have any weapons at all? -//============================================== -bool CBasePlayer::HasWeapons( void ) -{ - int i; - - for ( i = 0 ; i < WeaponCount() ; i++ ) - { - if ( GetWeapon(i) ) - { - return true; - } - } - - return false; -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : &vecForce - -//----------------------------------------------------------------------------- -void CBasePlayer::VelocityPunch( const Vector &vecForce ) -{ - // Clear onground and add velocity. - SetGroundEntity( NULL ); - ApplyAbsVelocityImpulse(vecForce ); -} - - -//-------------------------------------------------------------------------------------------------------------- -// VEHICLES -//----------------------------------------------------------------------------- - -//----------------------------------------------------------------------------- -// Purpose: Put this player in a vehicle -//----------------------------------------------------------------------------- -bool CBasePlayer::GetInVehicle( IServerVehicle *pVehicle, int nRole ) -{ - Assert( NULL == m_hVehicle.Get() ); - Assert( nRole >= 0 ); - - if ( pVehicle->GetPassenger( nRole ) ) - return false; - - CBaseEntity *pEnt = pVehicle->GetVehicleEnt(); - Assert( pEnt ); - - if (!pVehicle->IsPassengerUsingStandardWeapons( nRole )) - { - CBaseCombatWeapon *pWeapon = GetActiveWeapon(); - - //Must be able to stow our weapon - if ( ( pWeapon != NULL ) && ( pWeapon->Holster( NULL ) == false ) ) - return false; - -#ifndef HL2_DLL - m_Local.m_iHideHUD |= HIDEHUD_WEAPONSELECTION; -#endif - m_Local.m_iHideHUD |= HIDEHUD_INVEHICLE; - } - - if ( !pVehicle->IsPassengerVisible( nRole ) ) - { - AddEffects( EF_NODRAW ); - } - - ViewPunchReset(); - - // Setting the velocity to 0 will cause the IDLE animation to play - SetAbsVelocity( vec3_origin ); - SetMoveType( MOVETYPE_NOCLIP ); - - // Choose the entry point of the vehicle, - // By default, just stay at the point you started at... - // NOTE: we have to set this first so that when the parent is set - // the local position just works - Vector vNewPos = GetAbsOrigin(); - QAngle qAngles = GetAbsAngles(); - pVehicle->GetPassengerStartPoint( nRole, &vNewPos, &qAngles ); - SetAbsOrigin( vNewPos ); - SetAbsAngles( qAngles ); - SetParent( pEnt ); - - SetCollisionGroup( COLLISION_GROUP_IN_VEHICLE ); - - // We cannot be ducking -- do all this before SetPassenger because it - // saves our view offset for restoration when we exit the vehicle. - RemoveFlag( FL_DUCKING ); - SetViewOffset( VEC_VIEW ); - m_Local.m_bDucked = false; - m_Local.m_bDucking = false; - m_Local.m_flDucktime = 0.0f; - m_Local.m_flDuckJumpTime = 0.0f; - m_Local.m_flJumpTime = 0.0f; - - // Turn our toggled duck off - if ( GetToggledDuckState() ) - { - ToggleDuck(); - } - - pVehicle->SetPassenger( nRole, this ); - - m_hVehicle = pEnt; - - // Throw an event indicating that the player entered the vehicle. - g_pNotify->ReportNamedEvent( this, "PlayerEnteredVehicle" ); - - m_iVehicleAnalogBias = VEHICLE_ANALOG_BIAS_NONE; - - OnVehicleStart(); - - return true; -} - - -//----------------------------------------------------------------------------- -// Purpose: Remove this player from a vehicle -//----------------------------------------------------------------------------- -void CBasePlayer::LeaveVehicle( const Vector &vecExitPoint, const QAngle &vecExitAngles ) -{ - if ( NULL == m_hVehicle.Get() ) - return; - - IServerVehicle *pVehicle = GetVehicle(); - Assert( pVehicle ); - - int nRole = pVehicle->GetPassengerRole( this ); - Assert( nRole >= 0 ); - - SetParent( NULL ); - - // Find the first non-blocked exit point: - Vector vNewPos = GetAbsOrigin(); - QAngle qAngles = GetAbsAngles(); - if ( vecExitPoint == vec3_origin ) - { - // FIXME: this might fail to find a safe exit point!! - pVehicle->GetPassengerExitPoint( nRole, &vNewPos, &qAngles ); - } - else - { - vNewPos = vecExitPoint; - qAngles = vecExitAngles; - } - OnVehicleEnd( vNewPos ); - SetAbsOrigin( vNewPos ); - SetAbsAngles( qAngles ); - // Clear out any leftover velocity - SetAbsVelocity( vec3_origin ); - - qAngles[ROLL] = 0; - SnapEyeAngles( qAngles ); - -#ifndef HL2_DLL - m_Local.m_iHideHUD &= ~HIDEHUD_WEAPONSELECTION; -#endif - - m_Local.m_iHideHUD &= ~HIDEHUD_INVEHICLE; - - RemoveEffects( EF_NODRAW ); - - SetMoveType( MOVETYPE_WALK ); - SetCollisionGroup( COLLISION_GROUP_PLAYER ); - - if ( VPhysicsGetObject() ) - { - VPhysicsGetObject()->SetPosition( vNewPos, vec3_angle, true ); - } - - m_hVehicle = NULL; - pVehicle->SetPassenger(nRole, NULL); - - // Re-deploy our weapon - if ( IsAlive() ) - { - if ( GetActiveWeapon() && GetActiveWeapon()->IsWeaponVisible() == false ) - { - GetActiveWeapon()->Deploy(); - ShowCrosshair( true ); - } - } - -#ifdef _XBOX - // Just cut all of the rumble effects. - RumbleEffect( RUMBLE_STOP_ALL, 0, RUMBLE_FLAGS_NONE ); -#endif//_XBOX -} - - -//============================================== -// !!!UNDONE:ultra temporary SprayCan entity to apply -// decal frame at a time. For PreAlpha CD -//============================================== -class CSprayCan : public CPointEntity -{ -public: - DECLARE_CLASS( CSprayCan, CPointEntity ); - - void Spawn ( CBasePlayer *pOwner ); - void Think( void ); - - virtual void Precache(); - - virtual int ObjectCaps( void ) { return FCAP_DONT_SAVE; } -}; - -LINK_ENTITY_TO_CLASS( spraycan, CSprayCan ); -PRECACHE_REGISTER( spraycan ); - -void CSprayCan::Spawn ( CBasePlayer *pOwner ) -{ - SetLocalOrigin( pOwner->WorldSpaceCenter() + Vector ( 0 , 0 , 32 ) ); - SetLocalAngles( pOwner->EyeAngles() ); - SetOwnerEntity( pOwner ); - SetNextThink( gpGlobals->curtime ); - EmitSound( "SprayCan.Paint" ); -} - -void CSprayCan::Precache() -{ - BaseClass::Precache(); - - PrecacheScriptSound( "SprayCan.Paint" ); -} - -void CSprayCan::Think( void ) -{ - trace_t tr; - int playernum; - int nFrames; - CBasePlayer *pPlayer; - - pPlayer = ToBasePlayer( GetOwnerEntity() ); - - nFrames = 1; // FIXME, look up from material - - playernum = GetOwnerEntity()->entindex(); - - // Msg( "Spray by player %i, %i of %i\n", playernum, (int)(m_flFrame + 1), nFrames); - - Vector forward; - AngleVectors( GetAbsAngles(), &forward ); - UTIL_TraceLine ( GetAbsOrigin(), GetAbsOrigin() + forward * 128, - MASK_SOLID_BRUSHONLY, pPlayer, COLLISION_GROUP_NONE, & tr); - - UTIL_PlayerDecalTrace( &tr, playernum ); - - // Just painted last custom frame. - UTIL_Remove( this ); -} - -class CBloodSplat : public CPointEntity -{ -public: - DECLARE_CLASS( CBloodSplat, CPointEntity ); - - void Spawn ( CBaseEntity *pOwner ); - void Think ( void ); -}; - -void CBloodSplat::Spawn ( CBaseEntity *pOwner ) -{ - SetLocalOrigin( pOwner->WorldSpaceCenter() + Vector ( 0 , 0 , 32 ) ); - SetLocalAngles( pOwner->GetLocalAngles() ); - SetOwnerEntity( pOwner ); - - SetNextThink( gpGlobals->curtime + 0.1f ); -} - -void CBloodSplat::Think( void ) -{ - trace_t tr; - - if ( g_Language.GetInt() != LANGUAGE_GERMAN ) - { - CBasePlayer *pPlayer; - pPlayer = ToBasePlayer( GetOwnerEntity() ); - - Vector forward; - AngleVectors( GetAbsAngles(), &forward ); - UTIL_TraceLine ( GetAbsOrigin(), GetAbsOrigin() + forward * 128, - MASK_SOLID_BRUSHONLY, pPlayer, COLLISION_GROUP_NONE, & tr); - - UTIL_BloodDecalTrace( &tr, BLOOD_COLOR_RED ); - } - UTIL_Remove( this ); -} - -//============================================== - -//----------------------------------------------------------------------------- -// Purpose: Create and give the named item to the player. Then return it. -//----------------------------------------------------------------------------- -CBaseEntity *CBasePlayer::GiveNamedItem( const char *pszName, int iSubType ) -{ - // If I already own this type don't create one - if ( Weapon_OwnsThisType(pszName, iSubType) ) - return NULL; - - // Msg( "giving %s\n", pszName ); - - EHANDLE pent; - - pent = CreateEntityByName(pszName); - if ( pent == NULL ) - { - Msg( "NULL Ent in GiveNamedItem!\n" ); - return NULL; - } - - pent->SetLocalOrigin( GetLocalOrigin() ); - pent->AddSpawnFlags( SF_NORESPAWN ); - - if ( iSubType ) - { - CBaseCombatWeapon *pWeapon = dynamic_cast( (CBaseEntity*)pent ); - if ( pWeapon ) - { - pWeapon->SetSubType( iSubType ); - } - } - - DispatchSpawn( pent ); - - if ( pent != NULL && !(pent->IsMarkedForDeletion()) ) - { - pent->Touch( this ); - } - - return pent; -} - -//----------------------------------------------------------------------------- -// Purpose: Returns the nearest COLLIBALE entity in front of the player -// that has a clear line of sight with the given classname -// Input : -// Output : -//----------------------------------------------------------------------------- -CBaseEntity *FindEntityClassForward( CBasePlayer *pMe, char *classname ) -{ - trace_t tr; - - Vector forward; - pMe->EyeVectors( &forward ); - UTIL_TraceLine(pMe->EyePosition(), - pMe->EyePosition() + forward * MAX_COORD_RANGE, - MASK_SOLID, pMe, COLLISION_GROUP_NONE, &tr ); - if ( tr.fraction != 1.0 && tr.DidHitNonWorldEntity() ) - { - CBaseEntity *pHit = tr.m_pEnt; - if (FClassnameIs( pHit,classname ) ) - { - return pHit; - } - } - return NULL; -} - -//----------------------------------------------------------------------------- -// Purpose: Returns the nearest COLLIBALE entity in front of the player -// that has a clear line of sight. If HULL is true, the trace will -// hit the collision hull of entities. Otherwise, the trace will hit -// hitboxes. -// Input : -// Output : -//----------------------------------------------------------------------------- -CBaseEntity *FindEntityForward( CBasePlayer *pMe, bool fHull ) -{ - if ( pMe ) - { - trace_t tr; - Vector forward; - int mask; - - if( fHull ) - { - mask = MASK_SOLID; - } - else - { - mask = MASK_SHOT; - } - - pMe->EyeVectors( &forward ); - UTIL_TraceLine(pMe->EyePosition(), - pMe->EyePosition() + forward * MAX_COORD_RANGE, - mask, pMe, COLLISION_GROUP_NONE, &tr ); - if ( tr.fraction != 1.0 && tr.DidHitNonWorldEntity() ) - { - return tr.m_pEnt; - } - } - return NULL; - -} - -//----------------------------------------------------------------------------- -// Purpose: Finds the nearest entity in front of the player of the given -// classname, preferring collidable entities, but allows selection of -// enities that are on the other side of walls or objects -// -// Input : -// Output : -//----------------------------------------------------------------------------- -CBaseEntity *FindPickerEntityClass( CBasePlayer *pPlayer, char *classname ) -{ - // First try to trace a hull to an entity - CBaseEntity *pEntity = FindEntityClassForward( pPlayer, classname ); - - // If that fails just look for the nearest facing entity - if (!pEntity) - { - Vector forward; - Vector origin; - pPlayer->EyeVectors( &forward ); - origin = pPlayer->WorldSpaceCenter(); - pEntity = gEntList.FindEntityClassNearestFacing( origin, forward,0.95,classname); - } - return pEntity; -} - -//----------------------------------------------------------------------------- -// Purpose: Finds the nearest entity in front of the player, preferring -// collidable entities, but allows selection of enities that are -// on the other side of walls or objects -// Input : -// Output : -//----------------------------------------------------------------------------- -CBaseEntity *FindPickerEntity( CBasePlayer *pPlayer ) -{ - // First try to trace a hull to an entity - CBaseEntity *pEntity = FindEntityForward( pPlayer, true ); - - // If that fails just look for the nearest facing entity - if (!pEntity) - { - Vector forward; - Vector origin; - pPlayer->EyeVectors( &forward ); - origin = pPlayer->WorldSpaceCenter(); - pEntity = gEntList.FindEntityNearestFacing( origin, forward,0.95); - } - return pEntity; -} - -//----------------------------------------------------------------------------- -// Purpose: Finds the nearest node in front of the player -// Input : -// Output : -//----------------------------------------------------------------------------- -CAI_Node *FindPickerAINode( CBasePlayer *pPlayer, NodeType_e nNodeType ) -{ - Vector forward; - Vector origin; - - pPlayer->EyeVectors( &forward ); - origin = pPlayer->EyePosition(); - return g_pAINetworkManager->GetEditOps()->FindAINodeNearestFacing( origin, forward,0.90, nNodeType); -} - -//----------------------------------------------------------------------------- -// Purpose: Finds the nearest link in front of the player -// Input : -// Output : -//----------------------------------------------------------------------------- -CAI_Link *FindPickerAILink( CBasePlayer* pPlayer ) -{ - Vector forward; - Vector origin; - - pPlayer->EyeVectors( &forward ); - origin = pPlayer->EyePosition(); - return g_pAINetworkManager->GetEditOps()->FindAILinkNearestFacing( origin, forward,0.90); -} - -/* -=============== -ForceClientDllUpdate - -When recording a demo, we need to have the server tell us the entire client state -so that the client side .dll can behave correctly. -Reset stuff so that the state is transmitted. -=============== -*/ -void CBasePlayer::ForceClientDllUpdate( void ) -{ - m_iClientBattery = -1; - m_iTrain |= TRAIN_NEW; // Force new train message. - m_fWeapon = false; // Force weapon send - - // Force all HUD data to be resent to client - m_fInitHUD = true; - - // Now force all the necessary messages - // to be sent. - UpdateClientData(); - - UTIL_RestartAmbientSounds(); // MOTODO that updates the sounds for everybody -} - -/* -============ -ImpulseCommands -============ -*/ - -void CBasePlayer::ImpulseCommands( ) -{ - trace_t tr; - - int iImpulse = (int)m_nImpulse; - switch (iImpulse) - { - case 100: - // temporary flashlight for level designers - if ( FlashlightIsOn() ) - { - FlashlightTurnOff(); - } - else - { - FlashlightTurnOn(); - } - break; - - case 200: - if ( sv_cheats->GetBool() ) - { - CBaseCombatWeapon *pWeapon; - - pWeapon = GetActiveWeapon(); - - if( pWeapon->IsEffectActive( EF_NODRAW ) ) - { - pWeapon->Deploy(); - } - else - { - pWeapon->Holster(); - } - } - break; - - case 201:// paint decal - - if ( gpGlobals->curtime < m_flNextDecalTime ) - { - // too early! - break; - } - - { - Vector forward; - EyeVectors( &forward ); - UTIL_TraceLine ( EyePosition(), - EyePosition() + forward * 128, - MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, & tr); - } - - if ( tr.fraction != 1.0 ) - {// line hit something, so paint a decal - m_flNextDecalTime = gpGlobals->curtime + decalfrequency.GetFloat(); - CSprayCan *pCan = CREATE_UNSAVED_ENTITY( CSprayCan, "spraycan" ); - pCan->Spawn( this ); - } - - break; - - case 202:// player jungle sound - if ( gpGlobals->curtime < m_flNextDecalTime ) - { - // too early! - break; - - } - - EntityMessageBegin( this ); - WRITE_BYTE( PLAY_PLAYER_JINGLE ); - MessageEnd(); - - m_flNextDecalTime = gpGlobals->curtime + decalfrequency.GetFloat(); - break; - - default: - // check all of the cheat impulse commands now - CheatImpulseCommands( iImpulse ); - break; - } - - m_nImpulse = 0; -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -static void CreateJeep( CBasePlayer *pPlayer ) -{ - // Cheat to create a jeep in front of the player - Vector vecForward; - AngleVectors( pPlayer->EyeAngles(), &vecForward ); - CBaseEntity *pJeep = (CBaseEntity *)CreateEntityByName( "prop_vehicle_jeep" ); - if ( pJeep ) - { - Vector vecOrigin = pPlayer->GetAbsOrigin() + vecForward * 256 + Vector(0,0,64); - QAngle vecAngles( 0, pPlayer->GetAbsAngles().y - 90, 0 ); - pJeep->SetAbsOrigin( vecOrigin ); - pJeep->SetAbsAngles( vecAngles ); - pJeep->KeyValue( "model", "models/buggy.mdl" ); - pJeep->KeyValue( "solid", "6" ); - pJeep->KeyValue( "targetname", "jeep" ); - pJeep->KeyValue( "vehiclescript", "scripts/vehicles/jeep_test.txt" ); - pJeep->Spawn(); - pJeep->Activate(); - pJeep->Teleport( &vecOrigin, &vecAngles, NULL ); - } -} - - -void CC_CH_CreateJeep( void ) -{ - CBasePlayer *pPlayer = UTIL_GetCommandClient(); - if ( !pPlayer ) - return; - CreateJeep( pPlayer ); -} - -static ConCommand ch_createjeep("ch_createjeep", CC_CH_CreateJeep, "Spawn jeep in front of the player.", FCVAR_CHEAT); - - -//----------------------------------------------------------------------------- -// Create an airboat in front of the specified player -//----------------------------------------------------------------------------- -static void CreateAirboat( CBasePlayer *pPlayer ) -{ - // Cheat to create a jeep in front of the player - Vector vecForward; - AngleVectors( pPlayer->EyeAngles(), &vecForward ); - CBaseEntity *pJeep = ( CBaseEntity* )CreateEntityByName( "prop_vehicle_airboat" ); - if ( pJeep ) - { - Vector vecOrigin = pPlayer->GetAbsOrigin() + vecForward * 256 + Vector( 0,0,64 ); - QAngle vecAngles( 0, pPlayer->GetAbsAngles().y - 90, 0 ); - pJeep->SetAbsOrigin( vecOrigin ); - pJeep->SetAbsAngles( vecAngles ); - pJeep->KeyValue( "model", "models/airboat.mdl" ); - pJeep->KeyValue( "solid", "6" ); - pJeep->KeyValue( "targetname", "airboat" ); - pJeep->KeyValue( "vehiclescript", "scripts/vehicles/airboat.txt" ); - pJeep->Spawn(); - pJeep->Activate(); - } -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CC_CH_CreateAirboat( void ) -{ - CBasePlayer *pPlayer = UTIL_GetCommandClient(); - if ( !pPlayer ) - return; - - CreateAirboat( pPlayer ); - -} - -static ConCommand ch_createairboat( "ch_createairboat", CC_CH_CreateAirboat, "Spawn airboat in front of the player.", FCVAR_CHEAT ); - - -//========================================================= -//========================================================= -void CBasePlayer::CheatImpulseCommands( int iImpulse ) -{ -#if !defined( HLDEMO_BUILD ) - if ( !sv_cheats->GetBool() ) - { - return; - } - - CBaseEntity *pEntity; - trace_t tr; - - switch ( iImpulse ) - { - case 76: - { - if (!giPrecacheGrunt) - { - giPrecacheGrunt = 1; - Msg( "You must now restart to use Grunt-o-matic.\n"); - } - else - { - Vector forward = UTIL_YawToVector( EyeAngles().y ); - Create("NPC_human_grunt", GetLocalOrigin() + forward * 128, GetLocalAngles()); - } - break; - } - - case 81: - - GiveNamedItem( "weapon_cubemap" ); - break; - - case 82: - // Cheat to create a jeep in front of the player - CreateJeep( this ); - break; - - case 83: - // Cheat to create a airboat in front of the player - CreateAirboat( this ); - break; - - case 101: - gEvilImpulse101 = true; - - EquipSuit(); - - // Give the player everything! - GiveAmmo( 255, "Pistol"); - GiveAmmo( 255, "AR2"); - GiveAmmo( 5, "AR2AltFire"); - GiveAmmo( 255, "SMG1"); - GiveAmmo( 255, "Buckshot"); - GiveAmmo( 3, "smg1_grenade"); - GiveAmmo( 3, "rpg_round"); - GiveAmmo( 5, "grenade"); - GiveAmmo( 32, "357" ); - GiveAmmo( 16, "XBowBolt" ); -#ifdef HL2_EPISODIC - GiveAmmo( 5, "Hopwire" ); -#endif - GiveNamedItem( "weapon_smg1" ); - GiveNamedItem( "weapon_frag" ); - GiveNamedItem( "weapon_crowbar" ); - GiveNamedItem( "weapon_pistol" ); - GiveNamedItem( "weapon_ar2" ); - GiveNamedItem( "weapon_shotgun" ); - GiveNamedItem( "weapon_physcannon" ); - GiveNamedItem( "weapon_bugbait" ); - GiveNamedItem( "weapon_rpg" ); - GiveNamedItem( "weapon_357" ); - GiveNamedItem( "weapon_crossbow" ); -#ifdef HL2_EPISODIC - //GiveNamedItem( "weapon_hopwire" ); -#endif - if ( GetHealth() < 100 ) - { - TakeHealth( 25, DMG_GENERIC ); - } - - gEvilImpulse101 = false; - - break; - - case 102: - // Gibbage!!! - CGib::SpawnRandomGibs( this, 1, GIB_HUMAN ); - break; - - case 103: - // What the hell are you doing? - pEntity = FindEntityForward( this, true ); - if ( pEntity ) - { - CAI_BaseNPC *pNPC = pEntity->MyNPCPointer(); - if ( pNPC ) - pNPC->ReportAIState(); - } - break; - - case 106: - // Give me the classname and targetname of this entity. - pEntity = FindEntityForward( this, true ); - if ( pEntity ) - { - Msg( "Classname: %s", pEntity->GetClassname() ); - - if ( pEntity->GetEntityName() != NULL_STRING ) - { - Msg( " - Name: %s\n", STRING( pEntity->GetEntityName() ) ); - } - else - { - Msg( " - Name: No Targetname\n" ); - } - - if ( pEntity->m_iParent != NULL_STRING ) - Msg( "Parent: %s\n", STRING(pEntity->m_iParent) ); - - Msg( "Model: %s\n", STRING( pEntity->GetModelName() ) ); - if ( pEntity->m_iGlobalname != NULL_STRING ) - Msg( "Globalname: %s\n", STRING(pEntity->m_iGlobalname) ); - } - break; - - case 107: - { - trace_t tr; - - edict_t *pWorld = engine->PEntityOfEntIndex( 0 ); - - Vector start = EyePosition(); - Vector forward; - EyeVectors( &forward ); - Vector end = start + forward * 1024; - UTIL_TraceLine( start, end, MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr ); - if ( tr.m_pEnt ) - pWorld = tr.m_pEnt->edict(); - - const char *pTextureName = tr.surface.name; - - if ( pTextureName ) - Msg( "Texture: %s\n", pTextureName ); - } - break; - - // - // Sets the debug NPC to be the NPC under the crosshair. - // - case 108: - { - pEntity = FindEntityForward( this, true ); - if ( pEntity ) - { - CAI_BaseNPC *pNPC = pEntity->MyNPCPointer(); - if ( pNPC != NULL ) - { - Msg( "Debugging %s (0x%x)\n", pNPC->GetClassname(), pNPC ); - CAI_BaseNPC::SetDebugNPC( pNPC ); - } - } - break; - } - - case 195:// show shortest paths for entire level to nearest node - { - Create("node_viewer_fly", GetLocalOrigin(), GetLocalAngles()); - } - break; - case 196:// show shortest paths for entire level to nearest node - { - Create("node_viewer_large", GetLocalOrigin(), GetLocalAngles()); - } - break; - case 197:// show shortest paths for entire level to nearest node - { - Create("node_viewer_human", GetLocalOrigin(), GetLocalAngles()); - } - break; - case 202:// Random blood splatter - { - Vector forward; - EyeVectors( &forward ); - UTIL_TraceLine ( EyePosition(), - EyePosition() + forward * 128, - MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, & tr); - - if ( tr.fraction != 1.0 ) - {// line hit something, so paint a decal - CBloodSplat *pBlood = CREATE_UNSAVED_ENTITY( CBloodSplat, "bloodsplat" ); - pBlood->Spawn( this ); - } - } - break; - case 203:// remove creature. - pEntity = FindEntityForward( this, true ); - if ( pEntity ) - { - UTIL_Remove( pEntity ); -// if ( pEntity->m_takedamage ) -// pEntity->SetThink(SUB_Remove); - } - break; - } -#endif // HLDEMO_BUILD -} - - -bool CBasePlayer::ClientCommand(const char *cmd) -{ -#ifdef _DEBUG - if( stricmp( cmd, "test_SmokeGrenade" ) == 0 ) - { - if ( sv_cheats && sv_cheats->GetBool() ) - { - ParticleSmokeGrenade *pSmoke = dynamic_cast( CreateEntityByName(PARTICLESMOKEGRENADE_ENTITYNAME) ); - if ( pSmoke ) - { - Vector vForward; - AngleVectors( GetLocalAngles(), &vForward ); - vForward.z = 0; - VectorNormalize( vForward ); - - pSmoke->SetLocalOrigin( GetLocalOrigin() + vForward * 100 ); - pSmoke->SetFadeTime(25, 30); // Fade out between 25 seconds and 30 seconds. - pSmoke->Activate(); - pSmoke->SetLifetime(30); - pSmoke->FillVolume(); - - return true; - } - } - } - else -#endif // _DEBUG - if( stricmp( cmd, "vehicleRole" ) == 0 ) - { - // Get the vehicle role value. - if ( engine->Cmd_Argc() == 2 ) - { - // Check to see if a player is in a vehicle. - if ( IsInAVehicle() ) - { - int nRole = atoi( engine->Cmd_Argv( 1 ) ); - IServerVehicle *pVehicle = GetVehicle(); - if ( pVehicle ) - { - // Only switch roles if role is empty! - if ( !pVehicle->GetPassenger( nRole ) ) - { - LeaveVehicle(); - GetInVehicle( pVehicle, nRole ); - } - } - } - - return true; - } - } - else if ( stricmp( cmd, "spectate" ) == 0 ) // join spectator team & start observer mode - { - if ( GetTeamNumber() == TEAM_SPECTATOR ) - return true; - - if ( !IsDead() ) - { - ClientKill( edict() ); // kill player - - // add 1 to frags to balance out the 1 subtracted for killing yourself - IncrementFragCount( 1 ); - } - - RemoveAllItems( true ); - - ChangeTeam( TEAM_SPECTATOR ); - - StartObserverMode( OBS_MODE_ROAMING ); - return true; - } - else if ( stricmp( cmd, "spec_mode" ) == 0 ) // new observer mode - { - int mode; - - // check for parameters. - if ( engine->Cmd_Argc() >= 2 ) - { - mode = atoi( engine->Cmd_Argv(1) ); - - if ( mode < OBS_MODE_IN_EYE || mode > OBS_MODE_ROAMING ) - mode = OBS_MODE_IN_EYE; - } - else - { - // sitch to next spec mode if no parameter give - mode = GetObserverMode() + 1; - - if ( mode > OBS_MODE_ROAMING ) - { - mode = OBS_MODE_IN_EYE; - } - else if ( mode < OBS_MODE_IN_EYE ) - { - mode = OBS_MODE_ROAMING; - } - - } - - // don't allow input while player or death cam animation - if ( GetObserverMode() > OBS_MODE_DEATHCAM ) - { - // set new spectator mode, don't allow OBS_MODE_NONE - if ( !SetObserverMode( mode ) ) - ClientPrint( this, HUD_PRINTCONSOLE, "#Spectator_Mode_Unkown"); - else - engine->ClientCommand( edict(), "cl_spec_mode %d", mode ); - } - else - { - // remember spectator mode for later use - m_iObserverLastMode = mode; - engine->ClientCommand( edict(), "cl_spec_mode %d", mode ); - } - - return true; - } - else if ( stricmp( cmd, "spec_next" ) == 0 ) // chase next player - { - if ( GetObserverMode() > OBS_MODE_FIXED ) - { - // set new spectator mode - CBaseEntity * target = FindNextObserverTarget( false ); - if ( target ) - SetObserverTarget( target ); - } - - return true; - } - else if ( stricmp( cmd, "spec_prev" ) == 0 ) // chase prevoius player - { - if ( GetObserverMode() > OBS_MODE_FIXED ) - { - // set new spectator mode - CBaseEntity * target = FindNextObserverTarget( true ); - if ( target ) - SetObserverTarget( target ); - } - - return true; - } - - else if ( stricmp( cmd, "spec_player" ) == 0 ) // chase next player - { - if ( GetObserverMode() > OBS_MODE_FIXED && - engine->Cmd_Argc() == 2 ) - { - int index = atoi( engine->Cmd_Argv(1) ); - - CBasePlayer * target; - - if ( index == 0 ) - { - target = UTIL_PlayerByName( engine->Cmd_Argv(1) ); - } - else - { - target = UTIL_PlayerByIndex( index ); - } - - if ( IsValidObserverTarget( target ) ) - { - SetObserverTarget( target ); - } - } - - return true; - } - - else if ( stricmp( cmd, "spec_goto" ) == 0 ) // chase next player - { - if ( ( GetObserverMode() == OBS_MODE_FIXED || - GetObserverMode() == OBS_MODE_ROAMING ) && - engine->Cmd_Argc() == 6 ) - { - Vector origin; - origin.x = atof( engine->Cmd_Argv(1) ); - origin.y = atof( engine->Cmd_Argv(2) ); - origin.z = atof( engine->Cmd_Argv(3) ); - - QAngle angle; - angle.x = atof( engine->Cmd_Argv(4) ); - angle.y = atof( engine->Cmd_Argv(5) ); - angle.z = 0.0f; - - JumptoPosition( origin, angle ); - } - - return true; - } - - - - return false; -} - -extern bool UTIL_ItemCanBeTouchedByPlayer( CBaseEntity *pItem, CBasePlayer *pPlayer ); - -//----------------------------------------------------------------------------- -// Purpose: Player reacts to bumping a weapon. -// Input : pWeapon - the weapon that the player bumped into. -// Output : Returns true if player picked up the weapon -//----------------------------------------------------------------------------- -bool CBasePlayer::BumpWeapon( CBaseCombatWeapon *pWeapon ) -{ - CBaseCombatCharacter *pOwner = pWeapon->GetOwner(); - - // Can I have this weapon type? - if ( IsEFlagSet( EFL_NO_WEAPON_PICKUP ) ) - return false; - - if ( pOwner || !Weapon_CanUse( pWeapon ) || !g_pGameRules->CanHavePlayerItem( this, pWeapon ) ) - { - if ( gEvilImpulse101 ) - { - UTIL_Remove( pWeapon ); - } - return false; - } - - // Act differently in the episodes - if ( hl2_episodic.GetBool() ) - { - // Don't let the player touch the item unless unobstructed - if ( !UTIL_ItemCanBeTouchedByPlayer( pWeapon, this ) && !gEvilImpulse101 ) - return false; - } - else - { - // Don't let the player fetch weapons through walls (use MASK_SOLID so that you can't pickup through windows) - if( pWeapon->FVisible( this, MASK_SOLID ) == false && !(GetFlags() & FL_NOTARGET) ) - return false; - } - - // ---------------------------------------- - // If I already have it just take the ammo - // ---------------------------------------- - if (Weapon_OwnsThisType( pWeapon->GetClassname(), pWeapon->GetSubType())) - { - if( Weapon_EquipAmmoOnly( pWeapon ) ) - { - // Only remove me if I have no ammo left - if ( pWeapon->HasPrimaryAmmo() ) - return false; - - UTIL_Remove( pWeapon ); - return true; - } - else - { - return false; - } - } - // ------------------------- - // Otherwise take the weapon - // ------------------------- - else - { - pWeapon->CheckRespawn(); - - pWeapon->AddSolidFlags( FSOLID_NOT_SOLID ); - pWeapon->AddEffects( EF_NODRAW ); - - Weapon_Equip( pWeapon ); - if ( IsInAVehicle() ) - { - pWeapon->Holster(); - } - else - { -#ifdef HL2_DLL - -#ifdef _XBOX - CFmtStr hint; - hint.sprintf( "#valve_hint_select_%s", pWeapon->GetClassname() ); - UTIL_HudHintText( this, hint.Access() ); -#endif // _XBOX - - // Always switch to a newly-picked up weapon - if ( !PlayerHasMegaPhysCannon() ) - { - // If it uses clips, load it full. (this is the first time you've picked up this type of weapon) - if ( pWeapon->UsesClipsForAmmo1() ) - { - pWeapon->m_iClip1 = pWeapon->GetMaxClip1(); - } - - Weapon_Switch( pWeapon ); - } -#endif - } - return true; - } -} - - -bool CBasePlayer::RemovePlayerItem( CBaseCombatWeapon *pItem ) -{ - if (GetActiveWeapon() == pItem) - { - ResetAutoaim( ); - pItem->Holster( ); - pItem->SetNextThink( TICK_NEVER_THINK );; // crowbar may be trying to swing again, etc - pItem->SetThink( NULL ); - } - - if ( m_hLastWeapon.Get() == pItem ) - { - Weapon_SetLast( NULL ); - } - - return Weapon_Detach( pItem ); -} - - -//----------------------------------------------------------------------------- -// Purpose: Hides or shows the player's view model. The "r_drawviewmodel" cvar -// can still hide the viewmodel even if this is set to true. -// Input : bShow - true to show, false to hide the view model. -//----------------------------------------------------------------------------- -void CBasePlayer::ShowViewModel(bool bShow) -{ - m_Local.m_bDrawViewmodel = bShow; -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : bDraw - -//----------------------------------------------------------------------------- -void CBasePlayer::ShowCrosshair( bool bShow ) -{ - if ( bShow ) - { - m_Local.m_iHideHUD &= ~HIDEHUD_CROSSHAIR; - } - else - { - m_Local.m_iHideHUD |= HIDEHUD_CROSSHAIR; - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -QAngle CBasePlayer::BodyAngles() -{ - return EyeAngles(); -} - -//------------------------------------------------------------------------------ -// Purpose : Add noise to BodyTarget() to give enemy a better chance of -// getting a clear shot when the player is peeking above a hole -// or behind a ladder (eventually the randomly-picked point -// along the spine will be one that is exposed above the hole or -// between rungs of a ladder.) -// Input : -// Output : -//------------------------------------------------------------------------------ -Vector CBasePlayer::BodyTarget( const Vector &posSrc, bool bNoisy ) -{ - if ( IsInAVehicle() ) - { - return GetVehicle()->GetVehicleEnt()->BodyTarget( posSrc, bNoisy ); - } - if (bNoisy) - { - return GetAbsOrigin() + (GetViewOffset() * random->RandomFloat( 0.7, 1.0 )); - } - else - { - return EyePosition(); - } -}; - -/* -========================================================= - UpdateClientData - -resends any changed player HUD info to the client. -Called every frame by PlayerPreThink -Also called at start of demo recording and playback by -ForceClientDllUpdate to ensure the demo gets messages -reflecting all of the HUD state info. -========================================================= -*/ -void CBasePlayer::UpdateClientData( void ) -{ - CSingleUserRecipientFilter user( this ); - user.MakeReliable(); - - if (m_fInitHUD) - { - m_fInitHUD = false; - gInitHUD = false; - - UserMessageBegin( user, "ResetHUD" ); - WRITE_BYTE( 0 ); - MessageEnd(); - - if ( !m_fGameHUDInitialized ) - { - g_pGameRules->InitHUD( this ); - InitHUD(); - m_fGameHUDInitialized = true; - if ( g_pGameRules->IsMultiplayer() ) - { - variant_t value; - g_EventQueue.AddEvent( "game_player_manager", "OnPlayerJoin", value, 0, this, this ); - } - } - - variant_t value; - g_EventQueue.AddEvent( "game_player_manager", "OnPlayerSpawn", value, 0, this, this ); - } - - // HACKHACK -- send the message to display the game title - CWorld *world = GetWorldEntity(); - if ( world && world->GetDisplayTitle() ) - { - UserMessageBegin( user, "GameTitle" ); - MessageEnd(); - world->SetDisplayTitle( false ); - } - - if (m_ArmorValue != m_iClientBattery) - { - m_iClientBattery = m_ArmorValue; - - // send "battery" update message - if ( usermessages->LookupUserMessage( "Battery" ) != -1 ) - { - UserMessageBegin( user, "Battery" ); - WRITE_SHORT( (int)m_ArmorValue); - MessageEnd(); - } - } - -#if 0 // BYE BYE!! - // Update Flashlight - if ((m_flFlashLightTime) && (m_flFlashLightTime <= gpGlobals->curtime)) - { - if (FlashlightIsOn()) - { - if (m_iFlashBattery) - { - m_flFlashLightTime = FLASH_DRAIN_TIME + gpGlobals->curtime; - m_iFlashBattery--; - - if (!m_iFlashBattery) - FlashlightTurnOff(); - } - } - else - { - if (m_iFlashBattery < 100) - { - m_flFlashLightTime = FLASH_CHARGE_TIME + gpGlobals->curtime; - m_iFlashBattery++; - } - else - m_flFlashLightTime = 0; - } - } -#endif - - CheckTrainUpdate(); - - // Update all the items - for ( int i = 0; i < WeaponCount(); i++ ) - { - if ( GetWeapon(i) ) // each item updates it's successors - GetWeapon(i)->UpdateClientData( this ); - } - - // update the client with our poison state - m_Local.m_bPoisoned = ( m_bitsDamageType & DMG_POISON ) - && ( m_nPoisonDmg > m_nPoisonRestored ) - && ( m_iHealth < 100 ); - - // Let any global rules update the HUD, too - g_pGameRules->UpdateClientData( this ); -} - -#ifdef _XBOX -void CBasePlayer::RumbleEffect( unsigned char index, unsigned char rumbleData, unsigned char rumbleFlags ) -{ - if( !IsAlive() ) - return; - - CSingleUserRecipientFilter filter( this ); - filter.MakeReliable(); - - UserMessageBegin( filter, "XBoxRumble" ); - WRITE_BYTE( index ); - WRITE_BYTE( rumbleData ); - WRITE_BYTE( rumbleFlags ); - MessageEnd(); -} -#else -void CBasePlayer::RumbleEffect( unsigned char index, unsigned char rumbleData, unsigned char rumbleFlags ) -{ - return; -} -#endif//_XBOX - - -void CBasePlayer::EnableControl(bool fControl) -{ - if (!fControl) - AddFlag( FL_FROZEN ); - else - RemoveFlag( FL_FROZEN ); - -} - -void CBasePlayer::CheckTrainUpdate( void ) -{ - if ( IsPC() && ( m_iTrain & TRAIN_NEW ) ) - { - CSingleUserRecipientFilter user( this ); - user.MakeReliable(); - - // send "Train" update message - UserMessageBegin( user, "Train" ); - WRITE_BYTE(m_iTrain & 0xF); - MessageEnd(); - - m_iTrain &= ~TRAIN_NEW; - } -} - -//----------------------------------------------------------------------------- -// Purpose: Returns whether the player should autoaim or not -// Output : Returns true on success, false on failure. -//----------------------------------------------------------------------------- -bool CBasePlayer::ShouldAutoaim( void ) -{ - // cannot be in multiplayer - if ( gpGlobals->maxClients > 1 ) - return false; - - // autoaiming is only for easy and medium skill - return ( IsXbox() || !g_pGameRules->IsSkillLevel(SKILL_HARD) ); -} - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -Vector CBasePlayer::GetAutoaimVector( float flScale ) -{ - autoaim_params_t params; - - params.m_fScale = flScale; - params.m_fMaxDist = autoaim_max_dist.GetFloat(); - - GetAutoaimVector( params ); - return params.m_vecAutoAimDir; -} - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -Vector CBasePlayer::GetAutoaimVector( float flScale, float flMaxDist ) -{ - autoaim_params_t params; - - params.m_fScale = flScale; - params.m_fMaxDist = flMaxDist; - - GetAutoaimVector( params ); - return params.m_vecAutoAimDir; -} - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -void CBasePlayer::GetAutoaimVector( autoaim_params_t ¶ms ) -{ - // Assume autoaim will not be assisting. - params.m_bAutoAimAssisting = false; - - if ( ( ShouldAutoaim() == false ) || ( params.m_fScale == AUTOAIM_SCALE_DIRECT_ONLY ) ) - { - Vector forward; - AngleVectors( EyeAngles() + m_Local.m_vecPunchAngle, &forward ); - - params.m_vecAutoAimDir = forward; - params.m_hAutoAimEntity.Set(NULL); - params.m_vecAutoAimPoint = vec3_invalid; - params.m_bAutoAimAssisting = false; - return; - } - - Vector vecSrc = Weapon_ShootPosition( ); - - m_vecAutoAim.Init( 0.0f, 0.0f, 0.0f ); - - QAngle angles = AutoaimDeflection( vecSrc, params ); - - // update ontarget if changed - if ( !g_pGameRules->AllowAutoTargetCrosshair() ) - m_fOnTarget = false; - - if (angles.x > 180) - angles.x -= 360; - if (angles.x < -180) - angles.x += 360; - if (angles.y > 180) - angles.y -= 360; - if (angles.y < -180) - angles.y += 360; - - if (angles.x > 25) - angles.x = 25; - if (angles.x < -25) - angles.x = -25; - if (angles.y > 12) - angles.y = 12; - if (angles.y < -12) - angles.y = -12; - - Vector forward; - - if( IsXbox() && IsInAVehicle() ) - { - m_vecAutoAim = angles; - AngleVectors( EyeAngles() + m_vecAutoAim, &forward ); - } - else - { - // always use non-sticky autoaim - m_vecAutoAim = angles * 0.9f; - AngleVectors( EyeAngles() + m_Local.m_vecPunchAngle + m_vecAutoAim, &forward ); - } - - params.m_vecAutoAimDir = forward; -} - -//----------------------------------------------------------------------------- -// Targets represent themselves to autoaim as a viewplane-parallel disc with -// a radius specified by the target. The player then modifies this radius -// to achieve more or less aggressive aiming assistance -//----------------------------------------------------------------------------- -float CBasePlayer::GetAutoaimScore( const Vector &eyePosition, const Vector &viewDir, const Vector &vecTarget, CBaseEntity *pTarget, float fScale ) -{ - float radiusSqr; - float targetRadiusSqr = Square( (pTarget->GetAutoAimRadius() * fScale) ); - - Vector vecNearestPoint = PointOnLineNearestPoint( eyePosition, eyePosition + viewDir * 8192, vecTarget ); - Vector vecDiff = vecTarget - vecNearestPoint; - - radiusSqr = vecDiff.LengthSqr(); - - if( radiusSqr <= targetRadiusSqr ) - { - float score; - - score = 1.0f - (radiusSqr / targetRadiusSqr); - - Assert( score >= 0.0f && score <= 1.0f ); - return score; - } - - // 0 means no score- doesn't qualify for autoaim. - return 0.0f; -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : &vecSrc - -// flDist - -// flDelta - -// Output : Vector -//----------------------------------------------------------------------------- -QAngle CBasePlayer::AutoaimDeflection( Vector &vecSrc, autoaim_params_t ¶ms ) -{ - float bestscore; - float score; - QAngle eyeAngles; - Vector bestdir; - CBaseEntity *bestent; - trace_t tr; - Vector v_forward, v_right, v_up; - - if ( ShouldAutoaim() == false ) - { - m_fOnTarget = false; - return vec3_angle; - } - - eyeAngles = EyeAngles(); - AngleVectors( eyeAngles + m_Local.m_vecPunchAngle + m_vecAutoAim, &v_forward, &v_right, &v_up ); - - // try all possible entities - bestdir = v_forward; - bestscore = 0.0f; - bestent = NULL; - - //Reset this data - m_fOnTarget = false; - params.m_bOnTargetNatural = false; - - CBaseEntity *pIgnore = NULL; - - if( IsInAVehicle() ) - { - pIgnore = GetVehicleEntity(); - } - - CTraceFilterSkipTwoEntities traceFilter( this, pIgnore, COLLISION_GROUP_NONE ); - - UTIL_TraceLine( vecSrc, vecSrc + bestdir * MAX_COORD_FLOAT, MASK_SHOT, &traceFilter, &tr ); - - CBaseEntity *pEntHit = tr.m_pEnt; - - if ( pEntHit && pEntHit->m_takedamage != DAMAGE_NO && pEntHit->GetHealth() > 0 ) - { - // don't look through water - if (!((GetWaterLevel() != 3 && pEntHit->GetWaterLevel() == 3) || (GetWaterLevel() == 3 && pEntHit->GetWaterLevel() == 0))) - { - if( pEntHit->GetFlags() & FL_AIMTARGET ) - { - bool bAimAtThis = true; - - if( UseXboxAiming() && pEntHit->IsNPC() ) - { - int iRelationType = GetDefaultRelationshipDisposition( pEntHit->Classify() ); - - if( iRelationType != D_HT ) - { - bAimAtThis = false; - } - } - - if( bAimAtThis ) - { - if ( pEntHit->GetFlags() & FL_AIMTARGET ) - { - m_fOnTarget = true; - } - - // Player is already on target naturally, don't autoaim. - // Fill out the autoaim_params_t struct, though. - params.m_hAutoAimEntity.Set(pEntHit); - params.m_vecAutoAimDir = bestdir; - params.m_vecAutoAimPoint = tr.endpos; - params.m_bAutoAimAssisting = false; - params.m_bOnTargetNatural = true; - } - } - - return vec3_angle; - } - } - - int count = AimTarget_ListCount(); - if ( count ) - { - CBaseEntity **pList = (CBaseEntity **)stackalloc( sizeof(CBaseEntity *) * count ); - AimTarget_ListCopy( pList, count ); - - for ( int i = 0; i < count; i++ ) - { - Vector center; - Vector dir; - CBaseEntity *pEntity = pList[i]; - - // Don't shoot yourself - if ( pEntity == this ) - continue; - - if (!pEntity->IsAlive() || !pEntity->edict() ) - continue; - - if ( !g_pGameRules->ShouldAutoAim( this, pEntity->edict() ) ) - continue; - - // don't look through water - if ((GetWaterLevel() != 3 && pEntity->GetWaterLevel() == 3) || (GetWaterLevel() == 3 && pEntity->GetWaterLevel() == 0)) - continue; - - if( pEntity->MyNPCPointer() ) - { - // If this entity is an NPC, only aim if it is an enemy. - if ( IRelationType( pEntity ) != D_HT && !pEntity->ShouldAttractAutoAim(this) ) - { - if ( !pEntity->IsPlayer() && !g_pGameRules->IsDeathmatch()) - // Msg( "friend\n"); - continue; - } - } - - // Don't autoaim at the noisy bodytarget, this makes the autoaim crosshair wobble. - //center = pEntity->BodyTarget( vecSrc, false ); - center = pEntity->WorldSpaceCenter(); - - dir = (center - vecSrc); - - float dist = dir.Length2D(); - VectorNormalize( dir ); - - // Skip if out of range. - if( dist > params.m_fMaxDist ) - continue; - - float dot = DotProduct (dir, v_forward ); - - // make sure it's in front of the player - if( dot < 0 ) - continue; - - if( !(pEntity->GetFlags() & FL_FLY) ) - { - // Refuse to take wild shots at targets far from reticle. - if( GetActiveWeapon() != NULL && dot < GetActiveWeapon()->GetMaxAutoAimDeflection() ) - { - continue; - } - } - - score = GetAutoaimScore(vecSrc, v_forward, pEntity->GetAutoAimCenter(), pEntity, params.m_fScale); - - if( score <= bestscore ) - { - continue; - } - - UTIL_TraceLine( vecSrc, center, MASK_SHOT, &traceFilter, &tr ); - - if (tr.fraction != 1.0 && tr.m_pEnt != pEntity ) - { - // Msg( "hit %s, can't see %s\n", STRING( tr.u.ent->classname ), STRING( pEdict->classname ) ); - continue; - } - - // This is the best candidate so far. - bestscore = score; - bestent = pEntity; - bestdir = dir; - } - if ( bestent ) - { - QAngle bestang; - - VectorAngles( bestdir, bestang ); - - if( IsInAVehicle() ) - { - bestang -= EyeAngles(); - } - else - { - bestang -= EyeAngles() - m_Local.m_vecPunchAngle; - } - - m_fOnTarget = true; - - // Autoaim detected a target for us. Aim automatically at its bodytarget. - params.m_hAutoAimEntity.Set(bestent); - params.m_vecAutoAimDir = bestdir; - params.m_vecAutoAimPoint = bestent->BodyTarget( vecSrc, false ); - params.m_bAutoAimAssisting = true; - - return bestang; - } - } - - return QAngle( 0, 0, 0 ); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CBasePlayer::ResetAutoaim( void ) -{ - if (m_vecAutoAim.x != 0 || m_vecAutoAim.y != 0) - { - m_vecAutoAim = QAngle( 0, 0, 0 ); - engine->CrosshairAngle( edict(), 0, 0 ); - } - m_fOnTarget = false; -} - -// ========================================================================== -// > Weapon stuff -// ========================================================================== - -//----------------------------------------------------------------------------- -// Purpose: Override base class, player can always use weapon -// Input : A weapon -// Output : true or false -//----------------------------------------------------------------------------- -bool CBasePlayer::Weapon_CanUse( CBaseCombatWeapon *pWeapon ) -{ - return true; -} - - - -//----------------------------------------------------------------------------- -// Purpose: Override to clear dropped weapon from the hud -//----------------------------------------------------------------------------- -void CBasePlayer::Weapon_Drop( CBaseCombatWeapon *pWeapon, const Vector *pvecTarget /* = NULL */, const Vector *pVelocity /* = NULL */ ) -{ - bool bWasActiveWeapon = false; - if ( pWeapon == GetActiveWeapon() ) - { - bWasActiveWeapon = true; - } - - if ( pWeapon ) - { - if ( bWasActiveWeapon ) - { - pWeapon->SendWeaponAnim( ACT_VM_IDLE ); - } - } - - BaseClass::Weapon_Drop( pWeapon, pvecTarget, pVelocity ); - - if ( bWasActiveWeapon ) - { - if (!SwitchToNextBestWeapon( NULL )) - { - CBaseViewModel *vm = GetViewModel(); - if ( vm ) - { - vm->AddEffects( EF_NODRAW ); - } - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : weaponSlot - -//----------------------------------------------------------------------------- -void CBasePlayer::Weapon_DropSlot( int weaponSlot ) -{ - CBaseCombatWeapon *pWeapon; - - // Check for that slot being occupied already - for ( int i=0; i < MAX_WEAPONS; i++ ) - { - pWeapon = GetWeapon( i ); - - if ( pWeapon != NULL ) - { - // If the slots match, it's already occupied - if ( pWeapon->GetSlot() == weaponSlot ) - { - Weapon_Drop( pWeapon, NULL, NULL ); - } - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: Override to add weapon to the hud -//----------------------------------------------------------------------------- -void CBasePlayer::Weapon_Equip( CBaseCombatWeapon *pWeapon ) -{ - BaseClass::Weapon_Equip( pWeapon ); - - bool bShouldSwitch = g_pGameRules->FShouldSwitchWeapon( this, pWeapon ); - -#ifdef HL2_DLL - if ( bShouldSwitch == false && PhysCannonGetHeldEntity( GetActiveWeapon() ) == pWeapon && - Weapon_OwnsThisType( pWeapon->GetClassname(), pWeapon->GetSubType()) ) - { - bShouldSwitch = true; - } -#endif//HL2_DLL - - // should we switch to this item? - if ( bShouldSwitch ) - { - Weapon_Switch( pWeapon ); - } -} - - -//========================================================= -// HasNamedPlayerItem Does the player already have this item? -//========================================================= -CBaseEntity *CBasePlayer::HasNamedPlayerItem( const char *pszItemName ) -{ - for ( int i = 0 ; i < WeaponCount() ; i++ ) - { - if ( !GetWeapon(i) ) - continue; - - if ( FStrEq( pszItemName, GetWeapon(i)->GetClassname() ) ) - { - return GetWeapon(i); - } - } - - return NULL; -} - - - -//================================================================================ -// TEAM HANDLING -//================================================================================ -//----------------------------------------------------------------------------- -// Purpose: Put the player in the specified team -//----------------------------------------------------------------------------- - -void CBasePlayer::ChangeTeam( int iTeamNum ) -{ - if ( !GetGlobalTeam( iTeamNum ) ) - { - Warning( "CBasePlayer::ChangeTeam( %d ) - invalid team index.\n", iTeamNum ); - return; - } - - // if this is our current team, just abort - if ( iTeamNum == GetTeamNumber() ) - { - return; - } - - // Immediately tell all clients that he's changing team. This has to be done - // first, so that all user messages that follow as a result of the team change - // come after this one, allowing the client to be prepared for them. - IGameEvent * event = gameeventmanager->CreateEvent( "player_team" ); - if ( event ) - { - event->SetInt("userid", GetUserID() ); - event->SetInt("team", iTeamNum ); - event->SetInt("oldteam", GetTeamNumber() ); - event->SetInt("disconnect", IsDisconnecting()); - - gameeventmanager->FireEvent( event ); - } - - // Remove him from his current team - if ( GetTeam() ) - { - GetTeam()->RemovePlayer( this ); - } - - // Are we being added to a team? - if ( iTeamNum ) - { - GetGlobalTeam( iTeamNum )->AddPlayer( this ); - } - - BaseClass::ChangeTeam( iTeamNum ); -} - - - -//----------------------------------------------------------------------------- -// Purpose: Locks a player to the spot; they can't move, shoot, or be hurt -//----------------------------------------------------------------------------- -void CBasePlayer::LockPlayerInPlace( void ) -{ - if ( m_iPlayerLocked ) - return; - - AddFlag( FL_GODMODE | FL_FROZEN ); - SetMoveType( MOVETYPE_NONE ); - m_iPlayerLocked = true; - - // force a client data update, so that anything that has been done to - // this player previously this frame won't get delayed in being sent - UpdateClientData(); -} - -//----------------------------------------------------------------------------- -// Purpose: Unlocks a previously locked player -//----------------------------------------------------------------------------- -void CBasePlayer::UnlockPlayer( void ) -{ - if ( !m_iPlayerLocked ) - return; - - RemoveFlag( FL_GODMODE | FL_FROZEN ); - SetMoveType( MOVETYPE_WALK ); - m_iPlayerLocked = false; -} - -bool CBasePlayer::ClearUseEntity() -{ - if ( m_hUseEntity != NULL ) - { - // Stop controlling the train/object - // TODO: Send HUD Update - m_hUseEntity->Use( this, this, USE_OFF, 0 ); - m_hUseEntity = NULL; - return true; - } - - return false; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CBasePlayer::HideViewModels( void ) -{ - for ( int i = 0 ; i < MAX_VIEWMODELS; i++ ) - { - CBaseViewModel *vm = GetViewModel( i ); - if ( !vm ) - continue; - - vm->SetWeaponModel( NULL, NULL ); - } -} - -class CStripWeapons : public CPointEntity -{ - DECLARE_CLASS( CStripWeapons, CPointEntity ); -public: - void InputStripWeapons(inputdata_t &data); - void InputStripWeaponsAndSuit(inputdata_t &data); - - void StripWeapons(inputdata_t &data, bool stripSuit); - DECLARE_DATADESC(); -}; - -LINK_ENTITY_TO_CLASS( player_weaponstrip, CStripWeapons ); - -BEGIN_DATADESC( CStripWeapons ) - DEFINE_INPUTFUNC( FIELD_VOID, "Strip", InputStripWeapons ), - DEFINE_INPUTFUNC( FIELD_VOID, "StripWeaponsAndSuit", InputStripWeaponsAndSuit ), -END_DATADESC() - - -void CStripWeapons::InputStripWeapons(inputdata_t &data) -{ - StripWeapons(data, false); -} - -void CStripWeapons::InputStripWeaponsAndSuit(inputdata_t &data) -{ - StripWeapons(data, true); -} - -void CStripWeapons::StripWeapons(inputdata_t &data, bool stripSuit) -{ - CBasePlayer *pPlayer = NULL; - - if ( data.pActivator && data.pActivator->IsPlayer() ) - { - pPlayer = (CBasePlayer *)data.pActivator; - } - else if ( !g_pGameRules->IsDeathmatch() ) - { - pPlayer = UTIL_GetLocalPlayer(); - } - - if ( pPlayer ) - { - pPlayer->RemoveAllItems( stripSuit ); - } -} - - -class CRevertSaved : public CPointEntity -{ - DECLARE_CLASS( CRevertSaved, CPointEntity ); -public: - void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); - void LoadThink( void ); - - DECLARE_DATADESC(); - - inline float Duration( void ) { return m_Duration; } - inline float HoldTime( void ) { return m_HoldTime; } - inline float LoadTime( void ) { return m_loadTime; } - - inline void SetDuration( float duration ) { m_Duration = duration; } - inline void SetHoldTime( float hold ) { m_HoldTime = hold; } - inline void SetLoadTime( float time ) { m_loadTime = time; } - - //Inputs - void InputReload(inputdata_t &data); - -#ifdef HL1_DLL - void MessageThink( void ); - inline float MessageTime( void ) { return m_messageTime; } - inline void SetMessageTime( float time ) { m_messageTime = time; } -#endif - -private: - - float m_loadTime; - float m_Duration; - float m_HoldTime; - -#ifdef HL1_DLL - string_t m_iszMessage; - float m_messageTime; -#endif -}; - -LINK_ENTITY_TO_CLASS( player_loadsaved, CRevertSaved ); - -BEGIN_DATADESC( CRevertSaved ) - -#ifdef HL1_DLL - DEFINE_KEYFIELD( m_iszMessage, FIELD_STRING, "message" ), - DEFINE_KEYFIELD( m_messageTime, FIELD_FLOAT, "messagetime" ), // These are not actual times, but durations, so save as floats - - DEFINE_FUNCTION( MessageThink ), -#endif - - DEFINE_KEYFIELD( m_loadTime, FIELD_FLOAT, "loadtime" ), - DEFINE_KEYFIELD( m_Duration, FIELD_FLOAT, "duration" ), - DEFINE_KEYFIELD( m_HoldTime, FIELD_FLOAT, "holdtime" ), - - DEFINE_INPUTFUNC( FIELD_VOID, "Reload", InputReload ), - - - // Function Pointers - DEFINE_FUNCTION( LoadThink ), - -END_DATADESC() - -CBaseEntity *CreatePlayerLoadSave( Vector vOrigin, float flDuration, float flHoldTime, float flLoadTime ) -{ - CRevertSaved *pRevertSaved = (CRevertSaved *) CreateEntityByName( "player_loadsaved" ); - - if ( pRevertSaved == NULL ) - return NULL; - - UTIL_SetOrigin( pRevertSaved, vOrigin ); - - pRevertSaved->Spawn(); - pRevertSaved->SetDuration( flDuration ); - pRevertSaved->SetHoldTime( flHoldTime ); - pRevertSaved->SetLoadTime( flLoadTime ); - - return pRevertSaved; -} - - - -void CRevertSaved::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) -{ - UTIL_ScreenFadeAll( m_clrRender, Duration(), HoldTime(), FFADE_OUT ); - SetNextThink( gpGlobals->curtime + LoadTime() ); - SetThink( &CRevertSaved::LoadThink ); - - CBasePlayer *pPlayer = UTIL_GetLocalPlayer(); - - if ( pPlayer ) - { - //Adrian: Setting this flag so we can't move or save a game. - pPlayer->pl.deadflag = true; - pPlayer->AddFlag( (FL_NOTARGET|FL_FROZEN) ); - } -} - -void CRevertSaved::InputReload( inputdata_t &inputdata ) -{ - UTIL_ScreenFadeAll( m_clrRender, Duration(), HoldTime(), FFADE_OUT ); - -#ifdef HL1_DLL - SetNextThink( gpGlobals->curtime + MessageTime() ); - SetThink( &CRevertSaved::MessageThink ); -#else - SetNextThink( gpGlobals->curtime + LoadTime() ); - SetThink( &CRevertSaved::LoadThink ); -#endif - - CBasePlayer *pPlayer = UTIL_GetLocalPlayer(); - - if ( pPlayer ) - { - //Adrian: Setting this flag so we can't move or save a game. - pPlayer->pl.deadflag = true; - pPlayer->AddFlag( (FL_NOTARGET|FL_FROZEN) ); - } -} - -#ifdef HL1_DLL -void CRevertSaved::MessageThink( void ) -{ - UTIL_ShowMessageAll( STRING( m_iszMessage ) ); - float nextThink = LoadTime() - MessageTime(); - if ( nextThink > 0 ) - { - SetNextThink( gpGlobals->curtime + nextThink ); - SetThink( &CRevertSaved::LoadThink ); - } - else - LoadThink(); -} -#endif - - -void CRevertSaved::LoadThink( void ) -{ - if ( !gpGlobals->deathmatch ) - { - engine->ServerCommand("reload\n"); - } -} - -class CMovementSpeedMod : public CPointEntity -{ - DECLARE_CLASS( CMovementSpeedMod, CPointEntity ); -public: - void InputSpeedMod(inputdata_t &data); - - DECLARE_DATADESC(); -}; - -LINK_ENTITY_TO_CLASS( player_speedmod, CMovementSpeedMod ); - -BEGIN_DATADESC( CMovementSpeedMod ) - DEFINE_INPUTFUNC( FIELD_FLOAT, "ModifySpeed", InputSpeedMod ), -END_DATADESC() - - -void CMovementSpeedMod::InputSpeedMod(inputdata_t &data) -{ - CBasePlayer *pPlayer = NULL; - - if ( data.pActivator && data.pActivator->IsPlayer() ) - { - pPlayer = (CBasePlayer *)data.pActivator; - } - else if ( !g_pGameRules->IsDeathmatch() ) - { - pPlayer = UTIL_GetLocalPlayer(); - } - - if ( pPlayer ) - { - pPlayer->SetLaggedMovementValue( data.value.Float() ); - } -} - - -void SendProxy_CropFlagsToPlayerFlagBitsLength( const SendProp *pProp, const void *pStruct, const void *pVarData, DVariant *pOut, int iElement, int objectID) -{ - int mask = (1<m_Int = ( data & mask ); -} -// -------------------------------------------------------------------------------- // -// SendTable for CPlayerState. -// -------------------------------------------------------------------------------- // - - BEGIN_SEND_TABLE_NOBASE(CPlayerState, DT_PlayerState) - SendPropInt (SENDINFO(deadflag), 1, SPROP_UNSIGNED ), - END_SEND_TABLE() - -// -------------------------------------------------------------------------------- // -// This data only gets sent to clients that ARE this player entity. -// -------------------------------------------------------------------------------- // - - BEGIN_SEND_TABLE_NOBASE( CBasePlayer, DT_LocalPlayerExclusive ) - - SendPropDataTable ( SENDINFO_DT(m_Local), &REFERENCE_SEND_TABLE(DT_Local) ), - -// If HL2_DLL is defined, then baseflex.cpp already sends these. -#ifndef HL2_DLL - SendPropFloat ( SENDINFO_VECTORELEM(m_vecViewOffset, 0), 8, SPROP_ROUNDDOWN, -32.0, 32.0f), - SendPropFloat ( SENDINFO_VECTORELEM(m_vecViewOffset, 1), 8, SPROP_ROUNDDOWN, -32.0, 32.0f), - SendPropFloat ( SENDINFO_VECTORELEM(m_vecViewOffset, 2), 10, SPROP_CHANGES_OFTEN, 0.0f, 128.0f), -#endif - - SendPropFloat ( SENDINFO(m_flFriction), 8, SPROP_ROUNDDOWN, 0.0f, 4.0f), - - SendPropArray3 ( SENDINFO_ARRAY3(m_iAmmo), SendPropInt( SENDINFO_ARRAY(m_iAmmo), 10, SPROP_UNSIGNED ) ), - - SendPropInt ( SENDINFO( m_fOnTarget ), 2, SPROP_UNSIGNED ), - - SendPropInt ( SENDINFO( m_nTickBase ), -1, SPROP_CHANGES_OFTEN ), - SendPropInt ( SENDINFO( m_nNextThinkTick ) ), - - SendPropEHandle ( SENDINFO( m_hLastWeapon ) ), - SendPropEHandle ( SENDINFO( m_hGroundEntity ), SPROP_CHANGES_OFTEN ), - - SendPropFloat ( SENDINFO_VECTORELEM(m_vecVelocity, 0), 20, SPROP_CHANGES_OFTEN, -2048.0f, 2048.0f ), - SendPropFloat ( SENDINFO_VECTORELEM(m_vecVelocity, 1), 20, SPROP_CHANGES_OFTEN, -2048.0f, 2048.0f ), - SendPropFloat ( SENDINFO_VECTORELEM(m_vecVelocity, 2), 16, SPROP_CHANGES_OFTEN, -2048.0f, 2048.0f ), - - SendPropVector ( SENDINFO( m_vecBaseVelocity ), 20, 0, -1000, 1000 ), - - SendPropEHandle ( SENDINFO( m_hConstraintEntity)), - SendPropVector ( SENDINFO( m_vecConstraintCenter), 0, SPROP_NOSCALE ), - SendPropFloat ( SENDINFO( m_flConstraintRadius ), 0, SPROP_NOSCALE ), - SendPropFloat ( SENDINFO( m_flConstraintWidth ), 0, SPROP_NOSCALE ), - SendPropFloat ( SENDINFO( m_flConstraintSpeedFactor ), 0, SPROP_NOSCALE ), - - SendPropFloat ( SENDINFO( m_flDeathTime ), 0, SPROP_NOSCALE ), - - SendPropInt ( SENDINFO( m_nWaterLevel ), 2, SPROP_UNSIGNED ), - SendPropFloat ( SENDINFO( m_flLaggedMovementValue ), 0, SPROP_NOSCALE ), - - END_SEND_TABLE() - - -// -------------------------------------------------------------------------------- // -// DT_BasePlayer sendtable. -// -------------------------------------------------------------------------------- // - - IMPLEMENT_SERVERCLASS_ST( CBasePlayer, DT_BasePlayer ) - - SendPropDataTable(SENDINFO_DT(pl), &REFERENCE_SEND_TABLE(DT_PlayerState), SendProxy_DataTableToDataTable), - SendPropEHandle(SENDINFO(m_hVehicle)), - SendPropEHandle(SENDINFO(m_hUseEntity)), - SendPropInt (SENDINFO(m_iHealth), 10 ), - SendPropInt (SENDINFO(m_lifeState), 3, SPROP_UNSIGNED ), - SendPropFloat (SENDINFO(m_flMaxspeed), 12, SPROP_ROUNDDOWN, 0.0f, 2048.0f ), // CL - SendPropInt (SENDINFO(m_fFlags), PLAYER_FLAG_BITS, SPROP_UNSIGNED|SPROP_CHANGES_OFTEN, SendProxy_CropFlagsToPlayerFlagBitsLength ), - SendPropInt (SENDINFO(m_iObserverMode), 3, SPROP_UNSIGNED ), - SendPropEHandle (SENDINFO(m_hObserverTarget) ), - SendPropInt (SENDINFO(m_iFOV), 8, SPROP_UNSIGNED ), - SendPropInt (SENDINFO(m_iDefaultFOV), 8, SPROP_UNSIGNED ), - SendPropArray ( SendPropEHandle( SENDINFO_ARRAY( m_hViewModel ) ), m_hViewModel ), - SendPropString (SENDINFO(m_szLastPlaceName) ), - - // Data that only gets sent to the local player. - SendPropDataTable( "localdata", 0, &REFERENCE_SEND_TABLE(DT_LocalPlayerExclusive), SendProxy_SendLocalDataTable ), - - END_SEND_TABLE() - -//============================================================================= -// -// Player Physics Shadow Code -// - -void CBasePlayer::SetupVPhysicsShadow( CPhysCollide *pStandModel, const char *pStandHullName, CPhysCollide *pCrouchModel, const char *pCrouchHullName ) -{ - solid_t solid; - Q_strncpy( solid.surfaceprop, "player", sizeof(solid.surfaceprop) ); - solid.params = g_PhysDefaultObjectParams; - solid.params.mass = 85.0f; - solid.params.inertia = 1e24f; - solid.params.enableCollisions = false; - //disable drag - solid.params.dragCoefficient = 0; - // create standing hull - m_pShadowStand = PhysModelCreateCustom( this, pStandModel, GetLocalOrigin(), GetLocalAngles(), pStandHullName, false, &solid ); - m_pShadowStand->SetCallbackFlags( CALLBACK_GLOBAL_COLLISION | CALLBACK_SHADOW_COLLISION ); - - // create crouchig hull - m_pShadowCrouch = PhysModelCreateCustom( this, pCrouchModel, GetLocalOrigin(), GetLocalAngles(), pCrouchHullName, false, &solid ); - m_pShadowCrouch->SetCallbackFlags( CALLBACK_GLOBAL_COLLISION | CALLBACK_SHADOW_COLLISION ); - - // default to stand - VPhysicsSetObject( m_pShadowStand ); - - // tell physics lists I'm a shadow controller object - PhysAddShadow( this ); - m_pPhysicsController = physenv->CreatePlayerController( m_pShadowStand ); - m_pPhysicsController->SetPushMassLimit( 350.0f ); - m_pPhysicsController->SetPushSpeedLimit( 50.0f ); - - // Give the controller a valid position so it doesn't do anything rash. - UpdatePhysicsShadowToCurrentPosition(); - - // init state - if ( GetFlags() & FL_DUCKING ) - { - SetVCollisionState( VPHYS_CROUCH ); - } - else - { - SetVCollisionState( VPHYS_WALK ); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Empty, just want to keep the baseentity version from being called -// current so we don't kick up dust, etc. -//----------------------------------------------------------------------------- -void CBasePlayer::VPhysicsCollision( int index, gamevcollisionevent_t *pEvent ) -{ -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CBasePlayer::VPhysicsUpdate( IPhysicsObject *pPhysics ) -{ - float savedImpact = m_impactEnergyScale; - - // HACKHACK: Reduce player's stress by 1/8th - m_impactEnergyScale *= 0.125f; - ApplyStressDamage( pPhysics, true ); - m_impactEnergyScale = savedImpact; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CBasePlayer::VPhysicsShadowUpdate( IPhysicsObject *pPhysics ) -{ - if ( sv_turbophysics.GetBool() ) - return; - - Vector newPosition; - - bool physicsUpdated = m_pPhysicsController->GetShadowPosition( &newPosition, NULL ) > 0 ? true : false; - - // UNDONE: If the player is penetrating, but the player's game collisions are not stuck, teleport the physics shadow to the game position - if ( pPhysics->GetGameFlags() & FVPHYSICS_PENETRATING ) - { - CUtlVector list; - PhysGetListOfPenetratingEntities( this, list ); - for ( int i = list.Count()-1; i >= 0; --i ) - { - // filter out anything that isn't simulated by vphysics - // UNDONE: Filter out motion disabled objects? - if ( list[i]->GetMoveType() == MOVETYPE_VPHYSICS ) - { - // I'm currently stuck inside a moving object, so allow vphysics to - // apply velocity to the player in order to separate these objects - m_touchedPhysObject = true; - } - - // if it's an NPC, tell them that the player is intersecting them - CAI_BaseNPC *pNPC = list[i]->MyNPCPointer(); - if ( pNPC ) - { - pNPC->PlayerPenetratingVPhysics(); - } - } - } - - if ( m_pPhysicsController->IsInContact() || (m_afPhysicsFlags & PFLAG_VPHYSICS_MOTIONCONTROLLER) ) - { - m_touchedPhysObject = true; - } - - if ( IsFollowingPhysics() ) - { - m_touchedPhysObject = true; - } - - if ( GetMoveType() == MOVETYPE_NOCLIP ) - { - m_oldOrigin = GetAbsOrigin(); - return; - } - - if ( phys_timescale.GetFloat() == 0.0f ) - { - physicsUpdated = false; - } - - if ( !physicsUpdated ) - return; - - IPhysicsObject *pPhysGround = GetGroundVPhysics(); - - Vector newVelocity; - pPhysics->GetPosition( &newPosition, 0 ); - m_pPhysicsController->GetShadowVelocity( &newVelocity ); - - if ( physicsshadowupdate_render.GetBool() ) - { - NDebugOverlay::Box( GetAbsOrigin(), WorldAlignMins(), WorldAlignMaxs(), 255, 0, 0, 24, 15.0f ); - NDebugOverlay::Box( newPosition, WorldAlignMins(), WorldAlignMaxs(), 0,0,255, 24, 15.0f); - // NDebugOverlay::Box( newPosition, WorldAlignMins(), WorldAlignMaxs(), 0,0,255, 24, .01f); - } - - Vector tmp = GetAbsOrigin() - newPosition; - if ( !m_touchedPhysObject && !(GetFlags() & FL_ONGROUND) ) - { - tmp.z *= 0.5f; // don't care about z delta as much - } - - float dist = tmp.LengthSqr(); - float deltaV = (newVelocity - GetAbsVelocity()).LengthSqr(); - - float maxDistErrorSqr = VPHYS_MAX_DISTSQR; - float maxVelErrorSqr = VPHYS_MAX_VELSQR; - if ( IsRideablePhysics(pPhysGround) ) - { - maxDistErrorSqr *= 0.25; - maxVelErrorSqr *= 0.25; - } - - if ( dist >= maxDistErrorSqr || deltaV >= maxVelErrorSqr || (pPhysGround && !m_touchedPhysObject) ) - { - if ( m_touchedPhysObject || pPhysGround ) - { - // BUGBUG: Rewrite this code using fixed timestep - if ( deltaV >= maxVelErrorSqr ) - { - Vector dir = GetAbsVelocity(); - float len = VectorNormalize(dir); - float dot = DotProduct( newVelocity, dir ); - if ( dot > len ) - { - dot = len; - } - else if ( dot < -len ) - { - dot = -len; - } - - VectorMA( newVelocity, -dot, dir, newVelocity ); - - if ( m_afPhysicsFlags & PFLAG_VPHYSICS_MOTIONCONTROLLER ) - { - float val = Lerp( 0.1f, len, dot ); - VectorMA( newVelocity, val - len, dir, newVelocity ); - } - - if ( !IsRideablePhysics(pPhysGround) ) - { - if ( !(m_afPhysicsFlags & PFLAG_VPHYSICS_MOTIONCONTROLLER ) && IsSimulatingOnAlternateTicks() ) - { - newVelocity *= 0.5f; - } - ApplyAbsVelocityImpulse( newVelocity ); - } - } - - trace_t trace; - UTIL_TraceEntity( this, newPosition, newPosition, MASK_PLAYERSOLID, this, COLLISION_GROUP_PLAYER_MOVEMENT, &trace ); - if ( !trace.allsolid && !trace.startsolid ) - { - SetAbsOrigin( newPosition ); - } - } - else - { - trace_t trace; - UTIL_TraceEntity( this, GetAbsOrigin(), GetAbsOrigin(), MASK_PLAYERSOLID, this, COLLISION_GROUP_PLAYER_MOVEMENT, &trace ); - - // current position is not ok, fixup - if ( trace.allsolid || trace.startsolid ) - { - // STUCK!?!?! - //Warning( "Stuck2 on %s!!\n", trace.m_pEnt->GetClassname() ); - SetAbsOrigin( newPosition ); - } - } - } - else - { - if ( m_touchedPhysObject ) - { - // check my position (physics object could have simulated into my position - // physics is not very far away, check my position - trace_t trace; - UTIL_TraceEntity( this, GetAbsOrigin(), GetAbsOrigin(), - MASK_PLAYERSOLID, this, COLLISION_GROUP_PLAYER_MOVEMENT, &trace ); - - // is current position ok? - if ( trace.allsolid || trace.startsolid ) - { - // stuck????!?!? - //Msg("Stuck on %s\n", trace.m_pEnt->GetClassName()); - SetAbsOrigin( newPosition ); - UTIL_TraceEntity( this, GetAbsOrigin(), GetAbsOrigin(), - MASK_PLAYERSOLID, this, COLLISION_GROUP_PLAYER_MOVEMENT, &trace ); - if ( trace.allsolid || trace.startsolid ) - { - //Msg("Double Stuck\n"); - SetAbsOrigin( m_oldOrigin ); - } - } - } - } - m_oldOrigin = GetAbsOrigin(); - // UNDONE: Force physics object to be at player position when not touching phys??? -} - -// recreate physics on save/load, don't try to save the state! -bool CBasePlayer::ShouldSavePhysics() -{ - return false; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CBasePlayer::InitVCollision( void ) -{ - // Cleanup any old vphysics stuff. - VPhysicsDestroyObject(); - - // in turbo physics players dont have a physics shadow - if ( sv_turbophysics.GetBool() ) - return; - - CPhysCollide *pModel = PhysCreateBbox( VEC_HULL_MIN, VEC_HULL_MAX ); - CPhysCollide *pCrouchModel = PhysCreateBbox( VEC_DUCK_HULL_MIN, VEC_DUCK_HULL_MAX ); - - SetupVPhysicsShadow( pModel, "player_stand", pCrouchModel, "player_crouch" ); -} - - -void CBasePlayer::VPhysicsDestroyObject() -{ - // Since CBasePlayer aliases its pointer to the physics object, tell CBaseEntity to - // clear out its physics object pointer so we don't wind up deleting one of - // the aliased objects twice. - VPhysicsSetObject( NULL ); - - PhysRemoveShadow( this ); - - if ( m_pPhysicsController ) - { - physenv->DestroyPlayerController( m_pPhysicsController ); - m_pPhysicsController = NULL; - } - - if ( m_pShadowStand ) - { - PhysDestroyObject( m_pShadowStand ); - m_pShadowStand = NULL; - } - if ( m_pShadowCrouch ) - { - PhysDestroyObject( m_pShadowCrouch ); - m_pShadowCrouch = NULL; - } - - BaseClass::VPhysicsDestroyObject(); -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CBasePlayer::SetVCollisionState( int collisionState ) -{ - Vector vel = vec3_origin; - Vector pos = vec3_origin; - vel = GetAbsVelocity(); - pos = GetAbsOrigin(); - - m_vphysicsCollisionState = collisionState; - switch( collisionState ) - { - case VPHYS_WALK: - m_pShadowStand->SetPosition( pos, vec3_angle, true ); - m_pShadowStand->SetVelocity( &vel, NULL ); - m_pShadowCrouch->EnableCollisions( false ); - m_pPhysicsController->SetObject( m_pShadowStand ); - VPhysicsSwapObject( m_pShadowStand ); - m_pShadowStand->EnableCollisions( true ); - break; - - case VPHYS_CROUCH: - m_pShadowCrouch->SetPosition( pos, vec3_angle, true ); - m_pShadowCrouch->SetVelocity( &vel, NULL ); - m_pShadowStand->EnableCollisions( false ); - m_pPhysicsController->SetObject( m_pShadowCrouch ); - VPhysicsSwapObject( m_pShadowCrouch ); - m_pShadowCrouch->EnableCollisions( true ); - break; - - case VPHYS_NOCLIP: - m_pShadowCrouch->EnableCollisions( false ); - m_pShadowStand->EnableCollisions( false ); - break; - } -} - -//----------------------------------------------------------------------------- -// Purpose: -// Output : int -//----------------------------------------------------------------------------- -int CBasePlayer::GetFOV( void ) const -{ - if ( m_iFOV == 0 ) - return GetDefaultFOV(); - - return m_iFOV; -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : FOV - -// zoomRate - -//----------------------------------------------------------------------------- -bool CBasePlayer::SetFOV( CBaseEntity *pRequester, int FOV, float zoomRate ) -{ - //NOTENOTE: You MUST specify who is requesting the zoom change - assert( pRequester != NULL ); - if ( pRequester == NULL ) - return false; - - // If we already have an owner, we only allow requests from that owner - if ( ( m_hZoomOwner != NULL ) && ( m_hZoomOwner != pRequester ) ) - { - if ( CanOverrideEnvZoomOwner( m_hZoomOwner ) == false ) - return false; - } - else - { - //FIXME: Maybe do this is as an accessor instead - if ( FOV == 0 ) - { - m_hZoomOwner = NULL; - } - else - { - m_hZoomOwner = pRequester; - } - } - - m_iFOV = FOV; - - m_Local.m_flFOVRate = zoomRate; - - return true; -} - -//----------------------------------------------------------------------------- -// Purpose: Sets the default FOV for the player if nothing else is going on -// Input : FOV - the new base FOV for this player -//----------------------------------------------------------------------------- -void CBasePlayer::SetDefaultFOV( int FOV ) -{ - m_iDefaultFOV = ( FOV == 0 ) ? g_pGameRules->DefaultFOV() : FOV; -} - -//----------------------------------------------------------------------------- -// Purpose: // static func -// Input : set - -//----------------------------------------------------------------------------- -void CBasePlayer::ModifyOrAppendPlayerCriteria( AI_CriteriaSet& set ) -{ - // Append our health - set.AppendCriteria( "playerhealth", UTIL_VarArgs( "%i", GetHealth() ) ); - float healthfrac = 0.0f; - if ( GetMaxHealth() > 0 ) - { - healthfrac = (float)GetHealth() / (float)GetMaxHealth(); - } - - set.AppendCriteria( "playerhealthfrac", UTIL_VarArgs( "%.3f", healthfrac ) ); - - CBaseCombatWeapon *weapon = GetActiveWeapon(); - if ( weapon ) - { - set.AppendCriteria( "playerweapon", weapon->GetClassname() ); - } - else - { - set.AppendCriteria( "playerweapon", "none" ); - } - - // Append current activity name - set.AppendCriteria( "playeractivity", CAI_BaseNPC::GetActivityName( GetActivity() ) ); - - set.AppendCriteria( "playerspeed", UTIL_VarArgs( "%.3f", GetAbsVelocity().Length() ) ); - - AppendContextToCriteria( set, "player" ); -} - - -const QAngle& CBasePlayer::GetPunchAngle() -{ - return m_Local.m_vecPunchAngle.Get(); -} - - -void CBasePlayer::SetPunchAngle( const QAngle &punchAngle ) -{ - m_Local.m_vecPunchAngle = punchAngle; - - if ( IsAlive() ) - { - int index = entindex(); - - for ( int i = 1; i <= gpGlobals->maxClients; i++ ) - { - CBasePlayer *pPlayer = UTIL_PlayerByIndex( i ); - - if ( pPlayer && i != index && pPlayer->GetObserverTarget() == this && pPlayer->GetObserverMode() == OBS_MODE_IN_EYE ) - { - pPlayer->SetPunchAngle( punchAngle ); - } - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: Apply a movement constraint to the player -//----------------------------------------------------------------------------- -void CBasePlayer::ActivateMovementConstraint( CBaseEntity *pEntity, const Vector &vecCenter, float flRadius, float flConstraintWidth, float flSpeedFactor ) -{ - m_hConstraintEntity = pEntity; - m_vecConstraintCenter = vecCenter; - m_flConstraintRadius = flRadius; - m_flConstraintWidth = flConstraintWidth; - m_flConstraintSpeedFactor = flSpeedFactor; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CBasePlayer::DeactivateMovementConstraint( ) -{ - m_hConstraintEntity = NULL; - m_flConstraintRadius = 0.0f; - m_vecConstraintCenter = vec3_origin; -} - -//----------------------------------------------------------------------------- -// Perhaps a poorly-named function. This function traces against the supplied -// NPC's hitboxes (instead of hull). If the trace hits a different NPC, the -// new NPC is selected. Otherwise, the supplied NPC is determined to be the -// one the citizen wants. This function allows the selection of a citizen over -// another citizen's shoulder, which is impossible without tracing against -// hitboxes instead of the hull (sjb) -//----------------------------------------------------------------------------- -CBaseEntity *CBasePlayer::DoubleCheckUseNPC( CBaseEntity *pNPC, const Vector &vecSrc, const Vector &vecDir ) -{ - trace_t tr; - - UTIL_TraceLine( vecSrc, vecSrc + vecDir * 1024, MASK_SHOT, this, COLLISION_GROUP_NONE, &tr ); - - if( tr.m_pEnt != NULL && tr.m_pEnt->MyNPCPointer() && tr.m_pEnt != pNPC ) - { - // Player is selecting a different NPC through some negative space - // in the first NPC's hitboxes (between legs, over shoulder, etc). - return tr.m_pEnt; - } - - return pNPC; -} - - -bool CBasePlayer::IsBot() const -{ - return (GetFlags() & FL_FAKECLIENT) != 0; -} - -bool CBasePlayer::IsFakeClient() const -{ - return (GetFlags() & FL_FAKECLIENT) != 0; -} - -void CBasePlayer::EquipSuit( bool bPlayEffects ) -{ - m_Local.m_bWearingSuit = true; -} - -void CBasePlayer::RemoveSuit( void ) -{ - m_Local.m_bWearingSuit = false; -} - -//----------------------------------------------------------------------------- -// Purpose: -// Output : const char -//----------------------------------------------------------------------------- -const char *CBasePlayer::GetTracerType( void ) -{ - if ( GetActiveWeapon() ) - { - return GetActiveWeapon()->GetTracerType(); - } - - return BaseClass::GetTracerType(); -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : &tr - -// nDamageType - -//----------------------------------------------------------------------------- -void CBasePlayer::DoImpactEffect( trace_t &tr, int nDamageType ) -{ - if ( GetActiveWeapon() ) - { - GetActiveWeapon()->DoImpactEffect( tr, nDamageType ); - return; - } - - BaseClass::DoImpactEffect( tr, nDamageType ); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CBasePlayer::InputSetHealth( inputdata_t &inputdata ) -{ - int iNewHealth = inputdata.value.Int(); - int iDelta = abs(GetHealth() - iNewHealth); - if ( iNewHealth > GetHealth() ) - { - TakeHealth( iDelta, DMG_GENERIC ); - } - else if ( iNewHealth < GetHealth() ) - { - // Strip off and restore armor so that it doesn't absorb any of this damage. - int armor = m_ArmorValue; - m_ArmorValue = 0; - TakeDamage( CTakeDamageInfo( this, this, iDelta, DMG_GENERIC ) ); - m_ArmorValue = armor; - } -} - -//----------------------------------------------------------------------------- -// Purpose: Hides or displays the HUD -// Input : &inputdata - -//----------------------------------------------------------------------------- -void CBasePlayer::InputSetHUDVisibility( inputdata_t &inputdata ) -{ - bool bEnable = inputdata.value.Bool(); - - if ( bEnable ) - { - m_Local.m_iHideHUD &= ~HIDEHUD_ALL; - } - else - { - m_Local.m_iHideHUD |= HIDEHUD_ALL; - } -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *pEntity - -//----------------------------------------------------------------------------- -void CBasePlayer::SetViewEntity( CBaseEntity *pEntity ) -{ - m_hViewEntity = pEntity; - - if ( m_hViewEntity ) - { - engine->SetView( edict(), m_hViewEntity->edict() ); - } - else - { - engine->SetView( edict(), edict() ); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Looks at the player's reserve ammo and also all his weapons for any ammo -// of the specified type -// Input : nAmmoIndex - ammo to look for -// Output : Returns true on success, false on failure. -//----------------------------------------------------------------------------- -bool CBasePlayer::HasAnyAmmoOfType( int nAmmoIndex ) -{ - // Must be a valid index - if ( nAmmoIndex < 0 ) - return false; - - // If we have some in reserve, we're already done - if ( GetAmmoCount( nAmmoIndex ) ) - return true; - - CBaseCombatWeapon *pWeapon; - - // Check all held weapons - for ( int i=0; i < MAX_WEAPONS; i++ ) - { - pWeapon = GetWeapon( i ); - - if ( !pWeapon ) - continue; - - // We must use clips and use this sort of ammo - if ( pWeapon->UsesClipsForAmmo1() && pWeapon->GetPrimaryAmmoType() == nAmmoIndex ) - { - // If we have any ammo, we're done - if ( pWeapon->HasPrimaryAmmo() ) - return true; - } - - // We'll check both clips for the same ammo type, just in case - if ( pWeapon->UsesClipsForAmmo2() && pWeapon->GetSecondaryAmmoType() == nAmmoIndex ) - { - if ( pWeapon->HasSecondaryAmmo() ) - return true; - } - } - - // We're completely without this type of ammo - return false; -} - -//----------------------------------------------------------------------------- -// return a string version of the players network (i.e steam) ID. -// -//----------------------------------------------------------------------------- -const char *CBasePlayer::GetNetworkIDString() -{ - Q_strncpy( m_szNetworkIDString, engine->GetPlayerNetworkIDString( edict() ), sizeof(m_szNetworkIDString) ); - return m_szNetworkIDString; -} - -//----------------------------------------------------------------------------- -// Assign the player a name -//----------------------------------------------------------------------------- -void CBasePlayer::SetPlayerName( const char *name ) -{ - Assert( name ); - - if ( name ) - { - Assert( strlen(name) > 0 ); - - Q_strncpy( m_szNetname, name, sizeof(m_szNetname) ); - } -} - -//----------------------------------------------------------------------------- -// sets the "don't autokick me" flag on a player -//----------------------------------------------------------------------------- -class DisableAutokick -{ -public: - DisableAutokick( int userID ) - { - m_userID = userID; - } - - bool operator()( CBasePlayer *player ) - { - if ( player->GetUserID() == m_userID ) - { - Msg( "autokick is disabled for %s\n", player->GetPlayerName() ); - player->DisableAutoKick( true ); - return false; // don't need to check other players - } - - return true; // keep looking at other players - } - -private: - int m_userID; -}; - -//----------------------------------------------------------------------------- -// sets the "don't autokick me" flag on a player -//----------------------------------------------------------------------------- -CON_COMMAND( mp_disable_autokick, "Prevents a userid from being auto-kicked" ) -{ - if ( !UTIL_IsCommandIssuedByServerAdmin() ) - { - Msg( "You must be a server admin to use mp_disable_autokick\n" ); - return; - } - - if ( engine->Cmd_Argc() != 2 ) - { - Msg( "Usage: mp_disable_autokick \n" ); - return; - } - - int userID = atoi( engine->Cmd_Argv( 1 ) ); - DisableAutokick disable( userID ); - ForEachPlayer( disable ); -} - -//----------------------------------------------------------------------------- -// Purpose: Toggle between the duck being on and off -//----------------------------------------------------------------------------- -void CBasePlayer::ToggleDuck( void ) -{ - // Toggle the state - m_bDuckToggled = !m_bDuckToggled; -} - -//----------------------------------------------------------------------------- -// Just tells us how far the stick is from the center. No directional info -//----------------------------------------------------------------------------- -float CBasePlayer::GetStickDist() -{ - Vector2D controlStick; - - controlStick.x = m_flForwardMove; - controlStick.y = m_flSideMove; - - return controlStick.Length(); -} - -//----------------------------------------------------------------------------- -// CPlayerInfo functions (simple passthroughts to get around the CBasePlayer multiple inheritence limitation) -//----------------------------------------------------------------------------- -const char *CPlayerInfo::GetName() -{ - Assert( m_pParent ); - return m_pParent->GetPlayerName(); -} - -int CPlayerInfo::GetUserID() -{ - Assert( m_pParent ); - return engine->GetPlayerUserId( m_pParent->edict() ); -} - -const char *CPlayerInfo::GetNetworkIDString() -{ - Assert( m_pParent ); - return m_pParent->GetNetworkIDString(); -} - -int CPlayerInfo::GetTeamIndex() -{ - Assert( m_pParent ); - return m_pParent->GetTeamNumber(); -} - -void CPlayerInfo::ChangeTeam( int iTeamNum ) -{ - Assert( m_pParent ); - m_pParent->ChangeTeam(iTeamNum); -} - -int CPlayerInfo::GetFragCount() -{ - Assert( m_pParent ); - return m_pParent->FragCount(); -} - -int CPlayerInfo::GetDeathCount() -{ - Assert( m_pParent ); - return m_pParent->DeathCount(); -} - -bool CPlayerInfo::IsConnected() -{ - Assert( m_pParent ); - return m_pParent->IsConnected(); -} - -int CPlayerInfo::GetArmorValue() -{ - Assert( m_pParent ); - return m_pParent->ArmorValue(); -} - -bool CPlayerInfo::IsHLTV() -{ - Assert( m_pParent ); - return m_pParent->IsHLTV(); -} - -bool CPlayerInfo::IsPlayer() -{ - Assert( m_pParent ); - return m_pParent->IsPlayer(); -} - -bool CPlayerInfo::IsFakeClient() -{ - Assert( m_pParent ); - return m_pParent->IsFakeClient(); -} - -bool CPlayerInfo::IsDead() -{ - Assert( m_pParent ); - return m_pParent->IsDead(); -} - -bool CPlayerInfo::IsInAVehicle() -{ - Assert( m_pParent ); - return m_pParent->IsInAVehicle(); -} - -bool CPlayerInfo::IsObserver() -{ - Assert( m_pParent ); - return m_pParent->IsObserver(); -} - -const Vector CPlayerInfo::GetAbsOrigin() -{ - Assert( m_pParent ); - return m_pParent->GetAbsOrigin(); -} - -const QAngle CPlayerInfo::GetAbsAngles() -{ - Assert( m_pParent ); - return m_pParent->GetAbsAngles(); -} - -const Vector CPlayerInfo::GetPlayerMins() -{ - Assert( m_pParent ); - return m_pParent->GetPlayerMins(); -} - -const Vector CPlayerInfo::GetPlayerMaxs() -{ - Assert( m_pParent ); - return m_pParent->GetPlayerMaxs(); -} - -const char *CPlayerInfo::GetWeaponName() -{ - Assert( m_pParent ); - CBaseCombatWeapon *weap = m_pParent->GetActiveWeapon(); - if ( !weap ) - { - return NULL; - } - return weap->GetName(); -} - -const char *CPlayerInfo::GetModelName() -{ - Assert( m_pParent ); - return m_pParent->GetModelName().ToCStr(); -} - -const int CPlayerInfo::GetHealth() -{ - Assert( m_pParent ); - return m_pParent->GetHealth(); -} - -const int CPlayerInfo::GetMaxHealth() -{ - Assert( m_pParent ); - return m_pParent->GetMaxHealth(); -} - - - - - -void CPlayerInfo::SetAbsOrigin( Vector & vec ) -{ - Assert( m_pParent ); - if ( m_pParent->IsBot() ) - { - m_pParent->SetAbsOrigin(vec); - } -} - -void CPlayerInfo::SetAbsAngles( QAngle & ang ) -{ - Assert( m_pParent ); - if ( m_pParent->IsBot() ) - { - m_pParent->SetAbsAngles(ang); - } -} - -void CPlayerInfo::RemoveAllItems( bool removeSuit ) -{ - Assert( m_pParent ); - if ( m_pParent->IsBot() ) - { - m_pParent->RemoveAllItems(removeSuit); - } -} - -void CPlayerInfo::SetActiveWeapon( const char *WeaponName ) -{ - Assert( m_pParent ); - if ( m_pParent->IsBot() ) - { - CBaseCombatWeapon *weap = m_pParent->Weapon_Create( WeaponName ); - if ( weap ) - { - m_pParent->Weapon_Equip(weap); - m_pParent->Weapon_Switch(weap); - } - } -} - -void CPlayerInfo::SetLocalOrigin( const Vector& origin ) -{ - Assert( m_pParent ); - if ( m_pParent->IsBot() ) - { - m_pParent->SetLocalOrigin(origin); - } -} - -const Vector CPlayerInfo::GetLocalOrigin( void ) -{ - Assert( m_pParent ); - if ( m_pParent->IsBot() ) - { - Vector origin = m_pParent->GetLocalOrigin(); - return origin; - } - else - { - return Vector( 0, 0, 0 ); - } -} - -void CPlayerInfo::SetLocalAngles( const QAngle& angles ) -{ - Assert( m_pParent ); - if ( m_pParent->IsBot() ) - { - m_pParent->SetLocalAngles( angles ); - } -} - -const QAngle CPlayerInfo::GetLocalAngles( void ) -{ - Assert( m_pParent ); - if ( m_pParent->IsBot() ) - { - return m_pParent->GetLocalAngles(); - } - else - { - return QAngle(); - } -} - -void CPlayerInfo::PostClientMessagesSent( void ) -{ - Assert( m_pParent ); - if ( m_pParent->IsBot() ) - { - m_pParent->PostClientMessagesSent(); - } -} - -bool CPlayerInfo::IsEFlagSet( int nEFlagMask ) -{ - Assert( m_pParent ); - if ( m_pParent->IsBot() ) - { - return m_pParent->IsEFlagSet(nEFlagMask); - } - return false; -} - -void CPlayerInfo::RunPlayerMove( CBotCmd *ucmd ) -{ - if ( m_pParent->IsBot() ) - { - Assert( m_pParent ); - CUserCmd cmd; - cmd.buttons = ucmd->buttons; - cmd.command_number = ucmd->command_number; - cmd.forwardmove = ucmd->forwardmove; - cmd.hasbeenpredicted = ucmd->hasbeenpredicted; - cmd.impulse = ucmd->impulse; - cmd.mousedx = ucmd->mousedx; - cmd.mousedy = ucmd->mousedy; - cmd.random_seed = ucmd->random_seed; - cmd.sidemove = ucmd->sidemove; - cmd.tick_count = ucmd->tick_count; - cmd.upmove = ucmd->upmove; - cmd.viewangles = ucmd->viewangles; - cmd.weaponselect = ucmd->weaponselect; - cmd.weaponsubtype = ucmd->weaponsubtype; - - // Store off the globals.. they're gonna get whacked - float flOldFrametime = gpGlobals->frametime; - float flOldCurtime = gpGlobals->curtime; - - m_pParent->SetTimeBase( gpGlobals->curtime ); - - MoveHelperServer()->SetHost( m_pParent ); - m_pParent->PlayerRunCommand( &cmd, MoveHelperServer() ); - - // save off the last good usercmd - m_pParent->SetLastUserCommand( cmd ); - - // Clear out any fixangle that has been set - m_pParent->pl.fixangle = FIXANGLE_NONE; - - // Restore the globals.. - gpGlobals->frametime = flOldFrametime; - gpGlobals->curtime = flOldCurtime; - MoveHelperServer()->SetHost( NULL ); - } -} - -void CPlayerInfo::SetLastUserCommand( const CBotCmd &ucmd ) -{ - if ( m_pParent->IsBot() ) - { - Assert( m_pParent ); - CUserCmd cmd; - cmd.buttons = ucmd.buttons; - cmd.command_number = ucmd.command_number; - cmd.forwardmove = ucmd.forwardmove; - cmd.hasbeenpredicted = ucmd.hasbeenpredicted; - cmd.impulse = ucmd.impulse; - cmd.mousedx = ucmd.mousedx; - cmd.mousedy = ucmd.mousedy; - cmd.random_seed = ucmd.random_seed; - cmd.sidemove = ucmd.sidemove; - cmd.tick_count = ucmd.tick_count; - cmd.upmove = ucmd.upmove; - cmd.viewangles = ucmd.viewangles; - cmd.weaponselect = ucmd.weaponselect; - cmd.weaponsubtype = ucmd.weaponsubtype; - - m_pParent->SetLastUserCommand(cmd); - } -} - - -CBotCmd CPlayerInfo::GetLastUserCommand() -{ - CBotCmd cmd; - const CUserCmd *ucmd = m_pParent->GetLastUserCommand(); - if ( ucmd ) - { - cmd.buttons = ucmd->buttons; - cmd.command_number = ucmd->command_number; - cmd.forwardmove = ucmd->forwardmove; - cmd.hasbeenpredicted = ucmd->hasbeenpredicted; - cmd.impulse = ucmd->impulse; - cmd.mousedx = ucmd->mousedx; - cmd.mousedy = ucmd->mousedy; - cmd.random_seed = ucmd->random_seed; - cmd.sidemove = ucmd->sidemove; - cmd.tick_count = ucmd->tick_count; - cmd.upmove = ucmd->upmove; - cmd.viewangles = ucmd->viewangles; - cmd.weaponselect = ucmd->weaponselect; - cmd.weaponsubtype = ucmd->weaponsubtype; - } - return cmd; -} - +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: Functions dealing with the player. +// +//=============================================================================// + +#include "cbase.h" +#include "const.h" +#include "baseplayer_shared.h" +#include "trains.h" +#include "soundent.h" +#include "gib.h" +#include "shake.h" +#include "decals.h" +#include "gamerules.h" +#include "game.h" +#include "entityapi.h" +#include "entitylist.h" +#include "eventqueue.h" +#include "worldsize.h" +#include "isaverestore.h" +#include "globalstate.h" +#include "basecombatweapon.h" +#include "ai_basenpc.h" +#include "ai_network.h" +#include "ai_node.h" +#include "ai_networkmanager.h" +#include "ammodef.h" +#include "mathlib.h" +#include "ndebugoverlay.h" +#include "baseviewmodel.h" +#include "in_buttons.h" +#include "client.h" +#include "team.h" +#include "particle_smokegrenade.h" +#include "IEffects.h" +#include "vstdlib/random.h" +#include "engine/IEngineSound.h" +#include "movehelper_server.h" +#include "igamemovement.h" +#include "saverestoretypes.h" +#include "iservervehicle.h" +#include "movevars_shared.h" +#include "vcollide_parse.h" +#include "player_command.h" +#include "vehicle_base.h" +#include "AI_Criteria.h" +#include "globals.h" +#include "usermessages.h" +#include "gamevars_shared.h" +#include "world.h" +#include "physobj.h" +#include "KeyValues.h" +#include "coordsize.h" +#include "vphysics/player_controller.h" +#include "saverestore_utlvector.h" +#include "hltvdirector.h" +#include "nav_mesh.h" +#include "env_zoom.h" +#include "rumble_shared.h" + +#ifdef HL2_DLL +#include "combine_mine.h" +#include "weapon_physcannon.h" +#endif + +#ifdef HL2_DLL +extern ConVar hl2_xbox_aiming; +#define UseXboxAiming() hl2_xbox_aiming.GetBool() +#else +#define UseXboxAiming() 0 +#endif + +ConVar autoaim_max_dist( "autoaim_max_dist", "2160" ); // 2160 = 180 feet +ConVar autoaim_max_deflect( "autoaim_max_deflect", "0.99" ); + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +static ConVar old_armor( "player_old_armor", "0" ); + +static ConVar physicsshadowupdate_render( "physicsshadowupdate_render", "0" ); +bool IsInCommentaryMode( void ); +bool IsListeningToCommentary( void ); + +// This is declared in the engine, too +ConVar sv_noclipduringpause( "sv_noclipduringpause", "0", FCVAR_REPLICATED | FCVAR_CHEAT, "If cheats are enabled, then you can noclip with the game paused (for doing screenshots, etc.)." ); + +extern ConVar sv_maxunlag; +extern ConVar sv_turbophysics; +extern ConVar *sv_maxreplay; + +// TIME BASED DAMAGE AMOUNT +// tweak these values based on gameplay feedback: +#define PARALYZE_DURATION 2 // number of 2 second intervals to take damage +#define PARALYZE_DAMAGE 1.0 // damage to take each 2 second interval + +#define NERVEGAS_DURATION 2 +#define NERVEGAS_DAMAGE 5.0 + +#define POISON_DURATION 5 +#define POISON_DAMAGE 2.0 + +#define RADIATION_DURATION 2 +#define RADIATION_DAMAGE 1.0 + +#define ACID_DURATION 2 +#define ACID_DAMAGE 5.0 + +#define SLOWBURN_DURATION 2 +#define SLOWBURN_DAMAGE 1.0 + +#define SLOWFREEZE_DURATION 2 +#define SLOWFREEZE_DAMAGE 1.0 + +//---------------------------------------------------- +// Player Physics Shadow +//---------------------------------------------------- +#define VPHYS_MAX_DISTANCE 2.0 +#define VPHYS_MAX_VEL 10 +#define VPHYS_MAX_DISTSQR (VPHYS_MAX_DISTANCE*VPHYS_MAX_DISTANCE) +#define VPHYS_MAX_VELSQR (VPHYS_MAX_VEL*VPHYS_MAX_VEL) + + +extern bool g_fDrawLines; +int gEvilImpulse101; + +bool gInitHUD = true; + +extern void respawn(CBaseEntity *pEdict, bool fCopyCorpse); +int MapTextureTypeStepType(char chTextureType); +extern void SpawnBlood(Vector vecSpot, const Vector &vecDir, int bloodColor, float flDamage); +extern void AddMultiDamage( const CTakeDamageInfo &info, CBaseEntity *pEntity ); + + +#define CMD_MOSTRECENT 0 + +//#define FLASH_DRAIN_TIME 1.2 //100 units/3 minutes +//#define FLASH_CHARGE_TIME 0.2 // 100 units/20 seconds (seconds per unit) + + +//#define PLAYER_MAX_SAFE_FALL_DIST 20// falling any farther than this many feet will inflict damage +//#define PLAYER_FATAL_FALL_DIST 60// 100% damage inflicted if player falls this many feet +//#define DAMAGE_PER_UNIT_FALLEN (float)( 100 ) / ( ( PLAYER_FATAL_FALL_DIST - PLAYER_MAX_SAFE_FALL_DIST ) * 12 ) +//#define MAX_SAFE_FALL_UNITS ( PLAYER_MAX_SAFE_FALL_DIST * 12 ) + +// player damage adjusters +ConVar sk_player_head( "sk_player_head","2" ); +ConVar sk_player_chest( "sk_player_chest","1" ); +ConVar sk_player_stomach( "sk_player_stomach","1" ); +ConVar sk_player_arm( "sk_player_arm","1" ); +ConVar sk_player_leg( "sk_player_leg","1" ); + +void CC_GiveCurrentAmmo( void ) +{ +#ifdef _XBOX + CBasePlayer *pPlayer = UTIL_PlayerByIndex(1); + + if( pPlayer ) + { + CBaseCombatWeapon *pWeapon = pPlayer->GetActiveWeapon(); + + if( pWeapon ) + { + if( pWeapon->UsesPrimaryAmmo() ) + { + int ammoIndex = pWeapon->GetPrimaryAmmoType(); + + if( ammoIndex != -1 ) + { + int giveAmount; + giveAmount = GetAmmoDef()->MaxCarry(ammoIndex); + pPlayer->GiveAmmo( giveAmount, GetAmmoDef()->GetAmmoOfIndex(ammoIndex)->pName ); + } + } + if( pWeapon->UsesSecondaryAmmo() && pWeapon->HasSecondaryAmmo() ) + { + // Give secondary ammo out, as long as the player already has some + // from a presumeably natural source. This prevents players on XBox + // having Combine Balls and so forth in areas of the game that + // were not tested with these items. + int ammoIndex = pWeapon->GetSecondaryAmmoType(); + + if( ammoIndex != -1 ) + { + int giveAmount; + giveAmount = GetAmmoDef()->MaxCarry(ammoIndex); + pPlayer->GiveAmmo( giveAmount, GetAmmoDef()->GetAmmoOfIndex(ammoIndex)->pName ); + } + } + } + } +#endif//_XBOX +} +static ConCommand givecurrentammo("givecurrentammo", CC_GiveCurrentAmmo, "Give a supply of ammo for current weapon..\n", FCVAR_CHEAT ); + + +// pl +BEGIN_SIMPLE_DATADESC( CPlayerState ) + // DEFINE_FIELD( netname, FIELD_STRING ), // Don't stomp player name with what's in save/restore + DEFINE_FIELD( v_angle, FIELD_VECTOR ), + DEFINE_FIELD( deadflag, FIELD_BOOLEAN ), + + // this is always set to true on restore, don't bother saving it. + // DEFINE_FIELD( fixangle, FIELD_INTEGER ), + // DEFINE_FIELD( anglechange, FIELD_FLOAT ), + // DEFINE_FIELD( hltv, FIELD_BOOLEAN ), + // DEFINE_FIELD( frags, FIELD_INTEGER ), + // DEFINE_FIELD( deaths, FIELD_INTEGER ), +END_DATADESC() + +// Global Savedata for player +BEGIN_DATADESC( CBasePlayer ) + + DEFINE_EMBEDDED( m_Local ), + DEFINE_UTLVECTOR( m_hTriggerSoundscapeList, FIELD_EHANDLE ), + DEFINE_EMBEDDED( pl ), + + DEFINE_FIELD( m_StuckLast, FIELD_INTEGER ), + + DEFINE_FIELD( m_nButtons, FIELD_INTEGER ), + DEFINE_FIELD( m_afButtonLast, FIELD_INTEGER ), + DEFINE_FIELD( m_afButtonPressed, FIELD_INTEGER ), + DEFINE_FIELD( m_afButtonReleased, FIELD_INTEGER ), + + DEFINE_FIELD( m_iFOV, FIELD_INTEGER ), + DEFINE_FIELD( m_iDefaultFOV, FIELD_INTEGER ), + + //DEFINE_FIELD( m_fOnTarget, FIELD_BOOLEAN ), // Don't need to restore + DEFINE_FIELD( m_iObserverMode, FIELD_INTEGER ), + DEFINE_FIELD( m_iObserverLastMode, FIELD_INTEGER ), + DEFINE_FIELD( m_hObserverTarget, FIELD_EHANDLE ), + DEFINE_FIELD( m_bForcedObserverMode, FIELD_BOOLEAN ), + DEFINE_AUTO_ARRAY( m_szAnimExtension, FIELD_CHARACTER ), +// DEFINE_CUSTOM_FIELD( m_Activity, ActivityDataOps() ), + + DEFINE_FIELD( m_nUpdateRate, FIELD_INTEGER ), + DEFINE_FIELD( m_fLerpTime, FIELD_FLOAT ), + DEFINE_FIELD( m_bLagCompensation, FIELD_BOOLEAN ), + DEFINE_FIELD( m_bPredictWeapons, FIELD_BOOLEAN ), + + DEFINE_FIELD( m_vecAdditionalPVSOrigin, FIELD_POSITION_VECTOR ), + DEFINE_FIELD( m_vecCameraPVSOrigin, FIELD_POSITION_VECTOR ), + + DEFINE_FIELD( m_hUseEntity, FIELD_EHANDLE ), + DEFINE_FIELD( m_iTrain, FIELD_INTEGER ), + DEFINE_FIELD( m_iRespawnFrames, FIELD_FLOAT ), + DEFINE_FIELD( m_afPhysicsFlags, FIELD_INTEGER ), + DEFINE_FIELD( m_hVehicle, FIELD_EHANDLE ), + + // recreate, don't restore + // DEFINE_FIELD( m_CommandContext, CUtlVector < CCommandContext > ), + //DEFINE_FIELD( m_pPhysicsController, FIELD_POINTER ), + //DEFINE_FIELD( m_pShadowStand, FIELD_POINTER ), + //DEFINE_FIELD( m_pShadowCrouch, FIELD_POINTER ), + //DEFINE_FIELD( m_vphysicsCollisionState, FIELD_INTEGER ), + // DEFINE_FIELD( m_lastNavArea, CNavArea ), + DEFINE_ARRAY( m_szNetworkIDString, FIELD_CHARACTER, MAX_NETWORKID_LENGTH ), + DEFINE_FIELD( m_oldOrigin, FIELD_POSITION_VECTOR ), + DEFINE_FIELD( m_vecSmoothedVelocity, FIELD_VECTOR ), + //DEFINE_FIELD( m_touchedPhysObject, FIELD_BOOLEAN ), + //DEFINE_FIELD( m_iPlayerSound, FIELD_INTEGER ), // Don't restore, set in Precache() + DEFINE_FIELD( m_iTargetVolume, FIELD_INTEGER ), + DEFINE_AUTO_ARRAY( m_rgItems, FIELD_INTEGER ), + //DEFINE_FIELD( m_fNextSuicideTime, FIELD_TIME ), + + DEFINE_FIELD( m_flTimeStepSound, FIELD_TIME ), + DEFINE_FIELD( m_flSwimTime, FIELD_TIME ), + DEFINE_FIELD( m_flDuckTime, FIELD_TIME ), + DEFINE_FIELD( m_flDuckJumpTime, FIELD_TIME ), + + DEFINE_FIELD( m_flSuitUpdate, FIELD_TIME ), + DEFINE_AUTO_ARRAY( m_rgSuitPlayList, FIELD_INTEGER ), + DEFINE_FIELD( m_iSuitPlayNext, FIELD_INTEGER ), + DEFINE_AUTO_ARRAY( m_rgiSuitNoRepeat, FIELD_INTEGER ), + DEFINE_AUTO_ARRAY( m_rgflSuitNoRepeatTime, FIELD_TIME ), + DEFINE_FIELD( m_lastDamageAmount, FIELD_INTEGER ), + DEFINE_FIELD( m_tbdPrev, FIELD_TIME ), + + //DEFINE_FIELD( m_flgeigerRange, FIELD_FLOAT ), // Don't restore, reset in Precache() + //DEFINE_FIELD( m_flgeigerDelay, FIELD_FLOAT ), // Don't restore, reset in Precache() + //DEFINE_FIELD( m_igeigerRangePrev, FIELD_FLOAT ), // Don't restore, reset in Precache() + //DEFINE_FIELD( m_iStepLeft, FIELD_INTEGER ), // Don't need to restore + //DEFINE_FIELD( m_chTextureType, FIELD_CHARACTER ), // Don't need to restore + + DEFINE_FIELD( m_idrowndmg, FIELD_INTEGER ), + DEFINE_FIELD( m_idrownrestored, FIELD_INTEGER ), + + DEFINE_FIELD( m_nPoisonDmg, FIELD_INTEGER ), + DEFINE_FIELD( m_nPoisonRestored, FIELD_INTEGER ), + + DEFINE_FIELD( m_bitsHUDDamage, FIELD_INTEGER ), + DEFINE_FIELD( m_fInitHUD, FIELD_BOOLEAN ), + DEFINE_FIELD( m_flDeathTime, FIELD_TIME ), + + //DEFINE_FIELD( m_fGameHUDInitialized, FIELD_BOOLEAN ), // only used in multiplayer games + //DEFINE_FIELD( m_fWeapon, FIELD_BOOLEAN ), // Don't restore, client needs reset + //DEFINE_FIELD( m_iUpdateTime, FIELD_INTEGER ), // Don't need to restore + //DEFINE_FIELD( m_iClientBattery, FIELD_INTEGER ), // Don't restore, client needs reset + //DEFINE_FIELD( m_iClientHideHUD, FIELD_INTEGER ), // Don't restore, client needs reset + //DEFINE_FIELD( m_vecAutoAim, FIELD_VECTOR ), // Don't save/restore - this is recomputed + //DEFINE_FIELD( m_lastx, FIELD_INTEGER ), + //DEFINE_FIELD( m_lasty, FIELD_INTEGER ), + + DEFINE_FIELD( m_iFrags, FIELD_INTEGER ), + DEFINE_FIELD( m_iDeaths, FIELD_INTEGER ), + DEFINE_FIELD( m_flNextDecalTime, FIELD_TIME ), + //DEFINE_AUTO_ARRAY( m_szTeamName, FIELD_STRING ), // mp + + //DEFINE_FIELD( m_iConnected, FIELD_INTEGER ), + // from edict_t + DEFINE_FIELD( m_ArmorValue, FIELD_INTEGER ), + DEFINE_FIELD( m_DmgOrigin, FIELD_VECTOR ), + DEFINE_FIELD( m_DmgTake, FIELD_FLOAT ), + DEFINE_FIELD( m_DmgSave, FIELD_FLOAT ), + DEFINE_FIELD( m_AirFinished, FIELD_TIME ), + DEFINE_FIELD( m_PainFinished, FIELD_TIME ), + + DEFINE_FIELD( m_iPlayerLocked, FIELD_INTEGER ), + + DEFINE_AUTO_ARRAY( m_hViewModel, FIELD_EHANDLE ), + + DEFINE_FIELD( m_flMaxspeed, FIELD_FLOAT ), + DEFINE_FIELD( m_flWaterJumpTime, FIELD_TIME ), + DEFINE_FIELD( m_vecWaterJumpVel, FIELD_VECTOR ), + DEFINE_FIELD( m_nImpulse, FIELD_INTEGER ), + DEFINE_FIELD( m_flStepSoundTime, FIELD_TIME ), + DEFINE_FIELD( m_flSwimSoundTime, FIELD_TIME ), + DEFINE_FIELD( m_vecLadderNormal, FIELD_VECTOR ), + + DEFINE_FIELD( m_flFlashTime, FIELD_TIME ), + DEFINE_FIELD( m_nDrownDmgRate, FIELD_INTEGER ), + + // NOT SAVED + //DEFINE_FIELD( m_vForcedOrigin, FIELD_VECTOR ), + //DEFINE_FIELD( m_bForceOrigin, FIELD_BOOLEAN ), + //DEFINE_FIELD( m_nTickBase, FIELD_INTEGER ), + //DEFINE_FIELD( m_LastCmd, FIELD_ ), + // DEFINE_FIELD( m_pCurrentCommand, CUserCmd ), + //DEFINE_FIELD( m_bGamePaused, FIELD_BOOLEAN ), + + DEFINE_FIELD( m_bitsDamageType, FIELD_INTEGER ), + DEFINE_AUTO_ARRAY( m_rgbTimeBasedDamage, FIELD_CHARACTER ), + DEFINE_FIELD( m_fLastPlayerTalkTime, FIELD_FLOAT ), + DEFINE_FIELD( m_hLastWeapon, FIELD_EHANDLE ), + +#if !defined( NO_ENTITY_PREDICTION ) + // DEFINE_FIELD( m_SimulatedByThisPlayer, CUtlVector < CHandle < CBaseEntity > > ), +#endif + + DEFINE_FIELD( m_flOldPlayerZ, FIELD_FLOAT ), + DEFINE_FIELD( m_flOldPlayerViewOffsetZ, FIELD_FLOAT ), + DEFINE_FIELD( m_bPlayerUnderwater, FIELD_BOOLEAN ), + DEFINE_FIELD( m_hViewEntity, FIELD_EHANDLE ), + + DEFINE_FIELD( m_hConstraintEntity, FIELD_EHANDLE ), + DEFINE_FIELD( m_vecConstraintCenter, FIELD_VECTOR ), + DEFINE_FIELD( m_flConstraintRadius, FIELD_FLOAT ), + DEFINE_FIELD( m_flConstraintWidth, FIELD_FLOAT ), + DEFINE_FIELD( m_flConstraintSpeedFactor, FIELD_FLOAT ), + DEFINE_FIELD( m_hZoomOwner, FIELD_EHANDLE ), + + DEFINE_FIELD( m_flLaggedMovementValue, FIELD_FLOAT ), + + DEFINE_FIELD( m_vNewVPhysicsPosition, FIELD_VECTOR ), + DEFINE_FIELD( m_vNewVPhysicsVelocity, FIELD_VECTOR ), + + DEFINE_FIELD( m_bSinglePlayerGameEnding, FIELD_BOOLEAN ), + + // Function Pointers + DEFINE_FUNCTION( PlayerDeathThink ), + + // Inputs + DEFINE_INPUTFUNC( FIELD_INTEGER, "SetHealth", InputSetHealth ), + DEFINE_INPUTFUNC( FIELD_BOOLEAN, "SetHUDVisibility", InputSetHUDVisibility ), + + DEFINE_FIELD( m_nNumCrouches, FIELD_INTEGER ), + DEFINE_FIELD( m_bDuckToggled, FIELD_BOOLEAN ), + + DEFINE_FIELD( m_nNumCrateHudHints, FIELD_INTEGER ), + +END_DATADESC() + +int giPrecacheGrunt = 0; + +edict_t *CBasePlayer::s_PlayerEdict = NULL; + + +inline bool ShouldRunCommandsInContext( const CCommandContext *ctx ) +{ + // TODO: This should be enabled at some point. If usercmds can run while paused, then + // they can create entities which will never die and it will fill up the entity list. +#ifdef NO_USERCMDS_DURING_PAUSE + return !ctx->paused || sv_noclipduringpause.GetInt(); +#else + return true; +#endif +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Output : CBaseViewModel +//----------------------------------------------------------------------------- +CBaseViewModel *CBasePlayer::GetViewModel( int index /*= 0*/ ) +{ + Assert( index >= 0 && index < MAX_VIEWMODELS ); + return m_hViewModel[ index ].Get(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBasePlayer::CreateViewModel( int index /*=0*/ ) +{ + Assert( index >= 0 && index < MAX_VIEWMODELS ); + + if ( GetViewModel( index ) ) + return; + + CBaseViewModel *vm = ( CBaseViewModel * )CreateEntityByName( "viewmodel" ); + if ( vm ) + { + vm->SetAbsOrigin( GetAbsOrigin() ); + vm->SetOwner( this ); + vm->SetIndex( index ); + DispatchSpawn( vm ); + vm->FollowEntity( this ); + m_hViewModel.Set( index, vm ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBasePlayer::DestroyViewModels( void ) +{ + int i; + for ( i = MAX_VIEWMODELS - 1; i >= 0; i-- ) + { + CBaseViewModel *vm = GetViewModel( i ); + if ( !vm ) + continue; + + UTIL_Remove( vm ); + m_hViewModel.Set( i, NULL ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Static member function to create a player of the specified class +// Input : *className - +// *ed - +// Output : CBasePlayer +//----------------------------------------------------------------------------- +CBasePlayer *CBasePlayer::CreatePlayer( const char *className, edict_t *ed ) +{ + CBasePlayer *player; + CBasePlayer::s_PlayerEdict = ed; + player = ( CBasePlayer * )CreateEntityByName( className ); + return player; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : +// Output : +//----------------------------------------------------------------------------- +CBasePlayer::CBasePlayer( ) +{ + AddEFlags( EFL_NO_AUTO_EDICT_ATTACH ); + +#ifdef _DEBUG + m_vecAutoAim.Init(); + m_vecAdditionalPVSOrigin.Init(); + m_vecCameraPVSOrigin.Init(); + m_DmgOrigin.Init(); + m_vecLadderNormal.Init(); + + m_oldOrigin.Init(); + m_vecSmoothedVelocity.Init(); +#endif + + if ( s_PlayerEdict ) + { + // take the assigned edict_t and attach it + Assert( s_PlayerEdict != NULL ); + NetworkProp()->AttachEdict( s_PlayerEdict ); + s_PlayerEdict = NULL; + } + + m_flFlashTime = -1; + pl.fixangle = FIXANGLE_ABSOLUTE; + pl.hltv = false; + pl.frags = 0; + pl.deaths = 0; + + m_szNetname[0] = '\0'; + + m_iHealth = 0; + Weapon_SetLast( NULL ); + m_bitsDamageType = 0; + + m_bForceOrigin = false; + m_hVehicle = NULL; + m_pCurrentCommand = NULL; + + // Setup our default FOV + m_iDefaultFOV = g_pGameRules->DefaultFOV(); + + m_hZoomOwner = NULL; + + m_nUpdateRate = 20; // cl_updaterate defualt + m_fLerpTime = 0.1f; // cl_interp default + m_bPredictWeapons = true; + m_bLagCompensation = false; + m_flLaggedMovementValue = 1.0f; + m_StuckLast = 0; + m_impactEnergyScale = 1.0f; + m_fLastPlayerTalkTime = 0.0f; + m_PlayerInfo.SetParent( this ); + + ResetObserverMode(); + + m_surfaceProps = 0; + m_pSurfaceData = NULL; + m_surfaceFriction = 1.0f; + m_chTextureType = 0; + m_chPreviousTextureType = 0; + + m_fDelay = 0.0f; + m_fReplayEnd = -1; + m_iReplayEntity = 0; + + m_autoKickDisabled = false; + + m_nNumCrouches = 0; + m_bDuckToggled = false; +} + +CBasePlayer::~CBasePlayer( ) +{ + VPhysicsDestroyObject(); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : +// Output : +//----------------------------------------------------------------------------- +void CBasePlayer::UpdateOnRemove( void ) +{ + VPhysicsDestroyObject(); + + // Remove him from his current team + if ( GetTeam() ) + { + GetTeam()->RemovePlayer( this ); + } + + // Chain at end to mimic destructor unwind order + BaseClass::UpdateOnRemove(); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : **pvs - +// **pas - +//----------------------------------------------------------------------------- +void CBasePlayer::SetupVisibility( CBaseEntity *pViewEntity, unsigned char *pvs, int pvssize ) +{ + // If we have a viewentity, we don't add the player's origin. + if ( pViewEntity ) + return; + + Vector org; + org = EyePosition(); + + engine->AddOriginToPVS( org ); +} + +int CBasePlayer::UpdateTransmitState() +{ + // always call ShouldTransmit() for players + return SetTransmitState( FL_EDICT_FULLCHECK ); +} + +int CBasePlayer::ShouldTransmit( const CCheckTransmitInfo *pInfo ) +{ + // Allow me to introduce myself to, err, myself. + // I.e., always update the recipient player data even if it's nodraw (first person mode) + if ( pInfo->m_pClientEnt == edict() ) + { + return FL_EDICT_ALWAYS; + } + + // when HLTV is connected and spectators press +USE, they + // signal that they are recording a interesting scene + // so transmit these 'cameramans' to the HLTV client + if ( HLTVDirector()->GetCameraMan() == entindex() ) + { + CBaseEntity *pRecipientEntity = CBaseEntity::Instance( pInfo->m_pClientEnt ); + + Assert( pRecipientEntity->IsPlayer() ); + + CBasePlayer *pRecipientPlayer = static_cast( pRecipientEntity ); + if ( pRecipientPlayer->IsHLTV() ) + { + // HACK force calling RecomputePVSInformation to update PVS data + NetworkProp()->AreaNum(); + return FL_EDICT_ALWAYS; + } + } + + // Transmit for a short time after death so ragdolls can access reliable player data + if ( IsEffectActive( EF_NODRAW ) || ( IsObserver() && ( gpGlobals->curtime - m_flDeathTime > 0.5 ) ) ) + { + return FL_EDICT_DONTSEND; + } + + return BaseClass::ShouldTransmit( pInfo ); +} + + +bool CBasePlayer::WantsLagCompensationOnEntity( const CBasePlayer *pPlayer, const CUserCmd *pCmd, const CBitVec *pEntityTransmitBits ) const +{ + // Team members shouldn't be adjusted unless friendly fire is on. + if ( !friendlyfire.GetInt() && pPlayer->GetTeamNumber() == GetTeamNumber() ) + return false; + + // If this entity hasn't been transmitted to us and acked, then don't bother lag compensating it. + if ( pEntityTransmitBits && !pEntityTransmitBits->Get( pPlayer->entindex() ) ) + return false; + + const Vector &vMyOrigin = GetAbsOrigin(); + const Vector &vHisOrigin = pPlayer->GetAbsOrigin(); + + // get max distance player could have moved within max lag compensation time, + // multiply by 1.5 to to avoid "dead zones" (sqrt(2) would be the exact value) + float maxDistance = 1.5 * pPlayer->MaxSpeed() * sv_maxunlag.GetFloat(); + + // If the player is within this distance, lag compensate them in case they're running past us. + if ( vHisOrigin.DistTo( vMyOrigin ) < maxDistance ) + return true; + + // If their origin is not within a 45 degree cone in front of us, no need to lag compensate. + Vector vForward; + AngleVectors( pCmd->viewangles, &vForward ); + + Vector vDiff = vHisOrigin - vMyOrigin; + VectorNormalize( vDiff ); + + float flCosAngle = 0.707107f; // 45 degree angle + if ( vForward.Dot( vDiff ) < flCosAngle ) + return false; + + return true; +} + + +//----------------------------------------------------------------------------- +// Sets the view angles +//----------------------------------------------------------------------------- +void CBasePlayer::SnapEyeAngles( const QAngle &viewAngles ) +{ + pl.v_angle = viewAngles; + pl.fixangle = FIXANGLE_ABSOLUTE; +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : iSpeed - +// iMax - +// Output : int +//----------------------------------------------------------------------------- +int TrainSpeed(int iSpeed, int iMax) +{ + float fSpeed, fMax; + int iRet = 0; + + fMax = (float)iMax; + fSpeed = iSpeed; + + fSpeed = fSpeed/fMax; + + if (iSpeed < 0) + iRet = TRAIN_BACK; + else if (iSpeed == 0) + iRet = TRAIN_NEUTRAL; + else if (fSpeed < 0.33) + iRet = TRAIN_SLOW; + else if (fSpeed < 0.66) + iRet = TRAIN_MEDIUM; + else + iRet = TRAIN_FAST; + + return iRet; +} + +void CBasePlayer::DeathSound( const CTakeDamageInfo &info ) +{ + // temporarily using pain sounds for death sounds + + // Did we die from falling? + if ( m_bitsDamageType & DMG_FALL ) + { + // They died in the fall. Play a splat sound. + EmitSound( "Player.FallGib" ); + } + else + { + EmitSound( "Player.Death" ); + } + + // play one of the suit death alarms + if ( IsSuitEquipped() ) + { + UTIL_EmitGroupnameSuit(edict(), "HEV_DEAD"); + } +} + +// override takehealth +// bitsDamageType indicates type of damage healed. + +int CBasePlayer::TakeHealth( float flHealth, int bitsDamageType ) +{ + // clear out any damage types we healed. + // UNDONE: generic health should not heal any + // UNDONE: time-based damage + if (m_takedamage) + { + m_bitsDamageType &= ~(bitsDamageType & ~DMG_TIMEBASED); + } + + return BaseClass::TakeHealth (flHealth, bitsDamageType); +} + + +//----------------------------------------------------------------------------- +// Purpose: Draw all overlays (should be implemented in cascade by subclass to add +// any additional non-text overlays) +// Input : +// Output : Current text offset from the top +//----------------------------------------------------------------------------- +void CBasePlayer::DrawDebugGeometryOverlays(void) +{ + // -------------------------------------------------------- + // If in buddha mode and dead draw lines to indicate death + // -------------------------------------------------------- + if ((m_debugOverlays & OVERLAY_BUDDHA_MODE) && m_iHealth == 1) + { + Vector vBodyDir = BodyDirection2D( ); + Vector eyePos = EyePosition() + vBodyDir*10.0; + Vector vUp = Vector(0,0,8); + Vector vSide; + CrossProduct( vBodyDir, vUp, vSide); + NDebugOverlay::Line(eyePos+vSide+vUp, eyePos-vSide-vUp, 255,0,0, false, 0); + NDebugOverlay::Line(eyePos+vSide-vUp, eyePos-vSide+vUp, 255,0,0, false, 0); + } + BaseClass::DrawDebugGeometryOverlays(); +} + +//========================================================= +// TraceAttack +//========================================================= +void CBasePlayer::TraceAttack( const CTakeDamageInfo &inputInfo, const Vector &vecDir, trace_t *ptr ) +{ + if ( m_takedamage ) + { + CTakeDamageInfo info = inputInfo; + + // -------------------------------------------------- + // If an NPC check if friendly fire is disallowed + // -------------------------------------------------- + CAI_BaseNPC *pNPC = info.GetAttacker()->MyNPCPointer(); + if ( pNPC && (pNPC->CapabilitiesGet() & bits_CAP_NO_HIT_PLAYER) && pNPC->IRelationType( this ) != D_HT ) + { + return; + } + + // Prevent team damage here so blood doesn't appear + if ( info.GetAttacker()->IsPlayer() ) + { + if ( !g_pGameRules->FPlayerCanTakeDamage( this, info.GetAttacker() ) ) + return; + } + + SetLastHitGroup( ptr->hitgroup ); + + + switch ( ptr->hitgroup ) + { + case HITGROUP_GENERIC: + break; + case HITGROUP_HEAD: + info.ScaleDamage( sk_player_head.GetFloat() ); + break; + case HITGROUP_CHEST: + info.ScaleDamage( sk_player_chest.GetFloat() ); + break; + case HITGROUP_STOMACH: + info.ScaleDamage( sk_player_stomach.GetFloat() ); + break; + case HITGROUP_LEFTARM: + case HITGROUP_RIGHTARM: + info.ScaleDamage( sk_player_arm.GetFloat() ); + break; + case HITGROUP_LEFTLEG: + case HITGROUP_RIGHTLEG: + info.ScaleDamage( sk_player_leg.GetFloat() ); + break; + default: + break; + } + + SpawnBlood(ptr->endpos, vecDir, BloodColor(), info.GetDamage());// a little surface blood. + TraceBleed( info.GetDamage(), vecDir, ptr, info.GetDamageType() ); + AddMultiDamage( info, this ); + } +} + +//------------------------------------------------------------------------------ +// Purpose : Do some kind of damage effect for the type of damage +// Input : +// Output : +//------------------------------------------------------------------------------ +void CBasePlayer::DamageEffect(float flDamage, int fDamageType) +{ + if (fDamageType & DMG_CRUSH) + { + //Red damage indicator + color32 red = {128,0,0,128}; + UTIL_ScreenFade( this, red, 1.0f, 0.1f, FFADE_IN ); + } + else if (fDamageType & DMG_DROWN) + { + //Red damage indicator + color32 blue = {0,0,128,128}; + UTIL_ScreenFade( this, blue, 1.0f, 0.1f, FFADE_IN ); + } + else if (fDamageType & DMG_SLASH) + { + // If slash damage shoot some blood + SpawnBlood(EyePosition(), g_vecAttackDir, BloodColor(), flDamage); + } + else if (fDamageType & DMG_PLASMA) + { + // Blue screen fade + color32 blue = {0,0,255,100}; + UTIL_ScreenFade( this, blue, 0.2, 0.4, FFADE_MODULATE ); + + // Very small screen shake + ViewPunch(QAngle(random->RandomFloat(-0.1,0.1), random->RandomFloat(-0.1,0.1), random->RandomFloat(-0.1,0.1))); + + // Burn sound + EmitSound( "Player.PlasmaDamage" ); + } + else if (fDamageType & DMG_SONIC) + { + // Sonic damage sound + EmitSound( "Player.SonicDamage" ); + } + else if ( fDamageType & DMG_BULLET ) + { + EmitSound( "Flesh.BulletImpact" ); + } +} + +/* + Take some damage. + NOTE: each call to OnTakeDamage with bitsDamageType set to a time-based damage + type will cause the damage time countdown to be reset. Thus the ongoing effects of poison, radiation + etc are implemented with subsequent calls to OnTakeDamage using DMG_GENERIC. +*/ + +// Old values +#define OLD_ARMOR_RATIO 0.2 // Armor Takes 80% of the damage +#define OLD_ARMOR_BONUS 0.5 // Each Point of Armor is work 1/x points of health + +// New values +#define ARMOR_RATIO 0.2 +#define ARMOR_BONUS 1.0 + +//--------------------------------------------------------- +//--------------------------------------------------------- +bool CBasePlayer::ShouldTakeDamageInCommentaryMode( const CTakeDamageInfo &inputInfo ) +{ + // Only ignore damage when we're listening to a commentary node + if ( !IsListeningToCommentary() ) + return true; + + // Allow SetHealth inputs to kill player. + if ( inputInfo.GetInflictor() == this && inputInfo.GetAttacker() == this ) + return true; + + // In commentary, ignore all damage except for falling and leeches + if ( !(inputInfo.GetDamageType() & (DMG_BURN | DMG_PLASMA | DMG_FALL | DMG_CRUSH)) && inputInfo.GetDamageType() != DMG_GENERIC ) + return false; + + // We let DMG_CRUSH pass the check above so that we can check here for stress damage. Deny the CRUSH damage if there is no attacker, + // or if the attacker isn't a BSP model. Therefore, we're allowing any CRUSH damage done by a BSP model. + if ( (inputInfo.GetDamageType() & DMG_CRUSH) && ( inputInfo.GetAttacker() == NULL || !inputInfo.GetAttacker()->IsBSPModel() ) ) + return false; + + return true; +} + +int CBasePlayer::OnTakeDamage( const CTakeDamageInfo &inputInfo ) +{ + // have suit diagnose the problem - ie: report damage type + int bitsDamage = inputInfo.GetDamageType(); + int ffound = true; + int fmajor; + int fcritical; + int fTookDamage; + int ftrivial; + float flRatio; + float flBonus; + float flHealthPrev = m_iHealth; + + CTakeDamageInfo info = inputInfo; + + IServerVehicle *pVehicle = GetVehicle(); + if ( pVehicle ) + { + // Players don't take blast or radiation damage while in vehicles. + // The vehicle has to deal it to him + if ( info.GetDamageType() & (DMG_BLAST|DMG_RADIATION) ) + return 0; + + info.ScaleDamage(pVehicle->DamageModifier(info)); + } + + if ( IsInCommentaryMode() ) + { + if( !ShouldTakeDamageInCommentaryMode( info ) ) + return 0; + } + + if ( GetFlags() & FL_GODMODE ) + return 0; + + if ( m_debugOverlays & OVERLAY_BUDDHA_MODE ) + { + if ((m_iHealth - info.GetDamage()) <= 0) + { + m_iHealth = 1; + return 0; + } + } + + // Early out if there's no damage + if ( !info.GetDamage() ) + return 0; + + if( old_armor.GetBool() ) + { + flBonus = OLD_ARMOR_BONUS; + flRatio = OLD_ARMOR_RATIO; + } + else + { + flBonus = ARMOR_BONUS; + flRatio = ARMOR_RATIO; + } + + if ( ( info.GetDamageType() & DMG_BLAST ) && g_pGameRules->IsMultiplayer() ) + { + // blasts damage armor more. + flBonus *= 2; + } + + // Already dead + if ( !IsAlive() ) + return 0; + // go take the damage first + + + if ( !g_pGameRules->FPlayerCanTakeDamage( this, info.GetAttacker() ) ) + { + // Refuse the damage + return 0; + } + + // keep track of amount of damage last sustained + m_lastDamageAmount = static_cast(info.GetDamage()); + + // Armor. + if (m_ArmorValue && !(info.GetDamageType() & (DMG_FALL | DMG_DROWN | DMG_POISON | DMG_RADIATION)) )// armor doesn't protect against fall or drown damage! + { + float flNew = info.GetDamage() * flRatio; + + float flArmor; + + flArmor = (info.GetDamage() - flNew) * flBonus; + + if( !old_armor.GetBool() ) + { + if( flArmor < 1.0 ) + { + flArmor = 1.0; + } + } + + // Does this use more armor than we have? + if (flArmor > m_ArmorValue) + { + flArmor = m_ArmorValue; + flArmor *= (1/flBonus); + flNew = info.GetDamage() - flArmor; + m_DmgSave = m_ArmorValue; + m_ArmorValue = 0; + } + else + { + m_DmgSave = flArmor; + m_ArmorValue -= flArmor; + } + + info.SetDamage( flNew ); + } + + // this cast to INT is critical!!! If a player ends up with 0.5 health, the engine will get that + // as an int (zero) and think the player is dead! (this will incite a clientside screentilt, etc) + + // NOTENOTE: jdw - We are now capable of retaining the matissa of this damage value and deferring its application + + // info.SetDamage( (int)info.GetDamage() ); + + // Call up to the base class + fTookDamage = BaseClass::OnTakeDamage( info ); + + // Early out if the base class took no damage + if ( !fTookDamage ) + return 0; + + // add to the damage total for clients, which will be sent as a single + // message at the end of the frame + // todo: remove after combining shotgun blasts? + if ( info.GetInflictor() && info.GetInflictor()->edict() ) + m_DmgOrigin = info.GetInflictor()->GetAbsOrigin(); + + m_DmgTake += (int)info.GetDamage(); + + // reset damage time countdown for each type of time based damage player just sustained + + for (int i = 0; i < CDMG_TIMEBASED; i++) + { + if (info.GetDamageType() & (DMG_PARALYZE << i)) + { + m_rgbTimeBasedDamage[i] = 0; + } + } + + // Display any effect associate with this damage type + DamageEffect(info.GetDamage(),bitsDamage); + + // how bad is it, doc? + ftrivial = (m_iHealth > 75 || m_lastDamageAmount < 5); + fmajor = (m_lastDamageAmount > 25); + fcritical = (m_iHealth < 30); + + // handle all bits set in this damage message, + // let the suit give player the diagnosis + + // UNDONE: add sounds for types of damage sustained (ie: burn, shock, slash ) + + // UNDONE: still need to record damage and heal messages for the following types + + // DMG_BURN + // DMG_FREEZE + // DMG_BLAST + // DMG_SHOCK + + m_bitsDamageType |= bitsDamage; // Save this so we can report it to the client + m_bitsHUDDamage = -1; // make sure the damage bits get resent + + while (fTookDamage && (!ftrivial || (bitsDamage & DMG_TIMEBASED)) && ffound && bitsDamage) + { + ffound = false; + + if (bitsDamage & DMG_CLUB) + { + if (fmajor) + SetSuitUpdate("!HEV_DMG4", false, SUIT_NEXT_IN_30SEC); // minor fracture + bitsDamage &= ~DMG_CLUB; + ffound = true; + } + if (bitsDamage & (DMG_FALL | DMG_CRUSH)) + { + if (fmajor) + SetSuitUpdate("!HEV_DMG5", false, SUIT_NEXT_IN_30SEC); // major fracture + else + SetSuitUpdate("!HEV_DMG4", false, SUIT_NEXT_IN_30SEC); // minor fracture + + bitsDamage &= ~(DMG_FALL | DMG_CRUSH); + ffound = true; + } + + if (bitsDamage & DMG_BULLET) + { + if (m_lastDamageAmount > 5) + SetSuitUpdate("!HEV_DMG6", false, SUIT_NEXT_IN_30SEC); // blood loss detected + //else + // SetSuitUpdate("!HEV_DMG0", false, SUIT_NEXT_IN_30SEC); // minor laceration + + bitsDamage &= ~DMG_BULLET; + ffound = true; + } + + if (bitsDamage & DMG_SLASH) + { + if (fmajor) + SetSuitUpdate("!HEV_DMG1", false, SUIT_NEXT_IN_30SEC); // major laceration + else + SetSuitUpdate("!HEV_DMG0", false, SUIT_NEXT_IN_30SEC); // minor laceration + + bitsDamage &= ~DMG_SLASH; + ffound = true; + } + + if (bitsDamage & DMG_SONIC) + { + if (fmajor) + SetSuitUpdate("!HEV_DMG2", false, SUIT_NEXT_IN_1MIN); // internal bleeding + bitsDamage &= ~DMG_SONIC; + ffound = true; + } + + if (bitsDamage & (DMG_POISON | DMG_PARALYZE)) + { + if (bitsDamage & DMG_POISON) + { + m_nPoisonDmg += static_cast(info.GetDamage()); + m_tbdPrev = gpGlobals->curtime; + m_rgbTimeBasedDamage[itbd_PoisonRecover] = 0; + } + + SetSuitUpdate("!HEV_DMG3", false, SUIT_NEXT_IN_1MIN); // blood toxins detected + bitsDamage &= ~( DMG_POISON | DMG_PARALYZE ); + ffound = true; + } + + if (bitsDamage & DMG_ACID) + { + SetSuitUpdate("!HEV_DET1", false, SUIT_NEXT_IN_1MIN); // hazardous chemicals detected + bitsDamage &= ~DMG_ACID; + ffound = true; + } + + if (bitsDamage & DMG_NERVEGAS) + { + SetSuitUpdate("!HEV_DET0", false, SUIT_NEXT_IN_1MIN); // biohazard detected + bitsDamage &= ~DMG_NERVEGAS; + ffound = true; + } + + if (bitsDamage & DMG_RADIATION) + { + SetSuitUpdate("!HEV_DET2", false, SUIT_NEXT_IN_1MIN); // radiation detected + bitsDamage &= ~DMG_RADIATION; + ffound = true; + } + if (bitsDamage & DMG_SHOCK) + { + bitsDamage &= ~DMG_SHOCK; + ffound = true; + } + } + + m_Local.m_vecPunchAngle.SetX( -2 ); + + if (fTookDamage && !ftrivial && fmajor && flHealthPrev >= 75) + { + // first time we take major damage... + // turn automedic on if not on + SetSuitUpdate("!HEV_MED1", false, SUIT_NEXT_IN_30MIN); // automedic on + + // give morphine shot if not given recently + SetSuitUpdate("!HEV_HEAL7", false, SUIT_NEXT_IN_30MIN); // morphine shot + } + + if (fTookDamage && !ftrivial && fcritical && flHealthPrev < 75) + { + + // already took major damage, now it's critical... + if (m_iHealth < 6) + SetSuitUpdate("!HEV_HLTH3", false, SUIT_NEXT_IN_10MIN); // near death + else if (m_iHealth < 20) + SetSuitUpdate("!HEV_HLTH2", false, SUIT_NEXT_IN_10MIN); // health critical + + // give critical health warnings + if (!random->RandomInt(0,3) && flHealthPrev < 50) + SetSuitUpdate("!HEV_DMG7", false, SUIT_NEXT_IN_5MIN); //seek medical attention + } + + // if we're taking time based damage, warn about its continuing effects + if (fTookDamage && (info.GetDamageType() & DMG_TIMEBASED) && flHealthPrev < 75) + { + if (flHealthPrev < 50) + { + if (!random->RandomInt(0,3)) + SetSuitUpdate("!HEV_DMG7", false, SUIT_NEXT_IN_5MIN); //seek medical attention + } + else + SetSuitUpdate("!HEV_HLTH1", false, SUIT_NEXT_IN_10MIN); // health dropping + } + + // Do special explosion damage effect + if ( bitsDamage & DMG_BLAST ) + { + OnDamagedByExplosion( info ); + } + + return fTookDamage; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : &info - +// damageAmount - +//----------------------------------------------------------------------------- +#define MIN_SHOCK_AND_CONFUSION_DAMAGE 30.0f +#define MIN_EAR_RINGING_DISTANCE 240.0f // 20 feet + +//----------------------------------------------------------------------------- +// Purpose: +// Input : &info - +//----------------------------------------------------------------------------- +void CBasePlayer::OnDamagedByExplosion( const CTakeDamageInfo &info ) +{ + float lastDamage = info.GetDamage(); + + float distanceFromPlayer = 9999.0f; + + CBaseEntity *inflictor = info.GetInflictor(); + if ( inflictor ) + { + Vector delta = GetAbsOrigin() - inflictor->GetAbsOrigin(); + distanceFromPlayer = delta.Length(); + } + + bool ear_ringing = distanceFromPlayer < MIN_EAR_RINGING_DISTANCE ? true : false; + bool shock = lastDamage >= MIN_SHOCK_AND_CONFUSION_DAMAGE; + + if ( !shock && !ear_ringing ) + return; + + int effect = shock ? + random->RandomInt( 35, 37 ) : + random->RandomInt( 32, 34 ); + + CSingleUserRecipientFilter user( this ); + enginesound->SetPlayerDSP( user, effect, false ); +} + +//========================================================= +// PackDeadPlayerItems - call this when a player dies to +// pack up the appropriate weapons and ammo items, and to +// destroy anything that shouldn't be packed. +// +// This is pretty brute force :( +//========================================================= +void CBasePlayer::PackDeadPlayerItems( void ) +{ + int iWeaponRules; + int iAmmoRules; + int i; + CBaseCombatWeapon *rgpPackWeapons[ 20 ];// 20 hardcoded for now. How to determine exactly how many weapons we have? + int iPackAmmo[ MAX_AMMO_SLOTS + 1]; + int iPW = 0;// index into packweapons array + int iPA = 0;// index into packammo array + + memset(rgpPackWeapons, 0, sizeof(rgpPackWeapons) ); + memset(iPackAmmo, -1, sizeof(iPackAmmo) ); + + // get the game rules + iWeaponRules = g_pGameRules->DeadPlayerWeapons( this ); + iAmmoRules = g_pGameRules->DeadPlayerAmmo( this ); + + if ( iWeaponRules == GR_PLR_DROP_GUN_NO && iAmmoRules == GR_PLR_DROP_AMMO_NO ) + { + // nothing to pack. Remove the weapons and return. Don't call create on the box! + RemoveAllItems( true ); + return; + } + +// go through all of the weapons and make a list of the ones to pack + for ( i = 0 ; i < WeaponCount() ; i++ ) + { + // there's a weapon here. Should I pack it? + CBaseCombatWeapon *pPlayerItem = GetWeapon( i ); + if ( pPlayerItem ) + { + switch( iWeaponRules ) + { + case GR_PLR_DROP_GUN_ACTIVE: + if ( GetActiveWeapon() && pPlayerItem == GetActiveWeapon() ) + { + // this is the active item. Pack it. + rgpPackWeapons[ iPW++ ] = pPlayerItem; + } + break; + + case GR_PLR_DROP_GUN_ALL: + rgpPackWeapons[ iPW++ ] = pPlayerItem; + break; + + default: + break; + } + } + } + +// now go through ammo and make a list of which types to pack. + if ( iAmmoRules != GR_PLR_DROP_AMMO_NO ) + { + for ( i = 0 ; i < MAX_AMMO_SLOTS ; i++ ) + { + if ( GetAmmoCount( i ) > 0 ) + { + // player has some ammo of this type. + switch ( iAmmoRules ) + { + case GR_PLR_DROP_AMMO_ALL: + iPackAmmo[ iPA++ ] = i; + break; + + case GR_PLR_DROP_AMMO_ACTIVE: + // WEAPONTODO: Make this work + /* + if ( GetActiveWeapon() && i == GetActiveWeapon()->m_iPrimaryAmmoType ) + { + // this is the primary ammo type for the active weapon + iPackAmmo[ iPA++ ] = i; + } + else if ( GetActiveWeapon() && i == GetActiveWeapon()->m_iSecondaryAmmoType ) + { + // this is the secondary ammo type for the active weapon + iPackAmmo[ iPA++ ] = i; + } + */ + break; + + default: + break; + } + } + } + } + + RemoveAllItems( true );// now strip off everything that wasn't handled by the code above. +} + +void CBasePlayer::RemoveAllItems( bool removeSuit ) +{ + if (GetActiveWeapon()) + { + ResetAutoaim( ); + GetActiveWeapon()->Holster( ); + } + + Weapon_SetLast( NULL ); + RemoveAllWeapons(); + RemoveAllAmmo(); + + if ( removeSuit ) + { + RemoveSuit(); + } + + UpdateClientData(); +} + +bool CBasePlayer::IsDead() const +{ + return m_lifeState == LIFE_DEAD; +} + +static float DamageForce( const Vector &size, float damage ) +{ + float force = damage * ((32 * 32 * 72.0) / (size.x * size.y * size.z)) * 5; + + if ( force > 1000.0) + { + force = 1000.0; + } + + return force; +} + + +int CBasePlayer::OnTakeDamage_Alive( const CTakeDamageInfo &info ) +{ + // set damage type sustained + m_bitsDamageType |= info.GetDamageType(); + + if ( !BaseClass::OnTakeDamage_Alive( info ) ) + return 0; + + CBaseEntity * attacker = info.GetAttacker(); + + if ( !attacker ) + return 0; + + Vector vecDir = vec3_origin; + if ( info.GetInflictor() ) + { + vecDir = info.GetInflictor()->WorldSpaceCenter() - Vector ( 0, 0, 10 ) - WorldSpaceCenter(); + VectorNormalize( vecDir ); + } + + if ( info.GetInflictor() && (GetMoveType() == MOVETYPE_WALK) && + ( !attacker->IsSolidFlagSet(FSOLID_TRIGGER)) ) + { + Vector force = vecDir * -DamageForce( WorldAlignSize(), info.GetBaseDamage() ); + if ( force.z > 250.0f ) + { + force.z = 250.0f; + } + ApplyAbsVelocityImpulse( force ); + } + + // fire global game event + + IGameEvent * event = gameeventmanager->CreateEvent( "player_hurt" ); + if ( event ) + { + event->SetInt("userid", GetUserID() ); + event->SetInt("health", max(0, m_iHealth) ); + event->SetInt("priority", 5 ); // HLTV event priority, not transmitted + + if ( attacker->IsPlayer() ) + { + CBasePlayer *player = ToBasePlayer( attacker ); + event->SetInt("attacker", player->GetUserID() ); // hurt by other player + } + else + { + event->SetInt("attacker", 0 ); // hurt by "world" + } + + gameeventmanager->FireEvent( event ); + } + + // Insert a combat sound so that nearby NPCs hear battle + if ( attacker->IsNPC() ) + { + CSoundEnt::InsertSound( SOUND_COMBAT, GetAbsOrigin(), 512, 0.5, this );//<>//magic number + } + + return 1; +} + + +void CBasePlayer::Event_Killed( const CTakeDamageInfo &info ) +{ + CSound *pSound; + + g_pGameRules->PlayerKilled( this, info ); + + RumbleEffect( RUMBLE_STOP_ALL, 0, RUMBLE_FLAGS_NONE ); + + ClearUseEntity(); + + // this client isn't going to be thinking for a while, so reset the sound until they respawn + pSound = CSoundEnt::SoundPointerForIndex( CSoundEnt::ClientSoundIndex( edict() ) ); + { + if ( pSound ) + { + pSound->Reset(); + } + } + + // don't let the status bar glitch for players with <0 health. + if (m_iHealth < -99) + { + m_iHealth = 0; + } + + // holster the current weapon + if ( GetActiveWeapon() ) + { + GetActiveWeapon()->Holster(); + } + + SetAnimation( PLAYER_DIE ); + + SetViewOffset( VEC_DEAD_VIEWHEIGHT ); + m_lifeState = LIFE_DYING; + + pl.deadflag = true; + AddSolidFlags( FSOLID_NOT_SOLID ); + // force contact points to get flushed if no longer valid + // UNDONE: Always do this on RecheckCollisionFilter() ? + IPhysicsObject *pObject = VPhysicsGetObject(); + if ( pObject ) + { + pObject->RecheckContactPoints(); + } + + SetMoveType( MOVETYPE_FLYGRAVITY ); + SetGroundEntity( NULL ); + + // clear out the suit message cache so we don't keep chattering + SetSuitUpdate(NULL, false, 0); + + // reset FOV + SetFOV( this, 0 ); + + if ( FlashlightIsOn() ) + { + FlashlightTurnOff(); + } + + m_flDeathTime = gpGlobals->curtime; + + // only count alive players + if (m_lastNavArea) + { + m_lastNavArea->DecrementPlayerCount( GetTeamNumber() ); + m_lastNavArea = NULL; + } + + BaseClass::Event_Killed( info ); +} + +void CBasePlayer::Event_Dying() +{ + // NOT GIBBED, RUN THIS CODE + + CTakeDamageInfo info; + DeathSound( info ); + + // The dead body rolls out of the vehicle. + if ( IsInAVehicle() ) + { + LeaveVehicle(); + } + + QAngle angles = GetLocalAngles(); + + angles.x = 0; + angles.z = 0; + + SetLocalAngles( angles ); + + SetThink(&CBasePlayer::PlayerDeathThink); + SetNextThink( gpGlobals->curtime + 0.1f ); + BaseClass::Event_Dying(); +} + + +// Set the activity based on an event or current state +void CBasePlayer::SetAnimation( PLAYER_ANIM playerAnim ) +{ + int animDesired; + char szAnim[64]; + + float speed; + + speed = GetAbsVelocity().Length2D(); + + if (GetFlags() & (FL_FROZEN|FL_ATCONTROLS)) + { + speed = 0; + playerAnim = PLAYER_IDLE; + } + + Activity idealActivity = ACT_WALK;// TEMP!!!!! + + // This could stand to be redone. Why is playerAnim abstracted from activity? (sjb) + if (playerAnim == PLAYER_JUMP) + { + idealActivity = ACT_HOP; + } + else if (playerAnim == PLAYER_SUPERJUMP) + { + idealActivity = ACT_LEAP; + } + else if (playerAnim == PLAYER_DIE) + { + if ( m_lifeState == LIFE_ALIVE ) + { + idealActivity = GetDeathActivity(); + } + } + else if (playerAnim == PLAYER_ATTACK1) + { + if ( m_Activity == ACT_HOVER || + m_Activity == ACT_SWIM || + m_Activity == ACT_HOP || + m_Activity == ACT_LEAP || + m_Activity == ACT_DIESIMPLE ) + { + idealActivity = m_Activity; + } + else + { + idealActivity = ACT_RANGE_ATTACK1; + } + } + else if (playerAnim == PLAYER_IDLE || playerAnim == PLAYER_WALK) + { + if ( !( GetFlags() & FL_ONGROUND ) && (m_Activity == ACT_HOP || m_Activity == ACT_LEAP) ) // Still jumping + { + idealActivity = m_Activity; + } + else if ( GetWaterLevel() > 1 ) + { + if ( speed == 0 ) + idealActivity = ACT_HOVER; + else + idealActivity = ACT_SWIM; + } + else + { + idealActivity = ACT_WALK; + } + } + + + if (idealActivity == ACT_RANGE_ATTACK1) + { + if ( GetFlags() & FL_DUCKING ) // crouching + { + Q_strncpy( szAnim, "crouch_shoot_" ,sizeof(szAnim)); + } + else + { + Q_strncpy( szAnim, "ref_shoot_" ,sizeof(szAnim)); + } + Q_strncat( szAnim, m_szAnimExtension ,sizeof(szAnim), COPY_ALL_CHARACTERS ); + animDesired = LookupSequence( szAnim ); + if (animDesired == -1) + animDesired = 0; + + if ( GetSequence() != animDesired || !SequenceLoops() ) + { + SetCycle( 0 ); + } + + // Tracker 24588: In single player when firing own weapon this causes eye and punchangle to jitter + //if (!SequenceLoops()) + //{ + // AddEffects( EF_NOINTERP ); + //} + + SetActivity( idealActivity ); + ResetSequence( animDesired ); + } + else if (idealActivity == ACT_WALK) + { + if (GetActivity() != ACT_RANGE_ATTACK1 || IsActivityFinished()) + { + if ( GetFlags() & FL_DUCKING ) // crouching + { + Q_strncpy( szAnim, "crouch_aim_" ,sizeof(szAnim)); + } + else + { + Q_strncpy( szAnim, "ref_aim_" ,sizeof(szAnim)); + } + Q_strncat( szAnim, m_szAnimExtension,sizeof(szAnim), COPY_ALL_CHARACTERS ); + animDesired = LookupSequence( szAnim ); + if (animDesired == -1) + animDesired = 0; + SetActivity( ACT_WALK ); + } + else + { + animDesired = GetSequence(); + } + } + else + { + if ( GetActivity() == idealActivity) + return; + + SetActivity( idealActivity ); + + animDesired = SelectWeightedSequence( m_Activity ); + + // Already using the desired animation? + if (GetSequence() == animDesired) + return; + + ResetSequence( animDesired ); + SetCycle( 0 ); + return; + } + + // Already using the desired animation? + if (GetSequence() == animDesired) + return; + + //Msg( "Set animation to %d\n", animDesired ); + // Reset to first frame of desired animation + ResetSequence( animDesired ); + SetCycle( 0 ); +} + +//----------------------------------------------------------------------------- +// Purpose: data accessor +// ensure that for every emitsound there is a matching stopsound +//----------------------------------------------------------------------------- +void CBasePlayer::SetPlayerUnderwater( bool state ) +{ + if ( m_bPlayerUnderwater != state ) + { + m_bPlayerUnderwater = state; + + if ( state ) + EmitSound( "Player.AmbientUnderWater" ); + else + StopSound( "Player.AmbientUnderWater" ); + } +} + +/* +=========== +WaterMove +============ +*/ +#ifdef HL2_DLL + +// test for HL2 drowning damage increase (aux power used instead) +#define AIRTIME 7 // lung full of air lasts this many seconds +#define DROWNING_DAMAGE_INITIAL 10 +#define DROWNING_DAMAGE_MAX 10 + +#else + +#define AIRTIME 12 // lung full of air lasts this many seconds +#define DROWNING_DAMAGE_INITIAL 2 +#define DROWNING_DAMAGE_MAX 5 + +#endif + +void CBasePlayer::WaterMove() +{ + int air; + + if ( ( GetMoveType() == MOVETYPE_NOCLIP ) && !GetMoveParent() ) + { + m_AirFinished = gpGlobals->curtime + AIRTIME; + return; + } + + if ( m_iHealth < 0 || !IsAlive() ) + { + if ( GetWaterLevel() < WL_Eyes ) + { + if ( IsPlayerUnderwater() ) + { + SetPlayerUnderwater( false ); + } + } + else if ( GetWaterLevel() < WL_Waist ) + { + if ( GetWaterLevel() == 0 ) + { + if ( GetFlags() & FL_INWATER ) + { + RemoveFlag( FL_INWATER ); + } + return; + } + } + else if ( GetWaterLevel() > WL_Waist ) + { + if ( IsPlayerUnderwater() == false ) + { + SetPlayerUnderwater( true ); + } + return; + } + return; + } + + // waterlevel 0 - not in water (WL_NotInWater) + // waterlevel 1 - feet in water (WL_Feet) + // waterlevel 2 - waist in water (WL_Waist) + // waterlevel 3 - head in water (WL_Eyes) + + if (GetWaterLevel() != WL_Eyes || CanBreatheUnderwater()) + { + // not underwater + + // play 'up for air' sound + + if (m_AirFinished < gpGlobals->curtime) + { + EmitSound( "Player.DrownStart" ); + } + + m_AirFinished = gpGlobals->curtime + AIRTIME; + m_nDrownDmgRate = DROWNING_DAMAGE_INITIAL; + + // if we took drowning damage, give it back slowly + if (m_idrowndmg > m_idrownrestored) + { + // set drowning damage bit. hack - dmg_drownrecover actually + // makes the time based damage code 'give back' health over time. + // make sure counter is cleared so we start count correctly. + + // NOTE: this actually causes the count to continue restarting + // until all drowning damage is healed. + + m_bitsDamageType |= DMG_DROWNRECOVER; + m_bitsDamageType &= ~DMG_DROWN; + m_rgbTimeBasedDamage[itbd_DrownRecover] = 0; + } + + } + else + { // fully under water + // stop restoring damage while underwater + m_bitsDamageType &= ~DMG_DROWNRECOVER; + m_rgbTimeBasedDamage[itbd_DrownRecover] = 0; + + if (m_AirFinished < gpGlobals->curtime && !(GetFlags() & FL_GODMODE) ) // drown! + { + if (m_PainFinished < gpGlobals->curtime) + { + // take drowning damage + m_nDrownDmgRate += 1; + if (m_nDrownDmgRate > DROWNING_DAMAGE_MAX) + { + m_nDrownDmgRate = DROWNING_DAMAGE_MAX; + } + + OnTakeDamage( CTakeDamageInfo( GetContainingEntity(INDEXENT(0)), GetContainingEntity(INDEXENT(0)), m_nDrownDmgRate, DMG_DROWN ) ); + m_PainFinished = gpGlobals->curtime + 1; + + // track drowning damage, give it back when + // player finally takes a breath + m_idrowndmg += m_nDrownDmgRate; + } + } + else + { + m_bitsDamageType &= ~DMG_DROWN; + } + } + + if ( GetWaterLevel() < WL_Eyes ) + { + if ( IsPlayerUnderwater() ) + { + SetPlayerUnderwater( false ); + } + } + else if ( GetWaterLevel() < WL_Waist ) + { + if ( GetWaterLevel() == 0 ) + { + if ( GetFlags() & FL_INWATER ) + { + EmitSound( "Player.Wade" ); + RemoveFlag( FL_INWATER ); + } + return; + } + } + else if ( GetWaterLevel() > WL_Waist ) + { + if ( IsPlayerUnderwater() == false ) + { + SetPlayerUnderwater( true ); + } + return; + } + + // make bubbles + + air = (int)( m_AirFinished - gpGlobals->curtime ); + +#if 0 + if (GetWaterType() == CONTENT_LAVA) // do damage + { + if (m_flDamageTime < gpGlobals->curtime) + { + OnTakeDamage( GetContainingEntity(INDEXENT(0)), GetContainingEntity(INDEXENT(0)), 10 * GetWaterLevel(), DMG_BURN); + } + } + else if (GetWaterType() == CONTENT_SLIME) // do damage + { + m_flDamageTime = gpGlobals->curtime + 1; + OnTakeDamage(GetContainingEntity(INDEXENT(0)), GetContainingEntity(INDEXENT(0)), 4 * GetWaterLevel(), DMG_ACID); + } +#endif + + if (!(GetFlags() & FL_INWATER)) + { + // player enter water sound + if (GetWaterType() == CONTENTS_WATER) + { + EmitSound( "Player.Wade" ); + } + + AddFlag( FL_INWATER ); + } +} + + +// true if the player is attached to a ladder +bool CBasePlayer::IsOnLadder( void ) +{ + return (GetMoveType() == MOVETYPE_LADDER); +} + + +float CBasePlayer::GetWaterJumpTime() const +{ + return m_flWaterJumpTime; +} + +void CBasePlayer::SetWaterJumpTime( float flWaterJumpTime ) +{ + m_flWaterJumpTime = flWaterJumpTime; +} + +float CBasePlayer::GetSwimSoundTime( void ) const +{ + return m_flSwimSoundTime; +} + +void CBasePlayer::SetSwimSoundTime( float flSwimSoundTime ) +{ + m_flSwimSoundTime = flSwimSoundTime; +} + +void CBasePlayer::ShowViewPortPanel( const char * name, bool bShow, KeyValues *data ) +{ + CSingleUserRecipientFilter filter( this ); + filter.MakeReliable(); + + int count = 0; + KeyValues *subkey = NULL; + + if ( data ) + { + subkey = data->GetFirstSubKey(); + while ( subkey ) + { + count++; subkey = subkey->GetNextKey(); + } + + subkey = data->GetFirstSubKey(); // reset + } + + UserMessageBegin( filter, "VGUIMenu" ); + WRITE_STRING( name ); // menu name + WRITE_BYTE( bShow?1:0 ); + WRITE_BYTE( count ); + + // write additional data (be carefull not more than 192 bytes!) + while ( subkey ) + { + WRITE_STRING( subkey->GetName() ); + WRITE_STRING( subkey->GetString() ); + subkey = subkey->GetNextKey(); + } + MessageEnd(); +} + + +void CBasePlayer::PlayerDeathThink(void) +{ + float flForward; + + SetNextThink( gpGlobals->curtime + 0.1f ); + + if (GetFlags() & FL_ONGROUND) + { + flForward = GetAbsVelocity().Length() - 20; + if (flForward <= 0) + { + SetAbsVelocity( vec3_origin ); + } + else + { + Vector vecNewVelocity = GetAbsVelocity(); + VectorNormalize( vecNewVelocity ); + vecNewVelocity *= flForward; + SetAbsVelocity( vecNewVelocity ); + } + } + + if ( HasWeapons() ) + { + // we drop the guns here because weapons that have an area effect and can kill their user + // will sometimes crash coming back from CBasePlayer::Killed() if they kill their owner because the + // player class sometimes is freed. It's safer to manipulate the weapons once we know + // we aren't calling into any of their code anymore through the player pointer. + PackDeadPlayerItems(); + } + + if (GetModelIndex() && (!IsSequenceFinished()) && (m_lifeState == LIFE_DYING)) + { + StudioFrameAdvance( ); + + m_iRespawnFrames++; + if ( m_iRespawnFrames < 60 ) // animations should be no longer than this + return; + } + + if (m_lifeState == LIFE_DYING) + m_lifeState = LIFE_DEAD; + + StopAnimation(); + + AddEffects( EF_NOINTERP ); + m_flPlaybackRate = 0.0; + + int fAnyButtonDown = (m_nButtons & ~IN_SCORE); + + // Strip out the duck key from this check if it's toggled + if ( (fAnyButtonDown & IN_DUCK) && GetToggledDuckState()) + { + fAnyButtonDown &= ~IN_DUCK; + } + + // wait for all buttons released + if (m_lifeState == LIFE_DEAD) + { + if (fAnyButtonDown) + return; + + if ( g_pGameRules->FPlayerCanRespawn( this ) ) + { + m_lifeState = LIFE_RESPAWNABLE; + } + + return; + } + +// if the player has been dead for one second longer than allowed by forcerespawn, +// forcerespawn isn't on. Send the player off to an intermission camera until they +// choose to respawn. + if ( g_pGameRules->IsMultiplayer() && ( gpGlobals->curtime > (m_flDeathTime + DEATH_ANIMATION_TIME) ) && !IsObserver() ) + { + // go to dead camera. + StartObserverMode( m_iObserverLastMode ); + } + +// wait for any button down, or mp_forcerespawn is set and the respawn time is up + if (!fAnyButtonDown + && !( g_pGameRules->IsMultiplayer() && forcerespawn.GetInt() > 0 && (gpGlobals->curtime > (m_flDeathTime + 5))) ) + return; + + m_nButtons = 0; + m_iRespawnFrames = 0; + + //Msg( "Respawn\n"); + + respawn( this, !IsObserver() );// don't copy a corpse if we're in deathcam. + SetNextThink( TICK_NEVER_THINK ); +} + +/* + +//========================================================= +// StartDeathCam - find an intermission spot and send the +// player off into observer mode +//========================================================= +void CBasePlayer::StartDeathCam( void ) +{ + CBaseEntity *pSpot, *pNewSpot; + int iRand; + + if ( GetViewOffset() == vec3_origin ) + { + // don't accept subsequent attempts to StartDeathCam() + return; + } + + pSpot = gEntList.FindEntityByClassname( NULL, "info_intermission"); + + if ( pSpot ) + { + // at least one intermission spot in the world. + iRand = random->RandomInt( 0, 3 ); + + while ( iRand > 0 ) + { + pNewSpot = gEntList.FindEntityByClassname( pSpot, "info_intermission"); + + if ( pNewSpot ) + { + pSpot = pNewSpot; + } + + iRand--; + } + + CreateCorpse(); + StartObserverMode( pSpot->GetAbsOrigin(), pSpot->GetAbsAngles() ); + } + else + { + // no intermission spot. Push them up in the air, looking down at their corpse + trace_t tr; + + CreateCorpse(); + + UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() + Vector( 0, 0, 128 ), + MASK_PLAYERSOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr ); + QAngle angles; + VectorAngles( GetAbsOrigin() - tr.endpos, angles ); + StartObserverMode( tr.endpos, angles ); + return; + } +} */ + +void CBasePlayer::StopObserverMode() +{ + m_bForcedObserverMode = false; + m_afPhysicsFlags &= ~PFLAG_OBSERVER; + + if ( m_iObserverMode == OBS_MODE_NONE ) + return; + + if ( m_iObserverMode > OBS_MODE_DEATHCAM ) + { + m_iObserverLastMode = m_iObserverMode; + } + + m_iObserverMode.Set( OBS_MODE_NONE ); + + ShowViewPortPanel( "specmenu", false ); + ShowViewPortPanel( "specgui", false ); + ShowViewPortPanel( "overview", false ); +} + +bool CBasePlayer::StartObserverMode(int mode) +{ + if ( !IsObserver() ) + { + // set position to last view offset + SetAbsOrigin( GetAbsOrigin() + GetViewOffset() ); + SetViewOffset( vec3_origin ); + } + + Assert( mode > OBS_MODE_NONE ); + + m_afPhysicsFlags |= PFLAG_OBSERVER; + + // Holster weapon immediately, to allow it to cleanup + if ( GetActiveWeapon() ) + GetActiveWeapon()->Holster(); + + // clear out the suit message cache so we don't keep chattering + SetSuitUpdate(NULL, FALSE, 0); + + SetGroundEntity( (CBaseEntity *)NULL ); + + RemoveFlag( FL_DUCKING ); + + AddSolidFlags( FSOLID_NOT_SOLID ); + + SetObserverMode( mode ); + + ShowViewPortPanel( "specgui" ); + + // Setup flags + m_Local.m_iHideHUD = HIDEHUD_HEALTH; + m_takedamage = DAMAGE_NO; + + //Don't set the player to EF_NODRAW - the client can determine + //whether to draw the player or not with ShouldDraw + //AddEffects( EF_NODRAW ); + + m_iHealth = 1; + m_lifeState = LIFE_DEAD; // Can't be dead, otherwise movement doesn't work right. + pl.deadflag = true; + + return true; +} + +bool CBasePlayer::SetObserverMode(int mode ) +{ + if ( mode < OBS_MODE_NONE || mode > OBS_MODE_ROAMING ) + return false; + + + // check mp_forcecamera settings for dead players + if ( mode > OBS_MODE_FIXED && GetTeamNumber() > TEAM_SPECTATOR ) + { + switch ( mp_forcecamera.GetInt() ) + { + case OBS_ALLOW_ALL : break; // no restrictions + case OBS_ALLOW_TEAM : mode = OBS_MODE_IN_EYE; break; + case OBS_ALLOW_NONE : mode = OBS_MODE_FIXED; break; // donw't allow anything + } + } + + if ( m_iObserverMode > OBS_MODE_DEATHCAM ) + { + // remember mode if we were really spectating before + m_iObserverLastMode = m_iObserverMode; + } + + m_iObserverMode = mode; + + switch ( mode ) + { + case OBS_MODE_NONE: + case OBS_MODE_FIXED : + case OBS_MODE_DEATHCAM : + SetFOV( this, 0 ); // Reset FOV + SetViewOffset( vec3_origin ); + SetMoveType( MOVETYPE_NONE ); + break; + + case OBS_MODE_CHASE : + case OBS_MODE_IN_EYE : + // udpate FOV and viewmodels + SetObserverTarget( m_hObserverTarget ); + SetMoveType( MOVETYPE_OBSERVER ); + break; + + case OBS_MODE_ROAMING : + SetFOV( this, 0 ); // Reset FOV + SetObserverTarget( m_hObserverTarget ); + SetViewOffset( vec3_origin ); + SetMoveType( MOVETYPE_OBSERVER ); + break; + + } + + CheckObserverSettings(); + + return true; +} + +int CBasePlayer::GetObserverMode() +{ + return m_iObserverMode; +} + +void CBasePlayer::ForceObserverMode(int mode) +{ + int tempMode = OBS_MODE_ROAMING; + + if ( m_iObserverMode == mode ) + return; + + // don't change last mode if already in forced mode + + if ( m_bForcedObserverMode ) + { + tempMode = m_iObserverLastMode; + } + + SetObserverMode( mode ); + + if ( m_bForcedObserverMode ) + { + m_iObserverLastMode = tempMode; + } + + m_bForcedObserverMode = true; +} + +void CBasePlayer::CheckObserverSettings() +{ + // check if we are in forced mode and may go back to old mode + if ( m_bForcedObserverMode ) + { + CBaseEntity * target = m_hObserverTarget; + + if ( !IsValidObserverTarget(target) ) + { + // if old target is still invalid, try to find valid one + target = FindNextObserverTarget( false ); + } + + if ( target ) + { + // we found a valid target + m_bForcedObserverMode = false; // disable force mode + SetObserverMode( m_iObserverLastMode ); // switch to last mode + SetObserverTarget( target ); // goto target + + // TODO check for HUD icons + return; + } + else + { + // else stay in forced mode, no changes + return; + } + } + + // make sure our last mode is valid + if ( m_iObserverLastMode < OBS_MODE_FIXED ) + { + m_iObserverLastMode = OBS_MODE_ROAMING; + } + + // check if our spectating target is still a valid one + + if ( m_iObserverMode == OBS_MODE_IN_EYE || m_iObserverMode == OBS_MODE_CHASE ) + { + if ( !IsValidObserverTarget( m_hObserverTarget.Get() ) ) + { + // our target is not valid, try to find new target + CBaseEntity * target = FindNextObserverTarget( false ); + if ( target ) + { + // switch to new valid target + SetObserverTarget( target ); + } + else + { + // couldn't find new target, switch to temporary mode + if ( mp_forcecamera.GetInt() == OBS_ALLOW_ALL ) + { + // let player roam around + ForceObserverMode( OBS_MODE_ROAMING ); + } + else + { + // fix player view right where it is + ForceObserverMode( OBS_MODE_FIXED ); + m_hObserverTarget.Set( NULL ); // no traget to follow + } + } + } + + CBasePlayer *target = ToBasePlayer( m_hObserverTarget.Get() ); + + // for ineye mode we have to copy several data to see exactly the same + + if ( target && m_iObserverMode == OBS_MODE_IN_EYE ) + { + int flagMask = FL_ONGROUND | FL_DUCKING ; + + int flags = target->GetFlags() & flagMask; + + if ( (GetFlags() & flagMask) != flags ) + { + flags |= GetFlags() & (~flagMask); // keep other flags + ClearFlags(); + AddFlag( flags ); + } + + if ( target->GetViewOffset() != GetViewOffset() ) + { + SetViewOffset( target->GetViewOffset() ); + } + } + } +} + +bool CBasePlayer::StartReplayMode( float fDelay, float fDuration, int iEntity ) +{ + if ( ( sv_maxreplay == NULL ) || ( sv_maxreplay->GetFloat() <= 0 ) ) + return false; + + m_fDelay = fDelay; + m_fReplayEnd = gpGlobals->curtime + fDuration; + m_iReplayEntity = iEntity; + + return true; +} + +void CBasePlayer::StopReplayMode() +{ + m_fDelay = 0.0f; + m_fReplayEnd = -1; + m_iReplayEntity = 0; +} + +int CBasePlayer::GetDelayTicks() +{ + if ( m_fReplayEnd > gpGlobals->curtime ) + { + return TIME_TO_TICKS( m_fDelay ); + } + else + { + if ( m_fDelay > 0.0f ) + StopReplayMode(); + + return 0; + } +} + +int CBasePlayer::GetReplayEntity() +{ + return m_iReplayEntity; +} + +CBaseEntity * CBasePlayer::GetObserverTarget() +{ + return m_hObserverTarget.Get(); +} + +void CBasePlayer::ObserverUse( bool bIsPressed ) +{ +#ifndef _XBOX + if ( !HLTVDirector()->IsActive() ) + return; + + if ( GetTeamNumber() != TEAM_SPECTATOR ) + return; // only pure spectators can play cameraman + + if ( !bIsPressed ) + return; + + int iCameraManIndex = HLTVDirector()->GetCameraMan(); + + if ( iCameraManIndex == 0 ) + { + // turn camera on + HLTVDirector()->SetCameraMan( entindex() ); + } + else if ( iCameraManIndex == entindex() ) + { + // turn camera off + HLTVDirector()->SetCameraMan( 0 ); + } + else + { + ClientPrint( this, HUD_PRINTTALK, "Camera in use by other player." ); + } + + /* UTIL_SayText( "Spectator can not USE anything", this ); + + Vector dir,end; + Vector start = GetAbsOrigin(); + + AngleVectors( GetAbsAngles(), &dir ); + VectorNormalize( dir ); + + VectorMA( start, 32.0f, dir, end ); + + trace_t tr; + UTIL_TraceLine( start, end, MASK_PLAYERSOLID, this, COLLISION_GROUP_PLAYER_MOVEMENT, &tr ); + + if ( tr.fraction == 1.0f ) + return; // no obstacles in spectators way + + VectorMA( start, 128.0f, dir, end ); + + Ray_t ray; + ray.Init( end, start, VEC_DUCK_HULL_MIN, VEC_DUCK_HULL_MAX ); + + UTIL_TraceRay( ray, MASK_PLAYERSOLID, this, COLLISION_GROUP_PLAYER_MOVEMENT, &tr ); + + if ( tr.startsolid || tr.allsolid ) + return; + + SetAbsOrigin( tr.endpos ); */ +#endif +} + +void CBasePlayer::JumptoPosition(const Vector &origin, const QAngle &angles) +{ + SetAbsOrigin( origin ); + SetAbsVelocity( vec3_origin ); // stop movement + SetLocalAngles( angles ); + SnapEyeAngles( angles ); +} + +bool CBasePlayer::SetObserverTarget(CBaseEntity *target) +{ + if ( !IsValidObserverTarget( target ) ) + return false; + + // set new target + m_hObserverTarget.Set( target ); + + // reset fov to default + SetFOV( this, 0 ); + + if ( m_iObserverMode == OBS_MODE_ROAMING ) + { + Vector dir, end; + Vector start = target->EyePosition(); + + AngleVectors( target->EyeAngles(), &dir ); + VectorNormalize( dir ); + VectorMA( start, -64.0f, dir, end ); + + Ray_t ray; + ray.Init( start, end, VEC_DUCK_HULL_MIN , VEC_DUCK_HULL_MAX ); + + trace_t tr; + UTIL_TraceRay( ray, MASK_PLAYERSOLID, target, COLLISION_GROUP_PLAYER_MOVEMENT, &tr ); + + JumptoPosition( tr.endpos, target->EyeAngles() ); + } + + return true; +} + +bool CBasePlayer::IsValidObserverTarget(CBaseEntity * target) +{ + if ( target == NULL ) + return false; + + // MOD AUTHORS: Add checks on target here or in derived methode + + if ( !target->IsPlayer() ) // only track players + return false; + + CBasePlayer * player = ToBasePlayer( target ); + + /* Don't spec observers or players who haven't picked a class yet + if ( player->IsObserver() ) + return false; */ + + if( player == this ) + return false; // We can't observe ourselves. + + if ( player->IsEffectActive( EF_NODRAW ) ) // don't watch invisible players + return false; + + if ( player->m_lifeState == LIFE_RESPAWNABLE ) // target is dead, waiting for respawn + return false; + + if ( player->m_lifeState == LIFE_DEAD || player->m_lifeState == LIFE_DYING ) + { + if ( (player->m_flDeathTime + DEATH_ANIMATION_TIME ) < gpGlobals->curtime ) + { + return false; // allow watching until 3 seconds after death to see death animation + } + } + + // check forcecamera settings for active players + if ( GetTeamNumber() != TEAM_SPECTATOR ) + { + switch ( mp_forcecamera.GetInt() ) + { + case OBS_ALLOW_ALL : break; + case OBS_ALLOW_TEAM : if ( GetTeamNumber() != target->GetTeamNumber() ) + return false; + break; + case OBS_ALLOW_NONE : return false; + } + } + + return true; // passed all test +} + +int CBasePlayer::GetNextObserverSearchStartPoint( bool bReverse ) +{ + int iDir = bReverse ? -1 : 1; + + int startIndex; + + if ( m_hObserverTarget ) + { + // start using last followed player + startIndex = m_hObserverTarget->entindex(); + } + else + { + // start using own player index + startIndex = this->entindex(); + } + + startIndex += iDir; + if (startIndex > gpGlobals->maxClients) + startIndex = 1; + else if (startIndex < 1) + startIndex = gpGlobals->maxClients; + + return startIndex; +} + +CBaseEntity * CBasePlayer::FindNextObserverTarget(bool bReverse) +{ + // MOD AUTHORS: Modify the logic of this function if you want to restrict the observer to watching + // only a subset of the players. e.g. Make it check the target's team. + +/* if ( m_flNextFollowTime && m_flNextFollowTime > gpGlobals->time ) + { + return; + } + + m_flNextFollowTime = gpGlobals->time + 0.25; + */ // TODO move outside this function + + int startIndex = GetNextObserverSearchStartPoint( bReverse ); + + int currentIndex = startIndex; + int iDir = bReverse ? -1 : 1; + + do + { + CBaseEntity * nextTarget = UTIL_PlayerByIndex( currentIndex ); + + if ( IsValidObserverTarget( nextTarget ) ) + { + return nextTarget; // found next valid player + } + + currentIndex += iDir; + + // Loop through the clients + if (currentIndex > gpGlobals->maxClients) + currentIndex = 1; + else if (currentIndex < 1) + currentIndex = gpGlobals->maxClients; + + } while ( currentIndex != startIndex ); + + return NULL; +} + +//----------------------------------------------------------------------------- +// Purpose: Return true if this object can be +used by the player +//----------------------------------------------------------------------------- +bool CBasePlayer::IsUseableEntity( CBaseEntity *pEntity, unsigned int requiredCaps ) +{ + if ( pEntity ) + { + int caps = pEntity->ObjectCaps(); + if ( caps & (FCAP_IMPULSE_USE|FCAP_CONTINUOUS_USE|FCAP_ONOFF_USE|FCAP_DIRECTIONAL_USE) ) + { + if ( (caps & requiredCaps) == requiredCaps ) + { + return true; + } + } + } + + return false; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CBasePlayer::CanPickupObject( CBaseEntity *pObject, float massLimit, float sizeLimit ) +{ + // UNDONE: Make this virtual and move to HL2 player +#ifdef HL2_DLL + //Must be valid + if ( pObject == NULL ) + return false; + + //Must move with physics + if ( pObject->GetMoveType() != MOVETYPE_VPHYSICS ) + return false; + + IPhysicsObject *pList[VPHYSICS_MAX_OBJECT_LIST_COUNT]; + int count = pObject->VPhysicsGetObjectList( pList, ARRAYSIZE(pList) ); + + //Must have a physics object + if (!count) + return false; + + float objectMass = 0; + bool checkEnable = false; + for ( int i = 0; i < count; i++ ) + { + objectMass += pList[i]->GetMass(); + if ( !pList[i]->IsMoveable() ) + { + checkEnable = true; + } + if ( pList[i]->GetGameFlags() & FVPHYSICS_NO_PLAYER_PICKUP ) + return false; + if ( pList[i]->IsHinged() ) + return false; + } + + + //Msg( "Target mass: %f\n", pPhys->GetMass() ); + + //Must be under our threshold weight + if ( massLimit > 0 && objectMass > massLimit ) + return false; + + if ( checkEnable ) + { + // Allowing picking up of bouncebombs. + CBounceBomb *pBomb = dynamic_cast(pObject); + if( pBomb ) + return true; + + // Allow pickup of phys props that are motion enabled on player pickup + CPhysicsProp *pProp = dynamic_cast(pObject); + CPhysBox *pBox = dynamic_cast(pObject); + if ( !pProp && !pBox ) + return false; + + if ( pProp && !(pProp->HasSpawnFlags( SF_PHYSPROP_ENABLE_ON_PHYSCANNON )) ) + return false; + + if ( pBox && !(pBox->HasSpawnFlags( SF_PHYSBOX_ENABLE_ON_PHYSCANNON )) ) + return false; + } + + if ( sizeLimit > 0 ) + { + const Vector &size = pObject->CollisionProp()->OBBSize(); + if ( size.x > sizeLimit || size.y > sizeLimit || size.z > sizeLimit ) + return false; + } + + return true; +#else + return false; +#endif +} + +float CBasePlayer::GetHeldObjectMass( IPhysicsObject *pHeldObject ) +{ + return 0; +} + + +//----------------------------------------------------------------------------- +// Purpose: Server side of jumping rules. Most jumping logic is already +// handled in shared gamemovement code. Put stuff here that should +// only be done server side. +//----------------------------------------------------------------------------- +void CBasePlayer::Jump() +{ +} + +void CBasePlayer::Duck( ) +{ + if (m_nButtons & IN_DUCK) + { + if ( m_Activity != ACT_LEAP ) + { + SetAnimation( PLAYER_WALK ); + } + } +} + +// +// ID's player as such. +// +Class_T CBasePlayer::Classify ( void ) +{ + return CLASS_PLAYER; +} + + +void CBasePlayer::ResetFragCount() +{ + m_iFrags = 0; + pl.frags = m_iFrags; +} + +void CBasePlayer::IncrementFragCount( int nCount ) +{ + m_iFrags += nCount; + pl.frags = m_iFrags; +} + +void CBasePlayer::ResetDeathCount() +{ + m_iDeaths = 0; + pl.deaths = m_iDeaths; +} + +void CBasePlayer::IncrementDeathCount( int nCount ) +{ + m_iDeaths += nCount; + pl.deaths = m_iDeaths; +} + +void CBasePlayer::AddPoints( int score, bool bAllowNegativeScore ) +{ + // Positive score always adds + if ( score < 0 ) + { + if ( !bAllowNegativeScore ) + { + if ( m_iFrags < 0 ) // Can't go more negative + return; + + if ( -score > m_iFrags ) // Will this go negative? + { + score = -m_iFrags; // Sum will be 0 + } + } + } + + m_iFrags += score; + pl.frags = m_iFrags; +} + +void CBasePlayer::AddPointsToTeam( int score, bool bAllowNegativeScore ) +{ + if ( GetTeam() ) + { + GetTeam()->AddScore( score ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : int +//----------------------------------------------------------------------------- +int CBasePlayer::GetCommandContextCount( void ) const +{ + return m_CommandContext.Count(); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : index - +// Output : CCommandContext +//----------------------------------------------------------------------------- +CCommandContext *CBasePlayer::GetCommandContext( int index ) +{ + if ( index < 0 || index >= m_CommandContext.Count() ) + return NULL; + + return &m_CommandContext[ index ]; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CCommandContext *CBasePlayer::AllocCommandContext( void ) +{ + int idx = m_CommandContext.AddToTail(); + return &m_CommandContext[ idx ]; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : index - +//----------------------------------------------------------------------------- +void CBasePlayer::RemoveCommandContext( int index ) +{ + m_CommandContext.Remove( index ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBasePlayer::RemoveAllCommandContexts() +{ + m_CommandContext.RemoveAll(); +} + +//----------------------------------------------------------------------------- +// Purpose: Determine how much time we will be running this frame +// Output : float +//----------------------------------------------------------------------------- +int CBasePlayer::DetermineSimulationTicks( void ) +{ + int command_context_count = GetCommandContextCount(); + + int context_number; + + int simulation_ticks = 0; + + // Determine how much time we will be running this frame and fixup player clock as needed + for ( context_number = 0; context_number < command_context_count; context_number++ ) + { + CCommandContext const *ctx = GetCommandContext( context_number ); + Assert( ctx ); + Assert( ctx->numcmds > 0 ); + Assert( ctx->dropped_packets >= 0 ); + + // Determine how long it will take to run those packets + simulation_ticks += ctx->numcmds + ctx->dropped_packets; + } + + return simulation_ticks; +} + +// 2 ticks ahead or behind current clock means we need to fix clock on client +#define TARGET_CLOCK_CORRECTION_TICKS (TIME_TO_TICKS(0.06f)) + + +extern ConVar skip; + +//----------------------------------------------------------------------------- +// Purpose: Based upon amount of time in simulation time, adjust m_nTickBase so that +// we just end at the end of the current frame (so the player is basically on clock +// with the server) +// Input : simulation_ticks - +//----------------------------------------------------------------------------- +void CBasePlayer::AdjustPlayerTimeBase( int simulation_ticks ) +{ + Assert( simulation_ticks >= 0 ); + if ( simulation_ticks < 0 ) + return; + + // Start in the past so that we get to the sv.time that we'll hit at the end of the + // frame, just as we process the final command + + if ( gpGlobals->maxClients == 1 ) + { + // set TickBase so that player simulation tick matches gpGlobals->tickcount after + // all commands have been executed + m_nTickBase = gpGlobals->tickcount - simulation_ticks + 1; + } + else // multiplayer + { + // set the target tick 2 ticks ahead in the future. this way the client can + // alternate around this targettick without getting smaller than gpGlobals->tickcount + // after running the commands simulation time should never be smaller than the + // current gpGlobals->tickcount, otherwise the simulation time drops out of the + // clientside view interpolation buffer. + + int end_of_frame_ticks = gpGlobals->tickcount + TARGET_CLOCK_CORRECTION_TICKS; + + int estimated_end_tick = m_nTickBase + simulation_ticks; + + // If client gets ahead of this, we'll need to correct + int too_fast_limit = end_of_frame_ticks + TARGET_CLOCK_CORRECTION_TICKS; + // If client falls behind this, we'll also need to correct + int too_slow_limit = end_of_frame_ticks - TARGET_CLOCK_CORRECTION_TICKS; + + // See if we are too fast + if ( estimated_end_tick > too_fast_limit ) + { + // DevMsg( "client too fast by %i ticks\n", estimated_end_tick - end_of_frame_ticks ); + m_nTickBase = end_of_frame_ticks - simulation_ticks + 1; + } + // Or to slow + else if ( estimated_end_tick < too_slow_limit ) + { + // DevMsg( "client too slow by %i ticks\n", end_of_frame_ticks - estimated_end_tick ); + m_nTickBase = end_of_frame_ticks - simulation_ticks + 1; + } + } +} + +void CBasePlayer::RunNullCommand( void ) +{ + CUserCmd cmd; // NULL command + + // Store off the globals.. they're gonna get whacked + float flOldFrametime = gpGlobals->frametime; + float flOldCurtime = gpGlobals->curtime; + + pl.fixangle = FIXANGLE_NONE; + cmd.viewangles = EyeAngles(); + + float flTimeBase = gpGlobals->curtime; + SetTimeBase( flTimeBase ); + + MoveHelperServer()->SetHost( this ); + PlayerRunCommand( &cmd, MoveHelperServer() ); + + // save off the last good usercmd + SetLastUserCommand( cmd ); + + // Restore the globals.. + gpGlobals->frametime = flOldFrametime; + gpGlobals->curtime = flOldCurtime; + + MoveHelperServer()->SetHost( NULL ); +} + +//----------------------------------------------------------------------------- +// Purpose: Note, don't chain to BaseClass::PhysicsSimulate +//----------------------------------------------------------------------------- +void CBasePlayer::PhysicsSimulate( void ) +{ + VPROF_BUDGET( "CBasePlayer::PhysicsSimulate", VPROF_BUDGETGROUP_PLAYER ); + // If we've got a moveparent, we must simulate that first. + CBaseEntity *pMoveParent = GetMoveParent(); + if (pMoveParent) + { + pMoveParent->PhysicsSimulate(); + } + + // Make sure not to simulate this guy twice per frame + if (m_nSimulationTick == gpGlobals->tickcount ) + { + return; + } + + m_nSimulationTick = gpGlobals->tickcount; + + // See how much time has queued up for running + int simulation_ticks = DetermineSimulationTicks(); + + // If some time will elapse, make sure our clock (m_nTickBase) starts at the correct time + if ( simulation_ticks > 0 ) + { + AdjustPlayerTimeBase( simulation_ticks ); + } + + if ( IsHLTV() ) + { + // just run a single, empty command to makke sure + // all preThink/Postthink functions are called as usual + Assert ( GetCommandContextCount() == 0 ); + RunNullCommand(); + RemoveAllCommandContexts(); + return; + } + + // Store off true server timestamps + float savetime = gpGlobals->curtime; + float saveframetime = gpGlobals->frametime; + + int command_context_count = GetCommandContextCount(); + for ( int context_number = 0; context_number < command_context_count; context_number++ ) + { + // Get oldest ( newer are added to tail ) + CCommandContext *ctx = GetCommandContext( context_number ); + Assert( ctx ); + + int i; + int numbackup = ctx->totalcmds - ctx->numcmds; + + // If the server is paused, zero out motion,buttons,view changes + if ( ctx->paused ) + { + bool clear_angles = true; + + // If no clipping and cheats enabled and noclipduring game enabled, then leave + // forwardmove and angles stuff in usercmd + if ( GetMoveType() == MOVETYPE_NOCLIP && + sv_cheats->GetBool() && + sv_noclipduringpause.GetBool() ) + { + clear_angles = false; + } + + for ( i = 0; i < ctx->numcmds; i++ ) + { + ctx->cmds[ i ].buttons = 0; + if ( clear_angles ) + { + ctx->cmds[ i ].forwardmove = 0; + ctx->cmds[ i ].sidemove = 0; + ctx->cmds[ i ].upmove = 0; + VectorCopy ( pl.v_angle, ctx->cmds[ i ].viewangles ); + } + } + + ctx->dropped_packets = 0; + } + + MoveHelperServer()->SetHost( this ); + + // Suppress predicted events, etc. + if ( IsPredictingWeapons() ) + { + IPredictionSystem::SuppressHostEvents( this ); + } + + // If we haven't dropped too many packets, then run some commands + if ( ctx->dropped_packets < 24 ) + { + int droppedcmds = ctx->dropped_packets; + + if ( droppedcmds > numbackup ) + { + // Msg( "lost %i cmds\n", droppedcmds ); + } + + // run the last known cmd for each dropped cmd we don't have a backup for + while ( droppedcmds > numbackup ) + { + m_LastCmd.tick_count++; + + if ( ShouldRunCommandsInContext( ctx ) ) + { + PlayerRunCommand( &m_LastCmd, MoveHelperServer() ); + } + droppedcmds--; + } + + // Now run the "history" commands if we still have dropped packets + while ( droppedcmds > 0 ) + { + int cmdnum = ctx->numcmds + droppedcmds - 1; + if ( ShouldRunCommandsInContext( ctx ) ) + { + PlayerRunCommand( &ctx->cmds[cmdnum], MoveHelperServer() ); + } + droppedcmds--; + } + } + + // Now run any new command(s). Go backward because the most recent command is at index 0. + for ( i = ctx->numcmds - 1; i >= 0; i-- ) + { + if ( ShouldRunCommandsInContext( ctx ) ) + { + PlayerRunCommand( &ctx->cmds[ i ], MoveHelperServer() ); + } + } + + // Save off the last good command in case we drop > numbackup packets and need to rerun them + // we'll use this to "guess" at what was in the missing packets + m_LastCmd = ctx->cmds[ CMD_MOSTRECENT ]; + + // Update our vphysics object. + if ( m_pPhysicsController ) + { + VPROF( "CBasePlayer::PhysicsSimulate-UpdateVPhysicsPosition" ); + // If simulating at 2 * TICK_INTERVAL, add an extra TICK_INTERVAL to position arrival computation + int additionalTick = CBaseEntity::IsSimulatingOnAlternateTicks() ? 1 : 0; + + float flSecondsToArrival = ( ctx->numcmds + ctx->dropped_packets + additionalTick ) * TICK_INTERVAL; + UpdateVPhysicsPosition( m_vNewVPhysicsPosition, m_vNewVPhysicsVelocity, flSecondsToArrival ); + } + + // Always reset after running commands + IPredictionSystem::SuppressHostEvents( NULL ); + + MoveHelperServer()->SetHost( NULL ); + } + + // Clear all contexts + RemoveAllCommandContexts(); + + // Restore the true server clock + // FIXME: Should this occur after simulation of children so + // that they are in the timespace of the player? + gpGlobals->curtime = savetime; + gpGlobals->frametime = saveframetime; +} + +unsigned int CBasePlayer::PhysicsSolidMaskForEntity() const +{ + return MASK_PLAYERSOLID; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *buf - +// totalcmds - +// dropped_packets - +// ignore - +// paused - +// Output : float -- Time in seconds of last movement command +//----------------------------------------------------------------------------- +void CBasePlayer::ProcessUsercmds( CUserCmd *cmds, int numcmds, int totalcmds, + int dropped_packets, bool paused ) +{ + CCommandContext *ctx = AllocCommandContext(); + Assert( ctx ); + + int i; + for ( i = totalcmds - 1; i >= 0; i-- ) + { + ctx->cmds[ i ] = cmds[ i ]; + } + ctx->numcmds = numcmds; + ctx->totalcmds = totalcmds, + ctx->dropped_packets = dropped_packets; + ctx->paused = paused; + + // Set global pause state for this player + m_bGamePaused = paused; + + if ( paused ) + { + m_nSimulationTick = -1; + // Just run the commands right away if paused + PhysicsSimulate(); + } +} + +// Duck debouncing code to stop menu changes from disallowing crouch/uncrouch +ConVar xc_crouch_debounce( "xc_crouch_debounce", "0", FCVAR_NONE ); + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *ucmd - +// *moveHelper - +//----------------------------------------------------------------------------- +void CBasePlayer::PlayerRunCommand(CUserCmd *ucmd, IMoveHelper *moveHelper) +{ + m_touchedPhysObject = false; + + if ( pl.fixangle == FIXANGLE_NONE) + { + VectorCopy ( ucmd->viewangles, pl.v_angle ); + } + + // Handle FL_FROZEN. + // Prevent player moving for some seconds after New Game, so that they pick up everything + if( GetFlags() & FL_FROZEN || + (developer.GetInt() == 0 && gpGlobals->eLoadType == MapLoad_NewGame && gpGlobals->curtime < 3.0 ) ) + { + ucmd->forwardmove = 0; + ucmd->sidemove = 0; + ucmd->upmove = 0; + ucmd->buttons = 0; + ucmd->impulse = 0; + VectorCopy ( pl.v_angle, ucmd->viewangles ); + } +#ifdef _XBOX + else + { + // Force a duck if we're toggled + if ( GetToggledDuckState() ) + { + // If this is set, we've altered our menu options and need to debounce the duck + if ( xc_crouch_debounce.GetBool() ) + { + ToggleDuck(); + + // Mark it as handled + xc_crouch_debounce.SetValue( 0 ); + } + else + { + ucmd->buttons |= IN_DUCK; + } + } + } +#endif // _XBOX + + PlayerMove()->RunCommand(this, ucmd, moveHelper); +} + + +void CBasePlayer::HandleFuncTrain(void) +{ + if ( m_afPhysicsFlags & PFLAG_DIROVERRIDE ) + AddFlag( FL_ONTRAIN ); + else + RemoveFlag( FL_ONTRAIN ); + + // Train speed control + if (( m_afPhysicsFlags & PFLAG_DIROVERRIDE ) == 0) + { + if (m_iTrain & TRAIN_ACTIVE) + { + m_iTrain = TRAIN_NEW; // turn off train + } + return; + } + + CBaseEntity *pTrain = GetGroundEntity(); + float vel; + + if ( pTrain ) + { + if ( !(pTrain->ObjectCaps() & FCAP_DIRECTIONAL_USE) ) + pTrain = NULL; + } + + if ( !pTrain ) + { + if ( GetActiveWeapon()->ObjectCaps() & FCAP_DIRECTIONAL_USE ) + { + m_iTrain = TRAIN_ACTIVE | TRAIN_NEW; + + if ( m_nButtons & IN_FORWARD ) + { + m_iTrain |= TRAIN_FAST; + } + else if ( m_nButtons & IN_BACK ) + { + m_iTrain |= TRAIN_BACK; + } + else + { + m_iTrain |= TRAIN_NEUTRAL; + } + return; + } + else + { + trace_t trainTrace; + // Maybe this is on the other side of a level transition + UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() + Vector(0,0,-38), + MASK_PLAYERSOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &trainTrace ); + + if ( trainTrace.fraction != 1.0 && trainTrace.m_pEnt ) + pTrain = trainTrace.m_pEnt; + + + if ( !pTrain || !(pTrain->ObjectCaps() & FCAP_DIRECTIONAL_USE) || !pTrain->OnControls(this) ) + { + m_afPhysicsFlags &= ~PFLAG_DIROVERRIDE; + m_iTrain = TRAIN_NEW|TRAIN_OFF; + return; + } + } + } + else if ( !( GetFlags() & FL_ONGROUND ) || pTrain->HasSpawnFlags( SF_TRACKTRAIN_NOCONTROL ) || (m_nButtons & (IN_MOVELEFT|IN_MOVERIGHT) ) ) + { + // Turn off the train if you jump, strafe, or the train controls go dead + m_afPhysicsFlags &= ~PFLAG_DIROVERRIDE; + m_iTrain = TRAIN_NEW|TRAIN_OFF; + return; + } + + SetAbsVelocity( vec3_origin ); + vel = 0; + if ( m_afButtonPressed & IN_FORWARD ) + { + vel = 1; + pTrain->Use( this, this, USE_SET, (float)vel ); + } + else if ( m_afButtonPressed & IN_BACK ) + { + vel = -1; + pTrain->Use( this, this, USE_SET, (float)vel ); + } + + if (vel) + { + m_iTrain = TrainSpeed(static_cast(pTrain->m_flSpeed), static_cast(((CFuncTrackTrain*)pTrain)->GetMaxSpeed())); + m_iTrain |= TRAIN_ACTIVE|TRAIN_NEW; + } +} + + +void CBasePlayer::PreThink(void) +{ + if ( g_fGameOver || m_iPlayerLocked ) + return; // intermission or finale + + ItemPreFrame( ); + WaterMove(); + + if ( g_pGameRules && g_pGameRules->FAllowFlashlight() ) + m_Local.m_iHideHUD &= ~HIDEHUD_FLASHLIGHT; + else + m_Local.m_iHideHUD |= HIDEHUD_FLASHLIGHT; + + // checks if new client data (for HUD and view control) needs to be sent to the client + UpdateClientData(); + + CheckTimeBasedDamage(); + + CheckSuitUpdate(); + + if ( GetObserverMode() > OBS_MODE_FIXED ) + { + CheckObserverSettings(); // do this each frame + } + + if (m_lifeState >= LIFE_DYING) + return; + + HandleFuncTrain(); + + if (m_nButtons & IN_JUMP) + { + // If on a ladder, jump off the ladder + // else Jump + Jump(); + } + + // If trying to duck, already ducked, or in the process of ducking + if ((m_nButtons & IN_DUCK) || (GetFlags() & FL_DUCKING) || (m_afPhysicsFlags & PFLAG_DUCKING) ) + Duck(); + + // + // If we're not on the ground, we're falling. Update our falling velocity. + // + if ( !( GetFlags() & FL_ONGROUND ) ) + { + m_Local.m_flFallVelocity = -GetAbsVelocity().z; + } + +#ifndef _XBOX + CNavArea *area = TheNavMesh->GetNavArea( GetAbsOrigin() ); + if (area && area != m_lastNavArea) + { + // player entered a new nav area + if (m_lastNavArea) + { + m_lastNavArea->DecrementPlayerCount( GetTeamNumber() ); + } + + area->IncrementPlayerCount( GetTeamNumber() ); + + m_lastNavArea = area; + if ( area->GetPlace() != UNDEFINED_PLACE ) + { + const char *placeName = TheNavMesh->PlaceToName( area->GetPlace() ); + if ( placeName && *placeName ) + { + Q_strncpy( m_szLastPlaceName.GetForModify(), placeName, MAX_PLACE_NAME_LENGTH ); + } + } + + // generate event + //KeyValues *event = new KeyValues( "player_entered_area" ); + //event->SetInt( "userid", GetUserID() ); + //event->SetInt( "areaid", area->GetID() ); + //gameeventmanager->FireEvent( event ); + } +#endif + + // StudioFrameAdvance( );//!!!HACKHACK!!! Can't be hit by traceline when not animating? +} + + +/* Time based Damage works as follows: + 1) There are several types of timebased damage: + + #define DMG_PARALYZE (1 << 14) // slows affected creature down + #define DMG_NERVEGAS (1 << 15) // nerve toxins, very bad + #define DMG_POISON (1 << 16) // blood poisioning + #define DMG_RADIATION (1 << 17) // radiation exposure + #define DMG_DROWNRECOVER (1 << 18) // drown recovery + #define DMG_ACID (1 << 19) // toxic chemicals or acid burns + #define DMG_SLOWBURN (1 << 20) // in an oven + + 2) A new hit inflicting tbd restarts the tbd counter - each NPC has an 8bit counter, + per damage type. The counter is decremented every second, so the maximum time + an effect will last is 255/60 = 4.25 minutes. Of course, staying within the radius + of a damaging effect like fire, nervegas, radiation will continually reset the counter to max. + + 3) Every second that a tbd counter is running, the player takes damage. The damage + is determined by the type of tdb. + Paralyze - 1/2 movement rate, 30 second duration. + Nervegas - 5 points per second, 16 second duration = 80 points max dose. + Poison - 2 points per second, 25 second duration = 50 points max dose. + Radiation - 1 point per second, 50 second duration = 50 points max dose. + Drown - 5 points per second, 2 second duration. + Acid/Chemical - 5 points per second, 10 second duration = 50 points max. + Burn - 10 points per second, 2 second duration. + Freeze - 3 points per second, 10 second duration = 30 points max. + + 4) Certain actions or countermeasures counteract the damaging effects of tbds: + + Armor/Heater/Cooler - Chemical(acid),burn, freeze all do damage to armor power, then to body + - recharged by suit recharger + Air In Lungs - drowning damage is done to air in lungs first, then to body + - recharged by poking head out of water + - 10 seconds if swiming fast + Air In SCUBA - drowning damage is done to air in tanks first, then to body + - 2 minutes in tanks. Need new tank once empty. + Radiation Syringe - Each syringe full provides protection vs one radiation dosage + Antitoxin Syringe - Each syringe full provides protection vs one poisoning (nervegas or poison). + Health kit - Immediate stop to acid/chemical, fire or freeze damage. + Radiation Shower - Immediate stop to radiation damage, acid/chemical or fire damage. + + +*/ + +// If player is taking time based damage, continue doing damage to player - +// this simulates the effect of being poisoned, gassed, dosed with radiation etc - +// anything that continues to do damage even after the initial contact stops. +// Update all time based damage counters, and shut off any that are done. + +// The m_bitsDamageType bit MUST be set if any damage is to be taken. +// This routine will detect the initial on value of the m_bitsDamageType +// and init the appropriate counter. Only processes damage every second. + +//#define PARALYZE_DURATION 30 // number of 2 second intervals to take damage +//#define PARALYZE_DAMAGE 0.0 // damage to take each 2 second interval + +//#define NERVEGAS_DURATION 16 +//#define NERVEGAS_DAMAGE 5.0 + +//#define POISON_DURATION 25 +//#define POISON_DAMAGE 2.0 + +//#define RADIATION_DURATION 50 +//#define RADIATION_DAMAGE 1.0 + +//#define ACID_DURATION 10 +//#define ACID_DAMAGE 5.0 + +//#define SLOWBURN_DURATION 2 +//#define SLOWBURN_DAMAGE 1.0 + +//#define SLOWFREEZE_DURATION 1.0 +//#define SLOWFREEZE_DAMAGE 3.0 + +/* */ + + +void CBasePlayer::CheckTimeBasedDamage() +{ + int i; + byte bDuration = 0; + + if (!(m_bitsDamageType & DMG_TIMEBASED)) + return; + + // only check for time based damage approx. every 2 seconds + if (abs(static_cast(gpGlobals->curtime - m_tbdPrev) < 2.0)) + return; + + m_tbdPrev = gpGlobals->curtime; + + for (i = 0; i < CDMG_TIMEBASED; i++) + { + // make sure bit is set for damage type + if (m_bitsDamageType & (DMG_PARALYZE << i)) + { + switch (i) + { + case itbd_Paralyze: + // UNDONE - flag movement as half-speed + bDuration = PARALYZE_DURATION; + break; + case itbd_NerveGas: +// OnTakeDamage(pev, pev, NERVEGAS_DAMAGE, DMG_GENERIC); + bDuration = NERVEGAS_DURATION; + break; +// case itbd_Poison: +// OnTakeDamage( CTakeDamageInfo( this, this, POISON_DAMAGE, DMG_GENERIC ) ); +// bDuration = POISON_DURATION; +// break; + case itbd_Radiation: +// OnTakeDamage(pev, pev, RADIATION_DAMAGE, DMG_GENERIC); + bDuration = RADIATION_DURATION; + break; + case itbd_DrownRecover: + // NOTE: this hack is actually used to RESTORE health + // after the player has been drowning and finally takes a breath + if (m_idrowndmg > m_idrownrestored) + { + int idif = min(m_idrowndmg - m_idrownrestored, 10); + + TakeHealth(idif, DMG_GENERIC); + m_idrownrestored += idif; + } + bDuration = 4; // get up to 5*10 = 50 points back + break; + + case itbd_PoisonRecover: + { + // NOTE: this hack is actually used to RESTORE health + // after the player has been poisoned. + if (m_nPoisonDmg > m_nPoisonRestored) + { + int nDif = min(m_nPoisonDmg - m_nPoisonRestored, 10); + TakeHealth(nDif, DMG_GENERIC); + m_nPoisonRestored += nDif; + } + bDuration = 9; // get up to 10*10 = 100 points back + break; + } + + case itbd_Acid: +// OnTakeDamage(pev, pev, ACID_DAMAGE, DMG_GENERIC); + bDuration = ACID_DURATION; + break; + case itbd_SlowBurn: +// OnTakeDamage(pev, pev, SLOWBURN_DAMAGE, DMG_GENERIC); + bDuration = SLOWBURN_DURATION; + break; + case itbd_SlowFreeze: +// OnTakeDamage(pev, pev, SLOWFREEZE_DAMAGE, DMG_GENERIC); + bDuration = SLOWFREEZE_DURATION; + break; + default: + bDuration = 0; + } + + if (m_rgbTimeBasedDamage[i]) + { + // decrement damage duration, detect when done. + if (!m_rgbTimeBasedDamage[i] || --m_rgbTimeBasedDamage[i] == 0) + { + m_rgbTimeBasedDamage[i] = 0; + // if we're done, clear damage bits + m_bitsDamageType &= ~(DMG_PARALYZE << i); + } + } + else + // first time taking this damage type - init damage duration + m_rgbTimeBasedDamage[i] = bDuration; + } + } +} + +/* +THE POWER SUIT + +The Suit provides 3 main functions: Protection, Notification and Augmentation. +Some functions are automatic, some require power. +The player gets the suit shortly after getting off the train in C1A0 and it stays +with him for the entire game. + +Protection + + Heat/Cold + When the player enters a hot/cold area, the heating/cooling indicator on the suit + will come on and the battery will drain while the player stays in the area. + After the battery is dead, the player starts to take damage. + This feature is built into the suit and is automatically engaged. + Radiation Syringe + This will cause the player to be immune from the effects of radiation for N seconds. Single use item. + Anti-Toxin Syringe + This will cure the player from being poisoned. Single use item. + Health + Small (1st aid kits, food, etc.) + Large (boxes on walls) + Armor + The armor works using energy to create a protective field that deflects a + percentage of damage projectile and explosive attacks. After the armor has been deployed, + it will attempt to recharge itself to full capacity with the energy reserves from the battery. + It takes the armor N seconds to fully charge. + +Notification (via the HUD) + +x Health +x Ammo +x Automatic Health Care + Notifies the player when automatic healing has been engaged. +x Geiger counter + Classic Geiger counter sound and status bar at top of HUD + alerts player to dangerous levels of radiation. This is not visible when radiation levels are normal. +x Poison + Armor + Displays the current level of armor. + +Augmentation + + Reanimation (w/adrenaline) + Causes the player to come back to life after he has been dead for 3 seconds. + Will not work if player was gibbed. Single use. + Long Jump + Used by hitting the ??? key(s). Caused the player to further than normal. + SCUBA + Used automatically after picked up and after player enters the water. + Works for N seconds. Single use. + +Things powered by the battery + + Armor + Uses N watts for every M units of damage. + Heat/Cool + Uses N watts for every second in hot/cold area. + Long Jump + Uses N watts for every jump. + Alien Cloak + Uses N watts for each use. Each use lasts M seconds. + Alien Shield + Augments armor. Reduces Armor drain by one half + +*/ + +// if in range of radiation source, ping geiger counter + +#define GEIGERDELAY 0.25 + +void CBasePlayer::UpdateGeigerCounter( void ) +{ + byte range; + + // delay per update ie: don't flood net with these msgs + if (gpGlobals->curtime < m_flgeigerDelay) + return; + + m_flgeigerDelay = gpGlobals->curtime + GEIGERDELAY; + + // send range to radition source to client + range = (byte) clamp(m_flgeigerRange / 4, 0, 255); + + // This is to make sure you aren't driven crazy by geiger while in the airboat + if ( IsInAVehicle() ) + { + range = clamp( (int)range * 4, 0, 255 ); + } + + if (range != m_igeigerRangePrev) + { + m_igeigerRangePrev = range; + + CSingleUserRecipientFilter user( this ); + user.MakeReliable(); + UserMessageBegin( user, "Geiger" ); + WRITE_BYTE( range ); + MessageEnd(); + } + + // reset counter and semaphore + if (!random->RandomInt(0,3)) + { + m_flgeigerRange = 1000; + } +} + +/* +================ +CheckSuitUpdate + +Play suit update if it's time +================ +*/ + +#define SUITUPDATETIME 3.5 +#define SUITFIRSTUPDATETIME 0.1 + +void CBasePlayer::CheckSuitUpdate() +{ + int i; + int isentence = 0; + int isearch = m_iSuitPlayNext; + + // Ignore suit updates if no suit + if ( !IsSuitEquipped() ) + return; + + // if in range of radiation source, ping geiger counter + UpdateGeigerCounter(); + + if ( g_pGameRules->IsMultiplayer() ) + { + // don't bother updating HEV voice in multiplayer. + return; + } + + if ( gpGlobals->curtime >= m_flSuitUpdate && m_flSuitUpdate > 0) + { + // play a sentence off of the end of the queue + for (i = 0; i < CSUITPLAYLIST; i++) + { + if ((isentence = m_rgSuitPlayList[isearch]) != 0) + break; + + if (++isearch == CSUITPLAYLIST) + isearch = 0; + } + + if (isentence) + { + m_rgSuitPlayList[isearch] = 0; + if (isentence > 0) + { + // play sentence number + + char sentence[512]; + Q_snprintf( sentence, sizeof( sentence ), "!%s", engine->SentenceNameFromIndex( isentence ) ); + UTIL_EmitSoundSuit( edict(), sentence ); + } + else + { + // play sentence group + UTIL_EmitGroupIDSuit(edict(), -isentence); + } + m_flSuitUpdate = gpGlobals->curtime + SUITUPDATETIME; + } + else + // queue is empty, don't check + m_flSuitUpdate = 0; + } +} + +// add sentence to suit playlist queue. if fgroup is true, then +// name is a sentence group (HEV_AA), otherwise name is a specific +// sentence name ie: !HEV_AA0. If iNoRepeat is specified in +// seconds, then we won't repeat playback of this word or sentence +// for at least that number of seconds. + +void CBasePlayer::SetSuitUpdate(char *name, int fgroup, int iNoRepeatTime) +{ + int i; + int isentence; + int iempty = -1; + + + // Ignore suit updates if no suit + if ( !IsSuitEquipped() ) + return; + + if ( g_pGameRules->IsMultiplayer() ) + { + // due to static channel design, etc. We don't play HEV sounds in multiplayer right now. + return; + } + + // if name == NULL, then clear out the queue + + if (!name) + { + for (i = 0; i < CSUITPLAYLIST; i++) + m_rgSuitPlayList[i] = 0; + return; + } + // get sentence or group number + if (!fgroup) + { + isentence = SENTENCEG_Lookup(name); // Lookup sentence index (not group) by name + if (isentence < 0) + return; + } + else + // mark group number as negative + isentence = -SENTENCEG_GetIndex(name); // Lookup group index by name + + // check norepeat list - this list lets us cancel + // the playback of words or sentences that have already + // been played within a certain time. + + for (i = 0; i < CSUITNOREPEAT; i++) + { + if (isentence == m_rgiSuitNoRepeat[i]) + { + // this sentence or group is already in + // the norepeat list + + if (m_rgflSuitNoRepeatTime[i] < gpGlobals->curtime) + { + // norepeat time has expired, clear it out + m_rgiSuitNoRepeat[i] = 0; + m_rgflSuitNoRepeatTime[i] = 0.0; + iempty = i; + break; + } + else + { + // don't play, still marked as norepeat + return; + } + } + // keep track of empty slot + if (!m_rgiSuitNoRepeat[i]) + iempty = i; + } + + // sentence is not in norepeat list, save if norepeat time was given + + if (iNoRepeatTime) + { + if (iempty < 0) + iempty = random->RandomInt(0, CSUITNOREPEAT-1); // pick random slot to take over + m_rgiSuitNoRepeat[iempty] = isentence; + m_rgflSuitNoRepeatTime[iempty] = iNoRepeatTime + gpGlobals->curtime; + } + + // find empty spot in queue, or overwrite last spot + + m_rgSuitPlayList[m_iSuitPlayNext++] = isentence; + if (m_iSuitPlayNext == CSUITPLAYLIST) + m_iSuitPlayNext = 0; + + if (m_flSuitUpdate <= gpGlobals->curtime) + { + if (m_flSuitUpdate == 0) + // play queue is empty, don't delay too long before playback + m_flSuitUpdate = gpGlobals->curtime + SUITFIRSTUPDATETIME; + else + m_flSuitUpdate = gpGlobals->curtime + SUITUPDATETIME; + } + +} + +//========================================================= +// UpdatePlayerSound - updates the position of the player's +// reserved sound slot in the sound list. +//========================================================= +void CBasePlayer::UpdatePlayerSound ( void ) +{ + int iBodyVolume; + int iVolume; + CSound *pSound; + + pSound = CSoundEnt::SoundPointerForIndex( CSoundEnt::ClientSoundIndex( edict() ) ); + + if ( !pSound ) + { + Msg( "Client lost reserved sound!\n" ); + return; + } + + if (GetFlags() & FL_NOTARGET) + { + pSound->m_iVolume = 0; + return; + } + + // now figure out how loud the player's movement is. + if ( GetFlags() & FL_ONGROUND ) + { + iBodyVolume = static_cast(GetAbsVelocity().Length()); + + // clamp the noise that can be made by the body, in case a push trigger, + // weapon recoil, or anything shoves the player abnormally fast. + // NOTE: 512 units is a pretty large radius for a sound made by the player's body. + // then again, I think some materials are pretty loud. + if ( iBodyVolume > 512 ) + { + iBodyVolume = 512; + } + } + else + { + iBodyVolume = 0; + } + + if ( m_nButtons & IN_JUMP ) + { + // Jumping is a little louder. + iBodyVolume += 100; + } + + m_iTargetVolume = iBodyVolume; + + // if target volume is greater than the player sound's current volume, we paste the new volume in + // immediately. If target is less than the current volume, current volume is not set immediately to the + // lower volume, rather works itself towards target volume over time. This gives NPCs a much better chance + // to hear a sound, especially if they don't listen every frame. + iVolume = pSound->Volume(); + + if ( m_iTargetVolume > iVolume ) + { + iVolume = m_iTargetVolume; + } + else if ( iVolume > m_iTargetVolume ) + { + iVolume -= static_cast(250 * gpGlobals->frametime); + + if ( iVolume < m_iTargetVolume ) + { + iVolume = 0; + } + } + + if ( pSound ) + { + pSound->SetSoundOrigin( GetAbsOrigin() ); + pSound->m_iType = SOUND_PLAYER; + pSound->m_iVolume = iVolume; + } + + // Below are a couple of useful little bits that make it easier to visualize just how much noise the + // player is making. + //Vector forward = UTIL_YawToVector( pl.v_angle.y ); + //UTIL_Sparks( GetAbsOrigin() + forward * iVolume ); + //Msg( "%d/%d\n", iVolume, m_iTargetVolume ); +} + +// This is a glorious hack to find free space when you've crouched into some solid space +// Our crouching collisions do not work correctly for some reason and this is easier +// than fixing the problem :( +void FixPlayerCrouchStuck( CBasePlayer *pPlayer ) +{ + trace_t trace; + + // Move up as many as 18 pixels if the player is stuck. + for ( int i = 0; i < 18; i++ ) + { + UTIL_TraceHull( pPlayer->GetAbsOrigin(), pPlayer->GetAbsOrigin(), + VEC_DUCK_HULL_MIN, VEC_DUCK_HULL_MAX, MASK_PLAYERSOLID, pPlayer, COLLISION_GROUP_NONE, &trace ); + if ( trace.startsolid ) + { + Vector origin = pPlayer->GetAbsOrigin(); + origin.z += 1.0f; + pPlayer->SetLocalOrigin( origin ); + } + else + break; + } +} + +#define SMOOTHING_FACTOR 0.9 +extern CMoveData *g_pMoveData; + +// UNDONE: Look and see if the ground entity is in hierarchy with a MOVETYPE_VPHYSICS? +// Behavior in that case is not as good currently when the parent is rideable +bool CBasePlayer::IsRideablePhysics( IPhysicsObject *pPhysics ) +{ + if ( pPhysics ) + { + if ( pPhysics->GetMass() > (VPhysicsGetObject()->GetMass()*2) ) + return true; + } + + return false; +} + +IPhysicsObject *CBasePlayer::GetGroundVPhysics() +{ + CBaseEntity *pGroundEntity = GetGroundEntity(); + if ( pGroundEntity && pGroundEntity->GetMoveType() == MOVETYPE_VPHYSICS ) + { + IPhysicsObject *pPhysGround = pGroundEntity->VPhysicsGetObject(); + if ( pPhysGround && pPhysGround->IsMoveable() ) + return pPhysGround; + } + return NULL; +} + + +//----------------------------------------------------------------------------- +// For debugging... +//----------------------------------------------------------------------------- +void CBasePlayer::ForceOrigin( const Vector &vecOrigin ) +{ + m_bForceOrigin = true; + m_vForcedOrigin = vecOrigin; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBasePlayer::PostThink() +{ + m_vecSmoothedVelocity = m_vecSmoothedVelocity * SMOOTHING_FACTOR + GetAbsVelocity() * ( 1 - SMOOTHING_FACTOR ); + + if ( !g_fGameOver && !m_iPlayerLocked && IsAlive() ) + { + // set correct collision bounds (may have changed in player movement code) + VPROF_SCOPE_BEGIN( "CBasePlayer::PostThink-Bounds" ); + if ( GetFlags() & FL_DUCKING ) + { + SetCollisionBounds( VEC_DUCK_HULL_MIN, VEC_DUCK_HULL_MAX ); + } + else + { + SetCollisionBounds( VEC_HULL_MIN, VEC_HULL_MAX ); + } + VPROF_SCOPE_END(); + + VPROF_SCOPE_BEGIN( "CBasePlayer::PostThink-Use" ); + // Handle controlling an entity + if ( m_hUseEntity != NULL ) + { + // if they've moved too far from the gun, or deployed another weapon, unuse the gun + if ( m_hUseEntity->OnControls( this ) && + ( !GetActiveWeapon() || GetActiveWeapon()->IsEffectActive( EF_NODRAW ) || + ( GetActiveWeapon()->GetActivity() == ACT_VM_HOLSTER ) + ) ) + { + m_hUseEntity->Use( this, this, USE_SET, 2 ); // try fire the gun + } + else + { + // they've moved off the controls + ClearUseEntity(); + } + } + VPROF_SCOPE_END(); + + // do weapon stuff + VPROF_SCOPE_BEGIN( "CBasePlayer::PostThink-ItemPostFrame" ); + ItemPostFrame(); + VPROF_SCOPE_END(); + + if ( GetFlags() & FL_ONGROUND ) + { + if (m_Local.m_flFallVelocity > 64 && !g_pGameRules->IsMultiplayer()) + { + CSoundEnt::InsertSound ( SOUND_PLAYER, GetAbsOrigin(), m_Local.m_flFallVelocity, 0.2, this ); + // Msg( "fall %f\n", m_Local.m_flFallVelocity ); + } + m_Local.m_flFallVelocity = 0; + } + + // select the proper animation for the player character + if ( IsAlive() ) + { + VPROF( "CBasePlayer::PostThink-Animation" ); + // If he's in a vehicle, sit down + if ( IsInAVehicle() ) + SetAnimation( PLAYER_IN_VEHICLE ); + else if (!GetAbsVelocity().x && !GetAbsVelocity().y) + SetAnimation( PLAYER_IDLE ); + else if ((GetAbsVelocity().x || GetAbsVelocity().y) && ( GetFlags() & FL_ONGROUND )) + SetAnimation( PLAYER_WALK ); + else if (GetWaterLevel() > 1) + SetAnimation( PLAYER_WALK ); + } + + // Don't allow bogus sequence on player + if ( GetSequence() == -1 ) + { + SetSequence( 0 ); + } + + VPROF_SCOPE_BEGIN( "CBasePlayer::PostThink-StudioFrameAdvance" ); + StudioFrameAdvance(); + VPROF_SCOPE_END(); + + VPROF_SCOPE_BEGIN( "CBasePlayer::PostThink-DispatchAnimEvents" ); + DispatchAnimEvents( this ); + VPROF_SCOPE_END(); + + SetSimulationTime( gpGlobals->curtime ); + + //Let the weapon update as well + VPROF_SCOPE_BEGIN( "CBasePlayer::PostThink-Weapon_FrameUpdate" ); + Weapon_FrameUpdate(); + VPROF_SCOPE_END(); + + VPROF_SCOPE_BEGIN( "CBasePlayer::PostThink-UpdatePlayerSound" ); + UpdatePlayerSound(); + VPROF_SCOPE_END(); + + if ( m_bForceOrigin ) + { + SetLocalOrigin( m_vForcedOrigin ); + SetLocalAngles( m_Local.m_vecPunchAngle ); + m_Local.m_vecPunchAngle = RandomAngle( -25, 25 ); + m_Local.m_vecPunchAngleVel.Init(); + } + + VPROF_SCOPE_BEGIN( "CBasePlayer::PostThink-PostThinkVPhysics" ); + PostThinkVPhysics(); + VPROF_SCOPE_END(); + } + +#if !defined( NO_ENTITY_PREDICTION ) + // Even if dead simulate entities + SimulatePlayerSimulatedEntities(); +#endif + +} + +// handles touching physics objects +void CBasePlayer::Touch( CBaseEntity *pOther ) +{ + if ( pOther == GetGroundEntity() ) + return; + + if ( pOther->GetMoveType() != MOVETYPE_VPHYSICS || pOther->GetSolid() != SOLID_VPHYSICS || (pOther->GetSolidFlags() & FSOLID_TRIGGER) ) + return; + + IPhysicsObject *pPhys = pOther->VPhysicsGetObject(); + if ( !pPhys || !pPhys->IsMoveable() ) + return; + + SetTouchedPhysics( true ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBasePlayer::PostThinkVPhysics( void ) +{ + // Check to see if things are initialized! + if ( !m_pPhysicsController ) + return; + + Vector newPosition = GetAbsOrigin(); + float frametime = gpGlobals->frametime; + if ( frametime <= 0 || frametime > 0.1f ) + frametime = 0.1f; + + IPhysicsObject *pPhysGround = GetGroundVPhysics(); + + if ( !pPhysGround && m_touchedPhysObject && g_pMoveData->m_outStepHeight <= 0.f && (GetFlags() & FL_ONGROUND) ) + { + newPosition = m_oldOrigin + frametime * g_pMoveData->m_outWishVel; + newPosition = (GetAbsOrigin() * 0.5f) + (newPosition * 0.5f); + } + + int collisionState = VPHYS_WALK; + if ( GetMoveType() == MOVETYPE_NOCLIP || GetMoveType() == MOVETYPE_OBSERVER ) + { + collisionState = VPHYS_NOCLIP; + } + else if ( GetFlags() & FL_DUCKING ) + { + collisionState = VPHYS_CROUCH; + } + + if ( collisionState != m_vphysicsCollisionState ) + { + SetVCollisionState( collisionState ); + } + + if ( !(TouchedPhysics() || pPhysGround) ) + { + float maxSpeed = m_flMaxspeed > 0.0f ? m_flMaxspeed : sv_maxspeed.GetFloat(); + g_pMoveData->m_outWishVel.Init( maxSpeed, maxSpeed, maxSpeed ); + } + + // teleport the physics object up by stepheight (game code does this - reflect in the physics) + if ( g_pMoveData->m_outStepHeight > 0.1f ) + { + if ( g_pMoveData->m_outStepHeight > 4.0f ) + { + VPhysicsGetObject()->SetPosition( GetAbsOrigin(), vec3_angle, true ); + } + else + { + // don't ever teleport into solid + Vector position, end; + VPhysicsGetObject()->GetPosition( &position, NULL ); + end = position; + end.z += g_pMoveData->m_outStepHeight; + trace_t trace; + UTIL_TraceEntity( this, position, end, MASK_PLAYERSOLID, this, COLLISION_GROUP_PLAYER_MOVEMENT, &trace ); + if ( trace.DidHit() ) + { + g_pMoveData->m_outStepHeight = trace.endpos.z - position.z; + } + m_pPhysicsController->StepUp( g_pMoveData->m_outStepHeight ); + } + m_pPhysicsController->Jump(); + } + g_pMoveData->m_outStepHeight = 0.0f; + + // Store these off because after running the usercmds, it'll pass them + // to UpdateVPhysicsPosition. + m_vNewVPhysicsPosition = newPosition; + m_vNewVPhysicsVelocity = g_pMoveData->m_outWishVel; + + m_oldOrigin = GetAbsOrigin(); +} + +void CBasePlayer::UpdateVPhysicsPosition( const Vector &position, const Vector &velocity, float secondsToArrival ) +{ + bool onground = (GetFlags() & FL_ONGROUND) ? true : false; + IPhysicsObject *pPhysGround = GetGroundVPhysics(); + + // if the object is much heavier than the player, treat it as a local coordinate system + // the player controller will solve movement differently in this case. + if ( !IsRideablePhysics(pPhysGround) ) + { + pPhysGround = NULL; + } + + m_pPhysicsController->Update( position, velocity, secondsToArrival, onground, pPhysGround ); +} + +void CBasePlayer::UpdatePhysicsShadowToCurrentPosition() +{ + UpdateVPhysicsPosition( GetAbsOrigin(), vec3_origin, gpGlobals->frametime ); +} + +Vector CBasePlayer::GetSmoothedVelocity( void ) +{ + if ( IsInAVehicle() ) + { + return GetVehicle()->GetVehicleEnt()->GetSmoothedVelocity(); + } + return m_vecSmoothedVelocity; +} + + +CBaseEntity *g_pLastSpawn = NULL; + + +//----------------------------------------------------------------------------- +// Purpose: Finds a player start entity of the given classname. If any entity of +// of the given classname has the SF_PLAYER_START_MASTER flag set, that +// is the entity that will be returned. Otherwise, the first entity of +// the given classname is returned. +// Input : pszClassName - should be "info_player_start", "info_player_coop", or +// "info_player_deathmatch" +//----------------------------------------------------------------------------- +CBaseEntity *FindPlayerStart(const char *pszClassName) +{ + #define SF_PLAYER_START_MASTER 1 + + CBaseEntity *pStart = gEntList.FindEntityByClassname(NULL, pszClassName); + CBaseEntity *pStartFirst = pStart; + while (pStart != NULL) + { + if (pStart->HasSpawnFlags(SF_PLAYER_START_MASTER)) + { + return pStart; + } + + pStart = gEntList.FindEntityByClassname(pStart, pszClassName); + } + + return pStartFirst; +} + +/* +============ +EntSelectSpawnPoint + +Returns the entity to spawn at + +USES AND SETS GLOBAL g_pLastSpawn +============ +*/ +CBaseEntity *CBasePlayer::EntSelectSpawnPoint() +{ + CBaseEntity *pSpot; + edict_t *player; + + player = edict(); + +// choose a info_player_deathmatch point + if (g_pGameRules->IsCoOp()) + { + pSpot = gEntList.FindEntityByClassname( g_pLastSpawn, "info_player_coop"); + if ( pSpot ) + goto ReturnSpot; + pSpot = gEntList.FindEntityByClassname( g_pLastSpawn, "info_player_start"); + if ( pSpot ) + goto ReturnSpot; + } + else if ( g_pGameRules->IsDeathmatch() ) + { + pSpot = g_pLastSpawn; + // Randomize the start spot + for ( int i = random->RandomInt(1,5); i > 0; i-- ) + pSpot = gEntList.FindEntityByClassname( pSpot, "info_player_deathmatch" ); + if ( !pSpot ) // skip over the null point + pSpot = gEntList.FindEntityByClassname( pSpot, "info_player_deathmatch" ); + + CBaseEntity *pFirstSpot = pSpot; + + do + { + if ( pSpot ) + { + // check if pSpot is valid + if ( g_pGameRules->IsSpawnPointValid( pSpot, this ) ) + { + if ( pSpot->GetLocalOrigin() == vec3_origin ) + { + pSpot = gEntList.FindEntityByClassname( pSpot, "info_player_deathmatch" ); + continue; + } + + // if so, go to pSpot + goto ReturnSpot; + } + } + // increment pSpot + pSpot = gEntList.FindEntityByClassname( pSpot, "info_player_deathmatch" ); + } while ( pSpot != pFirstSpot ); // loop if we're not back to the start + + // we haven't found a place to spawn yet, so kill any guy at the first spawn point and spawn there + if ( pSpot ) + { + CBaseEntity *ent = NULL; + for ( CEntitySphereQuery sphere( pSpot->GetAbsOrigin(), 128 ); (ent = sphere.GetCurrentEntity()) != NULL; sphere.NextEntity() ) + { + // if ent is a client, kill em (unless they are ourselves) + if ( ent->IsPlayer() && !(ent->edict() == player) ) + ent->TakeDamage( CTakeDamageInfo( GetContainingEntity(INDEXENT(0)), GetContainingEntity(INDEXENT(0)), 300, DMG_GENERIC ) ); + } + goto ReturnSpot; + } + } + + // If startspot is set, (re)spawn there. + if ( !gpGlobals->startspot || !strlen(STRING(gpGlobals->startspot))) + { + pSpot = FindPlayerStart( "info_player_start" ); + if ( pSpot ) + goto ReturnSpot; + } + else + { + pSpot = gEntList.FindEntityByName( NULL, gpGlobals->startspot ); + if ( pSpot ) + goto ReturnSpot; + } + +ReturnSpot: + if ( !pSpot ) + { + Warning( "PutClientInServer: no info_player_start on level\n"); + return CBaseEntity::Instance( INDEXENT( 0 ) ); + } + + g_pLastSpawn = pSpot; + return pSpot; +} + +//----------------------------------------------------------------------------- +// Purpose: Called the first time the player's created +//----------------------------------------------------------------------------- +void CBasePlayer::InitialSpawn( void ) +{ + m_iConnected = PlayerConnected; +} + +//----------------------------------------------------------------------------- +// Purpose: Called everytime the player respawns +//----------------------------------------------------------------------------- +void CBasePlayer::Spawn( void ) +{ + SetClassname( "player" ); + + // Shared spawning code.. + SharedSpawn(); + + SetSimulatedEveryTick( true ); + SetAnimatedEveryTick( true ); + + m_ArmorValue = SpawnArmorValue(); + SetBlocksLOS( false ); + m_iMaxHealth = m_iHealth; + + // Clear all flags except for FL_FULLEDICT + if ( GetFlags() & FL_FAKECLIENT ) + { + ClearFlags(); + AddFlag( FL_CLIENT | FL_FAKECLIENT ); + } + else + { + ClearFlags(); + AddFlag( FL_CLIENT ); + } + + AddFlag( FL_AIMTARGET ); + + m_AirFinished = gpGlobals->curtime + AIRTIME; + m_nDrownDmgRate = DROWNING_DAMAGE_INITIAL; + + // only preserve the shadow flag + int effects = GetEffects() & EF_NOSHADOW; + SetEffects( effects ); + + m_DmgTake = 0; + m_DmgSave = 0; + m_bitsHUDDamage = -1; + m_bitsDamageType = 0; + m_afPhysicsFlags = 0; + + SetFOV( this, 0 ); + + m_flNextDecalTime = 0;// let this player decal as soon as he spawns. + + m_flgeigerDelay = gpGlobals->curtime + 2.0; // wait a few seconds until user-defined message registrations + // are recieved by all clients + + m_flTimeStepSound = 0; + m_flFieldOfView = 0.766;// some NPCs use this to determine whether or not the player is looking at them. + + m_vecAdditionalPVSOrigin = vec3_origin; + m_vecCameraPVSOrigin = vec3_origin; + + if ( !m_fGameHUDInitialized ) + g_pGameRules->SetDefaultPlayerTeam( this ); + + g_pGameRules->GetPlayerSpawnSpot( this ); + + m_Local.m_bDucked = false;// This will persist over round restart if you hold duck otherwise. + m_Local.m_bDucking = false; + SetViewOffset( VEC_VIEW ); + Precache(); + + m_bitsDamageType = 0; + m_bitsHUDDamage = -1; + SetPlayerUnderwater( false ); + + m_iTrain = TRAIN_NEW; + + m_HackedGunPos = Vector( 0, 32, 0 ); + + if ( m_iPlayerSound == SOUNDLIST_EMPTY ) + { + Msg( "Couldn't alloc player sound slot!\n" ); + } + + SetThink(NULL); + m_fInitHUD = true; + m_fWeapon = false; + m_iClientBattery = -1; + + m_lastx = m_lasty = 0; + + m_lastNavArea = NULL; + +#ifndef _XBOX + /// @todo Do this once per round instead of once per player + if (TheNavMesh) + { + TheNavMesh->ClearPlayerCounts(); + } +#endif + + Q_strncpy( m_szLastPlaceName.GetForModify(), "", MAX_PLACE_NAME_LENGTH ); + + CSingleUserRecipientFilter user( this ); + enginesound->SetPlayerDSP( user, 0, false ); + + CreateViewModel(); + + SetCollisionGroup( COLLISION_GROUP_PLAYER ); + + // if the player is locked, make sure he stays locked + if ( m_iPlayerLocked ) + { + m_iPlayerLocked = false; + LockPlayerInPlace(); + } + + if ( GetTeamNumber() != TEAM_SPECTATOR ) + { + StopObserverMode(); + } + else + { + StartObserverMode( m_iObserverLastMode ); + } + + StopReplayMode(); + + // Clear any screenfade + color32 nothing = {0,0,0,255}; + UTIL_ScreenFade( this, nothing, 0, 0, FFADE_IN | FFADE_PURGE ); + + g_pGameRules->PlayerSpawn( this ); + + m_flLaggedMovementValue = 1.0f; + m_vecSmoothedVelocity = vec3_origin; + InitVCollision(); + + IGameEvent *event = gameeventmanager->CreateEvent( "player_spawn" ); + + if ( event ) + { + event->SetInt("userid", GetUserID() ); + gameeventmanager->FireEvent( event ); + } + + RumbleEffect( RUMBLE_STOP_ALL, 0, RUMBLE_FLAGS_NONE ); +} + +void CBasePlayer::Activate( void ) +{ + BaseClass::Activate(); + + AimTarget_ForceRepopulateList(); + + RumbleEffect( RUMBLE_STOP_ALL, 0, RUMBLE_FLAGS_NONE ); + + // Reset the analog bias. If the player is in a vehicle when the game + // reloads, it will autosense and apply the correct bias. + m_iVehicleAnalogBias = VEHICLE_ANALOG_BIAS_NONE; +} + +void CBasePlayer::Precache( void ) +{ + BaseClass::Precache(); + + + PrecacheScriptSound( "Player.FallGib" ); + PrecacheScriptSound( "Player.Death" ); + PrecacheScriptSound( "Player.PlasmaDamage" ); + PrecacheScriptSound( "Player.SonicDamage" ); + PrecacheScriptSound( "Player.DrownStart" ); + PrecacheScriptSound( "Player.DrownContinue" ); + PrecacheScriptSound( "Player.Wade" ); + PrecacheScriptSound( "Player.AmbientUnderWater" ); + PrecacheScriptSound( "Player.Wade" ); + enginesound->PrecacheSentenceGroup( "HEV" ); + + // in the event that the player JUST spawned, and the level node graph + // was loaded, fix all of the node graph pointers before the game starts. + + // !!!BUGBUG - now that we have multiplayer, this needs to be moved! + /* todo - put in better spot and use new ainetowrk stuff + if ( WorldGraph.m_fGraphPresent && !WorldGraph.m_fGraphPointersSet ) + { + if ( !WorldGraph.FSetGraphPointers() ) + { + Msg( "**Graph pointers were not set!\n"); + } + else + { + Msg( "**Graph Pointers Set!\n" ); + } + } + */ + + // SOUNDS / MODELS ARE PRECACHED in ClientPrecache() (game specific) + // because they need to precache before any clients have connected + + // init geiger counter vars during spawn and each time + // we cross a level transition + m_flgeigerRange = 1000; + m_igeigerRangePrev = 1000; + +#if 0 + // @Note (toml 04-19-04): These are saved, used to be slammed here + m_bitsDamageType = 0; + m_bitsHUDDamage = -1; + SetPlayerUnderwter( false ); + + m_iTrain = TRAIN_NEW; +#endif + + m_iClientBattery = -1; + + m_iUpdateTime = 5; // won't update for 1/2 a second + + if ( gInitHUD ) + m_fInitHUD = true; + +} + + + +int CBasePlayer::Save( ISave &save ) +{ + if ( !BaseClass::Save(save) ) + return 0; + + return 1; +} + + +int CBasePlayer::Restore( IRestore &restore ) +{ + int status = BaseClass::Restore(restore); + if ( !status ) + return 0; + + CSaveRestoreData *pSaveData = gpGlobals->pSaveData; + // landmark isn't present. + if ( !pSaveData->levelInfo.fUseLandmark ) + { + Msg( "No Landmark:%s\n", pSaveData->levelInfo.szLandmarkName ); + + // default to normal spawn + CBaseEntity *pSpawnSpot = EntSelectSpawnPoint(); + SetLocalOrigin( pSpawnSpot->GetLocalOrigin() + Vector(0,0,1) ); + SetLocalAngles( pSpawnSpot->GetLocalAngles() ); + } + + QAngle newViewAngles = pl.v_angle; + newViewAngles.z = 0; // Clear out roll + SetLocalAngles( newViewAngles ); + SnapEyeAngles( newViewAngles ); + + // Copied from spawn() for now + SetBloodColor( BLOOD_COLOR_RED ); + + // clear this - it will get reset by touching the trigger again + m_afPhysicsFlags &= ~PFLAG_VPHYSICS_MOTIONCONTROLLER; + + if ( GetFlags() & FL_DUCKING ) + { + // Use the crouch HACK + FixPlayerCrouchStuck( this ); + UTIL_SetSize(this, VEC_DUCK_HULL_MIN, VEC_DUCK_HULL_MAX); + m_Local.m_bDucked = true; + } + else + { + m_Local.m_bDucked = false; + UTIL_SetSize(this, VEC_HULL_MIN, VEC_HULL_MAX); + } + + InitVCollision(); + + // success + return 1; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBasePlayer::OnRestore( void ) +{ + BaseClass::OnRestore(); + + SetViewEntity( m_hViewEntity ); +} + +/* void CBasePlayer::SetTeamName( const char *pTeamName ) +{ + Q_strncpy( m_szTeamName, pTeamName, TEAM_NAME_LENGTH ); +} */ + +void CBasePlayer::SetArmorValue( int value ) +{ + m_ArmorValue = value; +} + +void CBasePlayer::IncrementArmorValue( int nCount, int nMaxValue ) +{ + m_ArmorValue += nCount; + if (nMaxValue > 0) + { + if (m_ArmorValue > nMaxValue) + m_ArmorValue = nMaxValue; + } +} + +// Only used by the physics gun... is there a better interface? +void CBasePlayer::SetPhysicsFlag( int nFlag, bool bSet ) +{ + if (bSet) + m_afPhysicsFlags |= nFlag; + else + m_afPhysicsFlags &= ~nFlag; +} + + +void CBasePlayer::NotifyNearbyRadiationSource( float flRange ) +{ + // if player's current geiger counter range is larger + // than range to this trigger hurt, reset player's + // geiger counter range + + if (m_flgeigerRange >= flRange) + m_flgeigerRange = flRange; +} + +void CBasePlayer::AllowImmediateDecalPainting() +{ + m_flNextDecalTime = gpGlobals->curtime; +} + +// Suicide... +void CBasePlayer::CommitSuicide() +{ + if( !IsAlive() ) + return; + + // prevent suiciding too often + if ( m_fNextSuicideTime > gpGlobals->curtime ) + return; + + // don't let them suicide for 5 seconds after suiciding + m_fNextSuicideTime = gpGlobals->curtime + 5; + + // have the player kill themself + m_iHealth = 0; + Event_Killed( CTakeDamageInfo( this, this, 0, DMG_NEVERGIB ) ); + Event_Dying(); +} + +//============================================== +// HasWeapons - do I have any weapons at all? +//============================================== +bool CBasePlayer::HasWeapons( void ) +{ + int i; + + for ( i = 0 ; i < WeaponCount() ; i++ ) + { + if ( GetWeapon(i) ) + { + return true; + } + } + + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : &vecForce - +//----------------------------------------------------------------------------- +void CBasePlayer::VelocityPunch( const Vector &vecForce ) +{ + // Clear onground and add velocity. + SetGroundEntity( NULL ); + ApplyAbsVelocityImpulse(vecForce ); +} + + +//-------------------------------------------------------------------------------------------------------------- +// VEHICLES +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// Purpose: Put this player in a vehicle +//----------------------------------------------------------------------------- +bool CBasePlayer::GetInVehicle( IServerVehicle *pVehicle, int nRole ) +{ + Assert( NULL == m_hVehicle.Get() ); + Assert( nRole >= 0 ); + + if ( pVehicle->GetPassenger( nRole ) ) + return false; + + CBaseEntity *pEnt = pVehicle->GetVehicleEnt(); + Assert( pEnt ); + + if (!pVehicle->IsPassengerUsingStandardWeapons( nRole )) + { + CBaseCombatWeapon *pWeapon = GetActiveWeapon(); + + //Must be able to stow our weapon + if ( ( pWeapon != NULL ) && ( pWeapon->Holster( NULL ) == false ) ) + return false; + +#ifndef HL2_DLL + m_Local.m_iHideHUD |= HIDEHUD_WEAPONSELECTION; +#endif + m_Local.m_iHideHUD |= HIDEHUD_INVEHICLE; + } + + if ( !pVehicle->IsPassengerVisible( nRole ) ) + { + AddEffects( EF_NODRAW ); + } + + ViewPunchReset(); + + // Setting the velocity to 0 will cause the IDLE animation to play + SetAbsVelocity( vec3_origin ); + SetMoveType( MOVETYPE_NOCLIP ); + + // Choose the entry point of the vehicle, + // By default, just stay at the point you started at... + // NOTE: we have to set this first so that when the parent is set + // the local position just works + Vector vNewPos = GetAbsOrigin(); + QAngle qAngles = GetAbsAngles(); + pVehicle->GetPassengerStartPoint( nRole, &vNewPos, &qAngles ); + SetAbsOrigin( vNewPos ); + SetAbsAngles( qAngles ); + SetParent( pEnt ); + + SetCollisionGroup( COLLISION_GROUP_IN_VEHICLE ); + + // We cannot be ducking -- do all this before SetPassenger because it + // saves our view offset for restoration when we exit the vehicle. + RemoveFlag( FL_DUCKING ); + SetViewOffset( VEC_VIEW ); + m_Local.m_bDucked = false; + m_Local.m_bDucking = false; + m_Local.m_flDucktime = 0.0f; + m_Local.m_flDuckJumpTime = 0.0f; + m_Local.m_flJumpTime = 0.0f; + + // Turn our toggled duck off + if ( GetToggledDuckState() ) + { + ToggleDuck(); + } + + pVehicle->SetPassenger( nRole, this ); + + m_hVehicle = pEnt; + + // Throw an event indicating that the player entered the vehicle. + g_pNotify->ReportNamedEvent( this, "PlayerEnteredVehicle" ); + + m_iVehicleAnalogBias = VEHICLE_ANALOG_BIAS_NONE; + + OnVehicleStart(); + + return true; +} + + +//----------------------------------------------------------------------------- +// Purpose: Remove this player from a vehicle +//----------------------------------------------------------------------------- +void CBasePlayer::LeaveVehicle( const Vector &vecExitPoint, const QAngle &vecExitAngles ) +{ + if ( NULL == m_hVehicle.Get() ) + return; + + IServerVehicle *pVehicle = GetVehicle(); + Assert( pVehicle ); + + int nRole = pVehicle->GetPassengerRole( this ); + Assert( nRole >= 0 ); + + SetParent( NULL ); + + // Find the first non-blocked exit point: + Vector vNewPos = GetAbsOrigin(); + QAngle qAngles = GetAbsAngles(); + if ( vecExitPoint == vec3_origin ) + { + // FIXME: this might fail to find a safe exit point!! + pVehicle->GetPassengerExitPoint( nRole, &vNewPos, &qAngles ); + } + else + { + vNewPos = vecExitPoint; + qAngles = vecExitAngles; + } + OnVehicleEnd( vNewPos ); + SetAbsOrigin( vNewPos ); + SetAbsAngles( qAngles ); + // Clear out any leftover velocity + SetAbsVelocity( vec3_origin ); + + qAngles[ROLL] = 0; + SnapEyeAngles( qAngles ); + +#ifndef HL2_DLL + m_Local.m_iHideHUD &= ~HIDEHUD_WEAPONSELECTION; +#endif + + m_Local.m_iHideHUD &= ~HIDEHUD_INVEHICLE; + + RemoveEffects( EF_NODRAW ); + + SetMoveType( MOVETYPE_WALK ); + SetCollisionGroup( COLLISION_GROUP_PLAYER ); + + if ( VPhysicsGetObject() ) + { + VPhysicsGetObject()->SetPosition( vNewPos, vec3_angle, true ); + } + + m_hVehicle = NULL; + pVehicle->SetPassenger(nRole, NULL); + + // Re-deploy our weapon + if ( IsAlive() ) + { + if ( GetActiveWeapon() && GetActiveWeapon()->IsWeaponVisible() == false ) + { + GetActiveWeapon()->Deploy(); + ShowCrosshair( true ); + } + } + +#ifdef _XBOX + // Just cut all of the rumble effects. + RumbleEffect( RUMBLE_STOP_ALL, 0, RUMBLE_FLAGS_NONE ); +#endif//_XBOX +} + + +//============================================== +// !!!UNDONE:ultra temporary SprayCan entity to apply +// decal frame at a time. For PreAlpha CD +//============================================== +class CSprayCan : public CPointEntity +{ +public: + DECLARE_CLASS( CSprayCan, CPointEntity ); + + void Spawn ( CBasePlayer *pOwner ); + void Think( void ); + + virtual void Precache(); + + virtual int ObjectCaps( void ) { return FCAP_DONT_SAVE; } +}; + +LINK_ENTITY_TO_CLASS( spraycan, CSprayCan ); +PRECACHE_REGISTER( spraycan ); + +void CSprayCan::Spawn ( CBasePlayer *pOwner ) +{ + SetLocalOrigin( pOwner->WorldSpaceCenter() + Vector ( 0 , 0 , 32 ) ); + SetLocalAngles( pOwner->EyeAngles() ); + SetOwnerEntity( pOwner ); + SetNextThink( gpGlobals->curtime ); + EmitSound( "SprayCan.Paint" ); +} + +void CSprayCan::Precache() +{ + BaseClass::Precache(); + + PrecacheScriptSound( "SprayCan.Paint" ); +} + +void CSprayCan::Think( void ) +{ + trace_t tr; + int playernum; + int nFrames; + CBasePlayer *pPlayer; + + pPlayer = ToBasePlayer( GetOwnerEntity() ); + + nFrames = 1; // FIXME, look up from material + + playernum = GetOwnerEntity()->entindex(); + + // Msg( "Spray by player %i, %i of %i\n", playernum, (int)(m_flFrame + 1), nFrames); + + Vector forward; + AngleVectors( GetAbsAngles(), &forward ); + UTIL_TraceLine ( GetAbsOrigin(), GetAbsOrigin() + forward * 128, + MASK_SOLID_BRUSHONLY, pPlayer, COLLISION_GROUP_NONE, & tr); + + UTIL_PlayerDecalTrace( &tr, playernum ); + + // Just painted last custom frame. + UTIL_Remove( this ); +} + +class CBloodSplat : public CPointEntity +{ +public: + DECLARE_CLASS( CBloodSplat, CPointEntity ); + + void Spawn ( CBaseEntity *pOwner ); + void Think ( void ); +}; + +void CBloodSplat::Spawn ( CBaseEntity *pOwner ) +{ + SetLocalOrigin( pOwner->WorldSpaceCenter() + Vector ( 0 , 0 , 32 ) ); + SetLocalAngles( pOwner->GetLocalAngles() ); + SetOwnerEntity( pOwner ); + + SetNextThink( gpGlobals->curtime + 0.1f ); +} + +void CBloodSplat::Think( void ) +{ + trace_t tr; + + if ( g_Language.GetInt() != LANGUAGE_GERMAN ) + { + CBasePlayer *pPlayer; + pPlayer = ToBasePlayer( GetOwnerEntity() ); + + Vector forward; + AngleVectors( GetAbsAngles(), &forward ); + UTIL_TraceLine ( GetAbsOrigin(), GetAbsOrigin() + forward * 128, + MASK_SOLID_BRUSHONLY, pPlayer, COLLISION_GROUP_NONE, & tr); + + UTIL_BloodDecalTrace( &tr, BLOOD_COLOR_RED ); + } + UTIL_Remove( this ); +} + +//============================================== + +//----------------------------------------------------------------------------- +// Purpose: Create and give the named item to the player. Then return it. +//----------------------------------------------------------------------------- +CBaseEntity *CBasePlayer::GiveNamedItem( const char *pszName, int iSubType ) +{ + // If I already own this type don't create one + if ( Weapon_OwnsThisType(pszName, iSubType) ) + return NULL; + + // Msg( "giving %s\n", pszName ); + + EHANDLE pent; + + pent = CreateEntityByName(pszName); + if ( pent == NULL ) + { + Msg( "NULL Ent in GiveNamedItem!\n" ); + return NULL; + } + + pent->SetLocalOrigin( GetLocalOrigin() ); + pent->AddSpawnFlags( SF_NORESPAWN ); + + if ( iSubType ) + { + CBaseCombatWeapon *pWeapon = dynamic_cast( (CBaseEntity*)pent ); + if ( pWeapon ) + { + pWeapon->SetSubType( iSubType ); + } + } + + DispatchSpawn( pent ); + + if ( pent != NULL && !(pent->IsMarkedForDeletion()) ) + { + pent->Touch( this ); + } + + return pent; +} + +//----------------------------------------------------------------------------- +// Purpose: Returns the nearest COLLIBALE entity in front of the player +// that has a clear line of sight with the given classname +// Input : +// Output : +//----------------------------------------------------------------------------- +CBaseEntity *FindEntityClassForward( CBasePlayer *pMe, char *classname ) +{ + trace_t tr; + + Vector forward; + pMe->EyeVectors( &forward ); + UTIL_TraceLine(pMe->EyePosition(), + pMe->EyePosition() + forward * MAX_COORD_RANGE, + MASK_SOLID, pMe, COLLISION_GROUP_NONE, &tr ); + if ( tr.fraction != 1.0 && tr.DidHitNonWorldEntity() ) + { + CBaseEntity *pHit = tr.m_pEnt; + if (FClassnameIs( pHit,classname ) ) + { + return pHit; + } + } + return NULL; +} + +//----------------------------------------------------------------------------- +// Purpose: Returns the nearest COLLIBALE entity in front of the player +// that has a clear line of sight. If HULL is true, the trace will +// hit the collision hull of entities. Otherwise, the trace will hit +// hitboxes. +// Input : +// Output : +//----------------------------------------------------------------------------- +CBaseEntity *FindEntityForward( CBasePlayer *pMe, bool fHull ) +{ + if ( pMe ) + { + trace_t tr; + Vector forward; + int mask; + + if( fHull ) + { + mask = MASK_SOLID; + } + else + { + mask = MASK_SHOT; + } + + pMe->EyeVectors( &forward ); + UTIL_TraceLine(pMe->EyePosition(), + pMe->EyePosition() + forward * MAX_COORD_RANGE, + mask, pMe, COLLISION_GROUP_NONE, &tr ); + if ( tr.fraction != 1.0 && tr.DidHitNonWorldEntity() ) + { + return tr.m_pEnt; + } + } + return NULL; + +} + +//----------------------------------------------------------------------------- +// Purpose: Finds the nearest entity in front of the player of the given +// classname, preferring collidable entities, but allows selection of +// enities that are on the other side of walls or objects +// +// Input : +// Output : +//----------------------------------------------------------------------------- +CBaseEntity *FindPickerEntityClass( CBasePlayer *pPlayer, char *classname ) +{ + // First try to trace a hull to an entity + CBaseEntity *pEntity = FindEntityClassForward( pPlayer, classname ); + + // If that fails just look for the nearest facing entity + if (!pEntity) + { + Vector forward; + Vector origin; + pPlayer->EyeVectors( &forward ); + origin = pPlayer->WorldSpaceCenter(); + pEntity = gEntList.FindEntityClassNearestFacing( origin, forward,0.95,classname); + } + return pEntity; +} + +//----------------------------------------------------------------------------- +// Purpose: Finds the nearest entity in front of the player, preferring +// collidable entities, but allows selection of enities that are +// on the other side of walls or objects +// Input : +// Output : +//----------------------------------------------------------------------------- +CBaseEntity *FindPickerEntity( CBasePlayer *pPlayer ) +{ + // First try to trace a hull to an entity + CBaseEntity *pEntity = FindEntityForward( pPlayer, true ); + + // If that fails just look for the nearest facing entity + if (!pEntity) + { + Vector forward; + Vector origin; + pPlayer->EyeVectors( &forward ); + origin = pPlayer->WorldSpaceCenter(); + pEntity = gEntList.FindEntityNearestFacing( origin, forward,0.95); + } + return pEntity; +} + +//----------------------------------------------------------------------------- +// Purpose: Finds the nearest node in front of the player +// Input : +// Output : +//----------------------------------------------------------------------------- +CAI_Node *FindPickerAINode( CBasePlayer *pPlayer, NodeType_e nNodeType ) +{ + Vector forward; + Vector origin; + + pPlayer->EyeVectors( &forward ); + origin = pPlayer->EyePosition(); + return g_pAINetworkManager->GetEditOps()->FindAINodeNearestFacing( origin, forward,0.90, nNodeType); +} + +//----------------------------------------------------------------------------- +// Purpose: Finds the nearest link in front of the player +// Input : +// Output : +//----------------------------------------------------------------------------- +CAI_Link *FindPickerAILink( CBasePlayer* pPlayer ) +{ + Vector forward; + Vector origin; + + pPlayer->EyeVectors( &forward ); + origin = pPlayer->EyePosition(); + return g_pAINetworkManager->GetEditOps()->FindAILinkNearestFacing( origin, forward,0.90); +} + +/* +=============== +ForceClientDllUpdate + +When recording a demo, we need to have the server tell us the entire client state +so that the client side .dll can behave correctly. +Reset stuff so that the state is transmitted. +=============== +*/ +void CBasePlayer::ForceClientDllUpdate( void ) +{ + m_iClientBattery = -1; + m_iTrain |= TRAIN_NEW; // Force new train message. + m_fWeapon = false; // Force weapon send + + // Force all HUD data to be resent to client + m_fInitHUD = true; + + // Now force all the necessary messages + // to be sent. + UpdateClientData(); + + UTIL_RestartAmbientSounds(); // MOTODO that updates the sounds for everybody +} + +/* +============ +ImpulseCommands +============ +*/ + +void CBasePlayer::ImpulseCommands( ) +{ + trace_t tr; + + int iImpulse = (int)m_nImpulse; + switch (iImpulse) + { + case 100: + // temporary flashlight for level designers + if ( FlashlightIsOn() ) + { + FlashlightTurnOff(); + } + else + { + FlashlightTurnOn(); + } + break; + + case 200: + if ( sv_cheats->GetBool() ) + { + CBaseCombatWeapon *pWeapon; + + pWeapon = GetActiveWeapon(); + + if( pWeapon->IsEffectActive( EF_NODRAW ) ) + { + pWeapon->Deploy(); + } + else + { + pWeapon->Holster(); + } + } + break; + + case 201:// paint decal + + if ( gpGlobals->curtime < m_flNextDecalTime ) + { + // too early! + break; + } + + { + Vector forward; + EyeVectors( &forward ); + UTIL_TraceLine ( EyePosition(), + EyePosition() + forward * 128, + MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, & tr); + } + + if ( tr.fraction != 1.0 ) + {// line hit something, so paint a decal + m_flNextDecalTime = gpGlobals->curtime + decalfrequency.GetFloat(); + CSprayCan *pCan = CREATE_UNSAVED_ENTITY( CSprayCan, "spraycan" ); + pCan->Spawn( this ); + } + + break; + + case 202:// player jungle sound + if ( gpGlobals->curtime < m_flNextDecalTime ) + { + // too early! + break; + + } + + EntityMessageBegin( this ); + WRITE_BYTE( PLAY_PLAYER_JINGLE ); + MessageEnd(); + + m_flNextDecalTime = gpGlobals->curtime + decalfrequency.GetFloat(); + break; + + default: + // check all of the cheat impulse commands now + CheatImpulseCommands( iImpulse ); + break; + } + + m_nImpulse = 0; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +static void CreateJeep( CBasePlayer *pPlayer ) +{ + // Cheat to create a jeep in front of the player + Vector vecForward; + AngleVectors( pPlayer->EyeAngles(), &vecForward ); + CBaseEntity *pJeep = (CBaseEntity *)CreateEntityByName( "prop_vehicle_jeep" ); + if ( pJeep ) + { + Vector vecOrigin = pPlayer->GetAbsOrigin() + vecForward * 256 + Vector(0,0,64); + QAngle vecAngles( 0, pPlayer->GetAbsAngles().y - 90, 0 ); + pJeep->SetAbsOrigin( vecOrigin ); + pJeep->SetAbsAngles( vecAngles ); + pJeep->KeyValue( "model", "models/buggy.mdl" ); + pJeep->KeyValue( "solid", "6" ); + pJeep->KeyValue( "targetname", "jeep" ); + pJeep->KeyValue( "vehiclescript", "scripts/vehicles/jeep_test.txt" ); + pJeep->Spawn(); + pJeep->Activate(); + pJeep->Teleport( &vecOrigin, &vecAngles, NULL ); + } +} + + +void CC_CH_CreateJeep( void ) +{ + CBasePlayer *pPlayer = UTIL_GetCommandClient(); + if ( !pPlayer ) + return; + CreateJeep( pPlayer ); +} + +static ConCommand ch_createjeep("ch_createjeep", CC_CH_CreateJeep, "Spawn jeep in front of the player.", FCVAR_CHEAT); + + +//----------------------------------------------------------------------------- +// Create an airboat in front of the specified player +//----------------------------------------------------------------------------- +static void CreateAirboat( CBasePlayer *pPlayer ) +{ + // Cheat to create a jeep in front of the player + Vector vecForward; + AngleVectors( pPlayer->EyeAngles(), &vecForward ); + CBaseEntity *pJeep = ( CBaseEntity* )CreateEntityByName( "prop_vehicle_airboat" ); + if ( pJeep ) + { + Vector vecOrigin = pPlayer->GetAbsOrigin() + vecForward * 256 + Vector( 0,0,64 ); + QAngle vecAngles( 0, pPlayer->GetAbsAngles().y - 90, 0 ); + pJeep->SetAbsOrigin( vecOrigin ); + pJeep->SetAbsAngles( vecAngles ); + pJeep->KeyValue( "model", "models/airboat.mdl" ); + pJeep->KeyValue( "solid", "6" ); + pJeep->KeyValue( "targetname", "airboat" ); + pJeep->KeyValue( "vehiclescript", "scripts/vehicles/airboat.txt" ); + pJeep->Spawn(); + pJeep->Activate(); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CC_CH_CreateAirboat( void ) +{ + CBasePlayer *pPlayer = UTIL_GetCommandClient(); + if ( !pPlayer ) + return; + + CreateAirboat( pPlayer ); + +} + +static ConCommand ch_createairboat( "ch_createairboat", CC_CH_CreateAirboat, "Spawn airboat in front of the player.", FCVAR_CHEAT ); + + +//========================================================= +//========================================================= +void CBasePlayer::CheatImpulseCommands( int iImpulse ) +{ +#if !defined( HLDEMO_BUILD ) + if ( !sv_cheats->GetBool() ) + { + return; + } + + CBaseEntity *pEntity; + trace_t tr; + + switch ( iImpulse ) + { + case 76: + { + if (!giPrecacheGrunt) + { + giPrecacheGrunt = 1; + Msg( "You must now restart to use Grunt-o-matic.\n"); + } + else + { + Vector forward = UTIL_YawToVector( EyeAngles().y ); + Create("NPC_human_grunt", GetLocalOrigin() + forward * 128, GetLocalAngles()); + } + break; + } + + case 81: + + GiveNamedItem( "weapon_cubemap" ); + break; + + case 82: + // Cheat to create a jeep in front of the player + CreateJeep( this ); + break; + + case 83: + // Cheat to create a airboat in front of the player + CreateAirboat( this ); + break; + + case 101: + gEvilImpulse101 = true; + + EquipSuit(); + + // Give the player everything! + GiveAmmo( 255, "Pistol"); + GiveAmmo( 255, "AR2"); + GiveAmmo( 5, "AR2AltFire"); + GiveAmmo( 255, "SMG1"); + GiveAmmo( 255, "Buckshot"); + GiveAmmo( 3, "smg1_grenade"); + GiveAmmo( 3, "rpg_round"); + GiveAmmo( 5, "grenade"); + GiveAmmo( 32, "357" ); + GiveAmmo( 16, "XBowBolt" ); +#ifdef HL2_EPISODIC + GiveAmmo( 5, "Hopwire" ); +#endif + GiveNamedItem( "weapon_smg1" ); + GiveNamedItem( "weapon_frag" ); + GiveNamedItem( "weapon_crowbar" ); + GiveNamedItem( "weapon_pistol" ); + GiveNamedItem( "weapon_ar2" ); + GiveNamedItem( "weapon_shotgun" ); + GiveNamedItem( "weapon_physcannon" ); + GiveNamedItem( "weapon_bugbait" ); + GiveNamedItem( "weapon_rpg" ); + GiveNamedItem( "weapon_357" ); + GiveNamedItem( "weapon_crossbow" ); +#ifdef HL2_EPISODIC + //GiveNamedItem( "weapon_hopwire" ); +#endif + if ( GetHealth() < 100 ) + { + TakeHealth( 25, DMG_GENERIC ); + } + + gEvilImpulse101 = false; + + break; + + case 102: + // Gibbage!!! + CGib::SpawnRandomGibs( this, 1, GIB_HUMAN ); + break; + + case 103: + // What the hell are you doing? + pEntity = FindEntityForward( this, true ); + if ( pEntity ) + { + CAI_BaseNPC *pNPC = pEntity->MyNPCPointer(); + if ( pNPC ) + pNPC->ReportAIState(); + } + break; + + case 106: + // Give me the classname and targetname of this entity. + pEntity = FindEntityForward( this, true ); + if ( pEntity ) + { + Msg( "Classname: %s", pEntity->GetClassname() ); + + if ( pEntity->GetEntityName() != NULL_STRING ) + { + Msg( " - Name: %s\n", STRING( pEntity->GetEntityName() ) ); + } + else + { + Msg( " - Name: No Targetname\n" ); + } + + if ( pEntity->m_iParent != NULL_STRING ) + Msg( "Parent: %s\n", STRING(pEntity->m_iParent) ); + + Msg( "Model: %s\n", STRING( pEntity->GetModelName() ) ); + if ( pEntity->m_iGlobalname != NULL_STRING ) + Msg( "Globalname: %s\n", STRING(pEntity->m_iGlobalname) ); + } + break; + + case 107: + { + trace_t tr; + + edict_t *pWorld = engine->PEntityOfEntIndex( 0 ); + + Vector start = EyePosition(); + Vector forward; + EyeVectors( &forward ); + Vector end = start + forward * 1024; + UTIL_TraceLine( start, end, MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr ); + if ( tr.m_pEnt ) + pWorld = tr.m_pEnt->edict(); + + const char *pTextureName = tr.surface.name; + + if ( pTextureName ) + Msg( "Texture: %s\n", pTextureName ); + } + break; + + // + // Sets the debug NPC to be the NPC under the crosshair. + // + case 108: + { + pEntity = FindEntityForward( this, true ); + if ( pEntity ) + { + CAI_BaseNPC *pNPC = pEntity->MyNPCPointer(); + if ( pNPC != NULL ) + { + Msg( "Debugging %s (0x%x)\n", pNPC->GetClassname(), pNPC ); + CAI_BaseNPC::SetDebugNPC( pNPC ); + } + } + break; + } + + case 195:// show shortest paths for entire level to nearest node + { + Create("node_viewer_fly", GetLocalOrigin(), GetLocalAngles()); + } + break; + case 196:// show shortest paths for entire level to nearest node + { + Create("node_viewer_large", GetLocalOrigin(), GetLocalAngles()); + } + break; + case 197:// show shortest paths for entire level to nearest node + { + Create("node_viewer_human", GetLocalOrigin(), GetLocalAngles()); + } + break; + case 202:// Random blood splatter + { + Vector forward; + EyeVectors( &forward ); + UTIL_TraceLine ( EyePosition(), + EyePosition() + forward * 128, + MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, & tr); + + if ( tr.fraction != 1.0 ) + {// line hit something, so paint a decal + CBloodSplat *pBlood = CREATE_UNSAVED_ENTITY( CBloodSplat, "bloodsplat" ); + pBlood->Spawn( this ); + } + } + break; + case 203:// remove creature. + pEntity = FindEntityForward( this, true ); + if ( pEntity ) + { + UTIL_Remove( pEntity ); +// if ( pEntity->m_takedamage ) +// pEntity->SetThink(SUB_Remove); + } + break; + } +#endif // HLDEMO_BUILD +} + + +bool CBasePlayer::ClientCommand(const char *cmd) +{ +#ifdef _DEBUG + if( stricmp( cmd, "test_SmokeGrenade" ) == 0 ) + { + if ( sv_cheats && sv_cheats->GetBool() ) + { + ParticleSmokeGrenade *pSmoke = dynamic_cast( CreateEntityByName(PARTICLESMOKEGRENADE_ENTITYNAME) ); + if ( pSmoke ) + { + Vector vForward; + AngleVectors( GetLocalAngles(), &vForward ); + vForward.z = 0; + VectorNormalize( vForward ); + + pSmoke->SetLocalOrigin( GetLocalOrigin() + vForward * 100 ); + pSmoke->SetFadeTime(25, 30); // Fade out between 25 seconds and 30 seconds. + pSmoke->Activate(); + pSmoke->SetLifetime(30); + pSmoke->FillVolume(); + + return true; + } + } + } + else +#endif // _DEBUG + if( stricmp( cmd, "vehicleRole" ) == 0 ) + { + // Get the vehicle role value. + if ( engine->Cmd_Argc() == 2 ) + { + // Check to see if a player is in a vehicle. + if ( IsInAVehicle() ) + { + int nRole = atoi( engine->Cmd_Argv( 1 ) ); + IServerVehicle *pVehicle = GetVehicle(); + if ( pVehicle ) + { + // Only switch roles if role is empty! + if ( !pVehicle->GetPassenger( nRole ) ) + { + LeaveVehicle(); + GetInVehicle( pVehicle, nRole ); + } + } + } + + return true; + } + } + else if ( stricmp( cmd, "spectate" ) == 0 ) // join spectator team & start observer mode + { + if ( GetTeamNumber() == TEAM_SPECTATOR ) + return true; + + if ( !IsDead() ) + { + ClientKill( edict() ); // kill player + + // add 1 to frags to balance out the 1 subtracted for killing yourself + IncrementFragCount( 1 ); + } + + RemoveAllItems( true ); + + ChangeTeam( TEAM_SPECTATOR ); + + StartObserverMode( OBS_MODE_ROAMING ); + return true; + } + else if ( stricmp( cmd, "spec_mode" ) == 0 ) // new observer mode + { + int mode; + + // check for parameters. + if ( engine->Cmd_Argc() >= 2 ) + { + mode = atoi( engine->Cmd_Argv(1) ); + + if ( mode < OBS_MODE_IN_EYE || mode > OBS_MODE_ROAMING ) + mode = OBS_MODE_IN_EYE; + } + else + { + // sitch to next spec mode if no parameter give + mode = GetObserverMode() + 1; + + if ( mode > OBS_MODE_ROAMING ) + { + mode = OBS_MODE_IN_EYE; + } + else if ( mode < OBS_MODE_IN_EYE ) + { + mode = OBS_MODE_ROAMING; + } + + } + + // don't allow input while player or death cam animation + if ( GetObserverMode() > OBS_MODE_DEATHCAM ) + { + // set new spectator mode, don't allow OBS_MODE_NONE + if ( !SetObserverMode( mode ) ) + ClientPrint( this, HUD_PRINTCONSOLE, "#Spectator_Mode_Unkown"); + else + engine->ClientCommand( edict(), "cl_spec_mode %d", mode ); + } + else + { + // remember spectator mode for later use + m_iObserverLastMode = mode; + engine->ClientCommand( edict(), "cl_spec_mode %d", mode ); + } + + return true; + } + else if ( stricmp( cmd, "spec_next" ) == 0 ) // chase next player + { + if ( GetObserverMode() > OBS_MODE_FIXED ) + { + // set new spectator mode + CBaseEntity * target = FindNextObserverTarget( false ); + if ( target ) + SetObserverTarget( target ); + } + + return true; + } + else if ( stricmp( cmd, "spec_prev" ) == 0 ) // chase prevoius player + { + if ( GetObserverMode() > OBS_MODE_FIXED ) + { + // set new spectator mode + CBaseEntity * target = FindNextObserverTarget( true ); + if ( target ) + SetObserverTarget( target ); + } + + return true; + } + + else if ( stricmp( cmd, "spec_player" ) == 0 ) // chase next player + { + if ( GetObserverMode() > OBS_MODE_FIXED && + engine->Cmd_Argc() == 2 ) + { + int index = atoi( engine->Cmd_Argv(1) ); + + CBasePlayer * target; + + if ( index == 0 ) + { + target = UTIL_PlayerByName( engine->Cmd_Argv(1) ); + } + else + { + target = UTIL_PlayerByIndex( index ); + } + + if ( IsValidObserverTarget( target ) ) + { + SetObserverTarget( target ); + } + } + + return true; + } + + else if ( stricmp( cmd, "spec_goto" ) == 0 ) // chase next player + { + if ( ( GetObserverMode() == OBS_MODE_FIXED || + GetObserverMode() == OBS_MODE_ROAMING ) && + engine->Cmd_Argc() == 6 ) + { + Vector origin; + origin.x = atof( engine->Cmd_Argv(1) ); + origin.y = atof( engine->Cmd_Argv(2) ); + origin.z = atof( engine->Cmd_Argv(3) ); + + QAngle angle; + angle.x = atof( engine->Cmd_Argv(4) ); + angle.y = atof( engine->Cmd_Argv(5) ); + angle.z = 0.0f; + + JumptoPosition( origin, angle ); + } + + return true; + } + + + + return false; +} + +extern bool UTIL_ItemCanBeTouchedByPlayer( CBaseEntity *pItem, CBasePlayer *pPlayer ); + +//----------------------------------------------------------------------------- +// Purpose: Player reacts to bumping a weapon. +// Input : pWeapon - the weapon that the player bumped into. +// Output : Returns true if player picked up the weapon +//----------------------------------------------------------------------------- +bool CBasePlayer::BumpWeapon( CBaseCombatWeapon *pWeapon ) +{ + CBaseCombatCharacter *pOwner = pWeapon->GetOwner(); + + // Can I have this weapon type? + if ( IsEFlagSet( EFL_NO_WEAPON_PICKUP ) ) + return false; + + if ( pOwner || !Weapon_CanUse( pWeapon ) || !g_pGameRules->CanHavePlayerItem( this, pWeapon ) ) + { + if ( gEvilImpulse101 ) + { + UTIL_Remove( pWeapon ); + } + return false; + } + + // Act differently in the episodes + if ( hl2_episodic.GetBool() ) + { + // Don't let the player touch the item unless unobstructed + if ( !UTIL_ItemCanBeTouchedByPlayer( pWeapon, this ) && !gEvilImpulse101 ) + return false; + } + else + { + // Don't let the player fetch weapons through walls (use MASK_SOLID so that you can't pickup through windows) + if( pWeapon->FVisible( this, MASK_SOLID ) == false && !(GetFlags() & FL_NOTARGET) ) + return false; + } + + // ---------------------------------------- + // If I already have it just take the ammo + // ---------------------------------------- + if (Weapon_OwnsThisType( pWeapon->GetClassname(), pWeapon->GetSubType())) + { + if( Weapon_EquipAmmoOnly( pWeapon ) ) + { + // Only remove me if I have no ammo left + if ( pWeapon->HasPrimaryAmmo() ) + return false; + + UTIL_Remove( pWeapon ); + return true; + } + else + { + return false; + } + } + // ------------------------- + // Otherwise take the weapon + // ------------------------- + else + { + pWeapon->CheckRespawn(); + + pWeapon->AddSolidFlags( FSOLID_NOT_SOLID ); + pWeapon->AddEffects( EF_NODRAW ); + + Weapon_Equip( pWeapon ); + if ( IsInAVehicle() ) + { + pWeapon->Holster(); + } + else + { +#ifdef HL2_DLL + +#ifdef _XBOX + CFmtStr hint; + hint.sprintf( "#valve_hint_select_%s", pWeapon->GetClassname() ); + UTIL_HudHintText( this, hint.Access() ); +#endif // _XBOX + + // Always switch to a newly-picked up weapon + if ( !PlayerHasMegaPhysCannon() ) + { + // If it uses clips, load it full. (this is the first time you've picked up this type of weapon) + if ( pWeapon->UsesClipsForAmmo1() ) + { + pWeapon->m_iClip1 = pWeapon->GetMaxClip1(); + } + + Weapon_Switch( pWeapon ); + } +#endif + } + return true; + } +} + + +bool CBasePlayer::RemovePlayerItem( CBaseCombatWeapon *pItem ) +{ + if (GetActiveWeapon() == pItem) + { + ResetAutoaim( ); + pItem->Holster( ); + pItem->SetNextThink( TICK_NEVER_THINK );; // crowbar may be trying to swing again, etc + pItem->SetThink( NULL ); + } + + if ( m_hLastWeapon.Get() == pItem ) + { + Weapon_SetLast( NULL ); + } + + return Weapon_Detach( pItem ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Hides or shows the player's view model. The "r_drawviewmodel" cvar +// can still hide the viewmodel even if this is set to true. +// Input : bShow - true to show, false to hide the view model. +//----------------------------------------------------------------------------- +void CBasePlayer::ShowViewModel(bool bShow) +{ + m_Local.m_bDrawViewmodel = bShow; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : bDraw - +//----------------------------------------------------------------------------- +void CBasePlayer::ShowCrosshair( bool bShow ) +{ + if ( bShow ) + { + m_Local.m_iHideHUD &= ~HIDEHUD_CROSSHAIR; + } + else + { + m_Local.m_iHideHUD |= HIDEHUD_CROSSHAIR; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +QAngle CBasePlayer::BodyAngles() +{ + return EyeAngles(); +} + +//------------------------------------------------------------------------------ +// Purpose : Add noise to BodyTarget() to give enemy a better chance of +// getting a clear shot when the player is peeking above a hole +// or behind a ladder (eventually the randomly-picked point +// along the spine will be one that is exposed above the hole or +// between rungs of a ladder.) +// Input : +// Output : +//------------------------------------------------------------------------------ +Vector CBasePlayer::BodyTarget( const Vector &posSrc, bool bNoisy ) +{ + if ( IsInAVehicle() ) + { + return GetVehicle()->GetVehicleEnt()->BodyTarget( posSrc, bNoisy ); + } + if (bNoisy) + { + return GetAbsOrigin() + (GetViewOffset() * random->RandomFloat( 0.7, 1.0 )); + } + else + { + return EyePosition(); + } +}; + +/* +========================================================= + UpdateClientData + +resends any changed player HUD info to the client. +Called every frame by PlayerPreThink +Also called at start of demo recording and playback by +ForceClientDllUpdate to ensure the demo gets messages +reflecting all of the HUD state info. +========================================================= +*/ +void CBasePlayer::UpdateClientData( void ) +{ + CSingleUserRecipientFilter user( this ); + user.MakeReliable(); + + if (m_fInitHUD) + { + m_fInitHUD = false; + gInitHUD = false; + + UserMessageBegin( user, "ResetHUD" ); + WRITE_BYTE( 0 ); + MessageEnd(); + + if ( !m_fGameHUDInitialized ) + { + g_pGameRules->InitHUD( this ); + InitHUD(); + m_fGameHUDInitialized = true; + if ( g_pGameRules->IsMultiplayer() ) + { + variant_t value; + g_EventQueue.AddEvent( "game_player_manager", "OnPlayerJoin", value, 0, this, this ); + } + } + + variant_t value; + g_EventQueue.AddEvent( "game_player_manager", "OnPlayerSpawn", value, 0, this, this ); + } + + // HACKHACK -- send the message to display the game title + CWorld *world = GetWorldEntity(); + if ( world && world->GetDisplayTitle() ) + { + UserMessageBegin( user, "GameTitle" ); + MessageEnd(); + world->SetDisplayTitle( false ); + } + + if (m_ArmorValue != m_iClientBattery) + { + m_iClientBattery = m_ArmorValue; + + // send "battery" update message + if ( usermessages->LookupUserMessage( "Battery" ) != -1 ) + { + UserMessageBegin( user, "Battery" ); + WRITE_SHORT( (int)m_ArmorValue); + MessageEnd(); + } + } + +#if 0 // BYE BYE!! + // Update Flashlight + if ((m_flFlashLightTime) && (m_flFlashLightTime <= gpGlobals->curtime)) + { + if (FlashlightIsOn()) + { + if (m_iFlashBattery) + { + m_flFlashLightTime = FLASH_DRAIN_TIME + gpGlobals->curtime; + m_iFlashBattery--; + + if (!m_iFlashBattery) + FlashlightTurnOff(); + } + } + else + { + if (m_iFlashBattery < 100) + { + m_flFlashLightTime = FLASH_CHARGE_TIME + gpGlobals->curtime; + m_iFlashBattery++; + } + else + m_flFlashLightTime = 0; + } + } +#endif + + CheckTrainUpdate(); + + // Update all the items + for ( int i = 0; i < WeaponCount(); i++ ) + { + if ( GetWeapon(i) ) // each item updates it's successors + GetWeapon(i)->UpdateClientData( this ); + } + + // update the client with our poison state + m_Local.m_bPoisoned = ( m_bitsDamageType & DMG_POISON ) + && ( m_nPoisonDmg > m_nPoisonRestored ) + && ( m_iHealth < 100 ); + + // Let any global rules update the HUD, too + g_pGameRules->UpdateClientData( this ); +} + +#ifdef _XBOX +void CBasePlayer::RumbleEffect( unsigned char index, unsigned char rumbleData, unsigned char rumbleFlags ) +{ + if( !IsAlive() ) + return; + + CSingleUserRecipientFilter filter( this ); + filter.MakeReliable(); + + UserMessageBegin( filter, "XBoxRumble" ); + WRITE_BYTE( index ); + WRITE_BYTE( rumbleData ); + WRITE_BYTE( rumbleFlags ); + MessageEnd(); +} +#else +void CBasePlayer::RumbleEffect( unsigned char index, unsigned char rumbleData, unsigned char rumbleFlags ) +{ + return; +} +#endif//_XBOX + + +void CBasePlayer::EnableControl(bool fControl) +{ + if (!fControl) + AddFlag( FL_FROZEN ); + else + RemoveFlag( FL_FROZEN ); + +} + +void CBasePlayer::CheckTrainUpdate( void ) +{ + if ( IsPC() && ( m_iTrain & TRAIN_NEW ) ) + { + CSingleUserRecipientFilter user( this ); + user.MakeReliable(); + + // send "Train" update message + UserMessageBegin( user, "Train" ); + WRITE_BYTE(m_iTrain & 0xF); + MessageEnd(); + + m_iTrain &= ~TRAIN_NEW; + } +} + +//----------------------------------------------------------------------------- +// Purpose: Returns whether the player should autoaim or not +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CBasePlayer::ShouldAutoaim( void ) +{ + // cannot be in multiplayer + if ( gpGlobals->maxClients > 1 ) + return false; + + // autoaiming is only for easy and medium skill + return ( IsXbox() || !g_pGameRules->IsSkillLevel(SKILL_HARD) ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +Vector CBasePlayer::GetAutoaimVector( float flScale ) +{ + autoaim_params_t params; + + params.m_fScale = flScale; + params.m_fMaxDist = autoaim_max_dist.GetFloat(); + + GetAutoaimVector( params ); + return params.m_vecAutoAimDir; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +Vector CBasePlayer::GetAutoaimVector( float flScale, float flMaxDist ) +{ + autoaim_params_t params; + + params.m_fScale = flScale; + params.m_fMaxDist = flMaxDist; + + GetAutoaimVector( params ); + return params.m_vecAutoAimDir; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CBasePlayer::GetAutoaimVector( autoaim_params_t ¶ms ) +{ + // Assume autoaim will not be assisting. + params.m_bAutoAimAssisting = false; + + if ( ( ShouldAutoaim() == false ) || ( params.m_fScale == AUTOAIM_SCALE_DIRECT_ONLY ) ) + { + Vector forward; + AngleVectors( EyeAngles() + m_Local.m_vecPunchAngle, &forward ); + + params.m_vecAutoAimDir = forward; + params.m_hAutoAimEntity.Set(NULL); + params.m_vecAutoAimPoint = vec3_invalid; + params.m_bAutoAimAssisting = false; + return; + } + + Vector vecSrc = Weapon_ShootPosition( ); + + m_vecAutoAim.Init( 0.0f, 0.0f, 0.0f ); + + QAngle angles = AutoaimDeflection( vecSrc, params ); + + // update ontarget if changed + if ( !g_pGameRules->AllowAutoTargetCrosshair() ) + m_fOnTarget = false; + + if (angles.x > 180) + angles.x -= 360; + if (angles.x < -180) + angles.x += 360; + if (angles.y > 180) + angles.y -= 360; + if (angles.y < -180) + angles.y += 360; + + if (angles.x > 25) + angles.x = 25; + if (angles.x < -25) + angles.x = -25; + if (angles.y > 12) + angles.y = 12; + if (angles.y < -12) + angles.y = -12; + + Vector forward; + + if( IsXbox() && IsInAVehicle() ) + { + m_vecAutoAim = angles; + AngleVectors( EyeAngles() + m_vecAutoAim, &forward ); + } + else + { + // always use non-sticky autoaim + m_vecAutoAim = angles * 0.9f; + AngleVectors( EyeAngles() + m_Local.m_vecPunchAngle + m_vecAutoAim, &forward ); + } + + params.m_vecAutoAimDir = forward; +} + +//----------------------------------------------------------------------------- +// Targets represent themselves to autoaim as a viewplane-parallel disc with +// a radius specified by the target. The player then modifies this radius +// to achieve more or less aggressive aiming assistance +//----------------------------------------------------------------------------- +float CBasePlayer::GetAutoaimScore( const Vector &eyePosition, const Vector &viewDir, const Vector &vecTarget, CBaseEntity *pTarget, float fScale ) +{ + float radiusSqr; + float targetRadiusSqr = Square( (pTarget->GetAutoAimRadius() * fScale) ); + + Vector vecNearestPoint = PointOnLineNearestPoint( eyePosition, eyePosition + viewDir * 8192, vecTarget ); + Vector vecDiff = vecTarget - vecNearestPoint; + + radiusSqr = vecDiff.LengthSqr(); + + if( radiusSqr <= targetRadiusSqr ) + { + float score; + + score = 1.0f - (radiusSqr / targetRadiusSqr); + + Assert( score >= 0.0f && score <= 1.0f ); + return score; + } + + // 0 means no score- doesn't qualify for autoaim. + return 0.0f; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : &vecSrc - +// flDist - +// flDelta - +// Output : Vector +//----------------------------------------------------------------------------- +QAngle CBasePlayer::AutoaimDeflection( Vector &vecSrc, autoaim_params_t ¶ms ) +{ + float bestscore; + float score; + QAngle eyeAngles; + Vector bestdir; + CBaseEntity *bestent; + trace_t tr; + Vector v_forward, v_right, v_up; + + if ( ShouldAutoaim() == false ) + { + m_fOnTarget = false; + return vec3_angle; + } + + eyeAngles = EyeAngles(); + AngleVectors( eyeAngles + m_Local.m_vecPunchAngle + m_vecAutoAim, &v_forward, &v_right, &v_up ); + + // try all possible entities + bestdir = v_forward; + bestscore = 0.0f; + bestent = NULL; + + //Reset this data + m_fOnTarget = false; + params.m_bOnTargetNatural = false; + + CBaseEntity *pIgnore = NULL; + + if( IsInAVehicle() ) + { + pIgnore = GetVehicleEntity(); + } + + CTraceFilterSkipTwoEntities traceFilter( this, pIgnore, COLLISION_GROUP_NONE ); + + UTIL_TraceLine( vecSrc, vecSrc + bestdir * MAX_COORD_FLOAT, MASK_SHOT, &traceFilter, &tr ); + + CBaseEntity *pEntHit = tr.m_pEnt; + + if ( pEntHit && pEntHit->m_takedamage != DAMAGE_NO && pEntHit->GetHealth() > 0 ) + { + // don't look through water + if (!((GetWaterLevel() != 3 && pEntHit->GetWaterLevel() == 3) || (GetWaterLevel() == 3 && pEntHit->GetWaterLevel() == 0))) + { + if( pEntHit->GetFlags() & FL_AIMTARGET ) + { + bool bAimAtThis = true; + + if( UseXboxAiming() && pEntHit->IsNPC() ) + { + int iRelationType = GetDefaultRelationshipDisposition( pEntHit->Classify() ); + + if( iRelationType != D_HT ) + { + bAimAtThis = false; + } + } + + if( bAimAtThis ) + { + if ( pEntHit->GetFlags() & FL_AIMTARGET ) + { + m_fOnTarget = true; + } + + // Player is already on target naturally, don't autoaim. + // Fill out the autoaim_params_t struct, though. + params.m_hAutoAimEntity.Set(pEntHit); + params.m_vecAutoAimDir = bestdir; + params.m_vecAutoAimPoint = tr.endpos; + params.m_bAutoAimAssisting = false; + params.m_bOnTargetNatural = true; + } + } + + return vec3_angle; + } + } + + int count = AimTarget_ListCount(); + if ( count ) + { + CBaseEntity **pList = (CBaseEntity **)stackalloc( sizeof(CBaseEntity *) * count ); + AimTarget_ListCopy( pList, count ); + + for ( int i = 0; i < count; i++ ) + { + Vector center; + Vector dir; + CBaseEntity *pEntity = pList[i]; + + // Don't shoot yourself + if ( pEntity == this ) + continue; + + if (!pEntity->IsAlive() || !pEntity->edict() ) + continue; + + if ( !g_pGameRules->ShouldAutoAim( this, pEntity->edict() ) ) + continue; + + // don't look through water + if ((GetWaterLevel() != 3 && pEntity->GetWaterLevel() == 3) || (GetWaterLevel() == 3 && pEntity->GetWaterLevel() == 0)) + continue; + + if( pEntity->MyNPCPointer() ) + { + // If this entity is an NPC, only aim if it is an enemy. + if ( IRelationType( pEntity ) != D_HT && !pEntity->ShouldAttractAutoAim(this) ) + { + if ( !pEntity->IsPlayer() && !g_pGameRules->IsDeathmatch()) + // Msg( "friend\n"); + continue; + } + } + + // Don't autoaim at the noisy bodytarget, this makes the autoaim crosshair wobble. + //center = pEntity->BodyTarget( vecSrc, false ); + center = pEntity->WorldSpaceCenter(); + + dir = (center - vecSrc); + + float dist = dir.Length2D(); + VectorNormalize( dir ); + + // Skip if out of range. + if( dist > params.m_fMaxDist ) + continue; + + float dot = DotProduct (dir, v_forward ); + + // make sure it's in front of the player + if( dot < 0 ) + continue; + + if( !(pEntity->GetFlags() & FL_FLY) ) + { + // Refuse to take wild shots at targets far from reticle. + if( GetActiveWeapon() != NULL && dot < GetActiveWeapon()->GetMaxAutoAimDeflection() ) + { + continue; + } + } + + score = GetAutoaimScore(vecSrc, v_forward, pEntity->GetAutoAimCenter(), pEntity, params.m_fScale); + + if( score <= bestscore ) + { + continue; + } + + UTIL_TraceLine( vecSrc, center, MASK_SHOT, &traceFilter, &tr ); + + if (tr.fraction != 1.0 && tr.m_pEnt != pEntity ) + { + // Msg( "hit %s, can't see %s\n", STRING( tr.u.ent->classname ), STRING( pEdict->classname ) ); + continue; + } + + // This is the best candidate so far. + bestscore = score; + bestent = pEntity; + bestdir = dir; + } + if ( bestent ) + { + QAngle bestang; + + VectorAngles( bestdir, bestang ); + + if( IsInAVehicle() ) + { + bestang -= EyeAngles(); + } + else + { + bestang -= EyeAngles() - m_Local.m_vecPunchAngle; + } + + m_fOnTarget = true; + + // Autoaim detected a target for us. Aim automatically at its bodytarget. + params.m_hAutoAimEntity.Set(bestent); + params.m_vecAutoAimDir = bestdir; + params.m_vecAutoAimPoint = bestent->BodyTarget( vecSrc, false ); + params.m_bAutoAimAssisting = true; + + return bestang; + } + } + + return QAngle( 0, 0, 0 ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBasePlayer::ResetAutoaim( void ) +{ + if (m_vecAutoAim.x != 0 || m_vecAutoAim.y != 0) + { + m_vecAutoAim = QAngle( 0, 0, 0 ); + engine->CrosshairAngle( edict(), 0, 0 ); + } + m_fOnTarget = false; +} + +// ========================================================================== +// > Weapon stuff +// ========================================================================== + +//----------------------------------------------------------------------------- +// Purpose: Override base class, player can always use weapon +// Input : A weapon +// Output : true or false +//----------------------------------------------------------------------------- +bool CBasePlayer::Weapon_CanUse( CBaseCombatWeapon *pWeapon ) +{ + return true; +} + + + +//----------------------------------------------------------------------------- +// Purpose: Override to clear dropped weapon from the hud +//----------------------------------------------------------------------------- +void CBasePlayer::Weapon_Drop( CBaseCombatWeapon *pWeapon, const Vector *pvecTarget /* = NULL */, const Vector *pVelocity /* = NULL */ ) +{ + bool bWasActiveWeapon = false; + if ( pWeapon == GetActiveWeapon() ) + { + bWasActiveWeapon = true; + } + + if ( pWeapon ) + { + if ( bWasActiveWeapon ) + { + pWeapon->SendWeaponAnim( ACT_VM_IDLE ); + } + } + + BaseClass::Weapon_Drop( pWeapon, pvecTarget, pVelocity ); + + if ( bWasActiveWeapon ) + { + if (!SwitchToNextBestWeapon( NULL )) + { + CBaseViewModel *vm = GetViewModel(); + if ( vm ) + { + vm->AddEffects( EF_NODRAW ); + } + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : weaponSlot - +//----------------------------------------------------------------------------- +void CBasePlayer::Weapon_DropSlot( int weaponSlot ) +{ + CBaseCombatWeapon *pWeapon; + + // Check for that slot being occupied already + for ( int i=0; i < MAX_WEAPONS; i++ ) + { + pWeapon = GetWeapon( i ); + + if ( pWeapon != NULL ) + { + // If the slots match, it's already occupied + if ( pWeapon->GetSlot() == weaponSlot ) + { + Weapon_Drop( pWeapon, NULL, NULL ); + } + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Override to add weapon to the hud +//----------------------------------------------------------------------------- +void CBasePlayer::Weapon_Equip( CBaseCombatWeapon *pWeapon ) +{ + BaseClass::Weapon_Equip( pWeapon ); + + bool bShouldSwitch = g_pGameRules->FShouldSwitchWeapon( this, pWeapon ); + +#ifdef HL2_DLL + if ( bShouldSwitch == false && PhysCannonGetHeldEntity( GetActiveWeapon() ) == pWeapon && + Weapon_OwnsThisType( pWeapon->GetClassname(), pWeapon->GetSubType()) ) + { + bShouldSwitch = true; + } +#endif//HL2_DLL + + // should we switch to this item? + if ( bShouldSwitch ) + { + Weapon_Switch( pWeapon ); + } +} + + +//========================================================= +// HasNamedPlayerItem Does the player already have this item? +//========================================================= +CBaseEntity *CBasePlayer::HasNamedPlayerItem( const char *pszItemName ) +{ + for ( int i = 0 ; i < WeaponCount() ; i++ ) + { + if ( !GetWeapon(i) ) + continue; + + if ( FStrEq( pszItemName, GetWeapon(i)->GetClassname() ) ) + { + return GetWeapon(i); + } + } + + return NULL; +} + + + +//================================================================================ +// TEAM HANDLING +//================================================================================ +//----------------------------------------------------------------------------- +// Purpose: Put the player in the specified team +//----------------------------------------------------------------------------- + +void CBasePlayer::ChangeTeam( int iTeamNum ) +{ + if ( !GetGlobalTeam( iTeamNum ) ) + { + Warning( "CBasePlayer::ChangeTeam( %d ) - invalid team index.\n", iTeamNum ); + return; + } + + // if this is our current team, just abort + if ( iTeamNum == GetTeamNumber() ) + { + return; + } + + // Immediately tell all clients that he's changing team. This has to be done + // first, so that all user messages that follow as a result of the team change + // come after this one, allowing the client to be prepared for them. + IGameEvent * event = gameeventmanager->CreateEvent( "player_team" ); + if ( event ) + { + event->SetInt("userid", GetUserID() ); + event->SetInt("team", iTeamNum ); + event->SetInt("oldteam", GetTeamNumber() ); + event->SetInt("disconnect", IsDisconnecting()); + + gameeventmanager->FireEvent( event ); + } + + // Remove him from his current team + if ( GetTeam() ) + { + GetTeam()->RemovePlayer( this ); + } + + // Are we being added to a team? + if ( iTeamNum ) + { + GetGlobalTeam( iTeamNum )->AddPlayer( this ); + } + + BaseClass::ChangeTeam( iTeamNum ); +} + + + +//----------------------------------------------------------------------------- +// Purpose: Locks a player to the spot; they can't move, shoot, or be hurt +//----------------------------------------------------------------------------- +void CBasePlayer::LockPlayerInPlace( void ) +{ + if ( m_iPlayerLocked ) + return; + + AddFlag( FL_GODMODE | FL_FROZEN ); + SetMoveType( MOVETYPE_NONE ); + m_iPlayerLocked = true; + + // force a client data update, so that anything that has been done to + // this player previously this frame won't get delayed in being sent + UpdateClientData(); +} + +//----------------------------------------------------------------------------- +// Purpose: Unlocks a previously locked player +//----------------------------------------------------------------------------- +void CBasePlayer::UnlockPlayer( void ) +{ + if ( !m_iPlayerLocked ) + return; + + RemoveFlag( FL_GODMODE | FL_FROZEN ); + SetMoveType( MOVETYPE_WALK ); + m_iPlayerLocked = false; +} + +bool CBasePlayer::ClearUseEntity() +{ + if ( m_hUseEntity != NULL ) + { + // Stop controlling the train/object + // TODO: Send HUD Update + m_hUseEntity->Use( this, this, USE_OFF, 0 ); + m_hUseEntity = NULL; + return true; + } + + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBasePlayer::HideViewModels( void ) +{ + for ( int i = 0 ; i < MAX_VIEWMODELS; i++ ) + { + CBaseViewModel *vm = GetViewModel( i ); + if ( !vm ) + continue; + + vm->SetWeaponModel( NULL, NULL ); + } +} + +class CStripWeapons : public CPointEntity +{ + DECLARE_CLASS( CStripWeapons, CPointEntity ); +public: + void InputStripWeapons(inputdata_t &data); + void InputStripWeaponsAndSuit(inputdata_t &data); + + void StripWeapons(inputdata_t &data, bool stripSuit); + DECLARE_DATADESC(); +}; + +LINK_ENTITY_TO_CLASS( player_weaponstrip, CStripWeapons ); + +BEGIN_DATADESC( CStripWeapons ) + DEFINE_INPUTFUNC( FIELD_VOID, "Strip", InputStripWeapons ), + DEFINE_INPUTFUNC( FIELD_VOID, "StripWeaponsAndSuit", InputStripWeaponsAndSuit ), +END_DATADESC() + + +void CStripWeapons::InputStripWeapons(inputdata_t &data) +{ + StripWeapons(data, false); +} + +void CStripWeapons::InputStripWeaponsAndSuit(inputdata_t &data) +{ + StripWeapons(data, true); +} + +void CStripWeapons::StripWeapons(inputdata_t &data, bool stripSuit) +{ + CBasePlayer *pPlayer = NULL; + + if ( data.pActivator && data.pActivator->IsPlayer() ) + { + pPlayer = (CBasePlayer *)data.pActivator; + } + else if ( !g_pGameRules->IsDeathmatch() ) + { + pPlayer = UTIL_GetLocalPlayer(); + } + + if ( pPlayer ) + { + pPlayer->RemoveAllItems( stripSuit ); + } +} + + +class CRevertSaved : public CPointEntity +{ + DECLARE_CLASS( CRevertSaved, CPointEntity ); +public: + void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); + void LoadThink( void ); + + DECLARE_DATADESC(); + + inline float Duration( void ) { return m_Duration; } + inline float HoldTime( void ) { return m_HoldTime; } + inline float LoadTime( void ) { return m_loadTime; } + + inline void SetDuration( float duration ) { m_Duration = duration; } + inline void SetHoldTime( float hold ) { m_HoldTime = hold; } + inline void SetLoadTime( float time ) { m_loadTime = time; } + + //Inputs + void InputReload(inputdata_t &data); + +#ifdef HL1_DLL + void MessageThink( void ); + inline float MessageTime( void ) { return m_messageTime; } + inline void SetMessageTime( float time ) { m_messageTime = time; } +#endif + +private: + + float m_loadTime; + float m_Duration; + float m_HoldTime; + +#ifdef HL1_DLL + string_t m_iszMessage; + float m_messageTime; +#endif +}; + +LINK_ENTITY_TO_CLASS( player_loadsaved, CRevertSaved ); + +BEGIN_DATADESC( CRevertSaved ) + +#ifdef HL1_DLL + DEFINE_KEYFIELD( m_iszMessage, FIELD_STRING, "message" ), + DEFINE_KEYFIELD( m_messageTime, FIELD_FLOAT, "messagetime" ), // These are not actual times, but durations, so save as floats + + DEFINE_FUNCTION( MessageThink ), +#endif + + DEFINE_KEYFIELD( m_loadTime, FIELD_FLOAT, "loadtime" ), + DEFINE_KEYFIELD( m_Duration, FIELD_FLOAT, "duration" ), + DEFINE_KEYFIELD( m_HoldTime, FIELD_FLOAT, "holdtime" ), + + DEFINE_INPUTFUNC( FIELD_VOID, "Reload", InputReload ), + + + // Function Pointers + DEFINE_FUNCTION( LoadThink ), + +END_DATADESC() + +CBaseEntity *CreatePlayerLoadSave( Vector vOrigin, float flDuration, float flHoldTime, float flLoadTime ) +{ + CRevertSaved *pRevertSaved = (CRevertSaved *) CreateEntityByName( "player_loadsaved" ); + + if ( pRevertSaved == NULL ) + return NULL; + + UTIL_SetOrigin( pRevertSaved, vOrigin ); + + pRevertSaved->Spawn(); + pRevertSaved->SetDuration( flDuration ); + pRevertSaved->SetHoldTime( flHoldTime ); + pRevertSaved->SetLoadTime( flLoadTime ); + + return pRevertSaved; +} + + + +void CRevertSaved::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) +{ + UTIL_ScreenFadeAll( m_clrRender, Duration(), HoldTime(), FFADE_OUT ); + SetNextThink( gpGlobals->curtime + LoadTime() ); + SetThink( &CRevertSaved::LoadThink ); + + CBasePlayer *pPlayer = UTIL_GetLocalPlayer(); + + if ( pPlayer ) + { + //Adrian: Setting this flag so we can't move or save a game. + pPlayer->pl.deadflag = true; + pPlayer->AddFlag( (FL_NOTARGET|FL_FROZEN) ); + } +} + +void CRevertSaved::InputReload( inputdata_t &inputdata ) +{ + UTIL_ScreenFadeAll( m_clrRender, Duration(), HoldTime(), FFADE_OUT ); + +#ifdef HL1_DLL + SetNextThink( gpGlobals->curtime + MessageTime() ); + SetThink( &CRevertSaved::MessageThink ); +#else + SetNextThink( gpGlobals->curtime + LoadTime() ); + SetThink( &CRevertSaved::LoadThink ); +#endif + + CBasePlayer *pPlayer = UTIL_GetLocalPlayer(); + + if ( pPlayer ) + { + //Adrian: Setting this flag so we can't move or save a game. + pPlayer->pl.deadflag = true; + pPlayer->AddFlag( (FL_NOTARGET|FL_FROZEN) ); + } +} + +#ifdef HL1_DLL +void CRevertSaved::MessageThink( void ) +{ + UTIL_ShowMessageAll( STRING( m_iszMessage ) ); + float nextThink = LoadTime() - MessageTime(); + if ( nextThink > 0 ) + { + SetNextThink( gpGlobals->curtime + nextThink ); + SetThink( &CRevertSaved::LoadThink ); + } + else + LoadThink(); +} +#endif + + +void CRevertSaved::LoadThink( void ) +{ + if ( !gpGlobals->deathmatch ) + { + engine->ServerCommand("reload\n"); + } +} + +class CMovementSpeedMod : public CPointEntity +{ + DECLARE_CLASS( CMovementSpeedMod, CPointEntity ); +public: + void InputSpeedMod(inputdata_t &data); + + DECLARE_DATADESC(); +}; + +LINK_ENTITY_TO_CLASS( player_speedmod, CMovementSpeedMod ); + +BEGIN_DATADESC( CMovementSpeedMod ) + DEFINE_INPUTFUNC( FIELD_FLOAT, "ModifySpeed", InputSpeedMod ), +END_DATADESC() + + +void CMovementSpeedMod::InputSpeedMod(inputdata_t &data) +{ + CBasePlayer *pPlayer = NULL; + + if ( data.pActivator && data.pActivator->IsPlayer() ) + { + pPlayer = (CBasePlayer *)data.pActivator; + } + else if ( !g_pGameRules->IsDeathmatch() ) + { + pPlayer = UTIL_GetLocalPlayer(); + } + + if ( pPlayer ) + { + pPlayer->SetLaggedMovementValue( data.value.Float() ); + } +} + + +void SendProxy_CropFlagsToPlayerFlagBitsLength( const SendProp *pProp, const void *pStruct, const void *pVarData, DVariant *pOut, int iElement, int objectID) +{ + int mask = (1<m_Int = ( data & mask ); +} +// -------------------------------------------------------------------------------- // +// SendTable for CPlayerState. +// -------------------------------------------------------------------------------- // + + BEGIN_SEND_TABLE_NOBASE(CPlayerState, DT_PlayerState) + SendPropInt (SENDINFO(deadflag), 1, SPROP_UNSIGNED ), + END_SEND_TABLE() + +// -------------------------------------------------------------------------------- // +// This data only gets sent to clients that ARE this player entity. +// -------------------------------------------------------------------------------- // + + BEGIN_SEND_TABLE_NOBASE( CBasePlayer, DT_LocalPlayerExclusive ) + + SendPropDataTable ( SENDINFO_DT(m_Local), &REFERENCE_SEND_TABLE(DT_Local) ), + +// If HL2_DLL is defined, then baseflex.cpp already sends these. +#ifndef HL2_DLL + SendPropFloat ( SENDINFO_VECTORELEM(m_vecViewOffset, 0), 8, SPROP_ROUNDDOWN, -32.0, 32.0f), + SendPropFloat ( SENDINFO_VECTORELEM(m_vecViewOffset, 1), 8, SPROP_ROUNDDOWN, -32.0, 32.0f), + SendPropFloat ( SENDINFO_VECTORELEM(m_vecViewOffset, 2), 10, SPROP_CHANGES_OFTEN, 0.0f, 128.0f), +#endif + + SendPropFloat ( SENDINFO(m_flFriction), 8, SPROP_ROUNDDOWN, 0.0f, 4.0f), + + SendPropArray3 ( SENDINFO_ARRAY3(m_iAmmo), SendPropInt( SENDINFO_ARRAY(m_iAmmo), 10, SPROP_UNSIGNED ) ), + + SendPropInt ( SENDINFO( m_fOnTarget ), 2, SPROP_UNSIGNED ), + + SendPropInt ( SENDINFO( m_nTickBase ), -1, SPROP_CHANGES_OFTEN ), + SendPropInt ( SENDINFO( m_nNextThinkTick ) ), + + SendPropEHandle ( SENDINFO( m_hLastWeapon ) ), + SendPropEHandle ( SENDINFO( m_hGroundEntity ), SPROP_CHANGES_OFTEN ), + + SendPropFloat ( SENDINFO_VECTORELEM(m_vecVelocity, 0), 20, SPROP_CHANGES_OFTEN, -2048.0f, 2048.0f ), + SendPropFloat ( SENDINFO_VECTORELEM(m_vecVelocity, 1), 20, SPROP_CHANGES_OFTEN, -2048.0f, 2048.0f ), + SendPropFloat ( SENDINFO_VECTORELEM(m_vecVelocity, 2), 16, SPROP_CHANGES_OFTEN, -2048.0f, 2048.0f ), + + SendPropVector ( SENDINFO( m_vecBaseVelocity ), 20, 0, -1000, 1000 ), + + SendPropEHandle ( SENDINFO( m_hConstraintEntity)), + SendPropVector ( SENDINFO( m_vecConstraintCenter), 0, SPROP_NOSCALE ), + SendPropFloat ( SENDINFO( m_flConstraintRadius ), 0, SPROP_NOSCALE ), + SendPropFloat ( SENDINFO( m_flConstraintWidth ), 0, SPROP_NOSCALE ), + SendPropFloat ( SENDINFO( m_flConstraintSpeedFactor ), 0, SPROP_NOSCALE ), + + SendPropFloat ( SENDINFO( m_flDeathTime ), 0, SPROP_NOSCALE ), + + SendPropInt ( SENDINFO( m_nWaterLevel ), 2, SPROP_UNSIGNED ), + SendPropFloat ( SENDINFO( m_flLaggedMovementValue ), 0, SPROP_NOSCALE ), + + END_SEND_TABLE() + + +// -------------------------------------------------------------------------------- // +// DT_BasePlayer sendtable. +// -------------------------------------------------------------------------------- // + + IMPLEMENT_SERVERCLASS_ST( CBasePlayer, DT_BasePlayer ) + + SendPropDataTable(SENDINFO_DT(pl), &REFERENCE_SEND_TABLE(DT_PlayerState), SendProxy_DataTableToDataTable), + SendPropEHandle(SENDINFO(m_hVehicle)), + SendPropEHandle(SENDINFO(m_hUseEntity)), + SendPropInt (SENDINFO(m_iHealth), 10 ), + SendPropInt (SENDINFO(m_lifeState), 3, SPROP_UNSIGNED ), + SendPropFloat (SENDINFO(m_flMaxspeed), 12, SPROP_ROUNDDOWN, 0.0f, 2048.0f ), // CL + SendPropInt (SENDINFO(m_fFlags), PLAYER_FLAG_BITS, SPROP_UNSIGNED|SPROP_CHANGES_OFTEN, SendProxy_CropFlagsToPlayerFlagBitsLength ), + SendPropInt (SENDINFO(m_iObserverMode), 3, SPROP_UNSIGNED ), + SendPropEHandle (SENDINFO(m_hObserverTarget) ), + SendPropInt (SENDINFO(m_iFOV), 8, SPROP_UNSIGNED ), + SendPropInt (SENDINFO(m_iDefaultFOV), 8, SPROP_UNSIGNED ), + SendPropArray ( SendPropEHandle( SENDINFO_ARRAY( m_hViewModel ) ), m_hViewModel ), + SendPropString (SENDINFO(m_szLastPlaceName) ), + + // Data that only gets sent to the local player. + SendPropDataTable( "localdata", 0, &REFERENCE_SEND_TABLE(DT_LocalPlayerExclusive), SendProxy_SendLocalDataTable ), + + END_SEND_TABLE() + +//============================================================================= +// +// Player Physics Shadow Code +// + +void CBasePlayer::SetupVPhysicsShadow( CPhysCollide *pStandModel, const char *pStandHullName, CPhysCollide *pCrouchModel, const char *pCrouchHullName ) +{ + solid_t solid; + Q_strncpy( solid.surfaceprop, "player", sizeof(solid.surfaceprop) ); + solid.params = g_PhysDefaultObjectParams; + solid.params.mass = 85.0f; + solid.params.inertia = 1e24f; + solid.params.enableCollisions = false; + //disable drag + solid.params.dragCoefficient = 0; + // create standing hull + m_pShadowStand = PhysModelCreateCustom( this, pStandModel, GetLocalOrigin(), GetLocalAngles(), pStandHullName, false, &solid ); + m_pShadowStand->SetCallbackFlags( CALLBACK_GLOBAL_COLLISION | CALLBACK_SHADOW_COLLISION ); + + // create crouchig hull + m_pShadowCrouch = PhysModelCreateCustom( this, pCrouchModel, GetLocalOrigin(), GetLocalAngles(), pCrouchHullName, false, &solid ); + m_pShadowCrouch->SetCallbackFlags( CALLBACK_GLOBAL_COLLISION | CALLBACK_SHADOW_COLLISION ); + + // default to stand + VPhysicsSetObject( m_pShadowStand ); + + // tell physics lists I'm a shadow controller object + PhysAddShadow( this ); + m_pPhysicsController = physenv->CreatePlayerController( m_pShadowStand ); + m_pPhysicsController->SetPushMassLimit( 350.0f ); + m_pPhysicsController->SetPushSpeedLimit( 50.0f ); + + // Give the controller a valid position so it doesn't do anything rash. + UpdatePhysicsShadowToCurrentPosition(); + + // init state + if ( GetFlags() & FL_DUCKING ) + { + SetVCollisionState( VPHYS_CROUCH ); + } + else + { + SetVCollisionState( VPHYS_WALK ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Empty, just want to keep the baseentity version from being called +// current so we don't kick up dust, etc. +//----------------------------------------------------------------------------- +void CBasePlayer::VPhysicsCollision( int index, gamevcollisionevent_t *pEvent ) +{ +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBasePlayer::VPhysicsUpdate( IPhysicsObject *pPhysics ) +{ + float savedImpact = m_impactEnergyScale; + + // HACKHACK: Reduce player's stress by 1/8th + m_impactEnergyScale *= 0.125f; + ApplyStressDamage( pPhysics, true ); + m_impactEnergyScale = savedImpact; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBasePlayer::VPhysicsShadowUpdate( IPhysicsObject *pPhysics ) +{ + if ( sv_turbophysics.GetBool() ) + return; + + Vector newPosition; + + bool physicsUpdated = m_pPhysicsController->GetShadowPosition( &newPosition, NULL ) > 0 ? true : false; + + // UNDONE: If the player is penetrating, but the player's game collisions are not stuck, teleport the physics shadow to the game position + if ( pPhysics->GetGameFlags() & FVPHYSICS_PENETRATING ) + { + CUtlVector list; + PhysGetListOfPenetratingEntities( this, list ); + for ( int i = list.Count()-1; i >= 0; --i ) + { + // filter out anything that isn't simulated by vphysics + // UNDONE: Filter out motion disabled objects? + if ( list[i]->GetMoveType() == MOVETYPE_VPHYSICS ) + { + // I'm currently stuck inside a moving object, so allow vphysics to + // apply velocity to the player in order to separate these objects + m_touchedPhysObject = true; + } + + // if it's an NPC, tell them that the player is intersecting them + CAI_BaseNPC *pNPC = list[i]->MyNPCPointer(); + if ( pNPC ) + { + pNPC->PlayerPenetratingVPhysics(); + } + } + } + + if ( m_pPhysicsController->IsInContact() || (m_afPhysicsFlags & PFLAG_VPHYSICS_MOTIONCONTROLLER) ) + { + m_touchedPhysObject = true; + } + + if ( IsFollowingPhysics() ) + { + m_touchedPhysObject = true; + } + + if ( GetMoveType() == MOVETYPE_NOCLIP ) + { + m_oldOrigin = GetAbsOrigin(); + return; + } + + if ( phys_timescale.GetFloat() == 0.0f ) + { + physicsUpdated = false; + } + + if ( !physicsUpdated ) + return; + + IPhysicsObject *pPhysGround = GetGroundVPhysics(); + + Vector newVelocity; + pPhysics->GetPosition( &newPosition, 0 ); + m_pPhysicsController->GetShadowVelocity( &newVelocity ); + + if ( physicsshadowupdate_render.GetBool() ) + { + NDebugOverlay::Box( GetAbsOrigin(), WorldAlignMins(), WorldAlignMaxs(), 255, 0, 0, 24, 15.0f ); + NDebugOverlay::Box( newPosition, WorldAlignMins(), WorldAlignMaxs(), 0,0,255, 24, 15.0f); + // NDebugOverlay::Box( newPosition, WorldAlignMins(), WorldAlignMaxs(), 0,0,255, 24, .01f); + } + + Vector tmp = GetAbsOrigin() - newPosition; + if ( !m_touchedPhysObject && !(GetFlags() & FL_ONGROUND) ) + { + tmp.z *= 0.5f; // don't care about z delta as much + } + + float dist = tmp.LengthSqr(); + float deltaV = (newVelocity - GetAbsVelocity()).LengthSqr(); + + float maxDistErrorSqr = VPHYS_MAX_DISTSQR; + float maxVelErrorSqr = VPHYS_MAX_VELSQR; + if ( IsRideablePhysics(pPhysGround) ) + { + maxDistErrorSqr *= 0.25; + maxVelErrorSqr *= 0.25; + } + + if ( dist >= maxDistErrorSqr || deltaV >= maxVelErrorSqr || (pPhysGround && !m_touchedPhysObject) ) + { + if ( m_touchedPhysObject || pPhysGround ) + { + // BUGBUG: Rewrite this code using fixed timestep + if ( deltaV >= maxVelErrorSqr ) + { + Vector dir = GetAbsVelocity(); + float len = VectorNormalize(dir); + float dot = DotProduct( newVelocity, dir ); + if ( dot > len ) + { + dot = len; + } + else if ( dot < -len ) + { + dot = -len; + } + + VectorMA( newVelocity, -dot, dir, newVelocity ); + + if ( m_afPhysicsFlags & PFLAG_VPHYSICS_MOTIONCONTROLLER ) + { + float val = Lerp( 0.1f, len, dot ); + VectorMA( newVelocity, val - len, dir, newVelocity ); + } + + if ( !IsRideablePhysics(pPhysGround) ) + { + if ( !(m_afPhysicsFlags & PFLAG_VPHYSICS_MOTIONCONTROLLER ) && IsSimulatingOnAlternateTicks() ) + { + newVelocity *= 0.5f; + } + ApplyAbsVelocityImpulse( newVelocity ); + } + } + + trace_t trace; + UTIL_TraceEntity( this, newPosition, newPosition, MASK_PLAYERSOLID, this, COLLISION_GROUP_PLAYER_MOVEMENT, &trace ); + if ( !trace.allsolid && !trace.startsolid ) + { + SetAbsOrigin( newPosition ); + } + } + else + { + trace_t trace; + UTIL_TraceEntity( this, GetAbsOrigin(), GetAbsOrigin(), MASK_PLAYERSOLID, this, COLLISION_GROUP_PLAYER_MOVEMENT, &trace ); + + // current position is not ok, fixup + if ( trace.allsolid || trace.startsolid ) + { + // STUCK!?!?! + //Warning( "Stuck2 on %s!!\n", trace.m_pEnt->GetClassname() ); + SetAbsOrigin( newPosition ); + } + } + } + else + { + if ( m_touchedPhysObject ) + { + // check my position (physics object could have simulated into my position + // physics is not very far away, check my position + trace_t trace; + UTIL_TraceEntity( this, GetAbsOrigin(), GetAbsOrigin(), + MASK_PLAYERSOLID, this, COLLISION_GROUP_PLAYER_MOVEMENT, &trace ); + + // is current position ok? + if ( trace.allsolid || trace.startsolid ) + { + // stuck????!?!? + //Msg("Stuck on %s\n", trace.m_pEnt->GetClassName()); + SetAbsOrigin( newPosition ); + UTIL_TraceEntity( this, GetAbsOrigin(), GetAbsOrigin(), + MASK_PLAYERSOLID, this, COLLISION_GROUP_PLAYER_MOVEMENT, &trace ); + if ( trace.allsolid || trace.startsolid ) + { + //Msg("Double Stuck\n"); + SetAbsOrigin( m_oldOrigin ); + } + } + } + } + m_oldOrigin = GetAbsOrigin(); + // UNDONE: Force physics object to be at player position when not touching phys??? +} + +// recreate physics on save/load, don't try to save the state! +bool CBasePlayer::ShouldSavePhysics() +{ + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBasePlayer::InitVCollision( void ) +{ + // Cleanup any old vphysics stuff. + VPhysicsDestroyObject(); + + // in turbo physics players dont have a physics shadow + if ( sv_turbophysics.GetBool() ) + return; + + CPhysCollide *pModel = PhysCreateBbox( VEC_HULL_MIN, VEC_HULL_MAX ); + CPhysCollide *pCrouchModel = PhysCreateBbox( VEC_DUCK_HULL_MIN, VEC_DUCK_HULL_MAX ); + + SetupVPhysicsShadow( pModel, "player_stand", pCrouchModel, "player_crouch" ); +} + + +void CBasePlayer::VPhysicsDestroyObject() +{ + // Since CBasePlayer aliases its pointer to the physics object, tell CBaseEntity to + // clear out its physics object pointer so we don't wind up deleting one of + // the aliased objects twice. + VPhysicsSetObject( NULL ); + + PhysRemoveShadow( this ); + + if ( m_pPhysicsController ) + { + physenv->DestroyPlayerController( m_pPhysicsController ); + m_pPhysicsController = NULL; + } + + if ( m_pShadowStand ) + { + PhysDestroyObject( m_pShadowStand ); + m_pShadowStand = NULL; + } + if ( m_pShadowCrouch ) + { + PhysDestroyObject( m_pShadowCrouch ); + m_pShadowCrouch = NULL; + } + + BaseClass::VPhysicsDestroyObject(); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBasePlayer::SetVCollisionState( int collisionState ) +{ + Vector vel = vec3_origin; + Vector pos = vec3_origin; + vel = GetAbsVelocity(); + pos = GetAbsOrigin(); + + m_vphysicsCollisionState = collisionState; + switch( collisionState ) + { + case VPHYS_WALK: + m_pShadowStand->SetPosition( pos, vec3_angle, true ); + m_pShadowStand->SetVelocity( &vel, NULL ); + m_pShadowCrouch->EnableCollisions( false ); + m_pPhysicsController->SetObject( m_pShadowStand ); + VPhysicsSwapObject( m_pShadowStand ); + m_pShadowStand->EnableCollisions( true ); + break; + + case VPHYS_CROUCH: + m_pShadowCrouch->SetPosition( pos, vec3_angle, true ); + m_pShadowCrouch->SetVelocity( &vel, NULL ); + m_pShadowStand->EnableCollisions( false ); + m_pPhysicsController->SetObject( m_pShadowCrouch ); + VPhysicsSwapObject( m_pShadowCrouch ); + m_pShadowCrouch->EnableCollisions( true ); + break; + + case VPHYS_NOCLIP: + m_pShadowCrouch->EnableCollisions( false ); + m_pShadowStand->EnableCollisions( false ); + break; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : int +//----------------------------------------------------------------------------- +int CBasePlayer::GetFOV( void ) const +{ + if ( m_iFOV == 0 ) + return GetDefaultFOV(); + + return m_iFOV; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : FOV - +// zoomRate - +//----------------------------------------------------------------------------- +bool CBasePlayer::SetFOV( CBaseEntity *pRequester, int FOV, float zoomRate ) +{ + //NOTENOTE: You MUST specify who is requesting the zoom change + assert( pRequester != NULL ); + if ( pRequester == NULL ) + return false; + + // If we already have an owner, we only allow requests from that owner + if ( ( m_hZoomOwner != NULL ) && ( m_hZoomOwner != pRequester ) ) + { + if ( CanOverrideEnvZoomOwner( m_hZoomOwner ) == false ) + return false; + } + else + { + //FIXME: Maybe do this is as an accessor instead + if ( FOV == 0 ) + { + m_hZoomOwner = NULL; + } + else + { + m_hZoomOwner = pRequester; + } + } + + m_iFOV = FOV; + + m_Local.m_flFOVRate = zoomRate; + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: Sets the default FOV for the player if nothing else is going on +// Input : FOV - the new base FOV for this player +//----------------------------------------------------------------------------- +void CBasePlayer::SetDefaultFOV( int FOV ) +{ + m_iDefaultFOV = ( FOV == 0 ) ? g_pGameRules->DefaultFOV() : FOV; +} + +//----------------------------------------------------------------------------- +// Purpose: // static func +// Input : set - +//----------------------------------------------------------------------------- +void CBasePlayer::ModifyOrAppendPlayerCriteria( AI_CriteriaSet& set ) +{ + // Append our health + set.AppendCriteria( "playerhealth", UTIL_VarArgs( "%i", GetHealth() ) ); + float healthfrac = 0.0f; + if ( GetMaxHealth() > 0 ) + { + healthfrac = (float)GetHealth() / (float)GetMaxHealth(); + } + + set.AppendCriteria( "playerhealthfrac", UTIL_VarArgs( "%.3f", healthfrac ) ); + + CBaseCombatWeapon *weapon = GetActiveWeapon(); + if ( weapon ) + { + set.AppendCriteria( "playerweapon", weapon->GetClassname() ); + } + else + { + set.AppendCriteria( "playerweapon", "none" ); + } + + // Append current activity name + set.AppendCriteria( "playeractivity", CAI_BaseNPC::GetActivityName( GetActivity() ) ); + + set.AppendCriteria( "playerspeed", UTIL_VarArgs( "%.3f", GetAbsVelocity().Length() ) ); + + AppendContextToCriteria( set, "player" ); +} + + +const QAngle& CBasePlayer::GetPunchAngle() +{ + return m_Local.m_vecPunchAngle.Get(); +} + + +void CBasePlayer::SetPunchAngle( const QAngle &punchAngle ) +{ + m_Local.m_vecPunchAngle = punchAngle; + + if ( IsAlive() ) + { + int index = entindex(); + + for ( int i = 1; i <= gpGlobals->maxClients; i++ ) + { + CBasePlayer *pPlayer = UTIL_PlayerByIndex( i ); + + if ( pPlayer && i != index && pPlayer->GetObserverTarget() == this && pPlayer->GetObserverMode() == OBS_MODE_IN_EYE ) + { + pPlayer->SetPunchAngle( punchAngle ); + } + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Apply a movement constraint to the player +//----------------------------------------------------------------------------- +void CBasePlayer::ActivateMovementConstraint( CBaseEntity *pEntity, const Vector &vecCenter, float flRadius, float flConstraintWidth, float flSpeedFactor ) +{ + m_hConstraintEntity = pEntity; + m_vecConstraintCenter = vecCenter; + m_flConstraintRadius = flRadius; + m_flConstraintWidth = flConstraintWidth; + m_flConstraintSpeedFactor = flSpeedFactor; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBasePlayer::DeactivateMovementConstraint( ) +{ + m_hConstraintEntity = NULL; + m_flConstraintRadius = 0.0f; + m_vecConstraintCenter = vec3_origin; +} + +//----------------------------------------------------------------------------- +// Perhaps a poorly-named function. This function traces against the supplied +// NPC's hitboxes (instead of hull). If the trace hits a different NPC, the +// new NPC is selected. Otherwise, the supplied NPC is determined to be the +// one the citizen wants. This function allows the selection of a citizen over +// another citizen's shoulder, which is impossible without tracing against +// hitboxes instead of the hull (sjb) +//----------------------------------------------------------------------------- +CBaseEntity *CBasePlayer::DoubleCheckUseNPC( CBaseEntity *pNPC, const Vector &vecSrc, const Vector &vecDir ) +{ + trace_t tr; + + UTIL_TraceLine( vecSrc, vecSrc + vecDir * 1024, MASK_SHOT, this, COLLISION_GROUP_NONE, &tr ); + + if( tr.m_pEnt != NULL && tr.m_pEnt->MyNPCPointer() && tr.m_pEnt != pNPC ) + { + // Player is selecting a different NPC through some negative space + // in the first NPC's hitboxes (between legs, over shoulder, etc). + return tr.m_pEnt; + } + + return pNPC; +} + + +bool CBasePlayer::IsBot() const +{ + return (GetFlags() & FL_FAKECLIENT) != 0; +} + +bool CBasePlayer::IsFakeClient() const +{ + return (GetFlags() & FL_FAKECLIENT) != 0; +} + +void CBasePlayer::EquipSuit( bool bPlayEffects ) +{ + m_Local.m_bWearingSuit = true; +} + +void CBasePlayer::RemoveSuit( void ) +{ + m_Local.m_bWearingSuit = false; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : const char +//----------------------------------------------------------------------------- +const char *CBasePlayer::GetTracerType( void ) +{ + if ( GetActiveWeapon() ) + { + return GetActiveWeapon()->GetTracerType(); + } + + return BaseClass::GetTracerType(); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : &tr - +// nDamageType - +//----------------------------------------------------------------------------- +void CBasePlayer::DoImpactEffect( trace_t &tr, int nDamageType ) +{ + if ( GetActiveWeapon() ) + { + GetActiveWeapon()->DoImpactEffect( tr, nDamageType ); + return; + } + + BaseClass::DoImpactEffect( tr, nDamageType ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBasePlayer::InputSetHealth( inputdata_t &inputdata ) +{ + int iNewHealth = inputdata.value.Int(); + int iDelta = abs(GetHealth() - iNewHealth); + if ( iNewHealth > GetHealth() ) + { + TakeHealth( iDelta, DMG_GENERIC ); + } + else if ( iNewHealth < GetHealth() ) + { + // Strip off and restore armor so that it doesn't absorb any of this damage. + int armor = m_ArmorValue; + m_ArmorValue = 0; + TakeDamage( CTakeDamageInfo( this, this, iDelta, DMG_GENERIC ) ); + m_ArmorValue = armor; + } +} + +//----------------------------------------------------------------------------- +// Purpose: Hides or displays the HUD +// Input : &inputdata - +//----------------------------------------------------------------------------- +void CBasePlayer::InputSetHUDVisibility( inputdata_t &inputdata ) +{ + bool bEnable = inputdata.value.Bool(); + + if ( bEnable ) + { + m_Local.m_iHideHUD &= ~HIDEHUD_ALL; + } + else + { + m_Local.m_iHideHUD |= HIDEHUD_ALL; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *pEntity - +//----------------------------------------------------------------------------- +void CBasePlayer::SetViewEntity( CBaseEntity *pEntity ) +{ + m_hViewEntity = pEntity; + + if ( m_hViewEntity ) + { + engine->SetView( edict(), m_hViewEntity->edict() ); + } + else + { + engine->SetView( edict(), edict() ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Looks at the player's reserve ammo and also all his weapons for any ammo +// of the specified type +// Input : nAmmoIndex - ammo to look for +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CBasePlayer::HasAnyAmmoOfType( int nAmmoIndex ) +{ + // Must be a valid index + if ( nAmmoIndex < 0 ) + return false; + + // If we have some in reserve, we're already done + if ( GetAmmoCount( nAmmoIndex ) ) + return true; + + CBaseCombatWeapon *pWeapon; + + // Check all held weapons + for ( int i=0; i < MAX_WEAPONS; i++ ) + { + pWeapon = GetWeapon( i ); + + if ( !pWeapon ) + continue; + + // We must use clips and use this sort of ammo + if ( pWeapon->UsesClipsForAmmo1() && pWeapon->GetPrimaryAmmoType() == nAmmoIndex ) + { + // If we have any ammo, we're done + if ( pWeapon->HasPrimaryAmmo() ) + return true; + } + + // We'll check both clips for the same ammo type, just in case + if ( pWeapon->UsesClipsForAmmo2() && pWeapon->GetSecondaryAmmoType() == nAmmoIndex ) + { + if ( pWeapon->HasSecondaryAmmo() ) + return true; + } + } + + // We're completely without this type of ammo + return false; +} + +//----------------------------------------------------------------------------- +// return a string version of the players network (i.e steam) ID. +// +//----------------------------------------------------------------------------- +const char *CBasePlayer::GetNetworkIDString() +{ + Q_strncpy( m_szNetworkIDString, engine->GetPlayerNetworkIDString( edict() ), sizeof(m_szNetworkIDString) ); + return m_szNetworkIDString; +} + +//----------------------------------------------------------------------------- +// Assign the player a name +//----------------------------------------------------------------------------- +void CBasePlayer::SetPlayerName( const char *name ) +{ + Assert( name ); + + if ( name ) + { + Assert( strlen(name) > 0 ); + + Q_strncpy( m_szNetname, name, sizeof(m_szNetname) ); + } +} + +//----------------------------------------------------------------------------- +// sets the "don't autokick me" flag on a player +//----------------------------------------------------------------------------- +class DisableAutokick +{ +public: + DisableAutokick( int userID ) + { + m_userID = userID; + } + + bool operator()( CBasePlayer *player ) + { + if ( player->GetUserID() == m_userID ) + { + Msg( "autokick is disabled for %s\n", player->GetPlayerName() ); + player->DisableAutoKick( true ); + return false; // don't need to check other players + } + + return true; // keep looking at other players + } + +private: + int m_userID; +}; + +//----------------------------------------------------------------------------- +// sets the "don't autokick me" flag on a player +//----------------------------------------------------------------------------- +CON_COMMAND( mp_disable_autokick, "Prevents a userid from being auto-kicked" ) +{ + if ( !UTIL_IsCommandIssuedByServerAdmin() ) + { + Msg( "You must be a server admin to use mp_disable_autokick\n" ); + return; + } + + if ( engine->Cmd_Argc() != 2 ) + { + Msg( "Usage: mp_disable_autokick \n" ); + return; + } + + int userID = atoi( engine->Cmd_Argv( 1 ) ); + DisableAutokick disable( userID ); + ForEachPlayer( disable ); +} + +//----------------------------------------------------------------------------- +// Purpose: Toggle between the duck being on and off +//----------------------------------------------------------------------------- +void CBasePlayer::ToggleDuck( void ) +{ + // Toggle the state + m_bDuckToggled = !m_bDuckToggled; +} + +//----------------------------------------------------------------------------- +// Just tells us how far the stick is from the center. No directional info +//----------------------------------------------------------------------------- +float CBasePlayer::GetStickDist() +{ + Vector2D controlStick; + + controlStick.x = m_flForwardMove; + controlStick.y = m_flSideMove; + + return controlStick.Length(); +} + +//----------------------------------------------------------------------------- +// CPlayerInfo functions (simple passthroughts to get around the CBasePlayer multiple inheritence limitation) +//----------------------------------------------------------------------------- +const char *CPlayerInfo::GetName() +{ + Assert( m_pParent ); + return m_pParent->GetPlayerName(); +} + +int CPlayerInfo::GetUserID() +{ + Assert( m_pParent ); + return engine->GetPlayerUserId( m_pParent->edict() ); +} + +const char *CPlayerInfo::GetNetworkIDString() +{ + Assert( m_pParent ); + return m_pParent->GetNetworkIDString(); +} + +int CPlayerInfo::GetTeamIndex() +{ + Assert( m_pParent ); + return m_pParent->GetTeamNumber(); +} + +void CPlayerInfo::ChangeTeam( int iTeamNum ) +{ + Assert( m_pParent ); + m_pParent->ChangeTeam(iTeamNum); +} + +int CPlayerInfo::GetFragCount() +{ + Assert( m_pParent ); + return m_pParent->FragCount(); +} + +int CPlayerInfo::GetDeathCount() +{ + Assert( m_pParent ); + return m_pParent->DeathCount(); +} + +bool CPlayerInfo::IsConnected() +{ + Assert( m_pParent ); + return m_pParent->IsConnected(); +} + +int CPlayerInfo::GetArmorValue() +{ + Assert( m_pParent ); + return m_pParent->ArmorValue(); +} + +bool CPlayerInfo::IsHLTV() +{ + Assert( m_pParent ); + return m_pParent->IsHLTV(); +} + +bool CPlayerInfo::IsPlayer() +{ + Assert( m_pParent ); + return m_pParent->IsPlayer(); +} + +bool CPlayerInfo::IsFakeClient() +{ + Assert( m_pParent ); + return m_pParent->IsFakeClient(); +} + +bool CPlayerInfo::IsDead() +{ + Assert( m_pParent ); + return m_pParent->IsDead(); +} + +bool CPlayerInfo::IsInAVehicle() +{ + Assert( m_pParent ); + return m_pParent->IsInAVehicle(); +} + +bool CPlayerInfo::IsObserver() +{ + Assert( m_pParent ); + return m_pParent->IsObserver(); +} + +const Vector CPlayerInfo::GetAbsOrigin() +{ + Assert( m_pParent ); + return m_pParent->GetAbsOrigin(); +} + +const QAngle CPlayerInfo::GetAbsAngles() +{ + Assert( m_pParent ); + return m_pParent->GetAbsAngles(); +} + +const Vector CPlayerInfo::GetPlayerMins() +{ + Assert( m_pParent ); + return m_pParent->GetPlayerMins(); +} + +const Vector CPlayerInfo::GetPlayerMaxs() +{ + Assert( m_pParent ); + return m_pParent->GetPlayerMaxs(); +} + +const char *CPlayerInfo::GetWeaponName() +{ + Assert( m_pParent ); + CBaseCombatWeapon *weap = m_pParent->GetActiveWeapon(); + if ( !weap ) + { + return NULL; + } + return weap->GetName(); +} + +const char *CPlayerInfo::GetModelName() +{ + Assert( m_pParent ); + return m_pParent->GetModelName().ToCStr(); +} + +const int CPlayerInfo::GetHealth() +{ + Assert( m_pParent ); + return m_pParent->GetHealth(); +} + +const int CPlayerInfo::GetMaxHealth() +{ + Assert( m_pParent ); + return m_pParent->GetMaxHealth(); +} + + + + + +void CPlayerInfo::SetAbsOrigin( Vector & vec ) +{ + Assert( m_pParent ); + if ( m_pParent->IsBot() ) + { + m_pParent->SetAbsOrigin(vec); + } +} + +void CPlayerInfo::SetAbsAngles( QAngle & ang ) +{ + Assert( m_pParent ); + if ( m_pParent->IsBot() ) + { + m_pParent->SetAbsAngles(ang); + } +} + +void CPlayerInfo::RemoveAllItems( bool removeSuit ) +{ + Assert( m_pParent ); + if ( m_pParent->IsBot() ) + { + m_pParent->RemoveAllItems(removeSuit); + } +} + +void CPlayerInfo::SetActiveWeapon( const char *WeaponName ) +{ + Assert( m_pParent ); + if ( m_pParent->IsBot() ) + { + CBaseCombatWeapon *weap = m_pParent->Weapon_Create( WeaponName ); + if ( weap ) + { + m_pParent->Weapon_Equip(weap); + m_pParent->Weapon_Switch(weap); + } + } +} + +void CPlayerInfo::SetLocalOrigin( const Vector& origin ) +{ + Assert( m_pParent ); + if ( m_pParent->IsBot() ) + { + m_pParent->SetLocalOrigin(origin); + } +} + +const Vector CPlayerInfo::GetLocalOrigin( void ) +{ + Assert( m_pParent ); + if ( m_pParent->IsBot() ) + { + Vector origin = m_pParent->GetLocalOrigin(); + return origin; + } + else + { + return Vector( 0, 0, 0 ); + } +} + +void CPlayerInfo::SetLocalAngles( const QAngle& angles ) +{ + Assert( m_pParent ); + if ( m_pParent->IsBot() ) + { + m_pParent->SetLocalAngles( angles ); + } +} + +const QAngle CPlayerInfo::GetLocalAngles( void ) +{ + Assert( m_pParent ); + if ( m_pParent->IsBot() ) + { + return m_pParent->GetLocalAngles(); + } + else + { + return QAngle(); + } +} + +void CPlayerInfo::PostClientMessagesSent( void ) +{ + Assert( m_pParent ); + if ( m_pParent->IsBot() ) + { + m_pParent->PostClientMessagesSent(); + } +} + +bool CPlayerInfo::IsEFlagSet( int nEFlagMask ) +{ + Assert( m_pParent ); + if ( m_pParent->IsBot() ) + { + return m_pParent->IsEFlagSet(nEFlagMask); + } + return false; +} + +void CPlayerInfo::RunPlayerMove( CBotCmd *ucmd ) +{ + if ( m_pParent->IsBot() ) + { + Assert( m_pParent ); + CUserCmd cmd; + cmd.buttons = ucmd->buttons; + cmd.command_number = ucmd->command_number; + cmd.forwardmove = ucmd->forwardmove; + cmd.hasbeenpredicted = ucmd->hasbeenpredicted; + cmd.impulse = ucmd->impulse; + cmd.mousedx = ucmd->mousedx; + cmd.mousedy = ucmd->mousedy; + cmd.random_seed = ucmd->random_seed; + cmd.sidemove = ucmd->sidemove; + cmd.tick_count = ucmd->tick_count; + cmd.upmove = ucmd->upmove; + cmd.viewangles = ucmd->viewangles; + cmd.weaponselect = ucmd->weaponselect; + cmd.weaponsubtype = ucmd->weaponsubtype; + + // Store off the globals.. they're gonna get whacked + float flOldFrametime = gpGlobals->frametime; + float flOldCurtime = gpGlobals->curtime; + + m_pParent->SetTimeBase( gpGlobals->curtime ); + + MoveHelperServer()->SetHost( m_pParent ); + m_pParent->PlayerRunCommand( &cmd, MoveHelperServer() ); + + // save off the last good usercmd + m_pParent->SetLastUserCommand( cmd ); + + // Clear out any fixangle that has been set + m_pParent->pl.fixangle = FIXANGLE_NONE; + + // Restore the globals.. + gpGlobals->frametime = flOldFrametime; + gpGlobals->curtime = flOldCurtime; + MoveHelperServer()->SetHost( NULL ); + } +} + +void CPlayerInfo::SetLastUserCommand( const CBotCmd &ucmd ) +{ + if ( m_pParent->IsBot() ) + { + Assert( m_pParent ); + CUserCmd cmd; + cmd.buttons = ucmd.buttons; + cmd.command_number = ucmd.command_number; + cmd.forwardmove = ucmd.forwardmove; + cmd.hasbeenpredicted = ucmd.hasbeenpredicted; + cmd.impulse = ucmd.impulse; + cmd.mousedx = ucmd.mousedx; + cmd.mousedy = ucmd.mousedy; + cmd.random_seed = ucmd.random_seed; + cmd.sidemove = ucmd.sidemove; + cmd.tick_count = ucmd.tick_count; + cmd.upmove = ucmd.upmove; + cmd.viewangles = ucmd.viewangles; + cmd.weaponselect = ucmd.weaponselect; + cmd.weaponsubtype = ucmd.weaponsubtype; + + m_pParent->SetLastUserCommand(cmd); + } +} + + +CBotCmd CPlayerInfo::GetLastUserCommand() +{ + CBotCmd cmd; + const CUserCmd *ucmd = m_pParent->GetLastUserCommand(); + if ( ucmd ) + { + cmd.buttons = ucmd->buttons; + cmd.command_number = ucmd->command_number; + cmd.forwardmove = ucmd->forwardmove; + cmd.hasbeenpredicted = ucmd->hasbeenpredicted; + cmd.impulse = ucmd->impulse; + cmd.mousedx = ucmd->mousedx; + cmd.mousedy = ucmd->mousedy; + cmd.random_seed = ucmd->random_seed; + cmd.sidemove = ucmd->sidemove; + cmd.tick_count = ucmd->tick_count; + cmd.upmove = ucmd->upmove; + cmd.viewangles = ucmd->viewangles; + cmd.weaponselect = ucmd->weaponselect; + cmd.weaponsubtype = ucmd->weaponsubtype; + } + return cmd; +} + diff --git a/dlls/player_lagcompensation.cpp b/dlls/player_lagcompensation.cpp index 189e12d9..dc946d97 100644 --- a/dlls/player_lagcompensation.cpp +++ b/dlls/player_lagcompensation.cpp @@ -234,7 +234,7 @@ void CLagCompensationManager::FrameUpdatePostEntityThink() VPROF_BUDGET( "FrameUpdatePostEntityThink", "CLagCompensationManager" ); // remove all records before that time: - int flDeadtime = gpGlobals->curtime - sv_maxunlag.GetFloat(); + int flDeadtime = static_cast(gpGlobals->curtime - sv_maxunlag.GetFloat()); // Iterate all active players for ( int i = 1; i <= gpGlobals->maxClients; i++ ) diff --git a/dlls/player_resource.cpp b/dlls/player_resource.cpp index a62a35ea..8aa25b42 100644 --- a/dlls/player_resource.cpp +++ b/dlls/player_resource.cpp @@ -115,7 +115,7 @@ void CPlayerResource::UpdatePlayerData( void ) UTIL_GetPlayerConnectionInfo( i, ping, packetloss ); // calc avg for scoreboard so it's not so jittery - ping = 0.8f * m_iPing.Get(i) + 0.2f * ping; + ping = static_cast(0.8f * m_iPing.Get(i) + 0.2f * ping); m_iPing.Set( i, ping ); diff --git a/dlls/playerinfomanager.cpp b/dlls/playerinfomanager.cpp index 3e1285e4..c9f7359b 100644 --- a/dlls/playerinfomanager.cpp +++ b/dlls/playerinfomanager.cpp @@ -1,134 +1,134 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: implementation of player info manager -// -//=============================================================================// - -#include "cbase.h" -#include "player.h" -#include "playerinfomanager.h" -#include "edict.h" - -extern CGlobalVars *gpGlobals; -static CPlayerInfoManager s_PlayerInfoManager; -static CPluginBotManager s_BotManager; - -namespace -{ - - // - // Old version support - // - abstract_class IPlayerInfo_V1 - { - public: - // returns the players name (UTF-8 encoded) - virtual const char *GetName() = 0; - // returns the userid (slot number) - virtual int GetUserID() = 0; - // returns the string of their network (i.e Steam) ID - virtual const char *GetNetworkIDString() = 0; - // returns the team the player is on - virtual int GetTeamIndex() = 0; - // changes the player to a new team (if the game dll logic allows it) - virtual void ChangeTeam( int iTeamNum ) = 0; - // returns the number of kills this player has (exact meaning is mod dependent) - virtual int GetFragCount() = 0; - // returns the number of deaths this player has (exact meaning is mod dependent) - virtual int GetDeathCount() = 0; - // returns if this player slot is actually valid - virtual bool IsConnected() = 0; - // returns the armor/health of the player (exact meaning is mod dependent) - virtual int GetArmorValue() = 0; - }; - - abstract_class IPlayerInfoManager_V1 - { - public: - virtual IPlayerInfo_V1 *GetPlayerInfo( edict_t *pEdict ) = 0; - }; - - - class CPlayerInfoManager_V1: public IPlayerInfoManager_V1 - { - public: - virtual IPlayerInfo_V1 *GetPlayerInfo( edict_t *pEdict ); - }; - - static CPlayerInfoManager_V1 s_PlayerInfoManager_V1; - - - IPlayerInfo_V1 *CPlayerInfoManager_V1::GetPlayerInfo( edict_t *pEdict ) - { - CBasePlayer *pPlayer = ( ( CBasePlayer * )CBaseEntity::Instance( pEdict )); - if ( pPlayer ) - { - return (IPlayerInfo_V1 *)pPlayer->GetPlayerInfo(); - } - else - { - return NULL; - } - } - - EXPOSE_SINGLE_INTERFACE_GLOBALVAR(CPlayerInfoManager, IPlayerInfoManager_V1, "PlayerInfoManager001", s_PlayerInfoManager); -}; - -IPlayerInfo *CPlayerInfoManager::GetPlayerInfo( edict_t *pEdict ) -{ - CBasePlayer *pPlayer = ( ( CBasePlayer * )CBaseEntity::Instance( pEdict )); - if ( pPlayer ) - { - return pPlayer->GetPlayerInfo(); - } - else - { - return NULL; - } -} - -CGlobalVars *CPlayerInfoManager::GetGlobalVars() -{ - return gpGlobals; -} - - - -IBotController *CPluginBotManager::GetBotController( edict_t *pEdict ) -{ - CBasePlayer *pPlayer = ( ( CBasePlayer * )CBaseEntity::Instance( pEdict )); - if ( pPlayer && pPlayer->IsBot() ) - { - return pPlayer->GetBotController(); - } - else - { - return NULL; - } -} - -edict_t *CPluginBotManager::CreateBot( const char *botname ) -{ - edict_t *pEdict = engine->CreateFakeClient( botname ); - if (!pEdict) - { - Msg( "Failed to create Bot.\n"); - return NULL; - } - - // Allocate a player entity for the bot, and call spawn - CBasePlayer *pPlayer = ((CBasePlayer*)CBaseEntity::Instance( pEdict )); - - pPlayer->ClearFlags(); - pPlayer->AddFlag( FL_CLIENT | FL_FAKECLIENT ); - - pPlayer->ChangeTeam( TEAM_UNASSIGNED ); - pPlayer->RemoveAllItems( true ); - pPlayer->Spawn(); - - return pEdict; -} - -EXPOSE_SINGLE_INTERFACE_GLOBALVAR(CPlayerInfoManager, IPlayerInfoManager, INTERFACEVERSION_PLAYERINFOMANAGER, s_PlayerInfoManager); -EXPOSE_SINGLE_INTERFACE_GLOBALVAR(CPluginBotManager, IBotManager, INTERFACEVERSION_PLAYERBOTMANAGER, s_BotManager); - +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: implementation of player info manager +// +//=============================================================================// + +#include "cbase.h" +#include "player.h" +#include "playerinfomanager.h" +#include "edict.h" + +extern CGlobalVars *gpGlobals; +static CPlayerInfoManager s_PlayerInfoManager; +static CPluginBotManager s_BotManager; + +namespace +{ + + // + // Old version support + // + abstract_class IPlayerInfo_V1 + { + public: + // returns the players name (UTF-8 encoded) + virtual const char *GetName() = 0; + // returns the userid (slot number) + virtual int GetUserID() = 0; + // returns the string of their network (i.e Steam) ID + virtual const char *GetNetworkIDString() = 0; + // returns the team the player is on + virtual int GetTeamIndex() = 0; + // changes the player to a new team (if the game dll logic allows it) + virtual void ChangeTeam( int iTeamNum ) = 0; + // returns the number of kills this player has (exact meaning is mod dependent) + virtual int GetFragCount() = 0; + // returns the number of deaths this player has (exact meaning is mod dependent) + virtual int GetDeathCount() = 0; + // returns if this player slot is actually valid + virtual bool IsConnected() = 0; + // returns the armor/health of the player (exact meaning is mod dependent) + virtual int GetArmorValue() = 0; + }; + + abstract_class IPlayerInfoManager_V1 + { + public: + virtual IPlayerInfo_V1 *GetPlayerInfo( edict_t *pEdict ) = 0; + }; + + + class CPlayerInfoManager_V1: public IPlayerInfoManager_V1 + { + public: + virtual IPlayerInfo_V1 *GetPlayerInfo( edict_t *pEdict ); + }; + + static CPlayerInfoManager_V1 s_PlayerInfoManager_V1; + + + IPlayerInfo_V1 *CPlayerInfoManager_V1::GetPlayerInfo( edict_t *pEdict ) + { + CBasePlayer *pPlayer = ( ( CBasePlayer * )CBaseEntity::Instance( pEdict )); + if ( pPlayer ) + { + return (IPlayerInfo_V1 *)pPlayer->GetPlayerInfo(); + } + else + { + return NULL; + } + } + + EXPOSE_SINGLE_INTERFACE_GLOBALVAR(CPlayerInfoManager, IPlayerInfoManager_V1, "PlayerInfoManager001", s_PlayerInfoManager); +}; + +IPlayerInfo *CPlayerInfoManager::GetPlayerInfo( edict_t *pEdict ) +{ + CBasePlayer *pPlayer = ( ( CBasePlayer * )CBaseEntity::Instance( pEdict )); + if ( pPlayer ) + { + return pPlayer->GetPlayerInfo(); + } + else + { + return NULL; + } +} + +CGlobalVars *CPlayerInfoManager::GetGlobalVars() +{ + return gpGlobals; +} + + + +IBotController *CPluginBotManager::GetBotController( edict_t *pEdict ) +{ + CBasePlayer *pPlayer = ( ( CBasePlayer * )CBaseEntity::Instance( pEdict )); + if ( pPlayer && pPlayer->IsBot() ) + { + return pPlayer->GetBotController(); + } + else + { + return NULL; + } +} + +edict_t *CPluginBotManager::CreateBot( const char *botname ) +{ + edict_t *pEdict = engine->CreateFakeClient( botname ); + if (!pEdict) + { + Msg( "Failed to create Bot.\n"); + return NULL; + } + + // Allocate a player entity for the bot, and call spawn + CBasePlayer *pPlayer = ((CBasePlayer*)CBaseEntity::Instance( pEdict )); + + pPlayer->ClearFlags(); + pPlayer->AddFlag( FL_CLIENT | FL_FAKECLIENT ); + + pPlayer->ChangeTeam( TEAM_UNASSIGNED ); + pPlayer->RemoveAllItems( true ); + pPlayer->Spawn(); + + return pEdict; +} + +EXPOSE_SINGLE_INTERFACE_GLOBALVAR(CPlayerInfoManager, IPlayerInfoManager, INTERFACEVERSION_PLAYERINFOMANAGER, s_PlayerInfoManager); +EXPOSE_SINGLE_INTERFACE_GLOBALVAR(CPluginBotManager, IBotManager, INTERFACEVERSION_PLAYERBOTMANAGER, s_BotManager); + diff --git a/dlls/playerinfomanager.h b/dlls/playerinfomanager.h index b1a2c3cc..aabfc9ec 100644 --- a/dlls/playerinfomanager.h +++ b/dlls/playerinfomanager.h @@ -1,32 +1,32 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: implementation of player info manager -// -//=============================================================================// -#ifndef PLAYERINFOMANAGER_H -#define PLAYERINFOMANAGER_H -#ifdef _WIN32 -#pragma once -#endif - - -#include "dlls/iplayerinfo.h" - -//----------------------------------------------------------------------------- -// Purpose: interface for plugins to get player info -//----------------------------------------------------------------------------- -class CPlayerInfoManager: public IPlayerInfoManager -{ -public: - virtual IPlayerInfo *GetPlayerInfo( edict_t *pEdict ); - virtual CGlobalVars *GetGlobalVars(); -}; - -class CPluginBotManager: public IBotManager -{ -public: - virtual IBotController *GetBotController( edict_t *pEdict ); - virtual edict_t *CreateBot( const char *botname ); -}; - -#endif \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: implementation of player info manager +// +//=============================================================================// +#ifndef PLAYERINFOMANAGER_H +#define PLAYERINFOMANAGER_H +#ifdef _WIN32 +#pragma once +#endif + + +#include "dlls/iplayerinfo.h" + +//----------------------------------------------------------------------------- +// Purpose: interface for plugins to get player info +//----------------------------------------------------------------------------- +class CPlayerInfoManager: public IPlayerInfoManager +{ +public: + virtual IPlayerInfo *GetPlayerInfo( edict_t *pEdict ); + virtual CGlobalVars *GetGlobalVars(); +}; + +class CPluginBotManager: public IBotManager +{ +public: + virtual IBotController *GetBotController( edict_t *pEdict ); + virtual edict_t *CreateBot( const char *botname ); +}; + +#endif diff --git a/dlls/point_spotlight.cpp b/dlls/point_spotlight.cpp index 6a171729..d3b5d8fc 100644 --- a/dlls/point_spotlight.cpp +++ b/dlls/point_spotlight.cpp @@ -186,12 +186,12 @@ void CPointSpotlight::ComputeRenderInfo() } else if ( m_flSpotlightCurLength > m_flSpotlightMaxLength ) { - m_hSpotlightTarget->SetRenderColorA( (1-((m_flSpotlightCurLength-m_flSpotlightMaxLength)/m_flSpotlightMaxLength)) ); + m_hSpotlightTarget->SetRenderColorA( static_cast((1-((m_flSpotlightCurLength-m_flSpotlightMaxLength)/m_flSpotlightMaxLength))) ); m_hSpotlight->SetFadeLength( m_flSpotlightMaxLength ); } else { - m_hSpotlightTarget->SetRenderColorA( 1.0 ); + m_hSpotlightTarget->SetRenderColorA( 1 ); m_hSpotlight->SetFadeLength( m_flSpotlightCurLength ); } diff --git a/dlls/props.cpp b/dlls/props.cpp index 346240ce..c557e5d0 100644 --- a/dlls/props.cpp +++ b/dlls/props.cpp @@ -260,7 +260,7 @@ void CBaseProp::Activate( void ) // Make sure mapmakers haven't used the wrong prop type. if ( m_takedamage == DAMAGE_NO && m_iHealth != 0 ) { - Warning("%s has a health specified in model '%s'. Use prop_physics or prop_dynamic instead.\n", GetClassname(), GetModelName() ); + Warning("%s has a health specified in model '%s'. Use prop_physics or prop_dynamic instead.\n", GetClassname(), STRING(GetModelName()) ); } } @@ -365,7 +365,7 @@ void CBaseProp::DrawDebugGeometryOverlays( void ) // Remap health to green brightness float flG = RemapVal( m_iHealth, 0, 100, 64, 255 ); flG = clamp( flG, 0, 255 ); - NDebugOverlay::EntityBounds(this, 0, flG, 0, 0, 0 ); + NDebugOverlay::EntityBounds(this, 0, static_cast(flG), 0, 0, 0 ); } } } @@ -443,7 +443,7 @@ void CBreakableProp::Ignite( float flFlameLifetime, bool bNPCOnly, float flSize, } // Frighten AIs, just in case this is an exploding thing. - CSoundEnt::InsertSound( SOUND_DANGER, GetAbsOrigin(), 128.0f, 1.0f, this, SOUNDENT_CHANNEL_REPEATED_DANGER ); + CSoundEnt::InsertSound( SOUND_DANGER, GetAbsOrigin(), 128, 1.0f, this, SOUNDENT_CHANNEL_REPEATED_DANGER ); } //----------------------------------------------------------------------------- @@ -1469,7 +1469,7 @@ void CBreakableProp::OnPhysGunDrop( CBasePlayer *pPhysGunUser, PhysGunDrop_t Rea SetPhysicsAttacker( pPhysGunUser, gpGlobals->curtime ); - if( Reason == PUNTED_BY_CANNON ) + if( Reason == static_cast(PUNTED_BY_CANNON) ) { PlayPuntSound(); } @@ -1618,14 +1618,14 @@ void CBreakableProp::Break( CBaseEntity *pBreaker, const CTakeDamageInfo &info ) { if( HasInteraction( PROPINTER_PHYSGUN_BREAK_EXPLODE ) ) { - ExplosionCreate( WorldSpaceCenter(), angles, pAttacker, m_explodeDamage, m_explodeRadius, + ExplosionCreate( WorldSpaceCenter(), angles, pAttacker, static_cast(m_explodeDamage), static_cast(m_explodeRadius), SF_ENVEXPLOSION_NOSPARKS | SF_ENVEXPLOSION_NODLIGHTS | SF_ENVEXPLOSION_NOSMOKE | SF_ENVEXPLOSION_SURFACEONLY | SF_ENVEXPLOSION_NOSOUND, 0.0f, this ); EmitSound("PropaneTank.Burst"); } else { - ExplosionCreate( WorldSpaceCenter(), angles, pAttacker, m_explodeDamage, m_explodeRadius, + ExplosionCreate( WorldSpaceCenter(), angles, pAttacker, static_cast(m_explodeDamage), static_cast(m_explodeRadius), SF_ENVEXPLOSION_NOSPARKS | SF_ENVEXPLOSION_NODLIGHTS | SF_ENVEXPLOSION_NOSMOKE | SF_ENVEXPLOSION_SURFACEONLY, 0.0f, this ); } @@ -1688,7 +1688,7 @@ void CBreakableProp::Break( CBaseEntity *pBreaker, const CTakeDamageInfo &info ) { if ( bExploded == false ) { - ExplosionCreate( origin, angles, pAttacker, 1, m_explodeRadius, + ExplosionCreate( origin, angles, pAttacker, 1, static_cast(m_explodeRadius), SF_ENVEXPLOSION_NOSPARKS | SF_ENVEXPLOSION_NODLIGHTS | SF_ENVEXPLOSION_NOSMOKE, 0.0f, this ); } @@ -5004,7 +5004,7 @@ void CPropDoorRotating::BeginOpening(CBaseEntity *pOpenAwayFrom) // Make respectful entities move away from our path if( !HasSpawnFlags(SF_DOOR_SILENT_TO_NPCS) ) { - CSoundEnt::InsertSound( SOUND_MOVE_AWAY, volumeCenter, volumeRadius, 0.5f, pOpenAwayFrom ); + CSoundEnt::InsertSound( SOUND_MOVE_AWAY, volumeCenter, static_cast(volumeRadius), 0.5f, pOpenAwayFrom ); } // Do final setup @@ -5263,7 +5263,7 @@ class CPhysicsPropMultiplayer : public CPhysicsProp, public IMultiplayerPhysics DECLARE_SERVERCLASS(); DECLARE_DATADESC(); - CPhysicsPropMultiplayer::CPhysicsPropMultiplayer() + CPhysicsPropMultiplayer() { m_iPhysicsMode = PHYSICS_MULTIPLAYER_AUTODETECT; m_usingCustomCollisionBounds = false; diff --git a/dlls/recipientfilter.cpp b/dlls/recipientfilter.cpp index fbf78c20..2f4da1ac 100644 --- a/dlls/recipientfilter.cpp +++ b/dlls/recipientfilter.cpp @@ -1,373 +1,373 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -// -//=============================================================================// -#include "cbase.h" -#include "recipientfilter.h" -#include "team.h" -#include "ipredictionsystem.h" - -// memdbgon must be the last include file in a .cpp file!!! -#include "tier0/memdbgon.h" - -static IPredictionSystem g_RecipientFilterPredictionSystem; - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -CRecipientFilter::CRecipientFilter() -{ - Reset(); -} - -CRecipientFilter::~CRecipientFilter() -{ -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : src - -//----------------------------------------------------------------------------- -void CRecipientFilter::CopyFrom( const CRecipientFilter& src ) -{ - m_bReliable = src.IsReliable(); - m_bInitMessage = src.IsInitMessage(); - - m_bUsingPredictionRules = src.IsUsingPredictionRules(); - m_bIgnorePredictionCull = src.IgnorePredictionCull(); - - int c = src.GetRecipientCount(); - for ( int i = 0; i < c; ++i ) - { - m_Recipients.AddToTail( src.GetRecipientIndex( i ) ); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CRecipientFilter::Reset( void ) -{ - m_bReliable = false; - m_bInitMessage = false; - m_Recipients.RemoveAll(); - m_bUsingPredictionRules = false; - m_bIgnorePredictionCull = false; -} - -void CRecipientFilter::MakeReliable( void ) -{ - m_bReliable = true; -} - -bool CRecipientFilter::IsReliable( void ) const -{ - return m_bReliable; -} - -int CRecipientFilter::GetRecipientCount( void ) const -{ - return m_Recipients.Size(); -} - -int CRecipientFilter::GetRecipientIndex( int slot ) const -{ - if ( slot < 0 || slot >= GetRecipientCount() ) - return -1; - - return m_Recipients[ slot ]; -} - -void CRecipientFilter::AddAllPlayers( void ) -{ - m_Recipients.RemoveAll(); - - int i; - for ( i = 1; i <= gpGlobals->maxClients; i++ ) - { - CBasePlayer *pPlayer = UTIL_PlayerByIndex( i ); - if ( !pPlayer ) - { - continue; - } - - AddRecipient( pPlayer ); - } -} - -void CRecipientFilter::AddRecipient( CBasePlayer *player ) -{ - Assert( player ); - - int index = player->entindex(); - - // If we're predicting and this is not the first time we've predicted this sound - // then don't send it to the local player again. - if ( m_bUsingPredictionRules ) - { - // Only add local player if this is the first time doing prediction - if ( g_RecipientFilterPredictionSystem.GetSuppressHost() == player ) - { - return; - } - } - - // Already in list - if ( m_Recipients.Find( index ) != m_Recipients.InvalidIndex() ) - return; - - m_Recipients.AddToTail( index ); -} - -void CRecipientFilter::RemoveAllRecipients( void ) -{ - m_Recipients.RemoveAll(); -} - -void CRecipientFilter::RemoveRecipient( CBasePlayer *player ) -{ - Assert( player ); - - int index = player->entindex(); - - // Remove it if it's in the list - m_Recipients.FindAndRemove( index ); -} - -void CRecipientFilter::AddRecipientsByTeam( CTeam *team ) -{ - Assert( team ); - - int i; - int c = team->GetNumPlayers(); - for ( i = 0 ; i < c ; i++ ) - { - CBasePlayer *player = team->GetPlayer( i ); - if ( !player ) - continue; - - AddRecipient( player ); - } -} - -void CRecipientFilter::RemoveRecipientsByTeam( CTeam *team ) -{ - Assert( team ); - - int i; - int c = team->GetNumPlayers(); - for ( i = 0 ; i < c ; i++ ) - { - CBasePlayer *player = team->GetPlayer( i ); - if ( !player ) - continue; - - RemoveRecipient( player ); - } -} - -void CRecipientFilter::AddPlayersFromBitMask( CBitVec< ABSOLUTE_PLAYER_LIMIT >& playerbits ) -{ - int index = playerbits.FindNextSetBit( 0 ); - - while ( index > -1 ) - { - CBasePlayer *pPlayer = UTIL_PlayerByIndex( index + 1 ); - if ( pPlayer ) - { - AddRecipient( pPlayer ); - } - - index = playerbits.FindNextSetBit( index + 1 ); - } -} - -void CRecipientFilter::RemovePlayersFromBitMask( CBitVec< ABSOLUTE_PLAYER_LIMIT >& playerbits ) -{ - int index = playerbits.FindNextSetBit( 0 ); - - while ( index > -1 ) - { - CBasePlayer *pPlayer = UTIL_PlayerByIndex( index + 1 ); - if ( pPlayer ) - { - RemoveRecipient( pPlayer ); - } - - index = playerbits.FindNextSetBit( index + 1 ); - } -} - -void CRecipientFilter::AddRecipientsByPVS( const Vector& origin ) -{ - if ( gpGlobals->maxClients == 1 ) - { - AddAllPlayers(); - } - else - { - CBitVec< ABSOLUTE_PLAYER_LIMIT > playerbits; - engine->Message_DetermineMulticastRecipients( false, origin, playerbits ); - AddPlayersFromBitMask( playerbits ); - } -} - -void CRecipientFilter::RemoveRecipientsByPVS( const Vector& origin ) -{ - if ( gpGlobals->maxClients == 1 ) - { - m_Recipients.RemoveAll(); - } - else - { - CBitVec< ABSOLUTE_PLAYER_LIMIT > playerbits; - engine->Message_DetermineMulticastRecipients( false, origin, playerbits ); - RemovePlayersFromBitMask( playerbits ); - } -} - - - -void CRecipientFilter::AddRecipientsByPAS( const Vector& origin ) -{ - if ( gpGlobals->maxClients == 1 ) - { - AddAllPlayers(); - } - else - { - CBitVec< ABSOLUTE_PLAYER_LIMIT > playerbits; - engine->Message_DetermineMulticastRecipients( true, origin, playerbits ); - AddPlayersFromBitMask( playerbits ); - } -} - -bool CRecipientFilter::IsInitMessage( void ) const -{ - return m_bInitMessage; -} - -void CRecipientFilter::MakeInitMessage( void ) -{ - m_bInitMessage = true; -} - -void CRecipientFilter::UsePredictionRules( void ) -{ - if ( m_bUsingPredictionRules ) - return; - - m_bUsingPredictionRules = true; - - // Cull list now, if needed - if ( GetRecipientCount() == 0 ) - return; - - CBasePlayer *pPlayer = ToBasePlayer( (CBaseEntity*)g_RecipientFilterPredictionSystem.GetSuppressHost() ); - - if ( pPlayer) - { - RemoveRecipient( pPlayer ); - } -} - -bool CRecipientFilter::IsUsingPredictionRules( void ) const -{ - return m_bUsingPredictionRules; -} - -bool CRecipientFilter:: IgnorePredictionCull( void ) const -{ - return m_bIgnorePredictionCull; -} - -void CRecipientFilter::SetIgnorePredictionCull( bool ignore ) -{ - m_bIgnorePredictionCull = ignore; -} - -//----------------------------------------------------------------------------- -// Purpose: Simple class to create a filter for all players on a given team -//----------------------------------------------------------------------------- -CTeamRecipientFilter::CTeamRecipientFilter( int team, bool isReliable ) -{ - if (isReliable) - MakeReliable(); - - RemoveAllRecipients(); - - for ( int i = 1; i <= gpGlobals->maxClients; i++ ) - { - CBasePlayer *pPlayer = UTIL_PlayerByIndex( i ); - - if ( !pPlayer ) - { - continue; - } - - if ( pPlayer->GetTeamNumber() != team ) - { - continue; - } - - AddRecipient( pPlayer ); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : origin - -// ATTN_NORM - -//----------------------------------------------------------------------------- -void CPASAttenuationFilter::Filter( const Vector& origin, float attenuation /*= ATTN_NORM*/ ) -{ - // Don't crop for attenuation in single player - if ( gpGlobals->maxClients == 1 ) - return; - - // CPASFilter adds them by pure PVS in constructor - if ( attenuation <= 0 ) - return; - - // Now remove recipients that are outside sound radius - float distance, maxAudible; - Vector vecRelative; - - int c = GetRecipientCount(); - - for ( int i = c - 1; i >= 0; i-- ) - { - int index = GetRecipientIndex( i ); - - CBaseEntity *ent = CBaseEntity::Instance( index ); - if ( !ent || !ent->IsPlayer() ) - { - Assert( 0 ); - continue; - } - - CBasePlayer *player = ToBasePlayer( ent ); - if ( !player ) - { - Assert( 0 ); - continue; - } - -#ifndef _XBOX - // never remove the HLTV bot - if ( player->IsHLTV() ) - continue; -#endif - - VectorSubtract( player->EarPosition(), origin, vecRelative ); - distance = VectorLength( vecRelative ); - maxAudible = ( 2 * SOUND_NORMAL_CLIP_DIST ) / attenuation; - if ( distance <= maxAudible ) - continue; - - RemoveRecipient( player ); - } -} \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// +#include "cbase.h" +#include "recipientfilter.h" +#include "team.h" +#include "ipredictionsystem.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +static IPredictionSystem g_RecipientFilterPredictionSystem; + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CRecipientFilter::CRecipientFilter() +{ + Reset(); +} + +CRecipientFilter::~CRecipientFilter() +{ +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : src - +//----------------------------------------------------------------------------- +void CRecipientFilter::CopyFrom( const CRecipientFilter& src ) +{ + m_bReliable = src.IsReliable(); + m_bInitMessage = src.IsInitMessage(); + + m_bUsingPredictionRules = src.IsUsingPredictionRules(); + m_bIgnorePredictionCull = src.IgnorePredictionCull(); + + int c = src.GetRecipientCount(); + for ( int i = 0; i < c; ++i ) + { + m_Recipients.AddToTail( src.GetRecipientIndex( i ) ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CRecipientFilter::Reset( void ) +{ + m_bReliable = false; + m_bInitMessage = false; + m_Recipients.RemoveAll(); + m_bUsingPredictionRules = false; + m_bIgnorePredictionCull = false; +} + +void CRecipientFilter::MakeReliable( void ) +{ + m_bReliable = true; +} + +bool CRecipientFilter::IsReliable( void ) const +{ + return m_bReliable; +} + +int CRecipientFilter::GetRecipientCount( void ) const +{ + return m_Recipients.Size(); +} + +int CRecipientFilter::GetRecipientIndex( int slot ) const +{ + if ( slot < 0 || slot >= GetRecipientCount() ) + return -1; + + return m_Recipients[ slot ]; +} + +void CRecipientFilter::AddAllPlayers( void ) +{ + m_Recipients.RemoveAll(); + + int i; + for ( i = 1; i <= gpGlobals->maxClients; i++ ) + { + CBasePlayer *pPlayer = UTIL_PlayerByIndex( i ); + if ( !pPlayer ) + { + continue; + } + + AddRecipient( pPlayer ); + } +} + +void CRecipientFilter::AddRecipient( CBasePlayer *player ) +{ + Assert( player ); + + int index = player->entindex(); + + // If we're predicting and this is not the first time we've predicted this sound + // then don't send it to the local player again. + if ( m_bUsingPredictionRules ) + { + // Only add local player if this is the first time doing prediction + if ( g_RecipientFilterPredictionSystem.GetSuppressHost() == player ) + { + return; + } + } + + // Already in list + if ( m_Recipients.Find( index ) != m_Recipients.InvalidIndex() ) + return; + + m_Recipients.AddToTail( index ); +} + +void CRecipientFilter::RemoveAllRecipients( void ) +{ + m_Recipients.RemoveAll(); +} + +void CRecipientFilter::RemoveRecipient( CBasePlayer *player ) +{ + Assert( player ); + + int index = player->entindex(); + + // Remove it if it's in the list + m_Recipients.FindAndRemove( index ); +} + +void CRecipientFilter::AddRecipientsByTeam( CTeam *team ) +{ + Assert( team ); + + int i; + int c = team->GetNumPlayers(); + for ( i = 0 ; i < c ; i++ ) + { + CBasePlayer *player = team->GetPlayer( i ); + if ( !player ) + continue; + + AddRecipient( player ); + } +} + +void CRecipientFilter::RemoveRecipientsByTeam( CTeam *team ) +{ + Assert( team ); + + int i; + int c = team->GetNumPlayers(); + for ( i = 0 ; i < c ; i++ ) + { + CBasePlayer *player = team->GetPlayer( i ); + if ( !player ) + continue; + + RemoveRecipient( player ); + } +} + +void CRecipientFilter::AddPlayersFromBitMask( CBitVec< ABSOLUTE_PLAYER_LIMIT >& playerbits ) +{ + int index = playerbits.FindNextSetBit( 0 ); + + while ( index > -1 ) + { + CBasePlayer *pPlayer = UTIL_PlayerByIndex( index + 1 ); + if ( pPlayer ) + { + AddRecipient( pPlayer ); + } + + index = playerbits.FindNextSetBit( index + 1 ); + } +} + +void CRecipientFilter::RemovePlayersFromBitMask( CBitVec< ABSOLUTE_PLAYER_LIMIT >& playerbits ) +{ + int index = playerbits.FindNextSetBit( 0 ); + + while ( index > -1 ) + { + CBasePlayer *pPlayer = UTIL_PlayerByIndex( index + 1 ); + if ( pPlayer ) + { + RemoveRecipient( pPlayer ); + } + + index = playerbits.FindNextSetBit( index + 1 ); + } +} + +void CRecipientFilter::AddRecipientsByPVS( const Vector& origin ) +{ + if ( gpGlobals->maxClients == 1 ) + { + AddAllPlayers(); + } + else + { + CBitVec< ABSOLUTE_PLAYER_LIMIT > playerbits; + engine->Message_DetermineMulticastRecipients( false, origin, playerbits ); + AddPlayersFromBitMask( playerbits ); + } +} + +void CRecipientFilter::RemoveRecipientsByPVS( const Vector& origin ) +{ + if ( gpGlobals->maxClients == 1 ) + { + m_Recipients.RemoveAll(); + } + else + { + CBitVec< ABSOLUTE_PLAYER_LIMIT > playerbits; + engine->Message_DetermineMulticastRecipients( false, origin, playerbits ); + RemovePlayersFromBitMask( playerbits ); + } +} + + + +void CRecipientFilter::AddRecipientsByPAS( const Vector& origin ) +{ + if ( gpGlobals->maxClients == 1 ) + { + AddAllPlayers(); + } + else + { + CBitVec< ABSOLUTE_PLAYER_LIMIT > playerbits; + engine->Message_DetermineMulticastRecipients( true, origin, playerbits ); + AddPlayersFromBitMask( playerbits ); + } +} + +bool CRecipientFilter::IsInitMessage( void ) const +{ + return m_bInitMessage; +} + +void CRecipientFilter::MakeInitMessage( void ) +{ + m_bInitMessage = true; +} + +void CRecipientFilter::UsePredictionRules( void ) +{ + if ( m_bUsingPredictionRules ) + return; + + m_bUsingPredictionRules = true; + + // Cull list now, if needed + if ( GetRecipientCount() == 0 ) + return; + + CBasePlayer *pPlayer = ToBasePlayer( (CBaseEntity*)g_RecipientFilterPredictionSystem.GetSuppressHost() ); + + if ( pPlayer) + { + RemoveRecipient( pPlayer ); + } +} + +bool CRecipientFilter::IsUsingPredictionRules( void ) const +{ + return m_bUsingPredictionRules; +} + +bool CRecipientFilter:: IgnorePredictionCull( void ) const +{ + return m_bIgnorePredictionCull; +} + +void CRecipientFilter::SetIgnorePredictionCull( bool ignore ) +{ + m_bIgnorePredictionCull = ignore; +} + +//----------------------------------------------------------------------------- +// Purpose: Simple class to create a filter for all players on a given team +//----------------------------------------------------------------------------- +CTeamRecipientFilter::CTeamRecipientFilter( int team, bool isReliable ) +{ + if (isReliable) + MakeReliable(); + + RemoveAllRecipients(); + + for ( int i = 1; i <= gpGlobals->maxClients; i++ ) + { + CBasePlayer *pPlayer = UTIL_PlayerByIndex( i ); + + if ( !pPlayer ) + { + continue; + } + + if ( pPlayer->GetTeamNumber() != team ) + { + continue; + } + + AddRecipient( pPlayer ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : origin - +// ATTN_NORM - +//----------------------------------------------------------------------------- +void CPASAttenuationFilter::Filter( const Vector& origin, float attenuation /*= ATTN_NORM*/ ) +{ + // Don't crop for attenuation in single player + if ( gpGlobals->maxClients == 1 ) + return; + + // CPASFilter adds them by pure PVS in constructor + if ( attenuation <= 0 ) + return; + + // Now remove recipients that are outside sound radius + float distance, maxAudible; + Vector vecRelative; + + int c = GetRecipientCount(); + + for ( int i = c - 1; i >= 0; i-- ) + { + int index = GetRecipientIndex( i ); + + CBaseEntity *ent = CBaseEntity::Instance( index ); + if ( !ent || !ent->IsPlayer() ) + { + Assert( 0 ); + continue; + } + + CBasePlayer *player = ToBasePlayer( ent ); + if ( !player ) + { + Assert( 0 ); + continue; + } + +#ifndef _XBOX + // never remove the HLTV bot + if ( player->IsHLTV() ) + continue; +#endif + + VectorSubtract( player->EarPosition(), origin, vecRelative ); + distance = VectorLength( vecRelative ); + maxAudible = ( 2 * SOUND_NORMAL_CLIP_DIST ) / attenuation; + if ( distance <= maxAudible ) + continue; + + RemoveRecipient( player ); + } +} diff --git a/dlls/sceneentity.cpp b/dlls/sceneentity.cpp index 2b28d625..64fba186 100644 --- a/dlls/sceneentity.cpp +++ b/dlls/sceneentity.cpp @@ -136,7 +136,7 @@ private: CRestoreSceneSound() { actor = NULL; - soundname[ 0 ] = NULL; + soundname[ 0 ] = 0; soundlevel = SNDLVL_NORM; time_in_past = 0.0f; } @@ -3143,9 +3143,9 @@ CChoreoScene *CSceneEntity::BlockingLoadScene( const char *filename ) Q_FixSlashes( loadfile ); // Load the file - char *buffer = NULL; + void *buffer = NULL; - int filesize = filesystem->ReadFileEx( loadfile, ( IsXbox() ) ? "XGAME" : "GAME", (void **)&buffer, true ); + int filesize = filesystem->ReadFileEx( loadfile, ( IsXbox() ) ? "XGAME" : "GAME", &buffer, true ); if ( filesize <= 0 ) { @@ -3153,10 +3153,10 @@ CChoreoScene *CSceneEntity::BlockingLoadScene( const char *filename ) return NULL; } - g_TokenProcessor.SetBuffer( buffer ); + g_TokenProcessor.SetBuffer( reinterpret_cast(buffer) ); CChoreoScene *scene = ChoreoLoadScene( loadfile, NULL, &g_TokenProcessor, LocalScene_Printf ); - delete[] buffer; + delete[] reinterpret_cast(buffer); return scene; } @@ -4242,7 +4242,7 @@ bool CopySceneFileIntoMemory( char const *filename, void **buffer ) //----------------------------------------------------------------------------- void FreeSceneFileMemory( void *buffer ) { - delete[] buffer; + delete[] reinterpret_cast(buffer); } CON_COMMAND( scene_mem, "Spew memory usage for .vcds in the instanced scene resource manager." ) @@ -4645,7 +4645,7 @@ void CSceneManager::PauseActorsScenes( CBaseFlex *pActor, bool bInstancedOnly ) if ( pScene->InvolvesActor( pActor ) && pScene->IsPlayingBack() ) { - LocalScene_Printf( "Pausing actor %s scripted scene: %s\n", pActor->GetDebugName(), pScene->m_iszSceneFile ); + LocalScene_Printf( "Pausing actor %s scripted scene: %s\n", pActor->GetDebugName(), STRING(pScene->m_iszSceneFile) ); variant_t emptyVariant; pScene->AcceptInput( "Pause", pScene, pScene, emptyVariant, 0 ); @@ -4702,7 +4702,7 @@ void CSceneManager::ResumeActorsScenes( CBaseFlex *pActor, bool bInstancedOnly if ( pScene->InvolvesActor( pActor ) && pScene->IsPlayingBack() ) { - LocalScene_Printf( "Resuming actor %s scripted scene: %s\n", pActor->GetDebugName(), pScene->m_iszSceneFile ); + LocalScene_Printf( "Resuming actor %s scripted scene: %s\n", pActor->GetDebugName(), STRING(pScene->m_iszSceneFile) ); variant_t emptyVariant; pScene->AcceptInput( "Resume", pScene, pScene, emptyVariant, 0 ); diff --git a/dlls/sceneentity.h b/dlls/sceneentity.h index 57f2fb5c..9faa6ce2 100644 --- a/dlls/sceneentity.h +++ b/dlls/sceneentity.h @@ -1,46 +1,46 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -//=============================================================================// -#ifndef SCENEENTITY_H -#define SCENEENTITY_H -#ifdef _WIN32 -#pragma once -#endif - -// List of the last 5 lines of speech from NPCs for bug reports -#define SPEECH_LIST_MAX_SOUNDS 5 - -class AI_Response; - -struct recentNPCSpeech_t -{ - float time; - char name[ 512 ]; - char sceneName[ 128 ]; -}; - -int GetRecentNPCSpeech( recentNPCSpeech_t speech[ SPEECH_LIST_MAX_SOUNDS ] ); -float InstancedScriptedScene( CBaseFlex *pActor, const char *pszScene, EHANDLE *phSceneEnt = NULL, float flPostDelay = 0.0f, bool bIsBackground = false, AI_Response *response = NULL ); -float InstancedAutoGeneratedSoundScene( CBaseFlex *pActor, char const *soundname, EHANDLE *phSceneEnt = NULL ); -void StopScriptedScene( CBaseFlex *pActor, EHANDLE hSceneEnt ); -void RemoveActorFromScriptedScenes( CBaseFlex *pActor, bool instancedscenesonly, bool nonidlescenesonly = false, const char *pszThisSceneOnly = NULL ); -void PauseActorsScriptedScenes( CBaseFlex *pActor, bool instancedscenesonly ); -void ResumeActorsScriptedScenes( CBaseFlex *pActor, bool instancedscenesonly ); -void QueueActorsScriptedScenesToResume( CBaseFlex *pActor, bool instancedscenesonly ); -bool IsRunningScriptedScene( CBaseFlex *pActor, bool bIgnoreInstancedScenes = true ); -bool IsRunningScriptedSceneWithSpeech( CBaseFlex *pActor, bool bIgnoreInstancedScenes = false ); -float GetSceneDuration( char const *pszScene ); -int GetSceneSpeechCount( char const *pszScene ); -bool IsInInterruptableScenes( CBaseFlex *pActor ); - -void PrecacheInstancedScene( char const *pszScene ); -void ResetPrecacheInstancedSceneDictionary(); - -char const *GetSceneFilename( CBaseEntity *ent ); -void ReloadSceneFromDisk( CBaseEntity *ent ); - - -#endif // SCENEENTITY_H \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// +#ifndef SCENEENTITY_H +#define SCENEENTITY_H +#ifdef _WIN32 +#pragma once +#endif + +// List of the last 5 lines of speech from NPCs for bug reports +#define SPEECH_LIST_MAX_SOUNDS 5 + +class AI_Response; + +struct recentNPCSpeech_t +{ + float time; + char name[ 512 ]; + char sceneName[ 128 ]; +}; + +int GetRecentNPCSpeech( recentNPCSpeech_t speech[ SPEECH_LIST_MAX_SOUNDS ] ); +float InstancedScriptedScene( CBaseFlex *pActor, const char *pszScene, EHANDLE *phSceneEnt = NULL, float flPostDelay = 0.0f, bool bIsBackground = false, AI_Response *response = NULL ); +float InstancedAutoGeneratedSoundScene( CBaseFlex *pActor, char const *soundname, EHANDLE *phSceneEnt = NULL ); +void StopScriptedScene( CBaseFlex *pActor, EHANDLE hSceneEnt ); +void RemoveActorFromScriptedScenes( CBaseFlex *pActor, bool instancedscenesonly, bool nonidlescenesonly = false, const char *pszThisSceneOnly = NULL ); +void PauseActorsScriptedScenes( CBaseFlex *pActor, bool instancedscenesonly ); +void ResumeActorsScriptedScenes( CBaseFlex *pActor, bool instancedscenesonly ); +void QueueActorsScriptedScenesToResume( CBaseFlex *pActor, bool instancedscenesonly ); +bool IsRunningScriptedScene( CBaseFlex *pActor, bool bIgnoreInstancedScenes = true ); +bool IsRunningScriptedSceneWithSpeech( CBaseFlex *pActor, bool bIgnoreInstancedScenes = false ); +float GetSceneDuration( char const *pszScene ); +int GetSceneSpeechCount( char const *pszScene ); +bool IsInInterruptableScenes( CBaseFlex *pActor ); + +void PrecacheInstancedScene( char const *pszScene ); +void ResetPrecacheInstancedSceneDictionary(); + +char const *GetSceneFilename( CBaseEntity *ent ); +void ReloadSceneFromDisk( CBaseEntity *ent ); + + +#endif // SCENEENTITY_H diff --git a/dlls/scripted.cpp b/dlls/scripted.cpp index 1d2a2a15..16377622 100644 --- a/dlls/scripted.cpp +++ b/dlls/scripted.cpp @@ -745,7 +745,7 @@ void CAI_ScriptedSequence::ScriptThink( void ) else if (FindEntity()) { StartScript( ); - DevMsg( 2, "scripted_sequence \"%s\" using NPC \"%s\"(%s)\n", GetDebugName(), STRING( m_iszEntity ), GetTarget()->GetEntityName() ); + DevMsg( 2, "scripted_sequence \"%s\" using NPC \"%s\"(%s)\n", GetDebugName(), STRING( m_iszEntity ), STRING(GetTarget()->GetEntityName()) ); } else { @@ -1616,7 +1616,7 @@ void CAI_ScriptedSchedule::ScriptThink( void ) pTarget = FindScriptEntity( (m_spawnflags & SF_SCRIPT_SEARCH_CYCLICALLY) != 0 ); if ( pTarget ) { - DevMsg( 2, "scripted_schedule \"%s\" using NPC \"%s\"(%s)\n", GetDebugName(), STRING( m_iszEntity ), pTarget->GetEntityName() ); + DevMsg( 2, "scripted_schedule \"%s\" using NPC \"%s\"(%s)\n", GetDebugName(), STRING( m_iszEntity ), STRING(pTarget->GetEntityName()) ); StartSchedule( pTarget ); success = true; } @@ -1626,7 +1626,7 @@ void CAI_ScriptedSchedule::ScriptThink( void ) m_hLastFoundEntity = NULL; while ( ( pTarget = FindScriptEntity( true ) ) != NULL ) { - DevMsg( 2, "scripted_schedule \"%s\" using NPC \"%s\"(%s)\n", GetDebugName(), pTarget->GetEntityName(), STRING( m_iszEntity ) ); + DevMsg( 2, "scripted_schedule \"%s\" using NPC \"%s\"(%s)\n", GetDebugName(), STRING(pTarget->GetEntityName()), STRING( m_iszEntity ) ); StartSchedule( pTarget ); success = true; } @@ -1714,7 +1714,7 @@ void CAI_ScriptedSchedule::StartSchedule( CAI_BaseNPC *pTarget ) pTarget->ForceDecisionThink(); - Assert( m_nForceState >= 0 && m_nForceState < ARRAYSIZE(forcedStatesMap) ); + Assert( m_nForceState >= 0 && m_nForceState < static_cast(ARRAYSIZE(forcedStatesMap)) ); NPC_STATE forcedState = forcedStatesMap[m_nForceState]; @@ -1790,6 +1790,8 @@ void CAI_ScriptedSchedule::StartSchedule( CAI_BaseNPC *pTarget ) bDidSetSchedule = true; break; } + default: + break; } if ( bDidSetSchedule ) diff --git a/dlls/sdk/sdk_bot_temp.cpp b/dlls/sdk/sdk_bot_temp.cpp index 563f9564..de10ff8c 100644 --- a/dlls/sdk/sdk_bot_temp.cpp +++ b/dlls/sdk/sdk_bot_temp.cpp @@ -253,7 +253,7 @@ void Bot_UpdateDirection( CSDKBot *pBot ) float angledelta = 15.0; QAngle angle; - int maxtries = (int)360.0/angledelta; + int maxtries = static_cast(360.0 / angledelta); if ( pBot->m_bLastTurnToRight ) { diff --git a/dlls/sdk/sdk_env_sparkler.cpp b/dlls/sdk/sdk_env_sparkler.cpp index 9dbbb977..5d8a303a 100644 --- a/dlls/sdk/sdk_env_sparkler.cpp +++ b/dlls/sdk/sdk_env_sparkler.cpp @@ -1,104 +1,104 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: A simple test entity for creating special effects -// -//=============================================================================// - -#include "cbase.h" -#include "te_effect_dispatch.h" - -// Declare the sparkler entity for the server-side -class CSparkler : public CBaseEntity -{ -public: - DECLARE_SERVERCLASS(); - DECLARE_CLASS( CSparkler, CBaseEntity ); - - void Spawn( void ); - - void InputToggle( inputdata_t &input ); // Input function for toggling our effect's state - void InputScale( inputdata_t &input ); - -private: - CNetworkVar( bool, m_bEmit ); // Marks whether the effect should be active or not - CNetworkVar( float, m_flScale ); // The size and speed of the effect - - DECLARE_DATADESC(); -}; - -// Link our class to the "env_sparkler" entity classname -LINK_ENTITY_TO_CLASS( env_sparkler, CSparkler ); - -// Declare our data description for this entity -BEGIN_DATADESC( CSparkler ) - DEFINE_FIELD( m_bEmit, FIELD_BOOLEAN ), - - DEFINE_KEYFIELD( m_flScale, FIELD_FLOAT, "scale" ), - - DEFINE_INPUTFUNC( FIELD_VOID, "Toggle", InputToggle ), // Declare our toggle input function - DEFINE_INPUTFUNC( FIELD_FLOAT, "Scale", InputScale ), -END_DATADESC() - -// Declare the data-table for server/client communication -IMPLEMENT_SERVERCLASS_ST( CSparkler, DT_Sparkler ) - SendPropInt( SENDINFO( m_bEmit ), 1, SPROP_UNSIGNED ), // Declare our boolean state variable - SendPropFloat( SENDINFO( m_flScale ), 0, SPROP_NOSCALE ), -END_SEND_TABLE() - -//----------------------------------------------------------------------------- -// Purpose: Spawn function for this entity -//----------------------------------------------------------------------------- -void CSparkler::Spawn( void ) -{ - SetMoveType( MOVETYPE_NONE ); // Will not move on its own - SetSolid( SOLID_NONE ); // Will not collide with anything - - // Set a size for culling - UTIL_SetSize( this, -Vector(2,2,2), Vector(2,2,2) ); - - // We must add this flag to receive network transmitions - AddEFlags( EFL_FORCE_CHECK_TRANSMIT ); -} - -//----------------------------------------------------------------------------- -// Purpose: Toggles the emission state of the effect -//----------------------------------------------------------------------------- -void CSparkler::InputToggle( inputdata_t &input ) -{ - // Toggle our state - m_bEmit = !m_bEmit; -} - -//----------------------------------------------------------------------------- -// Purpose: Change our scale via a float value -//----------------------------------------------------------------------------- -void CSparkler::InputScale( inputdata_t &input ) -{ - // Change our scale - m_flScale = input.value.Float(); -} - -// ============================================================================ -// -// Dispatch Effect version -// -// ============================================================================ - -//----------------------------------------------------------------------------- -// Purpose: Create a sparkle effect at the given location of the given size -// Input : &position - Where to emit from -// flSize - Size of the effect -//----------------------------------------------------------------------------- -void MakeSparkle( const Vector &origin, float flScale ) -{ - CEffectData data; - - // Send our origin - data.m_vOrigin = origin; - - // Send our scale - data.m_flScale = flScale; - - // Send the effect off to the client - DispatchEffect( "Sparkle", data ); -} \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: A simple test entity for creating special effects +// +//=============================================================================// + +#include "cbase.h" +#include "te_effect_dispatch.h" + +// Declare the sparkler entity for the server-side +class CSparkler : public CBaseEntity +{ +public: + DECLARE_SERVERCLASS(); + DECLARE_CLASS( CSparkler, CBaseEntity ); + + void Spawn( void ); + + void InputToggle( inputdata_t &input ); // Input function for toggling our effect's state + void InputScale( inputdata_t &input ); + +private: + CNetworkVar( bool, m_bEmit ); // Marks whether the effect should be active or not + CNetworkVar( float, m_flScale ); // The size and speed of the effect + + DECLARE_DATADESC(); +}; + +// Link our class to the "env_sparkler" entity classname +LINK_ENTITY_TO_CLASS( env_sparkler, CSparkler ); + +// Declare our data description for this entity +BEGIN_DATADESC( CSparkler ) + DEFINE_FIELD( m_bEmit, FIELD_BOOLEAN ), + + DEFINE_KEYFIELD( m_flScale, FIELD_FLOAT, "scale" ), + + DEFINE_INPUTFUNC( FIELD_VOID, "Toggle", InputToggle ), // Declare our toggle input function + DEFINE_INPUTFUNC( FIELD_FLOAT, "Scale", InputScale ), +END_DATADESC() + +// Declare the data-table for server/client communication +IMPLEMENT_SERVERCLASS_ST( CSparkler, DT_Sparkler ) + SendPropInt( SENDINFO( m_bEmit ), 1, SPROP_UNSIGNED ), // Declare our boolean state variable + SendPropFloat( SENDINFO( m_flScale ), 0, SPROP_NOSCALE ), +END_SEND_TABLE() + +//----------------------------------------------------------------------------- +// Purpose: Spawn function for this entity +//----------------------------------------------------------------------------- +void CSparkler::Spawn( void ) +{ + SetMoveType( MOVETYPE_NONE ); // Will not move on its own + SetSolid( SOLID_NONE ); // Will not collide with anything + + // Set a size for culling + UTIL_SetSize( this, -Vector(2,2,2), Vector(2,2,2) ); + + // We must add this flag to receive network transmitions + AddEFlags( EFL_FORCE_CHECK_TRANSMIT ); +} + +//----------------------------------------------------------------------------- +// Purpose: Toggles the emission state of the effect +//----------------------------------------------------------------------------- +void CSparkler::InputToggle( inputdata_t &input ) +{ + // Toggle our state + m_bEmit = !m_bEmit; +} + +//----------------------------------------------------------------------------- +// Purpose: Change our scale via a float value +//----------------------------------------------------------------------------- +void CSparkler::InputScale( inputdata_t &input ) +{ + // Change our scale + m_flScale = input.value.Float(); +} + +// ============================================================================ +// +// Dispatch Effect version +// +// ============================================================================ + +//----------------------------------------------------------------------------- +// Purpose: Create a sparkle effect at the given location of the given size +// Input : &position - Where to emit from +// flSize - Size of the effect +//----------------------------------------------------------------------------- +void MakeSparkle( const Vector &origin, float flScale ) +{ + CEffectData data; + + // Send our origin + data.m_vOrigin = origin; + + // Send our scale + data.m_flScale = flScale; + + // Send the effect off to the client + DispatchEffect( "Sparkle", data ); +} diff --git a/dlls/sdk/sdk_logicalentity.cpp b/dlls/sdk/sdk_logicalentity.cpp index aee13ef6..6d181fec 100644 --- a/dlls/sdk/sdk_logicalentity.cpp +++ b/dlls/sdk/sdk_logicalentity.cpp @@ -1,66 +1,66 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: Simple logical entity that counts up to a threshold value, then -// fires an output when reached. -// -//=============================================================================// - -#include "cbase.h" - -class CMyLogicalEntity : public CLogicalEntity -{ -public: - DECLARE_CLASS( CMyLogicalEntity , CLogicalEntity ); - DECLARE_DATADESC(); - - // Constructor - CMyLogicalEntity ( void ) : m_nCounter( 0 ) {} - - // Input function - void InputTick( inputdata_t &inputData ); - -private: - - int m_nThreshold; // Count at which to fire our output - int m_nCounter; // Internal counter - - COutputEvent m_OnThreshold; // Output even when the counter reaches the threshold -}; - -LINK_ENTITY_TO_CLASS( my_logical_entity, CMyLogicalEntity ); - -// Start of our data description for the class -BEGIN_DATADESC( CMyLogicalEntity ) - - // For save/load - DEFINE_FIELD( m_nCounter, FIELD_INTEGER ), - - // Links our member variable to our keyvalue from Hammer - DEFINE_KEYFIELD( m_nThreshold, FIELD_INTEGER, "threshold" ), - - // Links our input name from Hammer to our input member function - DEFINE_INPUTFUNC( FIELD_VOID, "Tick", InputTick ), - - // Links our output member to the output name used by Hammer - DEFINE_OUTPUT( m_OnThreshold, "OnThreshold" ), - -END_DATADESC() - -//----------------------------------------------------------------------------- -// Purpose: Handle a tick input from another entity -//----------------------------------------------------------------------------- -void CMyLogicalEntity ::InputTick( inputdata_t &inputData ) -{ - // Increment our counter - m_nCounter++; - - // See if we've met or crossed our threshold value - if ( m_nCounter >= m_nThreshold ) - { - // Fire an output event - m_OnThreshold.FireOutput( inputData.pActivator, this ); - - // Reset our counter - m_nCounter = 0; - } -} \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: Simple logical entity that counts up to a threshold value, then +// fires an output when reached. +// +//=============================================================================// + +#include "cbase.h" + +class CMyLogicalEntity : public CLogicalEntity +{ +public: + DECLARE_CLASS( CMyLogicalEntity , CLogicalEntity ); + DECLARE_DATADESC(); + + // Constructor + CMyLogicalEntity ( void ) : m_nCounter( 0 ) {} + + // Input function + void InputTick( inputdata_t &inputData ); + +private: + + int m_nThreshold; // Count at which to fire our output + int m_nCounter; // Internal counter + + COutputEvent m_OnThreshold; // Output even when the counter reaches the threshold +}; + +LINK_ENTITY_TO_CLASS( my_logical_entity, CMyLogicalEntity ); + +// Start of our data description for the class +BEGIN_DATADESC( CMyLogicalEntity ) + + // For save/load + DEFINE_FIELD( m_nCounter, FIELD_INTEGER ), + + // Links our member variable to our keyvalue from Hammer + DEFINE_KEYFIELD( m_nThreshold, FIELD_INTEGER, "threshold" ), + + // Links our input name from Hammer to our input member function + DEFINE_INPUTFUNC( FIELD_VOID, "Tick", InputTick ), + + // Links our output member to the output name used by Hammer + DEFINE_OUTPUT( m_OnThreshold, "OnThreshold" ), + +END_DATADESC() + +//----------------------------------------------------------------------------- +// Purpose: Handle a tick input from another entity +//----------------------------------------------------------------------------- +void CMyLogicalEntity ::InputTick( inputdata_t &inputData ) +{ + // Increment our counter + m_nCounter++; + + // See if we've met or crossed our threshold value + if ( m_nCounter >= m_nThreshold ) + { + // Fire an output event + m_OnThreshold.FireOutput( inputData.pActivator, this ); + + // Reset our counter + m_nCounter = 0; + } +} diff --git a/dlls/sdk/sdk_vehicle_jeep.cpp b/dlls/sdk/sdk_vehicle_jeep.cpp index dc0917ee..5dada6b4 100644 --- a/dlls/sdk/sdk_vehicle_jeep.cpp +++ b/dlls/sdk/sdk_vehicle_jeep.cpp @@ -1438,8 +1438,8 @@ void CPropJeep::CreateDangerSounds( void ) const float radius = speed * 0.4; // 0.3 seconds ahead of the jeep vecSpot = vecStart + vecDir * (speed * 0.3f); - CSoundEnt::InsertSound( SOUND_DANGER, vecSpot, radius, soundDuration, this, 0 ); - CSoundEnt::InsertSound( SOUND_PHYSICS_DANGER, vecSpot, radius, soundDuration, this, 1 ); + CSoundEnt::InsertSound( SOUND_DANGER, vecSpot, static_cast(radius), soundDuration, this, 0 ); + CSoundEnt::InsertSound( SOUND_PHYSICS_DANGER, vecSpot, static_cast(radius), soundDuration, this, 1 ); //NDebugOverlay::Box(vecSpot, Vector(-radius,-radius,-radius),Vector(radius,radius,radius), 255, 0, 255, 0, soundDuration); #if 0 diff --git a/dlls/sendproxy.cpp b/dlls/sendproxy.cpp index 8b16a7f3..0f47d33e 100644 --- a/dlls/sendproxy.cpp +++ b/dlls/sendproxy.cpp @@ -18,7 +18,8 @@ void SendProxy_Color32ToInt( const SendProp *pProp, const void *pStruct, const void *pData, DVariant *pOut, int iElement, int objectID ) { color32 *pIn = (color32*)pData; - *((unsigned int*)&pOut->m_Int) = ((unsigned int)pIn->r << 24) | ((unsigned int)pIn->g << 16) | ((unsigned int)pIn->b << 8) | ((unsigned int)pIn->a); + void *out = &pOut->m_Int; + *reinterpret_cast(out) = ((unsigned int)pIn->r << 24) | ((unsigned int)pIn->g << 16) | ((unsigned int)pIn->b << 8) | ((unsigned int)pIn->a); } void SendProxy_EHandleToInt( const SendProp *pProp, const void *pStruct, const void *pVarData, DVariant *pOut, int iElement, int objectID) @@ -106,7 +107,7 @@ REGISTER_SEND_PROXY_NON_MODIFIED_POINTER( SendProxy_OnlyToTeam ); #define TIME_BITS 24 // This table encodes edict data. -static void SendProxy_Time( const SendProp *pProp, const void *pStruct, const void *pVarData, DVariant *pOut, int iElement, int objectID ) +/*static void SendProxy_Time( const SendProp *pProp, const void *pStruct, const void *pVarData, DVariant *pOut, int iElement, int objectID ) { float clock_base = floor( gpGlobals->curtime ); float t = *( float * )pVarData; @@ -118,7 +119,7 @@ static void SendProxy_Time( const SendProp *pProp, const void *pStruct, const vo addt = clamp( addt, -maxoffset, maxoffset ); pOut->m_Int = addt; -} +}*/ //----------------------------------------------------------------------------- // Purpose: diff --git a/dlls/server_hl2-2003.vcproj b/dlls/server_hl2-2003.vcproj index 24e46ad0..2cff11a6 100644 --- a/dlls/server_hl2-2003.vcproj +++ b/dlls/server_hl2-2003.vcproj @@ -22,10 +22,10 @@ Name="VCCLCompilerTool" Optimization="0" OptimizeForProcessor="2" - AdditionalIncludeDirectories="../game_shared/hl2,./,../Public,../Public/tier1,../game_shared,../utils/common,../dlls,../../dlls,../dlls/hl2_dll,./episodic" - PreprocessorDefinitions="HL2_EPISODIC;HL2_DLL;USES_SAVERESTORE;_DEBUG;fopen=dont_use_fopen;GAME_DLL;sprintf=use_Q_snprintf_instead_of_sprintf;_WINDOWS;VECTOR;strncpy=use_Q_strncpy_instead;_snprintf=use_Q_snprintf_instead;_WIN32;PROTECTED_THINGS_ENABLE" + AdditionalIncludeDirectories="../game_shared/hl2,./,../Public,../Public/tier1,../game_shared,../utils/common,../dlls,../../dlls,../dlls/hl2_dll,../dlls/episodic" + PreprocessorDefinitions="HL2_EPISODIC;HL2_DLL;USES_SAVERESTORE;_DEBUG;GAME_DLL;sprintf=use_Q_snprintf_instead_of_sprintf;_WINDOWS;VECTOR;strncpy=use_Q_strncpy_instead;_snprintf=use_Q_snprintf_instead;_WIN32;PROTECTED_THINGS_ENABLE" ExceptionHandling="FALSE" - RuntimeLibrary="5" + RuntimeLibrary="1" ForceConformanceInForLoopScope="TRUE" RuntimeTypeInfo="TRUE" UsePrecompiledHeader="3" @@ -54,7 +54,7 @@ if exist "$(TargetDir)"server.dll copy "$(TargetDir)"server. LinkIncremental="2" SuppressStartupBanner="TRUE" AdditionalLibraryDirectories="..\lib-vc7\public" - IgnoreDefaultLibraryNames="LIBCMT,LIBCMTD" + IgnoreDefaultLibraryNames="LIBC,LIBCD,LIBCMT" GenerateDebugInformation="TRUE" ProgramDatabaseFile="$(IntDir)/server.pdb" SubSystem="2" @@ -107,11 +107,11 @@ if exist "$(TargetDir)"server.dll copy "$(TargetDir)"server. EnableIntrinsicFunctions="TRUE" FavorSizeOrSpeed="1" OptimizeForProcessor="3" - AdditionalIncludeDirectories="../game_shared/hl2,./,../Public,../Public/tier1,../game_shared,../utils/common,../dlls,../../dlls,../dlls/hl2_dll,./episodic" + AdditionalIncludeDirectories="../game_shared/hl2,./,../Public,../Public/tier1,../game_shared,../utils/common,../dlls,../../dlls,../dlls/hl2_dll,../dlls/episodic" PreprocessorDefinitions="HL2_EPISODIC;HL2_DLL;USES_SAVERESTORE;NDEBUG;GAME_DLL;sprintf=use_Q_snprintf_instead_of_sprintf;_WINDOWS;VECTOR;strncpy=use_Q_strncpy_instead;_snprintf=use_Q_snprintf_instead;_WIN32;PROTECTED_THINGS_ENABLE" StringPooling="TRUE" ExceptionHandling="FALSE" - RuntimeLibrary="4" + RuntimeLibrary="0" BufferSecurityCheck="FALSE" EnableFunctionLevelLinking="TRUE" ForceConformanceInForLoopScope="TRUE" @@ -145,7 +145,7 @@ if exist "$(TargetDir)"server.pdb copy "$(TargetDir)"server. LinkIncremental="2" SuppressStartupBanner="TRUE" AdditionalLibraryDirectories="..\lib-vc7\public" - IgnoreDefaultLibraryNames="LIBCMT,LIBCMTD" + IgnoreDefaultLibraryNames="LIBC,LIBCD,LIBCMTD" GenerateDebugInformation="TRUE" ProgramDatabaseFile="$(IntDir)/server.pdb" GenerateMapFile="TRUE" @@ -3185,18 +3185,6 @@ if exist "$(TargetDir)"server.pdb copy "$(TargetDir)"server. - - - - - - @@ -4110,24 +4098,28 @@ if exist "$(TargetDir)"server.pdb copy "$(TargetDir)"server. - - - - - - - - - - - - + + + + + + + + + + + + + + diff --git a/dlls/server_hl2-2005.vcproj b/dlls/server_hl2-2005.vcproj index 26163358..a2271001 100644 --- a/dlls/server_hl2-2005.vcproj +++ b/dlls/server_hl2-2005.vcproj @@ -50,8 +50,8 @@ - - - - - - - - - - - - - - - + - - - - - - - - - + - - - + + + + + + + - - - + + + + + + + + + + + + diff --git a/dlls/server_hl2mp-2003.vcproj b/dlls/server_hl2mp-2003.vcproj index fc36efad..940a7532 100644 --- a/dlls/server_hl2mp-2003.vcproj +++ b/dlls/server_hl2mp-2003.vcproj @@ -22,10 +22,10 @@ Name="VCCLCompilerTool" Optimization="0" OptimizeForProcessor="2" - AdditionalIncludeDirectories="../game_shared/hl2,./,../Public,../Public/tier1,../game_shared,../utils/common,../dlls,../../dlls,../dlls/hl2_dll,../dlls/hl2mp_dll,../game_shared/hl2mp,./episodic" - PreprocessorDefinitions="HL2_EPISODIC;HL2MP;HL2_DLL;USES_SAVERESTORE;_DEBUG;fopen=dont_use_fopen;GAME_DLL;sprintf=use_Q_snprintf_instead_of_sprintf;_WINDOWS;VECTOR;strncpy=use_Q_strncpy_instead;_snprintf=use_Q_snprintf_instead;_WIN32;PROTECTED_THINGS_ENABLE" + AdditionalIncludeDirectories="../game_shared/hl2,./,../Public,../Public/tier1,../game_shared,../utils/common,../dlls,../../dlls,../dlls/hl2_dll,../dlls/hl2mp_dll,../game_shared/hl2mp,../dlls/episodic" + PreprocessorDefinitions="HL2_EPISODIC;HL2MP;HL2_DLL;USES_SAVERESTORE;_DEBUG;GAME_DLL;sprintf=use_Q_snprintf_instead_of_sprintf;_WINDOWS;VECTOR;strncpy=use_Q_strncpy_instead;_snprintf=use_Q_snprintf_instead;_WIN32;PROTECTED_THINGS_ENABLE" ExceptionHandling="FALSE" - RuntimeLibrary="5" + RuntimeLibrary="1" ForceConformanceInForLoopScope="TRUE" RuntimeTypeInfo="TRUE" UsePrecompiledHeader="3" @@ -54,7 +54,7 @@ if exist "$(TargetDir)"server.dll copy "$(TargetDir)"server. LinkIncremental="2" SuppressStartupBanner="TRUE" AdditionalLibraryDirectories="..\lib-vc7\public" - IgnoreDefaultLibraryNames="LIBCMT,LIBCMTD" + IgnoreDefaultLibraryNames="LIBC,LIBCD,LIBCMT" GenerateDebugInformation="TRUE" ProgramDatabaseFile="$(IntDir)/server.pdb" SubSystem="2" @@ -107,11 +107,11 @@ if exist "$(TargetDir)"server.dll copy "$(TargetDir)"server. EnableIntrinsicFunctions="TRUE" FavorSizeOrSpeed="1" OptimizeForProcessor="3" - AdditionalIncludeDirectories="../game_shared/hl2,./,../Public,../Public/tier1,../game_shared,../utils/common,../dlls,../../dlls,../dlls/hl2_dll,../dlls/hl2mp_dll,../game_shared/hl2mp,./episodic" + AdditionalIncludeDirectories="../game_shared/hl2,./,../Public,../Public/tier1,../game_shared,../utils/common,../dlls,../../dlls,../dlls/hl2_dll,../dlls/hl2mp_dll,../game_shared/hl2mp,../dlls/episodic" PreprocessorDefinitions="HL2_EPISODIC;HL2MP;HL2_DLL;USES_SAVERESTORE;NDEBUG;GAME_DLL;sprintf=use_Q_snprintf_instead_of_sprintf;_WINDOWS;VECTOR;strncpy=use_Q_strncpy_instead;_snprintf=use_Q_snprintf_instead;_WIN32;PROTECTED_THINGS_ENABLE" StringPooling="TRUE" ExceptionHandling="FALSE" - RuntimeLibrary="4" + RuntimeLibrary="0" BufferSecurityCheck="FALSE" EnableFunctionLevelLinking="TRUE" ForceConformanceInForLoopScope="TRUE" @@ -145,7 +145,7 @@ if exist "$(TargetDir)"server.pdb copy "$(TargetDir)"server. LinkIncremental="2" SuppressStartupBanner="TRUE" AdditionalLibraryDirectories="..\lib-vc7\public" - IgnoreDefaultLibraryNames="LIBCMT,LIBCMTD" + IgnoreDefaultLibraryNames="LIBC,LIBCD,LIBCMTD" GenerateDebugInformation="TRUE" ProgramDatabaseFile="$(IntDir)/server.pdb" GenerateMapFile="TRUE" @@ -3204,18 +3204,6 @@ if exist "$(TargetDir)"server.pdb copy "$(TargetDir)"server. - - - - - - @@ -3439,36 +3427,12 @@ if exist "$(TargetDir)"server.pdb copy "$(TargetDir)"server. Filter=""> - - - - - - - - - - - - @@ -3477,82 +3441,22 @@ if exist "$(TargetDir)"server.pdb copy "$(TargetDir)"server. RelativePath=".\hl2mp_dll\te_hl2mp_shotgun_shot.h"> - - - - - - + RelativePath="..\game_shared\hl2mp\weapon_357.cpp"> - - - - - - - - - - - - - - - - - - - - - - - - @@ -3580,117 +3484,33 @@ if exist "$(TargetDir)"server.pdb copy "$(TargetDir)"server. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -4365,24 +4185,28 @@ if exist "$(TargetDir)"server.pdb copy "$(TargetDir)"server. RelativePath="..\public\zip_uncompressed.h"> - - - - - - - - - - - - + + + + + + + + + + + + + + diff --git a/dlls/server_hl2mp-2005.vcproj b/dlls/server_hl2mp-2005.vcproj index f4471409..d81c1d7f 100644 --- a/dlls/server_hl2mp-2005.vcproj +++ b/dlls/server_hl2mp-2005.vcproj @@ -50,8 +50,8 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -5798,66 +5558,70 @@ > - - - - - - - - - + - - - - - - - - - + - - - + + + + + + + - - - + + + + + + + + + + + + diff --git a/dlls/server_scratch-2003.vcproj b/dlls/server_scratch-2003.vcproj index 244a6a03..c678c643 100644 --- a/dlls/server_scratch-2003.vcproj +++ b/dlls/server_scratch-2003.vcproj @@ -30,7 +30,7 @@ PreprocessorDefinitions="NDEBUG;BOTS;SDK_DLL;USES_SAVERESTORE;GAME_DLL;sprintf=use_Q_snprintf_instead_of_sprintf;_WINDOWS;VECTOR;strncpy=use_Q_strncpy_instead;_snprintf=use_Q_snprintf_instead;_WIN32;PROTECTED_THINGS_ENABLE" StringPooling="TRUE" ExceptionHandling="FALSE" - RuntimeLibrary="4" + RuntimeLibrary="0" BufferSecurityCheck="FALSE" EnableFunctionLevelLinking="TRUE" ForceConformanceInForLoopScope="TRUE" @@ -64,7 +64,7 @@ if exist "$(TargetDir)"server.pdb copy "$(TargetDir)"server. LinkIncremental="2" SuppressStartupBanner="TRUE" AdditionalLibraryDirectories="..\lib-vc7\public" - IgnoreDefaultLibraryNames="LIBCMT,LIBCMTD" + IgnoreDefaultLibraryNames="LIBC,LIBCD,LIBCMTD" GenerateDebugInformation="TRUE" ProgramDatabaseFile="$(IntDir)/server.pdb" GenerateMapFile="TRUE" @@ -115,9 +115,9 @@ if exist "$(TargetDir)"server.pdb copy "$(TargetDir)"server. Optimization="0" OptimizeForProcessor="2" AdditionalIncludeDirectories="../Public/tier1;../game_shared/sdk;./;../Public;../game_shared;../utils/common;../dlls;../dlls/sdk" - PreprocessorDefinitions="_DEBUG;fopen=dont_use_fopen;BOTS;SDK_DLL;USES_SAVERESTORE;GAME_DLL;sprintf=use_Q_snprintf_instead_of_sprintf;_WINDOWS;VECTOR;strncpy=use_Q_strncpy_instead;_snprintf=use_Q_snprintf_instead;_WIN32;PROTECTED_THINGS_ENABLE" + PreprocessorDefinitions="_DEBUG;BOTS;SDK_DLL;USES_SAVERESTORE;GAME_DLL;sprintf=use_Q_snprintf_instead_of_sprintf;_WINDOWS;VECTOR;strncpy=use_Q_strncpy_instead;_snprintf=use_Q_snprintf_instead;_WIN32;PROTECTED_THINGS_ENABLE" ExceptionHandling="FALSE" - RuntimeLibrary="5" + RuntimeLibrary="1" ForceConformanceInForLoopScope="TRUE" RuntimeTypeInfo="TRUE" UsePrecompiledHeader="3" @@ -147,7 +147,7 @@ if exist "$(TargetDir)"server.dll copy "$(TargetDir)"server. LinkIncremental="2" SuppressStartupBanner="TRUE" AdditionalLibraryDirectories="..\lib-vc7\public" - IgnoreDefaultLibraryNames="LIBCMT,LIBCMTD" + IgnoreDefaultLibraryNames="LIBC,LIBCD,LIBCMT" GenerateDebugInformation="TRUE" ProgramDatabaseFile="$(IntDir)/server.pdb" SubSystem="2" @@ -2694,16 +2694,6 @@ if exist "$(TargetDir)"server.dll copy "$(TargetDir)"server. - - - - - - @@ -2728,16 +2718,6 @@ if exist "$(TargetDir)"server.dll copy "$(TargetDir)"server. - - - - - - @@ -2750,16 +2730,6 @@ if exist "$(TargetDir)"server.dll copy "$(TargetDir)"server. - - - - - - @@ -2772,16 +2742,6 @@ if exist "$(TargetDir)"server.dll copy "$(TargetDir)"server. - - - - - - @@ -2791,48 +2751,18 @@ if exist "$(TargetDir)"server.dll copy "$(TargetDir)"server. - - - - - - - - - - - - - - - - - - @@ -2845,60 +2775,18 @@ if exist "$(TargetDir)"server.dll copy "$(TargetDir)"server. - - - - - - - - - - - - - - - - - - - - - - - - @@ -2911,31 +2799,9 @@ if exist "$(TargetDir)"server.dll copy "$(TargetDir)"server. - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + diff --git a/dlls/server_scratch-2005.vcproj b/dlls/server_scratch-2005.vcproj index 25b197e4..79aa5ce6 100644 --- a/dlls/server_scratch-2005.vcproj +++ b/dlls/server_scratch-2005.vcproj @@ -94,7 +94,7 @@ LinkIncremental="1" SuppressStartupBanner="true" AdditionalLibraryDirectories="..\lib\public" - IgnoreDefaultLibraryNames="LIBC,LIBCD" + IgnoreDefaultLibraryNames="LIBC,LIBCD,LIBCMTD" GenerateDebugInformation="true" ProgramDatabaseFile="$(IntDir)/server.pdb" GenerateMapFile="true" @@ -166,7 +166,7 @@ Name="VCCLCompilerTool" Optimization="0" AdditionalIncludeDirectories="../Public/tier1;../game_shared/sdk;./;../Public;../game_shared;../utils/common;../dlls;../dlls/sdk" - PreprocessorDefinitions="_DEBUG;fopen=dont_use_fopen;BOTS;SDK_DLL;USES_SAVERESTORE;GAME_DLL;sprintf=use_Q_snprintf_instead_of_sprintf;_WINDOWS;VECTOR;strncpy=use_Q_strncpy_instead;_snprintf=use_Q_snprintf_instead;_WIN32;PROTECTED_THINGS_ENABLE" + PreprocessorDefinitions="_DEBUG;BOTS;SDK_DLL;USES_SAVERESTORE;GAME_DLL;sprintf=use_Q_snprintf_instead_of_sprintf;_WINDOWS;VECTOR;strncpy=use_Q_strncpy_instead;_snprintf=use_Q_snprintf_instead;_WIN32;PROTECTED_THINGS_ENABLE" ExceptionHandling="0" RuntimeLibrary="1" ForceConformanceInForLoopScope="true" @@ -204,7 +204,7 @@ LinkIncremental="2" SuppressStartupBanner="true" AdditionalLibraryDirectories="..\lib\public" - IgnoreDefaultLibraryNames="LIBC,LIBCD" + IgnoreDefaultLibraryNames="LIBC,LIBCD,LIBCMT" GenerateDebugInformation="true" ProgramDatabaseFile="$(IntDir)/server.pdb" SubSystem="2" @@ -3573,20 +3573,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - + - - - + + + + + + + - - - + + + + + + + + + + + + diff --git a/dlls/sound.cpp b/dlls/sound.cpp index 87dc8a28..9fedb54f 100644 --- a/dlls/sound.cpp +++ b/dlls/sound.cpp @@ -356,7 +356,7 @@ void CAmbientGeneric::ComputeMaxAudibleDistance( ) //----------------------------------------------------------------------------- void CAmbientGeneric::InputPitch( inputdata_t &inputdata ) { - m_dpv.pitch = clamp( inputdata.value.Float(), 0, 255 ); + m_dpv.pitch = clamp( inputdata.value.Int(), 0, 255 ); SendSound( SND_CHANGE_PITCH ); } @@ -371,7 +371,7 @@ void CAmbientGeneric::InputVolume( inputdata_t &inputdata ) // // Multiply the input value by ten since volumes are expected to be from 0 - 100. // - m_dpv.vol = clamp( inputdata.value.Float(), 0, 10 ) * 10; + m_dpv.vol = static_cast(clamp( inputdata.value.Float(), 0, 10 ) * 10); m_dpv.volfrac = m_dpv.vol << 8; SendSound( SND_CHANGE_VOL ); @@ -387,7 +387,7 @@ void CAmbientGeneric::InputFadeIn( inputdata_t &inputdata ) // cancel any fade out that might be happening m_dpv.fadeout = 0; - m_dpv.fadein = inputdata.value.Float(); + m_dpv.fadein = inputdata.value.Int(); if (m_dpv.fadein > 100) m_dpv.fadein = 100; if (m_dpv.fadein < 0) m_dpv.fadein = 0; @@ -407,7 +407,7 @@ void CAmbientGeneric::InputFadeOut( inputdata_t &inputdata ) // cancel any fade in that might be happening m_dpv.fadein = 0; - m_dpv.fadeout = inputdata.value.Float(); + m_dpv.fadeout = inputdata.value.Int(); if (m_dpv.fadeout > 100) m_dpv.fadeout = 100; if (m_dpv.fadeout < 0) m_dpv.fadeout = 0; diff --git a/dlls/soundent.cpp b/dlls/soundent.cpp index 36c8b1b8..4b64473a 100644 --- a/dlls/soundent.cpp +++ b/dlls/soundent.cpp @@ -514,7 +514,7 @@ void CSoundEnt::Initialize ( void ) int i; int iSound; - m_cLastActiveSounds; + m_cLastActiveSounds = 0; m_iFreeSound = 0; m_iActiveSound = SOUNDLIST_EMPTY; diff --git a/dlls/soundscape.cpp b/dlls/soundscape.cpp index 3932dd4e..f27767da 100644 --- a/dlls/soundscape.cpp +++ b/dlls/soundscape.cpp @@ -50,7 +50,7 @@ void CEnvSoundscapeProxy::Activate() { // Copy the relevant parameters from our main soundscape. m_soundscapeIndex = m_hProxySoundscape->m_soundscapeIndex; - for ( int i=0; i < ARRAYSIZE( m_positionNames ); i++ ) + for ( size_t i=0; i < ARRAYSIZE( m_positionNames ); i++ ) m_positionNames[i] = m_hProxySoundscape->m_positionNames[i]; } else @@ -223,7 +223,7 @@ void CEnvSoundscape::WriteAudioParamsTo( audioparams_t &audio ) audio.ent.Set( this ); audio.soundscapeIndex = m_soundscapeIndex; audio.localBits = 0; - for ( int i = 0; i < ARRAYSIZE(m_positionNames); i++ ) + for ( int i = 0; i < static_cast(ARRAYSIZE(m_positionNames)); i++ ) { if ( m_positionNames[i] != NULL_STRING ) { diff --git a/dlls/soundscape_system.cpp b/dlls/soundscape_system.cpp index 49814ef0..7985bc55 100644 --- a/dlls/soundscape_system.cpp +++ b/dlls/soundscape_system.cpp @@ -1,300 +1,300 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -//=============================================================================// - - -#include "cbase.h" -#include "soundscape_system.h" -#include "soundscape.h" -#include "KeyValues.h" -#include "filesystem.h" -#include "game.h" - -// memdbgon must be the last include file in a .cpp file!!! -#include "tier0/memdbgon.h" - -#define SOUNDSCAPE_MANIFEST_FILE "scripts/soundscapes_manifest.txt" - -CON_COMMAND(soundscape_flush, "Flushes the server & client side soundscapes") -{ - g_SoundscapeSystem.FlushSoundscapes(); // don't bother forgetting about the entities - g_SoundscapeSystem.Init(); - engine->ClientCommand( UTIL_GetCommandClient()->edict(), "cl_soundscape_flush\n" ); -} - -CSoundscapeSystem g_SoundscapeSystem( "CSoundscapeSystem" ); - - -void CSoundscapeSystem::AddSoundscapeFile( const char *filename ) -{ - MEM_ALLOC_CREDIT(); - // Open the soundscape data file, and abort if we can't - KeyValues *pKeyValuesData = new KeyValues( filename ); - if ( filesystem->LoadKeyValues( *pKeyValuesData, IFileSystem::TYPE_SOUNDSCAPE, filename, "GAME" ) ) - { - // parse out all of the top level sections and save their names - KeyValues *pKeys = pKeyValuesData; - while ( pKeys ) - { - if ( pKeys->GetFirstSubKey() ) - { - if ( g_pDeveloper->GetBool() ) - { - if ( strstr( pKeys->GetName(), "{" ) ) - { - Msg("Error parsing soundscape file %s after %s\n", filename, m_soundscapeCount>0 ?m_soundscapes.GetStringText( m_soundscapeCount-1 ) : "FIRST" ); - } - } - m_soundscapes.AddString( pKeys->GetName(), m_soundscapeCount ); -#ifdef _XBOX - const char *pStr = pKeys->GetName(); - AddSoundscapeSounds( pKeys, m_soundscapeCount ); -#endif - m_soundscapeCount++; - } - pKeys = pKeys->GetNextKey(); - } - } - pKeyValuesData->deleteThis(); -} - -CON_COMMAND(sv_soundscape_printdebuginfo, "print soundscapes") -{ - g_SoundscapeSystem.PrintDebugInfo(); -} - - -void CSoundscapeSystem::PrintDebugInfo() -{ - Msg( "\n------- SERVER SOUNDSCAPES -------\n" ); - for ( int key=m_soundscapes.First(); key != m_soundscapes.InvalidIndex(); key = m_soundscapes.Next( key ) ) - { - int id = m_soundscapes.GetIDForKey( key ); - const char *pName = m_soundscapes.GetStringForKey( key ); - - Msg( "- %d: %s\n", id, pName ); - } - Msg( "-------- SOUNDSCAPE ENTITIES -----\n" ); - for( int entityIndex = 0; entityIndex < m_soundscapeEntities.Size(); ++entityIndex ) - { - CEnvSoundscape *currentSoundscape = m_soundscapeEntities[entityIndex]; - Msg("- %d: %s x:%.4f y:%.4f z:%.4f\n", - entityIndex, - currentSoundscape->GetSoundscapeName(), - currentSoundscape->GetAbsOrigin().x, - currentSoundscape->GetAbsOrigin().y, - currentSoundscape->GetAbsOrigin().z - ); - } - Msg( "----------------------------------\n\n" ); -} - -bool CSoundscapeSystem::Init() -{ - m_soundscapeCount = 0; - - const char *mapname = STRING( gpGlobals->mapname ); - const char *mapSoundscapeFilename = NULL; - if ( mapname && *mapname ) - { - mapSoundscapeFilename = UTIL_VarArgs( "scripts/soundscapes_%s.txt", mapname ); - } - - KeyValues *manifest = new KeyValues( SOUNDSCAPE_MANIFEST_FILE ); - if ( filesystem->LoadKeyValues( *manifest, IFileSystem::TYPE_SOUNDSCAPE, SOUNDSCAPE_MANIFEST_FILE, "GAME" ) ) - { - for ( KeyValues *sub = manifest->GetFirstSubKey(); sub != NULL; sub = sub->GetNextKey() ) - { - if ( !Q_stricmp( sub->GetName(), "file" ) ) - { - // Add - AddSoundscapeFile( sub->GetString() ); - if ( mapSoundscapeFilename && FStrEq( sub->GetString(), mapSoundscapeFilename ) ) - { - mapSoundscapeFilename = NULL; // we've already loaded the map's soundscape - } - continue; - } - - Warning( "CSoundscapeSystem::Init: Manifest '%s' with bogus file type '%s', expecting 'file'\n", - SOUNDSCAPE_MANIFEST_FILE, sub->GetName() ); - } - - if ( mapSoundscapeFilename && filesystem->FileExists( mapSoundscapeFilename ) ) - { - AddSoundscapeFile( mapSoundscapeFilename ); - } - } - else - { - Error( "Unable to load manifest file '%s'\n", SOUNDSCAPE_MANIFEST_FILE ); - } - manifest->deleteThis(); - m_activeIndex = -1; - - return true; -} - -void CSoundscapeSystem::FlushSoundscapes( void ) -{ - m_soundscapeCount = 0; - m_soundscapes.ClearStrings(); -} - -void CSoundscapeSystem::Shutdown() -{ - FlushSoundscapes(); - m_soundscapeEntities.RemoveAll(); - m_activeIndex = -1; -#ifdef _XBOX - m_soundscapeSounds.Purge(); -#endif - -} - -void CSoundscapeSystem::LevelInitPreEntity() -{ - g_SoundscapeSystem.Shutdown(); - g_SoundscapeSystem.Init(); -} - -void CSoundscapeSystem::LevelInitPostEntity() -{ -#ifdef _XBOX - m_soundscapeSounds.Purge(); -#endif -} - -int CSoundscapeSystem::GetSoundscapeIndex( const char *pName ) -{ - return m_soundscapes.GetStringID( pName ); -} - -bool CSoundscapeSystem::IsValidIndex( int index ) -{ - if ( index >= 0 && index < m_soundscapeCount ) - return true; - return false; -} - -void CSoundscapeSystem::AddSoundscapeEntity( CEnvSoundscape *pSoundscape ) -{ - if ( m_soundscapeEntities.Find( pSoundscape ) == -1 ) - { - m_soundscapeEntities.AddToTail( pSoundscape ); - } -} - -void CSoundscapeSystem::RemoveSoundscapeEntity( CEnvSoundscape *pSoundscape ) -{ - m_soundscapeEntities.FindAndRemove( pSoundscape ); -} - -void CSoundscapeSystem::FrameUpdatePostEntityThink() -{ - int total = m_soundscapeEntities.Count(); - if ( total > 0 ) - { - if ( !m_soundscapeEntities.IsValidIndex(m_activeIndex) ) - { - m_activeIndex = 0; - } - - // update 2 soundscape entities each tick - int count = min(2, total); - for ( int i = 0; i < count; i++ ) - { - m_activeIndex++; - m_activeIndex = m_activeIndex % total; - m_soundscapeEntities[m_activeIndex]->Update(); - } - } -} - -#ifdef _XBOX -void CSoundscapeSystem::AddSoundscapeSounds( KeyValues *pSoundscape, int soundscapeIndex ) -{ - int i = m_soundscapeSounds.AddToTail(); - Assert( i == soundscapeIndex ); - - KeyValues *pKey = pSoundscape->GetFirstSubKey(); - while ( pKey ) - { - if ( !Q_strcasecmp( pKey->GetName(), "playlooping" ) ) - { - KeyValues *pAmbientKey = pKey->GetFirstSubKey(); - while ( pAmbientKey ) - { - if ( !Q_strcasecmp( pAmbientKey->GetName(), "wave" ) ) - { - char const *pSoundName = pAmbientKey->GetString(); - m_soundscapeSounds[i].AddToTail( pSoundName ); - } - pAmbientKey = pAmbientKey->GetNextKey(); - } - } - else if ( !Q_strcasecmp( pKey->GetName(), "playrandom" ) ) - { - KeyValues *pRandomKey = pKey->GetFirstSubKey(); - while ( pRandomKey ) - { - if ( !Q_strcasecmp( pRandomKey->GetName(), "rndwave" ) ) - { - KeyValues *pRndWaveKey = pRandomKey->GetFirstSubKey(); - while ( pRndWaveKey ) - { - if ( !Q_strcasecmp( pRndWaveKey->GetName(), "wave" ) ) - { - char const *pSoundName = pRndWaveKey->GetString(); - m_soundscapeSounds[i].AddToTail( pSoundName ); - } - pRndWaveKey = pRndWaveKey->GetNextKey(); - } - } - pRandomKey = pRandomKey->GetNextKey(); - } - } - else if ( !Q_strcasecmp( pKey->GetName(), "playsoundscape" ) ) - { - KeyValues *pPlayKey = pKey->GetFirstSubKey(); - while ( pPlayKey ) - { - if ( !Q_strcasecmp( pPlayKey->GetName(), "name" ) ) - { - char const *pSoundName = pPlayKey->GetString(); - m_soundscapeSounds[i].AddToTail( pSoundName ); - } - pPlayKey = pPlayKey->GetNextKey(); - } - } - pKey = pKey->GetNextKey(); - } -} -#endif - -#ifdef _XBOX -void CSoundscapeSystem::PrecacheSounds( int soundscapeIndex ) -{ - if ( !IsValidIndex( soundscapeIndex ) ) - { - return; - } - - int count = m_soundscapeSounds[soundscapeIndex].Count(); - for ( int i=0; iClientCommand( UTIL_GetCommandClient()->edict(), "cl_soundscape_flush\n" ); +} + +CSoundscapeSystem g_SoundscapeSystem( "CSoundscapeSystem" ); + + +void CSoundscapeSystem::AddSoundscapeFile( const char *filename ) +{ + MEM_ALLOC_CREDIT(); + // Open the soundscape data file, and abort if we can't + KeyValues *pKeyValuesData = new KeyValues( filename ); + if ( filesystem->LoadKeyValues( *pKeyValuesData, IFileSystem::TYPE_SOUNDSCAPE, filename, "GAME" ) ) + { + // parse out all of the top level sections and save their names + KeyValues *pKeys = pKeyValuesData; + while ( pKeys ) + { + if ( pKeys->GetFirstSubKey() ) + { + if ( g_pDeveloper->GetBool() ) + { + if ( strstr( pKeys->GetName(), "{" ) ) + { + Msg("Error parsing soundscape file %s after %s\n", filename, m_soundscapeCount>0 ?m_soundscapes.GetStringText( m_soundscapeCount-1 ) : "FIRST" ); + } + } + m_soundscapes.AddString( pKeys->GetName(), m_soundscapeCount ); +#ifdef _XBOX + const char *pStr = pKeys->GetName(); + AddSoundscapeSounds( pKeys, m_soundscapeCount ); +#endif + m_soundscapeCount++; + } + pKeys = pKeys->GetNextKey(); + } + } + pKeyValuesData->deleteThis(); +} + +CON_COMMAND(sv_soundscape_printdebuginfo, "print soundscapes") +{ + g_SoundscapeSystem.PrintDebugInfo(); +} + + +void CSoundscapeSystem::PrintDebugInfo() +{ + Msg( "\n------- SERVER SOUNDSCAPES -------\n" ); + for ( int key=m_soundscapes.First(); key != m_soundscapes.InvalidIndex(); key = m_soundscapes.Next( key ) ) + { + int id = m_soundscapes.GetIDForKey( key ); + const char *pName = m_soundscapes.GetStringForKey( key ); + + Msg( "- %d: %s\n", id, pName ); + } + Msg( "-------- SOUNDSCAPE ENTITIES -----\n" ); + for( int entityIndex = 0; entityIndex < m_soundscapeEntities.Size(); ++entityIndex ) + { + CEnvSoundscape *currentSoundscape = m_soundscapeEntities[entityIndex]; + Msg("- %d: %s x:%.4f y:%.4f z:%.4f\n", + entityIndex, + STRING(currentSoundscape->GetSoundscapeName()), + currentSoundscape->GetAbsOrigin().x, + currentSoundscape->GetAbsOrigin().y, + currentSoundscape->GetAbsOrigin().z + ); + } + Msg( "----------------------------------\n\n" ); +} + +bool CSoundscapeSystem::Init() +{ + m_soundscapeCount = 0; + + const char *mapname = STRING( gpGlobals->mapname ); + const char *mapSoundscapeFilename = NULL; + if ( mapname && *mapname ) + { + mapSoundscapeFilename = UTIL_VarArgs( "scripts/soundscapes_%s.txt", mapname ); + } + + KeyValues *manifest = new KeyValues( SOUNDSCAPE_MANIFEST_FILE ); + if ( filesystem->LoadKeyValues( *manifest, IFileSystem::TYPE_SOUNDSCAPE, SOUNDSCAPE_MANIFEST_FILE, "GAME" ) ) + { + for ( KeyValues *sub = manifest->GetFirstSubKey(); sub != NULL; sub = sub->GetNextKey() ) + { + if ( !Q_stricmp( sub->GetName(), "file" ) ) + { + // Add + AddSoundscapeFile( sub->GetString() ); + if ( mapSoundscapeFilename && FStrEq( sub->GetString(), mapSoundscapeFilename ) ) + { + mapSoundscapeFilename = NULL; // we've already loaded the map's soundscape + } + continue; + } + + Warning( "CSoundscapeSystem::Init: Manifest '%s' with bogus file type '%s', expecting 'file'\n", + SOUNDSCAPE_MANIFEST_FILE, sub->GetName() ); + } + + if ( mapSoundscapeFilename && filesystem->FileExists( mapSoundscapeFilename ) ) + { + AddSoundscapeFile( mapSoundscapeFilename ); + } + } + else + { + Error( "Unable to load manifest file '%s'\n", SOUNDSCAPE_MANIFEST_FILE ); + } + manifest->deleteThis(); + m_activeIndex = -1; + + return true; +} + +void CSoundscapeSystem::FlushSoundscapes( void ) +{ + m_soundscapeCount = 0; + m_soundscapes.ClearStrings(); +} + +void CSoundscapeSystem::Shutdown() +{ + FlushSoundscapes(); + m_soundscapeEntities.RemoveAll(); + m_activeIndex = -1; +#ifdef _XBOX + m_soundscapeSounds.Purge(); +#endif + +} + +void CSoundscapeSystem::LevelInitPreEntity() +{ + g_SoundscapeSystem.Shutdown(); + g_SoundscapeSystem.Init(); +} + +void CSoundscapeSystem::LevelInitPostEntity() +{ +#ifdef _XBOX + m_soundscapeSounds.Purge(); +#endif +} + +int CSoundscapeSystem::GetSoundscapeIndex( const char *pName ) +{ + return m_soundscapes.GetStringID( pName ); +} + +bool CSoundscapeSystem::IsValidIndex( int index ) +{ + if ( index >= 0 && index < m_soundscapeCount ) + return true; + return false; +} + +void CSoundscapeSystem::AddSoundscapeEntity( CEnvSoundscape *pSoundscape ) +{ + if ( m_soundscapeEntities.Find( pSoundscape ) == -1 ) + { + m_soundscapeEntities.AddToTail( pSoundscape ); + } +} + +void CSoundscapeSystem::RemoveSoundscapeEntity( CEnvSoundscape *pSoundscape ) +{ + m_soundscapeEntities.FindAndRemove( pSoundscape ); +} + +void CSoundscapeSystem::FrameUpdatePostEntityThink() +{ + int total = m_soundscapeEntities.Count(); + if ( total > 0 ) + { + if ( !m_soundscapeEntities.IsValidIndex(m_activeIndex) ) + { + m_activeIndex = 0; + } + + // update 2 soundscape entities each tick + int count = min(2, total); + for ( int i = 0; i < count; i++ ) + { + m_activeIndex++; + m_activeIndex = m_activeIndex % total; + m_soundscapeEntities[m_activeIndex]->Update(); + } + } +} + +#ifdef _XBOX +void CSoundscapeSystem::AddSoundscapeSounds( KeyValues *pSoundscape, int soundscapeIndex ) +{ + int i = m_soundscapeSounds.AddToTail(); + Assert( i == soundscapeIndex ); + + KeyValues *pKey = pSoundscape->GetFirstSubKey(); + while ( pKey ) + { + if ( !Q_strcasecmp( pKey->GetName(), "playlooping" ) ) + { + KeyValues *pAmbientKey = pKey->GetFirstSubKey(); + while ( pAmbientKey ) + { + if ( !Q_strcasecmp( pAmbientKey->GetName(), "wave" ) ) + { + char const *pSoundName = pAmbientKey->GetString(); + m_soundscapeSounds[i].AddToTail( pSoundName ); + } + pAmbientKey = pAmbientKey->GetNextKey(); + } + } + else if ( !Q_strcasecmp( pKey->GetName(), "playrandom" ) ) + { + KeyValues *pRandomKey = pKey->GetFirstSubKey(); + while ( pRandomKey ) + { + if ( !Q_strcasecmp( pRandomKey->GetName(), "rndwave" ) ) + { + KeyValues *pRndWaveKey = pRandomKey->GetFirstSubKey(); + while ( pRndWaveKey ) + { + if ( !Q_strcasecmp( pRndWaveKey->GetName(), "wave" ) ) + { + char const *pSoundName = pRndWaveKey->GetString(); + m_soundscapeSounds[i].AddToTail( pSoundName ); + } + pRndWaveKey = pRndWaveKey->GetNextKey(); + } + } + pRandomKey = pRandomKey->GetNextKey(); + } + } + else if ( !Q_strcasecmp( pKey->GetName(), "playsoundscape" ) ) + { + KeyValues *pPlayKey = pKey->GetFirstSubKey(); + while ( pPlayKey ) + { + if ( !Q_strcasecmp( pPlayKey->GetName(), "name" ) ) + { + char const *pSoundName = pPlayKey->GetString(); + m_soundscapeSounds[i].AddToTail( pSoundName ); + } + pPlayKey = pPlayKey->GetNextKey(); + } + } + pKey = pKey->GetNextKey(); + } +} +#endif + +#ifdef _XBOX +void CSoundscapeSystem::PrecacheSounds( int soundscapeIndex ) +{ + if ( !IsValidIndex( soundscapeIndex ) ) + { + return; + } + + int count = m_soundscapeSounds[soundscapeIndex].Count(); + for ( int i=0; iGetMaxSpeed() ) { - return 90 + (fabs(pTrain->GetCurrentSpeed()) * (20) / pTrain->GetMaxSpeed()); + return static_cast(90 + (fabs(pTrain->GetCurrentSpeed()) * (20) / pTrain->GetMaxSpeed())); } return 100; } diff --git a/dlls/te.h b/dlls/te.h index 1070a665..acd8e00c 100644 --- a/dlls/te.h +++ b/dlls/te.h @@ -1,17 +1,17 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $Workfile: $ -// $NoKeywords: $ -//=============================================================================// - -#if !defined( TE_H ) -#define TE_H -#ifdef _WIN32 -#pragma once -#endif - -#include "itempents.h" - -#endif // TE_H \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $Workfile: $ +// $NoKeywords: $ +//=============================================================================// + +#if !defined( TE_H ) +#define TE_H +#ifdef _WIN32 +#pragma once +#endif + +#include "itempents.h" + +#endif // TE_H diff --git a/dlls/te_basebeam.h b/dlls/te_basebeam.h index c08e48fa..0a18b12b 100644 --- a/dlls/te_basebeam.h +++ b/dlls/te_basebeam.h @@ -1,59 +1,59 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $Workfile: $ -// $Date: $ -// -//----------------------------------------------------------------------------- -// $Log: $ -// -// $NoKeywords: $ -//=============================================================================// - -//----------------------------------------------------------------------------- -// Purpose: Dispatches a beam ring between two entities -//----------------------------------------------------------------------------- -#if !defined( TE_BASEBEAM_H ) -#define TE_BASEBEAM_H -#ifdef _WIN32 -#pragma once -#endif - -#include "basetempentity.h" - -abstract_class CTEBaseBeam : public CBaseTempEntity -{ -public: - - DECLARE_CLASS( CTEBaseBeam, CBaseTempEntity ); - DECLARE_SERVERCLASS(); - - -public: - CTEBaseBeam( const char *name ); - virtual ~CTEBaseBeam( void ); - - virtual void Test( const Vector& current_origin, const QAngle& current_angles ) = 0; - -public: - CNetworkVar( int, m_nModelIndex ); - CNetworkVar( int, m_nHaloIndex ); - CNetworkVar( int, m_nStartFrame ); - CNetworkVar( int, m_nFrameRate ); - CNetworkVar( float, m_fLife ); - CNetworkVar( float, m_fWidth ); - CNetworkVar( float, m_fEndWidth ); - CNetworkVar( int, m_nFadeLength ); - CNetworkVar( float, m_fAmplitude ); - CNetworkVar( int, r ); - CNetworkVar( int, g ); - CNetworkVar( int, b ); - CNetworkVar( int, a ); - CNetworkVar( int, m_nSpeed ); - CNetworkVar( int, m_nFlags ); -}; - -EXTERN_SEND_TABLE(DT_BaseBeam); - -#endif // TE_BASEBEAM_H \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $Workfile: $ +// $Date: $ +// +//----------------------------------------------------------------------------- +// $Log: $ +// +// $NoKeywords: $ +//=============================================================================// + +//----------------------------------------------------------------------------- +// Purpose: Dispatches a beam ring between two entities +//----------------------------------------------------------------------------- +#if !defined( TE_BASEBEAM_H ) +#define TE_BASEBEAM_H +#ifdef _WIN32 +#pragma once +#endif + +#include "basetempentity.h" + +abstract_class CTEBaseBeam : public CBaseTempEntity +{ +public: + + DECLARE_CLASS( CTEBaseBeam, CBaseTempEntity ); + DECLARE_SERVERCLASS(); + + +public: + CTEBaseBeam( const char *name ); + virtual ~CTEBaseBeam( void ); + + virtual void Test( const Vector& current_origin, const QAngle& current_angles ) = 0; + +public: + CNetworkVar( int, m_nModelIndex ); + CNetworkVar( int, m_nHaloIndex ); + CNetworkVar( int, m_nStartFrame ); + CNetworkVar( int, m_nFrameRate ); + CNetworkVar( float, m_fLife ); + CNetworkVar( float, m_fWidth ); + CNetworkVar( float, m_fEndWidth ); + CNetworkVar( int, m_nFadeLength ); + CNetworkVar( float, m_fAmplitude ); + CNetworkVar( int, r ); + CNetworkVar( int, g ); + CNetworkVar( int, b ); + CNetworkVar( int, a ); + CNetworkVar( int, m_nSpeed ); + CNetworkVar( int, m_nFlags ); +}; + +EXTERN_SEND_TABLE(DT_BaseBeam); + +#endif // TE_BASEBEAM_H diff --git a/dlls/te_beamentpoint.cpp b/dlls/te_beamentpoint.cpp index 280e9a5d..d379dd41 100644 --- a/dlls/te_beamentpoint.cpp +++ b/dlls/te_beamentpoint.cpp @@ -1,154 +1,154 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $Workfile: $ -// $Date: $ -// -//----------------------------------------------------------------------------- -// $Log: $ -// -// $NoKeywords: $ -//=============================================================================// -#include "cbase.h" -#include "basetempentity.h" -#include "te_basebeam.h" - -// memdbgon must be the last include file in a .cpp file!!! -#include "tier0/memdbgon.h" - -extern short g_sModelIndexSmoke; // (in combatweapon.cpp) holds the index for the smoke cloud - -//----------------------------------------------------------------------------- -// Purpose: Dispatches a beam ring between two entities -//----------------------------------------------------------------------------- -class CTEBeamEntPoint : public CTEBaseBeam -{ -public: - DECLARE_CLASS( CTEBeamEntPoint, CTEBaseBeam ); - DECLARE_SERVERCLASS(); - - CTEBeamEntPoint( const char *name ); - virtual ~CTEBeamEntPoint( void ); - - virtual void Test( const Vector& current_origin, const QAngle& current_angles ); - - -public: - CNetworkVar( int, m_nStartEntity ); - CNetworkVector( m_vecStartPoint ); - CNetworkVar( int, m_nEndEntity ); - CNetworkVector( m_vecEndPoint ); -}; - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *name - -//----------------------------------------------------------------------------- -CTEBeamEntPoint::CTEBeamEntPoint( const char *name ) : - CTEBaseBeam( name ) -{ - m_nStartEntity = 0; - m_nEndEntity = 0; - m_vecStartPoint.Init(); - m_vecEndPoint.Init(); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -CTEBeamEntPoint::~CTEBeamEntPoint( void ) -{ -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *current_origin - -// *current_angles - -//----------------------------------------------------------------------------- -void CTEBeamEntPoint::Test( const Vector& current_origin, const QAngle& current_angles ) -{ - m_nStartEntity = 1; - - m_nModelIndex = g_sModelIndexSmoke; - m_nStartFrame = 0; - m_nFrameRate = 10; - m_fLife = 2.0; - m_fWidth = 1.0; - m_fAmplitude = 1.0; - r = 0; - g = 63; - b = 127; - a = 150; - m_nSpeed = 1; - - m_vecEndPoint = current_origin; - - Vector forward, right; - - m_vecEndPoint += Vector( 0, 0, 24 ); - - AngleVectors( current_angles, &forward, &right, 0 ); - forward[2] = 0.0; - VectorNormalize( forward ); - - VectorMA( m_vecEndPoint, 50.0, forward, m_vecEndPoint.GetForModify() ); - - CBroadcastRecipientFilter filter; - Create( filter, 0.0 ); -} - -IMPLEMENT_SERVERCLASS_ST(CTEBeamEntPoint, DT_TEBeamEntPoint) - SendPropInt( SENDINFO(m_nStartEntity), 24, SPROP_UNSIGNED ), - SendPropInt( SENDINFO(m_nEndEntity), 24, SPROP_UNSIGNED ), - SendPropVector( SENDINFO(m_vecStartPoint), -1, SPROP_COORD ), - SendPropVector( SENDINFO(m_vecEndPoint), -1, SPROP_COORD ), -END_SEND_TABLE() - - -// Singleton to fire TEBeamEntPoint objects -static CTEBeamEntPoint g_TEBeamEntPoint( "BeamEntPoint" ); - -//----------------------------------------------------------------------------- -// Purpose: -// Input : msg_dest - -// delay - -// *origin - -// *recipient - -// int start - -// *end - -// modelindex - -// startframe - -// framerate - -// msg_dest - -// delay - -// origin - -// recipient - -//----------------------------------------------------------------------------- -void TE_BeamEntPoint( IRecipientFilter& filter, float delay, - int nStartEntity, const Vector *start, int nEndEntity, const Vector* end, - int modelindex, int haloindex, int startframe, int framerate, - float life, float width, float endWidth, int fadeLength, float amplitude, int r, int g, int b, int a, int speed ) -{ - g_TEBeamEntPoint.m_nStartEntity = (nStartEntity > 0) ? (nStartEntity & 0x0FFF) | ((1 & 0xF)<<12) : 0; - g_TEBeamEntPoint.m_nEndEntity = (nEndEntity > 0) ? (nEndEntity & 0x0FFF) | ((1 & 0xF)<<12) : 0; - g_TEBeamEntPoint.m_vecStartPoint = start ? *start : vec3_origin; - g_TEBeamEntPoint.m_vecEndPoint = end ? *end : vec3_origin; - g_TEBeamEntPoint.m_nModelIndex = modelindex; - g_TEBeamEntPoint.m_nHaloIndex = haloindex; - g_TEBeamEntPoint.m_nStartFrame = startframe; - g_TEBeamEntPoint.m_nFrameRate = framerate; - g_TEBeamEntPoint.m_fLife = life; - g_TEBeamEntPoint.m_fWidth = width; - g_TEBeamEntPoint.m_fEndWidth = endWidth; - g_TEBeamEntPoint.m_nFadeLength = fadeLength; - g_TEBeamEntPoint.m_fAmplitude = amplitude; - g_TEBeamEntPoint.m_nSpeed = speed; - g_TEBeamEntPoint.r = r; - g_TEBeamEntPoint.g = g; - g_TEBeamEntPoint.b = b; - g_TEBeamEntPoint.a = a; - - // Send it over the wire - g_TEBeamEntPoint.Create( filter, delay ); -} \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $Workfile: $ +// $Date: $ +// +//----------------------------------------------------------------------------- +// $Log: $ +// +// $NoKeywords: $ +//=============================================================================// +#include "cbase.h" +#include "basetempentity.h" +#include "te_basebeam.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +extern short g_sModelIndexSmoke; // (in combatweapon.cpp) holds the index for the smoke cloud + +//----------------------------------------------------------------------------- +// Purpose: Dispatches a beam ring between two entities +//----------------------------------------------------------------------------- +class CTEBeamEntPoint : public CTEBaseBeam +{ +public: + DECLARE_CLASS( CTEBeamEntPoint, CTEBaseBeam ); + DECLARE_SERVERCLASS(); + + CTEBeamEntPoint( const char *name ); + virtual ~CTEBeamEntPoint( void ); + + virtual void Test( const Vector& current_origin, const QAngle& current_angles ); + + +public: + CNetworkVar( int, m_nStartEntity ); + CNetworkVector( m_vecStartPoint ); + CNetworkVar( int, m_nEndEntity ); + CNetworkVector( m_vecEndPoint ); +}; + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *name - +//----------------------------------------------------------------------------- +CTEBeamEntPoint::CTEBeamEntPoint( const char *name ) : + CTEBaseBeam( name ) +{ + m_nStartEntity = 0; + m_nEndEntity = 0; + m_vecStartPoint.Init(); + m_vecEndPoint.Init(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CTEBeamEntPoint::~CTEBeamEntPoint( void ) +{ +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *current_origin - +// *current_angles - +//----------------------------------------------------------------------------- +void CTEBeamEntPoint::Test( const Vector& current_origin, const QAngle& current_angles ) +{ + m_nStartEntity = 1; + + m_nModelIndex = g_sModelIndexSmoke; + m_nStartFrame = 0; + m_nFrameRate = 10; + m_fLife = 2.0; + m_fWidth = 1.0; + m_fAmplitude = 1.0; + r = 0; + g = 63; + b = 127; + a = 150; + m_nSpeed = 1; + + m_vecEndPoint = current_origin; + + Vector forward, right; + + m_vecEndPoint += Vector( 0, 0, 24 ); + + AngleVectors( current_angles, &forward, &right, 0 ); + forward[2] = 0.0; + VectorNormalize( forward ); + + VectorMA( m_vecEndPoint, 50.0, forward, m_vecEndPoint.GetForModify() ); + + CBroadcastRecipientFilter filter; + Create( filter, 0.0 ); +} + +IMPLEMENT_SERVERCLASS_ST(CTEBeamEntPoint, DT_TEBeamEntPoint) + SendPropInt( SENDINFO(m_nStartEntity), 24, SPROP_UNSIGNED ), + SendPropInt( SENDINFO(m_nEndEntity), 24, SPROP_UNSIGNED ), + SendPropVector( SENDINFO(m_vecStartPoint), -1, SPROP_COORD ), + SendPropVector( SENDINFO(m_vecEndPoint), -1, SPROP_COORD ), +END_SEND_TABLE() + + +// Singleton to fire TEBeamEntPoint objects +static CTEBeamEntPoint g_TEBeamEntPoint( "BeamEntPoint" ); + +//----------------------------------------------------------------------------- +// Purpose: +// Input : msg_dest - +// delay - +// *origin - +// *recipient - +// int start - +// *end - +// modelindex - +// startframe - +// framerate - +// msg_dest - +// delay - +// origin - +// recipient - +//----------------------------------------------------------------------------- +void TE_BeamEntPoint( IRecipientFilter& filter, float delay, + int nStartEntity, const Vector *start, int nEndEntity, const Vector* end, + int modelindex, int haloindex, int startframe, int framerate, + float life, float width, float endWidth, int fadeLength, float amplitude, int r, int g, int b, int a, int speed ) +{ + g_TEBeamEntPoint.m_nStartEntity = (nStartEntity > 0) ? (nStartEntity & 0x0FFF) | ((1 & 0xF)<<12) : 0; + g_TEBeamEntPoint.m_nEndEntity = (nEndEntity > 0) ? (nEndEntity & 0x0FFF) | ((1 & 0xF)<<12) : 0; + g_TEBeamEntPoint.m_vecStartPoint = start ? *start : vec3_origin; + g_TEBeamEntPoint.m_vecEndPoint = end ? *end : vec3_origin; + g_TEBeamEntPoint.m_nModelIndex = modelindex; + g_TEBeamEntPoint.m_nHaloIndex = haloindex; + g_TEBeamEntPoint.m_nStartFrame = startframe; + g_TEBeamEntPoint.m_nFrameRate = framerate; + g_TEBeamEntPoint.m_fLife = life; + g_TEBeamEntPoint.m_fWidth = width; + g_TEBeamEntPoint.m_fEndWidth = endWidth; + g_TEBeamEntPoint.m_nFadeLength = fadeLength; + g_TEBeamEntPoint.m_fAmplitude = amplitude; + g_TEBeamEntPoint.m_nSpeed = speed; + g_TEBeamEntPoint.r = r; + g_TEBeamEntPoint.g = g; + g_TEBeamEntPoint.b = b; + g_TEBeamEntPoint.a = a; + + // Send it over the wire + g_TEBeamEntPoint.Create( filter, delay ); +} diff --git a/dlls/te_beaments.cpp b/dlls/te_beaments.cpp index b17e2464..c1240e1b 100644 --- a/dlls/te_beaments.cpp +++ b/dlls/te_beaments.cpp @@ -1,134 +1,134 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $Workfile: $ -// $Date: $ -// -//----------------------------------------------------------------------------- -// $Log: $ -// -// $NoKeywords: $ -//=============================================================================// -#include "cbase.h" -#include "basetempentity.h" -#include "te_basebeam.h" - -// memdbgon must be the last include file in a .cpp file!!! -#include "tier0/memdbgon.h" - -extern short g_sModelIndexSmoke; // (in combatweapon.cpp) holds the index for the smoke cloud - -//----------------------------------------------------------------------------- -// Purpose: Dispatches a beam between two entities -//----------------------------------------------------------------------------- -class CTEBeamEnts : public CTEBaseBeam -{ -public: - DECLARE_CLASS( CTEBeamEnts, CTEBaseBeam ); - DECLARE_SERVERCLASS(); - - CTEBeamEnts( const char *name ); - virtual ~CTEBeamEnts( void ); - - virtual void Test( const Vector& current_origin, const QAngle& current_angles ); - - -public: - CNetworkVar( int, m_nStartEntity ); - CNetworkVar( int, m_nEndEntity ); -}; - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *name - -//----------------------------------------------------------------------------- -CTEBeamEnts::CTEBeamEnts( const char *name ) : - CTEBaseBeam( name ) -{ - m_nStartEntity = 0; - m_nEndEntity = 0; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -CTEBeamEnts::~CTEBeamEnts( void ) -{ -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *current_origin - -// *current_angles - -//----------------------------------------------------------------------------- -void CTEBeamEnts::Test( const Vector& current_origin, const QAngle& current_angles ) -{ - m_nStartEntity = 1; - m_nEndEntity = 0; - - m_nModelIndex = g_sModelIndexSmoke; - m_nStartFrame = 0; - m_nFrameRate = 10; - m_fLife = 2.0; - m_fWidth = 1.0; - m_fAmplitude = 1; - r = 127; - g = 63; - b = 0; - a = 150; - m_nSpeed = 1; - - CBroadcastRecipientFilter filter; - Create( filter, 0.0 ); -} - -IMPLEMENT_SERVERCLASS_ST(CTEBeamEnts, DT_TEBeamEnts) - SendPropInt( SENDINFO(m_nStartEntity), 24, SPROP_UNSIGNED ), - SendPropInt( SENDINFO(m_nEndEntity), 24, SPROP_UNSIGNED ), -END_SEND_TABLE() - - -// Singleton to fire TEBeamEnts objects -static CTEBeamEnts g_TEBeamEnts( "BeamEnts" ); - -//----------------------------------------------------------------------------- -// Purpose: -// Input : msg_dest - -// delay - -// *origin - -// *recipient - -// int start - -// end - -// modelindex - -// startframe - -// framerate - -// msg_dest - -// delay - -// origin - -// recipient - -//----------------------------------------------------------------------------- -void TE_BeamEnts( IRecipientFilter& filter, float delay, - int start, int end, int modelindex, int haloindex, int startframe, int framerate, - float life, float width, float endWidth, int fadeLength, float amplitude, int r, int g, int b, int a, int speed ) -{ - g_TEBeamEnts.m_nStartEntity = (start & 0x0FFF) | ((1 & 0xF)<<12); - g_TEBeamEnts.m_nEndEntity = (end & 0x0FFF) | ((1 & 0xF)<<12); - g_TEBeamEnts.m_nModelIndex = modelindex; - g_TEBeamEnts.m_nHaloIndex = haloindex; - g_TEBeamEnts.m_nStartFrame = startframe; - g_TEBeamEnts.m_nFrameRate = framerate; - g_TEBeamEnts.m_fLife = life; - g_TEBeamEnts.m_fWidth = width; - g_TEBeamEnts.m_fEndWidth = endWidth; - g_TEBeamEnts.m_nFadeLength = fadeLength; - g_TEBeamEnts.m_fAmplitude = amplitude; - g_TEBeamEnts.m_nSpeed = speed; - g_TEBeamEnts.r = r; - g_TEBeamEnts.g = g; - g_TEBeamEnts.b = b; - g_TEBeamEnts.a = a; - - // Send it over the wire - g_TEBeamEnts.Create( filter, delay ); -} \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $Workfile: $ +// $Date: $ +// +//----------------------------------------------------------------------------- +// $Log: $ +// +// $NoKeywords: $ +//=============================================================================// +#include "cbase.h" +#include "basetempentity.h" +#include "te_basebeam.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +extern short g_sModelIndexSmoke; // (in combatweapon.cpp) holds the index for the smoke cloud + +//----------------------------------------------------------------------------- +// Purpose: Dispatches a beam between two entities +//----------------------------------------------------------------------------- +class CTEBeamEnts : public CTEBaseBeam +{ +public: + DECLARE_CLASS( CTEBeamEnts, CTEBaseBeam ); + DECLARE_SERVERCLASS(); + + CTEBeamEnts( const char *name ); + virtual ~CTEBeamEnts( void ); + + virtual void Test( const Vector& current_origin, const QAngle& current_angles ); + + +public: + CNetworkVar( int, m_nStartEntity ); + CNetworkVar( int, m_nEndEntity ); +}; + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *name - +//----------------------------------------------------------------------------- +CTEBeamEnts::CTEBeamEnts( const char *name ) : + CTEBaseBeam( name ) +{ + m_nStartEntity = 0; + m_nEndEntity = 0; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CTEBeamEnts::~CTEBeamEnts( void ) +{ +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *current_origin - +// *current_angles - +//----------------------------------------------------------------------------- +void CTEBeamEnts::Test( const Vector& current_origin, const QAngle& current_angles ) +{ + m_nStartEntity = 1; + m_nEndEntity = 0; + + m_nModelIndex = g_sModelIndexSmoke; + m_nStartFrame = 0; + m_nFrameRate = 10; + m_fLife = 2.0; + m_fWidth = 1.0; + m_fAmplitude = 1; + r = 127; + g = 63; + b = 0; + a = 150; + m_nSpeed = 1; + + CBroadcastRecipientFilter filter; + Create( filter, 0.0 ); +} + +IMPLEMENT_SERVERCLASS_ST(CTEBeamEnts, DT_TEBeamEnts) + SendPropInt( SENDINFO(m_nStartEntity), 24, SPROP_UNSIGNED ), + SendPropInt( SENDINFO(m_nEndEntity), 24, SPROP_UNSIGNED ), +END_SEND_TABLE() + + +// Singleton to fire TEBeamEnts objects +static CTEBeamEnts g_TEBeamEnts( "BeamEnts" ); + +//----------------------------------------------------------------------------- +// Purpose: +// Input : msg_dest - +// delay - +// *origin - +// *recipient - +// int start - +// end - +// modelindex - +// startframe - +// framerate - +// msg_dest - +// delay - +// origin - +// recipient - +//----------------------------------------------------------------------------- +void TE_BeamEnts( IRecipientFilter& filter, float delay, + int start, int end, int modelindex, int haloindex, int startframe, int framerate, + float life, float width, float endWidth, int fadeLength, float amplitude, int r, int g, int b, int a, int speed ) +{ + g_TEBeamEnts.m_nStartEntity = (start & 0x0FFF) | ((1 & 0xF)<<12); + g_TEBeamEnts.m_nEndEntity = (end & 0x0FFF) | ((1 & 0xF)<<12); + g_TEBeamEnts.m_nModelIndex = modelindex; + g_TEBeamEnts.m_nHaloIndex = haloindex; + g_TEBeamEnts.m_nStartFrame = startframe; + g_TEBeamEnts.m_nFrameRate = framerate; + g_TEBeamEnts.m_fLife = life; + g_TEBeamEnts.m_fWidth = width; + g_TEBeamEnts.m_fEndWidth = endWidth; + g_TEBeamEnts.m_nFadeLength = fadeLength; + g_TEBeamEnts.m_fAmplitude = amplitude; + g_TEBeamEnts.m_nSpeed = speed; + g_TEBeamEnts.r = r; + g_TEBeamEnts.g = g; + g_TEBeamEnts.b = b; + g_TEBeamEnts.a = a; + + // Send it over the wire + g_TEBeamEnts.Create( filter, delay ); +} diff --git a/dlls/te_beamfollow.cpp b/dlls/te_beamfollow.cpp index 28b06b3b..07b3c54c 100644 --- a/dlls/te_beamfollow.cpp +++ b/dlls/te_beamfollow.cpp @@ -1,112 +1,112 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $Workfile: $ -// $Date: $ -// -//----------------------------------------------------------------------------- -// $Log: $ -// -// $NoKeywords: $ -//=============================================================================// -#include "cbase.h" -#include "basetempentity.h" -#include "te_basebeam.h" - -// memdbgon must be the last include file in a .cpp file!!! -#include "tier0/memdbgon.h" - -//----------------------------------------------------------------------------- -// Purpose: Dispatches a beam ring between two entities -//----------------------------------------------------------------------------- -class CTEBeamFollow : public CTEBaseBeam -{ - DECLARE_CLASS( CTEBeamFollow, CTEBaseBeam ); -public: - - DECLARE_SERVERCLASS(); - - CTEBeamFollow( const char *name ); - virtual ~CTEBeamFollow( void ); - - virtual void Test( const Vector& current_origin, const QAngle& current_angles ); - -public: - - CNetworkVar( int, m_iEntIndex ); -}; - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *name - -//----------------------------------------------------------------------------- -CTEBeamFollow::CTEBeamFollow( const char *name ) : - CTEBaseBeam( name ) -{ - m_iEntIndex = -1; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -CTEBeamFollow::~CTEBeamFollow( void ) -{ -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *current_origin - -// *current_angles - -//----------------------------------------------------------------------------- -void CTEBeamFollow::Test( const Vector& current_origin, const QAngle& current_angles ) -{ - m_iEntIndex = 1; -} - -IMPLEMENT_SERVERCLASS_ST(CTEBeamFollow, DT_TEBeamFollow) - SendPropInt( SENDINFO(m_iEntIndex), 24, SPROP_UNSIGNED ), -END_SEND_TABLE() - - -// Singleton to fire TEBeamEntPoint objects -static CTEBeamFollow g_TEBeamFollow( "BeamFollow" ); - -//----------------------------------------------------------------------------- -// Purpose: -// Input : filter - -// delay - -// iEntIndex - -// modelIndex - -// modelindex - -// haloIndex - -// life - -// width - -// endWidth - -// fadeLength - -// r - -// g - -// b - -// a - -//----------------------------------------------------------------------------- -void TE_BeamFollow( IRecipientFilter& filter, float delay, - int iEntIndex, int modelIndex, int haloIndex, float life, float width, float endWidth, - float fadeLength,float r, float g, float b, float a ) -{ - g_TEBeamFollow.m_iEntIndex = (iEntIndex & 0x0FFF) | ((1 & 0xF)<<12); - g_TEBeamFollow.m_nModelIndex = modelIndex; - g_TEBeamFollow.m_nHaloIndex = haloIndex; - g_TEBeamFollow.m_nStartFrame = 0; - g_TEBeamFollow.m_nFrameRate = 0; - g_TEBeamFollow.m_fLife = life; - g_TEBeamFollow.m_fWidth = width; - g_TEBeamFollow.m_fEndWidth = endWidth; - g_TEBeamFollow.m_nFadeLength = fadeLength; - g_TEBeamFollow.r = r; - g_TEBeamFollow.g = g; - g_TEBeamFollow.b = b; - g_TEBeamFollow.a = a; - - // Send it over the wire - g_TEBeamFollow.Create( filter, delay ); -} \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $Workfile: $ +// $Date: $ +// +//----------------------------------------------------------------------------- +// $Log: $ +// +// $NoKeywords: $ +//=============================================================================// +#include "cbase.h" +#include "basetempentity.h" +#include "te_basebeam.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +//----------------------------------------------------------------------------- +// Purpose: Dispatches a beam ring between two entities +//----------------------------------------------------------------------------- +class CTEBeamFollow : public CTEBaseBeam +{ + DECLARE_CLASS( CTEBeamFollow, CTEBaseBeam ); +public: + + DECLARE_SERVERCLASS(); + + CTEBeamFollow( const char *name ); + virtual ~CTEBeamFollow( void ); + + virtual void Test( const Vector& current_origin, const QAngle& current_angles ); + +public: + + CNetworkVar( int, m_iEntIndex ); +}; + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *name - +//----------------------------------------------------------------------------- +CTEBeamFollow::CTEBeamFollow( const char *name ) : + CTEBaseBeam( name ) +{ + m_iEntIndex = -1; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CTEBeamFollow::~CTEBeamFollow( void ) +{ +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *current_origin - +// *current_angles - +//----------------------------------------------------------------------------- +void CTEBeamFollow::Test( const Vector& current_origin, const QAngle& current_angles ) +{ + m_iEntIndex = 1; +} + +IMPLEMENT_SERVERCLASS_ST(CTEBeamFollow, DT_TEBeamFollow) + SendPropInt( SENDINFO(m_iEntIndex), 24, SPROP_UNSIGNED ), +END_SEND_TABLE() + + +// Singleton to fire TEBeamEntPoint objects +static CTEBeamFollow g_TEBeamFollow( "BeamFollow" ); + +//----------------------------------------------------------------------------- +// Purpose: +// Input : filter - +// delay - +// iEntIndex - +// modelIndex - +// modelindex - +// haloIndex - +// life - +// width - +// endWidth - +// fadeLength - +// r - +// g - +// b - +// a - +//----------------------------------------------------------------------------- +void TE_BeamFollow( IRecipientFilter& filter, float delay, + int iEntIndex, int modelIndex, int haloIndex, float life, float width, float endWidth, + float fadeLength,float r, float g, float b, float a ) +{ + g_TEBeamFollow.m_iEntIndex = (iEntIndex & 0x0FFF) | ((1 & 0xF)<<12); + g_TEBeamFollow.m_nModelIndex = modelIndex; + g_TEBeamFollow.m_nHaloIndex = haloIndex; + g_TEBeamFollow.m_nStartFrame = 0; + g_TEBeamFollow.m_nFrameRate = 0; + g_TEBeamFollow.m_fLife = life; + g_TEBeamFollow.m_fWidth = width; + g_TEBeamFollow.m_fEndWidth = endWidth; + g_TEBeamFollow.m_nFadeLength = fadeLength; + g_TEBeamFollow.r = r; + g_TEBeamFollow.g = g; + g_TEBeamFollow.b = b; + g_TEBeamFollow.a = a; + + // Send it over the wire + g_TEBeamFollow.Create( filter, delay ); +} diff --git a/dlls/te_beamlaser.cpp b/dlls/te_beamlaser.cpp index 5cde9912..148e9874 100644 --- a/dlls/te_beamlaser.cpp +++ b/dlls/te_beamlaser.cpp @@ -1,127 +1,127 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: Beam used for Laser sights. Fades out when it's perpendicular to the viewpoint. -// -// $NoKeywords: $ -//=============================================================================// -#include "cbase.h" -#include "basetempentity.h" -#include "te_basebeam.h" - -// memdbgon must be the last include file in a .cpp file!!! -#include "tier0/memdbgon.h" - -extern short g_sModelIndexSmoke; // (in combatweapon.cpp) holds the index for the smoke cloud - -//----------------------------------------------------------------------------- -// Purpose: Beam used for Laser sights. Fades out when it's perpendicular to the viewpoint. -//----------------------------------------------------------------------------- -class CTEBeamLaser : public CTEBaseBeam -{ - DECLARE_CLASS( CTEBeamLaser, CTEBaseBeam ); -public: - DECLARE_SERVERCLASS(); - - CTEBeamLaser( const char *name ); - virtual ~CTEBeamLaser( void ); - - virtual void Test( const Vector& current_origin, const QAngle& current_angles ); - -public: - CNetworkVar( int, m_nStartEntity ); - CNetworkVar( int, m_nEndEntity ); -}; - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *name - -//----------------------------------------------------------------------------- -CTEBeamLaser::CTEBeamLaser( const char *name ) : - CTEBaseBeam( name ) -{ - m_nStartEntity = 0; - m_nEndEntity = 0; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -CTEBeamLaser::~CTEBeamLaser( void ) -{ -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *current_origin - -// *current_angles - -//----------------------------------------------------------------------------- -void CTEBeamLaser::Test( const Vector& current_origin, const QAngle& current_angles ) -{ - m_nStartEntity = 1; - m_nEndEntity = 0; - - m_nModelIndex = g_sModelIndexSmoke; - m_nStartFrame = 0; - m_nFrameRate = 10; - m_fLife = 2.0; - m_fWidth = 1.0; - m_fAmplitude = 1.0; - r = 127; - g = 63; - b = 0; - a = 150; - m_nSpeed = 1; - - CBroadcastRecipientFilter filter; - Create( filter, 0.0 ); -} - -IMPLEMENT_SERVERCLASS_ST( CTEBeamLaser, DT_TEBeamLaser) - SendPropInt( SENDINFO(m_nStartEntity), 24, SPROP_UNSIGNED ), - SendPropInt( SENDINFO(m_nEndEntity), 24, SPROP_UNSIGNED ), -END_SEND_TABLE() - - -// Singleton to fire TEBeamLaser objects -static CTEBeamLaser g_TEBeamLaser( "BeamLaser" ); - -//----------------------------------------------------------------------------- -// Purpose: -// Input : msg_dest - -// delay - -// *origin - -// *recipient - -// *start - -// *end - -// modelindex - -// startframe - -// framerate - -// msg_dest - -// delay - -// origin - -// recipient - -//----------------------------------------------------------------------------- -void TE_BeamLaser( IRecipientFilter& filter, float delay, - int start, int end, int modelindex, int haloindex, int startframe, int framerate, - float life, float width, float endWidth, int fadeLength, float amplitude, int r, int g, int b, int a, int speed ) -{ - g_TEBeamLaser.m_nStartEntity = (start & 0x0FFF) | ((1 & 0xF)<<12); - g_TEBeamLaser.m_nEndEntity = (end & 0x0FFF) | ((1 & 0xF)<<12); - g_TEBeamLaser.m_nModelIndex = modelindex; - g_TEBeamLaser.m_nHaloIndex = haloindex; - g_TEBeamLaser.m_nStartFrame = startframe; - g_TEBeamLaser.m_nFrameRate = framerate; - g_TEBeamLaser.m_fLife = life; - g_TEBeamLaser.m_fWidth = width; - g_TEBeamLaser.m_fEndWidth = endWidth; - g_TEBeamLaser.m_nFadeLength = fadeLength; - g_TEBeamLaser.m_fAmplitude = amplitude; - g_TEBeamLaser.m_nSpeed = speed; - g_TEBeamLaser.r = r; - g_TEBeamLaser.g = g; - g_TEBeamLaser.b = b; - g_TEBeamLaser.a = a; - - // Send it over the wire - g_TEBeamLaser.Create( filter, delay ); -} \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: Beam used for Laser sights. Fades out when it's perpendicular to the viewpoint. +// +// $NoKeywords: $ +//=============================================================================// +#include "cbase.h" +#include "basetempentity.h" +#include "te_basebeam.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +extern short g_sModelIndexSmoke; // (in combatweapon.cpp) holds the index for the smoke cloud + +//----------------------------------------------------------------------------- +// Purpose: Beam used for Laser sights. Fades out when it's perpendicular to the viewpoint. +//----------------------------------------------------------------------------- +class CTEBeamLaser : public CTEBaseBeam +{ + DECLARE_CLASS( CTEBeamLaser, CTEBaseBeam ); +public: + DECLARE_SERVERCLASS(); + + CTEBeamLaser( const char *name ); + virtual ~CTEBeamLaser( void ); + + virtual void Test( const Vector& current_origin, const QAngle& current_angles ); + +public: + CNetworkVar( int, m_nStartEntity ); + CNetworkVar( int, m_nEndEntity ); +}; + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *name - +//----------------------------------------------------------------------------- +CTEBeamLaser::CTEBeamLaser( const char *name ) : + CTEBaseBeam( name ) +{ + m_nStartEntity = 0; + m_nEndEntity = 0; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CTEBeamLaser::~CTEBeamLaser( void ) +{ +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *current_origin - +// *current_angles - +//----------------------------------------------------------------------------- +void CTEBeamLaser::Test( const Vector& current_origin, const QAngle& current_angles ) +{ + m_nStartEntity = 1; + m_nEndEntity = 0; + + m_nModelIndex = g_sModelIndexSmoke; + m_nStartFrame = 0; + m_nFrameRate = 10; + m_fLife = 2.0; + m_fWidth = 1.0; + m_fAmplitude = 1.0; + r = 127; + g = 63; + b = 0; + a = 150; + m_nSpeed = 1; + + CBroadcastRecipientFilter filter; + Create( filter, 0.0 ); +} + +IMPLEMENT_SERVERCLASS_ST( CTEBeamLaser, DT_TEBeamLaser) + SendPropInt( SENDINFO(m_nStartEntity), 24, SPROP_UNSIGNED ), + SendPropInt( SENDINFO(m_nEndEntity), 24, SPROP_UNSIGNED ), +END_SEND_TABLE() + + +// Singleton to fire TEBeamLaser objects +static CTEBeamLaser g_TEBeamLaser( "BeamLaser" ); + +//----------------------------------------------------------------------------- +// Purpose: +// Input : msg_dest - +// delay - +// *origin - +// *recipient - +// *start - +// *end - +// modelindex - +// startframe - +// framerate - +// msg_dest - +// delay - +// origin - +// recipient - +//----------------------------------------------------------------------------- +void TE_BeamLaser( IRecipientFilter& filter, float delay, + int start, int end, int modelindex, int haloindex, int startframe, int framerate, + float life, float width, float endWidth, int fadeLength, float amplitude, int r, int g, int b, int a, int speed ) +{ + g_TEBeamLaser.m_nStartEntity = (start & 0x0FFF) | ((1 & 0xF)<<12); + g_TEBeamLaser.m_nEndEntity = (end & 0x0FFF) | ((1 & 0xF)<<12); + g_TEBeamLaser.m_nModelIndex = modelindex; + g_TEBeamLaser.m_nHaloIndex = haloindex; + g_TEBeamLaser.m_nStartFrame = startframe; + g_TEBeamLaser.m_nFrameRate = framerate; + g_TEBeamLaser.m_fLife = life; + g_TEBeamLaser.m_fWidth = width; + g_TEBeamLaser.m_fEndWidth = endWidth; + g_TEBeamLaser.m_nFadeLength = fadeLength; + g_TEBeamLaser.m_fAmplitude = amplitude; + g_TEBeamLaser.m_nSpeed = speed; + g_TEBeamLaser.r = r; + g_TEBeamLaser.g = g; + g_TEBeamLaser.b = b; + g_TEBeamLaser.a = a; + + // Send it over the wire + g_TEBeamLaser.Create( filter, delay ); +} diff --git a/dlls/te_beampoints.cpp b/dlls/te_beampoints.cpp index bcf0cbe9..ecdb7216 100644 --- a/dlls/te_beampoints.cpp +++ b/dlls/te_beampoints.cpp @@ -1,144 +1,144 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $Workfile: $ -// $Date: $ -// -//----------------------------------------------------------------------------- -// $Log: $ -// -// $NoKeywords: $ -//=============================================================================// -#include "cbase.h" -#include "basetempentity.h" -#include "te_basebeam.h" - -// memdbgon must be the last include file in a .cpp file!!! -#include "tier0/memdbgon.h" - -extern short g_sModelIndexSmoke; // (in combatweapon.cpp) holds the index for the smoke cloud - -//----------------------------------------------------------------------------- -// Purpose: Dispatches a beam ring between two entities -//----------------------------------------------------------------------------- -class CTEBeamPoints : public CTEBaseBeam -{ -public: - DECLARE_CLASS( CTEBeamPoints, CTEBaseBeam ); - DECLARE_SERVERCLASS(); - - CTEBeamPoints( const char *name ); - virtual ~CTEBeamPoints( void ); - - virtual void Test( const Vector& current_origin, const QAngle& current_angles ); - -public: - CNetworkVector( m_vecStartPoint ); - CNetworkVector( m_vecEndPoint ); -}; - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *name - -//----------------------------------------------------------------------------- -CTEBeamPoints::CTEBeamPoints( const char *name ) : - CTEBaseBeam( name ) -{ - m_vecStartPoint.Init(); - m_vecEndPoint.Init(); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -CTEBeamPoints::~CTEBeamPoints( void ) -{ -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *current_origin - -// *current_angles - -//----------------------------------------------------------------------------- -void CTEBeamPoints::Test( const Vector& current_origin, const QAngle& current_angles ) -{ - m_nModelIndex = g_sModelIndexSmoke; - m_nStartFrame = 0; - m_nFrameRate = 10; - m_fLife = 2.0; - m_fWidth = 1.0; - m_fAmplitude = 1; - r = 0; - g = 63; - b = 127; - a = 150; - m_nSpeed = 1; - - m_vecStartPoint = current_origin; - - Vector forward, right; - - m_vecStartPoint += Vector( 0, 0, 30 ); - - AngleVectors( current_angles, &forward, &right, 0 ); - forward[2] = 0.0; - VectorNormalize( forward ); - - VectorMA( m_vecStartPoint, 75.0, forward, m_vecStartPoint.GetForModify() ); - VectorMA( m_vecStartPoint, 25.0, right, m_vecEndPoint.GetForModify() ); - VectorMA( m_vecStartPoint, -25.0, right, m_vecStartPoint.GetForModify() ); - - CBroadcastRecipientFilter filter; - Create( filter, 0.0 ); -} - -IMPLEMENT_SERVERCLASS_ST( CTEBeamPoints, DT_TEBeamPoints) - SendPropVector( SENDINFO(m_vecStartPoint), -1, SPROP_COORD ), - SendPropVector( SENDINFO(m_vecEndPoint), -1, SPROP_COORD ), -END_SEND_TABLE() - - -// Singleton to fire TEBeamPoints objects -static CTEBeamPoints g_TEBeamPoints( "BeamPoints" ); - -//----------------------------------------------------------------------------- -// Purpose: -// Input : msg_dest - -// delay - -// *origin - -// *recipient - -// *start - -// *end - -// modelindex - -// startframe - -// framerate - -// msg_dest - -// delay - -// origin - -// recipient - -//----------------------------------------------------------------------------- -void TE_BeamPoints( IRecipientFilter& filter, float delay, - const Vector* start, const Vector* end, int modelindex, int haloindex, int startframe, int framerate, - float life, float width, float endWidth, int fadeLength, float amplitude, int r, int g, int b, int a, int speed ) -{ - g_TEBeamPoints.m_vecStartPoint = *start; - g_TEBeamPoints.m_vecEndPoint = *end; - g_TEBeamPoints.m_nModelIndex = modelindex; - g_TEBeamPoints.m_nHaloIndex = haloindex; - g_TEBeamPoints.m_nStartFrame = startframe; - g_TEBeamPoints.m_nFrameRate = framerate; - g_TEBeamPoints.m_fLife = life; - g_TEBeamPoints.m_fWidth = width; - g_TEBeamPoints.m_fEndWidth = endWidth; - g_TEBeamPoints.m_nFadeLength = fadeLength; - g_TEBeamPoints.m_fAmplitude = amplitude; - g_TEBeamPoints.m_nSpeed = speed; - g_TEBeamPoints.r = r; - g_TEBeamPoints.g = g; - g_TEBeamPoints.b = b; - g_TEBeamPoints.a = a; - - // Send it over the wire - g_TEBeamPoints.Create( filter, delay ); -} \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $Workfile: $ +// $Date: $ +// +//----------------------------------------------------------------------------- +// $Log: $ +// +// $NoKeywords: $ +//=============================================================================// +#include "cbase.h" +#include "basetempentity.h" +#include "te_basebeam.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +extern short g_sModelIndexSmoke; // (in combatweapon.cpp) holds the index for the smoke cloud + +//----------------------------------------------------------------------------- +// Purpose: Dispatches a beam ring between two entities +//----------------------------------------------------------------------------- +class CTEBeamPoints : public CTEBaseBeam +{ +public: + DECLARE_CLASS( CTEBeamPoints, CTEBaseBeam ); + DECLARE_SERVERCLASS(); + + CTEBeamPoints( const char *name ); + virtual ~CTEBeamPoints( void ); + + virtual void Test( const Vector& current_origin, const QAngle& current_angles ); + +public: + CNetworkVector( m_vecStartPoint ); + CNetworkVector( m_vecEndPoint ); +}; + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *name - +//----------------------------------------------------------------------------- +CTEBeamPoints::CTEBeamPoints( const char *name ) : + CTEBaseBeam( name ) +{ + m_vecStartPoint.Init(); + m_vecEndPoint.Init(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CTEBeamPoints::~CTEBeamPoints( void ) +{ +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *current_origin - +// *current_angles - +//----------------------------------------------------------------------------- +void CTEBeamPoints::Test( const Vector& current_origin, const QAngle& current_angles ) +{ + m_nModelIndex = g_sModelIndexSmoke; + m_nStartFrame = 0; + m_nFrameRate = 10; + m_fLife = 2.0; + m_fWidth = 1.0; + m_fAmplitude = 1; + r = 0; + g = 63; + b = 127; + a = 150; + m_nSpeed = 1; + + m_vecStartPoint = current_origin; + + Vector forward, right; + + m_vecStartPoint += Vector( 0, 0, 30 ); + + AngleVectors( current_angles, &forward, &right, 0 ); + forward[2] = 0.0; + VectorNormalize( forward ); + + VectorMA( m_vecStartPoint, 75.0, forward, m_vecStartPoint.GetForModify() ); + VectorMA( m_vecStartPoint, 25.0, right, m_vecEndPoint.GetForModify() ); + VectorMA( m_vecStartPoint, -25.0, right, m_vecStartPoint.GetForModify() ); + + CBroadcastRecipientFilter filter; + Create( filter, 0.0 ); +} + +IMPLEMENT_SERVERCLASS_ST( CTEBeamPoints, DT_TEBeamPoints) + SendPropVector( SENDINFO(m_vecStartPoint), -1, SPROP_COORD ), + SendPropVector( SENDINFO(m_vecEndPoint), -1, SPROP_COORD ), +END_SEND_TABLE() + + +// Singleton to fire TEBeamPoints objects +static CTEBeamPoints g_TEBeamPoints( "BeamPoints" ); + +//----------------------------------------------------------------------------- +// Purpose: +// Input : msg_dest - +// delay - +// *origin - +// *recipient - +// *start - +// *end - +// modelindex - +// startframe - +// framerate - +// msg_dest - +// delay - +// origin - +// recipient - +//----------------------------------------------------------------------------- +void TE_BeamPoints( IRecipientFilter& filter, float delay, + const Vector* start, const Vector* end, int modelindex, int haloindex, int startframe, int framerate, + float life, float width, float endWidth, int fadeLength, float amplitude, int r, int g, int b, int a, int speed ) +{ + g_TEBeamPoints.m_vecStartPoint = *start; + g_TEBeamPoints.m_vecEndPoint = *end; + g_TEBeamPoints.m_nModelIndex = modelindex; + g_TEBeamPoints.m_nHaloIndex = haloindex; + g_TEBeamPoints.m_nStartFrame = startframe; + g_TEBeamPoints.m_nFrameRate = framerate; + g_TEBeamPoints.m_fLife = life; + g_TEBeamPoints.m_fWidth = width; + g_TEBeamPoints.m_fEndWidth = endWidth; + g_TEBeamPoints.m_nFadeLength = fadeLength; + g_TEBeamPoints.m_fAmplitude = amplitude; + g_TEBeamPoints.m_nSpeed = speed; + g_TEBeamPoints.r = r; + g_TEBeamPoints.g = g; + g_TEBeamPoints.b = b; + g_TEBeamPoints.a = a; + + // Send it over the wire + g_TEBeamPoints.Create( filter, delay ); +} diff --git a/dlls/te_beamring.cpp b/dlls/te_beamring.cpp index 2308f572..21b86add 100644 --- a/dlls/te_beamring.cpp +++ b/dlls/te_beamring.cpp @@ -1,135 +1,135 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $Workfile: $ -// $Date: $ -// -//----------------------------------------------------------------------------- -// $Log: $ -// -// $NoKeywords: $ -//=============================================================================// -#include "cbase.h" -#include "basetempentity.h" -#include "te_basebeam.h" - -// memdbgon must be the last include file in a .cpp file!!! -#include "tier0/memdbgon.h" - -extern short g_sModelIndexSmoke; // (in combatweapon.cpp) holds the index for the smoke cloud - -//----------------------------------------------------------------------------- -// Purpose: Dispatches a beam ring between two entities -//----------------------------------------------------------------------------- -class CTEBeamRing : public CTEBaseBeam -{ -public: - DECLARE_CLASS( CTEBeamRing, CTEBaseBeam ); - DECLARE_SERVERCLASS(); - - CTEBeamRing( const char *name ); - virtual ~CTEBeamRing( void ); - - virtual void Test( const Vector& current_origin, const QAngle& current_angles ); - -public: - CNetworkVar( int, m_nStartEntity ); - CNetworkVar( int, m_nEndEntity ); -}; - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *name - -//----------------------------------------------------------------------------- -CTEBeamRing::CTEBeamRing( const char *name ) : - CTEBaseBeam( name ) -{ - m_nStartEntity = 0; - m_nEndEntity = 0; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -CTEBeamRing::~CTEBeamRing( void ) -{ -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *current_origin - -// *current_angles - -//----------------------------------------------------------------------------- -void CTEBeamRing::Test( const Vector& current_origin, const QAngle& current_angles ) -{ - m_nStartEntity = 1; - m_nEndEntity = 0; - - m_nModelIndex = g_sModelIndexSmoke; - m_nStartFrame = 0; - m_nFrameRate = 2; - m_fLife = 10.0; - m_fWidth = 2.0; - m_fAmplitude = 1; - r = 255; - g = 255; - b = 0; - a = 127; - m_nSpeed = 5; - - CBroadcastRecipientFilter filter; - Create( filter, 0.0 ); -} - - -IMPLEMENT_SERVERCLASS_ST( CTEBeamRing, DT_TEBeamRing) - SendPropInt( SENDINFO(m_nStartEntity), MAX_EDICT_BITS, SPROP_UNSIGNED ), - SendPropInt( SENDINFO(m_nEndEntity), MAX_EDICT_BITS, SPROP_UNSIGNED ), -END_SEND_TABLE() - - -// Singleton to fire TEBeamRing objects -static CTEBeamRing g_TEBeamRing( "BeamRing" ); - -//----------------------------------------------------------------------------- -// Purpose: -// Input : msg_dest - -// delay - -// *origin - -// *recipient - -// int start - -// end - -// modelindex - -// startframe - -// framerate - -// msg_dest - -// delay - -// origin - -// recipient - -//----------------------------------------------------------------------------- -void TE_BeamRing( IRecipientFilter& filter, float delay, - int start, int end, int modelindex, int haloindex, int startframe, int framerate, - float life, float width, int spread, float amplitude, int r, int g, int b, int a, int speed, int flags ) -{ - g_TEBeamRing.m_nStartEntity = (start & 0x0FFF) | ((1 & 0xF)<<12); - g_TEBeamRing.m_nEndEntity = (end & 0x0FFF) | ((1 & 0xF)<<12); - g_TEBeamRing.m_nModelIndex = modelindex; - g_TEBeamRing.m_nHaloIndex = haloindex; - g_TEBeamRing.m_nStartFrame = startframe; - g_TEBeamRing.m_nFrameRate = framerate; - g_TEBeamRing.m_fLife = life; - g_TEBeamRing.m_fWidth = width; - g_TEBeamRing.m_fEndWidth = width; - g_TEBeamRing.m_nFadeLength = 0; - g_TEBeamRing.m_fAmplitude = amplitude; - g_TEBeamRing.m_nSpeed = speed; - g_TEBeamRing.r = r; - g_TEBeamRing.g = g; - g_TEBeamRing.b = b; - g_TEBeamRing.a = a; - g_TEBeamRing.m_nFlags = flags; - - // Send it over the wire - g_TEBeamRing.Create( filter, delay ); -} \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $Workfile: $ +// $Date: $ +// +//----------------------------------------------------------------------------- +// $Log: $ +// +// $NoKeywords: $ +//=============================================================================// +#include "cbase.h" +#include "basetempentity.h" +#include "te_basebeam.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +extern short g_sModelIndexSmoke; // (in combatweapon.cpp) holds the index for the smoke cloud + +//----------------------------------------------------------------------------- +// Purpose: Dispatches a beam ring between two entities +//----------------------------------------------------------------------------- +class CTEBeamRing : public CTEBaseBeam +{ +public: + DECLARE_CLASS( CTEBeamRing, CTEBaseBeam ); + DECLARE_SERVERCLASS(); + + CTEBeamRing( const char *name ); + virtual ~CTEBeamRing( void ); + + virtual void Test( const Vector& current_origin, const QAngle& current_angles ); + +public: + CNetworkVar( int, m_nStartEntity ); + CNetworkVar( int, m_nEndEntity ); +}; + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *name - +//----------------------------------------------------------------------------- +CTEBeamRing::CTEBeamRing( const char *name ) : + CTEBaseBeam( name ) +{ + m_nStartEntity = 0; + m_nEndEntity = 0; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CTEBeamRing::~CTEBeamRing( void ) +{ +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *current_origin - +// *current_angles - +//----------------------------------------------------------------------------- +void CTEBeamRing::Test( const Vector& current_origin, const QAngle& current_angles ) +{ + m_nStartEntity = 1; + m_nEndEntity = 0; + + m_nModelIndex = g_sModelIndexSmoke; + m_nStartFrame = 0; + m_nFrameRate = 2; + m_fLife = 10.0; + m_fWidth = 2.0; + m_fAmplitude = 1; + r = 255; + g = 255; + b = 0; + a = 127; + m_nSpeed = 5; + + CBroadcastRecipientFilter filter; + Create( filter, 0.0 ); +} + + +IMPLEMENT_SERVERCLASS_ST( CTEBeamRing, DT_TEBeamRing) + SendPropInt( SENDINFO(m_nStartEntity), MAX_EDICT_BITS, SPROP_UNSIGNED ), + SendPropInt( SENDINFO(m_nEndEntity), MAX_EDICT_BITS, SPROP_UNSIGNED ), +END_SEND_TABLE() + + +// Singleton to fire TEBeamRing objects +static CTEBeamRing g_TEBeamRing( "BeamRing" ); + +//----------------------------------------------------------------------------- +// Purpose: +// Input : msg_dest - +// delay - +// *origin - +// *recipient - +// int start - +// end - +// modelindex - +// startframe - +// framerate - +// msg_dest - +// delay - +// origin - +// recipient - +//----------------------------------------------------------------------------- +void TE_BeamRing( IRecipientFilter& filter, float delay, + int start, int end, int modelindex, int haloindex, int startframe, int framerate, + float life, float width, int spread, float amplitude, int r, int g, int b, int a, int speed, int flags ) +{ + g_TEBeamRing.m_nStartEntity = (start & 0x0FFF) | ((1 & 0xF)<<12); + g_TEBeamRing.m_nEndEntity = (end & 0x0FFF) | ((1 & 0xF)<<12); + g_TEBeamRing.m_nModelIndex = modelindex; + g_TEBeamRing.m_nHaloIndex = haloindex; + g_TEBeamRing.m_nStartFrame = startframe; + g_TEBeamRing.m_nFrameRate = framerate; + g_TEBeamRing.m_fLife = life; + g_TEBeamRing.m_fWidth = width; + g_TEBeamRing.m_fEndWidth = width; + g_TEBeamRing.m_nFadeLength = 0; + g_TEBeamRing.m_fAmplitude = amplitude; + g_TEBeamRing.m_nSpeed = speed; + g_TEBeamRing.r = r; + g_TEBeamRing.g = g; + g_TEBeamRing.b = b; + g_TEBeamRing.a = a; + g_TEBeamRing.m_nFlags = flags; + + // Send it over the wire + g_TEBeamRing.Create( filter, delay ); +} diff --git a/dlls/te_beamringpoint.cpp b/dlls/te_beamringpoint.cpp index 8d4d2f1d..f97eed43 100644 --- a/dlls/te_beamringpoint.cpp +++ b/dlls/te_beamringpoint.cpp @@ -1,124 +1,124 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $Workfile: $ -// $Date: $ -// -//----------------------------------------------------------------------------- -// $Log: $ -// -// $NoKeywords: $ -//=============================================================================// -#include "cbase.h" -#include "basetempentity.h" -#include "te_basebeam.h" - -// memdbgon must be the last include file in a .cpp file!!! -#include "tier0/memdbgon.h" - -extern short g_sModelIndexSmoke; // (in combatweapon.cpp) holds the index for the smoke cloud - -//----------------------------------------------------------------------------- -// Purpose: Dispatches a beam ring between two entities -//----------------------------------------------------------------------------- -class CTEBeamRingPoint : public CTEBaseBeam -{ -public: - DECLARE_CLASS( CTEBeamRingPoint, CTEBaseBeam ); - DECLARE_SERVERCLASS(); - - CTEBeamRingPoint( const char *name ); - virtual ~CTEBeamRingPoint( void ); - - virtual void Test( const Vector& current_origin, const QAngle& current_angles ); - -public: - CNetworkVector( m_vecCenter ); - CNetworkVar( float, m_flStartRadius ); - CNetworkVar( float, m_flEndRadius ); -}; - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *name - -//----------------------------------------------------------------------------- -CTEBeamRingPoint::CTEBeamRingPoint( const char *name ) : - CTEBaseBeam( name ) -{ - m_vecCenter.Init(); - m_flStartRadius = 0.0f; - m_flEndRadius = 0.0f; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -CTEBeamRingPoint::~CTEBeamRingPoint( void ) -{ -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *current_origin - -// *current_angles - -//----------------------------------------------------------------------------- -void CTEBeamRingPoint::Test( const Vector& current_origin, const QAngle& current_angles ) -{ - m_vecCenter = current_origin; - m_flEndRadius = 256.0f; - m_flStartRadius = 16.0f; - - m_nModelIndex = g_sModelIndexSmoke; - m_nStartFrame = 0; - m_nFrameRate = 2; - m_fLife = 10.0; - m_fWidth = 2.0; - m_fAmplitude = 1; - r = 255; - g = 255; - b = 0; - a = 127; - m_nSpeed = 5; - - CBroadcastRecipientFilter filter; - Create( filter, 0.0 ); -} - - -IMPLEMENT_SERVERCLASS_ST( CTEBeamRingPoint, DT_TEBeamRingPoint) - SendPropVector( SENDINFO(m_vecCenter), -1, SPROP_COORD ), - SendPropFloat( SENDINFO(m_flStartRadius), 16, SPROP_ROUNDUP, 0.0f, 4096.0f ), - SendPropFloat( SENDINFO(m_flEndRadius), 16, SPROP_ROUNDUP, 0.0f, 4096.0f ), -END_SEND_TABLE() - - -// Singleton to fire TEBeamRingPoint objects -static CTEBeamRingPoint g_TEBeamRingPoint( "BeamRingPoint" ); - -void TE_BeamRingPoint( IRecipientFilter& filter, float delay, - const Vector& center, float start_radius, float end_radius, int modelindex, int haloindex, int startframe, int framerate, - float life, float width, int spread, float amplitude, int r, int g, int b, int a, int speed, int flags ) -{ - g_TEBeamRingPoint.m_vecCenter = center; - g_TEBeamRingPoint.m_flStartRadius = start_radius; - g_TEBeamRingPoint.m_flEndRadius = end_radius; - g_TEBeamRingPoint.m_nModelIndex = modelindex; - g_TEBeamRingPoint.m_nHaloIndex = haloindex; - g_TEBeamRingPoint.m_nStartFrame = startframe; - g_TEBeamRingPoint.m_nFrameRate = framerate; - g_TEBeamRingPoint.m_fLife = life; - g_TEBeamRingPoint.m_fWidth = width; - g_TEBeamRingPoint.m_fEndWidth = width; - g_TEBeamRingPoint.m_nFadeLength = 0; - g_TEBeamRingPoint.m_fAmplitude = amplitude; - g_TEBeamRingPoint.m_nSpeed = speed; - g_TEBeamRingPoint.r = r; - g_TEBeamRingPoint.g = g; - g_TEBeamRingPoint.b = b; - g_TEBeamRingPoint.a = a; - g_TEBeamRingPoint.m_nFlags = flags; - - // Send it over the wire - g_TEBeamRingPoint.Create( filter, delay ); -} \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $Workfile: $ +// $Date: $ +// +//----------------------------------------------------------------------------- +// $Log: $ +// +// $NoKeywords: $ +//=============================================================================// +#include "cbase.h" +#include "basetempentity.h" +#include "te_basebeam.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +extern short g_sModelIndexSmoke; // (in combatweapon.cpp) holds the index for the smoke cloud + +//----------------------------------------------------------------------------- +// Purpose: Dispatches a beam ring between two entities +//----------------------------------------------------------------------------- +class CTEBeamRingPoint : public CTEBaseBeam +{ +public: + DECLARE_CLASS( CTEBeamRingPoint, CTEBaseBeam ); + DECLARE_SERVERCLASS(); + + CTEBeamRingPoint( const char *name ); + virtual ~CTEBeamRingPoint( void ); + + virtual void Test( const Vector& current_origin, const QAngle& current_angles ); + +public: + CNetworkVector( m_vecCenter ); + CNetworkVar( float, m_flStartRadius ); + CNetworkVar( float, m_flEndRadius ); +}; + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *name - +//----------------------------------------------------------------------------- +CTEBeamRingPoint::CTEBeamRingPoint( const char *name ) : + CTEBaseBeam( name ) +{ + m_vecCenter.Init(); + m_flStartRadius = 0.0f; + m_flEndRadius = 0.0f; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CTEBeamRingPoint::~CTEBeamRingPoint( void ) +{ +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *current_origin - +// *current_angles - +//----------------------------------------------------------------------------- +void CTEBeamRingPoint::Test( const Vector& current_origin, const QAngle& current_angles ) +{ + m_vecCenter = current_origin; + m_flEndRadius = 256.0f; + m_flStartRadius = 16.0f; + + m_nModelIndex = g_sModelIndexSmoke; + m_nStartFrame = 0; + m_nFrameRate = 2; + m_fLife = 10.0; + m_fWidth = 2.0; + m_fAmplitude = 1; + r = 255; + g = 255; + b = 0; + a = 127; + m_nSpeed = 5; + + CBroadcastRecipientFilter filter; + Create( filter, 0.0 ); +} + + +IMPLEMENT_SERVERCLASS_ST( CTEBeamRingPoint, DT_TEBeamRingPoint) + SendPropVector( SENDINFO(m_vecCenter), -1, SPROP_COORD ), + SendPropFloat( SENDINFO(m_flStartRadius), 16, SPROP_ROUNDUP, 0.0f, 4096.0f ), + SendPropFloat( SENDINFO(m_flEndRadius), 16, SPROP_ROUNDUP, 0.0f, 4096.0f ), +END_SEND_TABLE() + + +// Singleton to fire TEBeamRingPoint objects +static CTEBeamRingPoint g_TEBeamRingPoint( "BeamRingPoint" ); + +void TE_BeamRingPoint( IRecipientFilter& filter, float delay, + const Vector& center, float start_radius, float end_radius, int modelindex, int haloindex, int startframe, int framerate, + float life, float width, int spread, float amplitude, int r, int g, int b, int a, int speed, int flags ) +{ + g_TEBeamRingPoint.m_vecCenter = center; + g_TEBeamRingPoint.m_flStartRadius = start_radius; + g_TEBeamRingPoint.m_flEndRadius = end_radius; + g_TEBeamRingPoint.m_nModelIndex = modelindex; + g_TEBeamRingPoint.m_nHaloIndex = haloindex; + g_TEBeamRingPoint.m_nStartFrame = startframe; + g_TEBeamRingPoint.m_nFrameRate = framerate; + g_TEBeamRingPoint.m_fLife = life; + g_TEBeamRingPoint.m_fWidth = width; + g_TEBeamRingPoint.m_fEndWidth = width; + g_TEBeamRingPoint.m_nFadeLength = 0; + g_TEBeamRingPoint.m_fAmplitude = amplitude; + g_TEBeamRingPoint.m_nSpeed = speed; + g_TEBeamRingPoint.r = r; + g_TEBeamRingPoint.g = g; + g_TEBeamRingPoint.b = b; + g_TEBeamRingPoint.a = a; + g_TEBeamRingPoint.m_nFlags = flags; + + // Send it over the wire + g_TEBeamRingPoint.Create( filter, delay ); +} diff --git a/dlls/te_beamspline.cpp b/dlls/te_beamspline.cpp index 637fce13..77fefce5 100644 --- a/dlls/te_beamspline.cpp +++ b/dlls/te_beamspline.cpp @@ -1,134 +1,134 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $Workfile: $ -// $Date: $ -// -//----------------------------------------------------------------------------- -// $Log: $ -// -// $NoKeywords: $ -//=============================================================================// -#include "cbase.h" -#include "basetempentity.h" - -// memdbgon must be the last include file in a .cpp file!!! -#include "tier0/memdbgon.h" - -#define MAX_SPLINE_POINTS 16 -//----------------------------------------------------------------------------- -// Purpose: Dispatches beam spline tempentity -//----------------------------------------------------------------------------- -class CTEBeamSpline : public CBaseTempEntity -{ -public: - DECLARE_CLASS( CTEBeamSpline, CBaseTempEntity ); - - CTEBeamSpline( const char *name ); - virtual ~CTEBeamSpline( void ); - - virtual void Test( const Vector& current_origin, const QAngle& current_angles ); - - DECLARE_SERVERCLASS(); - -public: - CNetworkArray( Vector, m_vecPoints, MAX_SPLINE_POINTS ); - CNetworkVar( int, m_nPoints ); -}; - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *name - -//----------------------------------------------------------------------------- -CTEBeamSpline::CTEBeamSpline( const char *name ) : - CBaseTempEntity( name ) -{ - int i; - for ( i = 0; i < MAX_SPLINE_POINTS; i++ ) - { - m_vecPoints.GetForModify( i ).Init(); - } - m_nPoints = 0; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -CTEBeamSpline::~CTEBeamSpline( void ) -{ -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *current_origin - -// *current_angles - -//----------------------------------------------------------------------------- -void CTEBeamSpline::Test( const Vector& current_origin, const QAngle& current_angles ) -{ - // Fill in data - m_nPoints = 6; - Vector m_vecStart = current_origin; - - Vector forward, right; - - m_vecStart[2] += 24; - - AngleVectors( current_angles, &forward, &right, 0 ); - forward[2] = 0.0; - VectorNormalize( forward ); - - VectorMA( m_vecStart, 100.0, forward, m_vecStart ); - - VectorMA( m_vecStart, -128.0, right, m_vecStart ); - - for ( int i = 0; i < m_nPoints; i++ ) - { - m_vecPoints.Set( i, m_vecStart ); - VectorMA( m_vecStart, 128/m_nPoints, right, m_vecStart ); - VectorMA( m_vecStart, 30.0/m_nPoints, forward, m_vecStart ); - } - - CBroadcastRecipientFilter filter; - Create( filter, 0.0 ); -} - -IMPLEMENT_SERVERCLASS_ST_NOBASE(CTEBeamSpline, DT_TEBeamSpline) - SendPropInt( SENDINFO( m_nPoints ), 5, SPROP_UNSIGNED ), - - SendPropArray( - SendPropVector( SENDINFO_ARRAY(m_vecPoints), -1, SPROP_COORD), - m_vecPoints) -END_SEND_TABLE() - - -// Singleton to fire TEBeamSpline objects -static CTEBeamSpline g_TEBeamSpline( "BeamSpline" ); - -//----------------------------------------------------------------------------- -// Purpose: -// Input : msg_dest - -// delay - -// *origin - -// *recipient - -// points - -// *points - -//----------------------------------------------------------------------------- -void TE_BeamSpline( IRecipientFilter& filter, float delay, - int points, Vector* rgPoints ) -{ - int i; - g_TEBeamSpline.m_nPoints = points; - for ( i = 0; i < points; i++ ) - { - g_TEBeamSpline.m_vecPoints.Set( i, rgPoints[ i ] ); - } - - for ( ; i < MAX_SPLINE_POINTS; i++ ) - { - g_TEBeamSpline.m_vecPoints.GetForModify( i ).Init(); - } - - // Send it over the wire - g_TEBeamSpline.Create( filter, delay ); -} \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $Workfile: $ +// $Date: $ +// +//----------------------------------------------------------------------------- +// $Log: $ +// +// $NoKeywords: $ +//=============================================================================// +#include "cbase.h" +#include "basetempentity.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +#define MAX_SPLINE_POINTS 16 +//----------------------------------------------------------------------------- +// Purpose: Dispatches beam spline tempentity +//----------------------------------------------------------------------------- +class CTEBeamSpline : public CBaseTempEntity +{ +public: + DECLARE_CLASS( CTEBeamSpline, CBaseTempEntity ); + + CTEBeamSpline( const char *name ); + virtual ~CTEBeamSpline( void ); + + virtual void Test( const Vector& current_origin, const QAngle& current_angles ); + + DECLARE_SERVERCLASS(); + +public: + CNetworkArray( Vector, m_vecPoints, MAX_SPLINE_POINTS ); + CNetworkVar( int, m_nPoints ); +}; + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *name - +//----------------------------------------------------------------------------- +CTEBeamSpline::CTEBeamSpline( const char *name ) : + CBaseTempEntity( name ) +{ + int i; + for ( i = 0; i < MAX_SPLINE_POINTS; i++ ) + { + m_vecPoints.GetForModify( i ).Init(); + } + m_nPoints = 0; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CTEBeamSpline::~CTEBeamSpline( void ) +{ +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *current_origin - +// *current_angles - +//----------------------------------------------------------------------------- +void CTEBeamSpline::Test( const Vector& current_origin, const QAngle& current_angles ) +{ + // Fill in data + m_nPoints = 6; + Vector m_vecStart = current_origin; + + Vector forward, right; + + m_vecStart[2] += 24; + + AngleVectors( current_angles, &forward, &right, 0 ); + forward[2] = 0.0; + VectorNormalize( forward ); + + VectorMA( m_vecStart, 100.0, forward, m_vecStart ); + + VectorMA( m_vecStart, -128.0, right, m_vecStart ); + + for ( int i = 0; i < m_nPoints; i++ ) + { + m_vecPoints.Set( i, m_vecStart ); + VectorMA( m_vecStart, 128/m_nPoints, right, m_vecStart ); + VectorMA( m_vecStart, 30.0/m_nPoints, forward, m_vecStart ); + } + + CBroadcastRecipientFilter filter; + Create( filter, 0.0 ); +} + +IMPLEMENT_SERVERCLASS_ST_NOBASE(CTEBeamSpline, DT_TEBeamSpline) + SendPropInt( SENDINFO( m_nPoints ), 5, SPROP_UNSIGNED ), + + SendPropArray( + SendPropVector( SENDINFO_ARRAY(m_vecPoints), -1, SPROP_COORD), + m_vecPoints) +END_SEND_TABLE() + + +// Singleton to fire TEBeamSpline objects +static CTEBeamSpline g_TEBeamSpline( "BeamSpline" ); + +//----------------------------------------------------------------------------- +// Purpose: +// Input : msg_dest - +// delay - +// *origin - +// *recipient - +// points - +// *points - +//----------------------------------------------------------------------------- +void TE_BeamSpline( IRecipientFilter& filter, float delay, + int points, Vector* rgPoints ) +{ + int i; + g_TEBeamSpline.m_nPoints = points; + for ( i = 0; i < points; i++ ) + { + g_TEBeamSpline.m_vecPoints.Set( i, rgPoints[ i ] ); + } + + for ( ; i < MAX_SPLINE_POINTS; i++ ) + { + g_TEBeamSpline.m_vecPoints.GetForModify( i ).Init(); + } + + // Send it over the wire + g_TEBeamSpline.Create( filter, delay ); +} diff --git a/dlls/te_bloodsprite.cpp b/dlls/te_bloodsprite.cpp index 5b54c119..dbcc961b 100644 --- a/dlls/te_bloodsprite.cpp +++ b/dlls/te_bloodsprite.cpp @@ -1,151 +1,151 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $Workfile: $ -// $Date: $ -// -//----------------------------------------------------------------------------- -// $Log: $ -// -// $NoKeywords: $ -//=============================================================================// -#include "cbase.h" -#include "basetempentity.h" - -// memdbgon must be the last include file in a .cpp file!!! -#include "tier0/memdbgon.h" - -extern short g_sModelIndexBloodDrop; // (in combatweapon.cpp) holds the sprite index for the initial blood -extern short g_sModelIndexBloodSpray; // (in combatweapon.cpp) holds the sprite index for splattered blood - -//----------------------------------------------------------------------------- -// Purpose: Display's a blood sprite -//----------------------------------------------------------------------------- -class CTEBloodSprite : public CBaseTempEntity -{ -public: - DECLARE_CLASS( CTEBloodSprite, CBaseTempEntity ); - - CTEBloodSprite( const char *name ); - virtual ~CTEBloodSprite( void ); - - virtual void Test( const Vector& current_origin, const QAngle& current_angles ); - - DECLARE_SERVERCLASS(); - -public: - CNetworkVector( m_vecOrigin ); - CNetworkVector( m_vecDirection ); - CNetworkVar( int, m_nSprayModel ); - CNetworkVar( int, m_nDropModel ); - CNetworkVar( int, r ); - CNetworkVar( int, g ); - CNetworkVar( int, b ); - CNetworkVar( int, a ); - CNetworkVar( int, m_nSize ); -}; - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *name - -//----------------------------------------------------------------------------- -CTEBloodSprite::CTEBloodSprite( const char *name ) : - CBaseTempEntity( name ) -{ - m_vecOrigin.Init(); - m_nSprayModel = 0; - m_nDropModel = 0; - r = 0; - g = 0; - b = 0; - a = 0; - m_nSize = 0; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -CTEBloodSprite::~CTEBloodSprite( void ) -{ -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *current_origin - -// *current_angles - -//----------------------------------------------------------------------------- -void CTEBloodSprite::Test( const Vector& current_origin, const QAngle& current_angles ) -{ - // Fill in data - r = 255; - g = 255; - b = 63; - a = 255; - m_nSize = 16; - m_vecOrigin = current_origin; - - m_nSprayModel = g_sModelIndexBloodSpray; - m_nDropModel = g_sModelIndexBloodDrop; - - Vector forward; - - m_vecOrigin.GetForModify()[2] += 24; - - AngleVectors( current_angles, &forward ); - forward[2] = 0.0; - VectorNormalize( forward ); - - VectorMA( m_vecOrigin, 50.0, forward, m_vecOrigin.GetForModify() ); - - CBroadcastRecipientFilter filter; - Create( filter, 0.0 ); -} - -IMPLEMENT_SERVERCLASS_ST_NOBASE(CTEBloodSprite, DT_TEBloodSprite) - SendPropVector( SENDINFO(m_vecOrigin), -1, SPROP_COORD), - SendPropVector( SENDINFO(m_vecDirection), -1, SPROP_COORD), - SendPropInt( SENDINFO(r), 8, SPROP_UNSIGNED ), - SendPropInt( SENDINFO(g), 8, SPROP_UNSIGNED ), - SendPropInt( SENDINFO(b), 8, SPROP_UNSIGNED ), - SendPropInt( SENDINFO(a), 8, SPROP_UNSIGNED ), - SendPropModelIndex( SENDINFO(m_nSprayModel) ), - SendPropModelIndex( SENDINFO(m_nDropModel) ), - SendPropInt( SENDINFO(m_nSize), 8, SPROP_UNSIGNED ), -END_SEND_TABLE() - -// Singleton -static CTEBloodSprite g_TEBloodSprite( "Blood Sprite" ); - -//----------------------------------------------------------------------------- -// Purpose: Public interface -// Input : msg_dest - -// delay - -// *origin - -// *recipient - -// *org - -// r - -// g - -// b - -// a - -// size - -//----------------------------------------------------------------------------- -void TE_BloodSprite( IRecipientFilter& filter, float delay, - const Vector *org, const Vector *dir, int r, int g, int b, int a, int size ) -{ - // Set up parameters - g_TEBloodSprite.m_vecOrigin = *org; - g_TEBloodSprite.m_vecDirection = *dir; - g_TEBloodSprite.r = r; - g_TEBloodSprite.g = g; - g_TEBloodSprite.b = b; - g_TEBloodSprite.a = a; - g_TEBloodSprite.m_nSize = size; - - // Implicit - g_TEBloodSprite.m_nSprayModel = g_sModelIndexBloodSpray; - g_TEBloodSprite.m_nDropModel = g_sModelIndexBloodDrop; - - // Create it - g_TEBloodSprite.Create( filter, delay ); -} \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $Workfile: $ +// $Date: $ +// +//----------------------------------------------------------------------------- +// $Log: $ +// +// $NoKeywords: $ +//=============================================================================// +#include "cbase.h" +#include "basetempentity.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +extern short g_sModelIndexBloodDrop; // (in combatweapon.cpp) holds the sprite index for the initial blood +extern short g_sModelIndexBloodSpray; // (in combatweapon.cpp) holds the sprite index for splattered blood + +//----------------------------------------------------------------------------- +// Purpose: Display's a blood sprite +//----------------------------------------------------------------------------- +class CTEBloodSprite : public CBaseTempEntity +{ +public: + DECLARE_CLASS( CTEBloodSprite, CBaseTempEntity ); + + CTEBloodSprite( const char *name ); + virtual ~CTEBloodSprite( void ); + + virtual void Test( const Vector& current_origin, const QAngle& current_angles ); + + DECLARE_SERVERCLASS(); + +public: + CNetworkVector( m_vecOrigin ); + CNetworkVector( m_vecDirection ); + CNetworkVar( int, m_nSprayModel ); + CNetworkVar( int, m_nDropModel ); + CNetworkVar( int, r ); + CNetworkVar( int, g ); + CNetworkVar( int, b ); + CNetworkVar( int, a ); + CNetworkVar( int, m_nSize ); +}; + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *name - +//----------------------------------------------------------------------------- +CTEBloodSprite::CTEBloodSprite( const char *name ) : + CBaseTempEntity( name ) +{ + m_vecOrigin.Init(); + m_nSprayModel = 0; + m_nDropModel = 0; + r = 0; + g = 0; + b = 0; + a = 0; + m_nSize = 0; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CTEBloodSprite::~CTEBloodSprite( void ) +{ +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *current_origin - +// *current_angles - +//----------------------------------------------------------------------------- +void CTEBloodSprite::Test( const Vector& current_origin, const QAngle& current_angles ) +{ + // Fill in data + r = 255; + g = 255; + b = 63; + a = 255; + m_nSize = 16; + m_vecOrigin = current_origin; + + m_nSprayModel = g_sModelIndexBloodSpray; + m_nDropModel = g_sModelIndexBloodDrop; + + Vector forward; + + m_vecOrigin.GetForModify()[2] += 24; + + AngleVectors( current_angles, &forward ); + forward[2] = 0.0; + VectorNormalize( forward ); + + VectorMA( m_vecOrigin, 50.0, forward, m_vecOrigin.GetForModify() ); + + CBroadcastRecipientFilter filter; + Create( filter, 0.0 ); +} + +IMPLEMENT_SERVERCLASS_ST_NOBASE(CTEBloodSprite, DT_TEBloodSprite) + SendPropVector( SENDINFO(m_vecOrigin), -1, SPROP_COORD), + SendPropVector( SENDINFO(m_vecDirection), -1, SPROP_COORD), + SendPropInt( SENDINFO(r), 8, SPROP_UNSIGNED ), + SendPropInt( SENDINFO(g), 8, SPROP_UNSIGNED ), + SendPropInt( SENDINFO(b), 8, SPROP_UNSIGNED ), + SendPropInt( SENDINFO(a), 8, SPROP_UNSIGNED ), + SendPropModelIndex( SENDINFO(m_nSprayModel) ), + SendPropModelIndex( SENDINFO(m_nDropModel) ), + SendPropInt( SENDINFO(m_nSize), 8, SPROP_UNSIGNED ), +END_SEND_TABLE() + +// Singleton +static CTEBloodSprite g_TEBloodSprite( "Blood Sprite" ); + +//----------------------------------------------------------------------------- +// Purpose: Public interface +// Input : msg_dest - +// delay - +// *origin - +// *recipient - +// *org - +// r - +// g - +// b - +// a - +// size - +//----------------------------------------------------------------------------- +void TE_BloodSprite( IRecipientFilter& filter, float delay, + const Vector *org, const Vector *dir, int r, int g, int b, int a, int size ) +{ + // Set up parameters + g_TEBloodSprite.m_vecOrigin = *org; + g_TEBloodSprite.m_vecDirection = *dir; + g_TEBloodSprite.r = r; + g_TEBloodSprite.g = g; + g_TEBloodSprite.b = b; + g_TEBloodSprite.a = a; + g_TEBloodSprite.m_nSize = size; + + // Implicit + g_TEBloodSprite.m_nSprayModel = g_sModelIndexBloodSpray; + g_TEBloodSprite.m_nDropModel = g_sModelIndexBloodDrop; + + // Create it + g_TEBloodSprite.Create( filter, delay ); +} diff --git a/dlls/te_bloodstream.cpp b/dlls/te_bloodstream.cpp index d723c735..a5f9787c 100644 --- a/dlls/te_bloodstream.cpp +++ b/dlls/te_bloodstream.cpp @@ -1,135 +1,135 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $Workfile: $ -// $Date: $ -// -//----------------------------------------------------------------------------- -// $Log: $ -// -// $NoKeywords: $ -//=============================================================================// -#include "cbase.h" -#include "te_particlesystem.h" - -// memdbgon must be the last include file in a .cpp file!!! -#include "tier0/memdbgon.h" - -//----------------------------------------------------------------------------- -// Purpose: Dispatches blood stream tempentity -//----------------------------------------------------------------------------- -class CTEBloodStream : public CTEParticleSystem -{ -public: - DECLARE_CLASS( CTEBloodStream, CTEParticleSystem ); - - CTEBloodStream( const char *name ); - virtual ~CTEBloodStream( void ); - - virtual void Test( const Vector& current_origin, const QAngle& current_angles ); - - DECLARE_SERVERCLASS(); - -public: - CNetworkVector( m_vecDirection ); - CNetworkVar( int, r ); - CNetworkVar( int, g ); - CNetworkVar( int, b ); - CNetworkVar( int, a ); - CNetworkVar( int, m_nAmount ); -}; - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *name - -//----------------------------------------------------------------------------- -CTEBloodStream::CTEBloodStream( const char *name ) : - BaseClass( name ) -{ - m_vecDirection.Init(); - r = 0; - g = 0; - b = 0; - a = 0; - m_nAmount = 0; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -CTEBloodStream::~CTEBloodStream( void ) -{ -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *current_origin - -// *current_angles - -//----------------------------------------------------------------------------- -void CTEBloodStream::Test( const Vector& current_origin, const QAngle& current_angles ) -{ - // Fill in data - r = 247; - g = 0; - b = 0; - a = 255; - m_nAmount = random->RandomInt(50, 150); - m_vecOrigin = current_origin; - - Vector forward; - - m_vecOrigin.GetForModify()[2] += 24; - - AngleVectors( current_angles, &forward ); - forward[2] = 0.0; - VectorNormalize( forward ); - - m_vecOrigin += forward * 50; - - m_vecDirection = UTIL_RandomBloodVector(); - - CBroadcastRecipientFilter filter; - Create( filter, 0.0 ); -} - -IMPLEMENT_SERVERCLASS_ST(CTEBloodStream, DT_TEBloodStream) - SendPropVector( SENDINFO(m_vecDirection), 11, 0, -10.0, 10.0 ), - SendPropInt( SENDINFO(r), 8, SPROP_UNSIGNED ), - SendPropInt( SENDINFO(g), 8, SPROP_UNSIGNED ), - SendPropInt( SENDINFO(b), 8, SPROP_UNSIGNED ), - SendPropInt( SENDINFO(a), 8, SPROP_UNSIGNED ), - SendPropInt( SENDINFO(m_nAmount), 8, SPROP_UNSIGNED ), -END_SEND_TABLE() - -// Singleton to fire TEBloodStream objects -static CTEBloodStream g_TEBloodStream( "Blood Stream" ); - -//----------------------------------------------------------------------------- -// Purpose: Creates a blood stream -// Input : msg_dest - -// delay - -// *origin - -// *recipient - -// *org - -// *dir - -// r - -// g - -// b - -// a - -// amount - -//----------------------------------------------------------------------------- -void TE_BloodStream( IRecipientFilter& filter, float delay, - const Vector* org, const Vector* dir, int r, int g, int b, int a, int amount ) -{ - g_TEBloodStream.m_vecOrigin = *org; - g_TEBloodStream.m_vecDirection = *dir; - g_TEBloodStream.r = r; - g_TEBloodStream.g = g; - g_TEBloodStream.b = b; - g_TEBloodStream.a = a; - g_TEBloodStream.m_nAmount = amount; - - // Send it over the wire - g_TEBloodStream.Create( filter, delay ); -} \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $Workfile: $ +// $Date: $ +// +//----------------------------------------------------------------------------- +// $Log: $ +// +// $NoKeywords: $ +//=============================================================================// +#include "cbase.h" +#include "te_particlesystem.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +//----------------------------------------------------------------------------- +// Purpose: Dispatches blood stream tempentity +//----------------------------------------------------------------------------- +class CTEBloodStream : public CTEParticleSystem +{ +public: + DECLARE_CLASS( CTEBloodStream, CTEParticleSystem ); + + CTEBloodStream( const char *name ); + virtual ~CTEBloodStream( void ); + + virtual void Test( const Vector& current_origin, const QAngle& current_angles ); + + DECLARE_SERVERCLASS(); + +public: + CNetworkVector( m_vecDirection ); + CNetworkVar( int, r ); + CNetworkVar( int, g ); + CNetworkVar( int, b ); + CNetworkVar( int, a ); + CNetworkVar( int, m_nAmount ); +}; + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *name - +//----------------------------------------------------------------------------- +CTEBloodStream::CTEBloodStream( const char *name ) : + BaseClass( name ) +{ + m_vecDirection.Init(); + r = 0; + g = 0; + b = 0; + a = 0; + m_nAmount = 0; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CTEBloodStream::~CTEBloodStream( void ) +{ +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *current_origin - +// *current_angles - +//----------------------------------------------------------------------------- +void CTEBloodStream::Test( const Vector& current_origin, const QAngle& current_angles ) +{ + // Fill in data + r = 247; + g = 0; + b = 0; + a = 255; + m_nAmount = random->RandomInt(50, 150); + m_vecOrigin = current_origin; + + Vector forward; + + m_vecOrigin.GetForModify()[2] += 24; + + AngleVectors( current_angles, &forward ); + forward[2] = 0.0; + VectorNormalize( forward ); + + m_vecOrigin += forward * 50; + + m_vecDirection = UTIL_RandomBloodVector(); + + CBroadcastRecipientFilter filter; + Create( filter, 0.0 ); +} + +IMPLEMENT_SERVERCLASS_ST(CTEBloodStream, DT_TEBloodStream) + SendPropVector( SENDINFO(m_vecDirection), 11, 0, -10.0, 10.0 ), + SendPropInt( SENDINFO(r), 8, SPROP_UNSIGNED ), + SendPropInt( SENDINFO(g), 8, SPROP_UNSIGNED ), + SendPropInt( SENDINFO(b), 8, SPROP_UNSIGNED ), + SendPropInt( SENDINFO(a), 8, SPROP_UNSIGNED ), + SendPropInt( SENDINFO(m_nAmount), 8, SPROP_UNSIGNED ), +END_SEND_TABLE() + +// Singleton to fire TEBloodStream objects +static CTEBloodStream g_TEBloodStream( "Blood Stream" ); + +//----------------------------------------------------------------------------- +// Purpose: Creates a blood stream +// Input : msg_dest - +// delay - +// *origin - +// *recipient - +// *org - +// *dir - +// r - +// g - +// b - +// a - +// amount - +//----------------------------------------------------------------------------- +void TE_BloodStream( IRecipientFilter& filter, float delay, + const Vector* org, const Vector* dir, int r, int g, int b, int a, int amount ) +{ + g_TEBloodStream.m_vecOrigin = *org; + g_TEBloodStream.m_vecDirection = *dir; + g_TEBloodStream.r = r; + g_TEBloodStream.g = g; + g_TEBloodStream.b = b; + g_TEBloodStream.a = a; + g_TEBloodStream.m_nAmount = amount; + + // Send it over the wire + g_TEBloodStream.Create( filter, delay ); +} diff --git a/dlls/te_breakmodel.cpp b/dlls/te_breakmodel.cpp index 43cfced7..bfc8e689 100644 --- a/dlls/te_breakmodel.cpp +++ b/dlls/te_breakmodel.cpp @@ -1,146 +1,146 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $Workfile: $ -// $Date: $ -// $NoKeywords: $ -//=============================================================================// -#include "cbase.h" -#include "basetempentity.h" -#include "vstdlib/random.h" - -// memdbgon must be the last include file in a .cpp file!!! -#include "tier0/memdbgon.h" - -//----------------------------------------------------------------------------- -// Purpose: Dispatches model smash pieces -//----------------------------------------------------------------------------- -class CTEBreakModel : public CBaseTempEntity -{ -public: - DECLARE_CLASS( CTEBreakModel, CBaseTempEntity ); - - CTEBreakModel( const char *name ); - virtual ~CTEBreakModel( void ); - - virtual void Test( const Vector& current_origin, const QAngle& current_angles ); - - virtual void Precache( void ); - - DECLARE_SERVERCLASS(); - -public: - CNetworkVector( m_vecOrigin ); - CNetworkVector( m_vecSize ); - CNetworkVector( m_vecVelocity ); - CNetworkQAngle( m_angRotation ); - CNetworkVar( int, m_nRandomization ); - CNetworkVar( int, m_nModelIndex ); - CNetworkVar( int, m_nCount ); - CNetworkVar( float, m_fTime ); - CNetworkVar( int, m_nFlags ); -}; - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *name - -//----------------------------------------------------------------------------- -CTEBreakModel::CTEBreakModel( const char *name ) : - CBaseTempEntity( name ) -{ - m_vecOrigin.Init(); - m_vecSize.Init(); - m_vecVelocity.Init(); - m_angRotation.Init(); - m_nModelIndex = 0; - m_nRandomization = 0; - m_nCount = 0; - m_fTime = 0.0; - m_nFlags = 0; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -CTEBreakModel::~CTEBreakModel( void ) -{ -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CTEBreakModel::Precache( void ) -{ - CBaseEntity::PrecacheModel( "models/gibs/hgibs.mdl" ); -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *current_origin - -// *current_angles - -//----------------------------------------------------------------------------- -void CTEBreakModel::Test( const Vector& current_origin, const QAngle& current_angles ) -{ - // Fill in data - m_nModelIndex = CBaseEntity::PrecacheModel( "models/gibs/hgibs.mdl" ); - m_vecOrigin = current_origin; - m_angRotation = current_angles; - m_vecSize.Init( 16, 16, 16 ); - - m_vecVelocity.Init( random->RandomFloat( -10, 10 ), random->RandomFloat( -10, 10 ), random->RandomFloat( 0, 20 ) ); - - m_nRandomization = 100; - m_nCount = 10; - m_fTime = 5.0; - m_nFlags = 0; - - Vector forward, right; - - m_vecOrigin += Vector( 0, 0, 24 ); - - AngleVectors( current_angles, &forward, &right, 0 ); - forward[2] = 0.0; - VectorNormalize( forward ); - - VectorMA( m_vecOrigin, 50.0, forward, m_vecOrigin.GetForModify() ); - VectorMA( m_vecOrigin, 25.0, right, m_vecOrigin.GetForModify() ); - - CBroadcastRecipientFilter filter; - Create( filter, 0.0 ); -} - -IMPLEMENT_SERVERCLASS_ST(CTEBreakModel, DT_TEBreakModel) - SendPropVector( SENDINFO(m_vecOrigin), -1, SPROP_COORD), - SendPropAngle( SENDINFO_VECTORELEM(m_angRotation, 0), 13 ), - SendPropAngle( SENDINFO_VECTORELEM(m_angRotation, 1), 13 ), - SendPropAngle( SENDINFO_VECTORELEM(m_angRotation, 2), 13 ), - SendPropVector( SENDINFO(m_vecSize), -1, SPROP_COORD), - SendPropVector( SENDINFO(m_vecVelocity), -1, SPROP_COORD), - SendPropModelIndex( SENDINFO(m_nModelIndex) ), - SendPropInt( SENDINFO(m_nRandomization), 9, SPROP_UNSIGNED ), - SendPropInt( SENDINFO(m_nCount), 8, SPROP_UNSIGNED ), - SendPropFloat( SENDINFO(m_fTime), 10, 0, 0, 102.4 ), - SendPropInt( SENDINFO(m_nFlags), 8, SPROP_UNSIGNED ), -END_SEND_TABLE() - -// Singleton to fire TEBreakModel objects -static CTEBreakModel g_TEBreakModel( "breakmodel" ); - -void TE_BreakModel( IRecipientFilter& filter, float delay, - const Vector& pos, const QAngle& angles, const Vector& size, const Vector& vel, int modelindex, int randomization, - int count, float time, int flags ) -{ - g_TEBreakModel.m_vecOrigin = pos; - g_TEBreakModel.m_angRotation = angles; - g_TEBreakModel.m_vecSize = size; - g_TEBreakModel.m_vecVelocity = vel; - g_TEBreakModel.m_nModelIndex = modelindex; - g_TEBreakModel.m_nRandomization = randomization; - g_TEBreakModel.m_nCount = count; - g_TEBreakModel.m_fTime = time; - g_TEBreakModel.m_nFlags = flags; - - // Send it over the wire - g_TEBreakModel.Create( filter, delay ); -} \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $Workfile: $ +// $Date: $ +// $NoKeywords: $ +//=============================================================================// +#include "cbase.h" +#include "basetempentity.h" +#include "vstdlib/random.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +//----------------------------------------------------------------------------- +// Purpose: Dispatches model smash pieces +//----------------------------------------------------------------------------- +class CTEBreakModel : public CBaseTempEntity +{ +public: + DECLARE_CLASS( CTEBreakModel, CBaseTempEntity ); + + CTEBreakModel( const char *name ); + virtual ~CTEBreakModel( void ); + + virtual void Test( const Vector& current_origin, const QAngle& current_angles ); + + virtual void Precache( void ); + + DECLARE_SERVERCLASS(); + +public: + CNetworkVector( m_vecOrigin ); + CNetworkVector( m_vecSize ); + CNetworkVector( m_vecVelocity ); + CNetworkQAngle( m_angRotation ); + CNetworkVar( int, m_nRandomization ); + CNetworkVar( int, m_nModelIndex ); + CNetworkVar( int, m_nCount ); + CNetworkVar( float, m_fTime ); + CNetworkVar( int, m_nFlags ); +}; + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *name - +//----------------------------------------------------------------------------- +CTEBreakModel::CTEBreakModel( const char *name ) : + CBaseTempEntity( name ) +{ + m_vecOrigin.Init(); + m_vecSize.Init(); + m_vecVelocity.Init(); + m_angRotation.Init(); + m_nModelIndex = 0; + m_nRandomization = 0; + m_nCount = 0; + m_fTime = 0.0; + m_nFlags = 0; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CTEBreakModel::~CTEBreakModel( void ) +{ +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTEBreakModel::Precache( void ) +{ + CBaseEntity::PrecacheModel( "models/gibs/hgibs.mdl" ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *current_origin - +// *current_angles - +//----------------------------------------------------------------------------- +void CTEBreakModel::Test( const Vector& current_origin, const QAngle& current_angles ) +{ + // Fill in data + m_nModelIndex = CBaseEntity::PrecacheModel( "models/gibs/hgibs.mdl" ); + m_vecOrigin = current_origin; + m_angRotation = current_angles; + m_vecSize.Init( 16, 16, 16 ); + + m_vecVelocity.Init( random->RandomFloat( -10, 10 ), random->RandomFloat( -10, 10 ), random->RandomFloat( 0, 20 ) ); + + m_nRandomization = 100; + m_nCount = 10; + m_fTime = 5.0; + m_nFlags = 0; + + Vector forward, right; + + m_vecOrigin += Vector( 0, 0, 24 ); + + AngleVectors( current_angles, &forward, &right, 0 ); + forward[2] = 0.0; + VectorNormalize( forward ); + + VectorMA( m_vecOrigin, 50.0, forward, m_vecOrigin.GetForModify() ); + VectorMA( m_vecOrigin, 25.0, right, m_vecOrigin.GetForModify() ); + + CBroadcastRecipientFilter filter; + Create( filter, 0.0 ); +} + +IMPLEMENT_SERVERCLASS_ST(CTEBreakModel, DT_TEBreakModel) + SendPropVector( SENDINFO(m_vecOrigin), -1, SPROP_COORD), + SendPropAngle( SENDINFO_VECTORELEM(m_angRotation, 0), 13 ), + SendPropAngle( SENDINFO_VECTORELEM(m_angRotation, 1), 13 ), + SendPropAngle( SENDINFO_VECTORELEM(m_angRotation, 2), 13 ), + SendPropVector( SENDINFO(m_vecSize), -1, SPROP_COORD), + SendPropVector( SENDINFO(m_vecVelocity), -1, SPROP_COORD), + SendPropModelIndex( SENDINFO(m_nModelIndex) ), + SendPropInt( SENDINFO(m_nRandomization), 9, SPROP_UNSIGNED ), + SendPropInt( SENDINFO(m_nCount), 8, SPROP_UNSIGNED ), + SendPropFloat( SENDINFO(m_fTime), 10, 0, 0, 102.4 ), + SendPropInt( SENDINFO(m_nFlags), 8, SPROP_UNSIGNED ), +END_SEND_TABLE() + +// Singleton to fire TEBreakModel objects +static CTEBreakModel g_TEBreakModel( "breakmodel" ); + +void TE_BreakModel( IRecipientFilter& filter, float delay, + const Vector& pos, const QAngle& angles, const Vector& size, const Vector& vel, int modelindex, int randomization, + int count, float time, int flags ) +{ + g_TEBreakModel.m_vecOrigin = pos; + g_TEBreakModel.m_angRotation = angles; + g_TEBreakModel.m_vecSize = size; + g_TEBreakModel.m_vecVelocity = vel; + g_TEBreakModel.m_nModelIndex = modelindex; + g_TEBreakModel.m_nRandomization = randomization; + g_TEBreakModel.m_nCount = count; + g_TEBreakModel.m_fTime = time; + g_TEBreakModel.m_nFlags = flags; + + // Send it over the wire + g_TEBreakModel.Create( filter, delay ); +} diff --git a/dlls/te_bspdecal.cpp b/dlls/te_bspdecal.cpp index fd35901e..5572b856 100644 --- a/dlls/te_bspdecal.cpp +++ b/dlls/te_bspdecal.cpp @@ -1,125 +1,125 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $Workfile: $ -// $Date: $ -// -//----------------------------------------------------------------------------- -// $Log: $ -// -// $NoKeywords: $ -//=============================================================================// -#include "cbase.h" -#include "basetempentity.h" - -// memdbgon must be the last include file in a .cpp file!!! -#include "tier0/memdbgon.h" - -//----------------------------------------------------------------------------- -// Purpose: Dispatches BSP decal tempentity -//----------------------------------------------------------------------------- -class CTEBSPDecal : public CBaseTempEntity -{ -public: - DECLARE_CLASS( CTEBSPDecal, CBaseTempEntity ); - - CTEBSPDecal( const char *name ); - virtual ~CTEBSPDecal( void ); - - virtual void Test( const Vector& current_origin, const QAngle& current_angles ); - - DECLARE_SERVERCLASS(); - -public: - CNetworkVector( m_vecOrigin ); - CNetworkVar( int, m_nEntity ); - CNetworkVar( int, m_nIndex ); -}; - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *name - -//----------------------------------------------------------------------------- -CTEBSPDecal::CTEBSPDecal( const char *name ) : - CBaseTempEntity( name ) -{ - m_vecOrigin.Init(); - m_nEntity = 0; - m_nIndex = 0; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -CTEBSPDecal::~CTEBSPDecal( void ) -{ -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *current_origin - -// *current_angles - -//----------------------------------------------------------------------------- -void CTEBSPDecal::Test( const Vector& current_origin, const QAngle& current_angles ) -{ - // Fill in data - m_nEntity = 0; - m_nIndex = 0; - m_vecOrigin = current_origin; - - Vector vecEnd; - - Vector forward; - - m_vecOrigin.GetForModify()[2] += 24; - - AngleVectors( current_angles, &forward ); - forward[2] = 0.0; - VectorNormalize( forward ); - - VectorMA( m_vecOrigin, 50.0, forward, m_vecOrigin.GetForModify() ); - VectorMA( m_vecOrigin, 1024.0, forward, vecEnd ); - - trace_t tr; - - UTIL_TraceLine( m_vecOrigin, vecEnd, MASK_SOLID_BRUSHONLY, NULL, COLLISION_GROUP_NONE, &tr ); - - m_vecOrigin = tr.endpos; - - CBroadcastRecipientFilter filter; - Create( filter, 0.0 ); -} - - -IMPLEMENT_SERVERCLASS_ST(CTEBSPDecal, DT_TEBSPDecal) - SendPropVector( SENDINFO(m_vecOrigin), -1, SPROP_COORD), - SendPropInt( SENDINFO(m_nEntity), MAX_EDICT_BITS, SPROP_UNSIGNED ), - SendPropInt( SENDINFO(m_nIndex), 9, SPROP_UNSIGNED ), -END_SEND_TABLE() - - -// Singleton to fire TEBSPDecal objects -static CTEBSPDecal g_TEBSPDecal( "BSP Decal" ); - -//----------------------------------------------------------------------------- -// Purpose: -// Input : msg_dest - -// delay - -// *origin - -// *recipient - -// *pos - -// entity - -// index - -// modelindex - -//----------------------------------------------------------------------------- -void TE_BSPDecal( IRecipientFilter& filter, float delay, - const Vector* pos, int entity, int index ) -{ - g_TEBSPDecal.m_vecOrigin = *pos; - g_TEBSPDecal.m_nEntity = entity; - g_TEBSPDecal.m_nIndex = index; - - // Send it over the wire - g_TEBSPDecal.Create( filter, delay ); -} \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $Workfile: $ +// $Date: $ +// +//----------------------------------------------------------------------------- +// $Log: $ +// +// $NoKeywords: $ +//=============================================================================// +#include "cbase.h" +#include "basetempentity.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +//----------------------------------------------------------------------------- +// Purpose: Dispatches BSP decal tempentity +//----------------------------------------------------------------------------- +class CTEBSPDecal : public CBaseTempEntity +{ +public: + DECLARE_CLASS( CTEBSPDecal, CBaseTempEntity ); + + CTEBSPDecal( const char *name ); + virtual ~CTEBSPDecal( void ); + + virtual void Test( const Vector& current_origin, const QAngle& current_angles ); + + DECLARE_SERVERCLASS(); + +public: + CNetworkVector( m_vecOrigin ); + CNetworkVar( int, m_nEntity ); + CNetworkVar( int, m_nIndex ); +}; + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *name - +//----------------------------------------------------------------------------- +CTEBSPDecal::CTEBSPDecal( const char *name ) : + CBaseTempEntity( name ) +{ + m_vecOrigin.Init(); + m_nEntity = 0; + m_nIndex = 0; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CTEBSPDecal::~CTEBSPDecal( void ) +{ +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *current_origin - +// *current_angles - +//----------------------------------------------------------------------------- +void CTEBSPDecal::Test( const Vector& current_origin, const QAngle& current_angles ) +{ + // Fill in data + m_nEntity = 0; + m_nIndex = 0; + m_vecOrigin = current_origin; + + Vector vecEnd; + + Vector forward; + + m_vecOrigin.GetForModify()[2] += 24; + + AngleVectors( current_angles, &forward ); + forward[2] = 0.0; + VectorNormalize( forward ); + + VectorMA( m_vecOrigin, 50.0, forward, m_vecOrigin.GetForModify() ); + VectorMA( m_vecOrigin, 1024.0, forward, vecEnd ); + + trace_t tr; + + UTIL_TraceLine( m_vecOrigin, vecEnd, MASK_SOLID_BRUSHONLY, NULL, COLLISION_GROUP_NONE, &tr ); + + m_vecOrigin = tr.endpos; + + CBroadcastRecipientFilter filter; + Create( filter, 0.0 ); +} + + +IMPLEMENT_SERVERCLASS_ST(CTEBSPDecal, DT_TEBSPDecal) + SendPropVector( SENDINFO(m_vecOrigin), -1, SPROP_COORD), + SendPropInt( SENDINFO(m_nEntity), MAX_EDICT_BITS, SPROP_UNSIGNED ), + SendPropInt( SENDINFO(m_nIndex), 9, SPROP_UNSIGNED ), +END_SEND_TABLE() + + +// Singleton to fire TEBSPDecal objects +static CTEBSPDecal g_TEBSPDecal( "BSP Decal" ); + +//----------------------------------------------------------------------------- +// Purpose: +// Input : msg_dest - +// delay - +// *origin - +// *recipient - +// *pos - +// entity - +// index - +// modelindex - +//----------------------------------------------------------------------------- +void TE_BSPDecal( IRecipientFilter& filter, float delay, + const Vector* pos, int entity, int index ) +{ + g_TEBSPDecal.m_vecOrigin = *pos; + g_TEBSPDecal.m_nEntity = entity; + g_TEBSPDecal.m_nIndex = index; + + // Send it over the wire + g_TEBSPDecal.Create( filter, delay ); +} diff --git a/dlls/te_bubbles.cpp b/dlls/te_bubbles.cpp index 04495726..ab4f03d3 100644 --- a/dlls/te_bubbles.cpp +++ b/dlls/te_bubbles.cpp @@ -1,137 +1,137 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $Workfile: $ -// $Date: $ -// -//----------------------------------------------------------------------------- -// $Log: $ -// -// $NoKeywords: $ -//=============================================================================// -#include "cbase.h" -#include "basetempentity.h" - -// memdbgon must be the last include file in a .cpp file!!! -#include "tier0/memdbgon.h" - -extern short g_sModelIndexBubbles;// holds the index for the bubbles model - -//----------------------------------------------------------------------------- -// Purpose: Dispatches bubbles -//----------------------------------------------------------------------------- -class CTEBubbles : public CBaseTempEntity -{ -public: - DECLARE_CLASS( CTEBubbles, CBaseTempEntity ); - - CTEBubbles( const char *name ); - virtual ~CTEBubbles( void ); - - virtual void Test( const Vector& current_origin, const QAngle& current_angles ); - - DECLARE_SERVERCLASS(); - -public: - CNetworkVector( m_vecMins ); - CNetworkVector( m_vecMaxs ); - CNetworkVar( float, m_fHeight ); - CNetworkVar( int, m_nModelIndex ); - CNetworkVar( int, m_nCount ); - CNetworkVar( float, m_fSpeed ); -}; - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *name - -//----------------------------------------------------------------------------- -CTEBubbles::CTEBubbles( const char *name ) : - CBaseTempEntity( name ) -{ - m_vecMins.Init(); - m_vecMaxs.Init(); - m_fHeight = 0.0; - m_nModelIndex = 0; - m_nCount = 0; - m_fSpeed = 0; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -CTEBubbles::~CTEBubbles( void ) -{ -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *current_origin - -// *current_angles - -//----------------------------------------------------------------------------- -void CTEBubbles::Test( const Vector& current_origin, const QAngle& current_angles ) -{ - // Fill in data - m_vecMins = current_origin; - - Vector forward; - - m_vecMins.GetForModify()[2] += 24; - - AngleVectors( current_angles, &forward ); - forward[2] = 0.0; - VectorNormalize( forward ); - - VectorMA( m_vecMins, 100.0, forward, m_vecMins.GetForModify() ); - - m_vecMaxs = m_vecMins + Vector( 256, 256, 256 ); - - m_fSpeed = 2; - m_nCount = 50; - m_fHeight = 256; - - m_nModelIndex = g_sModelIndexBubbles; - - CBroadcastRecipientFilter filter; - Create( filter, 0.0 ); -} - -IMPLEMENT_SERVERCLASS_ST(CTEBubbles, DT_TEBubbles) - SendPropVector( SENDINFO(m_vecMins), -1, SPROP_COORD), - SendPropVector( SENDINFO(m_vecMaxs), -1, SPROP_COORD), - SendPropModelIndex( SENDINFO(m_nModelIndex) ), - SendPropFloat( SENDINFO(m_fHeight ), 17, 0, MIN_COORD_INTEGER, MAX_COORD_INTEGER ), - SendPropInt( SENDINFO(m_nCount), 8, SPROP_UNSIGNED ), - SendPropFloat( SENDINFO(m_fSpeed ), 17, 0, MIN_COORD_INTEGER, MAX_COORD_INTEGER ), -END_SEND_TABLE() - - -// Singleton to fire TEBubbles objects -static CTEBubbles g_TEBubbles( "Bubbles" ); - -//----------------------------------------------------------------------------- -// Purpose: -// Input : msg_dest - -// delay - -// *origin - -// *recipient - -// *mins - -// *maxs - -// height - -// modelindex - -// count - -// speed - -//----------------------------------------------------------------------------- -void TE_Bubbles( IRecipientFilter& filter, float delay, - const Vector* mins, const Vector* maxs, float height, int modelindex, int count, float speed ) -{ - g_TEBubbles.m_vecMins = *mins; - g_TEBubbles.m_vecMaxs = *maxs; - g_TEBubbles.m_fHeight = height; - g_TEBubbles.m_nModelIndex = modelindex; - g_TEBubbles.m_nCount = count; - g_TEBubbles.m_fSpeed = speed; - - // Send it over the wire - g_TEBubbles.Create( filter, delay ); -} \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $Workfile: $ +// $Date: $ +// +//----------------------------------------------------------------------------- +// $Log: $ +// +// $NoKeywords: $ +//=============================================================================// +#include "cbase.h" +#include "basetempentity.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +extern short g_sModelIndexBubbles;// holds the index for the bubbles model + +//----------------------------------------------------------------------------- +// Purpose: Dispatches bubbles +//----------------------------------------------------------------------------- +class CTEBubbles : public CBaseTempEntity +{ +public: + DECLARE_CLASS( CTEBubbles, CBaseTempEntity ); + + CTEBubbles( const char *name ); + virtual ~CTEBubbles( void ); + + virtual void Test( const Vector& current_origin, const QAngle& current_angles ); + + DECLARE_SERVERCLASS(); + +public: + CNetworkVector( m_vecMins ); + CNetworkVector( m_vecMaxs ); + CNetworkVar( float, m_fHeight ); + CNetworkVar( int, m_nModelIndex ); + CNetworkVar( int, m_nCount ); + CNetworkVar( float, m_fSpeed ); +}; + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *name - +//----------------------------------------------------------------------------- +CTEBubbles::CTEBubbles( const char *name ) : + CBaseTempEntity( name ) +{ + m_vecMins.Init(); + m_vecMaxs.Init(); + m_fHeight = 0.0; + m_nModelIndex = 0; + m_nCount = 0; + m_fSpeed = 0; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CTEBubbles::~CTEBubbles( void ) +{ +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *current_origin - +// *current_angles - +//----------------------------------------------------------------------------- +void CTEBubbles::Test( const Vector& current_origin, const QAngle& current_angles ) +{ + // Fill in data + m_vecMins = current_origin; + + Vector forward; + + m_vecMins.GetForModify()[2] += 24; + + AngleVectors( current_angles, &forward ); + forward[2] = 0.0; + VectorNormalize( forward ); + + VectorMA( m_vecMins, 100.0, forward, m_vecMins.GetForModify() ); + + m_vecMaxs = m_vecMins + Vector( 256, 256, 256 ); + + m_fSpeed = 2; + m_nCount = 50; + m_fHeight = 256; + + m_nModelIndex = g_sModelIndexBubbles; + + CBroadcastRecipientFilter filter; + Create( filter, 0.0 ); +} + +IMPLEMENT_SERVERCLASS_ST(CTEBubbles, DT_TEBubbles) + SendPropVector( SENDINFO(m_vecMins), -1, SPROP_COORD), + SendPropVector( SENDINFO(m_vecMaxs), -1, SPROP_COORD), + SendPropModelIndex( SENDINFO(m_nModelIndex) ), + SendPropFloat( SENDINFO(m_fHeight ), 17, 0, MIN_COORD_INTEGER, MAX_COORD_INTEGER ), + SendPropInt( SENDINFO(m_nCount), 8, SPROP_UNSIGNED ), + SendPropFloat( SENDINFO(m_fSpeed ), 17, 0, MIN_COORD_INTEGER, MAX_COORD_INTEGER ), +END_SEND_TABLE() + + +// Singleton to fire TEBubbles objects +static CTEBubbles g_TEBubbles( "Bubbles" ); + +//----------------------------------------------------------------------------- +// Purpose: +// Input : msg_dest - +// delay - +// *origin - +// *recipient - +// *mins - +// *maxs - +// height - +// modelindex - +// count - +// speed - +//----------------------------------------------------------------------------- +void TE_Bubbles( IRecipientFilter& filter, float delay, + const Vector* mins, const Vector* maxs, float height, int modelindex, int count, float speed ) +{ + g_TEBubbles.m_vecMins = *mins; + g_TEBubbles.m_vecMaxs = *maxs; + g_TEBubbles.m_fHeight = height; + g_TEBubbles.m_nModelIndex = modelindex; + g_TEBubbles.m_nCount = count; + g_TEBubbles.m_fSpeed = speed; + + // Send it over the wire + g_TEBubbles.Create( filter, delay ); +} diff --git a/dlls/te_bubbletrail.cpp b/dlls/te_bubbletrail.cpp index da0fbd20..3a535a1a 100644 --- a/dlls/te_bubbletrail.cpp +++ b/dlls/te_bubbletrail.cpp @@ -1,140 +1,140 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $Workfile: $ -// $Date: $ -// $NoKeywords: $ -//=============================================================================// -#include "cbase.h" -#include "basetempentity.h" - -// memdbgon must be the last include file in a .cpp file!!! -#include "tier0/memdbgon.h" - -extern short g_sModelIndexBubbles;// holds the index for the bubbles model - -enum -{ - BUBBLE_TRAIL_COUNT_BITS = 8, - BUBBLE_TRAIL_MAX_COUNT = ( (1 << BUBBLE_TRAIL_COUNT_BITS) - 1 ), -}; - - -//----------------------------------------------------------------------------- -// Purpose: Dispatches bubble trail -//----------------------------------------------------------------------------- -class CTEBubbleTrail : public CBaseTempEntity -{ -public: - DECLARE_CLASS( CTEBubbleTrail, CBaseTempEntity ); - - CTEBubbleTrail( const char *name ); - virtual ~CTEBubbleTrail( void ); - - virtual void Test( const Vector& current_origin, const QAngle& current_angles ); - - DECLARE_SERVERCLASS(); - -public: - CNetworkVector( m_vecMins ); - CNetworkVector( m_vecMaxs ); - CNetworkVar( float, m_flWaterZ ); - CNetworkVar( int, m_nModelIndex ); - CNetworkVar( int, m_nCount ); - CNetworkVar( float, m_fSpeed ); -}; - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *name - -//----------------------------------------------------------------------------- -CTEBubbleTrail::CTEBubbleTrail( const char *name ) : - CBaseTempEntity( name ) -{ - m_vecMins.Init(); - m_vecMaxs.Init(); - m_flWaterZ = 0.0; - m_nModelIndex = 0; - m_nCount = 0; - m_fSpeed = 0; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -CTEBubbleTrail::~CTEBubbleTrail( void ) -{ -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *current_origin - -// *current_angles - -//----------------------------------------------------------------------------- -void CTEBubbleTrail::Test( const Vector& current_origin, const QAngle& current_angles ) -{ - // Fill in data - m_vecMins = current_origin; - - Vector forward; - - m_vecMins.GetForModify()[2] += 24; - - AngleVectors( current_angles, &forward ); - forward[2] = 0.0; - VectorNormalize( forward ); - - VectorMA( m_vecMins, 100.0, forward, m_vecMins.GetForModify() ); - - m_vecMaxs = m_vecMins + Vector( 256, 256, 256 ); - - m_fSpeed = 8; - m_nCount = 20; - m_flWaterZ = 0; - - m_nModelIndex = g_sModelIndexBubbles; - - CBroadcastRecipientFilter filter; - Create( filter, 0.0 ); -} - -IMPLEMENT_SERVERCLASS_ST(CTEBubbleTrail, DT_TEBubbleTrail) - SendPropVector( SENDINFO(m_vecMins), -1, SPROP_COORD), - SendPropVector( SENDINFO(m_vecMaxs), -1, SPROP_COORD), - SendPropModelIndex( SENDINFO(m_nModelIndex) ), - SendPropFloat( SENDINFO(m_flWaterZ ), 17, 0, MIN_COORD_INTEGER, MAX_COORD_INTEGER ), - SendPropInt( SENDINFO(m_nCount), BUBBLE_TRAIL_COUNT_BITS, SPROP_UNSIGNED ), - SendPropFloat( SENDINFO(m_fSpeed ), 17, 0, MIN_COORD_INTEGER, MAX_COORD_INTEGER ), -END_SEND_TABLE() - - -// Singleton to fire TEBubbleTrail objects -static CTEBubbleTrail g_TEBubbleTrail( "Bubble Trail" ); - -//----------------------------------------------------------------------------- -// Purpose: -// Input : msg_dest - -// delay - -// *origin - -// *recipient - -// *mins - -// *maxs - -// height - -// modelindex - -// count - -// speed - -//----------------------------------------------------------------------------- -void TE_BubbleTrail( IRecipientFilter& filter, float delay, - const Vector* mins, const Vector* maxs, float flWaterZ, int modelindex, int count, float speed ) -{ - g_TEBubbleTrail.m_vecMins = *mins; - g_TEBubbleTrail.m_vecMaxs = *maxs; - g_TEBubbleTrail.m_flWaterZ = flWaterZ; - g_TEBubbleTrail.m_nModelIndex = modelindex; - g_TEBubbleTrail.m_nCount = min( count, BUBBLE_TRAIL_MAX_COUNT ); - g_TEBubbleTrail.m_fSpeed = speed; - - // Send it over the wire - g_TEBubbleTrail.Create( filter, delay ); -} \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $Workfile: $ +// $Date: $ +// $NoKeywords: $ +//=============================================================================// +#include "cbase.h" +#include "basetempentity.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +extern short g_sModelIndexBubbles;// holds the index for the bubbles model + +enum +{ + BUBBLE_TRAIL_COUNT_BITS = 8, + BUBBLE_TRAIL_MAX_COUNT = ( (1 << BUBBLE_TRAIL_COUNT_BITS) - 1 ), +}; + + +//----------------------------------------------------------------------------- +// Purpose: Dispatches bubble trail +//----------------------------------------------------------------------------- +class CTEBubbleTrail : public CBaseTempEntity +{ +public: + DECLARE_CLASS( CTEBubbleTrail, CBaseTempEntity ); + + CTEBubbleTrail( const char *name ); + virtual ~CTEBubbleTrail( void ); + + virtual void Test( const Vector& current_origin, const QAngle& current_angles ); + + DECLARE_SERVERCLASS(); + +public: + CNetworkVector( m_vecMins ); + CNetworkVector( m_vecMaxs ); + CNetworkVar( float, m_flWaterZ ); + CNetworkVar( int, m_nModelIndex ); + CNetworkVar( int, m_nCount ); + CNetworkVar( float, m_fSpeed ); +}; + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *name - +//----------------------------------------------------------------------------- +CTEBubbleTrail::CTEBubbleTrail( const char *name ) : + CBaseTempEntity( name ) +{ + m_vecMins.Init(); + m_vecMaxs.Init(); + m_flWaterZ = 0.0; + m_nModelIndex = 0; + m_nCount = 0; + m_fSpeed = 0; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CTEBubbleTrail::~CTEBubbleTrail( void ) +{ +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *current_origin - +// *current_angles - +//----------------------------------------------------------------------------- +void CTEBubbleTrail::Test( const Vector& current_origin, const QAngle& current_angles ) +{ + // Fill in data + m_vecMins = current_origin; + + Vector forward; + + m_vecMins.GetForModify()[2] += 24; + + AngleVectors( current_angles, &forward ); + forward[2] = 0.0; + VectorNormalize( forward ); + + VectorMA( m_vecMins, 100.0, forward, m_vecMins.GetForModify() ); + + m_vecMaxs = m_vecMins + Vector( 256, 256, 256 ); + + m_fSpeed = 8; + m_nCount = 20; + m_flWaterZ = 0; + + m_nModelIndex = g_sModelIndexBubbles; + + CBroadcastRecipientFilter filter; + Create( filter, 0.0 ); +} + +IMPLEMENT_SERVERCLASS_ST(CTEBubbleTrail, DT_TEBubbleTrail) + SendPropVector( SENDINFO(m_vecMins), -1, SPROP_COORD), + SendPropVector( SENDINFO(m_vecMaxs), -1, SPROP_COORD), + SendPropModelIndex( SENDINFO(m_nModelIndex) ), + SendPropFloat( SENDINFO(m_flWaterZ ), 17, 0, MIN_COORD_INTEGER, MAX_COORD_INTEGER ), + SendPropInt( SENDINFO(m_nCount), BUBBLE_TRAIL_COUNT_BITS, SPROP_UNSIGNED ), + SendPropFloat( SENDINFO(m_fSpeed ), 17, 0, MIN_COORD_INTEGER, MAX_COORD_INTEGER ), +END_SEND_TABLE() + + +// Singleton to fire TEBubbleTrail objects +static CTEBubbleTrail g_TEBubbleTrail( "Bubble Trail" ); + +//----------------------------------------------------------------------------- +// Purpose: +// Input : msg_dest - +// delay - +// *origin - +// *recipient - +// *mins - +// *maxs - +// height - +// modelindex - +// count - +// speed - +//----------------------------------------------------------------------------- +void TE_BubbleTrail( IRecipientFilter& filter, float delay, + const Vector* mins, const Vector* maxs, float flWaterZ, int modelindex, int count, float speed ) +{ + g_TEBubbleTrail.m_vecMins = *mins; + g_TEBubbleTrail.m_vecMaxs = *maxs; + g_TEBubbleTrail.m_flWaterZ = flWaterZ; + g_TEBubbleTrail.m_nModelIndex = modelindex; + g_TEBubbleTrail.m_nCount = min( count, BUBBLE_TRAIL_MAX_COUNT ); + g_TEBubbleTrail.m_fSpeed = speed; + + // Send it over the wire + g_TEBubbleTrail.Create( filter, delay ); +} diff --git a/dlls/te_decal.cpp b/dlls/te_decal.cpp index eacfe2cf..470e1926 100644 --- a/dlls/te_decal.cpp +++ b/dlls/te_decal.cpp @@ -1,131 +1,131 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $Workfile: $ -// $Date: $ -// -//----------------------------------------------------------------------------- -// $Log: $ -// -// $NoKeywords: $ -//=============================================================================// -#include "cbase.h" -#include "basetempentity.h" - -// memdbgon must be the last include file in a .cpp file!!! -#include "tier0/memdbgon.h" - -//----------------------------------------------------------------------------- -// Purpose: Dispatches decal tempentity -//----------------------------------------------------------------------------- -class CTEDecal : public CBaseTempEntity -{ -public: - DECLARE_CLASS( CTEDecal, CBaseTempEntity ); - - CTEDecal( const char *name ); - virtual ~CTEDecal( void ); - - virtual void Test( const Vector& current_origin, const QAngle& current_angles ); - - DECLARE_SERVERCLASS(); - -public: - CNetworkVector( m_vecOrigin ); - CNetworkVector( m_vecStart ); - CNetworkVar( int, m_nEntity ); - CNetworkVar( int, m_nHitbox ); - CNetworkVar( int, m_nIndex ); -}; - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *name - -//----------------------------------------------------------------------------- -CTEDecal::CTEDecal( const char *name ) : - CBaseTempEntity( name ) -{ - m_vecOrigin.Init(); - m_nEntity = 0; - m_nIndex = 0; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -CTEDecal::~CTEDecal( void ) -{ -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *current_origin - -// *current_angles - -//----------------------------------------------------------------------------- -void CTEDecal::Test( const Vector& current_origin, const QAngle& current_angles ) -{ - // Fill in data - m_nEntity = 0; - m_nIndex = 0; - m_vecOrigin = current_origin; - - Vector vecEnd; - - Vector forward; - - m_vecOrigin.GetForModify()[2] += 24; - - AngleVectors( current_angles, &forward ); - forward[2] = 0.0; - VectorNormalize( forward ); - - VectorMA( m_vecOrigin, 50.0, forward, m_vecOrigin.GetForModify() ); - VectorMA( m_vecOrigin, 1024.0, forward, vecEnd ); - - trace_t tr; - - UTIL_TraceLine( m_vecOrigin, vecEnd, MASK_SOLID_BRUSHONLY, NULL, COLLISION_GROUP_NONE, &tr ); - - m_vecOrigin = tr.endpos; - - CBroadcastRecipientFilter filter; - Create( filter, 0.0 ); -} - - -IMPLEMENT_SERVERCLASS_ST(CTEDecal, DT_TEDecal) - SendPropVector( SENDINFO(m_vecOrigin), -1, SPROP_COORD), - SendPropVector( SENDINFO(m_vecStart), -1, SPROP_COORD), - SendPropInt( SENDINFO(m_nEntity), MAX_EDICT_BITS, SPROP_UNSIGNED ), - SendPropInt( SENDINFO(m_nHitbox), 11, SPROP_UNSIGNED ), - SendPropInt( SENDINFO(m_nIndex), 9, SPROP_UNSIGNED ), -END_SEND_TABLE() - - -// Singleton to fire TEDecal objects -static CTEDecal g_TEDecal( "Entity Decal" ); - -//----------------------------------------------------------------------------- -// Purpose: -// Input : msg_dest - -// delay - -// *origin - -// *recipient - -// *pos - -// entity - -// index - -//----------------------------------------------------------------------------- -void TE_Decal( IRecipientFilter& filter, float delay, - const Vector* pos, const Vector* start, int entity, int hitbox, int index ) -{ - Assert( pos && start ); - g_TEDecal.m_vecOrigin = *pos; - g_TEDecal.m_vecStart = *start; - g_TEDecal.m_nEntity = entity; - g_TEDecal.m_nHitbox = hitbox; - g_TEDecal.m_nIndex = index; - - // Send it over the wire - g_TEDecal.Create( filter, delay ); -} \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $Workfile: $ +// $Date: $ +// +//----------------------------------------------------------------------------- +// $Log: $ +// +// $NoKeywords: $ +//=============================================================================// +#include "cbase.h" +#include "basetempentity.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +//----------------------------------------------------------------------------- +// Purpose: Dispatches decal tempentity +//----------------------------------------------------------------------------- +class CTEDecal : public CBaseTempEntity +{ +public: + DECLARE_CLASS( CTEDecal, CBaseTempEntity ); + + CTEDecal( const char *name ); + virtual ~CTEDecal( void ); + + virtual void Test( const Vector& current_origin, const QAngle& current_angles ); + + DECLARE_SERVERCLASS(); + +public: + CNetworkVector( m_vecOrigin ); + CNetworkVector( m_vecStart ); + CNetworkVar( int, m_nEntity ); + CNetworkVar( int, m_nHitbox ); + CNetworkVar( int, m_nIndex ); +}; + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *name - +//----------------------------------------------------------------------------- +CTEDecal::CTEDecal( const char *name ) : + CBaseTempEntity( name ) +{ + m_vecOrigin.Init(); + m_nEntity = 0; + m_nIndex = 0; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CTEDecal::~CTEDecal( void ) +{ +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *current_origin - +// *current_angles - +//----------------------------------------------------------------------------- +void CTEDecal::Test( const Vector& current_origin, const QAngle& current_angles ) +{ + // Fill in data + m_nEntity = 0; + m_nIndex = 0; + m_vecOrigin = current_origin; + + Vector vecEnd; + + Vector forward; + + m_vecOrigin.GetForModify()[2] += 24; + + AngleVectors( current_angles, &forward ); + forward[2] = 0.0; + VectorNormalize( forward ); + + VectorMA( m_vecOrigin, 50.0, forward, m_vecOrigin.GetForModify() ); + VectorMA( m_vecOrigin, 1024.0, forward, vecEnd ); + + trace_t tr; + + UTIL_TraceLine( m_vecOrigin, vecEnd, MASK_SOLID_BRUSHONLY, NULL, COLLISION_GROUP_NONE, &tr ); + + m_vecOrigin = tr.endpos; + + CBroadcastRecipientFilter filter; + Create( filter, 0.0 ); +} + + +IMPLEMENT_SERVERCLASS_ST(CTEDecal, DT_TEDecal) + SendPropVector( SENDINFO(m_vecOrigin), -1, SPROP_COORD), + SendPropVector( SENDINFO(m_vecStart), -1, SPROP_COORD), + SendPropInt( SENDINFO(m_nEntity), MAX_EDICT_BITS, SPROP_UNSIGNED ), + SendPropInt( SENDINFO(m_nHitbox), 11, SPROP_UNSIGNED ), + SendPropInt( SENDINFO(m_nIndex), 9, SPROP_UNSIGNED ), +END_SEND_TABLE() + + +// Singleton to fire TEDecal objects +static CTEDecal g_TEDecal( "Entity Decal" ); + +//----------------------------------------------------------------------------- +// Purpose: +// Input : msg_dest - +// delay - +// *origin - +// *recipient - +// *pos - +// entity - +// index - +//----------------------------------------------------------------------------- +void TE_Decal( IRecipientFilter& filter, float delay, + const Vector* pos, const Vector* start, int entity, int hitbox, int index ) +{ + Assert( pos && start ); + g_TEDecal.m_vecOrigin = *pos; + g_TEDecal.m_vecStart = *start; + g_TEDecal.m_nEntity = entity; + g_TEDecal.m_nHitbox = hitbox; + g_TEDecal.m_nIndex = index; + + // Send it over the wire + g_TEDecal.Create( filter, delay ); +} diff --git a/dlls/te_dynamiclight.cpp b/dlls/te_dynamiclight.cpp index f9639b43..8a36b701 100644 --- a/dlls/te_dynamiclight.cpp +++ b/dlls/te_dynamiclight.cpp @@ -1,144 +1,144 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $Workfile: $ -// $Date: $ -// -//----------------------------------------------------------------------------- -// $Log: $ -// -// $NoKeywords: $ -//=============================================================================// -#include "cbase.h" -#include "basetempentity.h" - -// memdbgon must be the last include file in a .cpp file!!! -#include "tier0/memdbgon.h" - -//----------------------------------------------------------------------------- -// Purpose: Displays a dynamic light -//----------------------------------------------------------------------------- -class CTEDynamicLight : public CBaseTempEntity -{ -public: - DECLARE_CLASS( CTEDynamicLight, CBaseTempEntity ); - - CTEDynamicLight( const char *name ); - virtual ~CTEDynamicLight( void ); - - virtual void Test( const Vector& current_origin, const QAngle& current_angles ); - - DECLARE_SERVERCLASS(); - -public: - CNetworkVector( m_vecOrigin ); - CNetworkVar( float, m_fRadius ); - CNetworkVar( int, r ); - CNetworkVar( int, g ); - CNetworkVar( int, b ); - CNetworkVar( int, exponent ); - CNetworkVar( float, m_fTime ); - CNetworkVar( float, m_fDecay ); -}; - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *name - -//----------------------------------------------------------------------------- -CTEDynamicLight::CTEDynamicLight( const char *name ) : - CBaseTempEntity( name ) -{ - m_vecOrigin.Init(); - r = 0; - g = 0; - b = 0; - exponent = 0; - m_fRadius = 0.0; - m_fTime = 0.0; - m_fDecay = 0.0; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -CTEDynamicLight::~CTEDynamicLight( void ) -{ -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *current_origin - -// *current_angles - -//----------------------------------------------------------------------------- -void CTEDynamicLight::Test( const Vector& current_origin, const QAngle& current_angles ) -{ - // Fill in data - r = 255; - g = 255; - b = 63; - m_vecOrigin = current_origin; - - m_fRadius = 200; - m_fTime = 2.0; - m_fDecay = 0.0; - - Vector forward; - - m_vecOrigin.GetForModify()[2] += 24; - - AngleVectors( current_angles, &forward ); - forward[2] = 0.0; - VectorNormalize( forward ); - - VectorMA( m_vecOrigin, 50.0, forward, m_vecOrigin.GetForModify() ); - - CBroadcastRecipientFilter filter; - Create( filter, 0.0 ); -} - -IMPLEMENT_SERVERCLASS_ST(CTEDynamicLight, DT_TEDynamicLight) - SendPropVector( SENDINFO(m_vecOrigin), -1, SPROP_COORD), - SendPropInt( SENDINFO(r), 8, SPROP_UNSIGNED ), - SendPropInt( SENDINFO(g), 8, SPROP_UNSIGNED ), - SendPropInt( SENDINFO(b), 8, SPROP_UNSIGNED ), - SendPropInt( SENDINFO(exponent), 8, 0 ), - SendPropFloat( SENDINFO(m_fRadius), 8, SPROP_ROUNDUP, 0, 2560.0 ), - SendPropFloat( SENDINFO(m_fTime), 8, SPROP_ROUNDDOWN, 0, 25.6 ), - SendPropFloat( SENDINFO(m_fDecay), 8, SPROP_ROUNDDOWN, 0, 2560.0 ), -END_SEND_TABLE() - - -// Singleton -static CTEDynamicLight g_TEDynamicLight( "Dynamic Light" ); - -//----------------------------------------------------------------------------- -// Purpose: -// Input : msg_dest - -// delay - -// *origin - -// *recipient - -// *org - -// r - -// g - -// b - -// radius - -// time - -// decay - -//----------------------------------------------------------------------------- -void TE_DynamicLight( IRecipientFilter& filter, float delay, - const Vector* org, int r, int g, int b, int exponent, float radius, float time, float decay ) -{ - // Set up parameters - g_TEDynamicLight.m_vecOrigin = *org; - g_TEDynamicLight.r = r; - g_TEDynamicLight.g = g; - g_TEDynamicLight.b = b; - g_TEDynamicLight.exponent = exponent; - g_TEDynamicLight.m_fRadius = radius; - g_TEDynamicLight.m_fTime = time; - g_TEDynamicLight.m_fDecay = decay; - - // Create it - g_TEDynamicLight.Create( filter, delay ); -} \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $Workfile: $ +// $Date: $ +// +//----------------------------------------------------------------------------- +// $Log: $ +// +// $NoKeywords: $ +//=============================================================================// +#include "cbase.h" +#include "basetempentity.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +//----------------------------------------------------------------------------- +// Purpose: Displays a dynamic light +//----------------------------------------------------------------------------- +class CTEDynamicLight : public CBaseTempEntity +{ +public: + DECLARE_CLASS( CTEDynamicLight, CBaseTempEntity ); + + CTEDynamicLight( const char *name ); + virtual ~CTEDynamicLight( void ); + + virtual void Test( const Vector& current_origin, const QAngle& current_angles ); + + DECLARE_SERVERCLASS(); + +public: + CNetworkVector( m_vecOrigin ); + CNetworkVar( float, m_fRadius ); + CNetworkVar( int, r ); + CNetworkVar( int, g ); + CNetworkVar( int, b ); + CNetworkVar( int, exponent ); + CNetworkVar( float, m_fTime ); + CNetworkVar( float, m_fDecay ); +}; + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *name - +//----------------------------------------------------------------------------- +CTEDynamicLight::CTEDynamicLight( const char *name ) : + CBaseTempEntity( name ) +{ + m_vecOrigin.Init(); + r = 0; + g = 0; + b = 0; + exponent = 0; + m_fRadius = 0.0; + m_fTime = 0.0; + m_fDecay = 0.0; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CTEDynamicLight::~CTEDynamicLight( void ) +{ +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *current_origin - +// *current_angles - +//----------------------------------------------------------------------------- +void CTEDynamicLight::Test( const Vector& current_origin, const QAngle& current_angles ) +{ + // Fill in data + r = 255; + g = 255; + b = 63; + m_vecOrigin = current_origin; + + m_fRadius = 200; + m_fTime = 2.0; + m_fDecay = 0.0; + + Vector forward; + + m_vecOrigin.GetForModify()[2] += 24; + + AngleVectors( current_angles, &forward ); + forward[2] = 0.0; + VectorNormalize( forward ); + + VectorMA( m_vecOrigin, 50.0, forward, m_vecOrigin.GetForModify() ); + + CBroadcastRecipientFilter filter; + Create( filter, 0.0 ); +} + +IMPLEMENT_SERVERCLASS_ST(CTEDynamicLight, DT_TEDynamicLight) + SendPropVector( SENDINFO(m_vecOrigin), -1, SPROP_COORD), + SendPropInt( SENDINFO(r), 8, SPROP_UNSIGNED ), + SendPropInt( SENDINFO(g), 8, SPROP_UNSIGNED ), + SendPropInt( SENDINFO(b), 8, SPROP_UNSIGNED ), + SendPropInt( SENDINFO(exponent), 8, 0 ), + SendPropFloat( SENDINFO(m_fRadius), 8, SPROP_ROUNDUP, 0, 2560.0 ), + SendPropFloat( SENDINFO(m_fTime), 8, SPROP_ROUNDDOWN, 0, 25.6 ), + SendPropFloat( SENDINFO(m_fDecay), 8, SPROP_ROUNDDOWN, 0, 2560.0 ), +END_SEND_TABLE() + + +// Singleton +static CTEDynamicLight g_TEDynamicLight( "Dynamic Light" ); + +//----------------------------------------------------------------------------- +// Purpose: +// Input : msg_dest - +// delay - +// *origin - +// *recipient - +// *org - +// r - +// g - +// b - +// radius - +// time - +// decay - +//----------------------------------------------------------------------------- +void TE_DynamicLight( IRecipientFilter& filter, float delay, + const Vector* org, int r, int g, int b, int exponent, float radius, float time, float decay ) +{ + // Set up parameters + g_TEDynamicLight.m_vecOrigin = *org; + g_TEDynamicLight.r = r; + g_TEDynamicLight.g = g; + g_TEDynamicLight.b = b; + g_TEDynamicLight.exponent = exponent; + g_TEDynamicLight.m_fRadius = radius; + g_TEDynamicLight.m_fTime = time; + g_TEDynamicLight.m_fDecay = decay; + + // Create it + g_TEDynamicLight.Create( filter, delay ); +} diff --git a/dlls/te_explosion.cpp b/dlls/te_explosion.cpp index 49c504fa..dd09c640 100644 --- a/dlls/te_explosion.cpp +++ b/dlls/te_explosion.cpp @@ -1,132 +1,132 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $Workfile: $ -// $Date: $ -// -//----------------------------------------------------------------------------- -// $Log: $ -// -// $NoKeywords: $ -//=============================================================================// -#include "cbase.h" -#include "te_particlesystem.h" - -// memdbgon must be the last include file in a .cpp file!!! -#include "tier0/memdbgon.h" - -extern short g_sModelIndexFireball; // (in combatweapon.cpp) holds the index for the smoke cloud - -//----------------------------------------------------------------------------- -// Purpose: Dispatches explosion tempentity -//----------------------------------------------------------------------------- -class CTEExplosion : public CTEParticleSystem -{ -public: - DECLARE_CLASS( CTEExplosion, CTEParticleSystem ); - DECLARE_SERVERCLASS(); - - CTEExplosion( const char *name ); - virtual ~CTEExplosion( void ); - - virtual void Test( const Vector& current_origin, const QAngle& current_angles ); - - -public: - CNetworkVar( int, m_nModelIndex ); - CNetworkVar( float, m_fScale ); - CNetworkVar( int, m_nFrameRate ); - CNetworkVar( int, m_nFlags ); - CNetworkVector( m_vecNormal ); - CNetworkVar( unsigned char, m_chMaterialType ); - CNetworkVar( int, m_nRadius ); - CNetworkVar( int, m_nMagnitude ); -}; - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *name - -//----------------------------------------------------------------------------- -CTEExplosion::CTEExplosion( const char *name ) : - BaseClass( name ) -{ - m_nModelIndex = 0; - m_fScale = 0; - m_nFrameRate = 0; - m_nFlags = 0; - m_vecNormal.Init(); - m_chMaterialType = 'C'; - m_nRadius = 0; - m_nMagnitude = 0; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -CTEExplosion::~CTEExplosion( void ) -{ -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *current_origin - -// *current_angles - -//----------------------------------------------------------------------------- -void CTEExplosion::Test( const Vector& current_origin, const QAngle& current_angles ) -{ - // Fill in data - m_nModelIndex = g_sModelIndexFireball; - m_fScale = 0.5; - m_nFrameRate = 15; - m_nFlags = TE_EXPLFLAG_NONE; - m_vecOrigin = current_origin; - - Vector forward; - - m_vecOrigin.GetForModify()[2] += 24; - - AngleVectors( current_angles, &forward ); - forward[2] = 0.0; - VectorNormalize( forward ); - - m_vecOrigin += forward * 50; - - CBroadcastRecipientFilter filter; - Create( filter, 0.0 ); -} - -IMPLEMENT_SERVERCLASS_ST(CTEExplosion, DT_TEExplosion) - SendPropModelIndex( SENDINFO(m_nModelIndex) ), - SendPropFloat( SENDINFO(m_fScale ), 9, 0, 0.0, 51.2 ), - SendPropInt( SENDINFO(m_nFrameRate), 8, SPROP_UNSIGNED ), - SendPropInt( SENDINFO(m_nFlags), 8, SPROP_UNSIGNED ), - SendPropVector( SENDINFO(m_vecNormal), -1, SPROP_COORD), - SendPropInt( SENDINFO(m_chMaterialType), 8, SPROP_UNSIGNED ), - SendPropInt( SENDINFO(m_nRadius), 32, SPROP_UNSIGNED ), - SendPropInt( SENDINFO(m_nMagnitude), 32, SPROP_UNSIGNED ), -END_SEND_TABLE() - -// Singleton to fire TEExplosion objects -static CTEExplosion g_TEExplosion( "Explosion" ); - -void TE_Explosion( IRecipientFilter& filter, float delay, - const Vector* pos, int modelindex, float scale, int framerate, int flags, int radius, int magnitude, const Vector* normal, unsigned char materialType ) -{ - g_TEExplosion.m_vecOrigin = *pos; - g_TEExplosion.m_nModelIndex = modelindex; - g_TEExplosion.m_fScale = scale; - g_TEExplosion.m_nFrameRate = framerate; - g_TEExplosion.m_nFlags = flags; - g_TEExplosion.m_nRadius = radius; - g_TEExplosion.m_nMagnitude = magnitude; - - if ( normal ) - g_TEExplosion.m_vecNormal = *normal; - else - g_TEExplosion.m_vecNormal = Vector(0,0,1); - g_TEExplosion.m_chMaterialType = materialType; - - // Send it over the wire - g_TEExplosion.Create( filter, delay ); -} \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $Workfile: $ +// $Date: $ +// +//----------------------------------------------------------------------------- +// $Log: $ +// +// $NoKeywords: $ +//=============================================================================// +#include "cbase.h" +#include "te_particlesystem.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +extern short g_sModelIndexFireball; // (in combatweapon.cpp) holds the index for the smoke cloud + +//----------------------------------------------------------------------------- +// Purpose: Dispatches explosion tempentity +//----------------------------------------------------------------------------- +class CTEExplosion : public CTEParticleSystem +{ +public: + DECLARE_CLASS( CTEExplosion, CTEParticleSystem ); + DECLARE_SERVERCLASS(); + + CTEExplosion( const char *name ); + virtual ~CTEExplosion( void ); + + virtual void Test( const Vector& current_origin, const QAngle& current_angles ); + + +public: + CNetworkVar( int, m_nModelIndex ); + CNetworkVar( float, m_fScale ); + CNetworkVar( int, m_nFrameRate ); + CNetworkVar( int, m_nFlags ); + CNetworkVector( m_vecNormal ); + CNetworkVar( unsigned char, m_chMaterialType ); + CNetworkVar( int, m_nRadius ); + CNetworkVar( int, m_nMagnitude ); +}; + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *name - +//----------------------------------------------------------------------------- +CTEExplosion::CTEExplosion( const char *name ) : + BaseClass( name ) +{ + m_nModelIndex = 0; + m_fScale = 0; + m_nFrameRate = 0; + m_nFlags = 0; + m_vecNormal.Init(); + m_chMaterialType = 'C'; + m_nRadius = 0; + m_nMagnitude = 0; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CTEExplosion::~CTEExplosion( void ) +{ +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *current_origin - +// *current_angles - +//----------------------------------------------------------------------------- +void CTEExplosion::Test( const Vector& current_origin, const QAngle& current_angles ) +{ + // Fill in data + m_nModelIndex = g_sModelIndexFireball; + m_fScale = 0.5; + m_nFrameRate = 15; + m_nFlags = TE_EXPLFLAG_NONE; + m_vecOrigin = current_origin; + + Vector forward; + + m_vecOrigin.GetForModify()[2] += 24; + + AngleVectors( current_angles, &forward ); + forward[2] = 0.0; + VectorNormalize( forward ); + + m_vecOrigin += forward * 50; + + CBroadcastRecipientFilter filter; + Create( filter, 0.0 ); +} + +IMPLEMENT_SERVERCLASS_ST(CTEExplosion, DT_TEExplosion) + SendPropModelIndex( SENDINFO(m_nModelIndex) ), + SendPropFloat( SENDINFO(m_fScale ), 9, 0, 0.0, 51.2 ), + SendPropInt( SENDINFO(m_nFrameRate), 8, SPROP_UNSIGNED ), + SendPropInt( SENDINFO(m_nFlags), 8, SPROP_UNSIGNED ), + SendPropVector( SENDINFO(m_vecNormal), -1, SPROP_COORD), + SendPropInt( SENDINFO(m_chMaterialType), 8, SPROP_UNSIGNED ), + SendPropInt( SENDINFO(m_nRadius), 32, SPROP_UNSIGNED ), + SendPropInt( SENDINFO(m_nMagnitude), 32, SPROP_UNSIGNED ), +END_SEND_TABLE() + +// Singleton to fire TEExplosion objects +static CTEExplosion g_TEExplosion( "Explosion" ); + +void TE_Explosion( IRecipientFilter& filter, float delay, + const Vector* pos, int modelindex, float scale, int framerate, int flags, int radius, int magnitude, const Vector* normal, unsigned char materialType ) +{ + g_TEExplosion.m_vecOrigin = *pos; + g_TEExplosion.m_nModelIndex = modelindex; + g_TEExplosion.m_fScale = scale; + g_TEExplosion.m_nFrameRate = framerate; + g_TEExplosion.m_nFlags = flags; + g_TEExplosion.m_nRadius = radius; + g_TEExplosion.m_nMagnitude = magnitude; + + if ( normal ) + g_TEExplosion.m_vecNormal = *normal; + else + g_TEExplosion.m_vecNormal = Vector(0,0,1); + g_TEExplosion.m_chMaterialType = materialType; + + // Send it over the wire + g_TEExplosion.Create( filter, delay ); +} diff --git a/dlls/te_fizz.cpp b/dlls/te_fizz.cpp index 75e614fb..8c806bbc 100644 --- a/dlls/te_fizz.cpp +++ b/dlls/te_fizz.cpp @@ -1,112 +1,112 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $Workfile: $ -// $Date: $ -// -//----------------------------------------------------------------------------- -// $Log: $ -// -// $NoKeywords: $ -//=============================================================================// -#include "cbase.h" -#include "basetempentity.h" - -// memdbgon must be the last include file in a .cpp file!!! -#include "tier0/memdbgon.h" - -//----------------------------------------------------------------------------- -// Purpose: Dispatches Fizz tempentity -//----------------------------------------------------------------------------- -class CTEFizz : public CBaseTempEntity -{ -public: - DECLARE_CLASS( CTEFizz, CBaseTempEntity ); - - CTEFizz( const char *name ); - virtual ~CTEFizz( void ); - - virtual void Test( const Vector& current_origin, const QAngle& current_angles ); - - virtual void Precache( void ); - - DECLARE_SERVERCLASS(); - -public: - CNetworkVar( int, m_nEntity ); - CNetworkVar( int, m_nModelIndex ); - CNetworkVar( int, m_nDensity ); - CNetworkVar( int, m_nCurrent ); -}; - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *name - -//----------------------------------------------------------------------------- -CTEFizz::CTEFizz( const char *name ) : - CBaseTempEntity( name ) -{ - m_nEntity = 0; - m_nModelIndex = 0; - m_nDensity = 0; - m_nCurrent = 0; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -CTEFizz::~CTEFizz( void ) -{ -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *current_origin - -// *current_angles - -//----------------------------------------------------------------------------- -void CTEFizz::Test( const Vector& current_origin, const QAngle& current_angles ) -{ - // Fill in data - m_nModelIndex = CBaseEntity::PrecacheModel( "sprites/bubble.vmt" );; - m_nDensity = 200; - m_nEntity = 1; - m_nCurrent = 100; - - CBroadcastRecipientFilter filter; - Create( filter, 0.0 ); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CTEFizz::Precache( void ) -{ - CBaseEntity::PrecacheModel( "sprites/bubble.vmt" ); -} - - -IMPLEMENT_SERVERCLASS_ST(CTEFizz, DT_TEFizz) - SendPropInt( SENDINFO(m_nEntity), MAX_EDICT_BITS, SPROP_UNSIGNED ), - SendPropModelIndex( SENDINFO(m_nModelIndex) ), - SendPropInt( SENDINFO(m_nDensity), 8, SPROP_UNSIGNED ), - SendPropInt(SENDINFO(m_nCurrent), 16 ), -END_SEND_TABLE() - - -// Singleton to fire TEFizz objects -static CTEFizz g_TEFizz( "Fizz" ); - -void TE_Fizz( IRecipientFilter& filter, float delay, - const CBaseEntity *entity, int modelindex, int density, int current ) -{ - Assert( entity ); - - g_TEFizz.m_nEntity = ENTINDEX( (edict_t *)entity->edict() ); - g_TEFizz.m_nModelIndex = modelindex; - g_TEFizz.m_nDensity = density; - g_TEFizz.m_nCurrent = current; - - // Send it over the wire - g_TEFizz.Create( filter, delay ); -} \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $Workfile: $ +// $Date: $ +// +//----------------------------------------------------------------------------- +// $Log: $ +// +// $NoKeywords: $ +//=============================================================================// +#include "cbase.h" +#include "basetempentity.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +//----------------------------------------------------------------------------- +// Purpose: Dispatches Fizz tempentity +//----------------------------------------------------------------------------- +class CTEFizz : public CBaseTempEntity +{ +public: + DECLARE_CLASS( CTEFizz, CBaseTempEntity ); + + CTEFizz( const char *name ); + virtual ~CTEFizz( void ); + + virtual void Test( const Vector& current_origin, const QAngle& current_angles ); + + virtual void Precache( void ); + + DECLARE_SERVERCLASS(); + +public: + CNetworkVar( int, m_nEntity ); + CNetworkVar( int, m_nModelIndex ); + CNetworkVar( int, m_nDensity ); + CNetworkVar( int, m_nCurrent ); +}; + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *name - +//----------------------------------------------------------------------------- +CTEFizz::CTEFizz( const char *name ) : + CBaseTempEntity( name ) +{ + m_nEntity = 0; + m_nModelIndex = 0; + m_nDensity = 0; + m_nCurrent = 0; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CTEFizz::~CTEFizz( void ) +{ +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *current_origin - +// *current_angles - +//----------------------------------------------------------------------------- +void CTEFizz::Test( const Vector& current_origin, const QAngle& current_angles ) +{ + // Fill in data + m_nModelIndex = CBaseEntity::PrecacheModel( "sprites/bubble.vmt" );; + m_nDensity = 200; + m_nEntity = 1; + m_nCurrent = 100; + + CBroadcastRecipientFilter filter; + Create( filter, 0.0 ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTEFizz::Precache( void ) +{ + CBaseEntity::PrecacheModel( "sprites/bubble.vmt" ); +} + + +IMPLEMENT_SERVERCLASS_ST(CTEFizz, DT_TEFizz) + SendPropInt( SENDINFO(m_nEntity), MAX_EDICT_BITS, SPROP_UNSIGNED ), + SendPropModelIndex( SENDINFO(m_nModelIndex) ), + SendPropInt( SENDINFO(m_nDensity), 8, SPROP_UNSIGNED ), + SendPropInt(SENDINFO(m_nCurrent), 16 ), +END_SEND_TABLE() + + +// Singleton to fire TEFizz objects +static CTEFizz g_TEFizz( "Fizz" ); + +void TE_Fizz( IRecipientFilter& filter, float delay, + const CBaseEntity *entity, int modelindex, int density, int current ) +{ + Assert( entity ); + + g_TEFizz.m_nEntity = ENTINDEX( (edict_t *)entity->edict() ); + g_TEFizz.m_nModelIndex = modelindex; + g_TEFizz.m_nDensity = density; + g_TEFizz.m_nCurrent = current; + + // Send it over the wire + g_TEFizz.Create( filter, delay ); +} diff --git a/dlls/te_footprintdecal.cpp b/dlls/te_footprintdecal.cpp index 6f463ae4..c625ab25 100644 --- a/dlls/te_footprintdecal.cpp +++ b/dlls/te_footprintdecal.cpp @@ -1,91 +1,91 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $Workfile: $ -// $Date: $ -// -//----------------------------------------------------------------------------- -// $Log: $ -// -// $NoKeywords: $ -//=============================================================================// -#include "cbase.h" -#include "basetempentity.h" - -// memdbgon must be the last include file in a .cpp file!!! -#include "tier0/memdbgon.h" - -//----------------------------------------------------------------------------- -// Purpose: Dispatches footprint decal tempentity -//----------------------------------------------------------------------------- - -#define FOOTPRINT_DECAY_TIME 3.0f - -class CTEFootprintDecal : public CBaseTempEntity -{ -public: - DECLARE_CLASS( CTEFootprintDecal, CBaseTempEntity ); - - CTEFootprintDecal( const char *name ); - virtual ~CTEFootprintDecal( void ); - - DECLARE_SERVERCLASS(); - -public: - CNetworkVector( m_vecOrigin ); - CNetworkVector( m_vecDirection ); - CNetworkVar( int, m_nEntity ); - CNetworkVar( int, m_nIndex ); - CNetworkVar( unsigned char, m_chMaterialType ); -}; - -IMPLEMENT_SERVERCLASS_ST(CTEFootprintDecal, DT_TEFootprintDecal) - SendPropVector( SENDINFO(m_vecOrigin), -1, SPROP_COORD), - SendPropVector( SENDINFO(m_vecDirection), -1, SPROP_COORD), - SendPropInt( SENDINFO(m_nEntity), 11, SPROP_UNSIGNED ), - SendPropInt( SENDINFO(m_nIndex), 8, SPROP_UNSIGNED ), - SendPropInt( SENDINFO(m_chMaterialType), 8, SPROP_UNSIGNED ), -END_SEND_TABLE() - - -// Singleton to fire TEFootprintDecal objects -static CTEFootprintDecal g_TEFootprintDecal( "Footprint Decal" ); - -//----------------------------------------------------------------------------- -// constructor, destructor -//----------------------------------------------------------------------------- - -CTEFootprintDecal::CTEFootprintDecal( const char *name ) : - CBaseTempEntity( name ) -{ - m_vecOrigin.Init(); - m_nEntity = 0; - m_nIndex = 0; - m_chMaterialType = 'C'; -} - -CTEFootprintDecal::~CTEFootprintDecal( void ) -{ -} - -//----------------------------------------------------------------------------- -// places a footprint decal -//----------------------------------------------------------------------------- - -void TE_FootprintDecal( IRecipientFilter& filter, float delay, - const Vector *origin, const Vector *right, int entity, int index, - unsigned char materialType ) -{ - Assert( origin ); - g_TEFootprintDecal.m_vecOrigin = *origin; - g_TEFootprintDecal.m_vecDirection = *right; - g_TEFootprintDecal.m_nEntity = entity; - g_TEFootprintDecal.m_nIndex = index; - g_TEFootprintDecal.m_chMaterialType = materialType; - - VectorNormalize(g_TEFootprintDecal.m_vecDirection.GetForModify()); - - // Send it over the wire - g_TEFootprintDecal.Create( filter, delay ); -} \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $Workfile: $ +// $Date: $ +// +//----------------------------------------------------------------------------- +// $Log: $ +// +// $NoKeywords: $ +//=============================================================================// +#include "cbase.h" +#include "basetempentity.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +//----------------------------------------------------------------------------- +// Purpose: Dispatches footprint decal tempentity +//----------------------------------------------------------------------------- + +#define FOOTPRINT_DECAY_TIME 3.0f + +class CTEFootprintDecal : public CBaseTempEntity +{ +public: + DECLARE_CLASS( CTEFootprintDecal, CBaseTempEntity ); + + CTEFootprintDecal( const char *name ); + virtual ~CTEFootprintDecal( void ); + + DECLARE_SERVERCLASS(); + +public: + CNetworkVector( m_vecOrigin ); + CNetworkVector( m_vecDirection ); + CNetworkVar( int, m_nEntity ); + CNetworkVar( int, m_nIndex ); + CNetworkVar( unsigned char, m_chMaterialType ); +}; + +IMPLEMENT_SERVERCLASS_ST(CTEFootprintDecal, DT_TEFootprintDecal) + SendPropVector( SENDINFO(m_vecOrigin), -1, SPROP_COORD), + SendPropVector( SENDINFO(m_vecDirection), -1, SPROP_COORD), + SendPropInt( SENDINFO(m_nEntity), 11, SPROP_UNSIGNED ), + SendPropInt( SENDINFO(m_nIndex), 8, SPROP_UNSIGNED ), + SendPropInt( SENDINFO(m_chMaterialType), 8, SPROP_UNSIGNED ), +END_SEND_TABLE() + + +// Singleton to fire TEFootprintDecal objects +static CTEFootprintDecal g_TEFootprintDecal( "Footprint Decal" ); + +//----------------------------------------------------------------------------- +// constructor, destructor +//----------------------------------------------------------------------------- + +CTEFootprintDecal::CTEFootprintDecal( const char *name ) : + CBaseTempEntity( name ) +{ + m_vecOrigin.Init(); + m_nEntity = 0; + m_nIndex = 0; + m_chMaterialType = 'C'; +} + +CTEFootprintDecal::~CTEFootprintDecal( void ) +{ +} + +//----------------------------------------------------------------------------- +// places a footprint decal +//----------------------------------------------------------------------------- + +void TE_FootprintDecal( IRecipientFilter& filter, float delay, + const Vector *origin, const Vector *right, int entity, int index, + unsigned char materialType ) +{ + Assert( origin ); + g_TEFootprintDecal.m_vecOrigin = *origin; + g_TEFootprintDecal.m_vecDirection = *right; + g_TEFootprintDecal.m_nEntity = entity; + g_TEFootprintDecal.m_nIndex = index; + g_TEFootprintDecal.m_chMaterialType = materialType; + + VectorNormalize(g_TEFootprintDecal.m_vecDirection.GetForModify()); + + // Send it over the wire + g_TEFootprintDecal.Create( filter, delay ); +} diff --git a/dlls/te_glowsprite.cpp b/dlls/te_glowsprite.cpp index e604b270..541b437d 100644 --- a/dlls/te_glowsprite.cpp +++ b/dlls/te_glowsprite.cpp @@ -1,130 +1,130 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $Workfile: $ -// $Date: $ -// -//----------------------------------------------------------------------------- -// $Log: $ -// -// $NoKeywords: $ -//=============================================================================// -#include "cbase.h" -#include "basetempentity.h" - -// memdbgon must be the last include file in a .cpp file!!! -#include "tier0/memdbgon.h" - -extern short g_sModelIndexSmoke; // (in combatweapon.cpp) holds the index for the smoke cloud - -//----------------------------------------------------------------------------- -// Purpose: Dispatches Sprite tempentity -//----------------------------------------------------------------------------- -class CTEGlowSprite : public CBaseTempEntity -{ -public: - DECLARE_CLASS( CTEGlowSprite, CBaseTempEntity ); - - CTEGlowSprite( const char *name ); - virtual ~CTEGlowSprite( void ); - - virtual void Test( const Vector& current_origin, const QAngle& current_angles ); - - DECLARE_SERVERCLASS(); - -public: - CNetworkVector( m_vecOrigin ); - CNetworkVar( int, m_nModelIndex ); - CNetworkVar( float, m_fScale ); - CNetworkVar( float, m_fLife ); - CNetworkVar( int, m_nBrightness ); -}; - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *name - -//----------------------------------------------------------------------------- -CTEGlowSprite::CTEGlowSprite( const char *name ) : - CBaseTempEntity( name ) -{ - m_vecOrigin.Init(); - m_nModelIndex = 0; - m_fScale = 0; - m_fLife = 0; - m_nBrightness = 0; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -CTEGlowSprite::~CTEGlowSprite( void ) -{ -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *current_origin - -// *current_angles - -//----------------------------------------------------------------------------- -void CTEGlowSprite::Test( const Vector& current_origin, const QAngle& current_angles ) -{ - // Fill in data - m_nModelIndex = g_sModelIndexSmoke; - m_fScale = 0.8; - m_nBrightness = 200; - m_fLife = 2.0; - m_vecOrigin = current_origin; - - Vector forward, right; - - m_vecOrigin.GetForModify()[2] += 24; - - AngleVectors( current_angles, &forward, &right, NULL ); - forward[2] = 0.0; - VectorNormalize( forward ); - - VectorMA( m_vecOrigin, 50.0, forward, m_vecOrigin.GetForModify() ); - VectorMA( m_vecOrigin, -25.0, right, m_vecOrigin.GetForModify() ); - - CBroadcastRecipientFilter filter; - Create( filter, 0.0 ); -} - - -IMPLEMENT_SERVERCLASS_ST(CTEGlowSprite, DT_TEGlowSprite) - SendPropVector( SENDINFO(m_vecOrigin), -1, SPROP_COORD), - SendPropModelIndex( SENDINFO(m_nModelIndex) ), - SendPropFloat( SENDINFO(m_fScale ), 8, SPROP_ROUNDDOWN, 0.0, 25.6 ), - SendPropFloat( SENDINFO(m_fLife ), 8, SPROP_ROUNDDOWN, 0.0, 25.6 ), - SendPropInt( SENDINFO(m_nBrightness), 8, SPROP_UNSIGNED ), -END_SEND_TABLE() - - -// Singleton to fire TEGlowSprite objects -static CTEGlowSprite g_TEGlowSprite( "GlowSprite" ); - -//----------------------------------------------------------------------------- -// Purpose: -// Input : msg_dest - -// delay - -// *origin - -// *recipient - -// *pos - -// modelindex - -// life - -// size - -// brightness - -//----------------------------------------------------------------------------- -void TE_GlowSprite( IRecipientFilter& filter, float delay, - const Vector* pos, int modelindex, float life, float size, int brightness ) -{ - g_TEGlowSprite.m_vecOrigin = *pos; - g_TEGlowSprite.m_nModelIndex = modelindex; - g_TEGlowSprite.m_fLife = life; - g_TEGlowSprite.m_fScale = size; - g_TEGlowSprite.m_nBrightness = brightness; - - // Send it over the wire - g_TEGlowSprite.Create( filter, delay ); -} \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $Workfile: $ +// $Date: $ +// +//----------------------------------------------------------------------------- +// $Log: $ +// +// $NoKeywords: $ +//=============================================================================// +#include "cbase.h" +#include "basetempentity.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +extern short g_sModelIndexSmoke; // (in combatweapon.cpp) holds the index for the smoke cloud + +//----------------------------------------------------------------------------- +// Purpose: Dispatches Sprite tempentity +//----------------------------------------------------------------------------- +class CTEGlowSprite : public CBaseTempEntity +{ +public: + DECLARE_CLASS( CTEGlowSprite, CBaseTempEntity ); + + CTEGlowSprite( const char *name ); + virtual ~CTEGlowSprite( void ); + + virtual void Test( const Vector& current_origin, const QAngle& current_angles ); + + DECLARE_SERVERCLASS(); + +public: + CNetworkVector( m_vecOrigin ); + CNetworkVar( int, m_nModelIndex ); + CNetworkVar( float, m_fScale ); + CNetworkVar( float, m_fLife ); + CNetworkVar( int, m_nBrightness ); +}; + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *name - +//----------------------------------------------------------------------------- +CTEGlowSprite::CTEGlowSprite( const char *name ) : + CBaseTempEntity( name ) +{ + m_vecOrigin.Init(); + m_nModelIndex = 0; + m_fScale = 0; + m_fLife = 0; + m_nBrightness = 0; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CTEGlowSprite::~CTEGlowSprite( void ) +{ +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *current_origin - +// *current_angles - +//----------------------------------------------------------------------------- +void CTEGlowSprite::Test( const Vector& current_origin, const QAngle& current_angles ) +{ + // Fill in data + m_nModelIndex = g_sModelIndexSmoke; + m_fScale = 0.8; + m_nBrightness = 200; + m_fLife = 2.0; + m_vecOrigin = current_origin; + + Vector forward, right; + + m_vecOrigin.GetForModify()[2] += 24; + + AngleVectors( current_angles, &forward, &right, NULL ); + forward[2] = 0.0; + VectorNormalize( forward ); + + VectorMA( m_vecOrigin, 50.0, forward, m_vecOrigin.GetForModify() ); + VectorMA( m_vecOrigin, -25.0, right, m_vecOrigin.GetForModify() ); + + CBroadcastRecipientFilter filter; + Create( filter, 0.0 ); +} + + +IMPLEMENT_SERVERCLASS_ST(CTEGlowSprite, DT_TEGlowSprite) + SendPropVector( SENDINFO(m_vecOrigin), -1, SPROP_COORD), + SendPropModelIndex( SENDINFO(m_nModelIndex) ), + SendPropFloat( SENDINFO(m_fScale ), 8, SPROP_ROUNDDOWN, 0.0, 25.6 ), + SendPropFloat( SENDINFO(m_fLife ), 8, SPROP_ROUNDDOWN, 0.0, 25.6 ), + SendPropInt( SENDINFO(m_nBrightness), 8, SPROP_UNSIGNED ), +END_SEND_TABLE() + + +// Singleton to fire TEGlowSprite objects +static CTEGlowSprite g_TEGlowSprite( "GlowSprite" ); + +//----------------------------------------------------------------------------- +// Purpose: +// Input : msg_dest - +// delay - +// *origin - +// *recipient - +// *pos - +// modelindex - +// life - +// size - +// brightness - +//----------------------------------------------------------------------------- +void TE_GlowSprite( IRecipientFilter& filter, float delay, + const Vector* pos, int modelindex, float life, float size, int brightness ) +{ + g_TEGlowSprite.m_vecOrigin = *pos; + g_TEGlowSprite.m_nModelIndex = modelindex; + g_TEGlowSprite.m_fLife = life; + g_TEGlowSprite.m_fScale = size; + g_TEGlowSprite.m_nBrightness = brightness; + + // Send it over the wire + g_TEGlowSprite.Create( filter, delay ); +} diff --git a/dlls/te_impact.cpp b/dlls/te_impact.cpp index eb34416e..4d1e15a0 100644 --- a/dlls/te_impact.cpp +++ b/dlls/te_impact.cpp @@ -1,97 +1,97 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: Send generic impact messages to the client for visualization -// -// $Workfile: $ -// $Date: $ -// -//----------------------------------------------------------------------------- -// $Log: $ -// -// $NoKeywords: $ -//=============================================================================// -#include "cbase.h" -#include "basetempentity.h" - -// memdbgon must be the last include file in a .cpp file!!! -#include "tier0/memdbgon.h" - -//----------------------------------------------------------------------------- -// Purpose: Dispatches Gunshot decal tempentity -//----------------------------------------------------------------------------- -class CTEImpact : public CBaseTempEntity -{ -public: - DECLARE_CLASS( CTEImpact, CBaseTempEntity ); - - DECLARE_SERVERCLASS(); - - CTEImpact( const char *name ); - virtual ~CTEImpact(); - - void Precache( void ); - void Test( const Vector& current_origin, const Vector& current_normal ); - -public: - - CNetworkVector( m_vecOrigin ); - CNetworkVector( m_vecNormal ); //NOTENOTE: In a multi-play setup we'll probably want non-oriented effects for bandwidth - CNetworkVar( int, m_iType ); -}; - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *name - -// Output : -//----------------------------------------------------------------------------- -CTEImpact::CTEImpact( const char *name ) : CBaseTempEntity( name ) -{ - m_vecOrigin.Init(); - m_vecNormal.Init(); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -CTEImpact::~CTEImpact( void ) -{ -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CTEImpact::Precache( void ) -{ -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *current_origin - -// *current_angles - -//----------------------------------------------------------------------------- -void CTEImpact::Test( const Vector& current_origin, const Vector& current_normal ) -{ -} - - -//Server class implementation -IMPLEMENT_SERVERCLASS_ST( CTEImpact, DT_TEImpact) - SendPropVector( SENDINFO( m_vecOrigin ), -1, SPROP_COORD ), - SendPropVector( SENDINFO( m_vecNormal ), -1, SPROP_COORD ), - SendPropInt( SENDINFO( m_iType ), 32, SPROP_UNSIGNED ), -END_SEND_TABLE() - -// Singleton to fire TEImpact objects -static CTEImpact g_TEImpact( "Impact" ); - -//----------------------------------------------------------------------------- -// Purpose: -// Input : msg_dest - -// delay - -// *origin - -// *recipient - -//----------------------------------------------------------------------------- -void TE_Impact( IRecipientFilter& filter, float delay ) -{ - g_TEImpact.Create( filter, delay ); -} \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: Send generic impact messages to the client for visualization +// +// $Workfile: $ +// $Date: $ +// +//----------------------------------------------------------------------------- +// $Log: $ +// +// $NoKeywords: $ +//=============================================================================// +#include "cbase.h" +#include "basetempentity.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +//----------------------------------------------------------------------------- +// Purpose: Dispatches Gunshot decal tempentity +//----------------------------------------------------------------------------- +class CTEImpact : public CBaseTempEntity +{ +public: + DECLARE_CLASS( CTEImpact, CBaseTempEntity ); + + DECLARE_SERVERCLASS(); + + CTEImpact( const char *name ); + virtual ~CTEImpact(); + + void Precache( void ); + void Test( const Vector& current_origin, const Vector& current_normal ); + +public: + + CNetworkVector( m_vecOrigin ); + CNetworkVector( m_vecNormal ); //NOTENOTE: In a multi-play setup we'll probably want non-oriented effects for bandwidth + CNetworkVar( int, m_iType ); +}; + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *name - +// Output : +//----------------------------------------------------------------------------- +CTEImpact::CTEImpact( const char *name ) : CBaseTempEntity( name ) +{ + m_vecOrigin.Init(); + m_vecNormal.Init(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CTEImpact::~CTEImpact( void ) +{ +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTEImpact::Precache( void ) +{ +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *current_origin - +// *current_angles - +//----------------------------------------------------------------------------- +void CTEImpact::Test( const Vector& current_origin, const Vector& current_normal ) +{ +} + + +//Server class implementation +IMPLEMENT_SERVERCLASS_ST( CTEImpact, DT_TEImpact) + SendPropVector( SENDINFO( m_vecOrigin ), -1, SPROP_COORD ), + SendPropVector( SENDINFO( m_vecNormal ), -1, SPROP_COORD ), + SendPropInt( SENDINFO( m_iType ), 32, SPROP_UNSIGNED ), +END_SEND_TABLE() + +// Singleton to fire TEImpact objects +static CTEImpact g_TEImpact( "Impact" ); + +//----------------------------------------------------------------------------- +// Purpose: +// Input : msg_dest - +// delay - +// *origin - +// *recipient - +//----------------------------------------------------------------------------- +void TE_Impact( IRecipientFilter& filter, float delay ) +{ + g_TEImpact.Create( filter, delay ); +} diff --git a/dlls/te_killplayerattachments.cpp b/dlls/te_killplayerattachments.cpp index c180b4f1..5cd7a8e9 100644 --- a/dlls/te_killplayerattachments.cpp +++ b/dlls/te_killplayerattachments.cpp @@ -1,92 +1,92 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $Workfile: $ -// $Date: $ -// -//----------------------------------------------------------------------------- -// $Log: $ -// -// $NoKeywords: $ -//=============================================================================// -#include "cbase.h" -#include "basetempentity.h" - -// memdbgon must be the last include file in a .cpp file!!! -#include "tier0/memdbgon.h" - -//----------------------------------------------------------------------------- -// Purpose: Dispatches blood stream tempentity -//----------------------------------------------------------------------------- -class CTEKillPlayerAttachments : public CBaseTempEntity -{ -public: - DECLARE_CLASS( CTEKillPlayerAttachments, CBaseTempEntity ); - - CTEKillPlayerAttachments( const char *name ); - virtual ~CTEKillPlayerAttachments( void ); - - virtual void Test( const Vector& current_origin, const QAngle& current_angles ); - - DECLARE_SERVERCLASS(); - -public: - CNetworkVar( int, m_nPlayer ); -}; - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *name - -//----------------------------------------------------------------------------- -CTEKillPlayerAttachments::CTEKillPlayerAttachments( const char *name ) : - CBaseTempEntity( name ) -{ - m_nPlayer = 0; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -CTEKillPlayerAttachments::~CTEKillPlayerAttachments( void ) -{ -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *current_origin - -// *current_angles - -//----------------------------------------------------------------------------- -void CTEKillPlayerAttachments::Test( const Vector& current_origin, const QAngle& current_angles ) -{ - m_nPlayer = 1; - - CBroadcastRecipientFilter filter; - Create( filter, 0.0 ); -} - - -IMPLEMENT_SERVERCLASS_ST(CTEKillPlayerAttachments, DT_TEKillPlayerAttachments) - SendPropInt( SENDINFO(m_nPlayer), 5, SPROP_UNSIGNED ), -END_SEND_TABLE() - - -// Singleton to fire TEKillPlayerAttachments objects -static CTEKillPlayerAttachments g_TEKillPlayerAttachments( "KillPlayerAttachments" ); - -//----------------------------------------------------------------------------- -// Purpose: -// Input : msg_dest - -// delay - -// *origin - -// *recipient - -// player - -//----------------------------------------------------------------------------- -void TE_KillPlayerAttachments( IRecipientFilter& filter, float delay, - int player ) -{ - g_TEKillPlayerAttachments.m_nPlayer = player; - - // Send it over the wire - g_TEKillPlayerAttachments.Create( filter, delay ); -} \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $Workfile: $ +// $Date: $ +// +//----------------------------------------------------------------------------- +// $Log: $ +// +// $NoKeywords: $ +//=============================================================================// +#include "cbase.h" +#include "basetempentity.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +//----------------------------------------------------------------------------- +// Purpose: Dispatches blood stream tempentity +//----------------------------------------------------------------------------- +class CTEKillPlayerAttachments : public CBaseTempEntity +{ +public: + DECLARE_CLASS( CTEKillPlayerAttachments, CBaseTempEntity ); + + CTEKillPlayerAttachments( const char *name ); + virtual ~CTEKillPlayerAttachments( void ); + + virtual void Test( const Vector& current_origin, const QAngle& current_angles ); + + DECLARE_SERVERCLASS(); + +public: + CNetworkVar( int, m_nPlayer ); +}; + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *name - +//----------------------------------------------------------------------------- +CTEKillPlayerAttachments::CTEKillPlayerAttachments( const char *name ) : + CBaseTempEntity( name ) +{ + m_nPlayer = 0; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CTEKillPlayerAttachments::~CTEKillPlayerAttachments( void ) +{ +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *current_origin - +// *current_angles - +//----------------------------------------------------------------------------- +void CTEKillPlayerAttachments::Test( const Vector& current_origin, const QAngle& current_angles ) +{ + m_nPlayer = 1; + + CBroadcastRecipientFilter filter; + Create( filter, 0.0 ); +} + + +IMPLEMENT_SERVERCLASS_ST(CTEKillPlayerAttachments, DT_TEKillPlayerAttachments) + SendPropInt( SENDINFO(m_nPlayer), 5, SPROP_UNSIGNED ), +END_SEND_TABLE() + + +// Singleton to fire TEKillPlayerAttachments objects +static CTEKillPlayerAttachments g_TEKillPlayerAttachments( "KillPlayerAttachments" ); + +//----------------------------------------------------------------------------- +// Purpose: +// Input : msg_dest - +// delay - +// *origin - +// *recipient - +// player - +//----------------------------------------------------------------------------- +void TE_KillPlayerAttachments( IRecipientFilter& filter, float delay, + int player ) +{ + g_TEKillPlayerAttachments.m_nPlayer = player; + + // Send it over the wire + g_TEKillPlayerAttachments.Create( filter, delay ); +} diff --git a/dlls/te_largefunnel.cpp b/dlls/te_largefunnel.cpp index c0d5427a..dbb0abab 100644 --- a/dlls/te_largefunnel.cpp +++ b/dlls/te_largefunnel.cpp @@ -1,103 +1,103 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $Workfile: $ -// $Date: $ -// -//----------------------------------------------------------------------------- -// $Log: $ -// -// $NoKeywords: $ -//=============================================================================// -#include "cbase.h" -#include "te_particlesystem.h" - -// memdbgon must be the last include file in a .cpp file!!! -#include "tier0/memdbgon.h" - -extern short g_sModelIndexSmoke; // (in combatweapon.cpp) holds the index for the smoke cloud - -//----------------------------------------------------------------------------- -// Purpose: Dispatches smoke tempentity -//----------------------------------------------------------------------------- -class CTELargeFunnel : public CTEParticleSystem -{ -public: - DECLARE_CLASS( CTELargeFunnel, CTEParticleSystem ); - DECLARE_SERVERCLASS(); - - CTELargeFunnel( const char *name ); - virtual ~CTELargeFunnel( void ); - - virtual void Test( const Vector& current_origin, const QAngle& current_angles ); - -public: - CNetworkVar( int, m_nModelIndex ); - CNetworkVar( int, m_nReversed ); -}; - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *name - -//----------------------------------------------------------------------------- -CTELargeFunnel::CTELargeFunnel( const char *name ) : - BaseClass( name ) -{ - m_nModelIndex = 0; - m_nReversed = 0; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -CTELargeFunnel::~CTELargeFunnel( void ) -{ -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *current_origin - -// *current_angles - -//----------------------------------------------------------------------------- -void CTELargeFunnel::Test( const Vector& current_origin, const QAngle& current_angles ) -{ - // Fill in data - m_nModelIndex = g_sModelIndexSmoke; - m_nReversed = 0; - m_vecOrigin = current_origin; - - Vector forward, right; - - m_vecOrigin.GetForModify()[2] += 24; - - AngleVectors( current_angles, &forward, &right, NULL ); - forward[2] = 0.0; - VectorNormalize( forward ); - - VectorMA( m_vecOrigin.Get(), 50.0, forward, m_vecOrigin.GetForModify() ); - VectorMA( m_vecOrigin.Get(), 25.0, right, m_vecOrigin.GetForModify() ); - - CBroadcastRecipientFilter filter; - Create( filter, 0.0 ); -} - -IMPLEMENT_SERVERCLASS_ST(CTELargeFunnel, DT_TELargeFunnel) - SendPropModelIndex( SENDINFO(m_nModelIndex) ), - SendPropInt( SENDINFO(m_nReversed), 2, SPROP_UNSIGNED ), -END_SEND_TABLE() - - -// Singleton to fire TELargeFunnel objects -static CTELargeFunnel g_TELargeFunnel( "Large Funnel" ); - -void TE_LargeFunnel( IRecipientFilter& filter, float delay, - const Vector* pos, int modelindex, int reversed ) -{ - g_TELargeFunnel.m_vecOrigin = *pos; - g_TELargeFunnel.m_nModelIndex = modelindex; - g_TELargeFunnel.m_nReversed = reversed; - - // Send it over the wire - g_TELargeFunnel.Create( filter, delay ); -} \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $Workfile: $ +// $Date: $ +// +//----------------------------------------------------------------------------- +// $Log: $ +// +// $NoKeywords: $ +//=============================================================================// +#include "cbase.h" +#include "te_particlesystem.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +extern short g_sModelIndexSmoke; // (in combatweapon.cpp) holds the index for the smoke cloud + +//----------------------------------------------------------------------------- +// Purpose: Dispatches smoke tempentity +//----------------------------------------------------------------------------- +class CTELargeFunnel : public CTEParticleSystem +{ +public: + DECLARE_CLASS( CTELargeFunnel, CTEParticleSystem ); + DECLARE_SERVERCLASS(); + + CTELargeFunnel( const char *name ); + virtual ~CTELargeFunnel( void ); + + virtual void Test( const Vector& current_origin, const QAngle& current_angles ); + +public: + CNetworkVar( int, m_nModelIndex ); + CNetworkVar( int, m_nReversed ); +}; + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *name - +//----------------------------------------------------------------------------- +CTELargeFunnel::CTELargeFunnel( const char *name ) : + BaseClass( name ) +{ + m_nModelIndex = 0; + m_nReversed = 0; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CTELargeFunnel::~CTELargeFunnel( void ) +{ +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *current_origin - +// *current_angles - +//----------------------------------------------------------------------------- +void CTELargeFunnel::Test( const Vector& current_origin, const QAngle& current_angles ) +{ + // Fill in data + m_nModelIndex = g_sModelIndexSmoke; + m_nReversed = 0; + m_vecOrigin = current_origin; + + Vector forward, right; + + m_vecOrigin.GetForModify()[2] += 24; + + AngleVectors( current_angles, &forward, &right, NULL ); + forward[2] = 0.0; + VectorNormalize( forward ); + + VectorMA( m_vecOrigin.Get(), 50.0, forward, m_vecOrigin.GetForModify() ); + VectorMA( m_vecOrigin.Get(), 25.0, right, m_vecOrigin.GetForModify() ); + + CBroadcastRecipientFilter filter; + Create( filter, 0.0 ); +} + +IMPLEMENT_SERVERCLASS_ST(CTELargeFunnel, DT_TELargeFunnel) + SendPropModelIndex( SENDINFO(m_nModelIndex) ), + SendPropInt( SENDINFO(m_nReversed), 2, SPROP_UNSIGNED ), +END_SEND_TABLE() + + +// Singleton to fire TELargeFunnel objects +static CTELargeFunnel g_TELargeFunnel( "Large Funnel" ); + +void TE_LargeFunnel( IRecipientFilter& filter, float delay, + const Vector* pos, int modelindex, int reversed ) +{ + g_TELargeFunnel.m_vecOrigin = *pos; + g_TELargeFunnel.m_nModelIndex = modelindex; + g_TELargeFunnel.m_nReversed = reversed; + + // Send it over the wire + g_TELargeFunnel.Create( filter, delay ); +} diff --git a/dlls/te_muzzleflash.cpp b/dlls/te_muzzleflash.cpp index 9cdda73f..d80fdf7c 100644 --- a/dlls/te_muzzleflash.cpp +++ b/dlls/te_muzzleflash.cpp @@ -1,99 +1,99 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: Create a muzzle flash temp ent -// -// $NoKeywords: $ -//=============================================================================// - -#include "cbase.h" -#include "basetempentity.h" - -// memdbgon must be the last include file in a .cpp file!!! -#include "tier0/memdbgon.h" - -//----------------------------------------------------------------------------- -// Purpose: Dispatches user tracer stream tempentity -//----------------------------------------------------------------------------- -class CTEMuzzleFlash : public CBaseTempEntity -{ -public: - DECLARE_CLASS( CTEMuzzleFlash, CBaseTempEntity ); - - DECLARE_SERVERCLASS(); - - CTEMuzzleFlash( const char *name ); - virtual ~CTEMuzzleFlash( void ); - - virtual void Test( const Vector& current_origin, const QAngle& current_angles ); - -public: - - CNetworkVector( m_vecOrigin ); - CNetworkQAngle( m_vecAngles ); - CNetworkVar( float, m_flScale ); - CNetworkVar( int, m_nType ); -}; - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *name - -//----------------------------------------------------------------------------- -CTEMuzzleFlash::CTEMuzzleFlash( const char *name ) : - CBaseTempEntity( name ) -{ - m_vecOrigin.Init(); - m_vecAngles.Init(); - - m_flScale = 1.0f; - m_nType = 0; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -CTEMuzzleFlash::~CTEMuzzleFlash( void ) -{ -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *current_origin - -// *current_angles - -//----------------------------------------------------------------------------- -void CTEMuzzleFlash::Test( const Vector& current_origin, const QAngle& current_angles ) -{ -} - - -IMPLEMENT_SERVERCLASS_ST( CTEMuzzleFlash, DT_TEMuzzleFlash ) - SendPropVector( SENDINFO(m_vecOrigin), -1, SPROP_COORD ), - SendPropVector( SENDINFO(m_vecAngles), -1, SPROP_COORD ), - SendPropFloat( SENDINFO(m_flScale), -1, SPROP_NOSCALE ), - SendPropInt( SENDINFO(m_nType), 32, SPROP_UNSIGNED ), -END_SEND_TABLE() - -// Singleton to fire TEMuzzleFlash objects -static CTEMuzzleFlash g_TEMuzzleFlash( "MuzzleFlash" ); - -//----------------------------------------------------------------------------- -// Purpose: -// Input : msg_dest - -// delay - -// origin - -// *recipient - -// *origin - -// *dir - -// scale - -// type - -//----------------------------------------------------------------------------- -void TE_MuzzleFlash( IRecipientFilter& filter, float delay, - const Vector &start, const QAngle &angles, float scale, int type ) -{ - g_TEMuzzleFlash.m_vecOrigin = start; - g_TEMuzzleFlash.m_vecAngles = angles; - g_TEMuzzleFlash.m_flScale = scale; - g_TEMuzzleFlash.m_nType = type; - - // Send it over the wire - g_TEMuzzleFlash.Create( filter, delay ); -} \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: Create a muzzle flash temp ent +// +// $NoKeywords: $ +//=============================================================================// + +#include "cbase.h" +#include "basetempentity.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +//----------------------------------------------------------------------------- +// Purpose: Dispatches user tracer stream tempentity +//----------------------------------------------------------------------------- +class CTEMuzzleFlash : public CBaseTempEntity +{ +public: + DECLARE_CLASS( CTEMuzzleFlash, CBaseTempEntity ); + + DECLARE_SERVERCLASS(); + + CTEMuzzleFlash( const char *name ); + virtual ~CTEMuzzleFlash( void ); + + virtual void Test( const Vector& current_origin, const QAngle& current_angles ); + +public: + + CNetworkVector( m_vecOrigin ); + CNetworkQAngle( m_vecAngles ); + CNetworkVar( float, m_flScale ); + CNetworkVar( int, m_nType ); +}; + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *name - +//----------------------------------------------------------------------------- +CTEMuzzleFlash::CTEMuzzleFlash( const char *name ) : + CBaseTempEntity( name ) +{ + m_vecOrigin.Init(); + m_vecAngles.Init(); + + m_flScale = 1.0f; + m_nType = 0; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CTEMuzzleFlash::~CTEMuzzleFlash( void ) +{ +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *current_origin - +// *current_angles - +//----------------------------------------------------------------------------- +void CTEMuzzleFlash::Test( const Vector& current_origin, const QAngle& current_angles ) +{ +} + + +IMPLEMENT_SERVERCLASS_ST( CTEMuzzleFlash, DT_TEMuzzleFlash ) + SendPropVector( SENDINFO(m_vecOrigin), -1, SPROP_COORD ), + SendPropVector( SENDINFO(m_vecAngles), -1, SPROP_COORD ), + SendPropFloat( SENDINFO(m_flScale), -1, SPROP_NOSCALE ), + SendPropInt( SENDINFO(m_nType), 32, SPROP_UNSIGNED ), +END_SEND_TABLE() + +// Singleton to fire TEMuzzleFlash objects +static CTEMuzzleFlash g_TEMuzzleFlash( "MuzzleFlash" ); + +//----------------------------------------------------------------------------- +// Purpose: +// Input : msg_dest - +// delay - +// origin - +// *recipient - +// *origin - +// *dir - +// scale - +// type - +//----------------------------------------------------------------------------- +void TE_MuzzleFlash( IRecipientFilter& filter, float delay, + const Vector &start, const QAngle &angles, float scale, int type ) +{ + g_TEMuzzleFlash.m_vecOrigin = start; + g_TEMuzzleFlash.m_vecAngles = angles; + g_TEMuzzleFlash.m_flScale = scale; + g_TEMuzzleFlash.m_nType = type; + + // Send it over the wire + g_TEMuzzleFlash.Create( filter, delay ); +} diff --git a/dlls/te_particlesystem.cpp b/dlls/te_particlesystem.cpp index bd978d24..f3def596 100644 --- a/dlls/te_particlesystem.cpp +++ b/dlls/te_particlesystem.cpp @@ -18,7 +18,3 @@ IMPLEMENT_SERVERCLASS_ST(CTEParticleSystem, DT_TEParticleSystem) SendPropFloat( SENDINFO_VECTORELEM(m_vecOrigin, 1), -1, SPROP_COORD), SendPropFloat( SENDINFO_VECTORELEM(m_vecOrigin, 2), -1, SPROP_COORD), END_SEND_TABLE() - - - - diff --git a/dlls/te_physicsprop.cpp b/dlls/te_physicsprop.cpp index 3db27ac9..68a5d573 100644 --- a/dlls/te_physicsprop.cpp +++ b/dlls/te_physicsprop.cpp @@ -1,133 +1,133 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $Workfile: $ -// $Date: $ -// $NoKeywords: $ -//=============================================================================// -#include "cbase.h" -#include "basetempentity.h" - -// memdbgon must be the last include file in a .cpp file!!! -#include "tier0/memdbgon.h" - -//----------------------------------------------------------------------------- -// Purpose: create clientside physics prop, as breaks model if needed -//----------------------------------------------------------------------------- -class CTEPhysicsProp : public CBaseTempEntity -{ -public: - DECLARE_CLASS( CTEPhysicsProp, CBaseTempEntity ); - - CTEPhysicsProp( const char *name ); - virtual ~CTEPhysicsProp( void ); - - virtual void Test( const Vector& current_origin, const QAngle& current_angles ); - - virtual void Precache( void ); - - DECLARE_SERVERCLASS(); - -public: - CNetworkVector( m_vecOrigin ); - CNetworkQAngle( m_angRotation ); - CNetworkVector( m_vecVelocity ); - CNetworkVar( int, m_nModelIndex ); - CNetworkVar( int, m_nSkin ); - CNetworkVar( int, m_nFlags ); - CNetworkVar( int, m_nEffects ); -}; - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *name - -//----------------------------------------------------------------------------- -CTEPhysicsProp::CTEPhysicsProp( const char *name ) : - CBaseTempEntity( name ) -{ - m_vecOrigin.Init(); - m_angRotation.Init(); - m_vecVelocity.Init(); - m_nModelIndex = 0; - m_nSkin = 0; - m_nFlags = 0; - m_nEffects = 0; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -CTEPhysicsProp::~CTEPhysicsProp( void ) -{ -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CTEPhysicsProp::Precache( void ) -{ - CBaseEntity::PrecacheModel( "models/gibs/hgibs.mdl" ); -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *current_origin - -// *current_angles - -//----------------------------------------------------------------------------- -void CTEPhysicsProp::Test( const Vector& current_origin, const QAngle& current_angles ) -{ - // Fill in data - m_nModelIndex = CBaseEntity::PrecacheModel( "models/gibs/hgibs.mdl" ); - m_nSkin = 0; - m_vecOrigin = current_origin; - m_angRotation = current_angles; - - m_vecVelocity.Init( random->RandomFloat( -10, 10 ), random->RandomFloat( -10, 10 ), random->RandomFloat( 0, 20 ) ); - m_nFlags = 0; - m_nEffects = 0; - - Vector forward, right; - - m_vecOrigin += Vector( 0, 0, 24 ); - - AngleVectors( current_angles, &forward, &right, 0 ); - forward[2] = 0.0; - VectorNormalize( forward ); - - VectorMA( m_vecOrigin, 50.0, forward, m_vecOrigin.GetForModify() ); - VectorMA( m_vecOrigin, 25.0, right, m_vecOrigin.GetForModify() ); - - CBroadcastRecipientFilter filter; - Create( filter, 0.0 ); -} - -IMPLEMENT_SERVERCLASS_ST(CTEPhysicsProp, DT_TEPhysicsProp) - SendPropVector( SENDINFO(m_vecOrigin), -1, SPROP_COORD), - SendPropAngle( SENDINFO_VECTORELEM(m_angRotation, 0), 13 ), - SendPropAngle( SENDINFO_VECTORELEM(m_angRotation, 1), 13 ), - SendPropAngle( SENDINFO_VECTORELEM(m_angRotation, 2), 13 ), - SendPropVector( SENDINFO(m_vecVelocity), -1, SPROP_COORD), - SendPropModelIndex( SENDINFO(m_nModelIndex) ), - SendPropInt( SENDINFO(m_nSkin), ANIMATION_SKIN_BITS), - SendPropInt( SENDINFO(m_nFlags), 2, SPROP_UNSIGNED ), - SendPropInt( SENDINFO(m_nEffects), EF_MAX_BITS, SPROP_UNSIGNED), -END_SEND_TABLE() - -// Singleton to fire TEBreakModel objects -static CTEPhysicsProp s_TEPhysicsProp( "physicsprop" ); - -void TE_PhysicsProp( IRecipientFilter& filter, float delay, - int modelindex, int skin, const Vector& pos, const QAngle &angles, const Vector& vel, int flags, int effects ) -{ - s_TEPhysicsProp.m_vecOrigin = pos; - s_TEPhysicsProp.m_angRotation = angles; - s_TEPhysicsProp.m_vecVelocity = vel; - s_TEPhysicsProp.m_nModelIndex = modelindex; - s_TEPhysicsProp.m_nSkin = skin; - s_TEPhysicsProp.m_nFlags = flags; - s_TEPhysicsProp.m_nEffects = effects; - - // Send it over the wire - s_TEPhysicsProp.Create( filter, delay ); -} \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $Workfile: $ +// $Date: $ +// $NoKeywords: $ +//=============================================================================// +#include "cbase.h" +#include "basetempentity.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +//----------------------------------------------------------------------------- +// Purpose: create clientside physics prop, as breaks model if needed +//----------------------------------------------------------------------------- +class CTEPhysicsProp : public CBaseTempEntity +{ +public: + DECLARE_CLASS( CTEPhysicsProp, CBaseTempEntity ); + + CTEPhysicsProp( const char *name ); + virtual ~CTEPhysicsProp( void ); + + virtual void Test( const Vector& current_origin, const QAngle& current_angles ); + + virtual void Precache( void ); + + DECLARE_SERVERCLASS(); + +public: + CNetworkVector( m_vecOrigin ); + CNetworkQAngle( m_angRotation ); + CNetworkVector( m_vecVelocity ); + CNetworkVar( int, m_nModelIndex ); + CNetworkVar( int, m_nSkin ); + CNetworkVar( int, m_nFlags ); + CNetworkVar( int, m_nEffects ); +}; + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *name - +//----------------------------------------------------------------------------- +CTEPhysicsProp::CTEPhysicsProp( const char *name ) : + CBaseTempEntity( name ) +{ + m_vecOrigin.Init(); + m_angRotation.Init(); + m_vecVelocity.Init(); + m_nModelIndex = 0; + m_nSkin = 0; + m_nFlags = 0; + m_nEffects = 0; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CTEPhysicsProp::~CTEPhysicsProp( void ) +{ +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTEPhysicsProp::Precache( void ) +{ + CBaseEntity::PrecacheModel( "models/gibs/hgibs.mdl" ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *current_origin - +// *current_angles - +//----------------------------------------------------------------------------- +void CTEPhysicsProp::Test( const Vector& current_origin, const QAngle& current_angles ) +{ + // Fill in data + m_nModelIndex = CBaseEntity::PrecacheModel( "models/gibs/hgibs.mdl" ); + m_nSkin = 0; + m_vecOrigin = current_origin; + m_angRotation = current_angles; + + m_vecVelocity.Init( random->RandomFloat( -10, 10 ), random->RandomFloat( -10, 10 ), random->RandomFloat( 0, 20 ) ); + m_nFlags = 0; + m_nEffects = 0; + + Vector forward, right; + + m_vecOrigin += Vector( 0, 0, 24 ); + + AngleVectors( current_angles, &forward, &right, 0 ); + forward[2] = 0.0; + VectorNormalize( forward ); + + VectorMA( m_vecOrigin, 50.0, forward, m_vecOrigin.GetForModify() ); + VectorMA( m_vecOrigin, 25.0, right, m_vecOrigin.GetForModify() ); + + CBroadcastRecipientFilter filter; + Create( filter, 0.0 ); +} + +IMPLEMENT_SERVERCLASS_ST(CTEPhysicsProp, DT_TEPhysicsProp) + SendPropVector( SENDINFO(m_vecOrigin), -1, SPROP_COORD), + SendPropAngle( SENDINFO_VECTORELEM(m_angRotation, 0), 13 ), + SendPropAngle( SENDINFO_VECTORELEM(m_angRotation, 1), 13 ), + SendPropAngle( SENDINFO_VECTORELEM(m_angRotation, 2), 13 ), + SendPropVector( SENDINFO(m_vecVelocity), -1, SPROP_COORD), + SendPropModelIndex( SENDINFO(m_nModelIndex) ), + SendPropInt( SENDINFO(m_nSkin), ANIMATION_SKIN_BITS), + SendPropInt( SENDINFO(m_nFlags), 2, SPROP_UNSIGNED ), + SendPropInt( SENDINFO(m_nEffects), EF_MAX_BITS, SPROP_UNSIGNED), +END_SEND_TABLE() + +// Singleton to fire TEBreakModel objects +static CTEPhysicsProp s_TEPhysicsProp( "physicsprop" ); + +void TE_PhysicsProp( IRecipientFilter& filter, float delay, + int modelindex, int skin, const Vector& pos, const QAngle &angles, const Vector& vel, int flags, int effects ) +{ + s_TEPhysicsProp.m_vecOrigin = pos; + s_TEPhysicsProp.m_angRotation = angles; + s_TEPhysicsProp.m_vecVelocity = vel; + s_TEPhysicsProp.m_nModelIndex = modelindex; + s_TEPhysicsProp.m_nSkin = skin; + s_TEPhysicsProp.m_nFlags = flags; + s_TEPhysicsProp.m_nEffects = effects; + + // Send it over the wire + s_TEPhysicsProp.Create( filter, delay ); +} diff --git a/dlls/te_playerdecal.cpp b/dlls/te_playerdecal.cpp index bfa4b5dc..68480c4d 100644 --- a/dlls/te_playerdecal.cpp +++ b/dlls/te_playerdecal.cpp @@ -1,124 +1,124 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $Workfile: $ -// $Date: $ -// -//----------------------------------------------------------------------------- -// $Log: $ -// -// $NoKeywords: $ -//=============================================================================// -#include "cbase.h" -#include "basetempentity.h" - -// memdbgon must be the last include file in a .cpp file!!! -#include "tier0/memdbgon.h" - -//----------------------------------------------------------------------------- -// Purpose: Dispatches decal tempentity -//----------------------------------------------------------------------------- -class CTEPlayerDecal : public CBaseTempEntity -{ -public: - DECLARE_CLASS( CTEPlayerDecal, CBaseTempEntity ); - - CTEPlayerDecal( const char *name ); - virtual ~CTEPlayerDecal( void ); - - virtual void Test( const Vector& current_origin, const QAngle& current_angles ); - - DECLARE_SERVERCLASS(); - -public: - CNetworkVar( int, m_nPlayer ); - CNetworkVector( m_vecOrigin ); - CNetworkVar( int, m_nEntity ); -}; - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *name - -//----------------------------------------------------------------------------- -CTEPlayerDecal::CTEPlayerDecal( const char *name ) : - CBaseTempEntity( name ) -{ - m_nPlayer = 0; - m_vecOrigin.Init(); - m_nEntity = 0; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -CTEPlayerDecal::~CTEPlayerDecal( void ) -{ -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *current_origin - -// *current_angles - -//----------------------------------------------------------------------------- -void CTEPlayerDecal::Test( const Vector& current_origin, const QAngle& current_angles ) -{ - // Fill in data - m_nPlayer = 1; - m_nEntity = 0; - m_vecOrigin = current_origin; - - Vector vecEnd; - - Vector forward; - - m_vecOrigin.GetForModify()[2] += 24; - - AngleVectors( current_angles, &forward ); - forward[2] = 0.0; - VectorNormalize( forward ); - - VectorMA( m_vecOrigin, 50.0, forward, m_vecOrigin.GetForModify() ); - VectorMA( m_vecOrigin, 1024.0, forward, vecEnd ); - - trace_t tr; - - UTIL_TraceLine( m_vecOrigin, vecEnd, MASK_SOLID_BRUSHONLY, NULL, COLLISION_GROUP_NONE, &tr ); - - m_vecOrigin = tr.endpos; - - CBroadcastRecipientFilter filter; - Create( filter, 0.0 ); -} - -IMPLEMENT_SERVERCLASS_ST(CTEPlayerDecal, DT_TEPlayerDecal) - SendPropVector( SENDINFO(m_vecOrigin), -1, SPROP_COORD), - SendPropInt( SENDINFO(m_nEntity), MAX_EDICT_BITS, SPROP_UNSIGNED ), - SendPropInt( SENDINFO(m_nPlayer), Q_log2( MAX_PLAYERS ), SPROP_UNSIGNED ), -END_SEND_TABLE() - - -// Singleton to fire TEPlayerDecal objects -static CTEPlayerDecal g_TEPlayerDecal( "Player Decal" ); - -//----------------------------------------------------------------------------- -// Purpose: -// Input : msg_dest - -// delay - -// *origin - -// *recipient - -// *pos - -// player - -// entity - -// index - -//----------------------------------------------------------------------------- -void TE_PlayerDecal( IRecipientFilter& filter, float delay, - const Vector* pos, int player, int entity ) -{ - g_TEPlayerDecal.m_vecOrigin = *pos; - g_TEPlayerDecal.m_nPlayer = player; - g_TEPlayerDecal.m_nEntity = entity; - - // Send it over the wire - g_TEPlayerDecal.Create( filter, delay ); -} \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $Workfile: $ +// $Date: $ +// +//----------------------------------------------------------------------------- +// $Log: $ +// +// $NoKeywords: $ +//=============================================================================// +#include "cbase.h" +#include "basetempentity.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +//----------------------------------------------------------------------------- +// Purpose: Dispatches decal tempentity +//----------------------------------------------------------------------------- +class CTEPlayerDecal : public CBaseTempEntity +{ +public: + DECLARE_CLASS( CTEPlayerDecal, CBaseTempEntity ); + + CTEPlayerDecal( const char *name ); + virtual ~CTEPlayerDecal( void ); + + virtual void Test( const Vector& current_origin, const QAngle& current_angles ); + + DECLARE_SERVERCLASS(); + +public: + CNetworkVar( int, m_nPlayer ); + CNetworkVector( m_vecOrigin ); + CNetworkVar( int, m_nEntity ); +}; + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *name - +//----------------------------------------------------------------------------- +CTEPlayerDecal::CTEPlayerDecal( const char *name ) : + CBaseTempEntity( name ) +{ + m_nPlayer = 0; + m_vecOrigin.Init(); + m_nEntity = 0; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CTEPlayerDecal::~CTEPlayerDecal( void ) +{ +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *current_origin - +// *current_angles - +//----------------------------------------------------------------------------- +void CTEPlayerDecal::Test( const Vector& current_origin, const QAngle& current_angles ) +{ + // Fill in data + m_nPlayer = 1; + m_nEntity = 0; + m_vecOrigin = current_origin; + + Vector vecEnd; + + Vector forward; + + m_vecOrigin.GetForModify()[2] += 24; + + AngleVectors( current_angles, &forward ); + forward[2] = 0.0; + VectorNormalize( forward ); + + VectorMA( m_vecOrigin, 50.0, forward, m_vecOrigin.GetForModify() ); + VectorMA( m_vecOrigin, 1024.0, forward, vecEnd ); + + trace_t tr; + + UTIL_TraceLine( m_vecOrigin, vecEnd, MASK_SOLID_BRUSHONLY, NULL, COLLISION_GROUP_NONE, &tr ); + + m_vecOrigin = tr.endpos; + + CBroadcastRecipientFilter filter; + Create( filter, 0.0 ); +} + +IMPLEMENT_SERVERCLASS_ST(CTEPlayerDecal, DT_TEPlayerDecal) + SendPropVector( SENDINFO(m_vecOrigin), -1, SPROP_COORD), + SendPropInt( SENDINFO(m_nEntity), MAX_EDICT_BITS, SPROP_UNSIGNED ), + SendPropInt( SENDINFO(m_nPlayer), Q_log2( MAX_PLAYERS ), SPROP_UNSIGNED ), +END_SEND_TABLE() + + +// Singleton to fire TEPlayerDecal objects +static CTEPlayerDecal g_TEPlayerDecal( "Player Decal" ); + +//----------------------------------------------------------------------------- +// Purpose: +// Input : msg_dest - +// delay - +// *origin - +// *recipient - +// *pos - +// player - +// entity - +// index - +//----------------------------------------------------------------------------- +void TE_PlayerDecal( IRecipientFilter& filter, float delay, + const Vector* pos, int player, int entity ) +{ + g_TEPlayerDecal.m_vecOrigin = *pos; + g_TEPlayerDecal.m_nPlayer = player; + g_TEPlayerDecal.m_nEntity = entity; + + // Send it over the wire + g_TEPlayerDecal.Create( filter, delay ); +} diff --git a/dlls/te_projecteddecal.cpp b/dlls/te_projecteddecal.cpp index ac25c810..a189524c 100644 --- a/dlls/te_projecteddecal.cpp +++ b/dlls/te_projecteddecal.cpp @@ -1,122 +1,122 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $Workfile: $ -// $Date: $ -// -//----------------------------------------------------------------------------- -// $Log: $ -// -// $NoKeywords: $ -//=============================================================================// -#include "cbase.h" -#include "basetempentity.h" - -// memdbgon must be the last include file in a .cpp file!!! -#include "tier0/memdbgon.h" - -//----------------------------------------------------------------------------- -// Purpose: Dispatches BSP decal tempentity -//----------------------------------------------------------------------------- -class CTEProjectedDecal : public CBaseTempEntity -{ -public: - DECLARE_CLASS( CTEProjectedDecal, CBaseTempEntity ); - - CTEProjectedDecal( const char *name ); - virtual ~CTEProjectedDecal( void ); - - virtual void Test( const Vector& current_origin, const QAngle& current_angles ); - - DECLARE_SERVERCLASS(); - -public: - CNetworkVector( m_vecOrigin ); - CNetworkVar( int, m_nIndex ); - CNetworkVar( float, m_flDistance ); - CNetworkQAngle( m_angRotation ); -}; - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *name - -//----------------------------------------------------------------------------- -CTEProjectedDecal::CTEProjectedDecal( const char *name ) : - CBaseTempEntity( name ) -{ - m_vecOrigin.Init(); - m_angRotation.Init(); - m_flDistance = 64.0f; - m_nIndex = 0; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -CTEProjectedDecal::~CTEProjectedDecal( void ) -{ -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *current_origin - -// *current_angles - -//----------------------------------------------------------------------------- -void CTEProjectedDecal::Test( const Vector& current_origin, const QAngle& current_angles ) -{ - // Fill in data - m_flDistance = 1024.0f; - m_nIndex = 0; - m_vecOrigin = current_origin; - m_angRotation = current_angles; - - Vector vecEnd; - - Vector forward; - - m_vecOrigin.GetForModify()[2] += 24; - - AngleVectors( current_angles, &forward ); - forward[2] = 0.0; - VectorNormalize( forward ); - - VectorMA( m_vecOrigin, 24.0, forward, m_vecOrigin.GetForModify() ); - - CBroadcastRecipientFilter filter; - Create( filter, 0.0 ); -} - -IMPLEMENT_SERVERCLASS_ST(CTEProjectedDecal, DT_TEProjectedDecal) - SendPropVector( SENDINFO(m_vecOrigin), -1, SPROP_COORD), - SendPropQAngles( SENDINFO(m_angRotation), 10 ), - SendPropFloat( SENDINFO(m_flDistance), 10, SPROP_ROUNDUP, 0, 1024 ), - SendPropInt( SENDINFO(m_nIndex), 9, SPROP_UNSIGNED ), -END_SEND_TABLE() - - -// Singleton to fire TEBSPDecal objects -static CTEProjectedDecal g_TEProjectedDecal( "Projected Decal" ); - -//----------------------------------------------------------------------------- -// Purpose: -// Input : msg_dest - -// delay - -// *origin - -// *recipient - -// *pos - -// entity - -// index - -// modelindex - -//----------------------------------------------------------------------------- -void TE_ProjectDecal( IRecipientFilter& filter, float delay, - const Vector* pos, const QAngle *angles, float distance, int index ) -{ - g_TEProjectedDecal.m_vecOrigin = *pos; - g_TEProjectedDecal.m_angRotation = *angles; - g_TEProjectedDecal.m_flDistance = distance; - g_TEProjectedDecal.m_nIndex = index; - - // Send it over the wire - g_TEProjectedDecal.Create( filter, delay ); -} \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $Workfile: $ +// $Date: $ +// +//----------------------------------------------------------------------------- +// $Log: $ +// +// $NoKeywords: $ +//=============================================================================// +#include "cbase.h" +#include "basetempentity.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +//----------------------------------------------------------------------------- +// Purpose: Dispatches BSP decal tempentity +//----------------------------------------------------------------------------- +class CTEProjectedDecal : public CBaseTempEntity +{ +public: + DECLARE_CLASS( CTEProjectedDecal, CBaseTempEntity ); + + CTEProjectedDecal( const char *name ); + virtual ~CTEProjectedDecal( void ); + + virtual void Test( const Vector& current_origin, const QAngle& current_angles ); + + DECLARE_SERVERCLASS(); + +public: + CNetworkVector( m_vecOrigin ); + CNetworkVar( int, m_nIndex ); + CNetworkVar( float, m_flDistance ); + CNetworkQAngle( m_angRotation ); +}; + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *name - +//----------------------------------------------------------------------------- +CTEProjectedDecal::CTEProjectedDecal( const char *name ) : + CBaseTempEntity( name ) +{ + m_vecOrigin.Init(); + m_angRotation.Init(); + m_flDistance = 64.0f; + m_nIndex = 0; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CTEProjectedDecal::~CTEProjectedDecal( void ) +{ +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *current_origin - +// *current_angles - +//----------------------------------------------------------------------------- +void CTEProjectedDecal::Test( const Vector& current_origin, const QAngle& current_angles ) +{ + // Fill in data + m_flDistance = 1024.0f; + m_nIndex = 0; + m_vecOrigin = current_origin; + m_angRotation = current_angles; + + Vector vecEnd; + + Vector forward; + + m_vecOrigin.GetForModify()[2] += 24; + + AngleVectors( current_angles, &forward ); + forward[2] = 0.0; + VectorNormalize( forward ); + + VectorMA( m_vecOrigin, 24.0, forward, m_vecOrigin.GetForModify() ); + + CBroadcastRecipientFilter filter; + Create( filter, 0.0 ); +} + +IMPLEMENT_SERVERCLASS_ST(CTEProjectedDecal, DT_TEProjectedDecal) + SendPropVector( SENDINFO(m_vecOrigin), -1, SPROP_COORD), + SendPropQAngles( SENDINFO(m_angRotation), 10 ), + SendPropFloat( SENDINFO(m_flDistance), 10, SPROP_ROUNDUP, 0, 1024 ), + SendPropInt( SENDINFO(m_nIndex), 9, SPROP_UNSIGNED ), +END_SEND_TABLE() + + +// Singleton to fire TEBSPDecal objects +static CTEProjectedDecal g_TEProjectedDecal( "Projected Decal" ); + +//----------------------------------------------------------------------------- +// Purpose: +// Input : msg_dest - +// delay - +// *origin - +// *recipient - +// *pos - +// entity - +// index - +// modelindex - +//----------------------------------------------------------------------------- +void TE_ProjectDecal( IRecipientFilter& filter, float delay, + const Vector* pos, const QAngle *angles, float distance, int index ) +{ + g_TEProjectedDecal.m_vecOrigin = *pos; + g_TEProjectedDecal.m_angRotation = *angles; + g_TEProjectedDecal.m_flDistance = distance; + g_TEProjectedDecal.m_nIndex = index; + + // Send it over the wire + g_TEProjectedDecal.Create( filter, delay ); +} diff --git a/dlls/te_showline.cpp b/dlls/te_showline.cpp index 58df9624..e1818e86 100644 --- a/dlls/te_showline.cpp +++ b/dlls/te_showline.cpp @@ -1,107 +1,107 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $Workfile: $ -// $Date: $ -// -//----------------------------------------------------------------------------- -// $Log: $ -// -// $NoKeywords: $ -//=============================================================================// -#include "cbase.h" -#include "te_particlesystem.h" - -// memdbgon must be the last include file in a .cpp file!!! -#include "tier0/memdbgon.h" - -//----------------------------------------------------------------------------- -// Purpose: Dispatches line -//----------------------------------------------------------------------------- -class CTEShowLine : public CTEParticleSystem -{ -public: - DECLARE_CLASS( CTEShowLine, CTEParticleSystem ); - DECLARE_SERVERCLASS(); - - CTEShowLine( const char *name ); - virtual ~CTEShowLine( void ); - - virtual void Test( const Vector& current_origin, const QAngle& current_angles ); - - -public: - CNetworkVector( m_vecEnd ); -}; - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *name - -//----------------------------------------------------------------------------- -CTEShowLine::CTEShowLine( const char *name ) : - BaseClass( name ) -{ - m_vecEnd.Init(); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -CTEShowLine::~CTEShowLine( void ) -{ -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *current_origin - -// *current_angles - -//----------------------------------------------------------------------------- -void CTEShowLine::Test( const Vector& current_origin, const QAngle& current_angles ) -{ - // Fill in data - m_vecOrigin = current_origin; - - Vector forward, right; - - m_vecOrigin.GetForModify()[2] += 24; - - AngleVectors( current_angles, &forward, &right, NULL ); - forward[2] = 0.0; - VectorNormalize( forward ); - - VectorMA( m_vecOrigin, 100.0, forward, m_vecEnd.GetForModify() ); - - m_vecOrigin = m_vecEnd + right * -128; - m_vecEnd += right * 128; - - CBroadcastRecipientFilter filter; - Create( filter, 0.0 ); -} - -IMPLEMENT_SERVERCLASS_ST( CTEShowLine, DT_TEShowLine) - SendPropVector( SENDINFO(m_vecEnd), -1, SPROP_COORD), -END_SEND_TABLE() - - -// Singleton to fire TEShowLine objects -static CTEShowLine g_TEShowLine( "Show Line" ); - -//----------------------------------------------------------------------------- -// Purpose: -// Input : msg_dest - -// delay - -// *origin - -// *recipient - -// *start - -// *end - -//----------------------------------------------------------------------------- -void TE_ShowLine( IRecipientFilter& filter, float delay, - const Vector* start, const Vector* end ) -{ - g_TEShowLine.m_vecOrigin = *start; - g_TEShowLine.m_vecEnd = *end; - - // Send it over the wire - g_TEShowLine.Create( filter, delay ); -} \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $Workfile: $ +// $Date: $ +// +//----------------------------------------------------------------------------- +// $Log: $ +// +// $NoKeywords: $ +//=============================================================================// +#include "cbase.h" +#include "te_particlesystem.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +//----------------------------------------------------------------------------- +// Purpose: Dispatches line +//----------------------------------------------------------------------------- +class CTEShowLine : public CTEParticleSystem +{ +public: + DECLARE_CLASS( CTEShowLine, CTEParticleSystem ); + DECLARE_SERVERCLASS(); + + CTEShowLine( const char *name ); + virtual ~CTEShowLine( void ); + + virtual void Test( const Vector& current_origin, const QAngle& current_angles ); + + +public: + CNetworkVector( m_vecEnd ); +}; + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *name - +//----------------------------------------------------------------------------- +CTEShowLine::CTEShowLine( const char *name ) : + BaseClass( name ) +{ + m_vecEnd.Init(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CTEShowLine::~CTEShowLine( void ) +{ +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *current_origin - +// *current_angles - +//----------------------------------------------------------------------------- +void CTEShowLine::Test( const Vector& current_origin, const QAngle& current_angles ) +{ + // Fill in data + m_vecOrigin = current_origin; + + Vector forward, right; + + m_vecOrigin.GetForModify()[2] += 24; + + AngleVectors( current_angles, &forward, &right, NULL ); + forward[2] = 0.0; + VectorNormalize( forward ); + + VectorMA( m_vecOrigin, 100.0, forward, m_vecEnd.GetForModify() ); + + m_vecOrigin = m_vecEnd + right * -128; + m_vecEnd += right * 128; + + CBroadcastRecipientFilter filter; + Create( filter, 0.0 ); +} + +IMPLEMENT_SERVERCLASS_ST( CTEShowLine, DT_TEShowLine) + SendPropVector( SENDINFO(m_vecEnd), -1, SPROP_COORD), +END_SEND_TABLE() + + +// Singleton to fire TEShowLine objects +static CTEShowLine g_TEShowLine( "Show Line" ); + +//----------------------------------------------------------------------------- +// Purpose: +// Input : msg_dest - +// delay - +// *origin - +// *recipient - +// *start - +// *end - +//----------------------------------------------------------------------------- +void TE_ShowLine( IRecipientFilter& filter, float delay, + const Vector* start, const Vector* end ) +{ + g_TEShowLine.m_vecOrigin = *start; + g_TEShowLine.m_vecEnd = *end; + + // Send it over the wire + g_TEShowLine.Create( filter, delay ); +} diff --git a/dlls/te_smoke.cpp b/dlls/te_smoke.cpp index 83b60cd7..5675661d 100644 --- a/dlls/te_smoke.cpp +++ b/dlls/te_smoke.cpp @@ -1,112 +1,112 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $Workfile: $ -// $Date: $ -// -//----------------------------------------------------------------------------- -// $Log: $ -// -// $NoKeywords: $ -//=============================================================================// -#include "cbase.h" -#include "basetempentity.h" - -// memdbgon must be the last include file in a .cpp file!!! -#include "tier0/memdbgon.h" - -extern short g_sModelIndexSmoke; // (in combatweapon.cpp) holds the index for the smoke cloud - -//----------------------------------------------------------------------------- -// Purpose: Dispatches smoke tempentity -//----------------------------------------------------------------------------- -class CTESmoke : public CBaseTempEntity -{ -public: - DECLARE_CLASS( CTESmoke, CBaseTempEntity ); - - CTESmoke( const char *name ); - virtual ~CTESmoke( void ); - - virtual void Test( const Vector& current_origin, const QAngle& current_angles ); - - DECLARE_SERVERCLASS(); - -public: - CNetworkVector( m_vecOrigin ); - CNetworkVar( int, m_nModelIndex ); - CNetworkVar( float, m_fScale ); - CNetworkVar( int, m_nFrameRate ); -}; - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *name - -//----------------------------------------------------------------------------- -CTESmoke::CTESmoke( const char *name ) : - CBaseTempEntity( name ) -{ - m_vecOrigin.Init(); - m_nModelIndex = 0; - m_fScale = 0; - m_nFrameRate = 0; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -CTESmoke::~CTESmoke( void ) -{ -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *current_origin - -// *current_angles - -//----------------------------------------------------------------------------- -void CTESmoke::Test( const Vector& current_origin, const QAngle& current_angles ) -{ - // Fill in data - m_nModelIndex = g_sModelIndexSmoke; - m_fScale = 5.0; - m_nFrameRate = 12; - m_vecOrigin = current_origin; - - Vector forward, right; - - m_vecOrigin.GetForModify()[2] += 24; - - AngleVectors( current_angles, &forward, &right, NULL ); - forward[2] = 0.0; - VectorNormalize( forward ); - - VectorMA( m_vecOrigin, 50.0, forward, m_vecOrigin.GetForModify() ); - VectorMA( m_vecOrigin, 25.0, right, m_vecOrigin.GetForModify() ); - - CBroadcastRecipientFilter filter; - Create( filter, 0.0 ); -} - -IMPLEMENT_SERVERCLASS_ST(CTESmoke, DT_TESmoke) - SendPropVector( SENDINFO(m_vecOrigin), -1, SPROP_COORD), - SendPropModelIndex( SENDINFO(m_nModelIndex) ), - SendPropFloat( SENDINFO(m_fScale ), 8, SPROP_ROUNDDOWN, 0.0, 25.6 ), - SendPropInt( SENDINFO(m_nFrameRate), 8, SPROP_UNSIGNED ), -END_SEND_TABLE() - - -// Singleton to fire TESmoke objects -static CTESmoke g_TESmoke( "Smoke" ); - -void TE_Smoke( IRecipientFilter& filter, float delay, - const Vector* pos, int modelindex, float scale, int framerate ) -{ - g_TESmoke.m_vecOrigin = *pos; - g_TESmoke.m_nModelIndex = modelindex; - g_TESmoke.m_fScale = scale; - g_TESmoke.m_nFrameRate = framerate; - - // Send it over the wire - g_TESmoke.Create( filter, delay ); -} \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $Workfile: $ +// $Date: $ +// +//----------------------------------------------------------------------------- +// $Log: $ +// +// $NoKeywords: $ +//=============================================================================// +#include "cbase.h" +#include "basetempentity.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +extern short g_sModelIndexSmoke; // (in combatweapon.cpp) holds the index for the smoke cloud + +//----------------------------------------------------------------------------- +// Purpose: Dispatches smoke tempentity +//----------------------------------------------------------------------------- +class CTESmoke : public CBaseTempEntity +{ +public: + DECLARE_CLASS( CTESmoke, CBaseTempEntity ); + + CTESmoke( const char *name ); + virtual ~CTESmoke( void ); + + virtual void Test( const Vector& current_origin, const QAngle& current_angles ); + + DECLARE_SERVERCLASS(); + +public: + CNetworkVector( m_vecOrigin ); + CNetworkVar( int, m_nModelIndex ); + CNetworkVar( float, m_fScale ); + CNetworkVar( int, m_nFrameRate ); +}; + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *name - +//----------------------------------------------------------------------------- +CTESmoke::CTESmoke( const char *name ) : + CBaseTempEntity( name ) +{ + m_vecOrigin.Init(); + m_nModelIndex = 0; + m_fScale = 0; + m_nFrameRate = 0; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CTESmoke::~CTESmoke( void ) +{ +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *current_origin - +// *current_angles - +//----------------------------------------------------------------------------- +void CTESmoke::Test( const Vector& current_origin, const QAngle& current_angles ) +{ + // Fill in data + m_nModelIndex = g_sModelIndexSmoke; + m_fScale = 5.0; + m_nFrameRate = 12; + m_vecOrigin = current_origin; + + Vector forward, right; + + m_vecOrigin.GetForModify()[2] += 24; + + AngleVectors( current_angles, &forward, &right, NULL ); + forward[2] = 0.0; + VectorNormalize( forward ); + + VectorMA( m_vecOrigin, 50.0, forward, m_vecOrigin.GetForModify() ); + VectorMA( m_vecOrigin, 25.0, right, m_vecOrigin.GetForModify() ); + + CBroadcastRecipientFilter filter; + Create( filter, 0.0 ); +} + +IMPLEMENT_SERVERCLASS_ST(CTESmoke, DT_TESmoke) + SendPropVector( SENDINFO(m_vecOrigin), -1, SPROP_COORD), + SendPropModelIndex( SENDINFO(m_nModelIndex) ), + SendPropFloat( SENDINFO(m_fScale ), 8, SPROP_ROUNDDOWN, 0.0, 25.6 ), + SendPropInt( SENDINFO(m_nFrameRate), 8, SPROP_UNSIGNED ), +END_SEND_TABLE() + + +// Singleton to fire TESmoke objects +static CTESmoke g_TESmoke( "Smoke" ); + +void TE_Smoke( IRecipientFilter& filter, float delay, + const Vector* pos, int modelindex, float scale, int framerate ) +{ + g_TESmoke.m_vecOrigin = *pos; + g_TESmoke.m_nModelIndex = modelindex; + g_TESmoke.m_fScale = scale; + g_TESmoke.m_nFrameRate = framerate; + + // Send it over the wire + g_TESmoke.Create( filter, delay ); +} diff --git a/dlls/te_sprite.cpp b/dlls/te_sprite.cpp index ce9af039..b9633f69 100644 --- a/dlls/te_sprite.cpp +++ b/dlls/te_sprite.cpp @@ -1,121 +1,121 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $Workfile: $ -// $Date: $ -// -//----------------------------------------------------------------------------- -// $Log: $ -// -// $NoKeywords: $ -//=============================================================================// -#include "cbase.h" -#include "basetempentity.h" - -// memdbgon must be the last include file in a .cpp file!!! -#include "tier0/memdbgon.h" - -//----------------------------------------------------------------------------- -// Purpose: Dispatches Sprite tempentity -//----------------------------------------------------------------------------- -class CTESprite : public CBaseTempEntity -{ -public: - DECLARE_CLASS( CTESprite, CBaseTempEntity ); - - CTESprite( const char *name ); - virtual ~CTESprite( void ); - - virtual void Test( const Vector& current_origin, const QAngle& current_angles ); - - virtual void Precache( void ); - - DECLARE_SERVERCLASS(); - -public: - CNetworkVector( m_vecOrigin ); - CNetworkVar( int, m_nModelIndex ); - CNetworkVar( float, m_fScale ); - CNetworkVar( int, m_nBrightness ); -}; - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *name - -//----------------------------------------------------------------------------- -CTESprite::CTESprite( const char *name ) : - CBaseTempEntity( name ) -{ - m_vecOrigin.Init(); - m_nModelIndex = 0; - m_fScale = 0; - m_nBrightness = 0; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -CTESprite::~CTESprite( void ) -{ -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CTESprite::Precache( void ) -{ - CBaseEntity::PrecacheModel("sprites/gunsmoke.vmt"); -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *current_origin - -// *current_angles - -//----------------------------------------------------------------------------- -void CTESprite::Test( const Vector& current_origin, const QAngle& current_angles ) -{ - // Fill in data - m_nModelIndex = CBaseEntity::PrecacheModel("sprites/gunsmoke.vmt"); - m_fScale = 0.8; - m_nBrightness = 200; - m_vecOrigin = current_origin; - - Vector forward, right; - - m_vecOrigin.GetForModify()[2] += 24; - - AngleVectors( current_angles, &forward, &right, NULL ); - forward[2] = 0.0; - VectorNormalize( forward ); - - VectorMA( m_vecOrigin, 50.0, forward, m_vecOrigin.GetForModify() ); - VectorMA( m_vecOrigin, -25.0, right, m_vecOrigin.GetForModify() ); - - CBroadcastRecipientFilter filter; - Create( filter, 0.0 ); -} - - -IMPLEMENT_SERVERCLASS_ST(CTESprite, DT_TESprite) - SendPropVector( SENDINFO(m_vecOrigin), -1, SPROP_COORD), - SendPropModelIndex( SENDINFO(m_nModelIndex) ), - SendPropFloat( SENDINFO(m_fScale ), 8, SPROP_ROUNDDOWN, 0.0, 25.6 ), - SendPropInt( SENDINFO(m_nBrightness), 8, SPROP_UNSIGNED ), -END_SEND_TABLE() - - -// Singleton to fire TESprite objects -static CTESprite g_TESprite( "Sprite" ); - -void TE_Sprite( IRecipientFilter& filter, float delay, - const Vector *pos, int modelindex, float size, int brightness ) -{ - g_TESprite.m_vecOrigin = *pos; - g_TESprite.m_nModelIndex = modelindex; - g_TESprite.m_fScale = size; - g_TESprite.m_nBrightness = brightness; - - // Send it over the wire - g_TESprite.Create( filter, delay ); -} \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $Workfile: $ +// $Date: $ +// +//----------------------------------------------------------------------------- +// $Log: $ +// +// $NoKeywords: $ +//=============================================================================// +#include "cbase.h" +#include "basetempentity.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +//----------------------------------------------------------------------------- +// Purpose: Dispatches Sprite tempentity +//----------------------------------------------------------------------------- +class CTESprite : public CBaseTempEntity +{ +public: + DECLARE_CLASS( CTESprite, CBaseTempEntity ); + + CTESprite( const char *name ); + virtual ~CTESprite( void ); + + virtual void Test( const Vector& current_origin, const QAngle& current_angles ); + + virtual void Precache( void ); + + DECLARE_SERVERCLASS(); + +public: + CNetworkVector( m_vecOrigin ); + CNetworkVar( int, m_nModelIndex ); + CNetworkVar( float, m_fScale ); + CNetworkVar( int, m_nBrightness ); +}; + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *name - +//----------------------------------------------------------------------------- +CTESprite::CTESprite( const char *name ) : + CBaseTempEntity( name ) +{ + m_vecOrigin.Init(); + m_nModelIndex = 0; + m_fScale = 0; + m_nBrightness = 0; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CTESprite::~CTESprite( void ) +{ +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTESprite::Precache( void ) +{ + CBaseEntity::PrecacheModel("sprites/gunsmoke.vmt"); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *current_origin - +// *current_angles - +//----------------------------------------------------------------------------- +void CTESprite::Test( const Vector& current_origin, const QAngle& current_angles ) +{ + // Fill in data + m_nModelIndex = CBaseEntity::PrecacheModel("sprites/gunsmoke.vmt"); + m_fScale = 0.8; + m_nBrightness = 200; + m_vecOrigin = current_origin; + + Vector forward, right; + + m_vecOrigin.GetForModify()[2] += 24; + + AngleVectors( current_angles, &forward, &right, NULL ); + forward[2] = 0.0; + VectorNormalize( forward ); + + VectorMA( m_vecOrigin, 50.0, forward, m_vecOrigin.GetForModify() ); + VectorMA( m_vecOrigin, -25.0, right, m_vecOrigin.GetForModify() ); + + CBroadcastRecipientFilter filter; + Create( filter, 0.0 ); +} + + +IMPLEMENT_SERVERCLASS_ST(CTESprite, DT_TESprite) + SendPropVector( SENDINFO(m_vecOrigin), -1, SPROP_COORD), + SendPropModelIndex( SENDINFO(m_nModelIndex) ), + SendPropFloat( SENDINFO(m_fScale ), 8, SPROP_ROUNDDOWN, 0.0, 25.6 ), + SendPropInt( SENDINFO(m_nBrightness), 8, SPROP_UNSIGNED ), +END_SEND_TABLE() + + +// Singleton to fire TESprite objects +static CTESprite g_TESprite( "Sprite" ); + +void TE_Sprite( IRecipientFilter& filter, float delay, + const Vector *pos, int modelindex, float size, int brightness ) +{ + g_TESprite.m_vecOrigin = *pos; + g_TESprite.m_nModelIndex = modelindex; + g_TESprite.m_fScale = size; + g_TESprite.m_nBrightness = brightness; + + // Send it over the wire + g_TESprite.Create( filter, delay ); +} diff --git a/dlls/te_spritespray.cpp b/dlls/te_spritespray.cpp index d1800569..39af3e23 100644 --- a/dlls/te_spritespray.cpp +++ b/dlls/te_spritespray.cpp @@ -1,136 +1,136 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $Workfile: $ -// $Date: $ -// -//----------------------------------------------------------------------------- -// $Log: $ -// -// $NoKeywords: $ -//=============================================================================// -#include "cbase.h" -#include "basetempentity.h" - -// memdbgon must be the last include file in a .cpp file!!! -#include "tier0/memdbgon.h" - -extern short g_sModelIndexSmoke; // (in combatweapon.cpp) holds the index for the smoke cloud - -//----------------------------------------------------------------------------- -// Purpose: Dispatches Sprite Spray tempentity -//----------------------------------------------------------------------------- -class CTESpriteSpray : public CBaseTempEntity -{ -public: - DECLARE_CLASS( CTESpriteSpray, CBaseTempEntity ); - - CTESpriteSpray( const char *name ); - virtual ~CTESpriteSpray( void ); - - virtual void Test( const Vector& current_origin, const QAngle& current_angles ); - - DECLARE_SERVERCLASS(); - -public: - CNetworkVector( m_vecOrigin ); - CNetworkVector( m_vecDirection ); - CNetworkVar( int, m_nModelIndex ); - CNetworkVar( int, m_nSpeed ); - CNetworkVar( float, m_fNoise ); - CNetworkVar( int, m_nCount ); -}; - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *name - -//----------------------------------------------------------------------------- -CTESpriteSpray::CTESpriteSpray( const char *name ) : - CBaseTempEntity( name ) -{ - m_vecOrigin.Init(); - m_vecDirection.Init(); - m_nModelIndex = 0; - m_fNoise = 0; - m_nSpeed = 0; - m_nCount = 0; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -CTESpriteSpray::~CTESpriteSpray( void ) -{ -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *current_origin - -// *current_angles - -//----------------------------------------------------------------------------- -void CTESpriteSpray::Test( const Vector& current_origin, const QAngle& current_angles ) -{ - // Fill in data - m_nModelIndex = g_sModelIndexSmoke; - m_fNoise = 0.8; - m_nCount = 5; - m_nSpeed = 30; - m_vecOrigin = current_origin; - - Vector forward, right; - - m_vecOrigin.GetForModify()[2] += 24; - - AngleVectors( current_angles, &forward, &right, NULL ); - forward[2] = 0.0; - VectorNormalize( forward ); - - VectorMA( m_vecOrigin, 50.0, forward, m_vecOrigin.GetForModify() ); - VectorMA( m_vecOrigin, -25.0, right, m_vecOrigin.GetForModify() ); - - m_vecDirection.Init( random->RandomInt( -100, 100 ), random->RandomInt( -100, 100 ), random->RandomInt( 0, 100 ) ); - - CBroadcastRecipientFilter filter; - Create( filter, 0.0 ); -} - -IMPLEMENT_SERVERCLASS_ST(CTESpriteSpray, DT_TESpriteSpray) - SendPropVector( SENDINFO(m_vecOrigin), -1, SPROP_COORD), - SendPropVector( SENDINFO(m_vecDirection), -1, SPROP_COORD), - SendPropModelIndex(SENDINFO(m_nModelIndex)), - SendPropFloat( SENDINFO(m_fNoise ), 8, SPROP_ROUNDDOWN, 0.0, 2.56 ), - SendPropInt( SENDINFO(m_nSpeed ), 8, SPROP_UNSIGNED ), - SendPropInt( SENDINFO(m_nCount), 8, SPROP_UNSIGNED ), -END_SEND_TABLE() - - -// Singleton to fire TESpriteSpray objects -static CTESpriteSpray g_TESpriteSpray( "Sprite Spray" ); - -//----------------------------------------------------------------------------- -// Purpose: -// Input : msg_dest - -// delay - -// *origin - -// *recipient - -// *pos - -// *dir - -// modelindex - -// speed - -// noise - -// count - -//----------------------------------------------------------------------------- -void TE_SpriteSpray( IRecipientFilter& filter, float delay, - const Vector *pos, const Vector *dir, int modelindex, int speed, float noise, int count ) -{ - g_TESpriteSpray.m_vecOrigin = *pos; - g_TESpriteSpray.m_vecDirection = *dir; - g_TESpriteSpray.m_nModelIndex = modelindex; - g_TESpriteSpray.m_nSpeed = speed; - g_TESpriteSpray.m_fNoise = noise; - g_TESpriteSpray.m_nCount = count; - - // Send it over the wire - g_TESpriteSpray.Create( filter, delay ); -} \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $Workfile: $ +// $Date: $ +// +//----------------------------------------------------------------------------- +// $Log: $ +// +// $NoKeywords: $ +//=============================================================================// +#include "cbase.h" +#include "basetempentity.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +extern short g_sModelIndexSmoke; // (in combatweapon.cpp) holds the index for the smoke cloud + +//----------------------------------------------------------------------------- +// Purpose: Dispatches Sprite Spray tempentity +//----------------------------------------------------------------------------- +class CTESpriteSpray : public CBaseTempEntity +{ +public: + DECLARE_CLASS( CTESpriteSpray, CBaseTempEntity ); + + CTESpriteSpray( const char *name ); + virtual ~CTESpriteSpray( void ); + + virtual void Test( const Vector& current_origin, const QAngle& current_angles ); + + DECLARE_SERVERCLASS(); + +public: + CNetworkVector( m_vecOrigin ); + CNetworkVector( m_vecDirection ); + CNetworkVar( int, m_nModelIndex ); + CNetworkVar( int, m_nSpeed ); + CNetworkVar( float, m_fNoise ); + CNetworkVar( int, m_nCount ); +}; + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *name - +//----------------------------------------------------------------------------- +CTESpriteSpray::CTESpriteSpray( const char *name ) : + CBaseTempEntity( name ) +{ + m_vecOrigin.Init(); + m_vecDirection.Init(); + m_nModelIndex = 0; + m_fNoise = 0; + m_nSpeed = 0; + m_nCount = 0; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CTESpriteSpray::~CTESpriteSpray( void ) +{ +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *current_origin - +// *current_angles - +//----------------------------------------------------------------------------- +void CTESpriteSpray::Test( const Vector& current_origin, const QAngle& current_angles ) +{ + // Fill in data + m_nModelIndex = g_sModelIndexSmoke; + m_fNoise = 0.8; + m_nCount = 5; + m_nSpeed = 30; + m_vecOrigin = current_origin; + + Vector forward, right; + + m_vecOrigin.GetForModify()[2] += 24; + + AngleVectors( current_angles, &forward, &right, NULL ); + forward[2] = 0.0; + VectorNormalize( forward ); + + VectorMA( m_vecOrigin, 50.0, forward, m_vecOrigin.GetForModify() ); + VectorMA( m_vecOrigin, -25.0, right, m_vecOrigin.GetForModify() ); + + m_vecDirection.Init( random->RandomInt( -100, 100 ), random->RandomInt( -100, 100 ), random->RandomInt( 0, 100 ) ); + + CBroadcastRecipientFilter filter; + Create( filter, 0.0 ); +} + +IMPLEMENT_SERVERCLASS_ST(CTESpriteSpray, DT_TESpriteSpray) + SendPropVector( SENDINFO(m_vecOrigin), -1, SPROP_COORD), + SendPropVector( SENDINFO(m_vecDirection), -1, SPROP_COORD), + SendPropModelIndex(SENDINFO(m_nModelIndex)), + SendPropFloat( SENDINFO(m_fNoise ), 8, SPROP_ROUNDDOWN, 0.0, 2.56 ), + SendPropInt( SENDINFO(m_nSpeed ), 8, SPROP_UNSIGNED ), + SendPropInt( SENDINFO(m_nCount), 8, SPROP_UNSIGNED ), +END_SEND_TABLE() + + +// Singleton to fire TESpriteSpray objects +static CTESpriteSpray g_TESpriteSpray( "Sprite Spray" ); + +//----------------------------------------------------------------------------- +// Purpose: +// Input : msg_dest - +// delay - +// *origin - +// *recipient - +// *pos - +// *dir - +// modelindex - +// speed - +// noise - +// count - +//----------------------------------------------------------------------------- +void TE_SpriteSpray( IRecipientFilter& filter, float delay, + const Vector *pos, const Vector *dir, int modelindex, int speed, float noise, int count ) +{ + g_TESpriteSpray.m_vecOrigin = *pos; + g_TESpriteSpray.m_vecDirection = *dir; + g_TESpriteSpray.m_nModelIndex = modelindex; + g_TESpriteSpray.m_nSpeed = speed; + g_TESpriteSpray.m_fNoise = noise; + g_TESpriteSpray.m_nCount = count; + + // Send it over the wire + g_TESpriteSpray.Create( filter, delay ); +} diff --git a/dlls/te_worlddecal.cpp b/dlls/te_worlddecal.cpp index 8f015a9d..77bb9932 100644 --- a/dlls/te_worlddecal.cpp +++ b/dlls/te_worlddecal.cpp @@ -1,117 +1,117 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $Workfile: $ -// $Date: $ -// -//----------------------------------------------------------------------------- -// $Log: $ -// -// $NoKeywords: $ -//=============================================================================// -#include "cbase.h" -#include "basetempentity.h" - -// memdbgon must be the last include file in a .cpp file!!! -#include "tier0/memdbgon.h" - -//----------------------------------------------------------------------------- -// Purpose: Dispatches world decal tempentity -//----------------------------------------------------------------------------- -class CTEWorldDecal : public CBaseTempEntity -{ -public: - DECLARE_CLASS( CTEWorldDecal, CBaseTempEntity ); - - CTEWorldDecal( const char *name ); - virtual ~CTEWorldDecal( void ); - - virtual void Test( const Vector& current_origin, const QAngle& current_angles ); - - DECLARE_SERVERCLASS(); - -public: - CNetworkVector( m_vecOrigin ); - CNetworkVar( int, m_nIndex ); -}; - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *name - -//----------------------------------------------------------------------------- -CTEWorldDecal::CTEWorldDecal( const char *name ) : - CBaseTempEntity( name ) -{ - m_vecOrigin.Init(); - m_nIndex = 0; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -CTEWorldDecal::~CTEWorldDecal( void ) -{ -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *current_origin - -// *current_angles - -//----------------------------------------------------------------------------- -void CTEWorldDecal::Test( const Vector& current_origin, const QAngle& current_angles ) -{ - // Fill in data - m_nIndex = 0; - m_vecOrigin = current_origin; - - Vector vecEnd; - - Vector forward; - - m_vecOrigin.GetForModify()[2] += 24; - - AngleVectors( current_angles, &forward ); - forward[2] = 0.0; - VectorNormalize( forward ); - - VectorMA( m_vecOrigin, 50.0, forward, m_vecOrigin.GetForModify() ); - VectorMA( m_vecOrigin, 1024.0, forward, vecEnd ); - - trace_t tr; - - UTIL_TraceLine( m_vecOrigin, vecEnd, MASK_SOLID_BRUSHONLY, NULL, COLLISION_GROUP_NONE, &tr ); - - m_vecOrigin = tr.endpos; - - CBroadcastRecipientFilter filter; - Create( filter, 0.0 ); -} - -IMPLEMENT_SERVERCLASS_ST(CTEWorldDecal, DT_TEWorldDecal) - SendPropVector( SENDINFO(m_vecOrigin), -1, SPROP_COORD), - SendPropInt( SENDINFO(m_nIndex), 9, SPROP_UNSIGNED ), -END_SEND_TABLE() - - -// Singleton to fire TEWorldDecal objects -static CTEWorldDecal g_TEWorldDecal( "World Decal" ); - -//----------------------------------------------------------------------------- -// Purpose: -// Input : msg_dest - -// delay - -// *origin - -// *recipient - -// *pos - -// index - -//----------------------------------------------------------------------------- -void TE_WorldDecal( IRecipientFilter& filter, float delay, - const Vector* pos, int index ) -{ - g_TEWorldDecal.m_vecOrigin = *pos; - g_TEWorldDecal.m_nIndex = index; - - // Send it over the wire - g_TEWorldDecal.Create( filter, delay ); -} \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $Workfile: $ +// $Date: $ +// +//----------------------------------------------------------------------------- +// $Log: $ +// +// $NoKeywords: $ +//=============================================================================// +#include "cbase.h" +#include "basetempentity.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +//----------------------------------------------------------------------------- +// Purpose: Dispatches world decal tempentity +//----------------------------------------------------------------------------- +class CTEWorldDecal : public CBaseTempEntity +{ +public: + DECLARE_CLASS( CTEWorldDecal, CBaseTempEntity ); + + CTEWorldDecal( const char *name ); + virtual ~CTEWorldDecal( void ); + + virtual void Test( const Vector& current_origin, const QAngle& current_angles ); + + DECLARE_SERVERCLASS(); + +public: + CNetworkVector( m_vecOrigin ); + CNetworkVar( int, m_nIndex ); +}; + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *name - +//----------------------------------------------------------------------------- +CTEWorldDecal::CTEWorldDecal( const char *name ) : + CBaseTempEntity( name ) +{ + m_vecOrigin.Init(); + m_nIndex = 0; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CTEWorldDecal::~CTEWorldDecal( void ) +{ +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *current_origin - +// *current_angles - +//----------------------------------------------------------------------------- +void CTEWorldDecal::Test( const Vector& current_origin, const QAngle& current_angles ) +{ + // Fill in data + m_nIndex = 0; + m_vecOrigin = current_origin; + + Vector vecEnd; + + Vector forward; + + m_vecOrigin.GetForModify()[2] += 24; + + AngleVectors( current_angles, &forward ); + forward[2] = 0.0; + VectorNormalize( forward ); + + VectorMA( m_vecOrigin, 50.0, forward, m_vecOrigin.GetForModify() ); + VectorMA( m_vecOrigin, 1024.0, forward, vecEnd ); + + trace_t tr; + + UTIL_TraceLine( m_vecOrigin, vecEnd, MASK_SOLID_BRUSHONLY, NULL, COLLISION_GROUP_NONE, &tr ); + + m_vecOrigin = tr.endpos; + + CBroadcastRecipientFilter filter; + Create( filter, 0.0 ); +} + +IMPLEMENT_SERVERCLASS_ST(CTEWorldDecal, DT_TEWorldDecal) + SendPropVector( SENDINFO(m_vecOrigin), -1, SPROP_COORD), + SendPropInt( SENDINFO(m_nIndex), 9, SPROP_UNSIGNED ), +END_SEND_TABLE() + + +// Singleton to fire TEWorldDecal objects +static CTEWorldDecal g_TEWorldDecal( "World Decal" ); + +//----------------------------------------------------------------------------- +// Purpose: +// Input : msg_dest - +// delay - +// *origin - +// *recipient - +// *pos - +// index - +//----------------------------------------------------------------------------- +void TE_WorldDecal( IRecipientFilter& filter, float delay, + const Vector* pos, int index ) +{ + g_TEWorldDecal.m_vecOrigin = *pos; + g_TEWorldDecal.m_nIndex = index; + + // Send it over the wire + g_TEWorldDecal.Create( filter, delay ); +} diff --git a/dlls/triggers.cpp b/dlls/triggers.cpp index 27cf9990..69d46dad 100644 --- a/dlls/triggers.cpp +++ b/dlls/triggers.cpp @@ -1522,7 +1522,6 @@ void CChangeLevel::WarnAboutActiveLead( void ) void CChangeLevel::ChangeLevelNow( CBaseEntity *pActivator ) { CBaseEntity *pLandmark; - levellist_t levels[16]; Assert(!FStrEq(m_szMapName, "")); @@ -3616,7 +3615,7 @@ END_DATADESC() void CTriggerWind::Spawn( void ) { m_bSwitch = true; - m_nDirBase = GetLocalAngles().y; + m_nDirBase = static_cast(GetLocalAngles().y); BaseClass::Spawn(); @@ -3739,7 +3738,7 @@ void CTriggerWind::WindThink( void ) // Set new target direction and speed m_nSpeedTarget = m_nSpeedBase + random->RandomInt( -m_nSpeedNoise, m_nSpeedNoise ); - m_nDirTarget = UTIL_AngleMod( m_nDirBase + random->RandomInt(-m_nDirNoise, m_nDirNoise) ); + m_nDirTarget = static_cast(UTIL_AngleMod( m_nDirBase + random->RandomInt(-m_nDirNoise, m_nDirNoise) )); } else { @@ -3747,14 +3746,14 @@ void CTriggerWind::WindThink( void ) // either ramp up, or sleep till change if (abs(m_nSpeedTarget - m_nSpeedCurrent) > MAX_WIND_CHANGE) { - m_nSpeedCurrent += (m_nSpeedTarget > m_nSpeedCurrent) ? MAX_WIND_CHANGE : -MAX_WIND_CHANGE; + m_nSpeedCurrent += (m_nSpeedTarget > m_nSpeedCurrent) ? static_cast(MAX_WIND_CHANGE) : static_cast(-MAX_WIND_CHANGE); bDone = false; } if (abs(m_nDirTarget - m_nDirCurrent) > MAX_WIND_CHANGE) { - m_nDirCurrent = UTIL_ApproachAngle( m_nDirTarget, m_nDirCurrent, MAX_WIND_CHANGE ); + m_nDirCurrent = static_cast(UTIL_ApproachAngle( m_nDirTarget, m_nDirCurrent, MAX_WIND_CHANGE )); bDone = false; } diff --git a/dlls/util.cpp b/dlls/util.cpp index 092b2158..3883953b 100644 --- a/dlls/util.cpp +++ b/dlls/util.cpp @@ -687,10 +687,10 @@ void UTIL_GetPlayerConnectionInfo( int playerIndex, int& ping, int &packetloss ) // Source pings by half a tick to match the old GoldSrc pings. latency -= TICKS_TO_TIME( 0.5f ); - ping = latency * 1000.0f; // as msecs + ping = static_cast(latency * 1000.0f); // as msecs ping = clamp( ping, 5, 1000 ); // set bounds, dont show pings under 5 msecs - packetloss = 100.0f * nci->GetAvgLoss( FLOW_INCOMING ); // loss in percentage + packetloss = static_cast(100.0f * nci->GetAvgLoss( FLOW_INCOMING )); // loss in percentage packetloss = clamp( packetloss, 0, 100 ); } else @@ -704,7 +704,7 @@ static unsigned short FixedUnsigned16( float value, float scale ) { int output; - output = value * scale; + output = static_cast(value * scale); if ( output < 0 ) output = 0; if ( output > 0xFFFF ) @@ -2151,7 +2151,7 @@ void UTIL_SetClientVisibilityPVS( edict_t *pClient, const unsigned char *pvs, in { if ( pClient == UTIL_GetCurrentCheckClient() ) { - Assert( pvssize <= sizeof(g_CheckClient.m_checkVisibilityPVS) ); + Assert( pvssize <= static_cast(sizeof(g_CheckClient.m_checkVisibilityPVS)) ); g_CheckClient.m_bClientPVSIsExpanded = false; @@ -2289,7 +2289,6 @@ CBaseEntity *UTIL_EntitiesInPVS( CBaseEntity *pPVSEntity, CBaseEntity *pStarting Vector org; static byte pvs[ MAX_MAP_CLUSTERS/8 ]; static Vector lastOrg( 0, 0, 0 ); - static int lastCluster = -1; if ( !pPVSEntity ) return NULL; diff --git a/dlls/util.h b/dlls/util.h index a1f823b0..87e95589 100644 --- a/dlls/util.h +++ b/dlls/util.h @@ -1,619 +1,620 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: Misc utility code. -// -// $NoKeywords: $ -//=============================================================================// - -#ifndef UTIL_H -#define UTIL_H -#ifdef _WIN32 -#pragma once -#endif - -#include "ai_activity.h" -#include "enginecallback.h" -#include "basetypes.h" -#include "tempentity.h" -#include "string_t.h" -#include "gamestringpool.h" -#include "engine/IEngineTrace.h" -#include "worldsize.h" -#include "dt_send.h" -#include "server_class.h" -#include "shake.h" - -#include "vstdlib/random.h" -#include - -#include "utlvector.h" -#include "util_shared.h" -#include "shareddefs.h" -#include "networkvar.h" - -struct levellist_t; -class IServerNetworkable; -class IEntityFactory; - -#ifdef _WIN32 - #define SETUP_EXTERNC(mapClassName)\ - extern "C" _declspec( dllexport ) IServerNetworkable* mapClassName( void ); -#else - #define SETUP_EXTERNC(mapClassName) -#endif - - -#include "tier0/memdbgon.h" - - -// entity creation -// creates an entity that has not been linked to a classname -template< class T > -T *_CreateEntityTemplate( T *newEnt, const char *className ) -{ - newEnt = new T; // this is the only place 'new' should be used! - newEnt->PostConstructor( className ); - return newEnt; -} - -#include "tier0/memdbgoff.h" - -// creates an entity by name, and ensure it's correctness -// does not spawn the entity -// use the CREATE_ENTITY() macro which wraps this, instead of using it directly -template< class T > -T *_CreateEntity( T *newClass, const char *className ) -{ - T *newEnt = dynamic_cast( CreateEntityByName(className) ); - if ( !newEnt ) - { - Warning( "classname %s used to create wrong class type\n" ); - Assert(0); - } - - return newEnt; -} - -#define CREATE_ENTITY( newClass, className ) _CreateEntity( (newClass*)NULL, className ) -#define CREATE_UNSAVED_ENTITY( newClass, className ) _CreateEntityTemplate( (newClass*)NULL, className ) - - -// This is the glue that hooks .MAP entity class names to our CPP classes -abstract_class IEntityFactoryDictionary -{ -public: - virtual void InstallFactory( IEntityFactory *pFactory, const char *pClassName ) = 0; - virtual IServerNetworkable *Create( const char *pClassName ) = 0; - virtual void Destroy( const char *pClassName, IServerNetworkable *pNetworkable ) = 0; - virtual IEntityFactory *FindFactory( const char *pClassName ) = 0; - virtual const char *GetCannonicalName( const char *pClassName ) = 0; -}; - -IEntityFactoryDictionary *EntityFactoryDictionary(); - -inline bool CanCreateEntityClass( const char *pszClassname ) -{ - return ( EntityFactoryDictionary() != NULL && EntityFactoryDictionary()->FindFactory( pszClassname ) != NULL ); -} - -abstract_class IEntityFactory -{ -public: - virtual IServerNetworkable *Create( const char *pClassName ) = 0; - virtual void Destroy( IServerNetworkable *pNetworkable ) = 0; - virtual size_t GetEntitySize() = 0; -}; - -template -class CEntityFactory : public IEntityFactory -{ -public: - CEntityFactory( const char *pClassName ) - { - EntityFactoryDictionary()->InstallFactory( this, pClassName ); - } - - IServerNetworkable *Create( const char *pClassName ) - { - T* pEnt = _CreateEntityTemplate((T*)NULL, pClassName); - return pEnt->NetworkProp(); - } - - void Destroy( IServerNetworkable *pNetworkable ) - { - if ( pNetworkable ) - { - pNetworkable->Release(); - } - } - - virtual size_t GetEntitySize() - { - return sizeof(T); - } -}; - -#define LINK_ENTITY_TO_CLASS(mapClassName,DLLClassName) \ - static CEntityFactory mapClassName( #mapClassName ); - - -// -// Conversion among the three types of "entity", including identity-conversions. -// -inline int ENTINDEX( edict_t *pEdict) -{ - return engine->IndexOfEdict(pEdict); -} - -int ENTINDEX( CBaseEntity *pEnt ); - -inline edict_t* INDEXENT( int iEdictNum ) -{ - return engine->PEntityOfEntIndex(iEdictNum); -} - -// Testing the three types of "entity" for nullity -inline bool FNullEnt(const edict_t* pent) -{ - return pent == NULL || ENTINDEX((edict_t*)pent) == 0; -} - -// Dot products for view cone checking -#define VIEW_FIELD_FULL (float)-1.0 // +-180 degrees -#define VIEW_FIELD_WIDE (float)-0.7 // +-135 degrees 0.1 // +-85 degrees, used for full FOV checks -#define VIEW_FIELD_NARROW (float)0.7 // +-45 degrees, more narrow check used to set up ranged attacks -#define VIEW_FIELD_ULTRA_NARROW (float)0.9 // +-25 degrees, more narrow check used to set up ranged attacks - -class CBaseEntity; -class CBasePlayer; - -extern CGlobalVars *gpGlobals; - -// Misc useful -inline bool FStrEq(const char *sz1, const char *sz2) -{ - return(stricmp(sz1, sz2) == 0); -} - -#if 0 -// UNDONE: Remove/alter MAKE_STRING so we can do this? -inline bool FStrEq( string_t str1, string_t str2 ) -{ - // now that these are pooled, we can compare them with - // integer equality - return str1 == str2; -} -#endif - -const char *nexttoken(char *token, const char *str, char sep); - -// Misc. Prototypes -void UTIL_SetSize (CBaseEntity *pEnt, const Vector &vecMin, const Vector &vecMax); -void UTIL_ClearTrace ( trace_t &trace ); -void UTIL_SetTrace (trace_t& tr, const Ray_t &ray, edict_t* edict, float fraction, int hitgroup, unsigned int contents, const Vector& normal, float intercept ); - -int UTIL_PrecacheDecal ( const char *name, bool preload = false ); - -//----------------------------------------------------------------------------- - -float UTIL_GetSimulationInterval(); - -//----------------------------------------------------------------------------- -// Purpose: Gets a player pointer by 1-based index -// If player is not yet spawned or connected, returns NULL -// Input : playerIndex - index of the player - first player is index 1 -//----------------------------------------------------------------------------- - -// NOTENOTE: Use UTIL_GetLocalPlayer instead of UTIL_PlayerByIndex IF you're in single player -// and you want the player. -CBasePlayer *UTIL_PlayerByIndex( int playerIndex ); - -// NOTENOTE: Use this instead of UTIL_PlayerByIndex IF you're in single player -// and you want the player. -// not useable in multiplayer - see UTIL_GetListenServerHost() -CBasePlayer* UTIL_GetLocalPlayer( void ); - -// get the local player on a listen server -CBasePlayer *UTIL_GetListenServerHost( void ); - -CBasePlayer* UTIL_PlayerByUserId( int userID ); -CBasePlayer* UTIL_PlayerByName( const char *name ); // not case sensitive - -// Returns true if the command was issued by the listenserver host, or by the dedicated server, via rcon or the server console. -// This is valid during ConCommand execution. -bool UTIL_IsCommandIssuedByServerAdmin( void ); - -CBaseEntity* UTIL_EntityByIndex( int entityIndex ); - -void UTIL_GetPlayerConnectionInfo( int playerIndex, int& ping, int &packetloss ); - -void UTIL_SetClientVisibilityPVS( edict_t *pClient, const unsigned char *pvs, int pvssize ); -bool UTIL_ClientPVSIsExpanded(); - -edict_t *UTIL_FindClientInPVS( edict_t *pEdict ); -edict_t *UTIL_FindClientInVisibilityPVS( edict_t *pEdict ); - -// This is a version which finds any clients whose PVS intersects the box -CBaseEntity *UTIL_FindClientInPVS( const Vector &vecBoxMins, const Vector &vecBoxMaxs ); - -CBaseEntity *UTIL_EntitiesInPVS( CBaseEntity *pPVSEntity, CBaseEntity *pStartingEntity ); - -//----------------------------------------------------------------------------- -// class CFlaggedEntitiesEnum -//----------------------------------------------------------------------------- -// enumerate entities that match a set of edict flags into a static array -class CFlaggedEntitiesEnum : public IPartitionEnumerator -{ -public: - CFlaggedEntitiesEnum( CBaseEntity **pList, int listMax, int flagMask ); - - // This gets called by the enumeration methods with each element - // that passes the test. - virtual IterationRetval_t EnumElement( IHandleEntity *pHandleEntity ); - - int GetCount() { return m_count; } - bool AddToList( CBaseEntity *pEntity ); - -private: - CBaseEntity **m_pList; - int m_listMax; - int m_flagMask; - int m_count; -}; - -// Pass in an array of pointers and an array size, it fills the array and returns the number inserted -int UTIL_EntitiesInBox( const Vector &mins, const Vector &maxs, CFlaggedEntitiesEnum *pEnum ); -int UTIL_EntitiesAlongRay( const Ray_t &ray, CFlaggedEntitiesEnum *pEnum ); -int UTIL_EntitiesInSphere( const Vector ¢er, float radius, CFlaggedEntitiesEnum *pEnum ); - -inline int UTIL_EntitiesInBox( CBaseEntity **pList, int listMax, const Vector &mins, const Vector &maxs, int flagMask ) -{ - CFlaggedEntitiesEnum boxEnum( pList, listMax, flagMask ); - return UTIL_EntitiesInBox( mins, maxs, &boxEnum ); -} - -inline int UTIL_EntitiesAlongRay( CBaseEntity **pList, int listMax, const Ray_t &ray, int flagMask ) -{ - CFlaggedEntitiesEnum rayEnum( pList, listMax, flagMask ); - return UTIL_EntitiesAlongRay( ray, &rayEnum ); -} - -inline int UTIL_EntitiesInSphere( CBaseEntity **pList, int listMax, const Vector ¢er, float radius, int flagMask ) -{ - CFlaggedEntitiesEnum sphereEnum( pList, listMax, flagMask ); - return UTIL_EntitiesInSphere( center, radius, &sphereEnum ); -} - -// marks the entity for deletion so it will get removed next frame -void UTIL_Remove( IServerNetworkable *oldObj ); -void UTIL_Remove( CBaseEntity *oldObj ); - -// deletes an entity, without any delay. Only use this when sure no pointers rely on this entity. -void UTIL_DisableRemoveImmediate(); -void UTIL_EnableRemoveImmediate(); -void UTIL_RemoveImmediate( CBaseEntity *oldObj ); - -// make this a fixed size so it just sits on the stack -#define MAX_SPHERE_QUERY 512 -class CEntitySphereQuery -{ -public: - // currently this builds the list in the constructor - // UNDONE: make an iterative query of ISpatialPartition so we could - // make queries like this optimal - CEntitySphereQuery( const Vector ¢er, float radius, int flagMask=0 ); - CBaseEntity *GetCurrentEntity(); - inline void NextEntity() { m_listIndex++; } - -private: - int m_listIndex; - int m_listCount; - CBaseEntity *m_pList[MAX_SPHERE_QUERY]; -}; - -enum soundlevel_t; - -// Drops an entity onto the floor -int UTIL_DropToFloor( CBaseEntity *pEntity, unsigned int mask ); - -// Returns false if any part of the bottom of the entity is off an edge that is not a staircase. -bool UTIL_CheckBottom( CBaseEntity *pEntity, ITraceFilter *pTraceFilter, float flStepSize ); - -void UTIL_SetOrigin ( CBaseEntity *entity, const Vector &vecOrigin, bool bFireTriggers = false ); -void UTIL_EmitAmbientSound ( int entindex, const Vector &vecOrigin, const char *samp, float vol, soundlevel_t soundlevel, int fFlags, int pitch, float soundtime = 0.0f, float *duration = NULL ); -void UTIL_ParticleEffect ( const Vector &vecOrigin, const Vector &vecDirection, ULONG ulColor, ULONG ulCount ); -void UTIL_ScreenShake ( const Vector ¢er, float amplitude, float frequency, float duration, float radius, ShakeCommand_t eCommand, bool bAirShake=false ); -void UTIL_ScreenShakeObject ( CBaseEntity *pEnt, const Vector ¢er, float amplitude, float frequency, float duration, float radius, ShakeCommand_t eCommand, bool bAirShake=false ); -void UTIL_ViewPunch ( const Vector ¢er, QAngle angPunch, float radius, bool bInAir ); -void UTIL_ShowMessage ( const char *pString, CBasePlayer *pPlayer ); -void UTIL_ShowMessageAll ( const char *pString ); -void UTIL_ScreenFadeAll ( const color32 &color, float fadeTime, float holdTime, int flags ); -void UTIL_ScreenFade ( CBaseEntity *pEntity, const color32 &color, float fadeTime, float fadeHold, int flags ); -void UTIL_MuzzleFlash ( const Vector &origin, const QAngle &angles, int scale, int type ); -Vector UTIL_PointOnLineNearestPoint(const Vector& vStartPos, const Vector& vEndPos, const Vector& vPoint, bool clampEnds = false ); - -int UTIL_EntityInSolid( CBaseEntity *ent ); - -bool UTIL_IsMasterTriggered (string_t sMaster, CBaseEntity *pActivator); -void UTIL_BloodStream( const Vector &origin, const Vector &direction, int color, int amount ); -void UTIL_BloodSpray( const Vector &pos, const Vector &dir, int color, int amount, int flags ); -Vector UTIL_RandomBloodVector( void ); -void UTIL_ImpactTrace( trace_t *pTrace, int iDamageType, char *pCustomImpactName = NULL ); -void UTIL_PlayerDecalTrace( trace_t *pTrace, int playernum ); -void UTIL_Smoke( const Vector &origin, const float scale, const float framerate ); -void UTIL_AxisStringToPointDir( Vector &start, Vector &dir, const char *pString ); -void UTIL_AxisStringToPointPoint( Vector &start, Vector &end, const char *pString ); -void UTIL_AxisStringToUnitDir( Vector &dir, const char *pString ); -void UTIL_ClipPunchAngleOffset( QAngle &in, const QAngle &punch, const QAngle &clip ); -void UTIL_PredictedPosition( CBaseEntity *pTarget, float flTimeDelta, Vector *vecPredictedPosition ); -void UTIL_Beam( Vector &Start, Vector &End, int nModelIndex, int nHaloIndex, unsigned char FrameStart, unsigned char FrameRate, - float Life, unsigned char Width, unsigned char EndWidth, unsigned char FadeLength, unsigned char Noise, unsigned char Red, unsigned char Green, - unsigned char Blue, unsigned char Brightness, unsigned char Speed); - -char *UTIL_VarArgs( char *format, ... ); -bool UTIL_IsValidEntity( CBaseEntity *pEnt ); -bool UTIL_TeamsMatch( const char *pTeamName1, const char *pTeamName2 ); - -// snaps a vector to the nearest axis vector (if within epsilon) -void UTIL_SnapDirectionToAxis( Vector &direction, float epsilon = 0.01f ); - -//Set the entity to point at the target specified -bool UTIL_PointAtEntity( CBaseEntity *pEnt, CBaseEntity *pTarget ); -void UTIL_PointAtNamedEntity( CBaseEntity *pEnt, string_t strTarget ); - -// Search for water transition along a vertical line -float UTIL_WaterLevel( const Vector &position, float minz, float maxz ); - -// Like UTIL_WaterLevel, but *way* less expensive. -// I didn't replace UTIL_WaterLevel everywhere to avoid breaking anything. -float UTIL_FindWaterSurface( const Vector &position, float minz, float maxz ); - -void UTIL_Bubbles( const Vector& mins, const Vector& maxs, int count ); -void UTIL_BubbleTrail( const Vector& from, const Vector& to, int count ); - -// allows precacheing of other entities -void UTIL_PrecacheOther( const char *szClassname, const char *modelName = NULL ); - -// prints a message to each client -void UTIL_ClientPrintAll( int msg_dest, const char *msg_name, const char *param1 = NULL, const char *param2 = NULL, const char *param3 = NULL, const char *param4 = NULL ); -inline void UTIL_CenterPrintAll( const char *msg_name, const char *param1 = NULL, const char *param2 = NULL, const char *param3 = NULL, const char *param4 = NULL ) -{ - UTIL_ClientPrintAll( HUD_PRINTCENTER, msg_name, param1, param2, param3, param4 ); -} - -void UTIL_ValidateSoundName( string_t &name, const char *defaultStr ); - -void UTIL_ClientPrintFilter( IRecipientFilter& filter, int msg_dest, const char *msg_name, const char *param1 = NULL, const char *param2 = NULL, const char *param3 = NULL, const char *param4 = NULL ); - -// prints messages through the HUD -void ClientPrint( CBasePlayer *player, int msg_dest, const char *msg_name, const char *param1 = NULL, const char *param2 = NULL, const char *param3 = NULL, const char *param4 = NULL ); - -// prints a message to the HUD say (chat) -void UTIL_SayText( const char *pText, CBasePlayer *pEntity ); -void UTIL_SayTextAll( const char *pText, CBasePlayer *pEntity = NULL, bool bChat = false ); -void UTIL_SayTextFilter( IRecipientFilter& filter, const char *pText, CBasePlayer *pEntity, bool bChat ); -void UTIL_SayText2Filter( IRecipientFilter& filter, CBasePlayer *pEntity, bool bChat, const char *msg_name, const char *param1 = NULL, const char *param2 = NULL, const char *param3 = NULL, const char *param4 = NULL ); - -byte *UTIL_LoadFileForMe( const char *filename, int *pLength ); -void UTIL_FreeFile( byte *buffer ); - -class CGameTrace; -typedef CGameTrace trace_t; - -//----------------------------------------------------------------------------- -// These are inlined for backwards compatibility -//----------------------------------------------------------------------------- -inline float UTIL_Approach( float target, float value, float speed ) -{ - return Approach( target, value, speed ); -} - -inline float UTIL_ApproachAngle( float target, float value, float speed ) -{ - return ApproachAngle( target, value, speed ); -} - -inline float UTIL_AngleDistance( float next, float cur ) -{ - return AngleDistance( next, cur ); -} - -inline float UTIL_AngleMod(float a) -{ - return anglemod(a); -} - -inline float UTIL_AngleDiff( float destAngle, float srcAngle ) -{ - return AngleDiff( destAngle, srcAngle ); -} - -typedef struct hudtextparms_s -{ - float x; - float y; - int effect; - byte r1, g1, b1, a1; - byte r2, g2, b2, a2; - float fadeinTime; - float fadeoutTime; - float holdTime; - float fxTime; - int channel; -} hudtextparms_t; - - -//----------------------------------------------------------------------------- -// Sets the model to be associated with an entity -//----------------------------------------------------------------------------- -void UTIL_SetModel( CBaseEntity *pEntity, const char *pModelName ); - - -// prints as transparent 'title' to the HUD -void UTIL_HudMessageAll( const hudtextparms_t &textparms, const char *pMessage ); -void UTIL_HudMessage( CBasePlayer *pToPlayer, const hudtextparms_t &textparms, const char *pMessage ); - -// brings up hud keyboard hints display -void UTIL_HudHintText( CBaseEntity *pEntity, const char *pMessage ); - -// Writes message to console with timestamp and FragLog header. -void UTIL_LogPrintf( char *fmt, ... ); - -// Sorta like FInViewCone, but for nonNPCs. -float UTIL_DotPoints ( const Vector &vecSrc, const Vector &vecCheck, const Vector &vecDir ); - -void UTIL_StripToken( const char *pKey, char *pDest );// for redundant keynames - -// Misc functions -int BuildChangeList( levellist_t *pLevelList, int maxList ); - -// computes gravity scale for an absolute gravity. Pass the result into CBaseEntity::SetGravity() -float UTIL_ScaleForGravity( float desiredGravity ); -// -// How did I ever live without ASSERT? -// -#ifdef DEBUG -void DBG_AssertFunction(bool fExpr, const char* szExpr, const char* szFile, int szLine, const char* szMessage); -#define ASSERT(f) DBG_AssertFunction((bool)((f)!=0), #f, __FILE__, __LINE__, NULL) -#define ASSERTSZ(f, sz) DBG_AssertFunction((bool)((f)!=0), #f, __FILE__, __LINE__, sz) -#else // !DEBUG -#define ASSERT(f) -#define ASSERTSZ(f, sz) -#endif // !DEBUG - - -// -// Constants that were used only by QC (maybe not used at all now) -// -// Un-comment only as needed -// - -#include "globals.h" - -#define LFO_SQUARE 1 -#define LFO_TRIANGLE 2 -#define LFO_RANDOM 3 - -// func_rotating -#define SF_BRUSH_ROTATE_Y_AXIS 0 -#define SF_BRUSH_ROTATE_START_ON 1 -#define SF_BRUSH_ROTATE_BACKWARDS 2 -#define SF_BRUSH_ROTATE_Z_AXIS 4 -#define SF_BRUSH_ROTATE_X_AXIS 8 - - -#define SF_BRUSH_ROTATE_SMALLRADIUS 128 -#define SF_BRUSH_ROTATE_MEDIUMRADIUS 256 -#define SF_BRUSH_ROTATE_LARGERADIUS 512 - -#define PUSH_BLOCK_ONLY_X 1 -#define PUSH_BLOCK_ONLY_Y 2 - -#define SF_LIGHT_START_OFF 1 - -#define SPAWNFLAG_NOMESSAGE 1 -#define SPAWNFLAG_NOTOUCH 1 -#define SPAWNFLAG_DROIDONLY 4 - -#define SPAWNFLAG_USEONLY 1 // can't be touched, must be used (buttons) - -#define TELE_PLAYER_ONLY 1 -#define TELE_SILENT 2 - -// Sound Utilities - -enum soundlevel_t; - -void SENTENCEG_Init(); -void SENTENCEG_Stop(edict_t *entity, int isentenceg, int ipick); -int SENTENCEG_PlayRndI(edict_t *entity, int isentenceg, float volume, soundlevel_t soundlevel, int flags, int pitch); -int SENTENCEG_PlayRndSz(edict_t *entity, const char *szrootname, float volume, soundlevel_t soundlevel, int flags, int pitch); -int SENTENCEG_PlaySequentialSz(edict_t *entity, const char *szrootname, float volume, soundlevel_t soundlevel, int flags, int pitch, int ipick, int freset); -void SENTENCEG_PlaySentenceIndex( edict_t *entity, int iSentenceIndex, float volume, soundlevel_t soundlevel, int flags, int pitch ); -int SENTENCEG_PickRndSz(const char *szrootname); -int SENTENCEG_GetIndex(const char *szrootname); -int SENTENCEG_Lookup(const char *sample); - -char TEXTURETYPE_Find( trace_t *ptr ); - -void UTIL_EmitSoundSuit(edict_t *entity, const char *sample); -int UTIL_EmitGroupIDSuit(edict_t *entity, int isentenceg); -int UTIL_EmitGroupnameSuit(edict_t *entity, const char *groupname); -void UTIL_RestartAmbientSounds( void ); - -class EntityMatrix : public VMatrix -{ -public: - void InitFromEntity( CBaseEntity *pEntity, int iAttachment=0 ); - void InitFromEntityLocal( CBaseEntity *entity ); - - inline Vector LocalToWorld( const Vector &vVec ) const - { - return VMul4x3( vVec ); - } - - inline Vector WorldToLocal( const Vector &vVec ) const - { - return VMul4x3Transpose( vVec ); - } - - inline Vector LocalToWorldRotation( const Vector &vVec ) const - { - return VMul3x3( vVec ); - } - - inline Vector WorldToLocalRotation( const Vector &vVec ) const - { - return VMul3x3Transpose( vVec ); - } -}; - -inline float UTIL_DistApprox( const Vector &vec1, const Vector &vec2 ); -inline float UTIL_DistApprox2D( const Vector &vec1, const Vector &vec2 ); - -//--------------------------------------------------------- -//--------------------------------------------------------- -inline float UTIL_DistApprox( const Vector &vec1, const Vector &vec2 ) -{ - float dx; - float dy; - float dz; - - dx = vec1.x - vec2.x; - dy = vec1.y - vec2.y; - dz = vec1.z - vec2.z; - - return fabs(dx) + fabs(dy) + fabs(dz); -} - -//--------------------------------------------------------- -//--------------------------------------------------------- -inline float UTIL_DistApprox2D( const Vector &vec1, const Vector &vec2 ) -{ - float dx; - float dy; - - dx = vec1.x - vec2.x; - dy = vec1.y - vec2.y; - - return fabs(dx) + fabs(dy); -} - -// Find out if an entity is facing another entity or position within a given tolerance range -bool UTIL_IsFacingWithinTolerance( CBaseEntity *pViewer, const Vector &vecPosition, float flDotTolerance, float *pflDot = NULL ); -bool UTIL_IsFacingWithinTolerance( CBaseEntity *pViewer, CBaseEntity *pTarget, float flDotTolerance, float *pflDot = NULL ); - -void UTIL_GetDebugColorForRelationship( int nRelationship, int &r, int &g, int &b ); - -struct datamap_t; -extern const char *UTIL_FunctionToName( datamap_t *pMap, void *function ); -extern void *UTIL_FunctionFromName( datamap_t *pMap, const char *pName ); - -int UTIL_GetCommandClientIndex( void ); -CBasePlayer *UTIL_GetCommandClient( void ); - -AngularImpulse WorldToLocalRotation( const VMatrix &localToWorld, const Vector &worldAxis, float rotation ); - -bool UTIL_LoadAndSpawnEntitiesFromScript( CUtlVector &entities, const char *pScriptFile, const char *pBlock, bool bActivate = true ); - -#endif // UTIL_H +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: Misc utility code. +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef UTIL_H +#define UTIL_H +#ifdef _WIN32 +#pragma once +#endif + +#include "ai_activity.h" +#include "enginecallback.h" +#include "basetypes.h" +#include "tempentity.h" +#include "string_t.h" +#include "gamestringpool.h" +#include "engine/IEngineTrace.h" +#include "worldsize.h" +#include "dt_send.h" +#include "server_class.h" +#include "shake.h" + +#include "vstdlib/random.h" +#include + +#include "utlvector.h" +#include "util_shared.h" +#include "shareddefs.h" +#include "networkvar.h" + +struct levellist_t; +class IServerNetworkable; +class IEntityFactory; + +#ifdef _WIN32 + #define SETUP_EXTERNC(mapClassName)\ + extern "C" _declspec( dllexport ) IServerNetworkable* mapClassName( void ); +#else + #define SETUP_EXTERNC(mapClassName) +#endif + + +#include "tier0/memdbgon.h" + +CBaseEntity *CreateEntityByName(const char *className, int iForceEdictIndex); + +// entity creation +// creates an entity that has not been linked to a classname +template< class T > +T *_CreateEntityTemplate( T *newEnt, const char *className ) +{ + newEnt = new T; // this is the only place 'new' should be used! + newEnt->PostConstructor( className ); + return newEnt; +} + +#include "tier0/memdbgoff.h" + +// creates an entity by name, and ensure it's correctness +// does not spawn the entity +// use the CREATE_ENTITY() macro which wraps this, instead of using it directly +template< class T > +T *_CreateEntity( T *newClass, const char *className ) +{ + T *newEnt = dynamic_cast( CreateEntityByName(className, -1) ); + if ( !newEnt ) + { + Warning( "classname %s used to create wrong class type\n" ); + Assert(0); + } + + return newEnt; +} + +#define CREATE_ENTITY( newClass, className ) _CreateEntity( (newClass*)NULL, className ) +#define CREATE_UNSAVED_ENTITY( newClass, className ) _CreateEntityTemplate( (newClass*)NULL, className ) + + +// This is the glue that hooks .MAP entity class names to our CPP classes +abstract_class IEntityFactoryDictionary +{ +public: + virtual void InstallFactory( IEntityFactory *pFactory, const char *pClassName ) = 0; + virtual IServerNetworkable *Create( const char *pClassName ) = 0; + virtual void Destroy( const char *pClassName, IServerNetworkable *pNetworkable ) = 0; + virtual IEntityFactory *FindFactory( const char *pClassName ) = 0; + virtual const char *GetCannonicalName( const char *pClassName ) = 0; +}; + +IEntityFactoryDictionary *EntityFactoryDictionary(); + +inline bool CanCreateEntityClass( const char *pszClassname ) +{ + return ( EntityFactoryDictionary() != NULL && EntityFactoryDictionary()->FindFactory( pszClassname ) != NULL ); +} + +abstract_class IEntityFactory +{ +public: + virtual IServerNetworkable *Create( const char *pClassName ) = 0; + virtual void Destroy( IServerNetworkable *pNetworkable ) = 0; + virtual size_t GetEntitySize() = 0; +}; + +template +class CEntityFactory : public IEntityFactory +{ +public: + CEntityFactory( const char *pClassName ) + { + EntityFactoryDictionary()->InstallFactory( this, pClassName ); + } + + IServerNetworkable *Create( const char *pClassName ) + { + T* pEnt = _CreateEntityTemplate((T*)NULL, pClassName); + return pEnt->NetworkProp(); + } + + void Destroy( IServerNetworkable *pNetworkable ) + { + if ( pNetworkable ) + { + pNetworkable->Release(); + } + } + + virtual size_t GetEntitySize() + { + return sizeof(T); + } +}; + +#define LINK_ENTITY_TO_CLASS(mapClassName,DLLClassName) \ + static CEntityFactory mapClassName( #mapClassName ); + + +// +// Conversion among the three types of "entity", including identity-conversions. +// +inline int ENTINDEX( edict_t *pEdict) +{ + return engine->IndexOfEdict(pEdict); +} + +int ENTINDEX( CBaseEntity *pEnt ); + +inline edict_t* INDEXENT( int iEdictNum ) +{ + return engine->PEntityOfEntIndex(iEdictNum); +} + +// Testing the three types of "entity" for nullity +inline bool FNullEnt(const edict_t* pent) +{ + return pent == NULL || ENTINDEX((edict_t*)pent) == 0; +} + +// Dot products for view cone checking +#define VIEW_FIELD_FULL (float)-1.0 // +-180 degrees +#define VIEW_FIELD_WIDE (float)-0.7 // +-135 degrees 0.1 // +-85 degrees, used for full FOV checks +#define VIEW_FIELD_NARROW (float)0.7 // +-45 degrees, more narrow check used to set up ranged attacks +#define VIEW_FIELD_ULTRA_NARROW (float)0.9 // +-25 degrees, more narrow check used to set up ranged attacks + +class CBaseEntity; +class CBasePlayer; + +extern CGlobalVars *gpGlobals; + +// Misc useful +inline bool FStrEq(const char *sz1, const char *sz2) +{ + return(stricmp(sz1, sz2) == 0); +} + +#if 0 +// UNDONE: Remove/alter MAKE_STRING so we can do this? +inline bool FStrEq( string_t str1, string_t str2 ) +{ + // now that these are pooled, we can compare them with + // integer equality + return str1 == str2; +} +#endif + +const char *nexttoken(char *token, const char *str, char sep); + +// Misc. Prototypes +void UTIL_SetSize (CBaseEntity *pEnt, const Vector &vecMin, const Vector &vecMax); +void UTIL_ClearTrace ( trace_t &trace ); +void UTIL_SetTrace (trace_t& tr, const Ray_t &ray, edict_t* edict, float fraction, int hitgroup, unsigned int contents, const Vector& normal, float intercept ); + +int UTIL_PrecacheDecal ( const char *name, bool preload = false ); + +//----------------------------------------------------------------------------- + +float UTIL_GetSimulationInterval(); + +//----------------------------------------------------------------------------- +// Purpose: Gets a player pointer by 1-based index +// If player is not yet spawned or connected, returns NULL +// Input : playerIndex - index of the player - first player is index 1 +//----------------------------------------------------------------------------- + +// NOTENOTE: Use UTIL_GetLocalPlayer instead of UTIL_PlayerByIndex IF you're in single player +// and you want the player. +CBasePlayer *UTIL_PlayerByIndex( int playerIndex ); + +// NOTENOTE: Use this instead of UTIL_PlayerByIndex IF you're in single player +// and you want the player. +// not useable in multiplayer - see UTIL_GetListenServerHost() +CBasePlayer* UTIL_GetLocalPlayer( void ); + +// get the local player on a listen server +CBasePlayer *UTIL_GetListenServerHost( void ); + +CBasePlayer* UTIL_PlayerByUserId( int userID ); +CBasePlayer* UTIL_PlayerByName( const char *name ); // not case sensitive + +// Returns true if the command was issued by the listenserver host, or by the dedicated server, via rcon or the server console. +// This is valid during ConCommand execution. +bool UTIL_IsCommandIssuedByServerAdmin( void ); + +CBaseEntity* UTIL_EntityByIndex( int entityIndex ); + +void UTIL_GetPlayerConnectionInfo( int playerIndex, int& ping, int &packetloss ); + +void UTIL_SetClientVisibilityPVS( edict_t *pClient, const unsigned char *pvs, int pvssize ); +bool UTIL_ClientPVSIsExpanded(); + +edict_t *UTIL_FindClientInPVS( edict_t *pEdict ); +edict_t *UTIL_FindClientInVisibilityPVS( edict_t *pEdict ); + +// This is a version which finds any clients whose PVS intersects the box +CBaseEntity *UTIL_FindClientInPVS( const Vector &vecBoxMins, const Vector &vecBoxMaxs ); + +CBaseEntity *UTIL_EntitiesInPVS( CBaseEntity *pPVSEntity, CBaseEntity *pStartingEntity ); + +//----------------------------------------------------------------------------- +// class CFlaggedEntitiesEnum +//----------------------------------------------------------------------------- +// enumerate entities that match a set of edict flags into a static array +class CFlaggedEntitiesEnum : public IPartitionEnumerator +{ +public: + CFlaggedEntitiesEnum( CBaseEntity **pList, int listMax, int flagMask ); + + // This gets called by the enumeration methods with each element + // that passes the test. + virtual IterationRetval_t EnumElement( IHandleEntity *pHandleEntity ); + + int GetCount() { return m_count; } + bool AddToList( CBaseEntity *pEntity ); + +private: + CBaseEntity **m_pList; + int m_listMax; + int m_flagMask; + int m_count; +}; + +// Pass in an array of pointers and an array size, it fills the array and returns the number inserted +int UTIL_EntitiesInBox( const Vector &mins, const Vector &maxs, CFlaggedEntitiesEnum *pEnum ); +int UTIL_EntitiesAlongRay( const Ray_t &ray, CFlaggedEntitiesEnum *pEnum ); +int UTIL_EntitiesInSphere( const Vector ¢er, float radius, CFlaggedEntitiesEnum *pEnum ); + +inline int UTIL_EntitiesInBox( CBaseEntity **pList, int listMax, const Vector &mins, const Vector &maxs, int flagMask ) +{ + CFlaggedEntitiesEnum boxEnum( pList, listMax, flagMask ); + return UTIL_EntitiesInBox( mins, maxs, &boxEnum ); +} + +inline int UTIL_EntitiesAlongRay( CBaseEntity **pList, int listMax, const Ray_t &ray, int flagMask ) +{ + CFlaggedEntitiesEnum rayEnum( pList, listMax, flagMask ); + return UTIL_EntitiesAlongRay( ray, &rayEnum ); +} + +inline int UTIL_EntitiesInSphere( CBaseEntity **pList, int listMax, const Vector ¢er, float radius, int flagMask ) +{ + CFlaggedEntitiesEnum sphereEnum( pList, listMax, flagMask ); + return UTIL_EntitiesInSphere( center, radius, &sphereEnum ); +} + +// marks the entity for deletion so it will get removed next frame +void UTIL_Remove( IServerNetworkable *oldObj ); +void UTIL_Remove( CBaseEntity *oldObj ); + +// deletes an entity, without any delay. Only use this when sure no pointers rely on this entity. +void UTIL_DisableRemoveImmediate(); +void UTIL_EnableRemoveImmediate(); +void UTIL_RemoveImmediate( CBaseEntity *oldObj ); + +// make this a fixed size so it just sits on the stack +#define MAX_SPHERE_QUERY 512 +class CEntitySphereQuery +{ +public: + // currently this builds the list in the constructor + // UNDONE: make an iterative query of ISpatialPartition so we could + // make queries like this optimal + CEntitySphereQuery( const Vector ¢er, float radius, int flagMask=0 ); + CBaseEntity *GetCurrentEntity(); + inline void NextEntity() { m_listIndex++; } + +private: + int m_listIndex; + int m_listCount; + CBaseEntity *m_pList[MAX_SPHERE_QUERY]; +}; + +enum soundlevel_t; + +// Drops an entity onto the floor +int UTIL_DropToFloor( CBaseEntity *pEntity, unsigned int mask ); + +// Returns false if any part of the bottom of the entity is off an edge that is not a staircase. +bool UTIL_CheckBottom( CBaseEntity *pEntity, ITraceFilter *pTraceFilter, float flStepSize ); + +void UTIL_SetOrigin ( CBaseEntity *entity, const Vector &vecOrigin, bool bFireTriggers = false ); +void UTIL_EmitAmbientSound ( int entindex, const Vector &vecOrigin, const char *samp, float vol, soundlevel_t soundlevel, int fFlags, int pitch, float soundtime = 0.0f, float *duration = NULL ); +void UTIL_ParticleEffect ( const Vector &vecOrigin, const Vector &vecDirection, ULONG ulColor, ULONG ulCount ); +void UTIL_ScreenShake ( const Vector ¢er, float amplitude, float frequency, float duration, float radius, ShakeCommand_t eCommand, bool bAirShake=false ); +void UTIL_ScreenShakeObject ( CBaseEntity *pEnt, const Vector ¢er, float amplitude, float frequency, float duration, float radius, ShakeCommand_t eCommand, bool bAirShake=false ); +void UTIL_ViewPunch ( const Vector ¢er, QAngle angPunch, float radius, bool bInAir ); +void UTIL_ShowMessage ( const char *pString, CBasePlayer *pPlayer ); +void UTIL_ShowMessageAll ( const char *pString ); +void UTIL_ScreenFadeAll ( const color32 &color, float fadeTime, float holdTime, int flags ); +void UTIL_ScreenFade ( CBaseEntity *pEntity, const color32 &color, float fadeTime, float fadeHold, int flags ); +void UTIL_MuzzleFlash ( const Vector &origin, const QAngle &angles, int scale, int type ); +Vector UTIL_PointOnLineNearestPoint(const Vector& vStartPos, const Vector& vEndPos, const Vector& vPoint, bool clampEnds = false ); + +int UTIL_EntityInSolid( CBaseEntity *ent ); + +bool UTIL_IsMasterTriggered (string_t sMaster, CBaseEntity *pActivator); +void UTIL_BloodStream( const Vector &origin, const Vector &direction, int color, int amount ); +void UTIL_BloodSpray( const Vector &pos, const Vector &dir, int color, int amount, int flags ); +Vector UTIL_RandomBloodVector( void ); +void UTIL_ImpactTrace( trace_t *pTrace, int iDamageType, char *pCustomImpactName = NULL ); +void UTIL_PlayerDecalTrace( trace_t *pTrace, int playernum ); +void UTIL_Smoke( const Vector &origin, const float scale, const float framerate ); +void UTIL_AxisStringToPointDir( Vector &start, Vector &dir, const char *pString ); +void UTIL_AxisStringToPointPoint( Vector &start, Vector &end, const char *pString ); +void UTIL_AxisStringToUnitDir( Vector &dir, const char *pString ); +void UTIL_ClipPunchAngleOffset( QAngle &in, const QAngle &punch, const QAngle &clip ); +void UTIL_PredictedPosition( CBaseEntity *pTarget, float flTimeDelta, Vector *vecPredictedPosition ); +void UTIL_Beam( Vector &Start, Vector &End, int nModelIndex, int nHaloIndex, unsigned char FrameStart, unsigned char FrameRate, + float Life, unsigned char Width, unsigned char EndWidth, unsigned char FadeLength, unsigned char Noise, unsigned char Red, unsigned char Green, + unsigned char Blue, unsigned char Brightness, unsigned char Speed); + +char *UTIL_VarArgs( char *format, ... ); +bool UTIL_IsValidEntity( CBaseEntity *pEnt ); +bool UTIL_TeamsMatch( const char *pTeamName1, const char *pTeamName2 ); + +// snaps a vector to the nearest axis vector (if within epsilon) +void UTIL_SnapDirectionToAxis( Vector &direction, float epsilon = 0.01f ); + +//Set the entity to point at the target specified +bool UTIL_PointAtEntity( CBaseEntity *pEnt, CBaseEntity *pTarget ); +void UTIL_PointAtNamedEntity( CBaseEntity *pEnt, string_t strTarget ); + +// Search for water transition along a vertical line +float UTIL_WaterLevel( const Vector &position, float minz, float maxz ); + +// Like UTIL_WaterLevel, but *way* less expensive. +// I didn't replace UTIL_WaterLevel everywhere to avoid breaking anything. +float UTIL_FindWaterSurface( const Vector &position, float minz, float maxz ); + +void UTIL_Bubbles( const Vector& mins, const Vector& maxs, int count ); +void UTIL_BubbleTrail( const Vector& from, const Vector& to, int count ); + +// allows precacheing of other entities +void UTIL_PrecacheOther( const char *szClassname, const char *modelName = NULL ); + +// prints a message to each client +void UTIL_ClientPrintAll( int msg_dest, const char *msg_name, const char *param1 = NULL, const char *param2 = NULL, const char *param3 = NULL, const char *param4 = NULL ); +inline void UTIL_CenterPrintAll( const char *msg_name, const char *param1 = NULL, const char *param2 = NULL, const char *param3 = NULL, const char *param4 = NULL ) +{ + UTIL_ClientPrintAll( HUD_PRINTCENTER, msg_name, param1, param2, param3, param4 ); +} + +void UTIL_ValidateSoundName( string_t &name, const char *defaultStr ); + +void UTIL_ClientPrintFilter( IRecipientFilter& filter, int msg_dest, const char *msg_name, const char *param1 = NULL, const char *param2 = NULL, const char *param3 = NULL, const char *param4 = NULL ); + +// prints messages through the HUD +void ClientPrint( CBasePlayer *player, int msg_dest, const char *msg_name, const char *param1 = NULL, const char *param2 = NULL, const char *param3 = NULL, const char *param4 = NULL ); + +// prints a message to the HUD say (chat) +void UTIL_SayText( const char *pText, CBasePlayer *pEntity ); +void UTIL_SayTextAll( const char *pText, CBasePlayer *pEntity = NULL, bool bChat = false ); +void UTIL_SayTextFilter( IRecipientFilter& filter, const char *pText, CBasePlayer *pEntity, bool bChat ); +void UTIL_SayText2Filter( IRecipientFilter& filter, CBasePlayer *pEntity, bool bChat, const char *msg_name, const char *param1 = NULL, const char *param2 = NULL, const char *param3 = NULL, const char *param4 = NULL ); + +byte *UTIL_LoadFileForMe( const char *filename, int *pLength ); +void UTIL_FreeFile( byte *buffer ); + +class CGameTrace; +typedef CGameTrace trace_t; + +//----------------------------------------------------------------------------- +// These are inlined for backwards compatibility +//----------------------------------------------------------------------------- +inline float UTIL_Approach( float target, float value, float speed ) +{ + return Approach( target, value, speed ); +} + +inline float UTIL_ApproachAngle( float target, float value, float speed ) +{ + return ApproachAngle( target, value, speed ); +} + +inline float UTIL_AngleDistance( float next, float cur ) +{ + return AngleDistance( next, cur ); +} + +inline float UTIL_AngleMod(float a) +{ + return anglemod(a); +} + +inline float UTIL_AngleDiff( float destAngle, float srcAngle ) +{ + return AngleDiff( destAngle, srcAngle ); +} + +typedef struct hudtextparms_s +{ + float x; + float y; + int effect; + byte r1, g1, b1, a1; + byte r2, g2, b2, a2; + float fadeinTime; + float fadeoutTime; + float holdTime; + float fxTime; + int channel; +} hudtextparms_t; + + +//----------------------------------------------------------------------------- +// Sets the model to be associated with an entity +//----------------------------------------------------------------------------- +void UTIL_SetModel( CBaseEntity *pEntity, const char *pModelName ); + + +// prints as transparent 'title' to the HUD +void UTIL_HudMessageAll( const hudtextparms_t &textparms, const char *pMessage ); +void UTIL_HudMessage( CBasePlayer *pToPlayer, const hudtextparms_t &textparms, const char *pMessage ); + +// brings up hud keyboard hints display +void UTIL_HudHintText( CBaseEntity *pEntity, const char *pMessage ); + +// Writes message to console with timestamp and FragLog header. +void UTIL_LogPrintf( char *fmt, ... ); + +// Sorta like FInViewCone, but for nonNPCs. +float UTIL_DotPoints ( const Vector &vecSrc, const Vector &vecCheck, const Vector &vecDir ); + +void UTIL_StripToken( const char *pKey, char *pDest );// for redundant keynames + +// Misc functions +int BuildChangeList( levellist_t *pLevelList, int maxList ); + +// computes gravity scale for an absolute gravity. Pass the result into CBaseEntity::SetGravity() +float UTIL_ScaleForGravity( float desiredGravity ); +// +// How did I ever live without ASSERT? +// +#ifdef DEBUG +void DBG_AssertFunction(bool fExpr, const char* szExpr, const char* szFile, int szLine, const char* szMessage); +#define ASSERT(f) DBG_AssertFunction((bool)((f)!=0), #f, __FILE__, __LINE__, NULL) +#define ASSERTSZ(f, sz) DBG_AssertFunction((bool)((f)!=0), #f, __FILE__, __LINE__, sz) +#else // !DEBUG +#define ASSERT(f) +#define ASSERTSZ(f, sz) +#endif // !DEBUG + + +// +// Constants that were used only by QC (maybe not used at all now) +// +// Un-comment only as needed +// + +#include "globals.h" + +#define LFO_SQUARE 1 +#define LFO_TRIANGLE 2 +#define LFO_RANDOM 3 + +// func_rotating +#define SF_BRUSH_ROTATE_Y_AXIS 0 +#define SF_BRUSH_ROTATE_START_ON 1 +#define SF_BRUSH_ROTATE_BACKWARDS 2 +#define SF_BRUSH_ROTATE_Z_AXIS 4 +#define SF_BRUSH_ROTATE_X_AXIS 8 + + +#define SF_BRUSH_ROTATE_SMALLRADIUS 128 +#define SF_BRUSH_ROTATE_MEDIUMRADIUS 256 +#define SF_BRUSH_ROTATE_LARGERADIUS 512 + +#define PUSH_BLOCK_ONLY_X 1 +#define PUSH_BLOCK_ONLY_Y 2 + +#define SF_LIGHT_START_OFF 1 + +#define SPAWNFLAG_NOMESSAGE 1 +#define SPAWNFLAG_NOTOUCH 1 +#define SPAWNFLAG_DROIDONLY 4 + +#define SPAWNFLAG_USEONLY 1 // can't be touched, must be used (buttons) + +#define TELE_PLAYER_ONLY 1 +#define TELE_SILENT 2 + +// Sound Utilities + +enum soundlevel_t; + +void SENTENCEG_Init(); +void SENTENCEG_Stop(edict_t *entity, int isentenceg, int ipick); +int SENTENCEG_PlayRndI(edict_t *entity, int isentenceg, float volume, soundlevel_t soundlevel, int flags, int pitch); +int SENTENCEG_PlayRndSz(edict_t *entity, const char *szrootname, float volume, soundlevel_t soundlevel, int flags, int pitch); +int SENTENCEG_PlaySequentialSz(edict_t *entity, const char *szrootname, float volume, soundlevel_t soundlevel, int flags, int pitch, int ipick, int freset); +void SENTENCEG_PlaySentenceIndex( edict_t *entity, int iSentenceIndex, float volume, soundlevel_t soundlevel, int flags, int pitch ); +int SENTENCEG_PickRndSz(const char *szrootname); +int SENTENCEG_GetIndex(const char *szrootname); +int SENTENCEG_Lookup(const char *sample); + +char TEXTURETYPE_Find( trace_t *ptr ); + +void UTIL_EmitSoundSuit(edict_t *entity, const char *sample); +int UTIL_EmitGroupIDSuit(edict_t *entity, int isentenceg); +int UTIL_EmitGroupnameSuit(edict_t *entity, const char *groupname); +void UTIL_RestartAmbientSounds( void ); + +class EntityMatrix : public VMatrix +{ +public: + void InitFromEntity( CBaseEntity *pEntity, int iAttachment=0 ); + void InitFromEntityLocal( CBaseEntity *entity ); + + inline Vector LocalToWorld( const Vector &vVec ) const + { + return VMul4x3( vVec ); + } + + inline Vector WorldToLocal( const Vector &vVec ) const + { + return VMul4x3Transpose( vVec ); + } + + inline Vector LocalToWorldRotation( const Vector &vVec ) const + { + return VMul3x3( vVec ); + } + + inline Vector WorldToLocalRotation( const Vector &vVec ) const + { + return VMul3x3Transpose( vVec ); + } +}; + +inline float UTIL_DistApprox( const Vector &vec1, const Vector &vec2 ); +inline float UTIL_DistApprox2D( const Vector &vec1, const Vector &vec2 ); + +//--------------------------------------------------------- +//--------------------------------------------------------- +inline float UTIL_DistApprox( const Vector &vec1, const Vector &vec2 ) +{ + float dx; + float dy; + float dz; + + dx = vec1.x - vec2.x; + dy = vec1.y - vec2.y; + dz = vec1.z - vec2.z; + + return fabs(dx) + fabs(dy) + fabs(dz); +} + +//--------------------------------------------------------- +//--------------------------------------------------------- +inline float UTIL_DistApprox2D( const Vector &vec1, const Vector &vec2 ) +{ + float dx; + float dy; + + dx = vec1.x - vec2.x; + dy = vec1.y - vec2.y; + + return fabs(dx) + fabs(dy); +} + +// Find out if an entity is facing another entity or position within a given tolerance range +bool UTIL_IsFacingWithinTolerance( CBaseEntity *pViewer, const Vector &vecPosition, float flDotTolerance, float *pflDot = NULL ); +bool UTIL_IsFacingWithinTolerance( CBaseEntity *pViewer, CBaseEntity *pTarget, float flDotTolerance, float *pflDot = NULL ); + +void UTIL_GetDebugColorForRelationship( int nRelationship, int &r, int &g, int &b ); + +struct datamap_t; +extern const char *UTIL_FunctionToName( datamap_t *pMap, void *function ); +extern void *UTIL_FunctionFromName( datamap_t *pMap, const char *pName ); + +int UTIL_GetCommandClientIndex( void ); +CBasePlayer *UTIL_GetCommandClient( void ); + +AngularImpulse WorldToLocalRotation( const VMatrix &localToWorld, const Vector &worldAxis, float rotation ); + +bool UTIL_LoadAndSpawnEntitiesFromScript( CUtlVector &entities, const char *pScriptFile, const char *pBlock, bool bActivate = true ); + +#endif // UTIL_H diff --git a/dlls/variant_t.h b/dlls/variant_t.h index 0add8063..024410a8 100644 --- a/dlls/variant_t.h +++ b/dlls/variant_t.h @@ -22,7 +22,7 @@ class CBaseEntity; // A variant class for passing data in entity input/output connections. // class variant_t -{ +{ union { bool bVal; @@ -33,13 +33,13 @@ class variant_t color32 rgbaVal; }; CHandle eVal; // this can't be in the union because it has a constructor. - + fieldtype_t fieldType; - + public: // constructor - variant_t() : fieldType(FIELD_VOID), iVal(0) {} + variant_t() : iVal(0), fieldType(FIELD_VOID) {} inline bool Bool( void ) const { return( fieldType == FIELD_BOOLEAN ) ? bVal : false; } inline const char *String( void ) const { return( fieldType == FIELD_STRING ) ? STRING(iszVal) : ToString(); } diff --git a/dlls/vehicle_base.cpp b/dlls/vehicle_base.cpp index bec6c0fb..9779e0d2 100644 --- a/dlls/vehicle_base.cpp +++ b/dlls/vehicle_base.cpp @@ -63,13 +63,16 @@ LINK_ENTITY_TO_CLASS( prop_vehicle, CPropVehicle ); //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- +#ifdef _MSC_VER #pragma warning (disable:4355) +#endif CPropVehicle::CPropVehicle() : m_VehiclePhysics( this ) { SetVehicleType( VEHICLE_TYPE_CAR_WHEELS ); } +#ifdef _MSC_VER #pragma warning (default:4355) - +#endif //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- diff --git a/dlls/vehicle_baseserver.cpp b/dlls/vehicle_baseserver.cpp index 3ef319d0..e6ff440b 100644 --- a/dlls/vehicle_baseserver.cpp +++ b/dlls/vehicle_baseserver.cpp @@ -834,7 +834,7 @@ void CBaseServerVehicle::ParseNPCRoles( KeyValues *pkvPassengerList ) Msg("Passenger Roles Parsed:\t%d\n\n", m_PassengerRoles.Count() ); for ( int i = 0; i < m_PassengerRoles.Count(); i++ ) { - Msg("\tPassenger Role:\t%s (%d seats)\n", m_PassengerRoles[i].m_strName, m_PassengerRoles[i].m_PassengerSeats.Count() ); + Msg("\tPassenger Role:\t%s (%d seats)\n", STRING(m_PassengerRoles[i].m_strName), m_PassengerRoles[i].m_PassengerSeats.Count() ); // Iterate through all information sets under this name for ( int j = 0; j < m_PassengerRoles[i].m_PassengerSeats.Count(); j++ ) @@ -847,7 +847,7 @@ void CBaseServerVehicle::ParseNPCRoles( KeyValues *pkvPassengerList ) for ( int nEntry = 0; nEntry < m_PassengerRoles[i].m_PassengerSeats[j].m_EntryTransitions.Count(); nEntry++ ) { - Msg("\t\t\tAnimation:\t%s\t(Priority %d)\n", m_PassengerRoles[i].m_PassengerSeats[j].m_EntryTransitions[nEntry].m_strAnimationName, + Msg("\t\t\tAnimation:\t%s\t(Priority %d)\n", STRING(m_PassengerRoles[i].m_PassengerSeats[j].m_EntryTransitions[nEntry].m_strAnimationName), m_PassengerRoles[i].m_PassengerSeats[j].m_EntryTransitions[nEntry].m_nPriority ); } @@ -859,7 +859,7 @@ void CBaseServerVehicle::ParseNPCRoles( KeyValues *pkvPassengerList ) for ( int nExits = 0; nExits < m_PassengerRoles[i].m_PassengerSeats[j].m_ExitTransitions.Count(); nExits++ ) { - Msg("\t\t\tAnimation:\t%s\t(Priority %d)\n", m_PassengerRoles[i].m_PassengerSeats[j].m_ExitTransitions[nExits].m_strAnimationName, + Msg("\t\t\tAnimation:\t%s\t(Priority %d)\n", STRING(m_PassengerRoles[i].m_PassengerSeats[j].m_ExitTransitions[nExits].m_strAnimationName), m_PassengerRoles[i].m_PassengerSeats[j].m_ExitTransitions[nExits].m_nPriority ); } } @@ -1609,7 +1609,7 @@ static int SoundStateIndexFromName( const char *pName ) { for ( int i = 0; i < SS_NUM_STATES; i++ ) { - Assert( i < ARRAYSIZE(pSoundStateNames) ); + Assert( i < static_cast(ARRAYSIZE(pSoundStateNames)) ); if ( !strcmpi( pSoundStateNames[i], pName ) ) return i; } @@ -1797,6 +1797,8 @@ sound_states CBaseServerVehicle::SoundState_ChooseState( vbs_sound_update_t &par case SS_SHUTDOWN: case SS_SHUTDOWN_WATER: return m_soundState; + default: + break; } return SS_SHUTDOWN; } @@ -1851,6 +1853,8 @@ sound_states CBaseServerVehicle::SoundState_ChooseState( vbs_sound_update_t &par if ( CheckCrash(params) ) return SS_IDLE; break; + default: + break; } switch( m_soundState ) @@ -2080,6 +2084,8 @@ void CBaseServerVehicle::SoundStartDisabled() case SS_START_WATER: PlaySound( StateSoundName(newState) ); break; + default: + break; } } diff --git a/dlls/vehicle_choreo_generic.cpp b/dlls/vehicle_choreo_generic.cpp index 47bda041..d39d3566 100644 --- a/dlls/vehicle_choreo_generic.cpp +++ b/dlls/vehicle_choreo_generic.cpp @@ -1,918 +1,917 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -//=============================================================================// - -#include "cbase.h" -#include "npcevent.h" -#include "vehicle_base.h" -#include "engine/IEngineSound.h" -#include "in_buttons.h" -#include "soundenvelope.h" -#include "soundent.h" -#include "physics_saverestore.h" -#include "vphysics/constraints.h" -#include "vcollide_parse.h" -#include "ndebugoverlay.h" -#include "hl2_player.h" -#include "props.h" -#include "vehicle_choreo_generic_shared.h" - -// memdbgon must be the last include file in a .cpp file!!! -#include "tier0/memdbgon.h" - -#define VEHICLE_HITBOX_DRIVER 1 - -#define CHOREO_VEHICLE_VIEW_FOV 90 -#define CHOREO_VEHICLE_VIEW_YAW_MIN -60 -#define CHOREO_VEHICLE_VIEW_YAW_MAX 60 -#define CHOREO_VEHICLE_VIEW_PITCH_MIN -90 -#define CHOREO_VEHICLE_VIEW_PITCH_MAX 38 - -BEGIN_DATADESC_NO_BASE( vehicleview_t ) - DEFINE_FIELD( bClampEyeAngles, FIELD_BOOLEAN ), - DEFINE_FIELD( flPitchCurveZero, FIELD_FLOAT ), - DEFINE_FIELD( flPitchCurveLinear, FIELD_FLOAT ), - DEFINE_FIELD( flRollCurveZero, FIELD_FLOAT ), - DEFINE_FIELD( flRollCurveLinear, FIELD_FLOAT ), - DEFINE_FIELD( flFOV, FIELD_FLOAT ), - DEFINE_FIELD( flYawMin, FIELD_FLOAT ), - DEFINE_FIELD( flYawMax, FIELD_FLOAT ), - DEFINE_FIELD( flPitchMin, FIELD_FLOAT ), - DEFINE_FIELD( flPitchMax, FIELD_FLOAT ), -END_DATADESC() - -// -// Anim events. -// -enum -{ - AE_CHOREO_VEHICLE_OPEN = 1, - AE_CHOREO_VEHICLE_CLOSE = 2, -}; - - -extern ConVar g_debug_vehicledriver; - - -class CPropVehicleChoreoGeneric; - -static const char *pChoreoGenericFollowerBoneNames[] = -{ - "base", -}; - - -//----------------------------------------------------------------------------- -// Purpose: A KeyValues parse for vehicle sound blocks -//----------------------------------------------------------------------------- -class CVehicleChoreoViewParser : public IVPhysicsKeyHandler -{ -public: - CVehicleChoreoViewParser( void ); - - void ParseVehicleSounds( const char *pScriptName, vehiclesounds_t *pSounds ); - -private: - virtual void ParseKeyValue( void *pData, const char *pKey, const char *pValue ); - virtual void SetDefaults( void *pData ); -}; - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -class CChoreoGenericServerVehicle : public CBaseServerVehicle -{ - typedef CBaseServerVehicle BaseClass; - -// IServerVehicle -public: - void GetVehicleViewPosition( int nRole, Vector *pAbsOrigin, QAngle *pAbsAngles ); - virtual void ItemPostFrame( CBasePlayer *pPlayer ); - -protected: - - CPropVehicleChoreoGeneric *GetVehicle( void ); -}; - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -class CPropVehicleChoreoGeneric : public CPhysicsProp, public IDrivableVehicle -{ - DECLARE_CLASS( CPropVehicleChoreoGeneric, CPhysicsProp ); - -public: - DECLARE_DATADESC(); - DECLARE_SERVERCLASS(); - - CPropVehicleChoreoGeneric( void ) - { - m_ServerVehicle.SetVehicle( this ); - m_bIgnoreMoveParent = false; - } - - ~CPropVehicleChoreoGeneric( void ) - { - } - - // CBaseEntity - virtual void Precache( void ); - void Spawn( void ); - void Think(void); - virtual int ObjectCaps( void ) { return BaseClass::ObjectCaps() | FCAP_IMPULSE_USE; }; - virtual void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); - virtual void DrawDebugGeometryOverlays( void ); - - virtual Vector BodyTarget( const Vector &posSrc, bool bNoisy = true ); - virtual void TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr ); - virtual int OnTakeDamage( const CTakeDamageInfo &info ); - - void PlayerControlInit( CBasePlayer *pPlayer ); - void PlayerControlShutdown( void ); - void ResetUseKey( CBasePlayer *pPlayer ); - - virtual bool OverridePropdata() { return true; } - - bool ParseViewParams( const char *pScriptName ); - - void GetVectors(Vector* pForward, Vector* pRight, Vector* pUp) const; - - bool CreateVPhysics() - { - SetSolid(SOLID_VPHYSICS); - SetMoveType(MOVETYPE_NONE); - return true; - } - bool ShouldForceExit() { return m_bForcedExit; } - void ClearForcedExit() { m_bForcedExit = false; } - - // CBaseAnimating - void HandleAnimEvent( animevent_t *pEvent ); - - // Inputs - void InputEnterVehicleImmediate( inputdata_t &inputdata ); - void InputEnterVehicle( inputdata_t &inputdata ); - void InputExitVehicle( inputdata_t &inputdata ); - void InputLock( inputdata_t &inputdata ); - void InputUnlock( inputdata_t &inputdata ); - void InputOpen( inputdata_t &inputdata ); - void InputClose( inputdata_t &inputdata ); - - bool ShouldIgnoreParent( void ) { return m_bIgnoreMoveParent; } - - CNetworkHandle( CBasePlayer, m_hPlayer ); - - CNetworkVarEmbedded( vehicleview_t, m_vehicleView ); - -// IDrivableVehicle -public: - - virtual CBaseEntity *GetDriver( void ); - virtual void ProcessMovement( CBasePlayer *pPlayer, CMoveData *pMoveData ) { return; } - virtual void FinishMove( CBasePlayer *player, CUserCmd *ucmd, CMoveData *move ) { return; } - virtual bool CanEnterVehicle( CBaseEntity *pEntity ); - virtual bool CanExitVehicle( CBaseEntity *pEntity ); - virtual void SetVehicleEntryAnim( bool bOn ); - virtual void SetVehicleExitAnim( bool bOn, Vector vecEyeExitEndpoint ) { m_bExitAnimOn = bOn; if ( bOn ) m_vecEyeExitEndpoint = vecEyeExitEndpoint; } - virtual void EnterVehicle( CBaseCombatCharacter *pPassenger ); - - virtual bool AllowBlockedExit( CBaseCombatCharacter *pPassenger, int nRole ) { return true; } - virtual bool AllowMidairExit( CBaseCombatCharacter *pPassenger, int nRole ) { return true; } - virtual void PreExitVehicle( CBaseCombatCharacter *pPassenger, int nRole ) {} - virtual void ExitVehicle( int nRole ); - - virtual void ItemPostFrame( CBasePlayer *pPlayer ) {} - virtual void SetupMove( CBasePlayer *player, CUserCmd *ucmd, IMoveHelper *pHelper, CMoveData *move ) {} - - // If this is a vehicle, returns the vehicle interface - virtual IServerVehicle *GetServerVehicle() { return &m_ServerVehicle; } - - bool ShouldCollide( int collisionGroup, int contentsMask ) const; - -protected: - - // Contained IServerVehicle - CChoreoGenericServerVehicle m_ServerVehicle; - -private: - - // Entering / Exiting - bool m_bLocked; - CNetworkVar( bool, m_bEnterAnimOn ); - CNetworkVar( bool, m_bExitAnimOn ); - CNetworkVector( m_vecEyeExitEndpoint ); - bool m_bForcedExit; - bool m_bIgnoreMoveParent; - bool m_bIgnorePlayerCollisions; - - // Vehicle script filename - string_t m_vehicleScript; - - COutputEvent m_playerOn; - COutputEvent m_playerOff; - COutputEvent m_OnOpen; - COutputEvent m_OnClose; -}; - -LINK_ENTITY_TO_CLASS( prop_vehicle_choreo_generic, CPropVehicleChoreoGeneric ); - -BEGIN_DATADESC( CPropVehicleChoreoGeneric ) - - // Inputs - DEFINE_INPUTFUNC( FIELD_VOID, "Lock", InputLock ), - DEFINE_INPUTFUNC( FIELD_VOID, "Unlock", InputUnlock ), - DEFINE_INPUTFUNC( FIELD_VOID, "EnterVehicle", InputEnterVehicle ), - DEFINE_INPUTFUNC( FIELD_VOID, "EnterVehicleImmediate", InputEnterVehicleImmediate ), - DEFINE_INPUTFUNC( FIELD_VOID, "ExitVehicle", InputExitVehicle ), - DEFINE_INPUTFUNC( FIELD_VOID, "Open", InputOpen ), - DEFINE_INPUTFUNC( FIELD_VOID, "Close", InputClose ), - - // Keys - DEFINE_EMBEDDED( m_ServerVehicle ), - - DEFINE_FIELD( m_hPlayer, FIELD_EHANDLE ), - DEFINE_FIELD( m_bEnterAnimOn, FIELD_BOOLEAN ), - DEFINE_FIELD( m_bExitAnimOn, FIELD_BOOLEAN ), - DEFINE_FIELD( m_bForcedExit, FIELD_BOOLEAN ), - DEFINE_FIELD( m_vecEyeExitEndpoint, FIELD_POSITION_VECTOR ), - - DEFINE_KEYFIELD( m_vehicleScript, FIELD_STRING, "vehiclescript" ), - DEFINE_KEYFIELD( m_bLocked, FIELD_BOOLEAN, "vehiclelocked" ), - - DEFINE_KEYFIELD( m_bIgnoreMoveParent, FIELD_BOOLEAN, "ignoremoveparent" ), - DEFINE_KEYFIELD( m_bIgnorePlayerCollisions, FIELD_BOOLEAN, "ignoreplayer" ), - - DEFINE_OUTPUT( m_playerOn, "PlayerOn" ), - DEFINE_OUTPUT( m_playerOff, "PlayerOff" ), - DEFINE_OUTPUT( m_OnOpen, "OnOpen" ), - DEFINE_OUTPUT( m_OnClose, "OnClose" ), - - DEFINE_EMBEDDED( m_vehicleView ), - -END_DATADESC() - -IMPLEMENT_SERVERCLASS_ST(CPropVehicleChoreoGeneric, DT_PropVehicleChoreoGeneric) - SendPropEHandle(SENDINFO(m_hPlayer)), - SendPropBool(SENDINFO(m_bEnterAnimOn)), - SendPropBool(SENDINFO(m_bExitAnimOn)), - SendPropVector(SENDINFO(m_vecEyeExitEndpoint), -1, SPROP_COORD), - SendPropBool( SENDINFO_STRUCTELEM( m_vehicleView.bClampEyeAngles ) ), - SendPropFloat( SENDINFO_STRUCTELEM( m_vehicleView.flPitchCurveZero ) ), - SendPropFloat( SENDINFO_STRUCTELEM( m_vehicleView.flPitchCurveLinear ) ), - SendPropFloat( SENDINFO_STRUCTELEM( m_vehicleView.flRollCurveZero ) ), - SendPropFloat( SENDINFO_STRUCTELEM( m_vehicleView.flRollCurveLinear ) ), - SendPropFloat( SENDINFO_STRUCTELEM( m_vehicleView.flFOV ) ), - SendPropFloat( SENDINFO_STRUCTELEM( m_vehicleView.flYawMin ) ), - SendPropFloat( SENDINFO_STRUCTELEM( m_vehicleView.flYawMax ) ), - SendPropFloat( SENDINFO_STRUCTELEM( m_vehicleView.flPitchMin ) ), - SendPropFloat( SENDINFO_STRUCTELEM( m_vehicleView.flPitchMax ) ), -END_SEND_TABLE(); - - -bool ShouldVehicleIgnoreEntity( CBaseEntity *pVehicle, CBaseEntity *pCollide ) -{ - CPropVehicleChoreoGeneric *pChoreoVehicle = dynamic_cast ( pVehicle ); - - if ( pChoreoVehicle == NULL ) - return false; - - if ( pCollide == NULL ) - return false; - - if ( pChoreoVehicle->ShouldIgnoreParent() == false ) - return false; - - if ( pChoreoVehicle->GetMoveParent() == pCollide ) - return true; - - return false; -} - - -//------------------------------------------------ -// Precache -//------------------------------------------------ -void CPropVehicleChoreoGeneric::Precache( void ) -{ - BaseClass::Precache(); - - m_ServerVehicle.Initialize( STRING(m_vehicleScript) ); -} - - -//------------------------------------------------ -// Spawn -//------------------------------------------------ -void CPropVehicleChoreoGeneric::Spawn( void ) -{ - Precache(); - SetModel( STRING( GetModelName() ) ); - SetCollisionGroup( COLLISION_GROUP_VEHICLE ); - - if ( GetSolid() != SOLID_NONE ) - { - BaseClass::Spawn(); - } - - m_takedamage = DAMAGE_EVENTS_ONLY; - - SetNextThink( gpGlobals->curtime ); - - ParseViewParams( STRING(m_vehicleScript) ); -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CPropVehicleChoreoGeneric::TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr ) -{ - if ( ptr->hitbox == VEHICLE_HITBOX_DRIVER ) - { - if ( m_hPlayer != NULL ) - { - m_hPlayer->TakeDamage( info ); - } - } -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -int CPropVehicleChoreoGeneric::OnTakeDamage( const CTakeDamageInfo &inputInfo ) -{ - CTakeDamageInfo info = inputInfo; - info.ScaleDamage( 25 ); - - // reset the damage - info.SetDamage( inputInfo.GetDamage() ); - - // Check to do damage to prisoner - if ( m_hPlayer != NULL ) - { - // Take no damage from physics damages - if ( info.GetDamageType() & DMG_CRUSH ) - return 0; - - // Take the damage - m_hPlayer->TakeDamage( info ); - } - - return 0; -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -Vector CPropVehicleChoreoGeneric::BodyTarget( const Vector &posSrc, bool bNoisy ) -{ - Vector shotPos; - - int eyeAttachmentIndex = LookupAttachment("vehicle_driver_eyes"); - GetAttachment( eyeAttachmentIndex, shotPos ); - - if ( bNoisy ) - { - shotPos[0] += random->RandomFloat( -8.0f, 8.0f ); - shotPos[1] += random->RandomFloat( -8.0f, 8.0f ); - shotPos[2] += random->RandomFloat( -8.0f, 8.0f ); - } - - return shotPos; -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CPropVehicleChoreoGeneric::Think(void) -{ - SetNextThink( gpGlobals->curtime + 0.1 ); - - if ( GetDriver() ) - { - BaseClass::Think(); - - // If the enter or exit animation has finished, tell the server vehicle - if ( IsSequenceFinished() && (m_bExitAnimOn || m_bEnterAnimOn) ) - { - GetServerVehicle()->HandleEntryExitFinish( m_bExitAnimOn, true ); - } - } - - StudioFrameAdvance(); - DispatchAnimEvents( this ); -} - - -//------------------------------------------------------------------------------ -// Purpose: -//------------------------------------------------------------------------------ -void CPropVehicleChoreoGeneric::InputOpen( inputdata_t &inputdata ) -{ - int nSequence = LookupSequence( "open" ); - - // Set to the desired anim, or default anim if the desired is not present - if ( nSequence > ACTIVITY_NOT_AVAILABLE ) - { - SetCycle( 0 ); - m_flAnimTime = gpGlobals->curtime; - ResetSequence( nSequence ); - ResetClientsideFrame(); - } - else - { - // Not available try to get default anim - Msg( "Choreo Generic Vehicle %s: missing open sequence\n", GetDebugName() ); - SetSequence( 0 ); - } -} - - -//------------------------------------------------------------------------------ -// Purpose: -//------------------------------------------------------------------------------ -void CPropVehicleChoreoGeneric::InputClose( inputdata_t &inputdata ) -{ - if ( m_bLocked || m_bEnterAnimOn ) - return; - - int nSequence = LookupSequence( "close" ); - - // Set to the desired anim, or default anim if the desired is not present - if ( nSequence > ACTIVITY_NOT_AVAILABLE ) - { - SetCycle( 0 ); - m_flAnimTime = gpGlobals->curtime; - ResetSequence( nSequence ); - ResetClientsideFrame(); - } - else - { - // Not available try to get default anim - Msg( "Choreo Generic Vehicle %s: missing close sequence\n", GetDebugName() ); - SetSequence( 0 ); - } -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CPropVehicleChoreoGeneric::HandleAnimEvent( animevent_t *pEvent ) -{ - if ( pEvent->event == AE_CHOREO_VEHICLE_OPEN ) - { - m_OnOpen.FireOutput( this, this ); - m_bLocked = false; - } - else if ( pEvent->event == AE_CHOREO_VEHICLE_CLOSE ) - { - m_OnClose.FireOutput( this, this ); - m_bLocked = true; - } -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CPropVehicleChoreoGeneric::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) -{ - CBasePlayer *pPlayer = ToBasePlayer( pActivator ); - if ( !pPlayer ) - return; - - ResetUseKey( pPlayer ); - - GetServerVehicle()->HandlePassengerEntry( pPlayer, (value > 0) ); -} - - -//----------------------------------------------------------------------------- -// Purpose: Return true of the player's allowed to enter / exit the vehicle -//----------------------------------------------------------------------------- -bool CPropVehicleChoreoGeneric::CanEnterVehicle( CBaseEntity *pEntity ) -{ - // Prevent entering if the vehicle's being driven by an NPC - if ( GetDriver() && GetDriver() != pEntity ) - return false; - - // Prevent entering if the vehicle's locked - return !m_bLocked; -} - - -//----------------------------------------------------------------------------- -// Purpose: Return true of the player is allowed to exit the vehicle. -//----------------------------------------------------------------------------- -bool CPropVehicleChoreoGeneric::CanExitVehicle( CBaseEntity *pEntity ) -{ - // Prevent exiting if the vehicle's locked, rotating, or playing an entry/exit anim. - return ( !m_bLocked && (GetLocalAngularVelocity() == vec3_angle) && !m_bEnterAnimOn && !m_bExitAnimOn ); -} - - -//----------------------------------------------------------------------------- -// Purpose: Override base class to add display -//----------------------------------------------------------------------------- -void CPropVehicleChoreoGeneric::DrawDebugGeometryOverlays(void) -{ - // Draw if BBOX is on - if ( m_debugOverlays & OVERLAY_BBOX_BIT ) - { - } - - BaseClass::DrawDebugGeometryOverlays(); -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CPropVehicleChoreoGeneric::EnterVehicle( CBaseCombatCharacter *pPassenger ) -{ - if ( pPassenger == NULL ) - return; - - CBasePlayer *pPlayer = ToBasePlayer( pPassenger ); - if ( pPlayer != NULL ) - { - // Remove any player who may be in the vehicle at the moment - if ( m_hPlayer ) - { - ExitVehicle( VEHICLE_ROLE_DRIVER ); - } - - m_hPlayer = pPlayer; - m_playerOn.FireOutput( pPlayer, this, 0 ); - - m_ServerVehicle.SoundStart(); - } - else - { - // NPCs not supported yet - jdw - Assert( 0 ); - } -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CPropVehicleChoreoGeneric::SetVehicleEntryAnim( bool bOn ) -{ - m_bEnterAnimOn = bOn; -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CPropVehicleChoreoGeneric::ExitVehicle( int nRole ) -{ - CBasePlayer *pPlayer = m_hPlayer; - if ( !pPlayer ) - return; - - m_hPlayer = NULL; - ResetUseKey( pPlayer ); - - m_playerOff.FireOutput( pPlayer, this, 0 ); - m_bEnterAnimOn = false; - - m_ServerVehicle.SoundShutdown( 1.0 ); -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CPropVehicleChoreoGeneric::ResetUseKey( CBasePlayer *pPlayer ) -{ - pPlayer->m_afButtonPressed &= ~IN_USE; -} - - -//----------------------------------------------------------------------------- -// Purpose: Vehicles are permanently oriented off angle for vphysics. -//----------------------------------------------------------------------------- -void CPropVehicleChoreoGeneric::GetVectors(Vector* pForward, Vector* pRight, Vector* pUp) const -{ - // This call is necessary to cause m_rgflCoordinateFrame to be recomputed - const matrix3x4_t &entityToWorld = EntityToWorldTransform(); - - if (pForward != NULL) - { - MatrixGetColumn( entityToWorld, 1, *pForward ); - } - - if (pRight != NULL) - { - MatrixGetColumn( entityToWorld, 0, *pRight ); - } - - if (pUp != NULL) - { - MatrixGetColumn( entityToWorld, 2, *pUp ); - } -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -CBaseEntity *CPropVehicleChoreoGeneric::GetDriver( void ) -{ - return m_hPlayer; -} - -//----------------------------------------------------------------------------- -// Purpose: Prevent the player from entering / exiting the vehicle -//----------------------------------------------------------------------------- -void CPropVehicleChoreoGeneric::InputLock( inputdata_t &inputdata ) -{ - m_bLocked = true; -} - - -//----------------------------------------------------------------------------- -// Purpose: Allow the player to enter / exit the vehicle -//----------------------------------------------------------------------------- -void CPropVehicleChoreoGeneric::InputUnlock( inputdata_t &inputdata ) -{ - m_bLocked = false; -} - - -//----------------------------------------------------------------------------- -// Purpose: Force the player to enter the vehicle. -//----------------------------------------------------------------------------- -void CPropVehicleChoreoGeneric::InputEnterVehicle( inputdata_t &inputdata ) -{ - if ( m_bEnterAnimOn ) - return; - - // Try the activator first & use them if they are a player. - CBaseCombatCharacter *pPassenger = ToBaseCombatCharacter( inputdata.pActivator ); - if ( pPassenger == NULL ) - { - // Activator was not a player, just grab the singleplayer player. - pPassenger = UTIL_PlayerByIndex( 1 ); - if ( pPassenger == NULL ) - return; - } - - // FIXME: I hate code like this. I should really add a parameter to HandlePassengerEntry - // to allow entry into locked vehicles - bool bWasLocked = m_bLocked; - m_bLocked = false; - GetServerVehicle()->HandlePassengerEntry( pPassenger, true ); - m_bLocked = bWasLocked; -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : &inputdata - -//----------------------------------------------------------------------------- -void CPropVehicleChoreoGeneric::InputEnterVehicleImmediate( inputdata_t &inputdata ) -{ - if ( m_bEnterAnimOn ) - return; - - // Try the activator first & use them if they are a player. - CBaseCombatCharacter *pPassenger = ToBaseCombatCharacter( inputdata.pActivator ); - if ( pPassenger == NULL ) - { - // Activator was not a player, just grab the singleplayer player. - pPassenger = UTIL_PlayerByIndex( 1 ); - if ( pPassenger == NULL ) - return; - } - - CBasePlayer *pPlayer = ToBasePlayer( pPassenger ); - if ( pPlayer != NULL ) - { - if ( pPlayer->IsInAVehicle() ) - { - // Force the player out of whatever vehicle they are in. - pPlayer->LeaveVehicle(); - } - - pPlayer->GetInVehicle( GetServerVehicle(), VEHICLE_ROLE_DRIVER ); - } - else - { - // NPCs not supported yet - jdw - Assert( 0 ); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Force the player to exit the vehicle. -//----------------------------------------------------------------------------- -void CPropVehicleChoreoGeneric::InputExitVehicle( inputdata_t &inputdata ) -{ - m_bForcedExit = true; -} - -//----------------------------------------------------------------------------- -// Purpose: Parses the vehicle's script for the vehicle view parameters -//----------------------------------------------------------------------------- -bool CPropVehicleChoreoGeneric::ParseViewParams( const char *pScriptName ) -{ - byte *pFile = UTIL_LoadFileForMe( pScriptName, NULL ); - if ( !pFile ) - return false; - - IVPhysicsKeyParser *pParse = physcollision->VPhysicsKeyParserCreate( (char *)pFile ); - CVehicleChoreoViewParser viewParser; - while ( !pParse->Finished() ) - { - const char *pBlock = pParse->GetCurrentBlockName(); - if ( !strcmpi( pBlock, "vehicle_view" ) ) - { - pParse->ParseCustom( &m_vehicleView, &viewParser ); - } - else - { - pParse->SkipBlock(); - } - } - physcollision->VPhysicsKeyParserDestroy( pParse ); - UTIL_FreeFile( pFile ); - - Precache(); - - return true; -} - -//======================================================================================================================================== -// CRANE VEHICLE SERVER VEHICLE -//======================================================================================================================================== -CPropVehicleChoreoGeneric *CChoreoGenericServerVehicle::GetVehicle( void ) -{ - return (CPropVehicleChoreoGeneric *)GetDrivableVehicle(); -} - - -//----------------------------------------------------------------------------- -// Purpose: -// Input : pPlayer - -//----------------------------------------------------------------------------- -void CChoreoGenericServerVehicle::ItemPostFrame( CBasePlayer *player ) -{ - Assert( player == GetDriver() ); - - GetDrivableVehicle()->ItemPostFrame( player ); - - if (( player->m_afButtonPressed & IN_USE ) || GetVehicle()->ShouldForceExit() ) - { - GetVehicle()->ClearForcedExit(); - if ( GetDrivableVehicle()->CanExitVehicle(player) ) - { - // Let the vehicle try to play the exit animation - if ( !HandlePassengerExit( player ) && ( player != NULL ) ) - { - player->PlayUseDenySound(); - } - } - } -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CChoreoGenericServerVehicle::GetVehicleViewPosition( int nRole, Vector *pAbsOrigin, QAngle *pAbsAngles ) -{ - Assert( nRole == VEHICLE_ROLE_DRIVER ); - CBasePlayer *pPlayer = ToBasePlayer( GetDrivableVehicle()->GetDriver() ); - Assert( pPlayer ); - - *pAbsAngles = pPlayer->EyeAngles(); // yuck. this is an in/out parameter. - - float flPitchFactor = 1.0; - matrix3x4_t vehicleEyePosToWorld; - Vector vehicleEyeOrigin; - QAngle vehicleEyeAngles; - GetVehicle()->GetAttachment( "vehicle_driver_eyes", vehicleEyeOrigin, vehicleEyeAngles ); - AngleMatrix( vehicleEyeAngles, vehicleEyePosToWorld ); - - // Compute the relative rotation between the unperterbed eye attachment + the eye angles - matrix3x4_t cameraToWorld; - AngleMatrix( *pAbsAngles, cameraToWorld ); - - matrix3x4_t worldToEyePos; - MatrixInvert( vehicleEyePosToWorld, worldToEyePos ); - - matrix3x4_t vehicleCameraToEyePos; - ConcatTransforms( worldToEyePos, cameraToWorld, vehicleCameraToEyePos ); - - // Now perterb the attachment point - vehicleEyeAngles.x = RemapAngleRange( PITCH_CURVE_ZERO * flPitchFactor, PITCH_CURVE_LINEAR, vehicleEyeAngles.x ); - vehicleEyeAngles.z = RemapAngleRange( ROLL_CURVE_ZERO * flPitchFactor, ROLL_CURVE_LINEAR, vehicleEyeAngles.z ); - AngleMatrix( vehicleEyeAngles, vehicleEyeOrigin, vehicleEyePosToWorld ); - - // Now treat the relative eye angles as being relative to this new, perterbed view position... - matrix3x4_t newCameraToWorld; - ConcatTransforms( vehicleEyePosToWorld, vehicleCameraToEyePos, newCameraToWorld ); - - // output new view abs angles - MatrixAngles( newCameraToWorld, *pAbsAngles ); - - // UNDONE: *pOrigin would already be correct in single player if the HandleView() on the server ran after vphysics - MatrixGetColumn( newCameraToWorld, 3, *pAbsOrigin ); -} - -bool CPropVehicleChoreoGeneric::ShouldCollide( int collisionGroup, int contentsMask ) const -{ - if ( m_bIgnorePlayerCollisions == true ) - { - if ( collisionGroup == COLLISION_GROUP_PLAYER || collisionGroup == COLLISION_GROUP_PLAYER_MOVEMENT ) - return false; - } - - return BaseClass::ShouldCollide( collisionGroup, contentsMask ); -} - - -CVehicleChoreoViewParser::CVehicleChoreoViewParser( void ) -{ - -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CVehicleChoreoViewParser::ParseKeyValue( void *pData, const char *pKey, const char *pValue ) -{ - vehicleview_t *pView = (vehicleview_t *)pData; - // New gear? - if ( !strcmpi( pKey, "clamp" ) ) - { - pView->bClampEyeAngles = !!atoi( pValue ); - } - else if ( !strcmpi( pKey, "pitchcurvezero" ) ) - { - pView->flPitchCurveZero = atof( pValue ); - } - else if ( !strcmpi( pKey, "pitchcurvelinear" ) ) - { - pView->flPitchCurveLinear = atof( pValue ); - } - else if ( !strcmpi( pKey, "rollcurvezero" ) ) - { - pView->flRollCurveZero = atof( pValue ); - } - else if ( !strcmpi( pKey, "rollcurvelinear" ) ) - { - pView->flRollCurveLinear = atof( pValue ); - } - else if ( !strcmpi( pKey, "yawmin" ) ) - { - pView->flYawMin = atof( pValue ); - } - else if ( !strcmpi( pKey, "yawmax" ) ) - { - pView->flYawMax = atof( pValue ); - } - else if ( !strcmpi( pKey, "pitchmin" ) ) - { - pView->flPitchMin = atof( pValue ); - } - else if ( !strcmpi( pKey, "pitchmax" ) ) - { - pView->flPitchMax = atof( pValue ); - } - else if ( !strcmpi( pKey, "fov" ) ) - { - pView->flFOV = atof( pValue ); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CVehicleChoreoViewParser::SetDefaults( void *pData ) -{ - vehicleview_t *pView = (vehicleview_t *)pData; - - pView->bClampEyeAngles = true; - - pView->flPitchCurveZero = PITCH_CURVE_ZERO; - pView->flPitchCurveLinear = PITCH_CURVE_LINEAR; - pView->flRollCurveZero = ROLL_CURVE_ZERO; - pView->flRollCurveLinear = ROLL_CURVE_LINEAR; - pView->flFOV = CHOREO_VEHICLE_VIEW_FOV; - pView->flYawMin = CHOREO_VEHICLE_VIEW_YAW_MIN; - pView->flYawMax = CHOREO_VEHICLE_VIEW_YAW_MAX; - pView->flPitchMin = CHOREO_VEHICLE_VIEW_PITCH_MIN; - pView->flPitchMax = CHOREO_VEHICLE_VIEW_PITCH_MAX; - -} \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "cbase.h" +#include "npcevent.h" +#include "vehicle_base.h" +#include "engine/IEngineSound.h" +#include "in_buttons.h" +#include "soundenvelope.h" +#include "soundent.h" +#include "physics_saverestore.h" +#include "vphysics/constraints.h" +#include "vcollide_parse.h" +#include "ndebugoverlay.h" +#include "hl2_player.h" +#include "props.h" +#include "vehicle_choreo_generic_shared.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +#define VEHICLE_HITBOX_DRIVER 1 + +#define CHOREO_VEHICLE_VIEW_FOV 90 +#define CHOREO_VEHICLE_VIEW_YAW_MIN -60 +#define CHOREO_VEHICLE_VIEW_YAW_MAX 60 +#define CHOREO_VEHICLE_VIEW_PITCH_MIN -90 +#define CHOREO_VEHICLE_VIEW_PITCH_MAX 38 + +BEGIN_DATADESC_NO_BASE( vehicleview_t ) + DEFINE_FIELD( bClampEyeAngles, FIELD_BOOLEAN ), + DEFINE_FIELD( flPitchCurveZero, FIELD_FLOAT ), + DEFINE_FIELD( flPitchCurveLinear, FIELD_FLOAT ), + DEFINE_FIELD( flRollCurveZero, FIELD_FLOAT ), + DEFINE_FIELD( flRollCurveLinear, FIELD_FLOAT ), + DEFINE_FIELD( flFOV, FIELD_FLOAT ), + DEFINE_FIELD( flYawMin, FIELD_FLOAT ), + DEFINE_FIELD( flYawMax, FIELD_FLOAT ), + DEFINE_FIELD( flPitchMin, FIELD_FLOAT ), + DEFINE_FIELD( flPitchMax, FIELD_FLOAT ), +END_DATADESC() + +// +// Anim events. +// +enum +{ + AE_CHOREO_VEHICLE_OPEN = 1, + AE_CHOREO_VEHICLE_CLOSE = 2, +}; + + +extern ConVar g_debug_vehicledriver; + + +class CPropVehicleChoreoGeneric; + +/*static const char *pChoreoGenericFollowerBoneNames[] = +{ + "base", +};*/ + + +//----------------------------------------------------------------------------- +// Purpose: A KeyValues parse for vehicle sound blocks +//----------------------------------------------------------------------------- +class CVehicleChoreoViewParser : public IVPhysicsKeyHandler +{ +public: + CVehicleChoreoViewParser( void ); + + void ParseVehicleSounds( const char *pScriptName, vehiclesounds_t *pSounds ); + +private: + virtual void ParseKeyValue( void *pData, const char *pKey, const char *pValue ); + virtual void SetDefaults( void *pData ); +}; + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +class CChoreoGenericServerVehicle : public CBaseServerVehicle +{ + typedef CBaseServerVehicle BaseClass; + +// IServerVehicle +public: + void GetVehicleViewPosition( int nRole, Vector *pAbsOrigin, QAngle *pAbsAngles ); + virtual void ItemPostFrame( CBasePlayer *pPlayer ); + +protected: + + CPropVehicleChoreoGeneric *GetVehicle( void ); +}; + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +class CPropVehicleChoreoGeneric : public CPhysicsProp, public IDrivableVehicle +{ + DECLARE_CLASS( CPropVehicleChoreoGeneric, CPhysicsProp ); + +public: + DECLARE_DATADESC(); + DECLARE_SERVERCLASS(); + + CPropVehicleChoreoGeneric( void ) + { + m_ServerVehicle.SetVehicle( this ); + m_bIgnoreMoveParent = false; + } + + ~CPropVehicleChoreoGeneric( void ) + { + } + + // CBaseEntity + virtual void Precache( void ); + void Spawn( void ); + void Think(void); + virtual int ObjectCaps( void ) { return BaseClass::ObjectCaps() | FCAP_IMPULSE_USE; }; + virtual void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); + virtual void DrawDebugGeometryOverlays( void ); + + virtual Vector BodyTarget( const Vector &posSrc, bool bNoisy = true ); + virtual void TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr ); + virtual int OnTakeDamage( const CTakeDamageInfo &info ); + + void PlayerControlInit( CBasePlayer *pPlayer ); + void PlayerControlShutdown( void ); + void ResetUseKey( CBasePlayer *pPlayer ); + + virtual bool OverridePropdata() { return true; } + + bool ParseViewParams( const char *pScriptName ); + + void GetVectors(Vector* pForward, Vector* pRight, Vector* pUp) const; + + bool CreateVPhysics() + { + SetSolid(SOLID_VPHYSICS); + SetMoveType(MOVETYPE_NONE); + return true; + } + bool ShouldForceExit() { return m_bForcedExit; } + void ClearForcedExit() { m_bForcedExit = false; } + + // CBaseAnimating + void HandleAnimEvent( animevent_t *pEvent ); + + // Inputs + void InputEnterVehicleImmediate( inputdata_t &inputdata ); + void InputEnterVehicle( inputdata_t &inputdata ); + void InputExitVehicle( inputdata_t &inputdata ); + void InputLock( inputdata_t &inputdata ); + void InputUnlock( inputdata_t &inputdata ); + void InputOpen( inputdata_t &inputdata ); + void InputClose( inputdata_t &inputdata ); + + bool ShouldIgnoreParent( void ) { return m_bIgnoreMoveParent; } + + CNetworkHandle( CBasePlayer, m_hPlayer ); + + CNetworkVarEmbedded( vehicleview_t, m_vehicleView ); + +// IDrivableVehicle +public: + + virtual CBaseEntity *GetDriver( void ); + virtual void ProcessMovement( CBasePlayer *pPlayer, CMoveData *pMoveData ) { return; } + virtual void FinishMove( CBasePlayer *player, CUserCmd *ucmd, CMoveData *move ) { return; } + virtual bool CanEnterVehicle( CBaseEntity *pEntity ); + virtual bool CanExitVehicle( CBaseEntity *pEntity ); + virtual void SetVehicleEntryAnim( bool bOn ); + virtual void SetVehicleExitAnim( bool bOn, Vector vecEyeExitEndpoint ) { m_bExitAnimOn = bOn; if ( bOn ) m_vecEyeExitEndpoint = vecEyeExitEndpoint; } + virtual void EnterVehicle( CBaseCombatCharacter *pPassenger ); + + virtual bool AllowBlockedExit( CBaseCombatCharacter *pPassenger, int nRole ) { return true; } + virtual bool AllowMidairExit( CBaseCombatCharacter *pPassenger, int nRole ) { return true; } + virtual void PreExitVehicle( CBaseCombatCharacter *pPassenger, int nRole ) {} + virtual void ExitVehicle( int nRole ); + + virtual void ItemPostFrame( CBasePlayer *pPlayer ) {} + virtual void SetupMove( CBasePlayer *player, CUserCmd *ucmd, IMoveHelper *pHelper, CMoveData *move ) {} + + // If this is a vehicle, returns the vehicle interface + virtual IServerVehicle *GetServerVehicle() { return &m_ServerVehicle; } + + bool ShouldCollide( int collisionGroup, int contentsMask ) const; + +protected: + + // Contained IServerVehicle + CChoreoGenericServerVehicle m_ServerVehicle; + +private: + + // Entering / Exiting + bool m_bLocked; + CNetworkVar( bool, m_bEnterAnimOn ); + CNetworkVar( bool, m_bExitAnimOn ); + CNetworkVector( m_vecEyeExitEndpoint ); + bool m_bForcedExit; + bool m_bIgnoreMoveParent; + bool m_bIgnorePlayerCollisions; + + // Vehicle script filename + string_t m_vehicleScript; + + COutputEvent m_playerOn; + COutputEvent m_playerOff; + COutputEvent m_OnOpen; + COutputEvent m_OnClose; +}; + +LINK_ENTITY_TO_CLASS( prop_vehicle_choreo_generic, CPropVehicleChoreoGeneric ); + +BEGIN_DATADESC( CPropVehicleChoreoGeneric ) + + // Inputs + DEFINE_INPUTFUNC( FIELD_VOID, "Lock", InputLock ), + DEFINE_INPUTFUNC( FIELD_VOID, "Unlock", InputUnlock ), + DEFINE_INPUTFUNC( FIELD_VOID, "EnterVehicle", InputEnterVehicle ), + DEFINE_INPUTFUNC( FIELD_VOID, "EnterVehicleImmediate", InputEnterVehicleImmediate ), + DEFINE_INPUTFUNC( FIELD_VOID, "ExitVehicle", InputExitVehicle ), + DEFINE_INPUTFUNC( FIELD_VOID, "Open", InputOpen ), + DEFINE_INPUTFUNC( FIELD_VOID, "Close", InputClose ), + + // Keys + DEFINE_EMBEDDED( m_ServerVehicle ), + + DEFINE_FIELD( m_hPlayer, FIELD_EHANDLE ), + DEFINE_FIELD( m_bEnterAnimOn, FIELD_BOOLEAN ), + DEFINE_FIELD( m_bExitAnimOn, FIELD_BOOLEAN ), + DEFINE_FIELD( m_bForcedExit, FIELD_BOOLEAN ), + DEFINE_FIELD( m_vecEyeExitEndpoint, FIELD_POSITION_VECTOR ), + + DEFINE_KEYFIELD( m_vehicleScript, FIELD_STRING, "vehiclescript" ), + DEFINE_KEYFIELD( m_bLocked, FIELD_BOOLEAN, "vehiclelocked" ), + + DEFINE_KEYFIELD( m_bIgnoreMoveParent, FIELD_BOOLEAN, "ignoremoveparent" ), + DEFINE_KEYFIELD( m_bIgnorePlayerCollisions, FIELD_BOOLEAN, "ignoreplayer" ), + + DEFINE_OUTPUT( m_playerOn, "PlayerOn" ), + DEFINE_OUTPUT( m_playerOff, "PlayerOff" ), + DEFINE_OUTPUT( m_OnOpen, "OnOpen" ), + DEFINE_OUTPUT( m_OnClose, "OnClose" ), + + DEFINE_EMBEDDED( m_vehicleView ), + +END_DATADESC() + +IMPLEMENT_SERVERCLASS_ST(CPropVehicleChoreoGeneric, DT_PropVehicleChoreoGeneric) + SendPropEHandle(SENDINFO(m_hPlayer)), + SendPropBool(SENDINFO(m_bEnterAnimOn)), + SendPropBool(SENDINFO(m_bExitAnimOn)), + SendPropVector(SENDINFO(m_vecEyeExitEndpoint), -1, SPROP_COORD), + SendPropBool( SENDINFO_STRUCTELEM( m_vehicleView.bClampEyeAngles ) ), + SendPropFloat( SENDINFO_STRUCTELEM( m_vehicleView.flPitchCurveZero ) ), + SendPropFloat( SENDINFO_STRUCTELEM( m_vehicleView.flPitchCurveLinear ) ), + SendPropFloat( SENDINFO_STRUCTELEM( m_vehicleView.flRollCurveZero ) ), + SendPropFloat( SENDINFO_STRUCTELEM( m_vehicleView.flRollCurveLinear ) ), + SendPropFloat( SENDINFO_STRUCTELEM( m_vehicleView.flFOV ) ), + SendPropFloat( SENDINFO_STRUCTELEM( m_vehicleView.flYawMin ) ), + SendPropFloat( SENDINFO_STRUCTELEM( m_vehicleView.flYawMax ) ), + SendPropFloat( SENDINFO_STRUCTELEM( m_vehicleView.flPitchMin ) ), + SendPropFloat( SENDINFO_STRUCTELEM( m_vehicleView.flPitchMax ) ), +END_SEND_TABLE(); + + +bool ShouldVehicleIgnoreEntity( CBaseEntity *pVehicle, CBaseEntity *pCollide ) +{ + CPropVehicleChoreoGeneric *pChoreoVehicle = dynamic_cast ( pVehicle ); + + if ( pChoreoVehicle == NULL ) + return false; + + if ( pCollide == NULL ) + return false; + + if ( pChoreoVehicle->ShouldIgnoreParent() == false ) + return false; + + if ( pChoreoVehicle->GetMoveParent() == pCollide ) + return true; + + return false; +} + + +//------------------------------------------------ +// Precache +//------------------------------------------------ +void CPropVehicleChoreoGeneric::Precache( void ) +{ + BaseClass::Precache(); + + m_ServerVehicle.Initialize( STRING(m_vehicleScript) ); +} + + +//------------------------------------------------ +// Spawn +//------------------------------------------------ +void CPropVehicleChoreoGeneric::Spawn( void ) +{ + Precache(); + SetModel( STRING( GetModelName() ) ); + SetCollisionGroup( COLLISION_GROUP_VEHICLE ); + + if ( GetSolid() != SOLID_NONE ) + { + BaseClass::Spawn(); + } + + m_takedamage = DAMAGE_EVENTS_ONLY; + + SetNextThink( gpGlobals->curtime ); + + ParseViewParams( STRING(m_vehicleScript) ); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPropVehicleChoreoGeneric::TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr ) +{ + if ( ptr->hitbox == VEHICLE_HITBOX_DRIVER ) + { + if ( m_hPlayer != NULL ) + { + m_hPlayer->TakeDamage( info ); + } + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +int CPropVehicleChoreoGeneric::OnTakeDamage( const CTakeDamageInfo &inputInfo ) +{ + CTakeDamageInfo info = inputInfo; + info.ScaleDamage( 25 ); + + // reset the damage + info.SetDamage( inputInfo.GetDamage() ); + + // Check to do damage to prisoner + if ( m_hPlayer != NULL ) + { + // Take no damage from physics damages + if ( info.GetDamageType() & DMG_CRUSH ) + return 0; + + // Take the damage + m_hPlayer->TakeDamage( info ); + } + + return 0; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +Vector CPropVehicleChoreoGeneric::BodyTarget( const Vector &posSrc, bool bNoisy ) +{ + Vector shotPos; + + int eyeAttachmentIndex = LookupAttachment("vehicle_driver_eyes"); + GetAttachment( eyeAttachmentIndex, shotPos ); + + if ( bNoisy ) + { + shotPos[0] += random->RandomFloat( -8.0f, 8.0f ); + shotPos[1] += random->RandomFloat( -8.0f, 8.0f ); + shotPos[2] += random->RandomFloat( -8.0f, 8.0f ); + } + + return shotPos; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPropVehicleChoreoGeneric::Think(void) +{ + SetNextThink( gpGlobals->curtime + 0.1 ); + + if ( GetDriver() ) + { + BaseClass::Think(); + + // If the enter or exit animation has finished, tell the server vehicle + if ( IsSequenceFinished() && (m_bExitAnimOn || m_bEnterAnimOn) ) + { + GetServerVehicle()->HandleEntryExitFinish( m_bExitAnimOn, true ); + } + } + + StudioFrameAdvance(); + DispatchAnimEvents( this ); +} + + +//------------------------------------------------------------------------------ +// Purpose: +//------------------------------------------------------------------------------ +void CPropVehicleChoreoGeneric::InputOpen( inputdata_t &inputdata ) +{ + int nSequence = LookupSequence( "open" ); + + // Set to the desired anim, or default anim if the desired is not present + if ( nSequence > ACTIVITY_NOT_AVAILABLE ) + { + SetCycle( 0 ); + m_flAnimTime = gpGlobals->curtime; + ResetSequence( nSequence ); + ResetClientsideFrame(); + } + else + { + // Not available try to get default anim + Msg( "Choreo Generic Vehicle %s: missing open sequence\n", GetDebugName() ); + SetSequence( 0 ); + } +} + + +//------------------------------------------------------------------------------ +// Purpose: +//------------------------------------------------------------------------------ +void CPropVehicleChoreoGeneric::InputClose( inputdata_t &inputdata ) +{ + if ( m_bLocked || m_bEnterAnimOn ) + return; + + int nSequence = LookupSequence( "close" ); + + // Set to the desired anim, or default anim if the desired is not present + if ( nSequence > ACTIVITY_NOT_AVAILABLE ) + { + SetCycle( 0 ); + m_flAnimTime = gpGlobals->curtime; + ResetSequence( nSequence ); + ResetClientsideFrame(); + } + else + { + // Not available try to get default anim + Msg( "Choreo Generic Vehicle %s: missing close sequence\n", GetDebugName() ); + SetSequence( 0 ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPropVehicleChoreoGeneric::HandleAnimEvent( animevent_t *pEvent ) +{ + if ( pEvent->event == AE_CHOREO_VEHICLE_OPEN ) + { + m_OnOpen.FireOutput( this, this ); + m_bLocked = false; + } + else if ( pEvent->event == AE_CHOREO_VEHICLE_CLOSE ) + { + m_OnClose.FireOutput( this, this ); + m_bLocked = true; + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPropVehicleChoreoGeneric::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) +{ + CBasePlayer *pPlayer = ToBasePlayer( pActivator ); + if ( !pPlayer ) + return; + + ResetUseKey( pPlayer ); + + GetServerVehicle()->HandlePassengerEntry( pPlayer, (value > 0) ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Return true of the player's allowed to enter / exit the vehicle +//----------------------------------------------------------------------------- +bool CPropVehicleChoreoGeneric::CanEnterVehicle( CBaseEntity *pEntity ) +{ + // Prevent entering if the vehicle's being driven by an NPC + if ( GetDriver() && GetDriver() != pEntity ) + return false; + + // Prevent entering if the vehicle's locked + return !m_bLocked; +} + + +//----------------------------------------------------------------------------- +// Purpose: Return true of the player is allowed to exit the vehicle. +//----------------------------------------------------------------------------- +bool CPropVehicleChoreoGeneric::CanExitVehicle( CBaseEntity *pEntity ) +{ + // Prevent exiting if the vehicle's locked, rotating, or playing an entry/exit anim. + return ( !m_bLocked && (GetLocalAngularVelocity() == vec3_angle) && !m_bEnterAnimOn && !m_bExitAnimOn ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Override base class to add display +//----------------------------------------------------------------------------- +void CPropVehicleChoreoGeneric::DrawDebugGeometryOverlays(void) +{ + // Draw if BBOX is on + if ( m_debugOverlays & OVERLAY_BBOX_BIT ) + { + } + + BaseClass::DrawDebugGeometryOverlays(); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPropVehicleChoreoGeneric::EnterVehicle( CBaseCombatCharacter *pPassenger ) +{ + if ( pPassenger == NULL ) + return; + + CBasePlayer *pPlayer = ToBasePlayer( pPassenger ); + if ( pPlayer != NULL ) + { + // Remove any player who may be in the vehicle at the moment + if ( m_hPlayer ) + { + ExitVehicle( VEHICLE_ROLE_DRIVER ); + } + + m_hPlayer = pPlayer; + m_playerOn.FireOutput( pPlayer, this, 0 ); + + m_ServerVehicle.SoundStart(); + } + else + { + // NPCs not supported yet - jdw + Assert( 0 ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPropVehicleChoreoGeneric::SetVehicleEntryAnim( bool bOn ) +{ + m_bEnterAnimOn = bOn; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPropVehicleChoreoGeneric::ExitVehicle( int nRole ) +{ + CBasePlayer *pPlayer = m_hPlayer; + if ( !pPlayer ) + return; + + m_hPlayer = NULL; + ResetUseKey( pPlayer ); + + m_playerOff.FireOutput( pPlayer, this, 0 ); + m_bEnterAnimOn = false; + + m_ServerVehicle.SoundShutdown( 1.0 ); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPropVehicleChoreoGeneric::ResetUseKey( CBasePlayer *pPlayer ) +{ + pPlayer->m_afButtonPressed &= ~IN_USE; +} + + +//----------------------------------------------------------------------------- +// Purpose: Vehicles are permanently oriented off angle for vphysics. +//----------------------------------------------------------------------------- +void CPropVehicleChoreoGeneric::GetVectors(Vector* pForward, Vector* pRight, Vector* pUp) const +{ + // This call is necessary to cause m_rgflCoordinateFrame to be recomputed + const matrix3x4_t &entityToWorld = EntityToWorldTransform(); + + if (pForward != NULL) + { + MatrixGetColumn( entityToWorld, 1, *pForward ); + } + + if (pRight != NULL) + { + MatrixGetColumn( entityToWorld, 0, *pRight ); + } + + if (pUp != NULL) + { + MatrixGetColumn( entityToWorld, 2, *pUp ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CBaseEntity *CPropVehicleChoreoGeneric::GetDriver( void ) +{ + return m_hPlayer; +} + +//----------------------------------------------------------------------------- +// Purpose: Prevent the player from entering / exiting the vehicle +//----------------------------------------------------------------------------- +void CPropVehicleChoreoGeneric::InputLock( inputdata_t &inputdata ) +{ + m_bLocked = true; +} + + +//----------------------------------------------------------------------------- +// Purpose: Allow the player to enter / exit the vehicle +//----------------------------------------------------------------------------- +void CPropVehicleChoreoGeneric::InputUnlock( inputdata_t &inputdata ) +{ + m_bLocked = false; +} + + +//----------------------------------------------------------------------------- +// Purpose: Force the player to enter the vehicle. +//----------------------------------------------------------------------------- +void CPropVehicleChoreoGeneric::InputEnterVehicle( inputdata_t &inputdata ) +{ + if ( m_bEnterAnimOn ) + return; + + // Try the activator first & use them if they are a player. + CBaseCombatCharacter *pPassenger = ToBaseCombatCharacter( inputdata.pActivator ); + if ( pPassenger == NULL ) + { + // Activator was not a player, just grab the singleplayer player. + pPassenger = UTIL_PlayerByIndex( 1 ); + if ( pPassenger == NULL ) + return; + } + + // FIXME: I hate code like this. I should really add a parameter to HandlePassengerEntry + // to allow entry into locked vehicles + bool bWasLocked = m_bLocked; + m_bLocked = false; + GetServerVehicle()->HandlePassengerEntry( pPassenger, true ); + m_bLocked = bWasLocked; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : &inputdata - +//----------------------------------------------------------------------------- +void CPropVehicleChoreoGeneric::InputEnterVehicleImmediate( inputdata_t &inputdata ) +{ + if ( m_bEnterAnimOn ) + return; + + // Try the activator first & use them if they are a player. + CBaseCombatCharacter *pPassenger = ToBaseCombatCharacter( inputdata.pActivator ); + if ( pPassenger == NULL ) + { + // Activator was not a player, just grab the singleplayer player. + pPassenger = UTIL_PlayerByIndex( 1 ); + if ( pPassenger == NULL ) + return; + } + + CBasePlayer *pPlayer = ToBasePlayer( pPassenger ); + if ( pPlayer != NULL ) + { + if ( pPlayer->IsInAVehicle() ) + { + // Force the player out of whatever vehicle they are in. + pPlayer->LeaveVehicle(); + } + + pPlayer->GetInVehicle( GetServerVehicle(), VEHICLE_ROLE_DRIVER ); + } + else + { + // NPCs not supported yet - jdw + Assert( 0 ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Force the player to exit the vehicle. +//----------------------------------------------------------------------------- +void CPropVehicleChoreoGeneric::InputExitVehicle( inputdata_t &inputdata ) +{ + m_bForcedExit = true; +} + +//----------------------------------------------------------------------------- +// Purpose: Parses the vehicle's script for the vehicle view parameters +//----------------------------------------------------------------------------- +bool CPropVehicleChoreoGeneric::ParseViewParams( const char *pScriptName ) +{ + byte *pFile = UTIL_LoadFileForMe( pScriptName, NULL ); + if ( !pFile ) + return false; + + IVPhysicsKeyParser *pParse = physcollision->VPhysicsKeyParserCreate( (char *)pFile ); + CVehicleChoreoViewParser viewParser; + while ( !pParse->Finished() ) + { + const char *pBlock = pParse->GetCurrentBlockName(); + if ( !strcmpi( pBlock, "vehicle_view" ) ) + { + pParse->ParseCustom( &m_vehicleView, &viewParser ); + } + else + { + pParse->SkipBlock(); + } + } + physcollision->VPhysicsKeyParserDestroy( pParse ); + UTIL_FreeFile( pFile ); + + Precache(); + + return true; +} + +//======================================================================================================================================== +// CRANE VEHICLE SERVER VEHICLE +//======================================================================================================================================== +CPropVehicleChoreoGeneric *CChoreoGenericServerVehicle::GetVehicle( void ) +{ + return (CPropVehicleChoreoGeneric *)GetDrivableVehicle(); +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : pPlayer - +//----------------------------------------------------------------------------- +void CChoreoGenericServerVehicle::ItemPostFrame( CBasePlayer *player ) +{ + Assert( player == GetDriver() ); + + GetDrivableVehicle()->ItemPostFrame( player ); + + if (( player->m_afButtonPressed & IN_USE ) || GetVehicle()->ShouldForceExit() ) + { + GetVehicle()->ClearForcedExit(); + if ( GetDrivableVehicle()->CanExitVehicle(player) ) + { + // Let the vehicle try to play the exit animation + if ( !HandlePassengerExit( player ) && ( player != NULL ) ) + { + player->PlayUseDenySound(); + } + } + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CChoreoGenericServerVehicle::GetVehicleViewPosition( int nRole, Vector *pAbsOrigin, QAngle *pAbsAngles ) +{ + Assert( nRole == VEHICLE_ROLE_DRIVER ); + CBasePlayer *pPlayer = ToBasePlayer( GetDrivableVehicle()->GetDriver() ); + Assert( pPlayer ); + + *pAbsAngles = pPlayer->EyeAngles(); // yuck. this is an in/out parameter. + + float flPitchFactor = 1.0; + matrix3x4_t vehicleEyePosToWorld; + Vector vehicleEyeOrigin; + QAngle vehicleEyeAngles; + GetVehicle()->GetAttachment( "vehicle_driver_eyes", vehicleEyeOrigin, vehicleEyeAngles ); + AngleMatrix( vehicleEyeAngles, vehicleEyePosToWorld ); + + // Compute the relative rotation between the unperterbed eye attachment + the eye angles + matrix3x4_t cameraToWorld; + AngleMatrix( *pAbsAngles, cameraToWorld ); + + matrix3x4_t worldToEyePos; + MatrixInvert( vehicleEyePosToWorld, worldToEyePos ); + + matrix3x4_t vehicleCameraToEyePos; + ConcatTransforms( worldToEyePos, cameraToWorld, vehicleCameraToEyePos ); + + // Now perterb the attachment point + vehicleEyeAngles.x = RemapAngleRange( PITCH_CURVE_ZERO * flPitchFactor, PITCH_CURVE_LINEAR, vehicleEyeAngles.x ); + vehicleEyeAngles.z = RemapAngleRange( ROLL_CURVE_ZERO * flPitchFactor, ROLL_CURVE_LINEAR, vehicleEyeAngles.z ); + AngleMatrix( vehicleEyeAngles, vehicleEyeOrigin, vehicleEyePosToWorld ); + + // Now treat the relative eye angles as being relative to this new, perterbed view position... + matrix3x4_t newCameraToWorld; + ConcatTransforms( vehicleEyePosToWorld, vehicleCameraToEyePos, newCameraToWorld ); + + // output new view abs angles + MatrixAngles( newCameraToWorld, *pAbsAngles ); + + // UNDONE: *pOrigin would already be correct in single player if the HandleView() on the server ran after vphysics + MatrixGetColumn( newCameraToWorld, 3, *pAbsOrigin ); +} + +bool CPropVehicleChoreoGeneric::ShouldCollide( int collisionGroup, int contentsMask ) const +{ + if ( m_bIgnorePlayerCollisions == true ) + { + if ( collisionGroup == COLLISION_GROUP_PLAYER || collisionGroup == COLLISION_GROUP_PLAYER_MOVEMENT ) + return false; + } + + return BaseClass::ShouldCollide( collisionGroup, contentsMask ); +} + + +CVehicleChoreoViewParser::CVehicleChoreoViewParser( void ) +{ + +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CVehicleChoreoViewParser::ParseKeyValue( void *pData, const char *pKey, const char *pValue ) +{ + vehicleview_t *pView = (vehicleview_t *)pData; + // New gear? + if ( !strcmpi( pKey, "clamp" ) ) + { + pView->bClampEyeAngles = !!atoi( pValue ); + } + else if ( !strcmpi( pKey, "pitchcurvezero" ) ) + { + pView->flPitchCurveZero = atof( pValue ); + } + else if ( !strcmpi( pKey, "pitchcurvelinear" ) ) + { + pView->flPitchCurveLinear = atof( pValue ); + } + else if ( !strcmpi( pKey, "rollcurvezero" ) ) + { + pView->flRollCurveZero = atof( pValue ); + } + else if ( !strcmpi( pKey, "rollcurvelinear" ) ) + { + pView->flRollCurveLinear = atof( pValue ); + } + else if ( !strcmpi( pKey, "yawmin" ) ) + { + pView->flYawMin = atof( pValue ); + } + else if ( !strcmpi( pKey, "yawmax" ) ) + { + pView->flYawMax = atof( pValue ); + } + else if ( !strcmpi( pKey, "pitchmin" ) ) + { + pView->flPitchMin = atof( pValue ); + } + else if ( !strcmpi( pKey, "pitchmax" ) ) + { + pView->flPitchMax = atof( pValue ); + } + else if ( !strcmpi( pKey, "fov" ) ) + { + pView->flFOV = atof( pValue ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CVehicleChoreoViewParser::SetDefaults( void *pData ) +{ + vehicleview_t *pView = (vehicleview_t *)pData; + + pView->bClampEyeAngles = true; + + pView->flPitchCurveZero = PITCH_CURVE_ZERO; + pView->flPitchCurveLinear = PITCH_CURVE_LINEAR; + pView->flRollCurveZero = ROLL_CURVE_ZERO; + pView->flRollCurveLinear = ROLL_CURVE_LINEAR; + pView->flFOV = CHOREO_VEHICLE_VIEW_FOV; + pView->flYawMin = CHOREO_VEHICLE_VIEW_YAW_MIN; + pView->flYawMax = CHOREO_VEHICLE_VIEW_YAW_MAX; + pView->flPitchMin = CHOREO_VEHICLE_VIEW_PITCH_MIN; + pView->flPitchMax = CHOREO_VEHICLE_VIEW_PITCH_MAX; +} diff --git a/dlls/wcedit.h b/dlls/wcedit.h index 102109b7..f7961456 100644 --- a/dlls/wcedit.h +++ b/dlls/wcedit.h @@ -1,36 +1,36 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: Namespace for functions having to do with WC Edit mode -// -// $Workfile: $ -// $Date: $ -// -//----------------------------------------------------------------------------- -// $Log: $ -// -// $NoKeywords: $ -//=============================================================================// - -#ifndef WCEDIT_H -#define WCEDIT_H -#pragma once - -class CBaseEntity; - -//============================================================================= -// >> NWCEdit -//============================================================================= -namespace NWCEdit -{ - Vector AirNodePlacementPosition( void ); - bool IsWCVersionValid(void); - void CreateAINode( CBasePlayer *pPlayer ); - void DestroyAINode( CBasePlayer *pPlayer ); - void CreateAILink( CBasePlayer *pPlayer ); - void DestroyAILink( CBasePlayer *pPlayer ); - void UndoDestroyAINode(void); - void RememberEntityPosition( CBaseEntity *pEntity ); - void UpdateEntityPosition( CBaseEntity *pEntity ); -}; - -#endif // WCEDIT_H \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: Namespace for functions having to do with WC Edit mode +// +// $Workfile: $ +// $Date: $ +// +//----------------------------------------------------------------------------- +// $Log: $ +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef WCEDIT_H +#define WCEDIT_H +#pragma once + +class CBaseEntity; + +//============================================================================= +// >> NWCEdit +//============================================================================= +namespace NWCEdit +{ + Vector AirNodePlacementPosition( void ); + bool IsWCVersionValid(void); + void CreateAINode( CBasePlayer *pPlayer ); + void DestroyAINode( CBasePlayer *pPlayer ); + void CreateAILink( CBasePlayer *pPlayer ); + void DestroyAILink( CBasePlayer *pPlayer ); + void UndoDestroyAINode(void); + void RememberEntityPosition( CBaseEntity *pEntity ); + void UpdateEntityPosition( CBaseEntity *pEntity ); +}; + +#endif // WCEDIT_H diff --git a/dlls/world.cpp b/dlls/world.cpp index 016b3256..0c2f13c0 100644 --- a/dlls/world.cpp +++ b/dlls/world.cpp @@ -166,7 +166,7 @@ void CDecal::StaticDecal( void ) if ( pEntity->IsEffectActive( EF_NODRAW ) ) return false; - for ( int i = 0; i < ARRAYSIZE(ppszIgnoredClasses); i++ ) + for ( size_t i = 0; i < ARRAYSIZE(ppszIgnoredClasses); i++ ) { if ( pEntity->ClassMatches( ppszIgnoredClasses[i] ) ) return false; @@ -186,7 +186,7 @@ void CDecal::StaticDecal( void ) bool canDraw = true; - entityIndex = (short)trace.m_pEnt ? trace.m_pEnt->entindex() : 0; + entityIndex = trace.m_pEnt ? trace.m_pEnt->entindex() : 0; if ( entityIndex ) { CBaseEntity *ent = trace.m_pEnt; @@ -561,7 +561,7 @@ static const char *g_DefaultLightstyles[] = const char *GetDefaultLightstyleString( int styleIndex ) { - if ( styleIndex < ARRAYSIZE(g_DefaultLightstyles) ) + if ( styleIndex < static_cast(ARRAYSIZE(g_DefaultLightstyles)) ) { return g_DefaultLightstyles[styleIndex]; } @@ -647,7 +647,7 @@ void CWorld::Precache( void ) // // Setup light animation tables. 'a' is total darkness, 'z' is maxbright. // - for ( int i = 0; i < ARRAYSIZE(g_DefaultLightstyles); i++ ) + for ( int i = 0; i < static_cast(ARRAYSIZE(g_DefaultLightstyles)); i++ ) { engine->LightStyle( i, GetDefaultLightstyleString(i) ); } diff --git a/game_shared/ModelSoundsCache.cpp b/game_shared/ModelSoundsCache.cpp index 6588b66a..12661619 100644 --- a/game_shared/ModelSoundsCache.cpp +++ b/game_shared/ModelSoundsCache.cpp @@ -1,217 +1,217 @@ -//====== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======= -// -// Purpose: -// -//============================================================================= - -#include "cbase.h" -#include "ModelSoundsCache.h" -#include "studio.h" -#include "eventlist.h" -#include "scriptevent.h" - -extern ISoundEmitterSystemBase *soundemitterbase; - -CStudioHdr *ModelSoundsCache_LoadModel( char const *filename ); -void ModelSoundsCache_PrecacheScriptSound( const char *soundname ); -void ModelSoundsCache_FinishModel( CStudioHdr *hdr ); -//----------------------------------------------------------------------------- -// Purpose: -// Input : *hdr - -// Output : static void -//----------------------------------------------------------------------------- - -void VerifySequenceIndex( CStudioHdr *pstudiohdr ); - -// HACK: This must match the #define in cl_animevent.h in the client .dll code!!! -#define CL_EVENT_SOUND 5004 -#define CL_EVENT_FOOTSTEP_LEFT 6004 -#define CL_EVENT_FOOTSTEP_RIGHT 6005 -#define CL_EVENT_MFOOTSTEP_LEFT 6006 -#define CL_EVENT_MFOOTSTEP_RIGHT 6007 - - -extern ISoundEmitterSystemBase *soundemitterbase; - -CModelSoundsCache::CModelSoundsCache() -{ -} - -CModelSoundsCache::CModelSoundsCache( const CModelSoundsCache& src ) -{ - sounds = src.sounds; -} - -char const *CModelSoundsCache::GetSoundName( int index ) -{ - return soundemitterbase->GetSoundName( sounds[ index ] ); -} - -void CModelSoundsCache::Save( CUtlBuffer& buf ) -{ - buf.PutShort( sounds.Count() ); - - for ( int i = 0; i < sounds.Count(); ++i ) - { - buf.PutString( GetSoundName( i ) ); - } -} - -void CModelSoundsCache::Restore( CUtlBuffer& buf ) -{ - MEM_ALLOC_CREDIT(); - unsigned short c; - - c = (unsigned short)buf.GetShort(); - - for ( int i = 0; i < c; ++i ) - { - char soundname[ 512 ]; - - buf.GetString( soundname, sizeof( soundname ) ); - - int idx = soundemitterbase->GetSoundIndex( soundname ); - if ( idx != -1 ) - { - Assert( idx <= 65535 ); - if ( sounds.Find( idx ) == sounds.InvalidIndex() ) - { - sounds.AddToTail( (unsigned short)idx ); - } - } - } -} - -void CModelSoundsCache::Rebuild( char const *filename ) -{ - sounds.RemoveAll(); - - CStudioHdr *hdr = ModelSoundsCache_LoadModel( filename ); - - if ( hdr ) - { - // Precache all sounds referenced in animation events - BuildAnimationEventSoundList( hdr, sounds ); - ModelSoundsCache_FinishModel( hdr ); - } -} - -void CModelSoundsCache::PrecacheSoundList() -{ - for ( int i = 0; i < sounds.Count(); ++i ) - { - ModelSoundsCache_PrecacheScriptSound( GetSoundName( i ) ); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Static method -// Input : sounds - -// *soundname - -//----------------------------------------------------------------------------- -void CModelSoundsCache::FindOrAddScriptSound( CUtlVector< unsigned short >& sounds, char const *soundname ) -{ - int soundindex = soundemitterbase->GetSoundIndex( soundname ); - if ( soundindex != -1 ) - { - // Only add it once per model... - if ( sounds.Find( soundindex ) == sounds.InvalidIndex() ) - { - MEM_ALLOC_CREDIT(); - sounds.AddToTail( soundindex ); - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: Static method -// Input : *hdr - -// sounds - -//----------------------------------------------------------------------------- -void CModelSoundsCache::BuildAnimationEventSoundList( CStudioHdr *hdr, CUtlVector< unsigned short >& sounds ) -{ - Assert( hdr ); - - // force animation event resolution!!! - VerifySequenceIndex( hdr ); - - // Find all animation events which fire off sound script entries... - for ( int iSeq=0; iSeq < hdr->GetNumSeq(); iSeq++ ) - { - mstudioseqdesc_t *pSeq = &hdr->pSeqdesc( iSeq ); - - // Now read out all the sound events with their timing - for ( int iEvent=0; iEvent < (int)pSeq->numevents; iEvent++ ) - { - mstudioevent_t *pEvent = pSeq->pEvent( iEvent ); - - switch ( pEvent->event ) - { - default: - { - if ( pEvent->type & AE_TYPE_NEWEVENTSYSTEM ) - { - if ( pEvent->event == AE_SV_PLAYSOUND ) - { - FindOrAddScriptSound( sounds, pEvent->pszOptions() ); - } - } - } - break; - // Old-style client .dll animation event - case CL_EVENT_SOUND: - { - FindOrAddScriptSound( sounds, pEvent->pszOptions() ); - } - break; - case CL_EVENT_FOOTSTEP_LEFT: - case CL_EVENT_FOOTSTEP_RIGHT: - { - char soundname[256]; - char const *options = pEvent->pszOptions(); - if ( !options || !options[0] ) - { - options = "NPC_CombineS"; - } - - Q_snprintf( soundname, 256, "%s.RunFootstepLeft", options ); - FindOrAddScriptSound( sounds, soundname ); - Q_snprintf( soundname, 256, "%s.RunFootstepRight", options ); - FindOrAddScriptSound( sounds, soundname ); - Q_snprintf( soundname, 256, "%s.FootstepLeft", options ); - FindOrAddScriptSound( sounds, soundname ); - Q_snprintf( soundname, 256, "%s.FootstepRight", options ); - FindOrAddScriptSound( sounds, soundname ); - } - break; - case AE_CL_PLAYSOUND: - { - if ( !( pEvent->type & AE_TYPE_CLIENT ) ) - break; - - if ( pEvent->pszOptions()[0] ) - { - FindOrAddScriptSound( sounds, pEvent->pszOptions() ); - } - else - { - Warning( "-- Error --: empty soundname, .qc error on AE_CL_PLAYSOUND in model %s, sequence %s, animevent # %i\n", - hdr->pszName(), pSeq->pszLabel(), iEvent+1 ); - } - } - break; - case SCRIPT_EVENT_SOUND: - { - FindOrAddScriptSound( sounds, pEvent->pszOptions() ); - } - break; - - case SCRIPT_EVENT_SOUND_VOICE: - { - FindOrAddScriptSound( sounds, pEvent->pszOptions() ); - } - break; - } - } - } -} \ No newline at end of file +//====== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======= +// +// Purpose: +// +//============================================================================= + +#include "cbase.h" +#include "ModelSoundsCache.h" +#include "studio.h" +#include "eventlist.h" +#include "scriptevent.h" + +extern ISoundEmitterSystemBase *soundemitterbase; + +CStudioHdr *ModelSoundsCache_LoadModel( char const *filename ); +void ModelSoundsCache_PrecacheScriptSound( const char *soundname ); +void ModelSoundsCache_FinishModel( CStudioHdr *hdr ); +//----------------------------------------------------------------------------- +// Purpose: +// Input : *hdr - +// Output : static void +//----------------------------------------------------------------------------- + +void VerifySequenceIndex( CStudioHdr *pstudiohdr ); + +// HACK: This must match the #define in cl_animevent.h in the client .dll code!!! +#define CL_EVENT_SOUND 5004 +#define CL_EVENT_FOOTSTEP_LEFT 6004 +#define CL_EVENT_FOOTSTEP_RIGHT 6005 +#define CL_EVENT_MFOOTSTEP_LEFT 6006 +#define CL_EVENT_MFOOTSTEP_RIGHT 6007 + + +extern ISoundEmitterSystemBase *soundemitterbase; + +CModelSoundsCache::CModelSoundsCache() +{ +} + +CModelSoundsCache::CModelSoundsCache( const CModelSoundsCache& src ) +{ + sounds = src.sounds; +} + +char const *CModelSoundsCache::GetSoundName( int index ) +{ + return soundemitterbase->GetSoundName( sounds[ index ] ); +} + +void CModelSoundsCache::Save( CUtlBuffer& buf ) +{ + buf.PutShort( sounds.Count() ); + + for ( int i = 0; i < sounds.Count(); ++i ) + { + buf.PutString( GetSoundName( i ) ); + } +} + +void CModelSoundsCache::Restore( CUtlBuffer& buf ) +{ + MEM_ALLOC_CREDIT(); + unsigned short c; + + c = (unsigned short)buf.GetShort(); + + for ( int i = 0; i < c; ++i ) + { + char soundname[ 512 ]; + + buf.GetString( soundname, sizeof( soundname ) ); + + int idx = soundemitterbase->GetSoundIndex( soundname ); + if ( idx != -1 ) + { + Assert( idx <= 65535 ); + if ( sounds.Find( idx ) == sounds.InvalidIndex() ) + { + sounds.AddToTail( (unsigned short)idx ); + } + } + } +} + +void CModelSoundsCache::Rebuild( char const *filename ) +{ + sounds.RemoveAll(); + + CStudioHdr *hdr = ModelSoundsCache_LoadModel( filename ); + + if ( hdr ) + { + // Precache all sounds referenced in animation events + BuildAnimationEventSoundList( hdr, sounds ); + ModelSoundsCache_FinishModel( hdr ); + } +} + +void CModelSoundsCache::PrecacheSoundList() +{ + for ( int i = 0; i < sounds.Count(); ++i ) + { + ModelSoundsCache_PrecacheScriptSound( GetSoundName( i ) ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Static method +// Input : sounds - +// *soundname - +//----------------------------------------------------------------------------- +void CModelSoundsCache::FindOrAddScriptSound( CUtlVector< unsigned short >& sounds, char const *soundname ) +{ + int soundindex = soundemitterbase->GetSoundIndex( soundname ); + if ( soundindex != -1 ) + { + // Only add it once per model... + if ( sounds.Find( soundindex ) == sounds.InvalidIndex() ) + { + MEM_ALLOC_CREDIT(); + sounds.AddToTail( soundindex ); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Static method +// Input : *hdr - +// sounds - +//----------------------------------------------------------------------------- +void CModelSoundsCache::BuildAnimationEventSoundList( CStudioHdr *hdr, CUtlVector< unsigned short >& sounds ) +{ + Assert( hdr ); + + // force animation event resolution!!! + VerifySequenceIndex( hdr ); + + // Find all animation events which fire off sound script entries... + for ( int iSeq=0; iSeq < hdr->GetNumSeq(); iSeq++ ) + { + mstudioseqdesc_t *pSeq = &hdr->pSeqdesc( iSeq ); + + // Now read out all the sound events with their timing + for ( int iEvent=0; iEvent < (int)pSeq->numevents; iEvent++ ) + { + mstudioevent_t *pEvent = pSeq->pEvent( iEvent ); + + switch ( pEvent->event ) + { + default: + { + if ( pEvent->type & AE_TYPE_NEWEVENTSYSTEM ) + { + if ( pEvent->event == AE_SV_PLAYSOUND ) + { + FindOrAddScriptSound( sounds, pEvent->pszOptions() ); + } + } + } + break; + // Old-style client .dll animation event + case CL_EVENT_SOUND: + { + FindOrAddScriptSound( sounds, pEvent->pszOptions() ); + } + break; + case CL_EVENT_FOOTSTEP_LEFT: + case CL_EVENT_FOOTSTEP_RIGHT: + { + char soundname[256]; + char const *options = pEvent->pszOptions(); + if ( !options || !options[0] ) + { + options = "NPC_CombineS"; + } + + Q_snprintf( soundname, 256, "%s.RunFootstepLeft", options ); + FindOrAddScriptSound( sounds, soundname ); + Q_snprintf( soundname, 256, "%s.RunFootstepRight", options ); + FindOrAddScriptSound( sounds, soundname ); + Q_snprintf( soundname, 256, "%s.FootstepLeft", options ); + FindOrAddScriptSound( sounds, soundname ); + Q_snprintf( soundname, 256, "%s.FootstepRight", options ); + FindOrAddScriptSound( sounds, soundname ); + } + break; + case AE_CL_PLAYSOUND: + { + if ( !( pEvent->type & AE_TYPE_CLIENT ) ) + break; + + if ( pEvent->pszOptions()[0] ) + { + FindOrAddScriptSound( sounds, pEvent->pszOptions() ); + } + else + { + Warning( "-- Error --: empty soundname, .qc error on AE_CL_PLAYSOUND in model %s, sequence %s, animevent # %i\n", + hdr->pszName(), pSeq->pszLabel(), iEvent+1 ); + } + } + break; + case SCRIPT_EVENT_SOUND: + { + FindOrAddScriptSound( sounds, pEvent->pszOptions() ); + } + break; + + case SCRIPT_EVENT_SOUND_VOICE: + { + FindOrAddScriptSound( sounds, pEvent->pszOptions() ); + } + break; + } + } + } +} diff --git a/game_shared/SoundEmitterSystem.cpp b/game_shared/SoundEmitterSystem.cpp index 6a534c93..9b301bfa 100644 --- a/game_shared/SoundEmitterSystem.cpp +++ b/game_shared/SoundEmitterSystem.cpp @@ -894,13 +894,13 @@ public: void StopSound( int entindex, const char *soundname ) { - int soundindex = soundemitterbase->GetSoundIndex( soundname ); + short soundindex = soundemitterbase->GetSoundIndex( soundname ); if ( soundindex == -1 ) { return; } - StopSoundByHandle( entindex, soundname, (HSOUNDSCRIPTHANDLE &)soundindex ); + StopSoundByHandle( entindex, soundname, soundindex); } diff --git a/game_shared/Sprite.cpp b/game_shared/Sprite.cpp index 74a94ec3..38cb6d4f 100644 --- a/game_shared/Sprite.cpp +++ b/game_shared/Sprite.cpp @@ -580,19 +580,19 @@ void CSprite::InputShowSprite( inputdata_t &inputdata ) void CSprite::InputColorRedValue( inputdata_t &inputdata ) { - int nNewColor = clamp( inputdata.value.Float(), 0, 255 ); + int nNewColor = clamp( inputdata.value.Int(), 0, 255 ); SetColor( nNewColor, m_clrRender->g, m_clrRender->b ); } void CSprite::InputColorGreenValue( inputdata_t &inputdata ) { - int nNewColor = clamp( inputdata.value.Float(), 0, 255 ); + int nNewColor = clamp( inputdata.value.Int(), 0, 255 ); SetColor( m_clrRender->r, nNewColor, m_clrRender->b ); } void CSprite::InputColorBlueValue( inputdata_t &inputdata ) { - int nNewColor = clamp( inputdata.value.Float(), 0, 255 ); + int nNewColor = clamp( inputdata.value.Int(), 0, 255 ); SetColor( m_clrRender->r, m_clrRender->g, nNewColor ); } diff --git a/game_shared/activitylist.h b/game_shared/activitylist.h index 8d5e2b3f..9a1af908 100644 --- a/game_shared/activitylist.h +++ b/game_shared/activitylist.h @@ -19,7 +19,7 @@ class CActivityRemap { public: - CActivityRemap() + CActivityRemap() : mappedActivity(ACT_IDLE) { pExtraBlock = NULL; } diff --git a/game_shared/ammodef.cpp b/game_shared/ammodef.cpp index ba0119de..55c0e494 100644 --- a/game_shared/ammodef.cpp +++ b/game_shared/ammodef.cpp @@ -59,7 +59,7 @@ int CAmmoDef::PlrDamage(int nAmmoIndex) { if ( m_AmmoType[nAmmoIndex].pPlrDmgCVar ) { - return m_AmmoType[nAmmoIndex].pPlrDmgCVar->GetFloat(); + return m_AmmoType[nAmmoIndex].pPlrDmgCVar->GetInt(); } return 0; @@ -84,7 +84,7 @@ int CAmmoDef::NPCDamage(int nAmmoIndex) { if ( m_AmmoType[nAmmoIndex].pNPCDmgCVar ) { - return m_AmmoType[nAmmoIndex].pNPCDmgCVar->GetFloat(); + return m_AmmoType[nAmmoIndex].pNPCDmgCVar->GetInt(); } return 0; @@ -108,7 +108,7 @@ int CAmmoDef::MaxCarry(int nAmmoIndex) if ( m_AmmoType[nAmmoIndex].pMaxCarry == USE_CVAR ) { if ( m_AmmoType[nAmmoIndex].pMaxCarryCVar ) - return m_AmmoType[nAmmoIndex].pMaxCarryCVar->GetFloat(); + return m_AmmoType[nAmmoIndex].pMaxCarryCVar->GetInt(); return 0; } diff --git a/game_shared/ammodef.h b/game_shared/ammodef.h index 84968ec9..ec1cf56a 100644 --- a/game_shared/ammodef.h +++ b/game_shared/ammodef.h @@ -1,103 +1,103 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: Holds defintion for game ammo types -// -// $Workfile: $ -// $Date: $ -// $NoKeywords: $ -//=============================================================================// - -#ifndef AI_AMMODEF_H -#define AI_AMMODEF_H - -#ifdef _WIN32 -#pragma once -#endif - -class ConVar; - -struct Ammo_t -{ - char *pName; - int nDamageType; - int eTracerType; - float physicsForceImpulse; - int nMinSplashSize; - int nMaxSplashSize; - - int nFlags; - - // Values for player/NPC damage and carrying capability - // If the integers are set, they override the CVars - int pPlrDmg; // CVar for player damage amount - int pNPCDmg; // CVar for NPC damage amount - int pMaxCarry; // CVar for maximum number can carry - const ConVar* pPlrDmgCVar; // CVar for player damage amount - const ConVar* pNPCDmgCVar; // CVar for NPC damage amount - const ConVar* pMaxCarryCVar; // CVar for maximum number can carry -}; - -// Used to tell AmmoDef to use the cvars, not the integers -#define USE_CVAR -1 -// Ammo is infinite -#define INFINITE_AMMO -2 - -enum AmmoTracer_t -{ - TRACER_NONE, - TRACER_LINE, - TRACER_RAIL, - TRACER_BEAM, - TRACER_LINE_AND_WHIZ, -}; - -enum AmmoFlags_t -{ - AMMO_FORCE_DROP_IF_CARRIED = 0x1, - AMMO_INTERPRET_PLRDAMAGE_AS_DAMAGE_TO_PLAYER = 0x2, -}; - - -#include "shareddefs.h" - -//============================================================================= -// >> CAmmoDef -//============================================================================= -class CAmmoDef -{ - -public: - int m_nAmmoIndex; - - Ammo_t m_AmmoType[MAX_AMMO_TYPES]; - - Ammo_t *GetAmmoOfIndex(int nAmmoIndex); - int Index(const char *psz); - int PlrDamage(int nAmmoIndex); - int NPCDamage(int nAmmoIndex); - int MaxCarry(int nAmmoIndex); - int DamageType(int nAmmoIndex); - int TracerType(int nAmmoIndex); - float DamageForce(int nAmmoIndex); - int MinSplashSize(int nAmmoIndex); - int MaxSplashSize(int nAmmoIndex); - int Flags(int nAmmoIndex); - - void AddAmmoType(char const* name, int damageType, int tracerType, int plr_dmg, int npc_dmg, int carry, float physicsForceImpulse, int nFlags, int minSplashSize = 4, int maxSplashSize = 8 ); - void AddAmmoType(char const* name, int damageType, int tracerType, char const* plr_cvar, char const* npc_var, char const* carry_cvar, float physicsForceImpulse, int nFlags, int minSplashSize = 4, int maxSplashSize = 8 ); - - CAmmoDef(void); - virtual ~CAmmoDef( void ); - -private: - bool AddAmmoType(char const* name, int damageType, int tracerType, int nFlags, int minSplashSize, int maxSplashSize ); -}; - - -// Get the global ammodef object. This is usually implemented in each mod's game rules file somewhere, -// so the mod can setup custom ammo types. -CAmmoDef* GetAmmoDef(); - - -#endif // AI_AMMODEF_H - \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: Holds defintion for game ammo types +// +// $Workfile: $ +// $Date: $ +// $NoKeywords: $ +//=============================================================================// + +#ifndef AI_AMMODEF_H +#define AI_AMMODEF_H + +#ifdef _WIN32 +#pragma once +#endif + +class ConVar; + +struct Ammo_t +{ + char *pName; + int nDamageType; + int eTracerType; + float physicsForceImpulse; + int nMinSplashSize; + int nMaxSplashSize; + + int nFlags; + + // Values for player/NPC damage and carrying capability + // If the integers are set, they override the CVars + int pPlrDmg; // CVar for player damage amount + int pNPCDmg; // CVar for NPC damage amount + int pMaxCarry; // CVar for maximum number can carry + const ConVar* pPlrDmgCVar; // CVar for player damage amount + const ConVar* pNPCDmgCVar; // CVar for NPC damage amount + const ConVar* pMaxCarryCVar; // CVar for maximum number can carry +}; + +// Used to tell AmmoDef to use the cvars, not the integers +#define USE_CVAR -1 +// Ammo is infinite +#define INFINITE_AMMO -2 + +enum AmmoTracer_t +{ + TRACER_NONE, + TRACER_LINE, + TRACER_RAIL, + TRACER_BEAM, + TRACER_LINE_AND_WHIZ, +}; + +enum AmmoFlags_t +{ + AMMO_FORCE_DROP_IF_CARRIED = 0x1, + AMMO_INTERPRET_PLRDAMAGE_AS_DAMAGE_TO_PLAYER = 0x2, +}; + + +#include "shareddefs.h" + +//============================================================================= +// >> CAmmoDef +//============================================================================= +class CAmmoDef +{ + +public: + int m_nAmmoIndex; + + Ammo_t m_AmmoType[MAX_AMMO_TYPES]; + + Ammo_t *GetAmmoOfIndex(int nAmmoIndex); + int Index(const char *psz); + int PlrDamage(int nAmmoIndex); + int NPCDamage(int nAmmoIndex); + int MaxCarry(int nAmmoIndex); + int DamageType(int nAmmoIndex); + int TracerType(int nAmmoIndex); + float DamageForce(int nAmmoIndex); + int MinSplashSize(int nAmmoIndex); + int MaxSplashSize(int nAmmoIndex); + int Flags(int nAmmoIndex); + + void AddAmmoType(char const* name, int damageType, int tracerType, int plr_dmg, int npc_dmg, int carry, float physicsForceImpulse, int nFlags, int minSplashSize = 4, int maxSplashSize = 8 ); + void AddAmmoType(char const* name, int damageType, int tracerType, char const* plr_cvar, char const* npc_var, char const* carry_cvar, float physicsForceImpulse, int nFlags, int minSplashSize = 4, int maxSplashSize = 8 ); + + CAmmoDef(void); + virtual ~CAmmoDef( void ); + +private: + bool AddAmmoType(char const* name, int damageType, int tracerType, int nFlags, int minSplashSize, int maxSplashSize ); +}; + + +// Get the global ammodef object. This is usually implemented in each mod's game rules file somewhere, +// so the mod can setup custom ammo types. +CAmmoDef* GetAmmoDef(); + + +#endif // AI_AMMODEF_H + diff --git a/game_shared/animation.cpp b/game_shared/animation.cpp index b7f1d14a..e9325678 100644 --- a/game_shared/animation.cpp +++ b/game_shared/animation.cpp @@ -25,7 +25,9 @@ // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" +#ifdef _MSC_VER #pragma warning( disable : 4244 ) +#endif #define iabs(i) (( (i) >= 0 ) ? (i) : -(i) ) int ExtractBbox( CStudioHdr *pstudiohdr, int sequence, Vector& mins, Vector& maxs ) diff --git a/game_shared/basecombatweapon_shared.cpp b/game_shared/basecombatweapon_shared.cpp index c10f44a9..10e24e9e 100644 --- a/game_shared/basecombatweapon_shared.cpp +++ b/game_shared/basecombatweapon_shared.cpp @@ -1606,7 +1606,7 @@ void CBaseCombatWeapon::WeaponSound( WeaponSound_t sound_type, float soundtime / #if !defined( CLIENT_DLL ) if( sound_type == EMPTY ) { - CSoundEnt::InsertSound( SOUND_COMBAT, GetOwner()->GetAbsOrigin(), SOUNDENT_VOLUME_EMPTY, 0.2, GetOwner() ); + CSoundEnt::InsertSound( SOUND_COMBAT, GetOwner()->GetAbsOrigin(), static_cast(SOUNDENT_VOLUME_EMPTY), 0.2, GetOwner() ); } #endif } diff --git a/game_shared/baseentity_shared.cpp b/game_shared/baseentity_shared.cpp index 2770088e..4ddb039b 100644 --- a/game_shared/baseentity_shared.cpp +++ b/game_shared/baseentity_shared.cpp @@ -619,7 +619,7 @@ BASEPTR CBaseEntity::ThinkSet( BASEPTR func, float thinkTime, const char *szCont { #if !defined( CLIENT_DLL ) #ifdef _DEBUG - COMPILE_TIME_ASSERT( sizeof(func) == 4 ); + COMPILE_TIME_ASSERT( sizeof(func) == MFP_SIZE ); #endif #endif @@ -1001,6 +1001,9 @@ void CBaseEntity::VPhysicsUpdate( IPhysicsObject *pPhysics ) VPhysicsUpdatePusher( pPhysics ); #endif break; + + default: + break; } } diff --git a/game_shared/basegrenade_shared.cpp b/game_shared/basegrenade_shared.cpp index db5a58d5..279c511e 100644 --- a/game_shared/basegrenade_shared.cpp +++ b/game_shared/basegrenade_shared.cpp @@ -307,7 +307,7 @@ void CBaseGrenade::DangerSoundThink( void ) } #if !defined( CLIENT_DLL ) - CSoundEnt::InsertSound ( SOUND_DANGER, GetAbsOrigin() + GetAbsVelocity() * 0.5, GetAbsVelocity().Length( ), 0.2, this ); + CSoundEnt::InsertSound ( SOUND_DANGER, GetAbsOrigin() + GetAbsVelocity() * 0.5, static_cast(GetAbsVelocity().Length()), 0.2, this ); #endif SetNextThink( gpGlobals->curtime + 0.2 ); @@ -364,7 +364,7 @@ void CBaseGrenade::BounceTouch( CBaseEntity *pOther ) // register a radius louder than the explosion, so we make sure everyone gets out of the way #if !defined( CLIENT_DLL ) - CSoundEnt::InsertSound ( SOUND_DANGER, GetAbsOrigin(), m_flDamage / 0.4, 0.3, this ); + CSoundEnt::InsertSound ( SOUND_DANGER, GetAbsOrigin(), static_cast(m_flDamage / 0.4), 0.3, this ); #endif m_bHasWarnedAI = true; } diff --git a/game_shared/baseplayer_shared.cpp b/game_shared/baseplayer_shared.cpp index 9e4f5e6e..62512485 100644 --- a/game_shared/baseplayer_shared.cpp +++ b/game_shared/baseplayer_shared.cpp @@ -1117,7 +1117,7 @@ void CBasePlayer::PlayerUse ( void ) if ( pTrain && !(m_nButtons & IN_JUMP) && (GetFlags() & FL_ONGROUND) && (pTrain->ObjectCaps() & FCAP_DIRECTIONAL_USE) && pTrain->OnControls(this) ) { m_afPhysicsFlags |= PFLAG_DIROVERRIDE; - m_iTrain = TrainSpeed(pTrain->m_flSpeed, ((CFuncTrackTrain*)pTrain)->GetMaxSpeed()); + m_iTrain = TrainSpeed(static_cast(pTrain->m_flSpeed), static_cast(((CFuncTrackTrain*)pTrain)->GetMaxSpeed())); m_iTrain |= TRAIN_NEW; EmitSound( "Player.UseTrain" ); return; @@ -1267,7 +1267,7 @@ void CBasePlayer::SmoothViewOnStairs( Vector& eyeOrigin ) } } -static bool IsWaterContents( int contents ) +/*static bool IsWaterContents( int contents ) { if ( contents & MASK_WATER ) return true; @@ -1276,7 +1276,7 @@ static bool IsWaterContents( int contents ) // return true; return false; -} +}*/ void CBasePlayer::ResetObserverMode() { diff --git a/game_shared/beam_flags.h b/game_shared/beam_flags.h index a7f2e65f..7268d288 100644 --- a/game_shared/beam_flags.h +++ b/game_shared/beam_flags.h @@ -1,37 +1,37 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $Workfile: $ -// $Date: $ -// $NoKeywords: $ -//=============================================================================// - -#if !defined( BEAM_FLAGS_H ) -#define BEAM_FLAGS_H -#ifdef _WIN32 -#pragma once -#endif - -enum -{ - FBEAM_STARTENTITY = 0x00000001, - FBEAM_ENDENTITY = 0x00000002, - FBEAM_FADEIN = 0x00000004, - FBEAM_FADEOUT = 0x00000008, - FBEAM_SINENOISE = 0x00000010, - FBEAM_SOLID = 0x00000020, - FBEAM_SHADEIN = 0x00000040, - FBEAM_SHADEOUT = 0x00000080, - FBEAM_ONLYNOISEONCE = 0x00000100, // Only calculate our noise once - FBEAM_NOTILE = 0x00000200, - FBEAM_USE_HITBOXES = 0x00000400, // Attachment indices represent hitbox indices instead when this is set. - FBEAM_STARTVISIBLE = 0x00000800, // Has this client actually seen this beam's start entity yet? - FBEAM_ENDVISIBLE = 0x00001000, // Has this client actually seen this beam's end entity yet? - FBEAM_ISACTIVE = 0x00002000, - FBEAM_FOREVER = 0x00004000, - FBEAM_HALOBEAM = 0x00008000, // When drawing a beam with a halo, don't ignore the segments and endwidth - NUM_BEAM_FLAGS = 16 // KEEP THIS UPDATED! -}; - -#endif // BEAM_FLAGS_H \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $Workfile: $ +// $Date: $ +// $NoKeywords: $ +//=============================================================================// + +#if !defined( BEAM_FLAGS_H ) +#define BEAM_FLAGS_H +#ifdef _WIN32 +#pragma once +#endif + +enum +{ + FBEAM_STARTENTITY = 0x00000001, + FBEAM_ENDENTITY = 0x00000002, + FBEAM_FADEIN = 0x00000004, + FBEAM_FADEOUT = 0x00000008, + FBEAM_SINENOISE = 0x00000010, + FBEAM_SOLID = 0x00000020, + FBEAM_SHADEIN = 0x00000040, + FBEAM_SHADEOUT = 0x00000080, + FBEAM_ONLYNOISEONCE = 0x00000100, // Only calculate our noise once + FBEAM_NOTILE = 0x00000200, + FBEAM_USE_HITBOXES = 0x00000400, // Attachment indices represent hitbox indices instead when this is set. + FBEAM_STARTVISIBLE = 0x00000800, // Has this client actually seen this beam's start entity yet? + FBEAM_ENDVISIBLE = 0x00001000, // Has this client actually seen this beam's end entity yet? + FBEAM_ISACTIVE = 0x00002000, + FBEAM_FOREVER = 0x00004000, + FBEAM_HALOBEAM = 0x00008000, // When drawing a beam with a halo, don't ignore the segments and endwidth + NUM_BEAM_FLAGS = 16 // KEEP THIS UPDATED! +}; + +#endif // BEAM_FLAGS_H diff --git a/game_shared/beam_shared.cpp b/game_shared/beam_shared.cpp index 1233570d..4c5a1c75 100644 --- a/game_shared/beam_shared.cpp +++ b/game_shared/beam_shared.cpp @@ -758,19 +758,19 @@ void CBeam::InputWidth( inputdata_t &inputdata ) void CBeam::InputColorRedValue( inputdata_t &inputdata ) { - int nNewColor = clamp( inputdata.value.Float(), 0, 255 ); + int nNewColor = clamp( inputdata.value.Int(), 0, 255 ); SetColor( nNewColor, m_clrRender->g, m_clrRender->b ); } void CBeam::InputColorGreenValue( inputdata_t &inputdata ) { - int nNewColor = clamp( inputdata.value.Float(), 0, 255 ); + int nNewColor = clamp( inputdata.value.Int(), 0, 255 ); SetColor( m_clrRender->r, nNewColor, m_clrRender->b ); } void CBeam::InputColorBlueValue( inputdata_t &inputdata ) { - int nNewColor = clamp( inputdata.value.Float(), 0, 255 ); + int nNewColor = clamp( inputdata.value.Int(), 0, 255 ); SetColor( m_clrRender->r, m_clrRender->g, nNewColor ); } diff --git a/game_shared/debugoverlay_shared.cpp b/game_shared/debugoverlay_shared.cpp index 1209d132..2d416759 100644 --- a/game_shared/debugoverlay_shared.cpp +++ b/game_shared/debugoverlay_shared.cpp @@ -335,7 +335,7 @@ void NDebugOverlay::DrawTickMarkedLine(const Vector &startPos, const Vector &end Vector lineDir = (endPos - startPos); float lineDist = VectorNormalize( lineDir ); - int numTicks = lineDist/tickDist; + int numTicks = static_cast(lineDist / tickDist); Vector vBodyDir; #if defined( CLIENT_DLL ) diff --git a/game_shared/effect_color_tables.h b/game_shared/effect_color_tables.h index d52420ee..080c3642 100644 --- a/game_shared/effect_color_tables.h +++ b/game_shared/effect_color_tables.h @@ -31,12 +31,13 @@ enum }; // Commander mode table +#ifdef CLIENT_DLL static colorentry_t commandercolors[] = { - { COMMAND_POINT_RED, 1.0, 0.0, 0.0 }, - { COMMAND_POINT_BLUE, 0.0, 0.0, 1.0 }, - { COMMAND_POINT_GREEN, 0.0, 1.0, 0.0 }, - { COMMAND_POINT_YELLOW, 1.0, 1.0, 0.0 }, + { COMMAND_POINT_RED, 1, 0, 0 }, + { COMMAND_POINT_BLUE, 0, 0, 1 }, + { COMMAND_POINT_GREEN, 0, 1, 0 }, + { COMMAND_POINT_YELLOW, 1, 1, 0 }, }; static colorentry_t bloodcolors[] = @@ -45,5 +46,6 @@ static colorentry_t bloodcolors[] = { BLOOD_COLOR_YELLOW, 195, 195, 0 }, { BLOOD_COLOR_MECH, 20, 20, 20 }, }; +#endif #endif // EFFECT_COLOR_TABLES_H diff --git a/game_shared/env_wind_shared.cpp b/game_shared/env_wind_shared.cpp index e6b17ff5..bc0a1868 100644 --- a/game_shared/env_wind_shared.cpp +++ b/game_shared/env_wind_shared.cpp @@ -257,7 +257,7 @@ float CEnvWindShared::WindThink( float flTime ) m_flAveWindSpeed = m_Stream.RandomInt( m_iMinGust, m_iMaxGust ); // change wind direction, maybe a lot - m_iWindDir = anglemod( m_iWindDir + m_Stream.RandomInt(-m_iGustDirChange, m_iGustDirChange) ); + m_iWindDir = static_cast(anglemod( m_iWindDir + m_Stream.RandomInt(-m_iGustDirChange, m_iGustDirChange) )); // set up to stop the gust in a short while m_bGusting = true; diff --git a/game_shared/eventlist.cpp b/game_shared/eventlist.cpp index 494e03cf..5b75a0b0 100644 --- a/game_shared/eventlist.cpp +++ b/game_shared/eventlist.cpp @@ -1,233 +1,232 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -// -//=============================================================================// -#include "cbase.h" -#include "eventlist.h" -#include "stringregistry.h" - -// memdbgon must be the last include file in a .cpp file!!! -#include "tier0/memdbgon.h" - -// NOTE: If CStringRegistry allowed storing arbitrary data, we could just use that. -// in this case we have the "isPrivate" member and the replacement rules -// (eventIndex can be reused by private activities), so a custom table is necessary -struct eventlist_t -{ - int eventIndex; - int iType; - unsigned short stringKey; - short isPrivate; -}; - -CUtlVector g_EventList; - -// This stores the actual event names. Also, the string ID in the registry is simply an index -// into the g_EventList array. -CStringRegistry g_EventStrings; - -// this is just here to accelerate adds -static int g_HighestEvent = 0; - -int g_nEventListVersion = 1; - - -void EventList_Init( void ) -{ - g_HighestEvent = 0; -} - -void EventList_Free( void ) -{ - g_EventStrings.ClearStrings(); - g_EventList.Purge(); - - // So studiohdrs can reindex event indices - ++g_nEventListVersion; -} - -// add a new event to the database -eventlist_t *EventList_AddEventEntry( const char *pName, int iEventIndex, bool isPrivate, int iType ) -{ - MEM_ALLOC_CREDIT(); - int index = g_EventList.AddToTail(); - eventlist_t *pList = &g_EventList[index]; - pList->eventIndex = iEventIndex; - pList->stringKey = g_EventStrings.AddString( pName, index ); - pList->isPrivate = isPrivate; - pList->iType = iType; - - // UNDONE: This implies that ALL shared activities are added before ANY custom activities - // UNDONE: Segment these instead? It's a 32-bit int, how many activities do we need? - if ( iEventIndex > g_HighestEvent ) - { - g_HighestEvent = iEventIndex; - } - - return pList; -} - -// get the database entry from a string -static eventlist_t *ListFromString( const char *pString ) -{ - // just use the string registry to do this search/map - int stringID = g_EventStrings.GetStringID( pString ); - if ( stringID < 0 ) - return NULL; - - return &g_EventList[stringID]; -} - -// Get the database entry for an index -static eventlist_t *ListFromEvent( int eventIndex ) -{ - // ugly linear search - for ( int i = 0; i < g_EventList.Size(); i++ ) - { - if ( g_EventList[i].eventIndex == eventIndex ) - { - return &g_EventList[i]; - } - } - - return NULL; -} - -int EventList_GetEventType( int eventIndex ) -{ - eventlist_t *pEvent = ListFromEvent( eventIndex ); - - if ( pEvent ) - { - return pEvent->iType; - } - - return -1; -} - - -bool EventList_RegisterSharedEvent( const char *pszEventName, int iEventIndex, int iType ) -{ - // UNDONE: Do we want to do these checks when not in developer mode? or maybe DEBUG only? - // They really only matter when you change the list of code controlled activities. IDs - // for content controlled activities never collide because they are generated. - - // first, check to make sure the slot we're asking for is free. It must be for - // a shared event. - eventlist_t *pList = ListFromString( pszEventName ); - if ( !pList ) - { - pList = ListFromEvent( iEventIndex ); - } - - //Already in list. - if ( pList ) - { - return false; - } - // ---------------------------------------------------------------- - - EventList_AddEventEntry( pszEventName, iEventIndex, false, iType ); - return true; -} - -Animevent EventList_RegisterPrivateEvent( const char *pszEventName ) -{ - eventlist_t *pList = ListFromString( pszEventName ); - if ( pList ) - { - // this activity is already in the list. If the activity we collided with is also private, - // then the collision is OK. Otherwise, it's a bug. - if ( pList->isPrivate ) - { - return (Animevent)pList->eventIndex; - } - else - { - // this private activity collides with a shared activity. That is not allowed. - Warning( "***\nShared<->Private Event collision!\n***\n" ); - Assert(0); - return AE_INVALID; - } - } - - pList = EventList_AddEventEntry( pszEventName, g_HighestEvent+1, true, AE_TYPE_SERVER ); - return (Animevent)pList->eventIndex; -} - -// Get the index for a given Event name -// Done at load time for all models -int EventList_IndexForName( const char *pszEventName ) -{ - // this is a fast O(lgn) search (actually does 2 O(lgn) searches) - eventlist_t *pList = ListFromString( pszEventName ); - - if ( pList ) - { - return pList->eventIndex; - } - - return -1; -} - -// Get the name for a given index -// This should only be used in debug code, it does a linear search -// But at least it only compares integers -const char *EventList_NameForIndex( int eventIndex ) -{ - eventlist_t *pList = ListFromEvent( eventIndex ); - if ( pList ) - { - return g_EventStrings.GetStringForKey( pList->stringKey ); - } - return NULL; -} - -void EventList_RegisterSharedEvents( void ) -{ - REGISTER_SHARED_ANIMEVENT( AE_EMPTY, AE_TYPE_SERVER ); - - REGISTER_SHARED_ANIMEVENT( AE_NPC_LEFTFOOT, AE_TYPE_SERVER ); - REGISTER_SHARED_ANIMEVENT( AE_NPC_RIGHTFOOT, AE_TYPE_SERVER ); - REGISTER_SHARED_ANIMEVENT( AE_NPC_BODYDROP_LIGHT, AE_TYPE_SERVER ); - REGISTER_SHARED_ANIMEVENT( AE_NPC_BODYDROP_HEAVY, AE_TYPE_SERVER ); - REGISTER_SHARED_ANIMEVENT( AE_NPC_SWISHSOUND, AE_TYPE_SERVER ); - REGISTER_SHARED_ANIMEVENT( AE_NPC_180TURN, AE_TYPE_SERVER ); - REGISTER_SHARED_ANIMEVENT( AE_NPC_ITEM_PICKUP, AE_TYPE_SERVER ); - REGISTER_SHARED_ANIMEVENT( AE_NPC_WEAPON_DROP, AE_TYPE_SERVER ); - REGISTER_SHARED_ANIMEVENT( AE_NPC_WEAPON_SET_SEQUENCE_NAME, AE_TYPE_SERVER ); - REGISTER_SHARED_ANIMEVENT( AE_NPC_WEAPON_SET_SEQUENCE_NUMBER, AE_TYPE_SERVER ); - REGISTER_SHARED_ANIMEVENT( AE_NPC_WEAPON_SET_ACTIVITY, AE_TYPE_SERVER ); - REGISTER_SHARED_ANIMEVENT( AE_NPC_HOLSTER, AE_TYPE_SERVER ); - REGISTER_SHARED_ANIMEVENT( AE_NPC_DRAW, AE_TYPE_SERVER ); - REGISTER_SHARED_ANIMEVENT( AE_NPC_WEAPON_FIRE, AE_TYPE_SERVER | AE_TYPE_WEAPON ); - - REGISTER_SHARED_ANIMEVENT( AE_CL_PLAYSOUND, AE_TYPE_CLIENT ); - REGISTER_SHARED_ANIMEVENT( AE_SV_PLAYSOUND, AE_TYPE_SERVER ); - REGISTER_SHARED_ANIMEVENT( AE_CL_STOPSOUND, AE_TYPE_CLIENT ); - - REGISTER_SHARED_ANIMEVENT( AE_START_SCRIPTED_EFFECT, AE_TYPE_SERVER ); - REGISTER_SHARED_ANIMEVENT( AE_STOP_SCRIPTED_EFFECT, AE_TYPE_SERVER ); - - REGISTER_SHARED_ANIMEVENT( AE_CLIENT_EFFECT_ATTACH, AE_TYPE_CLIENT ); - - REGISTER_SHARED_ANIMEVENT( AE_MUZZLEFLASH, AE_TYPE_CLIENT ); - REGISTER_SHARED_ANIMEVENT( AE_NPC_MUZZLEFLASH, AE_TYPE_CLIENT ); - - REGISTER_SHARED_ANIMEVENT( AE_THUMPER_THUMP, AE_TYPE_SERVER ); - REGISTER_SHARED_ANIMEVENT( AE_AMMOCRATE_PICKUP_AMMO, AE_TYPE_SERVER ); - - REGISTER_SHARED_ANIMEVENT( AE_NPC_RAGDOLL, AE_TYPE_SERVER ); - - REGISTER_SHARED_ANIMEVENT( AE_NPC_ADDGESTURE, AE_TYPE_SERVER ); - REGISTER_SHARED_ANIMEVENT( AE_NPC_RESTARTGESTURE, AE_TYPE_SERVER ); - - REGISTER_SHARED_ANIMEVENT( AE_NPC_ATTACK_BROADCAST, AE_TYPE_SERVER ); - - REGISTER_SHARED_ANIMEVENT( AE_NPC_HURT_INTERACTION_PARTNER, AE_TYPE_SERVER ); - REGISTER_SHARED_ANIMEVENT( AE_NPC_SET_INTERACTION_CANTDIE, AE_TYPE_SERVER ); - -} \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// +#include "cbase.h" +#include "eventlist.h" +#include "stringregistry.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +// NOTE: If CStringRegistry allowed storing arbitrary data, we could just use that. +// in this case we have the "isPrivate" member and the replacement rules +// (eventIndex can be reused by private activities), so a custom table is necessary +struct eventlist_t +{ + int eventIndex; + int iType; + unsigned short stringKey; + short isPrivate; +}; + +CUtlVector g_EventList; + +// This stores the actual event names. Also, the string ID in the registry is simply an index +// into the g_EventList array. +CStringRegistry g_EventStrings; + +// this is just here to accelerate adds +static int g_HighestEvent = 0; + +int g_nEventListVersion = 1; + + +void EventList_Init( void ) +{ + g_HighestEvent = 0; +} + +void EventList_Free( void ) +{ + g_EventStrings.ClearStrings(); + g_EventList.Purge(); + + // So studiohdrs can reindex event indices + ++g_nEventListVersion; +} + +// add a new event to the database +eventlist_t *EventList_AddEventEntry( const char *pName, int iEventIndex, bool isPrivate, int iType ) +{ + MEM_ALLOC_CREDIT(); + int index = g_EventList.AddToTail(); + eventlist_t *pList = &g_EventList[index]; + pList->eventIndex = iEventIndex; + pList->stringKey = g_EventStrings.AddString( pName, index ); + pList->isPrivate = isPrivate; + pList->iType = iType; + + // UNDONE: This implies that ALL shared activities are added before ANY custom activities + // UNDONE: Segment these instead? It's a 32-bit int, how many activities do we need? + if ( iEventIndex > g_HighestEvent ) + { + g_HighestEvent = iEventIndex; + } + + return pList; +} + +// get the database entry from a string +static eventlist_t *ListFromString( const char *pString ) +{ + // just use the string registry to do this search/map + int stringID = g_EventStrings.GetStringID( pString ); + if ( stringID < 0 ) + return NULL; + + return &g_EventList[stringID]; +} + +// Get the database entry for an index +static eventlist_t *ListFromEvent( int eventIndex ) +{ + // ugly linear search + for ( int i = 0; i < g_EventList.Size(); i++ ) + { + if ( g_EventList[i].eventIndex == eventIndex ) + { + return &g_EventList[i]; + } + } + + return NULL; +} + +int EventList_GetEventType( int eventIndex ) +{ + eventlist_t *pEvent = ListFromEvent( eventIndex ); + + if ( pEvent ) + { + return pEvent->iType; + } + + return -1; +} + + +bool EventList_RegisterSharedEvent( const char *pszEventName, int iEventIndex, int iType ) +{ + // UNDONE: Do we want to do these checks when not in developer mode? or maybe DEBUG only? + // They really only matter when you change the list of code controlled activities. IDs + // for content controlled activities never collide because they are generated. + + // first, check to make sure the slot we're asking for is free. It must be for + // a shared event. + eventlist_t *pList = ListFromString( pszEventName ); + if ( !pList ) + { + pList = ListFromEvent( iEventIndex ); + } + + //Already in list. + if ( pList ) + { + return false; + } + // ---------------------------------------------------------------- + + EventList_AddEventEntry( pszEventName, iEventIndex, false, iType ); + return true; +} + +Animevent EventList_RegisterPrivateEvent( const char *pszEventName ) +{ + eventlist_t *pList = ListFromString( pszEventName ); + if ( pList ) + { + // this activity is already in the list. If the activity we collided with is also private, + // then the collision is OK. Otherwise, it's a bug. + if ( pList->isPrivate ) + { + return (Animevent)pList->eventIndex; + } + else + { + // this private activity collides with a shared activity. That is not allowed. + Warning( "***\nShared<->Private Event collision!\n***\n" ); + Assert(0); + return AE_INVALID; + } + } + + pList = EventList_AddEventEntry( pszEventName, g_HighestEvent+1, true, AE_TYPE_SERVER ); + return (Animevent)pList->eventIndex; +} + +// Get the index for a given Event name +// Done at load time for all models +int EventList_IndexForName( const char *pszEventName ) +{ + // this is a fast O(lgn) search (actually does 2 O(lgn) searches) + eventlist_t *pList = ListFromString( pszEventName ); + + if ( pList ) + { + return pList->eventIndex; + } + + return -1; +} + +// Get the name for a given index +// This should only be used in debug code, it does a linear search +// But at least it only compares integers +const char *EventList_NameForIndex( int eventIndex ) +{ + eventlist_t *pList = ListFromEvent( eventIndex ); + if ( pList ) + { + return g_EventStrings.GetStringForKey( pList->stringKey ); + } + return NULL; +} + +void EventList_RegisterSharedEvents( void ) +{ + REGISTER_SHARED_ANIMEVENT( AE_EMPTY, AE_TYPE_SERVER ); + + REGISTER_SHARED_ANIMEVENT( AE_NPC_LEFTFOOT, AE_TYPE_SERVER ); + REGISTER_SHARED_ANIMEVENT( AE_NPC_RIGHTFOOT, AE_TYPE_SERVER ); + REGISTER_SHARED_ANIMEVENT( AE_NPC_BODYDROP_LIGHT, AE_TYPE_SERVER ); + REGISTER_SHARED_ANIMEVENT( AE_NPC_BODYDROP_HEAVY, AE_TYPE_SERVER ); + REGISTER_SHARED_ANIMEVENT( AE_NPC_SWISHSOUND, AE_TYPE_SERVER ); + REGISTER_SHARED_ANIMEVENT( AE_NPC_180TURN, AE_TYPE_SERVER ); + REGISTER_SHARED_ANIMEVENT( AE_NPC_ITEM_PICKUP, AE_TYPE_SERVER ); + REGISTER_SHARED_ANIMEVENT( AE_NPC_WEAPON_DROP, AE_TYPE_SERVER ); + REGISTER_SHARED_ANIMEVENT( AE_NPC_WEAPON_SET_SEQUENCE_NAME, AE_TYPE_SERVER ); + REGISTER_SHARED_ANIMEVENT( AE_NPC_WEAPON_SET_SEQUENCE_NUMBER, AE_TYPE_SERVER ); + REGISTER_SHARED_ANIMEVENT( AE_NPC_WEAPON_SET_ACTIVITY, AE_TYPE_SERVER ); + REGISTER_SHARED_ANIMEVENT( AE_NPC_HOLSTER, AE_TYPE_SERVER ); + REGISTER_SHARED_ANIMEVENT( AE_NPC_DRAW, AE_TYPE_SERVER ); + REGISTER_SHARED_ANIMEVENT( AE_NPC_WEAPON_FIRE, AE_TYPE_SERVER | AE_TYPE_WEAPON ); + + REGISTER_SHARED_ANIMEVENT( AE_CL_PLAYSOUND, AE_TYPE_CLIENT ); + REGISTER_SHARED_ANIMEVENT( AE_SV_PLAYSOUND, AE_TYPE_SERVER ); + REGISTER_SHARED_ANIMEVENT( AE_CL_STOPSOUND, AE_TYPE_CLIENT ); + + REGISTER_SHARED_ANIMEVENT( AE_START_SCRIPTED_EFFECT, AE_TYPE_SERVER ); + REGISTER_SHARED_ANIMEVENT( AE_STOP_SCRIPTED_EFFECT, AE_TYPE_SERVER ); + + REGISTER_SHARED_ANIMEVENT( AE_CLIENT_EFFECT_ATTACH, AE_TYPE_CLIENT ); + + REGISTER_SHARED_ANIMEVENT( AE_MUZZLEFLASH, AE_TYPE_CLIENT ); + REGISTER_SHARED_ANIMEVENT( AE_NPC_MUZZLEFLASH, AE_TYPE_CLIENT ); + + REGISTER_SHARED_ANIMEVENT( AE_THUMPER_THUMP, AE_TYPE_SERVER ); + REGISTER_SHARED_ANIMEVENT( AE_AMMOCRATE_PICKUP_AMMO, AE_TYPE_SERVER ); + + REGISTER_SHARED_ANIMEVENT( AE_NPC_RAGDOLL, AE_TYPE_SERVER ); + + REGISTER_SHARED_ANIMEVENT( AE_NPC_ADDGESTURE, AE_TYPE_SERVER ); + REGISTER_SHARED_ANIMEVENT( AE_NPC_RESTARTGESTURE, AE_TYPE_SERVER ); + + REGISTER_SHARED_ANIMEVENT( AE_NPC_ATTACK_BROADCAST, AE_TYPE_SERVER ); + + REGISTER_SHARED_ANIMEVENT( AE_NPC_HURT_INTERACTION_PARTNER, AE_TYPE_SERVER ); + REGISTER_SHARED_ANIMEVENT( AE_NPC_SET_INTERACTION_CANTDIE, AE_TYPE_SERVER ); +} diff --git a/game_shared/func_ladder.cpp b/game_shared/func_ladder.cpp index 7f5d3b11..ef059cae 100644 --- a/game_shared/func_ladder.cpp +++ b/game_shared/func_ladder.cpp @@ -87,7 +87,7 @@ void CFuncLadder::Spawn() m_vecPlayerMountPositionBottom.GetZ(), bottomtrace.m_pEnt ? - UTIL_VarArgs( "%s/%s", bottomtrace.m_pEnt->GetClassname(), bottomtrace.m_pEnt->GetEntityName() ) + UTIL_VarArgs( "%s/%s", bottomtrace.m_pEnt->GetClassname(), STRING(bottomtrace.m_pEnt->GetEntityName()) ) : "NULL" ); } @@ -99,7 +99,7 @@ void CFuncLadder::Spawn() m_vecPlayerMountPositionTop.GetZ(), toptrace.m_pEnt ? - UTIL_VarArgs( "%s/%s", toptrace.m_pEnt->GetClassname(), toptrace.m_pEnt->GetEntityName() ) + UTIL_VarArgs( "%s/%s", toptrace.m_pEnt->GetClassname(), STRING(toptrace.m_pEnt->GetEntityName()) ) : "NULL" ); } diff --git a/game_shared/gameeventdefs.h b/game_shared/gameeventdefs.h index e738ecd8..620e0d96 100644 --- a/game_shared/gameeventdefs.h +++ b/game_shared/gameeventdefs.h @@ -1,21 +1,21 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -//=============================================================================// - -#ifndef GAMEEVENTDEFS_H -#define GAMEEVENTDEFS_H - -#ifdef _WIN32 -#pragma once -#endif - -// Make sure your gameevents.res and this file is in sync -// Event names may be 32 characters long and are case sensitive -// 256 is the maximum number of game events - -#define GAME_EVENT_PLAYER_DEATH "player_death" -#define GAME_EVENT_SAY_TEXT "say_text" - -#endif // GAMEEVENTDEFS_H \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#ifndef GAMEEVENTDEFS_H +#define GAMEEVENTDEFS_H + +#ifdef _WIN32 +#pragma once +#endif + +// Make sure your gameevents.res and this file is in sync +// Event names may be 32 characters long and are case sensitive +// 256 is the maximum number of game events + +#define GAME_EVENT_PLAYER_DEATH "player_death" +#define GAME_EVENT_SAY_TEXT "say_text" + +#endif // GAMEEVENTDEFS_H diff --git a/game_shared/gamemovement.cpp b/game_shared/gamemovement.cpp index b9dcf468..3b9dc87c 100644 --- a/game_shared/gamemovement.cpp +++ b/game_shared/gamemovement.cpp @@ -2813,7 +2813,7 @@ void CreateStuckTable( void ) } } } - Assert( idx < sizeof(rgv3tStuckTable)/sizeof(rgv3tStuckTable[0])); + Assert( idx < static_cast(sizeof(rgv3tStuckTable)/sizeof(rgv3tStuckTable[0]))); } #else extern void CreateStuckTable( void ); @@ -4202,7 +4202,9 @@ void CGameMovement::FullTossMove( void ) // Purpose: //----------------------------------------------------------------------------- +#ifdef _MSC_VER #pragma warning (disable : 4701) +#endif void CGameMovement::IsometricMove( void ) { @@ -4235,4 +4237,6 @@ void CGameMovement::IsometricMove( void ) mv->m_vecVelocity.Init(); } +#ifdef _MSC_VER #pragma warning (default : 4701) +#endif diff --git a/game_shared/gamestats/ep1_gamestats.cpp b/game_shared/gamestats/ep1_gamestats.cpp index 8dc43fae..d3a94c77 100644 --- a/game_shared/gamestats/ep1_gamestats.cpp +++ b/game_shared/gamestats/ep1_gamestats.cpp @@ -1,197 +1,197 @@ -//====== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======= -// -// Purpose: -// -//============================================================================= - -#include "gamestats/ep1_gamestats.h" -#include "tier1/UtlBuffer.h" - -static int g_nParseVersion = -1; - -void GameStatsRecord_t::Clear() -{ - m_nCount = 0; - m_nSeconds = 0; - m_nCommentary = 0; - m_nHDR = 0; - m_nCaptions = 0; - m_bSteam = true; - m_bCyberCafe = false; - Q_memset( m_nSkill, 0, sizeof( m_nSkill ) ); - m_nDeaths = 0; -} - -void GameStatsRecord_t::SaveToBuffer( CUtlBuffer &buf ) -{ - buf.PutInt( m_nCount ); - buf.PutInt( m_nSeconds ); - buf.PutInt( m_nCommentary ); - buf.PutInt( m_nHDR ); - buf.PutInt( m_nCaptions ); - for ( int i = 0; i < 3; ++i ) - { - buf.PutInt( m_nSkill[ i ] ); - } - - buf.PutChar( m_bSteam ? 1 : 0 ); - buf.PutChar( m_bCyberCafe ? 1 : 0 ); - buf.PutInt( m_nDeaths ); -} - -bool GameStatsRecord_t::ParseFromBuffer( CUtlBuffer &buf ) -{ - bool bret = true; - m_nCount = buf.GetInt(); - - if ( m_nCount > 100000 || m_nCount < 0 ) - { - bret = false; - } - - m_nSeconds = buf.GetInt(); - // Note, don't put the buf.GetInt() in the macro since it'll get evaluated twice!!! - m_nSeconds = max( m_nSeconds, 0 ); - - m_nCommentary = buf.GetInt(); - if ( m_nCommentary < 0 || m_nCommentary > 100000 ) - { - bret = false; - } - - m_nHDR = buf.GetInt(); - if ( m_nHDR < 0 || m_nHDR > 100000 ) - { - bret = false; - } - - m_nCaptions = buf.GetInt(); - if ( m_nCaptions < 0 || m_nCaptions > 100000 ) - { - bret = false; - } - - for ( int i = 0; i < 3; ++i ) - { - m_nSkill[ i ] = buf.GetInt(); - if ( m_nSkill[ i ] < 0 || m_nSkill[ i ] > 100000 ) - { - bret = false; - } - } - - if ( g_nParseVersion > GAMESTATS_FILE_VERSION_OLD ) - { - m_bSteam = buf.GetChar() ? true : false; - } - if ( g_nParseVersion > GAMESTATS_FILE_VERSION_OLD2 ) - { - m_bCyberCafe = buf.GetChar() ? true : false; - } - if ( g_nParseVersion > GAMESTATS_FILE_VERSION_OLD5 ) - { - m_nDeaths = buf.GetInt(); - } - - return bret; -} - -void GameStats_t::Clear() -{ - m_nVersion = GAMESTATS_FILE_VERSION; - m_szUserID[ 0 ] = 0; - m_nSecondsToCompleteGame = 0; - m_Summary.Clear(); - m_MapTotals.Purge(); -} - -void GameStats_t::SaveToBuffer( CUtlBuffer& buf ) -{ - buf.PutShort( GAMESTATS_FILE_VERSION ); - buf.Put( m_szUserID, 16 ); - buf.PutInt( m_nSecondsToCompleteGame ); - - m_Summary.SaveToBuffer( buf ); - - int c = m_MapTotals.Count(); - buf.PutInt( c ); - for ( int i = m_MapTotals.First(); i != m_MapTotals.InvalidIndex(); i = m_MapTotals.Next( i ) ) - { - char const *name = m_MapTotals.GetElementName( i ); - GameStatsRecord_t &rec = m_MapTotals[ i ]; - - buf.PutString( name ); - rec.SaveToBuffer( buf ); - } - - buf.PutChar( (char)m_nHL2ChaptureUnlocked ); - buf.PutChar( m_bSteam ? 1 : 0 ); - buf.PutChar( m_bCyberCafe ? 1 : 0 ); - buf.PutShort( (short)m_nDXLevel ); -} - -GameStatsRecord_t *GameStats_t::FindOrAddRecordForMap( char const *mapname ) -{ - int idx = m_MapTotals.Find( mapname ); - if ( idx == m_MapTotals.InvalidIndex() ) - { - idx = m_MapTotals.Insert( mapname ); - } - - return &m_MapTotals[ idx ]; -} - -bool GameStats_t::ParseFromBuffer( CUtlBuffer& buf ) -{ - bool bret = true; - int version = buf.GetShort(); - if ( version > GAMESTATS_FILE_VERSION ) - return false; - - // Set global parse version - g_nParseVersion = version; - m_nVersion = version; - - buf.Get( m_szUserID, 16 ); - m_szUserID[ sizeof( m_szUserID ) - 1 ] = 0; - m_nSecondsToCompleteGame = buf.GetInt(); - if ( m_nSecondsToCompleteGame < 0 || m_nSecondsToCompleteGame > 10000000 ) - { - bret = false; - } - - m_Summary.ParseFromBuffer( buf ); - int c = buf.GetInt(); - if ( c > 1024 || c < 0 ) - { - bret = false; - } - - for ( int i = 0; i < c; ++i ) - { - char mapname[ 256 ]; - buf.GetString( mapname, sizeof( mapname ) ); - - GameStatsRecord_t *rec = FindOrAddRecordForMap( mapname ); - bool valid= rec->ParseFromBuffer( buf ); - if ( !valid ) - { - bret = false; - } - } - - if ( g_nParseVersion >= GAMESTATS_FILE_VERSION_OLD2 ) - { - m_nHL2ChaptureUnlocked = (int)buf.GetChar(); - m_bSteam = buf.GetChar() ? true : false; - } - if ( g_nParseVersion > GAMESTATS_FILE_VERSION_OLD2 ) - { - m_bCyberCafe = buf.GetChar() ? true : false; - } - if ( g_nParseVersion > GAMESTATS_FILE_VERSION_OLD3 ) - { - m_nDXLevel = (int)buf.GetShort(); - } - return bret; -} \ No newline at end of file +//====== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======= +// +// Purpose: +// +//============================================================================= + +#include "gamestats/ep1_gamestats.h" +#include "tier1/utlbuffer.h" + +static int g_nParseVersion = -1; + +void GameStatsRecord_t::Clear() +{ + m_nCount = 0; + m_nSeconds = 0; + m_nCommentary = 0; + m_nHDR = 0; + m_nCaptions = 0; + m_bSteam = true; + m_bCyberCafe = false; + Q_memset( m_nSkill, 0, sizeof( m_nSkill ) ); + m_nDeaths = 0; +} + +void GameStatsRecord_t::SaveToBuffer( CUtlBuffer &buf ) +{ + buf.PutInt( m_nCount ); + buf.PutInt( m_nSeconds ); + buf.PutInt( m_nCommentary ); + buf.PutInt( m_nHDR ); + buf.PutInt( m_nCaptions ); + for ( int i = 0; i < 3; ++i ) + { + buf.PutInt( m_nSkill[ i ] ); + } + + buf.PutChar( m_bSteam ? 1 : 0 ); + buf.PutChar( m_bCyberCafe ? 1 : 0 ); + buf.PutInt( m_nDeaths ); +} + +bool GameStatsRecord_t::ParseFromBuffer( CUtlBuffer &buf ) +{ + bool bret = true; + m_nCount = buf.GetInt(); + + if ( m_nCount > 100000 || m_nCount < 0 ) + { + bret = false; + } + + m_nSeconds = buf.GetInt(); + // Note, don't put the buf.GetInt() in the macro since it'll get evaluated twice!!! + m_nSeconds = max( m_nSeconds, 0 ); + + m_nCommentary = buf.GetInt(); + if ( m_nCommentary < 0 || m_nCommentary > 100000 ) + { + bret = false; + } + + m_nHDR = buf.GetInt(); + if ( m_nHDR < 0 || m_nHDR > 100000 ) + { + bret = false; + } + + m_nCaptions = buf.GetInt(); + if ( m_nCaptions < 0 || m_nCaptions > 100000 ) + { + bret = false; + } + + for ( int i = 0; i < 3; ++i ) + { + m_nSkill[ i ] = buf.GetInt(); + if ( m_nSkill[ i ] < 0 || m_nSkill[ i ] > 100000 ) + { + bret = false; + } + } + + if ( g_nParseVersion > GAMESTATS_FILE_VERSION_OLD ) + { + m_bSteam = buf.GetChar() ? true : false; + } + if ( g_nParseVersion > GAMESTATS_FILE_VERSION_OLD2 ) + { + m_bCyberCafe = buf.GetChar() ? true : false; + } + if ( g_nParseVersion > GAMESTATS_FILE_VERSION_OLD5 ) + { + m_nDeaths = buf.GetInt(); + } + + return bret; +} + +void GameStats_t::Clear() +{ + m_nVersion = GAMESTATS_FILE_VERSION; + m_szUserID[ 0 ] = 0; + m_nSecondsToCompleteGame = 0; + m_Summary.Clear(); + m_MapTotals.Purge(); +} + +void GameStats_t::SaveToBuffer( CUtlBuffer& buf ) +{ + buf.PutShort( GAMESTATS_FILE_VERSION ); + buf.Put( m_szUserID, 16 ); + buf.PutInt( m_nSecondsToCompleteGame ); + + m_Summary.SaveToBuffer( buf ); + + int c = m_MapTotals.Count(); + buf.PutInt( c ); + for ( int i = m_MapTotals.First(); i != m_MapTotals.InvalidIndex(); i = m_MapTotals.Next( i ) ) + { + char const *name = m_MapTotals.GetElementName( i ); + GameStatsRecord_t &rec = m_MapTotals[ i ]; + + buf.PutString( name ); + rec.SaveToBuffer( buf ); + } + + buf.PutChar( (char)m_nHL2ChaptureUnlocked ); + buf.PutChar( m_bSteam ? 1 : 0 ); + buf.PutChar( m_bCyberCafe ? 1 : 0 ); + buf.PutShort( (short)m_nDXLevel ); +} + +GameStatsRecord_t *GameStats_t::FindOrAddRecordForMap( char const *mapname ) +{ + int idx = m_MapTotals.Find( mapname ); + if ( idx == m_MapTotals.InvalidIndex() ) + { + idx = m_MapTotals.Insert( mapname ); + } + + return &m_MapTotals[ idx ]; +} + +bool GameStats_t::ParseFromBuffer( CUtlBuffer& buf ) +{ + bool bret = true; + int version = buf.GetShort(); + if ( version > GAMESTATS_FILE_VERSION ) + return false; + + // Set global parse version + g_nParseVersion = version; + m_nVersion = version; + + buf.Get( m_szUserID, 16 ); + m_szUserID[ sizeof( m_szUserID ) - 1 ] = 0; + m_nSecondsToCompleteGame = buf.GetInt(); + if ( m_nSecondsToCompleteGame < 0 || m_nSecondsToCompleteGame > 10000000 ) + { + bret = false; + } + + m_Summary.ParseFromBuffer( buf ); + int c = buf.GetInt(); + if ( c > 1024 || c < 0 ) + { + bret = false; + } + + for ( int i = 0; i < c; ++i ) + { + char mapname[ 256 ]; + buf.GetString( mapname, sizeof( mapname ) ); + + GameStatsRecord_t *rec = FindOrAddRecordForMap( mapname ); + bool valid= rec->ParseFromBuffer( buf ); + if ( !valid ) + { + bret = false; + } + } + + if ( g_nParseVersion >= GAMESTATS_FILE_VERSION_OLD2 ) + { + m_nHL2ChaptureUnlocked = (int)buf.GetChar(); + m_bSteam = buf.GetChar() ? true : false; + } + if ( g_nParseVersion > GAMESTATS_FILE_VERSION_OLD2 ) + { + m_bCyberCafe = buf.GetChar() ? true : false; + } + if ( g_nParseVersion > GAMESTATS_FILE_VERSION_OLD3 ) + { + m_nDXLevel = (int)buf.GetShort(); + } + return bret; +} diff --git a/game_shared/gamestats/ep1_gamestats.h b/game_shared/gamestats/ep1_gamestats.h index b5439eb4..b70529dd 100644 --- a/game_shared/gamestats/ep1_gamestats.h +++ b/game_shared/gamestats/ep1_gamestats.h @@ -11,7 +11,7 @@ #endif class CUtlBuffer; -#include "tier1/UtlDict.h" +#include "tier1/utldict.h" #define GAMESTATS_FILE_VERSION_OLD 001 #define GAMESTATS_FILE_VERSION_OLD2 002 @@ -61,9 +61,9 @@ public: GameStats_t() : m_nVersion( GAMESTATS_FILE_VERSION ), m_nSecondsToCompleteGame( 0 ), - m_nHL2ChaptureUnlocked( 0 ), m_bSteam( true ), m_bCyberCafe( false ), + m_nHL2ChaptureUnlocked( 0 ), m_nDXLevel( 0 ) { m_szUserID[ 0 ] = 0; diff --git a/game_shared/gamestringpool.cpp b/game_shared/gamestringpool.cpp index 5b504ddb..859a37cd 100644 --- a/game_shared/gamestringpool.cpp +++ b/game_shared/gamestringpool.cpp @@ -1,77 +1,77 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -//=============================================================================// - -#include "cbase.h" - -#include "stringpool.h" -#include "igamesystem.h" -#include "gamestringpool.h" - -// memdbgon must be the last include file in a .cpp file!!! -#include "tier0/memdbgon.h" - -//----------------------------------------------------------------------------- -// Purpose: The actual storage for pooled per-level strings -//----------------------------------------------------------------------------- -class CGameStringPool : public CStringPool, public CBaseGameSystem -{ - virtual char const *Name() { return "CGameStringPool"; } - - virtual void LevelShutdownPostEntity() - { - FreeAll(); - } - -public: - void CGameStringPool::Dump( void ) - { - for ( int i = m_Strings.FirstInorder(); i != m_Strings.InvalidIndex(); i = m_Strings.NextInorder(i) ) - { - DevMsg( " %d (0x%x) : %s\n", i, m_Strings[i], m_Strings[i] ); - } - DevMsg( "\n" ); - DevMsg( "Size: %d items\n", m_Strings.Count() ); - } -}; - -static CGameStringPool g_GameStringPool; - - -//----------------------------------------------------------------------------- -// String system accessor -//----------------------------------------------------------------------------- -IGameSystem *GameStringSystem() -{ - return &g_GameStringPool; -} - - -//----------------------------------------------------------------------------- -// Purpose: The public accessor for the level-global pooled strings -//----------------------------------------------------------------------------- -string_t AllocPooledString( const char * pszValue ) -{ - if (pszValue && *pszValue) - return MAKE_STRING( g_GameStringPool.Allocate( pszValue ) ); - return NULL_STRING; -} - -string_t FindPooledString( const char *pszValue ) -{ - return MAKE_STRING( g_GameStringPool.Find( pszValue ) ); -} - -#ifndef CLIENT_DLL -//------------------------------------------------------------------------------ -// Purpose: -//------------------------------------------------------------------------------ -void CC_DumpGameStringTable( void ) -{ - g_GameStringPool.Dump(); -} -static ConCommand dumpgamestringtable("dumpgamestringtable", CC_DumpGameStringTable, "Dump the contents of the game string table to the console.", FCVAR_CHEAT); -#endif \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include "cbase.h" + +#include "stringpool.h" +#include "igamesystem.h" +#include "gamestringpool.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +//----------------------------------------------------------------------------- +// Purpose: The actual storage for pooled per-level strings +//----------------------------------------------------------------------------- +class CGameStringPool : public CStringPool, public CBaseGameSystem +{ + virtual char const *Name() { return "CGameStringPool"; } + + virtual void LevelShutdownPostEntity() + { + FreeAll(); + } + +public: + void Dump( void ) + { + for ( int i = m_Strings.FirstInorder(); i != m_Strings.InvalidIndex(); i = m_Strings.NextInorder(i) ) + { + DevMsg( " %d (0x%x) : %s\n", i, m_Strings[i], m_Strings[i] ); + } + DevMsg( "\n" ); + DevMsg( "Size: %d items\n", m_Strings.Count() ); + } +}; + +static CGameStringPool g_GameStringPool; + + +//----------------------------------------------------------------------------- +// String system accessor +//----------------------------------------------------------------------------- +IGameSystem *GameStringSystem() +{ + return &g_GameStringPool; +} + + +//----------------------------------------------------------------------------- +// Purpose: The public accessor for the level-global pooled strings +//----------------------------------------------------------------------------- +string_t AllocPooledString( const char * pszValue ) +{ + if (pszValue && *pszValue) + return MAKE_STRING( g_GameStringPool.Allocate( pszValue ) ); + return NULL_STRING; +} + +string_t FindPooledString( const char *pszValue ) +{ + return MAKE_STRING( g_GameStringPool.Find( pszValue ) ); +} + +#ifndef CLIENT_DLL +//------------------------------------------------------------------------------ +// Purpose: +//------------------------------------------------------------------------------ +void CC_DumpGameStringTable( void ) +{ + g_GameStringPool.Dump(); +} +static ConCommand dumpgamestringtable("dumpgamestringtable", CC_DumpGameStringTable, "Dump the contents of the game string table to the console.", FCVAR_CHEAT); +#endif diff --git a/game_shared/hl2/basehlcombatweapon_shared.cpp b/game_shared/hl2/basehlcombatweapon_shared.cpp index dc0cb4d3..a63c05ec 100644 --- a/game_shared/hl2/basehlcombatweapon_shared.cpp +++ b/game_shared/hl2/basehlcombatweapon_shared.cpp @@ -1,420 +1,420 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -//=============================================================================// - -#include "cbase.h" -#include "basehlcombatweapon_shared.h" - -#include "hl2_player_shared.h" - -// memdbgon must be the last include file in a .cpp file!!! -#include "tier0/memdbgon.h" - -LINK_ENTITY_TO_CLASS( basehlcombatweapon, CBaseHLCombatWeapon ); - -IMPLEMENT_NETWORKCLASS_ALIASED( BaseHLCombatWeapon , DT_BaseHLCombatWeapon ) - -BEGIN_NETWORK_TABLE( CBaseHLCombatWeapon , DT_BaseHLCombatWeapon ) -#if !defined( CLIENT_DLL ) -// SendPropInt( SENDINFO( m_bReflectViewModelAnimations ), 1, SPROP_UNSIGNED ), -#else -// RecvPropInt( RECVINFO( m_bReflectViewModelAnimations ) ), -#endif -END_NETWORK_TABLE() - - -#if !defined( CLIENT_DLL ) - -#include "globalstate.h" - -//--------------------------------------------------------- -// Save/Restore -//--------------------------------------------------------- -BEGIN_DATADESC( CBaseHLCombatWeapon ) - - DEFINE_FIELD( m_bLowered, FIELD_BOOLEAN ), - DEFINE_FIELD( m_flRaiseTime, FIELD_TIME ), - DEFINE_FIELD( m_flHolsterTime, FIELD_TIME ), - -END_DATADESC() - -#endif - -BEGIN_PREDICTION_DATA( CBaseHLCombatWeapon ) -END_PREDICTION_DATA() - -ConVar sk_auto_reload_time( "sk_auto_reload_time", "3", FCVAR_REPLICATED ); - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CBaseHLCombatWeapon::ItemHolsterFrame( void ) -{ - BaseClass::ItemHolsterFrame(); - - // Must be player held - if ( GetOwner() && GetOwner()->IsPlayer() == false ) - return; - - // We can't be active - if ( GetOwner()->GetActiveWeapon() == this ) - return; - - // If it's been longer than three seconds, reload - if ( ( gpGlobals->curtime - m_flHolsterTime ) > sk_auto_reload_time.GetFloat() ) - { - // Just load the clip with no animations - FinishReload(); - m_flHolsterTime = gpGlobals->curtime; - } -} - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -bool CBaseHLCombatWeapon::CanLower() -{ - if ( SelectWeightedSequence( ACT_VM_IDLE_LOWERED ) == ACTIVITY_NOT_AVAILABLE ) - return false; - return true; -} - -//----------------------------------------------------------------------------- -// Purpose: Drops the weapon into a lowered pose -// Output : Returns true on success, false on failure. -//----------------------------------------------------------------------------- -bool CBaseHLCombatWeapon::Lower( void ) -{ - //Don't bother if we don't have the animation - if ( SelectWeightedSequence( ACT_VM_IDLE_LOWERED ) == ACTIVITY_NOT_AVAILABLE ) - return false; - - m_bLowered = true; - return true; -} - -//----------------------------------------------------------------------------- -// Purpose: Brings the weapon up to the ready position -// Output : Returns true on success, false on failure. -//----------------------------------------------------------------------------- -bool CBaseHLCombatWeapon::Ready( void ) -{ - //Don't bother if we don't have the animation - if ( SelectWeightedSequence( ACT_VM_LOWERED_TO_IDLE ) == ACTIVITY_NOT_AVAILABLE ) - return false; - - m_bLowered = false; - m_flRaiseTime = gpGlobals->curtime + 0.5f; - return true; -} - -//----------------------------------------------------------------------------- -// Purpose: -// Output : Returns true on success, false on failure. -//----------------------------------------------------------------------------- -bool CBaseHLCombatWeapon::Deploy( void ) -{ - // If we should be lowered, deploy in the lowered position - // We have to ask the player if the last time it checked, the weapon was lowered - if ( GetOwner() && GetOwner()->IsPlayer() ) - { - CHL2_Player *pPlayer = assert_cast( GetOwner() ); - if ( pPlayer->IsWeaponLowered() ) - { - if ( SelectWeightedSequence( ACT_VM_IDLE_LOWERED ) != ACTIVITY_NOT_AVAILABLE ) - { - if ( DefaultDeploy( (char*)GetViewModel(), (char*)GetWorldModel(), ACT_VM_IDLE_LOWERED, (char*)GetAnimPrefix() ) ) - { - m_bLowered = true; - - // Stomp the next attack time to fix the fact that the lower idles are long - pPlayer->SetNextAttack( gpGlobals->curtime + 1.0 ); - m_flNextPrimaryAttack = gpGlobals->curtime + 1.0; - m_flNextSecondaryAttack = gpGlobals->curtime + 1.0; - return true; - } - } - } - } - - m_bLowered = false; - return BaseClass::Deploy(); -} - -//----------------------------------------------------------------------------- -// Purpose: -// Output : Returns true on success, false on failure. -//----------------------------------------------------------------------------- -bool CBaseHLCombatWeapon::Holster( CBaseCombatWeapon *pSwitchingTo ) -{ - if ( BaseClass::Holster( pSwitchingTo ) ) - { - m_flHolsterTime = gpGlobals->curtime; - return true; - } - - return false; -} - -//----------------------------------------------------------------------------- -// Purpose: -// Output : Returns true on success, false on failure. -//----------------------------------------------------------------------------- -bool CBaseHLCombatWeapon::WeaponShouldBeLowered( void ) -{ - // Can't be in the middle of another animation - if ( GetIdealActivity() != ACT_VM_IDLE_LOWERED && GetIdealActivity() != ACT_VM_IDLE && - GetIdealActivity() != ACT_VM_IDLE_TO_LOWERED && GetIdealActivity() != ACT_VM_LOWERED_TO_IDLE ) - return false; - - if ( m_bLowered ) - return true; - -#if !defined( CLIENT_DLL ) - - if ( GlobalEntity_GetState( "friendly_encounter" ) == GLOBAL_ON ) - return true; - -#endif - - return false; -} - -//----------------------------------------------------------------------------- -// Purpose: Allows the weapon to choose proper weapon idle animation -//----------------------------------------------------------------------------- -void CBaseHLCombatWeapon::WeaponIdle( void ) -{ - //See if we should idle high or low - if ( WeaponShouldBeLowered() ) - { - // Move to lowered position if we're not there yet - if ( GetActivity() != ACT_VM_IDLE_LOWERED && GetActivity() != ACT_VM_IDLE_TO_LOWERED - && GetActivity() != ACT_TRANSITION ) - { - SendWeaponAnim( ACT_VM_IDLE_LOWERED ); - } - else if ( HasWeaponIdleTimeElapsed() ) - { - // Keep idling low - SendWeaponAnim( ACT_VM_IDLE_LOWERED ); - } - } - else - { - // See if we need to raise immediately - if ( m_flRaiseTime < gpGlobals->curtime && GetActivity() == ACT_VM_IDLE_LOWERED ) - { - SendWeaponAnim( ACT_VM_IDLE ); - } - else if ( HasWeaponIdleTimeElapsed() ) - { - SendWeaponAnim( ACT_VM_IDLE ); - } - } -} - -float g_lateralBob; -float g_verticalBob; - -#if defined( CLIENT_DLL ) && !defined( HL2MP ) - -#define HL2_BOB_CYCLE_MIN 1.0f -#define HL2_BOB_CYCLE_MAX 0.45f -#define HL2_BOB 0.002f -#define HL2_BOB_UP 0.5f - - -static ConVar cl_bobcycle( "cl_bobcycle","0.8" ); -static ConVar cl_bob( "cl_bob","0.002" ); -static ConVar cl_bobup( "cl_bobup","0.5" ); - -// Register these cvars if needed for easy tweaking -static ConVar v_iyaw_cycle( "v_iyaw_cycle", "2"/*, FCVAR_UNREGISTERED*/ ); -static ConVar v_iroll_cycle( "v_iroll_cycle", "0.5"/*, FCVAR_UNREGISTERED*/ ); -static ConVar v_ipitch_cycle( "v_ipitch_cycle", "1"/*, FCVAR_UNREGISTERED*/ ); -static ConVar v_iyaw_level( "v_iyaw_level", "0.3"/*, FCVAR_UNREGISTERED*/ ); -static ConVar v_iroll_level( "v_iroll_level", "0.1"/*, FCVAR_UNREGISTERED*/ ); -static ConVar v_ipitch_level( "v_ipitch_level", "0.3"/*, FCVAR_UNREGISTERED*/ ); - -//----------------------------------------------------------------------------- -// Purpose: -// Output : float -//----------------------------------------------------------------------------- -float CBaseHLCombatWeapon::CalcViewmodelBob( void ) -{ - static float bobtime; - static float lastbobtime; - float cycle; - - CBasePlayer *player = ToBasePlayer( GetOwner() ); - //Assert( player ); - - //NOTENOTE: For now, let this cycle continue when in the air, because it snaps badly without it - - if ( ( !gpGlobals->frametime ) || ( player == NULL ) ) - { - //NOTENOTE: We don't use this return value in our case (need to restructure the calculation function setup!) - return 0.0f;// just use old value - } - - //Find the speed of the player - float speed = player->GetLocalVelocity().Length2D(); - - //FIXME: This maximum speed value must come from the server. - // MaxSpeed() is not sufficient for dealing with sprinting - jdw - - speed = clamp( speed, -320, 320 ); - - float bob_offset = RemapVal( speed, 0, 320, 0.0f, 1.0f ); - - bobtime += ( gpGlobals->curtime - lastbobtime ) * bob_offset; - lastbobtime = gpGlobals->curtime; - - //Calculate the vertical bob - cycle = bobtime - (int)(bobtime/HL2_BOB_CYCLE_MAX)*HL2_BOB_CYCLE_MAX; - cycle /= HL2_BOB_CYCLE_MAX; - - if ( cycle < HL2_BOB_UP ) - { - cycle = M_PI * cycle / HL2_BOB_UP; - } - else - { - cycle = M_PI + M_PI*(cycle-HL2_BOB_UP)/(1.0 - HL2_BOB_UP); - } - - g_verticalBob = speed*0.005f; - g_verticalBob = g_verticalBob*0.3 + g_verticalBob*0.7*sin(cycle); - - g_verticalBob = clamp( g_verticalBob, -7.0f, 4.0f ); - - //Calculate the lateral bob - cycle = bobtime - (int)(bobtime/HL2_BOB_CYCLE_MAX*2)*HL2_BOB_CYCLE_MAX*2; - cycle /= HL2_BOB_CYCLE_MAX*2; - - if ( cycle < HL2_BOB_UP ) - { - cycle = M_PI * cycle / HL2_BOB_UP; - } - else - { - cycle = M_PI + M_PI*(cycle-HL2_BOB_UP)/(1.0 - HL2_BOB_UP); - } - - g_lateralBob = speed*0.005f; - g_lateralBob = g_lateralBob*0.3 + g_lateralBob*0.7*sin(cycle); - g_lateralBob = clamp( g_lateralBob, -7.0f, 4.0f ); - - //NOTENOTE: We don't use this return value in our case (need to restructure the calculation function setup!) - return 0.0f; -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : &origin - -// &angles - -// viewmodelindex - -//----------------------------------------------------------------------------- -void CBaseHLCombatWeapon::AddViewmodelBob( CBaseViewModel *viewmodel, Vector &origin, QAngle &angles ) -{ - Vector forward, right; - AngleVectors( angles, &forward, &right, NULL ); - - CalcViewmodelBob(); - - // Apply bob, but scaled down to 40% - VectorMA( origin, g_verticalBob * 0.1f, forward, origin ); - - // Z bob a bit more - origin[2] += g_verticalBob * 0.1f; - - // bob the angles - angles[ ROLL ] += g_verticalBob * 0.5f; - angles[ PITCH ] -= g_verticalBob * 0.4f; - - angles[ YAW ] -= g_lateralBob * 0.3f; - - VectorMA( origin, g_lateralBob * 0.8f, right, origin ); -} - -//----------------------------------------------------------------------------- -Vector CBaseHLCombatWeapon::GetBulletSpread( WeaponProficiency_t proficiency ) -{ - return BaseClass::GetBulletSpread( proficiency ); -} - -//----------------------------------------------------------------------------- -float CBaseHLCombatWeapon::GetSpreadBias( WeaponProficiency_t proficiency ) -{ - return BaseClass::GetSpreadBias( proficiency ); -} -//----------------------------------------------------------------------------- - -const WeaponProficiencyInfo_t *CBaseHLCombatWeapon::GetProficiencyValues() -{ - return NULL; -} - -#else - -// Server stubs -float CBaseHLCombatWeapon::CalcViewmodelBob( void ) -{ - return 0.0f; -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : &origin - -// &angles - -// viewmodelindex - -//----------------------------------------------------------------------------- -void CBaseHLCombatWeapon::AddViewmodelBob( CBaseViewModel *viewmodel, Vector &origin, QAngle &angles ) -{ -} - - -//----------------------------------------------------------------------------- -Vector CBaseHLCombatWeapon::GetBulletSpread( WeaponProficiency_t proficiency ) -{ - Vector baseSpread = BaseClass::GetBulletSpread( proficiency ); - - const WeaponProficiencyInfo_t *pProficiencyValues = GetProficiencyValues(); - float flModifier = (pProficiencyValues)[ proficiency ].spreadscale; - return ( baseSpread * flModifier ); -} - -//----------------------------------------------------------------------------- -float CBaseHLCombatWeapon::GetSpreadBias( WeaponProficiency_t proficiency ) -{ - const WeaponProficiencyInfo_t *pProficiencyValues = GetProficiencyValues(); - return (pProficiencyValues)[ proficiency ].bias; -} - -//----------------------------------------------------------------------------- -const WeaponProficiencyInfo_t *CBaseHLCombatWeapon::GetProficiencyValues() -{ - return GetDefaultProficiencyValues(); -} - -//----------------------------------------------------------------------------- -const WeaponProficiencyInfo_t *CBaseHLCombatWeapon::GetDefaultProficiencyValues() -{ - // Weapon proficiency table. Keep this in sync with WeaponProficiency_t enum in the header!! - static WeaponProficiencyInfo_t g_BaseWeaponProficiencyTable[] = - { - { 2.50, 1.0 }, - { 2.00, 1.0 }, - { 1.50, 1.0 }, - { 1.25, 1.0 }, - { 1.00, 1.0 }, - }; - - COMPILE_TIME_ASSERT( ARRAYSIZE(g_BaseWeaponProficiencyTable) == WEAPON_PROFICIENCY_PERFECT + 1); - - return g_BaseWeaponProficiencyTable; -} - -#endif \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "cbase.h" +#include "basehlcombatweapon_shared.h" + +#include "hl2_player_shared.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +LINK_ENTITY_TO_CLASS( basehlcombatweapon, CBaseHLCombatWeapon ); + +IMPLEMENT_NETWORKCLASS_ALIASED( BaseHLCombatWeapon , DT_BaseHLCombatWeapon ) + +BEGIN_NETWORK_TABLE( CBaseHLCombatWeapon , DT_BaseHLCombatWeapon ) +#if !defined( CLIENT_DLL ) +// SendPropInt( SENDINFO( m_bReflectViewModelAnimations ), 1, SPROP_UNSIGNED ), +#else +// RecvPropInt( RECVINFO( m_bReflectViewModelAnimations ) ), +#endif +END_NETWORK_TABLE() + + +#if !defined( CLIENT_DLL ) + +#include "globalstate.h" + +//--------------------------------------------------------- +// Save/Restore +//--------------------------------------------------------- +BEGIN_DATADESC( CBaseHLCombatWeapon ) + + DEFINE_FIELD( m_bLowered, FIELD_BOOLEAN ), + DEFINE_FIELD( m_flRaiseTime, FIELD_TIME ), + DEFINE_FIELD( m_flHolsterTime, FIELD_TIME ), + +END_DATADESC() + +#endif + +BEGIN_PREDICTION_DATA( CBaseHLCombatWeapon ) +END_PREDICTION_DATA() + +ConVar sk_auto_reload_time( "sk_auto_reload_time", "3", FCVAR_REPLICATED ); + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBaseHLCombatWeapon::ItemHolsterFrame( void ) +{ + BaseClass::ItemHolsterFrame(); + + // Must be player held + if ( GetOwner() && GetOwner()->IsPlayer() == false ) + return; + + // We can't be active + if ( GetOwner()->GetActiveWeapon() == this ) + return; + + // If it's been longer than three seconds, reload + if ( ( gpGlobals->curtime - m_flHolsterTime ) > sk_auto_reload_time.GetFloat() ) + { + // Just load the clip with no animations + FinishReload(); + m_flHolsterTime = gpGlobals->curtime; + } +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +bool CBaseHLCombatWeapon::CanLower() +{ + if ( SelectWeightedSequence( ACT_VM_IDLE_LOWERED ) == ACTIVITY_NOT_AVAILABLE ) + return false; + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: Drops the weapon into a lowered pose +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CBaseHLCombatWeapon::Lower( void ) +{ + //Don't bother if we don't have the animation + if ( SelectWeightedSequence( ACT_VM_IDLE_LOWERED ) == ACTIVITY_NOT_AVAILABLE ) + return false; + + m_bLowered = true; + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: Brings the weapon up to the ready position +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CBaseHLCombatWeapon::Ready( void ) +{ + //Don't bother if we don't have the animation + if ( SelectWeightedSequence( ACT_VM_LOWERED_TO_IDLE ) == ACTIVITY_NOT_AVAILABLE ) + return false; + + m_bLowered = false; + m_flRaiseTime = gpGlobals->curtime + 0.5f; + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CBaseHLCombatWeapon::Deploy( void ) +{ + // If we should be lowered, deploy in the lowered position + // We have to ask the player if the last time it checked, the weapon was lowered + if ( GetOwner() && GetOwner()->IsPlayer() ) + { + CHL2_Player *pPlayer = assert_cast( GetOwner() ); + if ( pPlayer->IsWeaponLowered() ) + { + if ( SelectWeightedSequence( ACT_VM_IDLE_LOWERED ) != ACTIVITY_NOT_AVAILABLE ) + { + if ( DefaultDeploy( (char*)GetViewModel(), (char*)GetWorldModel(), ACT_VM_IDLE_LOWERED, (char*)GetAnimPrefix() ) ) + { + m_bLowered = true; + + // Stomp the next attack time to fix the fact that the lower idles are long + pPlayer->SetNextAttack( gpGlobals->curtime + 1.0 ); + m_flNextPrimaryAttack = gpGlobals->curtime + 1.0; + m_flNextSecondaryAttack = gpGlobals->curtime + 1.0; + return true; + } + } + } + } + + m_bLowered = false; + return BaseClass::Deploy(); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CBaseHLCombatWeapon::Holster( CBaseCombatWeapon *pSwitchingTo ) +{ + if ( BaseClass::Holster( pSwitchingTo ) ) + { + m_flHolsterTime = gpGlobals->curtime; + return true; + } + + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CBaseHLCombatWeapon::WeaponShouldBeLowered( void ) +{ + // Can't be in the middle of another animation + if ( GetIdealActivity() != ACT_VM_IDLE_LOWERED && GetIdealActivity() != ACT_VM_IDLE && + GetIdealActivity() != ACT_VM_IDLE_TO_LOWERED && GetIdealActivity() != ACT_VM_LOWERED_TO_IDLE ) + return false; + + if ( m_bLowered ) + return true; + +#if !defined( CLIENT_DLL ) + + if ( GlobalEntity_GetState( "friendly_encounter" ) == GLOBAL_ON ) + return true; + +#endif + + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: Allows the weapon to choose proper weapon idle animation +//----------------------------------------------------------------------------- +void CBaseHLCombatWeapon::WeaponIdle( void ) +{ + //See if we should idle high or low + if ( WeaponShouldBeLowered() ) + { + // Move to lowered position if we're not there yet + if ( GetActivity() != ACT_VM_IDLE_LOWERED && GetActivity() != ACT_VM_IDLE_TO_LOWERED + && GetActivity() != ACT_TRANSITION ) + { + SendWeaponAnim( ACT_VM_IDLE_LOWERED ); + } + else if ( HasWeaponIdleTimeElapsed() ) + { + // Keep idling low + SendWeaponAnim( ACT_VM_IDLE_LOWERED ); + } + } + else + { + // See if we need to raise immediately + if ( m_flRaiseTime < gpGlobals->curtime && GetActivity() == ACT_VM_IDLE_LOWERED ) + { + SendWeaponAnim( ACT_VM_IDLE ); + } + else if ( HasWeaponIdleTimeElapsed() ) + { + SendWeaponAnim( ACT_VM_IDLE ); + } + } +} + +float g_lateralBob; +float g_verticalBob; + +#if defined( CLIENT_DLL ) && !defined( HL2MP ) + +#define HL2_BOB_CYCLE_MIN 1.0f +#define HL2_BOB_CYCLE_MAX 0.45f +#define HL2_BOB 0.002f +#define HL2_BOB_UP 0.5f + + +static ConVar cl_bobcycle( "cl_bobcycle","0.8" ); +static ConVar cl_bob( "cl_bob","0.002" ); +static ConVar cl_bobup( "cl_bobup","0.5" ); + +// Register these cvars if needed for easy tweaking +static ConVar v_iyaw_cycle( "v_iyaw_cycle", "2"/*, FCVAR_UNREGISTERED*/ ); +static ConVar v_iroll_cycle( "v_iroll_cycle", "0.5"/*, FCVAR_UNREGISTERED*/ ); +static ConVar v_ipitch_cycle( "v_ipitch_cycle", "1"/*, FCVAR_UNREGISTERED*/ ); +static ConVar v_iyaw_level( "v_iyaw_level", "0.3"/*, FCVAR_UNREGISTERED*/ ); +static ConVar v_iroll_level( "v_iroll_level", "0.1"/*, FCVAR_UNREGISTERED*/ ); +static ConVar v_ipitch_level( "v_ipitch_level", "0.3"/*, FCVAR_UNREGISTERED*/ ); + +//----------------------------------------------------------------------------- +// Purpose: +// Output : float +//----------------------------------------------------------------------------- +float CBaseHLCombatWeapon::CalcViewmodelBob( void ) +{ + static float bobtime; + static float lastbobtime; + float cycle; + + CBasePlayer *player = ToBasePlayer( GetOwner() ); + //Assert( player ); + + //NOTENOTE: For now, let this cycle continue when in the air, because it snaps badly without it + + if ( ( !gpGlobals->frametime ) || ( player == NULL ) ) + { + //NOTENOTE: We don't use this return value in our case (need to restructure the calculation function setup!) + return 0.0f;// just use old value + } + + //Find the speed of the player + float speed = player->GetLocalVelocity().Length2D(); + + //FIXME: This maximum speed value must come from the server. + // MaxSpeed() is not sufficient for dealing with sprinting - jdw + + speed = clamp( speed, -320, 320 ); + + float bob_offset = RemapVal( speed, 0, 320, 0.0f, 1.0f ); + + bobtime += ( gpGlobals->curtime - lastbobtime ) * bob_offset; + lastbobtime = gpGlobals->curtime; + + //Calculate the vertical bob + cycle = bobtime - (int)(bobtime/HL2_BOB_CYCLE_MAX)*HL2_BOB_CYCLE_MAX; + cycle /= HL2_BOB_CYCLE_MAX; + + if ( cycle < HL2_BOB_UP ) + { + cycle = M_PI * cycle / HL2_BOB_UP; + } + else + { + cycle = M_PI + M_PI*(cycle-HL2_BOB_UP)/(1.0 - HL2_BOB_UP); + } + + g_verticalBob = speed*0.005f; + g_verticalBob = g_verticalBob*0.3 + g_verticalBob*0.7*sin(cycle); + + g_verticalBob = clamp( g_verticalBob, -7.0f, 4.0f ); + + //Calculate the lateral bob + cycle = bobtime - (int)(bobtime/HL2_BOB_CYCLE_MAX*2)*HL2_BOB_CYCLE_MAX*2; + cycle /= HL2_BOB_CYCLE_MAX*2; + + if ( cycle < HL2_BOB_UP ) + { + cycle = M_PI * cycle / HL2_BOB_UP; + } + else + { + cycle = M_PI + M_PI*(cycle-HL2_BOB_UP)/(1.0 - HL2_BOB_UP); + } + + g_lateralBob = speed*0.005f; + g_lateralBob = g_lateralBob*0.3 + g_lateralBob*0.7*sin(cycle); + g_lateralBob = clamp( g_lateralBob, -7.0f, 4.0f ); + + //NOTENOTE: We don't use this return value in our case (need to restructure the calculation function setup!) + return 0.0f; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : &origin - +// &angles - +// viewmodelindex - +//----------------------------------------------------------------------------- +void CBaseHLCombatWeapon::AddViewmodelBob( CBaseViewModel *viewmodel, Vector &origin, QAngle &angles ) +{ + Vector forward, right; + AngleVectors( angles, &forward, &right, NULL ); + + CalcViewmodelBob(); + + // Apply bob, but scaled down to 40% + VectorMA( origin, g_verticalBob * 0.1f, forward, origin ); + + // Z bob a bit more + origin[2] += g_verticalBob * 0.1f; + + // bob the angles + angles[ ROLL ] += g_verticalBob * 0.5f; + angles[ PITCH ] -= g_verticalBob * 0.4f; + + angles[ YAW ] -= g_lateralBob * 0.3f; + + VectorMA( origin, g_lateralBob * 0.8f, right, origin ); +} + +//----------------------------------------------------------------------------- +Vector CBaseHLCombatWeapon::GetBulletSpread( WeaponProficiency_t proficiency ) +{ + return BaseClass::GetBulletSpread( proficiency ); +} + +//----------------------------------------------------------------------------- +float CBaseHLCombatWeapon::GetSpreadBias( WeaponProficiency_t proficiency ) +{ + return BaseClass::GetSpreadBias( proficiency ); +} +//----------------------------------------------------------------------------- + +const WeaponProficiencyInfo_t *CBaseHLCombatWeapon::GetProficiencyValues() +{ + return NULL; +} + +#else + +// Server stubs +float CBaseHLCombatWeapon::CalcViewmodelBob( void ) +{ + return 0.0f; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : &origin - +// &angles - +// viewmodelindex - +//----------------------------------------------------------------------------- +void CBaseHLCombatWeapon::AddViewmodelBob( CBaseViewModel *viewmodel, Vector &origin, QAngle &angles ) +{ +} + + +//----------------------------------------------------------------------------- +Vector CBaseHLCombatWeapon::GetBulletSpread( WeaponProficiency_t proficiency ) +{ + Vector baseSpread = BaseClass::GetBulletSpread( proficiency ); + + const WeaponProficiencyInfo_t *pProficiencyValues = GetProficiencyValues(); + float flModifier = (pProficiencyValues)[ proficiency ].spreadscale; + return ( baseSpread * flModifier ); +} + +//----------------------------------------------------------------------------- +float CBaseHLCombatWeapon::GetSpreadBias( WeaponProficiency_t proficiency ) +{ + const WeaponProficiencyInfo_t *pProficiencyValues = GetProficiencyValues(); + return (pProficiencyValues)[ proficiency ].bias; +} + +//----------------------------------------------------------------------------- +const WeaponProficiencyInfo_t *CBaseHLCombatWeapon::GetProficiencyValues() +{ + return GetDefaultProficiencyValues(); +} + +//----------------------------------------------------------------------------- +const WeaponProficiencyInfo_t *CBaseHLCombatWeapon::GetDefaultProficiencyValues() +{ + // Weapon proficiency table. Keep this in sync with WeaponProficiency_t enum in the header!! + static WeaponProficiencyInfo_t g_BaseWeaponProficiencyTable[] = + { + { 2.50, 1.0 }, + { 2.00, 1.0 }, + { 1.50, 1.0 }, + { 1.25, 1.0 }, + { 1.00, 1.0 }, + }; + + COMPILE_TIME_ASSERT( ARRAYSIZE(g_BaseWeaponProficiencyTable) == WEAPON_PROFICIENCY_PERFECT + 1); + + return g_BaseWeaponProficiencyTable; +} + +#endif diff --git a/game_shared/hl2/hl2_gamerules.cpp b/game_shared/hl2/hl2_gamerules.cpp index b8ed75f6..40a5cca2 100644 --- a/game_shared/hl2/hl2_gamerules.cpp +++ b/game_shared/hl2/hl2_gamerules.cpp @@ -1696,7 +1696,7 @@ CAmmoDef *GetAmmoDef() def.AddAmmoType("Thumper", DMG_SONIC, TRACER_NONE, 10, 10, 2, 0, 0 ); def.AddAmmoType("Gravity", DMG_CLUB, TRACER_NONE, 0, 0, 8, 0, 0 ); // def.AddAmmoType("Extinguisher", DMG_BURN, TRACER_NONE, 0, 0, 100, 0, 0 ); - def.AddAmmoType("Battery", DMG_CLUB, TRACER_NONE, NULL, NULL, NULL, 0, 0 ); + def.AddAmmoType("Battery", DMG_CLUB, TRACER_NONE, 0, 0, 0, 0, 0 ); def.AddAmmoType("GaussEnergy", DMG_SHOCK, TRACER_NONE, "sk_jeep_gauss_damage", "sk_jeep_gauss_damage", "sk_max_gauss_round", BULLET_IMPULSE(650, 8000), 0 ); // hit like a 10kg weight at 400 in/s def.AddAmmoType("CombineCannon", DMG_BULLET, TRACER_LINE, "sk_npc_dmg_gunship_to_plr", "sk_npc_dmg_gunship", NULL, 1.5 * 750 * 12, 0 ); // hit like a 1.5kg weight at 750 ft/s def.AddAmmoType("AirboatGun", DMG_AIRBOAT, TRACER_LINE, "sk_plr_dmg_airboat", "sk_npc_dmg_airboat", NULL, BULLET_IMPULSE(10, 600), 0 ); @@ -1707,7 +1707,7 @@ CAmmoDef *GetAmmoDef() def.AddAmmoType("Grenade", DMG_BURN, TRACER_NONE, "sk_plr_dmg_grenade", "sk_npc_dmg_grenade", "sk_max_grenade", 0, 0); #ifdef HL2_EPISODIC def.AddAmmoType("Hopwire", DMG_BLAST, TRACER_NONE, "sk_plr_dmg_grenade", "sk_npc_dmg_grenade", "sk_max_hopwire", 0, 0); - def.AddAmmoType("CombineHeavyCannon", DMG_BULLET, TRACER_LINE, 40, 40, NULL, 1.5 * 750 * 12, AMMO_FORCE_DROP_IF_CARRIED ); // hit like a 100 kg weight at 750 ft/s + def.AddAmmoType("CombineHeavyCannon", DMG_BULLET, TRACER_LINE, 40, 40, 0, 1.5 * 750 * 12, AMMO_FORCE_DROP_IF_CARRIED ); // hit like a 100 kg weight at 750 ft/s #endif // HL2_EPISODIC } diff --git a/game_shared/hl2/hl2_usermessages.cpp b/game_shared/hl2/hl2_usermessages.cpp index e9f2aa19..325566d2 100644 --- a/game_shared/hl2/hl2_usermessages.cpp +++ b/game_shared/hl2/hl2_usermessages.cpp @@ -1,42 +1,42 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -//=============================================================================// - -#include "cbase.h" -#include "usermessages.h" -#include "shake.h" -#include "voice_gamemgr.h" - -// memdbgon must be the last include file in a .cpp file!!! -#include "tier0/memdbgon.h" - -void RegisterUserMessages( void ) -{ - usermessages->Register( "Geiger", 1 ); - if ( !IsXbox() ) - usermessages->Register( "Train", 1 ); - usermessages->Register( "HudText", -1 ); - usermessages->Register( "SayText", -1 ); - usermessages->Register( "TextMsg", -1 ); - usermessages->Register( "HudMsg", -1 ); - usermessages->Register( "ResetHUD", 1); // called every respawn - usermessages->Register( "GameTitle", 0 ); - usermessages->Register( "ItemPickup", -1 ); - usermessages->Register( "ShowMenu", -1 ); - usermessages->Register( "Shake", 13 ); - usermessages->Register( "Fade", 10 ); - usermessages->Register( "VGUIMenu", -1 ); // Show VGUI menu - usermessages->Register( "XBoxRumble", 3 ); // Send a rumble to XBox controller - usermessages->Register( "Battery", 2 ); - usermessages->Register( "Damage", 18 ); // BUG: floats are sent for coords, no variable bitfields in hud & fixed size Msg - usermessages->Register( "VoiceMask", VOICE_MAX_PLAYERS_DW*4 * 2 + 1 ); - usermessages->Register( "RequestState", 0 ); - usermessages->Register( "CloseCaption", ( !IsXbox() ) ? 7 : -1 ); // Show a caption (by string id number)(duration in 10th of a second) - usermessages->Register( "HintText", -1 ); // Displays hint text display - usermessages->Register( "SquadMemberDied", 0 ); - usermessages->Register( "AmmoDenied", 2 ); - usermessages->Register( "CreditsMsg", 1 ); - usermessages->Register( "LogoTimeMsg", 4 ); -} \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "cbase.h" +#include "usermessages.h" +#include "shake.h" +#include "voice_gamemgr.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +void RegisterUserMessages( void ) +{ + usermessages->Register( "Geiger", 1 ); + if ( !IsXbox() ) + usermessages->Register( "Train", 1 ); + usermessages->Register( "HudText", -1 ); + usermessages->Register( "SayText", -1 ); + usermessages->Register( "TextMsg", -1 ); + usermessages->Register( "HudMsg", -1 ); + usermessages->Register( "ResetHUD", 1); // called every respawn + usermessages->Register( "GameTitle", 0 ); + usermessages->Register( "ItemPickup", -1 ); + usermessages->Register( "ShowMenu", -1 ); + usermessages->Register( "Shake", 13 ); + usermessages->Register( "Fade", 10 ); + usermessages->Register( "VGUIMenu", -1 ); // Show VGUI menu + usermessages->Register( "XBoxRumble", 3 ); // Send a rumble to XBox controller + usermessages->Register( "Battery", 2 ); + usermessages->Register( "Damage", 18 ); // BUG: floats are sent for coords, no variable bitfields in hud & fixed size Msg + usermessages->Register( "VoiceMask", VOICE_MAX_PLAYERS_DW*4 * 2 + 1 ); + usermessages->Register( "RequestState", 0 ); + usermessages->Register( "CloseCaption", ( !IsXbox() ) ? 7 : -1 ); // Show a caption (by string id number)(duration in 10th of a second) + usermessages->Register( "HintText", -1 ); // Displays hint text display + usermessages->Register( "SquadMemberDied", 0 ); + usermessages->Register( "AmmoDenied", 2 ); + usermessages->Register( "CreditsMsg", 1 ); + usermessages->Register( "LogoTimeMsg", 4 ); +} diff --git a/game_shared/hl2/survival_gamerules.cpp b/game_shared/hl2/survival_gamerules.cpp index 88fd3dcd..bca8709c 100644 --- a/game_shared/hl2/survival_gamerules.cpp +++ b/game_shared/hl2/survival_gamerules.cpp @@ -1,272 +1,272 @@ -#include "cbase.h" - -#ifdef HL2_EPISODIC - -#include "hl2_gamerules.h" -#include "ammodef.h" -#include "hl2_shareddefs.h" -#include "filesystem.h" -#include - -#ifdef CLIENT_DLL - -#else -#include "player.h" -#include "game.h" -#include "gamerules.h" -#include "teamplay_gamerules.h" -#include "hl2_player.h" -#include "voice_gamemgr.h" -#include "globalstate.h" -#include "ai_basenpc.h" -#include "weapon_physcannon.h" -#include "ammodef.h" -#endif - -#ifdef CLIENT_DLL -#define CHalfLife2Survival C_HalfLife2Survival -#define CHalfLife2SurvivalProxy C_HalfLife2SurvivalProxy -#endif - -ConVar gamerules_survival( "gamerules_survival", "0", FCVAR_REPLICATED ); - -class CHalfLife2SurvivalProxy : public CGameRulesProxy -{ -public: - DECLARE_CLASS( CHalfLife2SurvivalProxy, CGameRulesProxy ); - DECLARE_NETWORKCLASS(); -}; - -class CSurvivalAmmo -{ -public: - - char m_szAmmoName[256]; - int m_iAmount; -}; - -class CSurvivalSettings -{ -public: - - CSurvivalSettings(); - - CUtlVector > m_Loadout; - int m_iSpawnHealth; - string_t m_szPickups; - CUtlVector m_Ammo; -}; - -CSurvivalSettings::CSurvivalSettings() -{ - m_iSpawnHealth = 100; -} - -class CHalfLife2Survival : public CHalfLife2 -{ -public: - DECLARE_CLASS( CHalfLife2Survival, CHalfLife2 ); - -#ifdef CLIENT_DLL - - DECLARE_CLIENTCLASS_NOBASE(); // This makes datatables able to access our private vars. - -#else - - DECLARE_SERVERCLASS_NOBASE(); // This makes datatables able to access our private vars. - - CHalfLife2Survival(); - virtual ~CHalfLife2Survival() {} - - virtual void Think( void ); - virtual void PlayerSpawn( CBasePlayer *pPlayer ); - virtual bool IsAllowedToSpawn( CBaseEntity *pEntity ); - virtual void CreateStandardEntities(); - - void ReadSurvivalScriptFile( void ); - void ParseSurvivalSettings( KeyValues *pSubKey ); - void ParseSurvivalAmmo( KeyValues *pSubKey ); - -private: - bool m_bActive; - CSurvivalSettings m_SurvivalSettings; -#endif - -}; - -//----------------------------------------------------------------------------- -// Gets us at the Half-Life 2 game rules -//----------------------------------------------------------------------------- -inline CHalfLife2Survival* HL2SurvivalGameRules() -{ - return static_cast(g_pGameRules); -} - -REGISTER_GAMERULES_CLASS( CHalfLife2Survival ); - -BEGIN_NETWORK_TABLE_NOBASE( CHalfLife2Survival, DT_HL2SurvivalGameRules ) -END_NETWORK_TABLE() - - -LINK_ENTITY_TO_CLASS( hl2_survival_gamerules, CHalfLife2SurvivalProxy ); -IMPLEMENT_NETWORKCLASS_ALIASED( HalfLife2SurvivalProxy, DT_HalfLife2SurvivalProxy ) - -#ifdef CLIENT_DLL - void RecvProxy_HL2SurvivalGameRules( const RecvProp *pProp, void **pOut, void *pData, int objectID ) - { - CHalfLife2Survival *pRules = HL2SurvivalGameRules(); - Assert( pRules ); - *pOut = pRules; - } - - BEGIN_RECV_TABLE( CHalfLife2SurvivalProxy, DT_HalfLife2SurvivalProxy ) - RecvPropDataTable( "hl2_survival_gamerules_data", 0, 0, &REFERENCE_RECV_TABLE( DT_HL2SurvivalGameRules ), RecvProxy_HL2SurvivalGameRules ) - END_RECV_TABLE() - #else - void* SendProxy_HL2SurvivalGameRules( const SendProp *pProp, const void *pStructBase, const void *pData, CSendProxyRecipients *pRecipients, int objectID ) - { - CHalfLife2Survival *pRules = HL2SurvivalGameRules(); - Assert( pRules ); - pRecipients->SetAllRecipients(); - return pRules; - } - - BEGIN_SEND_TABLE( CHalfLife2SurvivalProxy, DT_HalfLife2SurvivalProxy ) - SendPropDataTable( "hl2_survival_gamerules_data", 0, &REFERENCE_SEND_TABLE( DT_HL2SurvivalGameRules ), SendProxy_HL2SurvivalGameRules ) - END_SEND_TABLE() -#endif - -#ifndef CLIENT_DLL - -CHalfLife2Survival::CHalfLife2Survival() -{ - m_bActive = false; -} - -void CHalfLife2Survival::Think( void ) -{ - -} - -bool CHalfLife2Survival::IsAllowedToSpawn( CBaseEntity *pEntity ) -{ - if ( !m_bActive ) - return BaseClass::IsAllowedToSpawn( pEntity ); - - const char *pPickups = STRING( m_SurvivalSettings.m_szPickups ); - if ( !pPickups ) - return false; - - if ( Q_stristr( pPickups, "everything" ) ) - return true; - - if ( Q_stristr( pPickups, pEntity->GetClassname() ) || Q_stristr( pPickups, STRING( pEntity->GetEntityName() ) ) ) - return true; - - return false; -} - -void CHalfLife2Survival::PlayerSpawn( CBasePlayer *pPlayer ) -{ - BaseClass::PlayerSpawn( pPlayer ); - - if ( !m_bActive ) - return; - - pPlayer->EquipSuit(); - pPlayer->SetHealth( m_SurvivalSettings.m_iSpawnHealth ); - - for ( int i = 0; i < m_SurvivalSettings.m_Loadout.Count(); ++i ) - { - pPlayer->GiveNamedItem( m_SurvivalSettings.m_Loadout[i] ); - } - - for ( int i = 0; i < m_SurvivalSettings.m_Ammo.Count(); ++i ) - { - pPlayer->CBasePlayer::GiveAmmo( m_SurvivalSettings.m_Ammo[i].m_iAmount , m_SurvivalSettings.m_Ammo[i].m_szAmmoName ); - } -} - -void CHalfLife2Survival::ParseSurvivalSettings( KeyValues *pSubKey ) -{ - if ( pSubKey == NULL ) - return; - - m_SurvivalSettings.m_szPickups = NULL_STRING; - m_SurvivalSettings.m_iSpawnHealth = 100; - - KeyValues *pTestKey = pSubKey->GetFirstSubKey(); - - while ( pTestKey ) - { - if ( !stricmp( pTestKey->GetName(), "weapons" ) ) - { - const char *pLoadout = pTestKey->GetString(); - Q_SplitString( pLoadout, ";", m_SurvivalSettings.m_Loadout ); - } - else if ( !stricmp( pTestKey->GetName(), "spawnhealth" ) ) - { - m_SurvivalSettings.m_iSpawnHealth = pTestKey->GetInt( NULL, 100 ); - } - else if ( !stricmp( pTestKey->GetName(), "allowedpickups" ) ) - { - m_SurvivalSettings.m_szPickups = MAKE_STRING( pTestKey->GetString() ); - } - - pTestKey = pTestKey->GetNextKey(); - } -} - -void CHalfLife2Survival::ParseSurvivalAmmo( KeyValues *pSubKey ) -{ - if ( pSubKey ) - { - KeyValues *pAmmoKey = pSubKey->GetFirstSubKey(); - - while ( pAmmoKey ) - { - CSurvivalAmmo ammo; - - Q_strcpy( ammo.m_szAmmoName, pAmmoKey->GetName() ); - ammo.m_iAmount = pAmmoKey->GetInt(); - - m_SurvivalSettings.m_Ammo.AddToTail( ammo ); - - pAmmoKey = pAmmoKey->GetNextKey(); - } - } -} - -void CHalfLife2Survival::ReadSurvivalScriptFile( void ) -{ - char szFullName[512]; - Q_snprintf( szFullName, sizeof( szFullName ), "maps/%s_survival.txt", gpGlobals->mapname ); - - KeyValues *pkvFile = new KeyValues( "Survival" ); - if ( pkvFile->LoadFromFile( filesystem, szFullName, "MOD" ) ) - { - ParseSurvivalSettings( pkvFile->FindKey( "settings" ) ); - ParseSurvivalAmmo( pkvFile->FindKey( "ammo" ) ); - - CUtlVector entities; - UTIL_LoadAndSpawnEntitiesFromScript( entities, szFullName, "Survival", true ); - - // It's important to turn on survival mode after we create all the entities - // in the script, so that we don't remove them if they violate survival rules. - // i.e. we want the player to start with a shotgun, but prevent all future shotguns from spawning. - m_bActive = true; - } - else - { - m_bActive = false; - } -} - -void CHalfLife2Survival::CreateStandardEntities( void ) -{ - ReadSurvivalScriptFile(); -} - -#endif - -#endif \ No newline at end of file +#include "cbase.h" + +#ifdef HL2_EPISODIC + +#include "hl2_gamerules.h" +#include "ammodef.h" +#include "hl2_shareddefs.h" +#include "filesystem.h" +#include + +#ifdef CLIENT_DLL + +#else +#include "player.h" +#include "game.h" +#include "gamerules.h" +#include "teamplay_gamerules.h" +#include "hl2_player.h" +#include "voice_gamemgr.h" +#include "globalstate.h" +#include "ai_basenpc.h" +#include "weapon_physcannon.h" +#include "ammodef.h" +#endif + +#ifdef CLIENT_DLL +#define CHalfLife2Survival C_HalfLife2Survival +#define CHalfLife2SurvivalProxy C_HalfLife2SurvivalProxy +#endif + +ConVar gamerules_survival( "gamerules_survival", "0", FCVAR_REPLICATED ); + +class CHalfLife2SurvivalProxy : public CGameRulesProxy +{ +public: + DECLARE_CLASS( CHalfLife2SurvivalProxy, CGameRulesProxy ); + DECLARE_NETWORKCLASS(); +}; + +class CSurvivalAmmo +{ +public: + + char m_szAmmoName[256]; + int m_iAmount; +}; + +class CSurvivalSettings +{ +public: + + CSurvivalSettings(); + + CUtlVector > m_Loadout; + int m_iSpawnHealth; + string_t m_szPickups; + CUtlVector m_Ammo; +}; + +CSurvivalSettings::CSurvivalSettings() +{ + m_iSpawnHealth = 100; +} + +class CHalfLife2Survival : public CHalfLife2 +{ +public: + DECLARE_CLASS( CHalfLife2Survival, CHalfLife2 ); + +#ifdef CLIENT_DLL + + DECLARE_CLIENTCLASS_NOBASE(); // This makes datatables able to access our private vars. + +#else + + DECLARE_SERVERCLASS_NOBASE(); // This makes datatables able to access our private vars. + + CHalfLife2Survival(); + virtual ~CHalfLife2Survival() {} + + virtual void Think( void ); + virtual void PlayerSpawn( CBasePlayer *pPlayer ); + virtual bool IsAllowedToSpawn( CBaseEntity *pEntity ); + virtual void CreateStandardEntities(); + + void ReadSurvivalScriptFile( void ); + void ParseSurvivalSettings( KeyValues *pSubKey ); + void ParseSurvivalAmmo( KeyValues *pSubKey ); + +private: + bool m_bActive; + CSurvivalSettings m_SurvivalSettings; +#endif + +}; + +//----------------------------------------------------------------------------- +// Gets us at the Half-Life 2 game rules +//----------------------------------------------------------------------------- +inline CHalfLife2Survival* HL2SurvivalGameRules() +{ + return static_cast(g_pGameRules); +} + +REGISTER_GAMERULES_CLASS( CHalfLife2Survival ); + +BEGIN_NETWORK_TABLE_NOBASE( CHalfLife2Survival, DT_HL2SurvivalGameRules ) +END_NETWORK_TABLE() + + +LINK_ENTITY_TO_CLASS( hl2_survival_gamerules, CHalfLife2SurvivalProxy ); +IMPLEMENT_NETWORKCLASS_ALIASED( HalfLife2SurvivalProxy, DT_HalfLife2SurvivalProxy ) + +#ifdef CLIENT_DLL + void RecvProxy_HL2SurvivalGameRules( const RecvProp *pProp, void **pOut, void *pData, int objectID ) + { + CHalfLife2Survival *pRules = HL2SurvivalGameRules(); + Assert( pRules ); + *pOut = pRules; + } + + BEGIN_RECV_TABLE( CHalfLife2SurvivalProxy, DT_HalfLife2SurvivalProxy ) + RecvPropDataTable( "hl2_survival_gamerules_data", 0, 0, &REFERENCE_RECV_TABLE( DT_HL2SurvivalGameRules ), RecvProxy_HL2SurvivalGameRules ) + END_RECV_TABLE() + #else + void* SendProxy_HL2SurvivalGameRules( const SendProp *pProp, const void *pStructBase, const void *pData, CSendProxyRecipients *pRecipients, int objectID ) + { + CHalfLife2Survival *pRules = HL2SurvivalGameRules(); + Assert( pRules ); + pRecipients->SetAllRecipients(); + return pRules; + } + + BEGIN_SEND_TABLE( CHalfLife2SurvivalProxy, DT_HalfLife2SurvivalProxy ) + SendPropDataTable( "hl2_survival_gamerules_data", 0, &REFERENCE_SEND_TABLE( DT_HL2SurvivalGameRules ), SendProxy_HL2SurvivalGameRules ) + END_SEND_TABLE() +#endif + +#ifndef CLIENT_DLL + +CHalfLife2Survival::CHalfLife2Survival() +{ + m_bActive = false; +} + +void CHalfLife2Survival::Think( void ) +{ + +} + +bool CHalfLife2Survival::IsAllowedToSpawn( CBaseEntity *pEntity ) +{ + if ( !m_bActive ) + return BaseClass::IsAllowedToSpawn( pEntity ); + + const char *pPickups = STRING( m_SurvivalSettings.m_szPickups ); + if ( !pPickups ) + return false; + + if ( Q_stristr( pPickups, "everything" ) ) + return true; + + if ( Q_stristr( pPickups, pEntity->GetClassname() ) || Q_stristr( pPickups, STRING( pEntity->GetEntityName() ) ) ) + return true; + + return false; +} + +void CHalfLife2Survival::PlayerSpawn( CBasePlayer *pPlayer ) +{ + BaseClass::PlayerSpawn( pPlayer ); + + if ( !m_bActive ) + return; + + pPlayer->EquipSuit(); + pPlayer->SetHealth( m_SurvivalSettings.m_iSpawnHealth ); + + for ( int i = 0; i < m_SurvivalSettings.m_Loadout.Count(); ++i ) + { + pPlayer->GiveNamedItem( m_SurvivalSettings.m_Loadout[i] ); + } + + for ( int i = 0; i < m_SurvivalSettings.m_Ammo.Count(); ++i ) + { + pPlayer->CBasePlayer::GiveAmmo( m_SurvivalSettings.m_Ammo[i].m_iAmount , m_SurvivalSettings.m_Ammo[i].m_szAmmoName ); + } +} + +void CHalfLife2Survival::ParseSurvivalSettings( KeyValues *pSubKey ) +{ + if ( pSubKey == NULL ) + return; + + m_SurvivalSettings.m_szPickups = NULL_STRING; + m_SurvivalSettings.m_iSpawnHealth = 100; + + KeyValues *pTestKey = pSubKey->GetFirstSubKey(); + + while ( pTestKey ) + { + if ( !stricmp( pTestKey->GetName(), "weapons" ) ) + { + const char *pLoadout = pTestKey->GetString(); + Q_SplitString( pLoadout, ";", m_SurvivalSettings.m_Loadout ); + } + else if ( !stricmp( pTestKey->GetName(), "spawnhealth" ) ) + { + m_SurvivalSettings.m_iSpawnHealth = pTestKey->GetInt( 0, 100 ); + } + else if ( !stricmp( pTestKey->GetName(), "allowedpickups" ) ) + { + m_SurvivalSettings.m_szPickups = MAKE_STRING( pTestKey->GetString() ); + } + + pTestKey = pTestKey->GetNextKey(); + } +} + +void CHalfLife2Survival::ParseSurvivalAmmo( KeyValues *pSubKey ) +{ + if ( pSubKey ) + { + KeyValues *pAmmoKey = pSubKey->GetFirstSubKey(); + + while ( pAmmoKey ) + { + CSurvivalAmmo ammo; + + Q_strcpy( ammo.m_szAmmoName, pAmmoKey->GetName() ); + ammo.m_iAmount = pAmmoKey->GetInt(); + + m_SurvivalSettings.m_Ammo.AddToTail( ammo ); + + pAmmoKey = pAmmoKey->GetNextKey(); + } + } +} + +void CHalfLife2Survival::ReadSurvivalScriptFile( void ) +{ + char szFullName[512]; + Q_snprintf( szFullName, sizeof( szFullName ), "maps/%s_survival.txt", STRING(gpGlobals->mapname) ); + + KeyValues *pkvFile = new KeyValues( "Survival" ); + if ( pkvFile->LoadFromFile( filesystem, szFullName, "MOD" ) ) + { + ParseSurvivalSettings( pkvFile->FindKey( "settings" ) ); + ParseSurvivalAmmo( pkvFile->FindKey( "ammo" ) ); + + CUtlVector entities; + UTIL_LoadAndSpawnEntitiesFromScript( entities, szFullName, "Survival", true ); + + // It's important to turn on survival mode after we create all the entities + // in the script, so that we don't remove them if they violate survival rules. + // i.e. we want the player to start with a shotgun, but prevent all future shotguns from spawning. + m_bActive = true; + } + else + { + m_bActive = false; + } +} + +void CHalfLife2Survival::CreateStandardEntities( void ) +{ + ReadSurvivalScriptFile(); +} + +#endif + +#endif diff --git a/game_shared/hl2mp/hl2mp_gamerules.cpp b/game_shared/hl2mp/hl2mp_gamerules.cpp index cb3797e3..4f403718 100644 --- a/game_shared/hl2mp/hl2mp_gamerules.cpp +++ b/game_shared/hl2mp/hl2mp_gamerules.cpp @@ -1,1237 +1,1237 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -//=============================================================================// -#include "cbase.h" -#include "hl2mp_gamerules.h" -#include "viewport_panel_names.h" -#include "gameeventdefs.h" -#include -#include "ammodef.h" - -#ifdef CLIENT_DLL - #include "c_hl2mp_player.h" -#else - - #include "eventqueue.h" - #include "player.h" - #include "gamerules.h" - #include "game.h" - #include "items.h" - #include "entitylist.h" - #include "mapentities.h" - #include "in_buttons.h" - #include - #include "voice_gamemgr.h" - #include "iscorer.h" - #include "hl2mp_player.h" - #include "weapon_hl2mpbasehlmpcombatweapon.h" - #include "team.h" - #include "voice_gamemgr.h" - #include "hl2mp_gameinterface.h" - #include "hl2mp_cvars.h" - -#ifdef DEBUG - #include "hl2mp_bot_temp.h" -#endif - -extern void respawn(CBaseEntity *pEdict, bool fCopyCorpse); - - -ConVar sv_hl2mp_weapon_respawn_time( "sv_hl2mp_weapon_respawn_time", "20", FCVAR_GAMEDLL | FCVAR_NOTIFY ); -ConVar sv_hl2mp_item_respawn_time( "sv_hl2mp_item_respawn_time", "30", FCVAR_GAMEDLL | FCVAR_NOTIFY ); -ConVar mp_restartgame( "mp_restartgame", "0", 0, "If non-zero, game will restart in the specified number of seconds" ); -ConVar sv_report_client_settings("sv_report_client_settings", "0", FCVAR_GAMEDLL | FCVAR_NOTIFY ); - -extern ConVar mp_chattime; - -extern CBaseEntity *g_pLastCombineSpawn; -extern CBaseEntity *g_pLastRebelSpawn; - -#define WEAPON_MAX_DISTANCE_FROM_SPAWN 64 - -#endif - - -REGISTER_GAMERULES_CLASS( CHL2MPRules ); - -BEGIN_NETWORK_TABLE_NOBASE( CHL2MPRules, DT_HL2MPRules ) - - #ifdef CLIENT_DLL - RecvPropBool( RECVINFO( m_bTeamPlayEnabled ) ), - #else - SendPropBool( SENDINFO( m_bTeamPlayEnabled ) ), - #endif - -END_NETWORK_TABLE() - - -LINK_ENTITY_TO_CLASS( hl2mp_gamerules, CHL2MPGameRulesProxy ); -IMPLEMENT_NETWORKCLASS_ALIASED( HL2MPGameRulesProxy, DT_HL2MPGameRulesProxy ) - -static HL2MPViewVectors g_HL2MPViewVectors( - Vector( 0, 0, 64 ), //m_vView - - Vector(-16, -16, 0 ), //m_vHullMin - Vector( 16, 16, 72 ), //m_vHullMax - - Vector(-16, -16, 0 ), //m_vDuckHullMin - Vector( 16, 16, 36 ), //m_vDuckHullMax - Vector( 0, 0, 28 ), //m_vDuckView - - Vector(-10, -10, -10 ), //m_vObsHullMin - Vector( 10, 10, 10 ), //m_vObsHullMax - - Vector( 0, 0, 14 ), //m_vDeadViewHeight - - Vector(-16, -16, 0 ), //m_vCrouchTraceMin - Vector( 16, 16, 60 ) //m_vCrouchTraceMax -); - -static const char *s_PreserveEnts[] = -{ - "ai_network", - "ai_hint", - "hl2mp_gamerules", - "team_manager", - "player_manager", - "env_soundscape", - "env_soundscape_proxy", - "env_soundscape_triggerable", - "env_sun", - "env_wind", - "env_fog_controller", - "func_brush", - "func_wall", - "func_buyzone", - "func_illusionary", - "infodecal", - "info_projecteddecal", - "info_node", - "info_target", - "info_node_hint", - "info_player_deathmatch", - "info_player_combine", - "info_player_rebel", - "info_map_parameters", - "keyframe_rope", - "move_rope", - "info_ladder", - "player", - "point_viewcontrol", - "scene_manager", - "shadow_control", - "sky_camera", - "soundent", - "trigger_soundscape", - "viewmodel", - "predicted_viewmodel", - "worldspawn", - "point_devshot_camera", - "", // END Marker -}; - - - -#ifdef CLIENT_DLL - void RecvProxy_HL2MPRules( const RecvProp *pProp, void **pOut, void *pData, int objectID ) - { - CHL2MPRules *pRules = HL2MPRules(); - Assert( pRules ); - *pOut = pRules; - } - - BEGIN_RECV_TABLE( CHL2MPGameRulesProxy, DT_HL2MPGameRulesProxy ) - RecvPropDataTable( "hl2mp_gamerules_data", 0, 0, &REFERENCE_RECV_TABLE( DT_HL2MPRules ), RecvProxy_HL2MPRules ) - END_RECV_TABLE() -#else - void* SendProxy_HL2MPRules( const SendProp *pProp, const void *pStructBase, const void *pData, CSendProxyRecipients *pRecipients, int objectID ) - { - CHL2MPRules *pRules = HL2MPRules(); - Assert( pRules ); - return pRules; - } - - BEGIN_SEND_TABLE( CHL2MPGameRulesProxy, DT_HL2MPGameRulesProxy ) - SendPropDataTable( "hl2mp_gamerules_data", 0, &REFERENCE_SEND_TABLE( DT_HL2MPRules ), SendProxy_HL2MPRules ) - END_SEND_TABLE() -#endif - -#ifndef CLIENT_DLL - - class CVoiceGameMgrHelper : public IVoiceGameMgrHelper - { - public: - virtual bool CanPlayerHearPlayer( CBasePlayer *pListener, CBasePlayer *pTalker ) - { - return ( pListener->GetTeamNumber() == pTalker->GetTeamNumber() ); - } - }; - CVoiceGameMgrHelper g_VoiceGameMgrHelper; - IVoiceGameMgrHelper *g_pVoiceGameMgrHelper = &g_VoiceGameMgrHelper; - -#endif - -// NOTE: the indices here must match TEAM_TERRORIST, TEAM_CT, TEAM_SPECTATOR, etc. -char *sTeamNames[] = -{ - "Unassigned", - "Spectator", - "Combine", - "Rebels", -}; - -CHL2MPRules::CHL2MPRules() -{ -#ifndef CLIENT_DLL - // Create the team managers - for ( int i = 0; i < ARRAYSIZE( sTeamNames ); i++ ) - { - CTeam *pTeam = static_cast(CreateEntityByName( "team_manager" )); - pTeam->Init( sTeamNames[i], i ); - - g_Teams.AddToTail( pTeam ); - } - - m_bTeamPlayEnabled = teamplay.GetBool(); - m_flIntermissionEndTime = 0.0f; - m_flGameStartTime = 0; - - m_hRespawnableItemsAndWeapons.RemoveAll(); - m_tmNextPeriodicThink = 0; - m_flRestartGameTime = 0; - m_bCompleteReset = false; - m_bHeardAllPlayersReady = false; - m_bAwaitingReadyRestart = false; - -#endif -} - -const CViewVectors* CHL2MPRules::GetViewVectors()const -{ - return &g_HL2MPViewVectors; -} - -const HL2MPViewVectors* CHL2MPRules::GetHL2MPViewVectors()const -{ - return &g_HL2MPViewVectors; -} - -CHL2MPRules::~CHL2MPRules( void ) -{ -#ifndef CLIENT_DLL - // Note, don't delete each team since they are in the gEntList and will - // automatically be deleted from there, instead. - g_Teams.Purge(); -#endif -} - -void CHL2MPRules::CreateStandardEntities( void ) -{ - -#ifndef CLIENT_DLL - // Create the entity that will send our data to the client. - - BaseClass::CreateStandardEntities(); - - g_pLastCombineSpawn = NULL; - g_pLastRebelSpawn = NULL; - -#ifdef _DEBUG - CBaseEntity *pEnt = -#endif - CBaseEntity::Create( "hl2mp_gamerules", vec3_origin, vec3_angle ); - Assert( pEnt ); -#endif -} - -//========================================================= -// FlWeaponRespawnTime - what is the time in the future -// at which this weapon may spawn? -//========================================================= -float CHL2MPRules::FlWeaponRespawnTime( CBaseCombatWeapon *pWeapon ) -{ -#ifndef CLIENT_DLL - if ( weaponstay.GetInt() > 0 ) - { - // make sure it's only certain weapons - if ( !(pWeapon->GetWeaponFlags() & ITEM_FLAG_LIMITINWORLD) ) - { - return 0; // weapon respawns almost instantly - } - } - - return sv_hl2mp_weapon_respawn_time.GetFloat(); -#endif - - return 0; // weapon respawns almost instantly -} - - -bool CHL2MPRules::IsIntermission( void ) -{ -#ifndef CLIENT_DLL - return m_flIntermissionEndTime > gpGlobals->curtime; -#endif - - return false; -} - -void CHL2MPRules::PlayerKilled( CBasePlayer *pVictim, const CTakeDamageInfo &info ) -{ -#ifndef CLIENT_DLL - if ( IsIntermission() ) - return; - BaseClass::PlayerKilled( pVictim, info ); -#endif -} - - -void CHL2MPRules::Think( void ) -{ - -#ifndef CLIENT_DLL - - CGameRules::Think(); - - if ( g_fGameOver ) // someone else quit the game already - { - // check to see if we should change levels now - if ( m_flIntermissionEndTime < gpGlobals->curtime ) - { - ChangeLevel(); // intermission is over - } - - return; - } - -// float flTimeLimit = mp_timelimit.GetFloat() * 60; - float flFragLimit = fraglimit.GetFloat(); - - if ( GetMapRemainingTime() < 0 ) - { - GoToIntermission(); - return; - } - - if ( flFragLimit ) - { - if( IsTeamplay() == true ) - { - CTeam *pCombine = g_Teams[TEAM_COMBINE]; - CTeam *pRebels = g_Teams[TEAM_REBELS]; - - if ( pCombine->GetScore() >= flFragLimit || pRebels->GetScore() >= flFragLimit ) - { - GoToIntermission(); - return; - } - } - else - { - // check if any player is over the frag limit - for ( int i = 1; i <= gpGlobals->maxClients; i++ ) - { - CBasePlayer *pPlayer = UTIL_PlayerByIndex( i ); - - if ( pPlayer && pPlayer->FragCount() >= flFragLimit ) - { - GoToIntermission(); - return; - } - } - } - } - - if ( gpGlobals->curtime > m_tmNextPeriodicThink ) - { - CheckAllPlayersReady(); - CheckRestartGame(); - m_tmNextPeriodicThink = gpGlobals->curtime + 1.0; - } - - if ( m_flRestartGameTime > 0.0f && m_flRestartGameTime <= gpGlobals->curtime ) - { - RestartGame(); - } - - if( m_bAwaitingReadyRestart && m_bHeardAllPlayersReady ) - { - UTIL_ClientPrintAll( HUD_PRINTCENTER, "All players ready. Game will restart in 5 seconds" ); - UTIL_ClientPrintAll( HUD_PRINTCONSOLE, "All players ready. Game will restart in 5 seconds" ); - - m_flRestartGameTime = gpGlobals->curtime + 5; - m_bAwaitingReadyRestart = false; - } - - ManageObjectRelocation(); - -#endif -} - -void CHL2MPRules::GoToIntermission( void ) -{ -#ifndef CLIENT_DLL - if ( g_fGameOver ) - return; - - g_fGameOver = true; - - m_flIntermissionEndTime = gpGlobals->curtime + mp_chattime.GetInt(); - - for ( int i = 0; i < MAX_PLAYERS; i++ ) - { - CBasePlayer *pPlayer = UTIL_PlayerByIndex( i ); - - if ( !pPlayer ) - continue; - - pPlayer->ShowViewPortPanel( PANEL_SCOREBOARD ); - pPlayer->AddFlag( FL_FROZEN ); - } -#endif - -} - -bool CHL2MPRules::CheckGameOver() -{ -#ifndef CLIENT_DLL - if ( g_fGameOver ) // someone else quit the game already - { - // check to see if we should change levels now - if ( m_flIntermissionEndTime < gpGlobals->curtime ) - { - ChangeLevel(); // intermission is over - } - - return true; - } -#endif - - return false; -} - -// when we are within this close to running out of entities, items -// marked with the ITEM_FLAG_LIMITINWORLD will delay their respawn -#define ENTITY_INTOLERANCE 100 - -//========================================================= -// FlWeaponRespawnTime - Returns 0 if the weapon can respawn -// now, otherwise it returns the time at which it can try -// to spawn again. -//========================================================= -float CHL2MPRules::FlWeaponTryRespawn( CBaseCombatWeapon *pWeapon ) -{ -#ifndef CLIENT_DLL - if ( pWeapon && (pWeapon->GetWeaponFlags() & ITEM_FLAG_LIMITINWORLD) ) - { - if ( gEntList.NumberOfEntities() < (gpGlobals->maxEntities - ENTITY_INTOLERANCE) ) - return 0; - - // we're past the entity tolerance level, so delay the respawn - return FlWeaponRespawnTime( pWeapon ); - } -#endif - return 0; -} - -//========================================================= -// VecWeaponRespawnSpot - where should this weapon spawn? -// Some game variations may choose to randomize spawn locations -//========================================================= -Vector CHL2MPRules::VecWeaponRespawnSpot( CBaseCombatWeapon *pWeapon ) -{ -#ifndef CLIENT_DLL - CWeaponHL2MPBase *pHL2Weapon = dynamic_cast< CWeaponHL2MPBase*>( pWeapon ); - - if ( pHL2Weapon ) - { - return pHL2Weapon->GetOriginalSpawnOrigin(); - } -#endif - - return pWeapon->GetAbsOrigin(); -} - -#ifndef CLIENT_DLL - -CItem* IsManagedObjectAnItem( CBaseEntity *pObject ) -{ - return dynamic_cast< CItem*>( pObject ); -} - -CWeaponHL2MPBase* IsManagedObjectAWeapon( CBaseEntity *pObject ) -{ - return dynamic_cast< CWeaponHL2MPBase*>( pObject ); -} - -bool GetObjectsOriginalParameters( CBaseEntity *pObject, Vector &vOriginalOrigin, QAngle &vOriginalAngles ) -{ - if ( CItem *pItem = IsManagedObjectAnItem( pObject ) ) - { - if ( pItem->m_flNextResetCheckTime > gpGlobals->curtime ) - return false; - - vOriginalOrigin = pItem->GetOriginalSpawnOrigin(); - vOriginalAngles = pItem->GetOriginalSpawnAngles(); - - pItem->m_flNextResetCheckTime = gpGlobals->curtime + sv_hl2mp_item_respawn_time.GetFloat(); - return true; - } - else if ( CWeaponHL2MPBase *pWeapon = IsManagedObjectAWeapon( pObject )) - { - if ( pWeapon->m_flNextResetCheckTime > gpGlobals->curtime ) - return false; - - vOriginalOrigin = pWeapon->GetOriginalSpawnOrigin(); - vOriginalAngles = pWeapon->GetOriginalSpawnAngles(); - - pWeapon->m_flNextResetCheckTime = gpGlobals->curtime + sv_hl2mp_weapon_respawn_time.GetFloat(); - return true; - } - - return false; -} - -void CHL2MPRules::ManageObjectRelocation( void ) -{ - int iTotal = m_hRespawnableItemsAndWeapons.Count(); - - if ( iTotal > 0 ) - { - for ( int i = 0; i < iTotal; i++ ) - { - CBaseEntity *pObject = m_hRespawnableItemsAndWeapons[i].Get(); - - if ( pObject ) - { - Vector vSpawOrigin; - QAngle vSpawnAngles; - - if ( GetObjectsOriginalParameters( pObject, vSpawOrigin, vSpawnAngles ) == true ) - { - float flDistanceFromSpawn = (pObject->GetAbsOrigin() - vSpawOrigin ).Length(); - - if ( flDistanceFromSpawn > WEAPON_MAX_DISTANCE_FROM_SPAWN ) - { - bool shouldReset = false; - IPhysicsObject *pPhysics = pObject->VPhysicsGetObject(); - - if ( pPhysics ) - { - shouldReset = pPhysics->IsAsleep(); - } - else - { - shouldReset = (pObject->GetFlags() & FL_ONGROUND) ? true : false; - } - - if ( shouldReset ) - { - pObject->Teleport( &vSpawOrigin, &vSpawnAngles, NULL ); - pObject->EmitSound( "AlyxEmp.Charge" ); - - IPhysicsObject *pPhys = pObject->VPhysicsGetObject(); - - if ( pPhys ) - { - pPhys->Wake(); - } - } - } - } - } - } - } -} - -//========================================================= -//AddLevelDesignerPlacedWeapon -//========================================================= -void CHL2MPRules::AddLevelDesignerPlacedObject( CBaseEntity *pEntity ) -{ - if ( m_hRespawnableItemsAndWeapons.Find( pEntity ) == -1 ) - { - m_hRespawnableItemsAndWeapons.AddToTail( pEntity ); - } -} - -//========================================================= -//RemoveLevelDesignerPlacedWeapon -//========================================================= -void CHL2MPRules::RemoveLevelDesignerPlacedObject( CBaseEntity *pEntity ) -{ - if ( m_hRespawnableItemsAndWeapons.Find( pEntity ) != -1 ) - { - m_hRespawnableItemsAndWeapons.FindAndRemove( pEntity ); - } -} - -//========================================================= -// Where should this item respawn? -// Some game variations may choose to randomize spawn locations -//========================================================= -Vector CHL2MPRules::VecItemRespawnSpot( CItem *pItem ) -{ - return pItem->GetOriginalSpawnOrigin(); -} - -//========================================================= -// What angles should this item use to respawn? -//========================================================= -QAngle CHL2MPRules::VecItemRespawnAngles( CItem *pItem ) -{ - return pItem->GetOriginalSpawnAngles(); -} - -//========================================================= -// At what time in the future may this Item respawn? -//========================================================= -float CHL2MPRules::FlItemRespawnTime( CItem *pItem ) -{ - return sv_hl2mp_item_respawn_time.GetFloat(); -} - - -//========================================================= -// CanHaveWeapon - returns false if the player is not allowed -// to pick up this weapon -//========================================================= -bool CHL2MPRules::CanHavePlayerItem( CBasePlayer *pPlayer, CBaseCombatWeapon *pItem ) -{ - if ( weaponstay.GetInt() > 0 ) - { - if ( pPlayer->Weapon_OwnsThisType( pItem->GetClassname(), pItem->GetSubType() ) ) - return false; - } - - return BaseClass::CanHavePlayerItem( pPlayer, pItem ); -} - -#endif - -//========================================================= -// WeaponShouldRespawn - any conditions inhibiting the -// respawning of this weapon? -//========================================================= -int CHL2MPRules::WeaponShouldRespawn( CBaseCombatWeapon *pWeapon ) -{ -#ifndef CLIENT_DLL - if ( pWeapon->HasSpawnFlags( SF_NORESPAWN ) ) - { - return GR_WEAPON_RESPAWN_NO; - } -#endif - - return GR_WEAPON_RESPAWN_YES; -} - -//----------------------------------------------------------------------------- -// Purpose: Player has just left the game -//----------------------------------------------------------------------------- -void CHL2MPRules::ClientDisconnected( edict_t *pClient ) -{ -#ifndef CLIENT_DLL - // Msg( "CLIENT DISCONNECTED, REMOVING FROM TEAM.\n" ); - - CBasePlayer *pPlayer = (CBasePlayer *)CBaseEntity::Instance( pClient ); - if ( pPlayer ) - { - // Remove the player from his team - if ( pPlayer->GetTeam() ) - { - pPlayer->GetTeam()->RemovePlayer( pPlayer ); - } - } - - BaseClass::ClientDisconnected( pClient ); - -#endif -} - - -//========================================================= -// Deathnotice. -//========================================================= -void CHL2MPRules::DeathNotice( CBasePlayer *pVictim, const CTakeDamageInfo &info ) -{ -#ifndef CLIENT_DLL - // Work out what killed the player, and send a message to all clients about it - const char *killer_weapon_name = "world"; // by default, the player is killed by the world - int killer_ID = 0; - - // Find the killer & the scorer - CBaseEntity *pInflictor = info.GetInflictor(); - CBaseEntity *pKiller = info.GetAttacker(); - CBasePlayer *pScorer = GetDeathScorer( pKiller, pInflictor ); - - // Custom kill type? - if ( info.GetCustomKill() ) - { - killer_weapon_name = GetCustomKillString( info ); - if ( pScorer ) - { - killer_ID = pScorer->GetUserID(); - } - } - else - { - // Is the killer a client? - if ( pScorer ) - { - killer_ID = pScorer->GetUserID(); - - if ( pInflictor ) - { - if ( pInflictor == pScorer ) - { - // If the inflictor is the killer, then it must be their current weapon doing the damage - if ( pScorer->GetActiveWeapon() ) - { - killer_weapon_name = pScorer->GetActiveWeapon()->GetClassname(); - } - } - else - { - killer_weapon_name = pInflictor->GetClassname(); // it's just that easy - } - } - } - else - { - killer_weapon_name = pInflictor->GetClassname(); - } - - // strip the NPC_* or weapon_* from the inflictor's classname - if ( strncmp( killer_weapon_name, "weapon_", 7 ) == 0 ) - { - killer_weapon_name += 7; - } - else if ( strncmp( killer_weapon_name, "npc_", 4 ) == 0 ) - { - killer_weapon_name += 4; - } - else if ( strncmp( killer_weapon_name, "func_", 5 ) == 0 ) - { - killer_weapon_name += 5; - } - else if ( strstr( killer_weapon_name, "physics" ) ) - { - killer_weapon_name = "physics"; - } - - if ( strcmp( killer_weapon_name, "prop_combine_ball" ) == 0 ) - { - killer_weapon_name = "combine_ball"; - } - else if ( strcmp( killer_weapon_name, "grenade_ar2" ) == 0 ) - { - killer_weapon_name = "smg1_grenade"; - } - else if ( strcmp( killer_weapon_name, "satchel" ) == 0 || strcmp( killer_weapon_name, "tripmine" ) == 0) - { - killer_weapon_name = "slam"; - } - - - } - - IGameEvent *event = gameeventmanager->CreateEvent( "player_death" ); - if( event ) - { - event->SetInt("userid", pVictim->GetUserID() ); - event->SetInt("attacker", killer_ID ); - event->SetString("weapon", killer_weapon_name ); - event->SetInt( "priority", 7 ); - gameeventmanager->FireEvent( event ); - } -#endif - -} - -void CHL2MPRules::ClientSettingsChanged( CBasePlayer *pPlayer ) -{ -#ifndef CLIENT_DLL - - CHL2MP_Player *pHL2Player = ToHL2MPPlayer( pPlayer ); - - if ( pHL2Player == NULL ) - return; - - const char *pCurrentModel = modelinfo->GetModelName( pPlayer->GetModel() ); - const char *szModelName = engine->GetClientConVarValue( engine->IndexOfEdict( pPlayer->edict() ), "cl_playermodel" ); - - //If we're different. - if ( stricmp( szModelName, pCurrentModel ) ) - { - //Too soon, set the cvar back to what it was. - //Note: this will make this function be called again - //but since our models will match it'll just skip this whole dealio. - if ( pHL2Player->GetNextModelChangeTime() >= gpGlobals->curtime ) - { - char szReturnString[512]; - - Q_snprintf( szReturnString, sizeof (szReturnString ), "cl_playermodel %s\n", pCurrentModel ); - engine->ClientCommand ( pHL2Player->edict(), szReturnString ); - - Q_snprintf( szReturnString, sizeof( szReturnString ), "Please wait %d more seconds before trying to switch.\n", (int)(pHL2Player->GetNextModelChangeTime() - gpGlobals->curtime) ); - ClientPrint( pHL2Player, HUD_PRINTTALK, szReturnString ); - return; - } - - if ( HL2MPRules()->IsTeamplay() == false ) - { - pHL2Player->SetPlayerModel(); - - const char *pszCurrentModelName = modelinfo->GetModelName( pHL2Player->GetModel() ); - - char szReturnString[128]; - Q_snprintf( szReturnString, sizeof( szReturnString ), "Your player model is: %s\n", pszCurrentModelName ); - - ClientPrint( pHL2Player, HUD_PRINTTALK, szReturnString ); - } - else - { - if ( Q_stristr( szModelName, "models/human") ) - { - pHL2Player->ChangeTeam( TEAM_REBELS ); - } - else - { - pHL2Player->ChangeTeam( TEAM_COMBINE ); - } - } - } - if ( sv_report_client_settings.GetInt() == 1 ) - { - UTIL_LogPrintf( "\"%s\" cl_cmdrate = \"%s\"\n", pHL2Player->GetPlayerName(), engine->GetClientConVarValue( pHL2Player->entindex(), "cl_cmdrate" )); - } - - BaseClass::ClientSettingsChanged( pPlayer ); -#endif - -} - -int CHL2MPRules::PlayerRelationship( CBaseEntity *pPlayer, CBaseEntity *pTarget ) -{ -#ifndef CLIENT_DLL - // half life multiplay has a simple concept of Player Relationships. - // you are either on another player's team, or you are not. - if ( !pPlayer || !pTarget || !pTarget->IsPlayer() || IsTeamplay() == false ) - return GR_NOTTEAMMATE; - - if ( (*GetTeamID(pPlayer) != '\0') && (*GetTeamID(pTarget) != '\0') && !stricmp( GetTeamID(pPlayer), GetTeamID(pTarget) ) ) - { - return GR_TEAMMATE; - } -#endif - - return GR_NOTTEAMMATE; -} - -const char *CHL2MPRules::GetGameDescription( void ) -{ - if ( IsTeamplay() ) - return "Team Deathmatch"; - - return "Deathmatch"; -} - - -float CHL2MPRules::GetMapRemainingTime() -{ - // if timelimit is disabled, return 0 - if ( mp_timelimit.GetInt() <= 0 ) - return 0; - - // timelimit is in minutes - - float timeleft = (m_flGameStartTime + mp_timelimit.GetInt() * 60.0f ) - gpGlobals->curtime; - - return timeleft; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CHL2MPRules::Precache( void ) -{ - CBaseEntity::PrecacheScriptSound( "AlyxEmp.Charge" ); -} - -bool CHL2MPRules::ShouldCollide( int collisionGroup0, int collisionGroup1 ) -{ - if ( collisionGroup0 > collisionGroup1 ) - { - // swap so that lowest is always first - swap(collisionGroup0,collisionGroup1); - } - - if ( (collisionGroup0 == COLLISION_GROUP_PLAYER || collisionGroup0 == COLLISION_GROUP_PLAYER_MOVEMENT) && - collisionGroup1 == COLLISION_GROUP_WEAPON ) - { - return false; - } - - return BaseClass::ShouldCollide( collisionGroup0, collisionGroup1 ); - -} - -bool CHL2MPRules::ClientCommand(const char *pcmd, CBaseEntity *pEdict ) -{ - -#ifndef CLIENT_DLL - if( BaseClass::ClientCommand(pcmd, pEdict) ) - return true; - - - CHL2MP_Player *pPlayer = (CHL2MP_Player *) pEdict; - - if ( pPlayer->ClientCommand( pcmd ) ) - return true; -#endif - - return false; -} - -// shared ammo definition -// JAY: Trying to make a more physical bullet response -#define BULLET_MASS_GRAINS_TO_LB(grains) (0.002285*(grains)/16.0f) -#define BULLET_MASS_GRAINS_TO_KG(grains) lbs2kg(BULLET_MASS_GRAINS_TO_LB(grains)) - -// exaggerate all of the forces, but use real numbers to keep them consistent -#define BULLET_IMPULSE_EXAGGERATION 3.5 -// convert a velocity in ft/sec and a mass in grains to an impulse in kg in/s -#define BULLET_IMPULSE(grains, ftpersec) ((ftpersec)*12*BULLET_MASS_GRAINS_TO_KG(grains)*BULLET_IMPULSE_EXAGGERATION) - - -CAmmoDef *GetAmmoDef() -{ - static CAmmoDef def; - static bool bInitted = false; - - if ( !bInitted ) - { - bInitted = true; - - def.AddAmmoType("AR2", DMG_BULLET, TRACER_LINE_AND_WHIZ, 0, 0, 60, BULLET_IMPULSE(200, 1225), 0 ); - def.AddAmmoType("AR2AltFire", DMG_DISSOLVE, TRACER_NONE, 0, 0, 3, 0, 0 ); - def.AddAmmoType("Pistol", DMG_BULLET, TRACER_LINE_AND_WHIZ, 0, 0, 150, BULLET_IMPULSE(200, 1225), 0 ); - def.AddAmmoType("SMG1", DMG_BULLET, TRACER_LINE_AND_WHIZ, 0, 0, 225, BULLET_IMPULSE(200, 1225), 0 ); - def.AddAmmoType("357", DMG_BULLET, TRACER_LINE_AND_WHIZ, 0, 0, 12, BULLET_IMPULSE(800, 5000), 0 ); - def.AddAmmoType("XBowBolt", DMG_BULLET, TRACER_LINE, 0, 0, 10, BULLET_IMPULSE(800, 8000), 0 ); - def.AddAmmoType("Buckshot", DMG_BULLET | DMG_BUCKSHOT, TRACER_LINE, 0, 0, 30, BULLET_IMPULSE(400, 1200), 0 ); - def.AddAmmoType("RPG_Round", DMG_BURN, TRACER_NONE, 0, 0, 3, 0, 0 ); - def.AddAmmoType("SMG1_Grenade", DMG_BURN, TRACER_NONE, 0, 0, 3, 0, 0 ); - def.AddAmmoType("Grenade", DMG_BURN, TRACER_NONE, 0, 0, 5, 0, 0 ); - def.AddAmmoType("slam", DMG_BURN, TRACER_NONE, 0, 0, 5, 0, 0 ); - } - - return &def; -} - -#ifdef CLIENT_DLL - - ConVar cl_autowepswitch( - "cl_autowepswitch", - "1", - FCVAR_ARCHIVE | FCVAR_USERINFO, - "Automatically switch to picked up weapons (if more powerful)" ); - -#else - -#ifdef DEBUG - - // Handler for the "bot" command. - void Bot_f() - { - // Look at -count. - int count = 1; - count = clamp( count, 1, 16 ); - - int iTeam = TEAM_COMBINE; - - // Look at -frozen. - bool bFrozen = false; - - // Ok, spawn all the bots. - while ( --count >= 0 ) - { - BotPutInServer( bFrozen, iTeam ); - } - } - - - ConCommand cc_Bot( "bot", Bot_f, "Add a bot.", FCVAR_CHEAT ); - -#endif - - bool CHL2MPRules::FShouldSwitchWeapon( CBasePlayer *pPlayer, CBaseCombatWeapon *pWeapon ) - { - if ( pPlayer->GetActiveWeapon() && pPlayer->IsNetClient() ) - { - // Player has an active item, so let's check cl_autowepswitch. - const char *cl_autowepswitch = engine->GetClientConVarValue( engine->IndexOfEdict( pPlayer->edict() ), "cl_autowepswitch" ); - if ( cl_autowepswitch && atoi( cl_autowepswitch ) <= 0 ) - { - return false; - } - } - - return BaseClass::FShouldSwitchWeapon( pPlayer, pWeapon ); - } - -#endif - -#ifndef CLIENT_DLL - -bool FindInList( const char **pStrings, const char *pToFind ) -{ - int i = 0; - while ( pStrings[i][0] != 0 ) - { - if ( Q_stricmp( pStrings[i], pToFind ) == 0 ) - return true; - i++; - } - - return false; -} - -void CHL2MPRules::RestartGame() -{ - // bounds check - if ( mp_timelimit.GetInt() < 0 ) - { - mp_timelimit.SetValue( 0 ); - } - m_flGameStartTime = gpGlobals->curtime; - if ( !IsFinite( m_flGameStartTime.Get() ) ) - { - Warning( "Trying to set a NaN game start time\n" ); - m_flGameStartTime.GetForModify() = 0.0f; - } - - CleanUpMap(); - - // now respawn all players - for (int i = 1; i <= gpGlobals->maxClients; i++ ) - { - CHL2MP_Player *pPlayer = (CHL2MP_Player*) UTIL_PlayerByIndex( i ); - - if ( !pPlayer ) - continue; - - if ( pPlayer->GetActiveWeapon() ) - { - pPlayer->GetActiveWeapon()->Holster(); - } - pPlayer->RemoveAllItems( true ); - respawn( pPlayer, false ); - pPlayer->Reset(); - } - - // Respawn entities (glass, doors, etc..) - - CTeam *pRebels = GetGlobalTeam( TEAM_REBELS ); - CTeam *pCombine = GetGlobalTeam( TEAM_COMBINE ); - - if ( pRebels ) - { - pRebels->SetScore( 0 ); - } - - if ( pCombine ) - { - pCombine->SetScore( 0 ); - } - - m_flIntermissionEndTime = 0; - m_flRestartGameTime = 0.0; - m_bCompleteReset = false; - - IGameEvent * event = gameeventmanager->CreateEvent( "round_start" ); - if ( event ) - { - event->SetInt("fraglimit", 0 ); - event->SetInt( "priority", 6 ); // HLTV event priority, not transmitted - - event->SetString("objective","DEATHMATCH"); - - gameeventmanager->FireEvent( event ); - } -} - -void CHL2MPRules::CleanUpMap() -{ - // Recreate all the map entities from the map data (preserving their indices), - // then remove everything else except the players. - - // Get rid of all entities except players. - CBaseEntity *pCur = gEntList.FirstEnt(); - while ( pCur ) - { - CBaseHL2MPCombatWeapon *pWeapon = dynamic_cast< CBaseHL2MPCombatWeapon* >( pCur ); - // Weapons with owners don't want to be removed.. - if ( pWeapon ) - { - if ( !pWeapon->GetPlayerOwner() ) - { - UTIL_Remove( pCur ); - } - } - // remove entities that has to be restored on roundrestart (breakables etc) - else if ( !FindInList( s_PreserveEnts, pCur->GetClassname() ) ) - { - UTIL_Remove( pCur ); - } - - pCur = gEntList.NextEnt( pCur ); - } - - // Really remove the entities so we can have access to their slots below. - gEntList.CleanupDeleteList(); - - // Cancel all queued events, in case a func_bomb_target fired some delayed outputs that - // could kill respawning CTs - g_EventQueue.Clear(); - - // Now reload the map entities. - class CHL2MPMapEntityFilter : public IMapEntityFilter - { - public: - virtual bool ShouldCreateEntity( const char *pClassname ) - { - // Don't recreate the preserved entities. - if ( !FindInList( s_PreserveEnts, pClassname ) ) - { - return true; - } - else - { - // Increment our iterator since it's not going to call CreateNextEntity for this ent. - if ( m_iIterator != g_MapEntityRefs.InvalidIndex() ) - m_iIterator = g_MapEntityRefs.Next( m_iIterator ); - - return false; - } - } - - - virtual CBaseEntity* CreateNextEntity( const char *pClassname ) - { - if ( m_iIterator == g_MapEntityRefs.InvalidIndex() ) - { - // This shouldn't be possible. When we loaded the map, it should have used - // CCSMapLoadEntityFilter, which should have built the g_MapEntityRefs list - // with the same list of entities we're referring to here. - Assert( false ); - return NULL; - } - else - { - CMapEntityRef &ref = g_MapEntityRefs[m_iIterator]; - m_iIterator = g_MapEntityRefs.Next( m_iIterator ); // Seek to the next entity. - - if ( ref.m_iEdict == -1 || engine->PEntityOfEntIndex( ref.m_iEdict ) ) - { - // Doh! The entity was delete and its slot was reused. - // Just use any old edict slot. This case sucks because we lose the baseline. - return CreateEntityByName( pClassname ); - } - else - { - // Cool, the slot where this entity was is free again (most likely, the entity was - // freed above). Now create an entity with this specific index. - return CreateEntityByName( pClassname, ref.m_iEdict ); - } - } - } - - public: - int m_iIterator; // Iterator into g_MapEntityRefs. - }; - CHL2MPMapEntityFilter filter; - filter.m_iIterator = g_MapEntityRefs.Head(); - - // DO NOT CALL SPAWN ON info_node ENTITIES! - - MapEntity_ParseAllEntities( engine->GetMapEntitiesString(), &filter, true ); -} - -void CHL2MPRules::CheckChatForReadySignal( CHL2MP_Player *pPlayer, const char *chatmsg ) -{ - if( m_bAwaitingReadyRestart && FStrEq( chatmsg, mp_ready_signal.GetString() ) ) - { - if( !pPlayer->IsReady() ) - { - pPlayer->SetReady( true ); - } - } -} - -void CHL2MPRules::CheckRestartGame( void ) -{ - // Restart the game if specified by the server - int iRestartDelay = mp_restartgame.GetInt(); - - if ( iRestartDelay > 0 ) - { - if ( iRestartDelay > 60 ) - iRestartDelay = 60; - - - // let the players know - char strRestartDelay[64]; - Q_snprintf( strRestartDelay, sizeof( strRestartDelay ), "%d", iRestartDelay ); - UTIL_ClientPrintAll( HUD_PRINTCENTER, "Game will restart in %s1 %s2", strRestartDelay, iRestartDelay == 1 ? "SECOND" : "SECONDS" ); - UTIL_ClientPrintAll( HUD_PRINTCONSOLE, "Game will restart in %s1 %s2", strRestartDelay, iRestartDelay == 1 ? "SECOND" : "SECONDS" ); - - m_flRestartGameTime = gpGlobals->curtime + iRestartDelay; - m_bCompleteReset = true; - mp_restartgame.SetValue( 0 ); - } - - if( mp_readyrestart.GetBool() ) - { - m_bAwaitingReadyRestart = true; - m_bHeardAllPlayersReady = false; - - - const char *pszReadyString = mp_ready_signal.GetString(); - - - // Don't let them put anything malicious in there - if( pszReadyString == NULL || Q_strlen(pszReadyString) > 16 ) - { - pszReadyString = "ready"; - } - - IGameEvent *event = gameeventmanager->CreateEvent( "hl2mp_ready_restart" ); - if ( event ) - gameeventmanager->FireEvent( event ); - - mp_readyrestart.SetValue( 0 ); - - // cancel any restart round in progress - m_flRestartGameTime = -1; - } -} - -void CHL2MPRules::CheckAllPlayersReady( void ) -{ - for (int i = 1; i <= gpGlobals->maxClients; i++ ) - { - CHL2MP_Player *pPlayer = (CHL2MP_Player*) UTIL_PlayerByIndex( i ); - - if ( !pPlayer ) - continue; - if ( !pPlayer->IsReady() ) - return; - } - m_bHeardAllPlayersReady = true; -} - -#endif \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// +#include "cbase.h" +#include "hl2mp_gamerules.h" +#include "viewport_panel_names.h" +#include "gameeventdefs.h" +#include +#include "ammodef.h" + +#ifdef CLIENT_DLL + #include "c_hl2mp_player.h" +#else + + #include "eventqueue.h" + #include "player.h" + #include "gamerules.h" + #include "game.h" + #include "items.h" + #include "entitylist.h" + #include "mapentities.h" + #include "in_buttons.h" + #include + #include "voice_gamemgr.h" + #include "iscorer.h" + #include "hl2mp_player.h" + #include "weapon_hl2mpbasehlmpcombatweapon.h" + #include "team.h" + #include "voice_gamemgr.h" + #include "hl2mp_gameinterface.h" + #include "hl2mp_cvars.h" + +#ifdef DEBUG + #include "hl2mp_bot_temp.h" +#endif + +extern void respawn(CBaseEntity *pEdict, bool fCopyCorpse); + + +ConVar sv_hl2mp_weapon_respawn_time( "sv_hl2mp_weapon_respawn_time", "20", FCVAR_GAMEDLL | FCVAR_NOTIFY ); +ConVar sv_hl2mp_item_respawn_time( "sv_hl2mp_item_respawn_time", "30", FCVAR_GAMEDLL | FCVAR_NOTIFY ); +ConVar mp_restartgame( "mp_restartgame", "0", 0, "If non-zero, game will restart in the specified number of seconds" ); +ConVar sv_report_client_settings("sv_report_client_settings", "0", FCVAR_GAMEDLL | FCVAR_NOTIFY ); + +extern ConVar mp_chattime; + +extern CBaseEntity *g_pLastCombineSpawn; +extern CBaseEntity *g_pLastRebelSpawn; + +#define WEAPON_MAX_DISTANCE_FROM_SPAWN 64 + +#endif + + +REGISTER_GAMERULES_CLASS( CHL2MPRules ); + +BEGIN_NETWORK_TABLE_NOBASE( CHL2MPRules, DT_HL2MPRules ) + + #ifdef CLIENT_DLL + RecvPropBool( RECVINFO( m_bTeamPlayEnabled ) ), + #else + SendPropBool( SENDINFO( m_bTeamPlayEnabled ) ), + #endif + +END_NETWORK_TABLE() + + +LINK_ENTITY_TO_CLASS( hl2mp_gamerules, CHL2MPGameRulesProxy ); +IMPLEMENT_NETWORKCLASS_ALIASED( HL2MPGameRulesProxy, DT_HL2MPGameRulesProxy ) + +static HL2MPViewVectors g_HL2MPViewVectors( + Vector( 0, 0, 64 ), //m_vView + + Vector(-16, -16, 0 ), //m_vHullMin + Vector( 16, 16, 72 ), //m_vHullMax + + Vector(-16, -16, 0 ), //m_vDuckHullMin + Vector( 16, 16, 36 ), //m_vDuckHullMax + Vector( 0, 0, 28 ), //m_vDuckView + + Vector(-10, -10, -10 ), //m_vObsHullMin + Vector( 10, 10, 10 ), //m_vObsHullMax + + Vector( 0, 0, 14 ), //m_vDeadViewHeight + + Vector(-16, -16, 0 ), //m_vCrouchTraceMin + Vector( 16, 16, 60 ) //m_vCrouchTraceMax +); + +static const char *s_PreserveEnts[] = +{ + "ai_network", + "ai_hint", + "hl2mp_gamerules", + "team_manager", + "player_manager", + "env_soundscape", + "env_soundscape_proxy", + "env_soundscape_triggerable", + "env_sun", + "env_wind", + "env_fog_controller", + "func_brush", + "func_wall", + "func_buyzone", + "func_illusionary", + "infodecal", + "info_projecteddecal", + "info_node", + "info_target", + "info_node_hint", + "info_player_deathmatch", + "info_player_combine", + "info_player_rebel", + "info_map_parameters", + "keyframe_rope", + "move_rope", + "info_ladder", + "player", + "point_viewcontrol", + "scene_manager", + "shadow_control", + "sky_camera", + "soundent", + "trigger_soundscape", + "viewmodel", + "predicted_viewmodel", + "worldspawn", + "point_devshot_camera", + "", // END Marker +}; + + + +#ifdef CLIENT_DLL + void RecvProxy_HL2MPRules( const RecvProp *pProp, void **pOut, void *pData, int objectID ) + { + CHL2MPRules *pRules = HL2MPRules(); + Assert( pRules ); + *pOut = pRules; + } + + BEGIN_RECV_TABLE( CHL2MPGameRulesProxy, DT_HL2MPGameRulesProxy ) + RecvPropDataTable( "hl2mp_gamerules_data", 0, 0, &REFERENCE_RECV_TABLE( DT_HL2MPRules ), RecvProxy_HL2MPRules ) + END_RECV_TABLE() +#else + void* SendProxy_HL2MPRules( const SendProp *pProp, const void *pStructBase, const void *pData, CSendProxyRecipients *pRecipients, int objectID ) + { + CHL2MPRules *pRules = HL2MPRules(); + Assert( pRules ); + return pRules; + } + + BEGIN_SEND_TABLE( CHL2MPGameRulesProxy, DT_HL2MPGameRulesProxy ) + SendPropDataTable( "hl2mp_gamerules_data", 0, &REFERENCE_SEND_TABLE( DT_HL2MPRules ), SendProxy_HL2MPRules ) + END_SEND_TABLE() +#endif + +#ifndef CLIENT_DLL + + class CVoiceGameMgrHelper : public IVoiceGameMgrHelper + { + public: + virtual bool CanPlayerHearPlayer( CBasePlayer *pListener, CBasePlayer *pTalker ) + { + return ( pListener->GetTeamNumber() == pTalker->GetTeamNumber() ); + } + }; + CVoiceGameMgrHelper g_VoiceGameMgrHelper; + IVoiceGameMgrHelper *g_pVoiceGameMgrHelper = &g_VoiceGameMgrHelper; + +#endif + +// NOTE: the indices here must match TEAM_TERRORIST, TEAM_CT, TEAM_SPECTATOR, etc. +char *sTeamNames[] = +{ + "Unassigned", + "Spectator", + "Combine", + "Rebels", +}; + +CHL2MPRules::CHL2MPRules() +{ +#ifndef CLIENT_DLL + // Create the team managers + for ( int i = 0; i < static_cast(ARRAYSIZE( sTeamNames )); i++ ) + { + CTeam *pTeam = static_cast(CreateEntityByName( "team_manager" )); + pTeam->Init( sTeamNames[i], i ); + + g_Teams.AddToTail( pTeam ); + } + + m_bTeamPlayEnabled = teamplay.GetBool(); + m_flIntermissionEndTime = 0.0f; + m_flGameStartTime = 0; + + m_hRespawnableItemsAndWeapons.RemoveAll(); + m_tmNextPeriodicThink = 0; + m_flRestartGameTime = 0; + m_bCompleteReset = false; + m_bHeardAllPlayersReady = false; + m_bAwaitingReadyRestart = false; + +#endif +} + +const CViewVectors* CHL2MPRules::GetViewVectors()const +{ + return &g_HL2MPViewVectors; +} + +const HL2MPViewVectors* CHL2MPRules::GetHL2MPViewVectors()const +{ + return &g_HL2MPViewVectors; +} + +CHL2MPRules::~CHL2MPRules( void ) +{ +#ifndef CLIENT_DLL + // Note, don't delete each team since they are in the gEntList and will + // automatically be deleted from there, instead. + g_Teams.Purge(); +#endif +} + +void CHL2MPRules::CreateStandardEntities( void ) +{ + +#ifndef CLIENT_DLL + // Create the entity that will send our data to the client. + + BaseClass::CreateStandardEntities(); + + g_pLastCombineSpawn = NULL; + g_pLastRebelSpawn = NULL; + +#ifdef _DEBUG + CBaseEntity *pEnt = +#endif + CBaseEntity::Create( "hl2mp_gamerules", vec3_origin, vec3_angle ); + Assert( pEnt ); +#endif +} + +//========================================================= +// FlWeaponRespawnTime - what is the time in the future +// at which this weapon may spawn? +//========================================================= +float CHL2MPRules::FlWeaponRespawnTime( CBaseCombatWeapon *pWeapon ) +{ +#ifndef CLIENT_DLL + if ( weaponstay.GetInt() > 0 ) + { + // make sure it's only certain weapons + if ( !(pWeapon->GetWeaponFlags() & ITEM_FLAG_LIMITINWORLD) ) + { + return 0; // weapon respawns almost instantly + } + } + + return sv_hl2mp_weapon_respawn_time.GetFloat(); +#endif + + return 0; // weapon respawns almost instantly +} + + +bool CHL2MPRules::IsIntermission( void ) +{ +#ifndef CLIENT_DLL + return m_flIntermissionEndTime > gpGlobals->curtime; +#endif + + return false; +} + +void CHL2MPRules::PlayerKilled( CBasePlayer *pVictim, const CTakeDamageInfo &info ) +{ +#ifndef CLIENT_DLL + if ( IsIntermission() ) + return; + BaseClass::PlayerKilled( pVictim, info ); +#endif +} + + +void CHL2MPRules::Think( void ) +{ + +#ifndef CLIENT_DLL + + CGameRules::Think(); + + if ( g_fGameOver ) // someone else quit the game already + { + // check to see if we should change levels now + if ( m_flIntermissionEndTime < gpGlobals->curtime ) + { + ChangeLevel(); // intermission is over + } + + return; + } + +// float flTimeLimit = mp_timelimit.GetFloat() * 60; + float flFragLimit = fraglimit.GetFloat(); + + if ( GetMapRemainingTime() < 0 ) + { + GoToIntermission(); + return; + } + + if ( flFragLimit ) + { + if( IsTeamplay() == true ) + { + CTeam *pCombine = g_Teams[TEAM_COMBINE]; + CTeam *pRebels = g_Teams[TEAM_REBELS]; + + if ( pCombine->GetScore() >= flFragLimit || pRebels->GetScore() >= flFragLimit ) + { + GoToIntermission(); + return; + } + } + else + { + // check if any player is over the frag limit + for ( int i = 1; i <= gpGlobals->maxClients; i++ ) + { + CBasePlayer *pPlayer = UTIL_PlayerByIndex( i ); + + if ( pPlayer && pPlayer->FragCount() >= flFragLimit ) + { + GoToIntermission(); + return; + } + } + } + } + + if ( gpGlobals->curtime > m_tmNextPeriodicThink ) + { + CheckAllPlayersReady(); + CheckRestartGame(); + m_tmNextPeriodicThink = gpGlobals->curtime + 1.0; + } + + if ( m_flRestartGameTime > 0.0f && m_flRestartGameTime <= gpGlobals->curtime ) + { + RestartGame(); + } + + if( m_bAwaitingReadyRestart && m_bHeardAllPlayersReady ) + { + UTIL_ClientPrintAll( HUD_PRINTCENTER, "All players ready. Game will restart in 5 seconds" ); + UTIL_ClientPrintAll( HUD_PRINTCONSOLE, "All players ready. Game will restart in 5 seconds" ); + + m_flRestartGameTime = gpGlobals->curtime + 5; + m_bAwaitingReadyRestart = false; + } + + ManageObjectRelocation(); + +#endif +} + +void CHL2MPRules::GoToIntermission( void ) +{ +#ifndef CLIENT_DLL + if ( g_fGameOver ) + return; + + g_fGameOver = true; + + m_flIntermissionEndTime = gpGlobals->curtime + mp_chattime.GetInt(); + + for ( int i = 0; i < MAX_PLAYERS; i++ ) + { + CBasePlayer *pPlayer = UTIL_PlayerByIndex( i ); + + if ( !pPlayer ) + continue; + + pPlayer->ShowViewPortPanel( PANEL_SCOREBOARD ); + pPlayer->AddFlag( FL_FROZEN ); + } +#endif + +} + +bool CHL2MPRules::CheckGameOver() +{ +#ifndef CLIENT_DLL + if ( g_fGameOver ) // someone else quit the game already + { + // check to see if we should change levels now + if ( m_flIntermissionEndTime < gpGlobals->curtime ) + { + ChangeLevel(); // intermission is over + } + + return true; + } +#endif + + return false; +} + +// when we are within this close to running out of entities, items +// marked with the ITEM_FLAG_LIMITINWORLD will delay their respawn +#define ENTITY_INTOLERANCE 100 + +//========================================================= +// FlWeaponRespawnTime - Returns 0 if the weapon can respawn +// now, otherwise it returns the time at which it can try +// to spawn again. +//========================================================= +float CHL2MPRules::FlWeaponTryRespawn( CBaseCombatWeapon *pWeapon ) +{ +#ifndef CLIENT_DLL + if ( pWeapon && (pWeapon->GetWeaponFlags() & ITEM_FLAG_LIMITINWORLD) ) + { + if ( gEntList.NumberOfEntities() < (gpGlobals->maxEntities - ENTITY_INTOLERANCE) ) + return 0; + + // we're past the entity tolerance level, so delay the respawn + return FlWeaponRespawnTime( pWeapon ); + } +#endif + return 0; +} + +//========================================================= +// VecWeaponRespawnSpot - where should this weapon spawn? +// Some game variations may choose to randomize spawn locations +//========================================================= +Vector CHL2MPRules::VecWeaponRespawnSpot( CBaseCombatWeapon *pWeapon ) +{ +#ifndef CLIENT_DLL + CWeaponHL2MPBase *pHL2Weapon = dynamic_cast< CWeaponHL2MPBase*>( pWeapon ); + + if ( pHL2Weapon ) + { + return pHL2Weapon->GetOriginalSpawnOrigin(); + } +#endif + + return pWeapon->GetAbsOrigin(); +} + +#ifndef CLIENT_DLL + +CItem* IsManagedObjectAnItem( CBaseEntity *pObject ) +{ + return dynamic_cast< CItem*>( pObject ); +} + +CWeaponHL2MPBase* IsManagedObjectAWeapon( CBaseEntity *pObject ) +{ + return dynamic_cast< CWeaponHL2MPBase*>( pObject ); +} + +bool GetObjectsOriginalParameters( CBaseEntity *pObject, Vector &vOriginalOrigin, QAngle &vOriginalAngles ) +{ + if ( CItem *pItem = IsManagedObjectAnItem( pObject ) ) + { + if ( pItem->m_flNextResetCheckTime > gpGlobals->curtime ) + return false; + + vOriginalOrigin = pItem->GetOriginalSpawnOrigin(); + vOriginalAngles = pItem->GetOriginalSpawnAngles(); + + pItem->m_flNextResetCheckTime = gpGlobals->curtime + sv_hl2mp_item_respawn_time.GetFloat(); + return true; + } + else if ( CWeaponHL2MPBase *pWeapon = IsManagedObjectAWeapon( pObject )) + { + if ( pWeapon->m_flNextResetCheckTime > gpGlobals->curtime ) + return false; + + vOriginalOrigin = pWeapon->GetOriginalSpawnOrigin(); + vOriginalAngles = pWeapon->GetOriginalSpawnAngles(); + + pWeapon->m_flNextResetCheckTime = gpGlobals->curtime + sv_hl2mp_weapon_respawn_time.GetFloat(); + return true; + } + + return false; +} + +void CHL2MPRules::ManageObjectRelocation( void ) +{ + int iTotal = m_hRespawnableItemsAndWeapons.Count(); + + if ( iTotal > 0 ) + { + for ( int i = 0; i < iTotal; i++ ) + { + CBaseEntity *pObject = m_hRespawnableItemsAndWeapons[i].Get(); + + if ( pObject ) + { + Vector vSpawOrigin; + QAngle vSpawnAngles; + + if ( GetObjectsOriginalParameters( pObject, vSpawOrigin, vSpawnAngles ) == true ) + { + float flDistanceFromSpawn = (pObject->GetAbsOrigin() - vSpawOrigin ).Length(); + + if ( flDistanceFromSpawn > WEAPON_MAX_DISTANCE_FROM_SPAWN ) + { + bool shouldReset = false; + IPhysicsObject *pPhysics = pObject->VPhysicsGetObject(); + + if ( pPhysics ) + { + shouldReset = pPhysics->IsAsleep(); + } + else + { + shouldReset = (pObject->GetFlags() & FL_ONGROUND) ? true : false; + } + + if ( shouldReset ) + { + pObject->Teleport( &vSpawOrigin, &vSpawnAngles, NULL ); + pObject->EmitSound( "AlyxEmp.Charge" ); + + IPhysicsObject *pPhys = pObject->VPhysicsGetObject(); + + if ( pPhys ) + { + pPhys->Wake(); + } + } + } + } + } + } + } +} + +//========================================================= +//AddLevelDesignerPlacedWeapon +//========================================================= +void CHL2MPRules::AddLevelDesignerPlacedObject( CBaseEntity *pEntity ) +{ + if ( m_hRespawnableItemsAndWeapons.Find( pEntity ) == -1 ) + { + m_hRespawnableItemsAndWeapons.AddToTail( pEntity ); + } +} + +//========================================================= +//RemoveLevelDesignerPlacedWeapon +//========================================================= +void CHL2MPRules::RemoveLevelDesignerPlacedObject( CBaseEntity *pEntity ) +{ + if ( m_hRespawnableItemsAndWeapons.Find( pEntity ) != -1 ) + { + m_hRespawnableItemsAndWeapons.FindAndRemove( pEntity ); + } +} + +//========================================================= +// Where should this item respawn? +// Some game variations may choose to randomize spawn locations +//========================================================= +Vector CHL2MPRules::VecItemRespawnSpot( CItem *pItem ) +{ + return pItem->GetOriginalSpawnOrigin(); +} + +//========================================================= +// What angles should this item use to respawn? +//========================================================= +QAngle CHL2MPRules::VecItemRespawnAngles( CItem *pItem ) +{ + return pItem->GetOriginalSpawnAngles(); +} + +//========================================================= +// At what time in the future may this Item respawn? +//========================================================= +float CHL2MPRules::FlItemRespawnTime( CItem *pItem ) +{ + return sv_hl2mp_item_respawn_time.GetFloat(); +} + + +//========================================================= +// CanHaveWeapon - returns false if the player is not allowed +// to pick up this weapon +//========================================================= +bool CHL2MPRules::CanHavePlayerItem( CBasePlayer *pPlayer, CBaseCombatWeapon *pItem ) +{ + if ( weaponstay.GetInt() > 0 ) + { + if ( pPlayer->Weapon_OwnsThisType( pItem->GetClassname(), pItem->GetSubType() ) ) + return false; + } + + return BaseClass::CanHavePlayerItem( pPlayer, pItem ); +} + +#endif + +//========================================================= +// WeaponShouldRespawn - any conditions inhibiting the +// respawning of this weapon? +//========================================================= +int CHL2MPRules::WeaponShouldRespawn( CBaseCombatWeapon *pWeapon ) +{ +#ifndef CLIENT_DLL + if ( pWeapon->HasSpawnFlags( SF_NORESPAWN ) ) + { + return GR_WEAPON_RESPAWN_NO; + } +#endif + + return GR_WEAPON_RESPAWN_YES; +} + +//----------------------------------------------------------------------------- +// Purpose: Player has just left the game +//----------------------------------------------------------------------------- +void CHL2MPRules::ClientDisconnected( edict_t *pClient ) +{ +#ifndef CLIENT_DLL + // Msg( "CLIENT DISCONNECTED, REMOVING FROM TEAM.\n" ); + + CBasePlayer *pPlayer = (CBasePlayer *)CBaseEntity::Instance( pClient ); + if ( pPlayer ) + { + // Remove the player from his team + if ( pPlayer->GetTeam() ) + { + pPlayer->GetTeam()->RemovePlayer( pPlayer ); + } + } + + BaseClass::ClientDisconnected( pClient ); + +#endif +} + + +//========================================================= +// Deathnotice. +//========================================================= +void CHL2MPRules::DeathNotice( CBasePlayer *pVictim, const CTakeDamageInfo &info ) +{ +#ifndef CLIENT_DLL + // Work out what killed the player, and send a message to all clients about it + const char *killer_weapon_name = "world"; // by default, the player is killed by the world + int killer_ID = 0; + + // Find the killer & the scorer + CBaseEntity *pInflictor = info.GetInflictor(); + CBaseEntity *pKiller = info.GetAttacker(); + CBasePlayer *pScorer = GetDeathScorer( pKiller, pInflictor ); + + // Custom kill type? + if ( info.GetCustomKill() ) + { + killer_weapon_name = GetCustomKillString( info ); + if ( pScorer ) + { + killer_ID = pScorer->GetUserID(); + } + } + else + { + // Is the killer a client? + if ( pScorer ) + { + killer_ID = pScorer->GetUserID(); + + if ( pInflictor ) + { + if ( pInflictor == pScorer ) + { + // If the inflictor is the killer, then it must be their current weapon doing the damage + if ( pScorer->GetActiveWeapon() ) + { + killer_weapon_name = pScorer->GetActiveWeapon()->GetClassname(); + } + } + else + { + killer_weapon_name = pInflictor->GetClassname(); // it's just that easy + } + } + } + else + { + killer_weapon_name = pInflictor->GetClassname(); + } + + // strip the NPC_* or weapon_* from the inflictor's classname + if ( strncmp( killer_weapon_name, "weapon_", 7 ) == 0 ) + { + killer_weapon_name += 7; + } + else if ( strncmp( killer_weapon_name, "npc_", 4 ) == 0 ) + { + killer_weapon_name += 4; + } + else if ( strncmp( killer_weapon_name, "func_", 5 ) == 0 ) + { + killer_weapon_name += 5; + } + else if ( strstr( killer_weapon_name, "physics" ) ) + { + killer_weapon_name = "physics"; + } + + if ( strcmp( killer_weapon_name, "prop_combine_ball" ) == 0 ) + { + killer_weapon_name = "combine_ball"; + } + else if ( strcmp( killer_weapon_name, "grenade_ar2" ) == 0 ) + { + killer_weapon_name = "smg1_grenade"; + } + else if ( strcmp( killer_weapon_name, "satchel" ) == 0 || strcmp( killer_weapon_name, "tripmine" ) == 0) + { + killer_weapon_name = "slam"; + } + + + } + + IGameEvent *event = gameeventmanager->CreateEvent( "player_death" ); + if( event ) + { + event->SetInt("userid", pVictim->GetUserID() ); + event->SetInt("attacker", killer_ID ); + event->SetString("weapon", killer_weapon_name ); + event->SetInt( "priority", 7 ); + gameeventmanager->FireEvent( event ); + } +#endif + +} + +void CHL2MPRules::ClientSettingsChanged( CBasePlayer *pPlayer ) +{ +#ifndef CLIENT_DLL + + CHL2MP_Player *pHL2Player = ToHL2MPPlayer( pPlayer ); + + if ( pHL2Player == NULL ) + return; + + const char *pCurrentModel = modelinfo->GetModelName( pPlayer->GetModel() ); + const char *szModelName = engine->GetClientConVarValue( engine->IndexOfEdict( pPlayer->edict() ), "cl_playermodel" ); + + //If we're different. + if ( stricmp( szModelName, pCurrentModel ) ) + { + //Too soon, set the cvar back to what it was. + //Note: this will make this function be called again + //but since our models will match it'll just skip this whole dealio. + if ( pHL2Player->GetNextModelChangeTime() >= gpGlobals->curtime ) + { + char szReturnString[512]; + + Q_snprintf( szReturnString, sizeof (szReturnString ), "cl_playermodel %s\n", pCurrentModel ); + engine->ClientCommand ( pHL2Player->edict(), szReturnString ); + + Q_snprintf( szReturnString, sizeof( szReturnString ), "Please wait %d more seconds before trying to switch.\n", (int)(pHL2Player->GetNextModelChangeTime() - gpGlobals->curtime) ); + ClientPrint( pHL2Player, HUD_PRINTTALK, szReturnString ); + return; + } + + if ( HL2MPRules()->IsTeamplay() == false ) + { + pHL2Player->SetPlayerModel(); + + const char *pszCurrentModelName = modelinfo->GetModelName( pHL2Player->GetModel() ); + + char szReturnString[128]; + Q_snprintf( szReturnString, sizeof( szReturnString ), "Your player model is: %s\n", pszCurrentModelName ); + + ClientPrint( pHL2Player, HUD_PRINTTALK, szReturnString ); + } + else + { + if ( Q_stristr( szModelName, "models/human") ) + { + pHL2Player->ChangeTeam( TEAM_REBELS ); + } + else + { + pHL2Player->ChangeTeam( TEAM_COMBINE ); + } + } + } + if ( sv_report_client_settings.GetInt() == 1 ) + { + UTIL_LogPrintf( "\"%s\" cl_cmdrate = \"%s\"\n", pHL2Player->GetPlayerName(), engine->GetClientConVarValue( pHL2Player->entindex(), "cl_cmdrate" )); + } + + BaseClass::ClientSettingsChanged( pPlayer ); +#endif + +} + +int CHL2MPRules::PlayerRelationship( CBaseEntity *pPlayer, CBaseEntity *pTarget ) +{ +#ifndef CLIENT_DLL + // half life multiplay has a simple concept of Player Relationships. + // you are either on another player's team, or you are not. + if ( !pPlayer || !pTarget || !pTarget->IsPlayer() || IsTeamplay() == false ) + return GR_NOTTEAMMATE; + + if ( (*GetTeamID(pPlayer) != '\0') && (*GetTeamID(pTarget) != '\0') && !stricmp( GetTeamID(pPlayer), GetTeamID(pTarget) ) ) + { + return GR_TEAMMATE; + } +#endif + + return GR_NOTTEAMMATE; +} + +const char *CHL2MPRules::GetGameDescription( void ) +{ + if ( IsTeamplay() ) + return "Team Deathmatch"; + + return "Deathmatch"; +} + + +float CHL2MPRules::GetMapRemainingTime() +{ + // if timelimit is disabled, return 0 + if ( mp_timelimit.GetInt() <= 0 ) + return 0; + + // timelimit is in minutes + + float timeleft = (m_flGameStartTime + mp_timelimit.GetInt() * 60.0f ) - gpGlobals->curtime; + + return timeleft; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CHL2MPRules::Precache( void ) +{ + CBaseEntity::PrecacheScriptSound( "AlyxEmp.Charge" ); +} + +bool CHL2MPRules::ShouldCollide( int collisionGroup0, int collisionGroup1 ) +{ + if ( collisionGroup0 > collisionGroup1 ) + { + // swap so that lowest is always first + swap(collisionGroup0,collisionGroup1); + } + + if ( (collisionGroup0 == COLLISION_GROUP_PLAYER || collisionGroup0 == COLLISION_GROUP_PLAYER_MOVEMENT) && + collisionGroup1 == COLLISION_GROUP_WEAPON ) + { + return false; + } + + return BaseClass::ShouldCollide( collisionGroup0, collisionGroup1 ); + +} + +bool CHL2MPRules::ClientCommand(const char *pcmd, CBaseEntity *pEdict ) +{ + +#ifndef CLIENT_DLL + if( BaseClass::ClientCommand(pcmd, pEdict) ) + return true; + + + CHL2MP_Player *pPlayer = (CHL2MP_Player *) pEdict; + + if ( pPlayer->ClientCommand( pcmd ) ) + return true; +#endif + + return false; +} + +// shared ammo definition +// JAY: Trying to make a more physical bullet response +#define BULLET_MASS_GRAINS_TO_LB(grains) (0.002285*(grains)/16.0f) +#define BULLET_MASS_GRAINS_TO_KG(grains) lbs2kg(BULLET_MASS_GRAINS_TO_LB(grains)) + +// exaggerate all of the forces, but use real numbers to keep them consistent +#define BULLET_IMPULSE_EXAGGERATION 3.5 +// convert a velocity in ft/sec and a mass in grains to an impulse in kg in/s +#define BULLET_IMPULSE(grains, ftpersec) ((ftpersec)*12*BULLET_MASS_GRAINS_TO_KG(grains)*BULLET_IMPULSE_EXAGGERATION) + + +CAmmoDef *GetAmmoDef() +{ + static CAmmoDef def; + static bool bInitted = false; + + if ( !bInitted ) + { + bInitted = true; + + def.AddAmmoType("AR2", DMG_BULLET, TRACER_LINE_AND_WHIZ, 0, 0, 60, BULLET_IMPULSE(200, 1225), 0 ); + def.AddAmmoType("AR2AltFire", DMG_DISSOLVE, TRACER_NONE, 0, 0, 3, 0, 0 ); + def.AddAmmoType("Pistol", DMG_BULLET, TRACER_LINE_AND_WHIZ, 0, 0, 150, BULLET_IMPULSE(200, 1225), 0 ); + def.AddAmmoType("SMG1", DMG_BULLET, TRACER_LINE_AND_WHIZ, 0, 0, 225, BULLET_IMPULSE(200, 1225), 0 ); + def.AddAmmoType("357", DMG_BULLET, TRACER_LINE_AND_WHIZ, 0, 0, 12, BULLET_IMPULSE(800, 5000), 0 ); + def.AddAmmoType("XBowBolt", DMG_BULLET, TRACER_LINE, 0, 0, 10, BULLET_IMPULSE(800, 8000), 0 ); + def.AddAmmoType("Buckshot", DMG_BULLET | DMG_BUCKSHOT, TRACER_LINE, 0, 0, 30, BULLET_IMPULSE(400, 1200), 0 ); + def.AddAmmoType("RPG_Round", DMG_BURN, TRACER_NONE, 0, 0, 3, 0, 0 ); + def.AddAmmoType("SMG1_Grenade", DMG_BURN, TRACER_NONE, 0, 0, 3, 0, 0 ); + def.AddAmmoType("Grenade", DMG_BURN, TRACER_NONE, 0, 0, 5, 0, 0 ); + def.AddAmmoType("slam", DMG_BURN, TRACER_NONE, 0, 0, 5, 0, 0 ); + } + + return &def; +} + +#ifdef CLIENT_DLL + + ConVar cl_autowepswitch( + "cl_autowepswitch", + "1", + FCVAR_ARCHIVE | FCVAR_USERINFO, + "Automatically switch to picked up weapons (if more powerful)" ); + +#else + +#ifdef DEBUG + + // Handler for the "bot" command. + void Bot_f() + { + // Look at -count. + int count = 1; + count = clamp( count, 1, 16 ); + + int iTeam = TEAM_COMBINE; + + // Look at -frozen. + bool bFrozen = false; + + // Ok, spawn all the bots. + while ( --count >= 0 ) + { + BotPutInServer( bFrozen, iTeam ); + } + } + + + ConCommand cc_Bot( "bot", Bot_f, "Add a bot.", FCVAR_CHEAT ); + +#endif + + bool CHL2MPRules::FShouldSwitchWeapon( CBasePlayer *pPlayer, CBaseCombatWeapon *pWeapon ) + { + if ( pPlayer->GetActiveWeapon() && pPlayer->IsNetClient() ) + { + // Player has an active item, so let's check cl_autowepswitch. + const char *cl_autowepswitch = engine->GetClientConVarValue( engine->IndexOfEdict( pPlayer->edict() ), "cl_autowepswitch" ); + if ( cl_autowepswitch && atoi( cl_autowepswitch ) <= 0 ) + { + return false; + } + } + + return BaseClass::FShouldSwitchWeapon( pPlayer, pWeapon ); + } + +#endif + +#ifndef CLIENT_DLL + +bool FindInList( const char **pStrings, const char *pToFind ) +{ + int i = 0; + while ( pStrings[i][0] != 0 ) + { + if ( Q_stricmp( pStrings[i], pToFind ) == 0 ) + return true; + i++; + } + + return false; +} + +void CHL2MPRules::RestartGame() +{ + // bounds check + if ( mp_timelimit.GetInt() < 0 ) + { + mp_timelimit.SetValue( 0 ); + } + m_flGameStartTime = gpGlobals->curtime; + if ( !IsFinite( m_flGameStartTime.Get() ) ) + { + Warning( "Trying to set a NaN game start time\n" ); + m_flGameStartTime.GetForModify() = 0.0f; + } + + CleanUpMap(); + + // now respawn all players + for (int i = 1; i <= gpGlobals->maxClients; i++ ) + { + CHL2MP_Player *pPlayer = (CHL2MP_Player*) UTIL_PlayerByIndex( i ); + + if ( !pPlayer ) + continue; + + if ( pPlayer->GetActiveWeapon() ) + { + pPlayer->GetActiveWeapon()->Holster(); + } + pPlayer->RemoveAllItems( true ); + respawn( pPlayer, false ); + pPlayer->Reset(); + } + + // Respawn entities (glass, doors, etc..) + + CTeam *pRebels = GetGlobalTeam( TEAM_REBELS ); + CTeam *pCombine = GetGlobalTeam( TEAM_COMBINE ); + + if ( pRebels ) + { + pRebels->SetScore( 0 ); + } + + if ( pCombine ) + { + pCombine->SetScore( 0 ); + } + + m_flIntermissionEndTime = 0; + m_flRestartGameTime = 0.0; + m_bCompleteReset = false; + + IGameEvent * event = gameeventmanager->CreateEvent( "round_start" ); + if ( event ) + { + event->SetInt("fraglimit", 0 ); + event->SetInt( "priority", 6 ); // HLTV event priority, not transmitted + + event->SetString("objective","DEATHMATCH"); + + gameeventmanager->FireEvent( event ); + } +} + +void CHL2MPRules::CleanUpMap() +{ + // Recreate all the map entities from the map data (preserving their indices), + // then remove everything else except the players. + + // Get rid of all entities except players. + CBaseEntity *pCur = gEntList.FirstEnt(); + while ( pCur ) + { + CBaseHL2MPCombatWeapon *pWeapon = dynamic_cast< CBaseHL2MPCombatWeapon* >( pCur ); + // Weapons with owners don't want to be removed.. + if ( pWeapon ) + { + if ( !pWeapon->GetPlayerOwner() ) + { + UTIL_Remove( pCur ); + } + } + // remove entities that has to be restored on roundrestart (breakables etc) + else if ( !FindInList( s_PreserveEnts, pCur->GetClassname() ) ) + { + UTIL_Remove( pCur ); + } + + pCur = gEntList.NextEnt( pCur ); + } + + // Really remove the entities so we can have access to their slots below. + gEntList.CleanupDeleteList(); + + // Cancel all queued events, in case a func_bomb_target fired some delayed outputs that + // could kill respawning CTs + g_EventQueue.Clear(); + + // Now reload the map entities. + class CHL2MPMapEntityFilter : public IMapEntityFilter + { + public: + virtual bool ShouldCreateEntity( const char *pClassname ) + { + // Don't recreate the preserved entities. + if ( !FindInList( s_PreserveEnts, pClassname ) ) + { + return true; + } + else + { + // Increment our iterator since it's not going to call CreateNextEntity for this ent. + if ( m_iIterator != g_MapEntityRefs.InvalidIndex() ) + m_iIterator = g_MapEntityRefs.Next( m_iIterator ); + + return false; + } + } + + + virtual CBaseEntity* CreateNextEntity( const char *pClassname ) + { + if ( m_iIterator == g_MapEntityRefs.InvalidIndex() ) + { + // This shouldn't be possible. When we loaded the map, it should have used + // CCSMapLoadEntityFilter, which should have built the g_MapEntityRefs list + // with the same list of entities we're referring to here. + Assert( false ); + return NULL; + } + else + { + CMapEntityRef &ref = g_MapEntityRefs[m_iIterator]; + m_iIterator = g_MapEntityRefs.Next( m_iIterator ); // Seek to the next entity. + + if ( ref.m_iEdict == -1 || engine->PEntityOfEntIndex( ref.m_iEdict ) ) + { + // Doh! The entity was delete and its slot was reused. + // Just use any old edict slot. This case sucks because we lose the baseline. + return CreateEntityByName( pClassname ); + } + else + { + // Cool, the slot where this entity was is free again (most likely, the entity was + // freed above). Now create an entity with this specific index. + return CreateEntityByName( pClassname, ref.m_iEdict ); + } + } + } + + public: + int m_iIterator; // Iterator into g_MapEntityRefs. + }; + CHL2MPMapEntityFilter filter; + filter.m_iIterator = g_MapEntityRefs.Head(); + + // DO NOT CALL SPAWN ON info_node ENTITIES! + + MapEntity_ParseAllEntities( engine->GetMapEntitiesString(), &filter, true ); +} + +void CHL2MPRules::CheckChatForReadySignal( CHL2MP_Player *pPlayer, const char *chatmsg ) +{ + if( m_bAwaitingReadyRestart && FStrEq( chatmsg, mp_ready_signal.GetString() ) ) + { + if( !pPlayer->IsReady() ) + { + pPlayer->SetReady( true ); + } + } +} + +void CHL2MPRules::CheckRestartGame( void ) +{ + // Restart the game if specified by the server + int iRestartDelay = mp_restartgame.GetInt(); + + if ( iRestartDelay > 0 ) + { + if ( iRestartDelay > 60 ) + iRestartDelay = 60; + + + // let the players know + char strRestartDelay[64]; + Q_snprintf( strRestartDelay, sizeof( strRestartDelay ), "%d", iRestartDelay ); + UTIL_ClientPrintAll( HUD_PRINTCENTER, "Game will restart in %s1 %s2", strRestartDelay, iRestartDelay == 1 ? "SECOND" : "SECONDS" ); + UTIL_ClientPrintAll( HUD_PRINTCONSOLE, "Game will restart in %s1 %s2", strRestartDelay, iRestartDelay == 1 ? "SECOND" : "SECONDS" ); + + m_flRestartGameTime = gpGlobals->curtime + iRestartDelay; + m_bCompleteReset = true; + mp_restartgame.SetValue( 0 ); + } + + if( mp_readyrestart.GetBool() ) + { + m_bAwaitingReadyRestart = true; + m_bHeardAllPlayersReady = false; + + + const char *pszReadyString = mp_ready_signal.GetString(); + + + // Don't let them put anything malicious in there + if( pszReadyString == NULL || Q_strlen(pszReadyString) > 16 ) + { + pszReadyString = "ready"; + } + + IGameEvent *event = gameeventmanager->CreateEvent( "hl2mp_ready_restart" ); + if ( event ) + gameeventmanager->FireEvent( event ); + + mp_readyrestart.SetValue( 0 ); + + // cancel any restart round in progress + m_flRestartGameTime = -1; + } +} + +void CHL2MPRules::CheckAllPlayersReady( void ) +{ + for (int i = 1; i <= gpGlobals->maxClients; i++ ) + { + CHL2MP_Player *pPlayer = (CHL2MP_Player*) UTIL_PlayerByIndex( i ); + + if ( !pPlayer ) + continue; + if ( !pPlayer->IsReady() ) + return; + } + m_bHeardAllPlayersReady = true; +} + +#endif diff --git a/game_shared/hl2mp/hl2mp_gamerules.h b/game_shared/hl2mp/hl2mp_gamerules.h index 1f9f28ae..bd28764b 100644 --- a/game_shared/hl2mp/hl2mp_gamerules.h +++ b/game_shared/hl2mp/hl2mp_gamerules.h @@ -1,167 +1,167 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $Workfile: $ -// $Date: $ -// -//----------------------------------------------------------------------------- -// $Log: $ -// -// $NoKeywords: $ -//=============================================================================// - -#ifndef HL2MP_GAMERULES_H -#define HL2MP_GAMERULES_H -#pragma once - -#include "gamerules.h" -#include "teamplay_gamerules.h" -#include "gamevars_shared.h" - -#ifndef CLIENT_DLL -#include "hl2mp_player.h" -#endif - -#define VEC_CROUCH_TRACE_MIN HL2MPRules()->GetHL2MPViewVectors()->m_vCrouchTraceMin -#define VEC_CROUCH_TRACE_MAX HL2MPRules()->GetHL2MPViewVectors()->m_vCrouchTraceMax - -enum -{ - TEAM_COMBINE = 2, - TEAM_REBELS, -}; - - -#ifdef CLIENT_DLL - #define CHL2MPRules C_HL2MPRules - #define CHL2MPGameRulesProxy C_HL2MPGameRulesProxy -#endif - -class CHL2MPGameRulesProxy : public CGameRulesProxy -{ -public: - DECLARE_CLASS( CHL2MPGameRulesProxy, CGameRulesProxy ); - DECLARE_NETWORKCLASS(); -}; - -class HL2MPViewVectors : public CViewVectors -{ -public: - HL2MPViewVectors( - Vector vView, - Vector vHullMin, - Vector vHullMax, - Vector vDuckHullMin, - Vector vDuckHullMax, - Vector vDuckView, - Vector vObsHullMin, - Vector vObsHullMax, - Vector vDeadViewHeight, - Vector vCrouchTraceMin, - Vector vCrouchTraceMax ) : - CViewVectors( - vView, - vHullMin, - vHullMax, - vDuckHullMin, - vDuckHullMax, - vDuckView, - vObsHullMin, - vObsHullMax, - vDeadViewHeight ) - { - m_vCrouchTraceMin = vCrouchTraceMin; - m_vCrouchTraceMax = vCrouchTraceMax; - } - - Vector m_vCrouchTraceMin; - Vector m_vCrouchTraceMax; -}; - -class CHL2MPRules : public CTeamplayRules -{ -public: - DECLARE_CLASS( CHL2MPRules, CTeamplayRules ); - -#ifdef CLIENT_DLL - - DECLARE_CLIENTCLASS_NOBASE(); // This makes datatables able to access our private vars. - -#else - - DECLARE_SERVERCLASS_NOBASE(); // This makes datatables able to access our private vars. -#endif - - CHL2MPRules(); - virtual ~CHL2MPRules(); - - virtual void Precache( void ); - virtual bool ShouldCollide( int collisionGroup0, int collisionGroup1 ); - virtual bool ClientCommand( const char *pcmd, CBaseEntity *pEdict ); - - virtual float FlWeaponRespawnTime( CBaseCombatWeapon *pWeapon ); - virtual float FlWeaponTryRespawn( CBaseCombatWeapon *pWeapon ); - virtual Vector VecWeaponRespawnSpot( CBaseCombatWeapon *pWeapon ); - virtual int WeaponShouldRespawn( CBaseCombatWeapon *pWeapon ); - virtual void Think( void ); - virtual void CreateStandardEntities( void ); - virtual void ClientSettingsChanged( CBasePlayer *pPlayer ); - virtual int PlayerRelationship( CBaseEntity *pPlayer, CBaseEntity *pTarget ); - virtual void GoToIntermission( void ); - virtual void DeathNotice( CBasePlayer *pVictim, const CTakeDamageInfo &info ); - virtual const char *GetGameDescription( void ); - // derive this function if you mod uses encrypted weapon info files - virtual const unsigned char *GetEncryptionKey( void ) { return (unsigned char *)"x9Ke0BY7"; } - virtual const CViewVectors* GetViewVectors() const; - const HL2MPViewVectors* GetHL2MPViewVectors() const; - - float GetMapRemainingTime(); - void CleanUpMap(); - void CheckRestartGame(); - void RestartGame(); - -#ifndef CLIENT_DLL - virtual Vector VecItemRespawnSpot( CItem *pItem ); - virtual QAngle VecItemRespawnAngles( CItem *pItem ); - virtual float FlItemRespawnTime( CItem *pItem ); - virtual bool CanHavePlayerItem( CBasePlayer *pPlayer, CBaseCombatWeapon *pItem ); - virtual bool FShouldSwitchWeapon( CBasePlayer *pPlayer, CBaseCombatWeapon *pWeapon ); - - void AddLevelDesignerPlacedObject( CBaseEntity *pEntity ); - void RemoveLevelDesignerPlacedObject( CBaseEntity *pEntity ); - void ManageObjectRelocation( void ); - void CheckChatForReadySignal( CHL2MP_Player *pPlayer, const char *chatmsg ); - -#endif - virtual void ClientDisconnected( edict_t *pClient ); - - bool CheckGameOver( void ); - bool IsIntermission( void ); - - void PlayerKilled( CBasePlayer *pVictim, const CTakeDamageInfo &info ); - - - bool IsTeamplay( void ) { return m_bTeamPlayEnabled; } - void CheckAllPlayersReady( void ); - -private: - - CNetworkVar( bool, m_bTeamPlayEnabled ); - CNetworkVar( float, m_flGameStartTime ); - CUtlVector m_hRespawnableItemsAndWeapons; - float m_tmNextPeriodicThink; - float m_flRestartGameTime; - bool m_bCompleteReset; - bool m_bAwaitingReadyRestart; - bool m_bHeardAllPlayersReady; - - -}; - -inline CHL2MPRules* HL2MPRules() -{ - return static_cast(g_pGameRules); -} - -#endif //HL2MP_GAMERULES_H +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $Workfile: $ +// $Date: $ +// +//----------------------------------------------------------------------------- +// $Log: $ +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef HL2MP_GAMERULES_H +#define HL2MP_GAMERULES_H +#pragma once + +#include "gamerules.h" +#include "teamplay_gamerules.h" +#include "gamevars_shared.h" + +#ifndef CLIENT_DLL +#include "hl2mp_player.h" +#endif + +#define VEC_CROUCH_TRACE_MIN HL2MPRules()->GetHL2MPViewVectors()->m_vCrouchTraceMin +#define VEC_CROUCH_TRACE_MAX HL2MPRules()->GetHL2MPViewVectors()->m_vCrouchTraceMax + +enum +{ + TEAM_COMBINE = 2, + TEAM_REBELS, +}; + + +#ifdef CLIENT_DLL + #define CHL2MPRules C_HL2MPRules + #define CHL2MPGameRulesProxy C_HL2MPGameRulesProxy +#endif + +class CHL2MPGameRulesProxy : public CGameRulesProxy +{ +public: + DECLARE_CLASS( CHL2MPGameRulesProxy, CGameRulesProxy ); + DECLARE_NETWORKCLASS(); +}; + +class HL2MPViewVectors : public CViewVectors +{ +public: + HL2MPViewVectors( + Vector vView, + Vector vHullMin, + Vector vHullMax, + Vector vDuckHullMin, + Vector vDuckHullMax, + Vector vDuckView, + Vector vObsHullMin, + Vector vObsHullMax, + Vector vDeadViewHeight, + Vector vCrouchTraceMin, + Vector vCrouchTraceMax ) : + CViewVectors( + vView, + vHullMin, + vHullMax, + vDuckHullMin, + vDuckHullMax, + vDuckView, + vObsHullMin, + vObsHullMax, + vDeadViewHeight ) + { + m_vCrouchTraceMin = vCrouchTraceMin; + m_vCrouchTraceMax = vCrouchTraceMax; + } + + Vector m_vCrouchTraceMin; + Vector m_vCrouchTraceMax; +}; + +class CHL2MPRules : public CTeamplayRules +{ +public: + DECLARE_CLASS( CHL2MPRules, CTeamplayRules ); + +#ifdef CLIENT_DLL + + DECLARE_CLIENTCLASS_NOBASE(); // This makes datatables able to access our private vars. + +#else + + DECLARE_SERVERCLASS_NOBASE(); // This makes datatables able to access our private vars. +#endif + + CHL2MPRules(); + virtual ~CHL2MPRules(); + + virtual void Precache( void ); + virtual bool ShouldCollide( int collisionGroup0, int collisionGroup1 ); + virtual bool ClientCommand( const char *pcmd, CBaseEntity *pEdict ); + + virtual float FlWeaponRespawnTime( CBaseCombatWeapon *pWeapon ); + virtual float FlWeaponTryRespawn( CBaseCombatWeapon *pWeapon ); + virtual Vector VecWeaponRespawnSpot( CBaseCombatWeapon *pWeapon ); + virtual int WeaponShouldRespawn( CBaseCombatWeapon *pWeapon ); + virtual void Think( void ); + virtual void CreateStandardEntities( void ); + virtual void ClientSettingsChanged( CBasePlayer *pPlayer ); + virtual int PlayerRelationship( CBaseEntity *pPlayer, CBaseEntity *pTarget ); + virtual void GoToIntermission( void ); + virtual void DeathNotice( CBasePlayer *pVictim, const CTakeDamageInfo &info ); + virtual const char *GetGameDescription( void ); + // derive this function if you mod uses encrypted weapon info files + virtual const unsigned char *GetEncryptionKey( void ) { return (unsigned char *)"x9Ke0BY7"; } + virtual const CViewVectors* GetViewVectors() const; + const HL2MPViewVectors* GetHL2MPViewVectors() const; + + float GetMapRemainingTime(); + void CleanUpMap(); + void CheckRestartGame(); + void RestartGame(); + +#ifndef CLIENT_DLL + virtual Vector VecItemRespawnSpot( CItem *pItem ); + virtual QAngle VecItemRespawnAngles( CItem *pItem ); + virtual float FlItemRespawnTime( CItem *pItem ); + virtual bool CanHavePlayerItem( CBasePlayer *pPlayer, CBaseCombatWeapon *pItem ); + virtual bool FShouldSwitchWeapon( CBasePlayer *pPlayer, CBaseCombatWeapon *pWeapon ); + + void AddLevelDesignerPlacedObject( CBaseEntity *pEntity ); + void RemoveLevelDesignerPlacedObject( CBaseEntity *pEntity ); + void ManageObjectRelocation( void ); + void CheckChatForReadySignal( CHL2MP_Player *pPlayer, const char *chatmsg ); + +#endif + virtual void ClientDisconnected( edict_t *pClient ); + + bool CheckGameOver( void ); + bool IsIntermission( void ); + + void PlayerKilled( CBasePlayer *pVictim, const CTakeDamageInfo &info ); + + + bool IsTeamplay( void ) { return m_bTeamPlayEnabled; } + void CheckAllPlayersReady( void ); + +private: + + CNetworkVar( bool, m_bTeamPlayEnabled ); + CNetworkVar( float, m_flGameStartTime ); + CUtlVector m_hRespawnableItemsAndWeapons; + float m_tmNextPeriodicThink; + float m_flRestartGameTime; + bool m_bCompleteReset; + bool m_bAwaitingReadyRestart; + bool m_bHeardAllPlayersReady; + + +}; + +inline CHL2MPRules* HL2MPRules() +{ + return static_cast(g_pGameRules); +} + +#endif //HL2MP_GAMERULES_H diff --git a/game_shared/hl2mp/hl2mp_player_shared.cpp b/game_shared/hl2mp/hl2mp_player_shared.cpp index 0464bb6d..a6ec6cb6 100644 --- a/game_shared/hl2mp/hl2mp_player_shared.cpp +++ b/game_shared/hl2mp/hl2mp_player_shared.cpp @@ -1,573 +1,573 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -// -//=============================================================================// -#include "cbase.h" - -#ifdef CLIENT_DLL -#include "c_hl2mp_player.h" -#include "prediction.h" -#define CRecipientFilter C_RecipientFilter -#else -#include "hl2mp_player.h" -#endif - -#include "engine/IEngineSound.h" -#include "SoundEmitterSystem/isoundemittersystembase.h" - -extern ConVar sv_footsteps; - -const char *g_ppszPlayerSoundPrefixNames[PLAYER_SOUNDS_MAX] = -{ - "NPC_Citizen", - "NPC_CombineS", - "NPC_MetroPolice", -}; - -const char *CHL2MP_Player::GetPlayerModelSoundPrefix( void ) -{ - return g_ppszPlayerSoundPrefixNames[m_iPlayerSoundType]; -} - -void CHL2MP_Player::PrecacheFootStepSounds( void ) -{ - int iFootstepSounds = ARRAYSIZE( g_ppszPlayerSoundPrefixNames ); - int i; - - for ( i = 0; i < iFootstepSounds; ++i ) - { - char szFootStepName[128]; - - Q_snprintf( szFootStepName, sizeof( szFootStepName ), "%s.RunFootstepLeft", g_ppszPlayerSoundPrefixNames[i] ); - PrecacheScriptSound( szFootStepName ); - - Q_snprintf( szFootStepName, sizeof( szFootStepName ), "%s.RunFootstepRight", g_ppszPlayerSoundPrefixNames[i] ); - PrecacheScriptSound( szFootStepName ); - } -} - -//----------------------------------------------------------------------------- -// Consider the weapon's built-in accuracy, this character's proficiency with -// the weapon, and the status of the target. Use this information to determine -// how accurately to shoot at the target. -//----------------------------------------------------------------------------- -Vector CHL2MP_Player::GetAttackSpread( CBaseCombatWeapon *pWeapon, CBaseEntity *pTarget ) -{ - if ( pWeapon ) - return pWeapon->GetBulletSpread( WEAPON_PROFICIENCY_PERFECT ); - - return VECTOR_CONE_15DEGREES; -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : step - -// fvol - -// force - force sound to play -//----------------------------------------------------------------------------- -void CHL2MP_Player::PlayStepSound( Vector &vecOrigin, surfacedata_t *psurface, float fvol, bool force ) -{ - if ( gpGlobals->maxClients > 1 && !sv_footsteps.GetFloat() ) - return; - -#if defined( CLIENT_DLL ) - // during prediction play footstep sounds only once - if ( !prediction->IsFirstTimePredicted() ) - return; -#endif - - if ( GetFlags() & FL_DUCKING ) - return; - - m_Local.m_nStepside = !m_Local.m_nStepside; - - char szStepSound[128]; - - if ( m_Local.m_nStepside ) - { - Q_snprintf( szStepSound, sizeof( szStepSound ), "%s.RunFootstepLeft", g_ppszPlayerSoundPrefixNames[m_iPlayerSoundType] ); - } - else - { - Q_snprintf( szStepSound, sizeof( szStepSound ), "%s.RunFootstepRight", g_ppszPlayerSoundPrefixNames[m_iPlayerSoundType] ); - } - - CSoundParameters params; - if ( GetParametersForSound( szStepSound, params, NULL ) == false ) - return; - - CRecipientFilter filter; - filter.AddRecipientsByPAS( vecOrigin ); - -#ifndef CLIENT_DLL - // im MP, server removed all players in origins PVS, these players - // generate the footsteps clientside - if ( gpGlobals->maxClients > 1 ) - filter.RemoveRecipientsByPVS( vecOrigin ); -#endif - - EmitSound_t ep; - ep.m_nChannel = CHAN_BODY; - ep.m_pSoundName = params.soundname; - ep.m_flVolume = fvol; - ep.m_SoundLevel = params.soundlevel; - ep.m_nFlags = 0; - ep.m_nPitch = params.pitch; - ep.m_pOrigin = &vecOrigin; - - EmitSound( filter, entindex(), ep ); -} - - -//========================== -// ANIMATION CODE -//========================== - - -// Below this many degrees, slow down turning rate linearly -#define FADE_TURN_DEGREES 45.0f -// After this, need to start turning feet -#define MAX_TORSO_ANGLE 90.0f -// Below this amount, don't play a turning animation/perform IK -#define MIN_TURN_ANGLE_REQUIRING_TURN_ANIMATION 15.0f - -static ConVar tf2_feetyawrunscale( "tf2_feetyawrunscale", "2", FCVAR_REPLICATED, "Multiplier on tf2_feetyawrate to allow turning faster when running." ); -extern ConVar sv_backspeed; -extern ConVar mp_feetyawrate; -extern ConVar mp_facefronttime; -extern ConVar mp_ik; - -CPlayerAnimState::CPlayerAnimState( CHL2MP_Player *outer ) - : m_pOuter( outer ) -{ - m_flGaitYaw = 0.0f; - m_flGoalFeetYaw = 0.0f; - m_flCurrentFeetYaw = 0.0f; - m_flCurrentTorsoYaw = 0.0f; - m_flLastYaw = 0.0f; - m_flLastTurnTime = 0.0f; - m_flTurnCorrectionTime = 0.0f; -}; - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CPlayerAnimState::Update() -{ - m_angRender = GetOuter()->GetLocalAngles(); - - ComputePoseParam_BodyYaw(); - ComputePoseParam_BodyPitch(GetOuter()->GetModelPtr()); - ComputePoseParam_BodyLookYaw(); - - ComputePlaybackRate(); - -#ifdef CLIENT_DLL - GetOuter()->UpdateLookAt(); -#endif - -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CPlayerAnimState::ComputePlaybackRate() -{ - // Determine ideal playback rate - Vector vel; - GetOuterAbsVelocity( vel ); - - float speed = vel.Length2D(); - - bool isMoving = ( speed > 0.5f ) ? true : false; - - float maxspeed = GetOuter()->GetSequenceGroundSpeed( GetOuter()->GetSequence() ); - - if ( isMoving && ( maxspeed > 0.0f ) ) - { - float flFactor = 1.0f; - - // Note this gets set back to 1.0 if sequence changes due to ResetSequenceInfo below - GetOuter()->SetPlaybackRate( ( speed * flFactor ) / maxspeed ); - - // BUG BUG: - // This stuff really should be m_flPlaybackRate = speed / m_flGroundSpeed - } - else - { - GetOuter()->SetPlaybackRate( 1.0f ); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -// Output : CBasePlayer -//----------------------------------------------------------------------------- -CHL2MP_Player *CPlayerAnimState::GetOuter() -{ - return m_pOuter; -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : dt - -//----------------------------------------------------------------------------- -void CPlayerAnimState::EstimateYaw( void ) -{ - float dt = gpGlobals->frametime; - - if ( !dt ) - { - return; - } - - Vector est_velocity; - QAngle angles; - - GetOuterAbsVelocity( est_velocity ); - - angles = GetOuter()->GetLocalAngles(); - - if ( est_velocity[1] == 0 && est_velocity[0] == 0 ) - { - float flYawDiff = angles[YAW] - m_flGaitYaw; - flYawDiff = flYawDiff - (int)(flYawDiff / 360) * 360; - if (flYawDiff > 180) - flYawDiff -= 360; - if (flYawDiff < -180) - flYawDiff += 360; - - if (dt < 0.25) - flYawDiff *= dt * 4; - else - flYawDiff *= dt; - - m_flGaitYaw += flYawDiff; - m_flGaitYaw = m_flGaitYaw - (int)(m_flGaitYaw / 360) * 360; - } - else - { - m_flGaitYaw = (atan2(est_velocity[1], est_velocity[0]) * 180 / M_PI); - - if (m_flGaitYaw > 180) - m_flGaitYaw = 180; - else if (m_flGaitYaw < -180) - m_flGaitYaw = -180; - } -} - -//----------------------------------------------------------------------------- -// Purpose: Override for backpeddling -// Input : dt - -//----------------------------------------------------------------------------- -void CPlayerAnimState::ComputePoseParam_BodyYaw( void ) -{ - int iYaw = GetOuter()->LookupPoseParameter( "move_yaw" ); - if ( iYaw < 0 ) - return; - - // view direction relative to movement - float flYaw; - - EstimateYaw(); - - QAngle angles = GetOuter()->GetLocalAngles(); - float ang = angles[ YAW ]; - if ( ang > 180.0f ) - { - ang -= 360.0f; - } - else if ( ang < -180.0f ) - { - ang += 360.0f; - } - - // calc side to side turning - flYaw = ang - m_flGaitYaw; - // Invert for mapping into 8way blend - flYaw = -flYaw; - flYaw = flYaw - (int)(flYaw / 360) * 360; - - if (flYaw < -180) - { - flYaw = flYaw + 360; - } - else if (flYaw > 180) - { - flYaw = flYaw - 360; - } - - GetOuter()->SetPoseParameter( iYaw, flYaw ); - -#ifndef CLIENT_DLL - //Adrian: Make the model's angle match the legs so the hitboxes match on both sides. - GetOuter()->SetLocalAngles( QAngle( GetOuter()->GetAnimEyeAngles().x, m_flCurrentFeetYaw, 0 ) ); -#endif -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CPlayerAnimState::ComputePoseParam_BodyPitch( CStudioHdr *pStudioHdr ) -{ - // Get pitch from v_angle - float flPitch = GetOuter()->GetLocalAngles()[ PITCH ]; - - if ( flPitch > 180.0f ) - { - flPitch -= 360.0f; - } - flPitch = clamp( flPitch, -90, 90 ); - - QAngle absangles = GetOuter()->GetAbsAngles(); - absangles.x = 0.0f; - m_angRender = absangles; - - // See if we have a blender for pitch - GetOuter()->SetPoseParameter( pStudioHdr, "aim_pitch", flPitch ); -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : goal - -// maxrate - -// dt - -// current - -// Output : int -//----------------------------------------------------------------------------- -int CPlayerAnimState::ConvergeAngles( float goal,float maxrate, float dt, float& current ) -{ - int direction = TURN_NONE; - - float anglediff = goal - current; - float anglediffabs = fabs( anglediff ); - - anglediff = AngleNormalize( anglediff ); - - float scale = 1.0f; - if ( anglediffabs <= FADE_TURN_DEGREES ) - { - scale = anglediffabs / FADE_TURN_DEGREES; - // Always do at least a bit of the turn ( 1% ) - scale = clamp( scale, 0.01f, 1.0f ); - } - - float maxmove = maxrate * dt * scale; - - if ( fabs( anglediff ) < maxmove ) - { - current = goal; - } - else - { - if ( anglediff > 0 ) - { - current += maxmove; - direction = TURN_LEFT; - } - else - { - current -= maxmove; - direction = TURN_RIGHT; - } - } - - current = AngleNormalize( current ); - - return direction; -} - -void CPlayerAnimState::ComputePoseParam_BodyLookYaw( void ) -{ - QAngle absangles = GetOuter()->GetAbsAngles(); - absangles.y = AngleNormalize( absangles.y ); - m_angRender = absangles; - - // See if we even have a blender for pitch - int upper_body_yaw = GetOuter()->LookupPoseParameter( "aim_yaw" ); - if ( upper_body_yaw < 0 ) - { - return; - } - - // Assume upper and lower bodies are aligned and that we're not turning - float flGoalTorsoYaw = 0.0f; - int turning = TURN_NONE; - float turnrate = 360.0f; - - Vector vel; - - GetOuterAbsVelocity( vel ); - - bool isMoving = ( vel.Length() > 1.0f ) ? true : false; - - if ( !isMoving ) - { - // Just stopped moving, try and clamp feet - if ( m_flLastTurnTime <= 0.0f ) - { - m_flLastTurnTime = gpGlobals->curtime; - m_flLastYaw = GetOuter()->GetAnimEyeAngles().y; - // Snap feet to be perfectly aligned with torso/eyes - m_flGoalFeetYaw = GetOuter()->GetAnimEyeAngles().y; - m_flCurrentFeetYaw = m_flGoalFeetYaw; - m_nTurningInPlace = TURN_NONE; - } - - // If rotating in place, update stasis timer - if ( m_flLastYaw != GetOuter()->GetAnimEyeAngles().y ) - { - m_flLastTurnTime = gpGlobals->curtime; - m_flLastYaw = GetOuter()->GetAnimEyeAngles().y; - } - - if ( m_flGoalFeetYaw != m_flCurrentFeetYaw ) - { - m_flLastTurnTime = gpGlobals->curtime; - } - - turning = ConvergeAngles( m_flGoalFeetYaw, turnrate, gpGlobals->frametime, m_flCurrentFeetYaw ); - - QAngle eyeAngles = GetOuter()->GetAnimEyeAngles(); - QAngle vAngle = GetOuter()->GetLocalAngles(); - - // See how far off current feetyaw is from true yaw - float yawdelta = GetOuter()->GetAnimEyeAngles().y - m_flCurrentFeetYaw; - yawdelta = AngleNormalize( yawdelta ); - - bool rotated_too_far = false; - - float yawmagnitude = fabs( yawdelta ); - - // If too far, then need to turn in place - if ( yawmagnitude > 45 ) - { - rotated_too_far = true; - } - - // Standing still for a while, rotate feet around to face forward - // Or rotated too far - // FIXME: Play an in place turning animation - if ( rotated_too_far || - ( gpGlobals->curtime > m_flLastTurnTime + mp_facefronttime.GetFloat() ) ) - { - m_flGoalFeetYaw = GetOuter()->GetAnimEyeAngles().y; - m_flLastTurnTime = gpGlobals->curtime; - - /* float yd = m_flCurrentFeetYaw - m_flGoalFeetYaw; - if ( yd > 0 ) - { - m_nTurningInPlace = TURN_RIGHT; - } - else if ( yd < 0 ) - { - m_nTurningInPlace = TURN_LEFT; - } - else - { - m_nTurningInPlace = TURN_NONE; - } - - turning = ConvergeAngles( m_flGoalFeetYaw, turnrate, gpGlobals->frametime, m_flCurrentFeetYaw ); - yawdelta = GetOuter()->GetAnimEyeAngles().y - m_flCurrentFeetYaw;*/ - - } - - // Snap upper body into position since the delta is already smoothed for the feet - flGoalTorsoYaw = yawdelta; - m_flCurrentTorsoYaw = flGoalTorsoYaw; - } - else - { - m_flLastTurnTime = 0.0f; - m_nTurningInPlace = TURN_NONE; - m_flCurrentFeetYaw = m_flGoalFeetYaw = GetOuter()->GetAnimEyeAngles().y; - flGoalTorsoYaw = 0.0f; - m_flCurrentTorsoYaw = GetOuter()->GetAnimEyeAngles().y - m_flCurrentFeetYaw; - } - - - if ( turning == TURN_NONE ) - { - m_nTurningInPlace = turning; - } - - if ( m_nTurningInPlace != TURN_NONE ) - { - // If we're close to finishing the turn, then turn off the turning animation - if ( fabs( m_flCurrentFeetYaw - m_flGoalFeetYaw ) < MIN_TURN_ANGLE_REQUIRING_TURN_ANIMATION ) - { - m_nTurningInPlace = TURN_NONE; - } - } - - // Rotate entire body into position - absangles = GetOuter()->GetAbsAngles(); - absangles.y = m_flCurrentFeetYaw; - m_angRender = absangles; - - GetOuter()->SetPoseParameter( upper_body_yaw, clamp( m_flCurrentTorsoYaw, -60.0f, 60.0f ) ); - - /* - // FIXME: Adrian, what is this? - int body_yaw = GetOuter()->LookupPoseParameter( "body_yaw" ); - - if ( body_yaw >= 0 ) - { - GetOuter()->SetPoseParameter( body_yaw, 30 ); - } - */ - -} - - - -//----------------------------------------------------------------------------- -// Purpose: -// Input : activity - -// Output : Activity -//----------------------------------------------------------------------------- -Activity CPlayerAnimState::BodyYawTranslateActivity( Activity activity ) -{ - // Not even standing still, sigh - if ( activity != ACT_IDLE ) - return activity; - - // Not turning - switch ( m_nTurningInPlace ) - { - default: - case TURN_NONE: - return activity; - /* - case TURN_RIGHT: - return ACT_TURNRIGHT45; - case TURN_LEFT: - return ACT_TURNLEFT45; - */ - case TURN_RIGHT: - case TURN_LEFT: - return mp_ik.GetBool() ? ACT_TURN : activity; - } - - Assert( 0 ); - return activity; -} - -const QAngle& CPlayerAnimState::GetRenderAngles() -{ - return m_angRender; -} - - -void CPlayerAnimState::GetOuterAbsVelocity( Vector& vel ) -{ -#if defined( CLIENT_DLL ) - GetOuter()->EstimateAbsVelocity( vel ); -#else - vel = GetOuter()->GetAbsVelocity(); -#endif -} \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// +#include "cbase.h" + +#ifdef CLIENT_DLL +#include "c_hl2mp_player.h" +#include "prediction.h" +#define CRecipientFilter C_RecipientFilter +#else +#include "hl2mp_player.h" +#endif + +#include "engine/IEngineSound.h" +#include "SoundEmitterSystem/isoundemittersystembase.h" + +extern ConVar sv_footsteps; + +const char *g_ppszPlayerSoundPrefixNames[PLAYER_SOUNDS_MAX] = +{ + "NPC_Citizen", + "NPC_CombineS", + "NPC_MetroPolice", +}; + +const char *CHL2MP_Player::GetPlayerModelSoundPrefix( void ) +{ + return g_ppszPlayerSoundPrefixNames[m_iPlayerSoundType]; +} + +void CHL2MP_Player::PrecacheFootStepSounds( void ) +{ + int iFootstepSounds = ARRAYSIZE( g_ppszPlayerSoundPrefixNames ); + int i; + + for ( i = 0; i < iFootstepSounds; ++i ) + { + char szFootStepName[128]; + + Q_snprintf( szFootStepName, sizeof( szFootStepName ), "%s.RunFootstepLeft", g_ppszPlayerSoundPrefixNames[i] ); + PrecacheScriptSound( szFootStepName ); + + Q_snprintf( szFootStepName, sizeof( szFootStepName ), "%s.RunFootstepRight", g_ppszPlayerSoundPrefixNames[i] ); + PrecacheScriptSound( szFootStepName ); + } +} + +//----------------------------------------------------------------------------- +// Consider the weapon's built-in accuracy, this character's proficiency with +// the weapon, and the status of the target. Use this information to determine +// how accurately to shoot at the target. +//----------------------------------------------------------------------------- +Vector CHL2MP_Player::GetAttackSpread( CBaseCombatWeapon *pWeapon, CBaseEntity *pTarget ) +{ + if ( pWeapon ) + return pWeapon->GetBulletSpread( WEAPON_PROFICIENCY_PERFECT ); + + return VECTOR_CONE_15DEGREES; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : step - +// fvol - +// force - force sound to play +//----------------------------------------------------------------------------- +void CHL2MP_Player::PlayStepSound( Vector &vecOrigin, surfacedata_t *psurface, float fvol, bool force ) +{ + if ( gpGlobals->maxClients > 1 && !sv_footsteps.GetFloat() ) + return; + +#if defined( CLIENT_DLL ) + // during prediction play footstep sounds only once + if ( !prediction->IsFirstTimePredicted() ) + return; +#endif + + if ( GetFlags() & FL_DUCKING ) + return; + + m_Local.m_nStepside = !m_Local.m_nStepside; + + char szStepSound[128]; + + if ( m_Local.m_nStepside ) + { + Q_snprintf( szStepSound, sizeof( szStepSound ), "%s.RunFootstepLeft", g_ppszPlayerSoundPrefixNames[m_iPlayerSoundType] ); + } + else + { + Q_snprintf( szStepSound, sizeof( szStepSound ), "%s.RunFootstepRight", g_ppszPlayerSoundPrefixNames[m_iPlayerSoundType] ); + } + + CSoundParameters params; + if ( GetParametersForSound( szStepSound, params, NULL ) == false ) + return; + + CRecipientFilter filter; + filter.AddRecipientsByPAS( vecOrigin ); + +#ifndef CLIENT_DLL + // im MP, server removed all players in origins PVS, these players + // generate the footsteps clientside + if ( gpGlobals->maxClients > 1 ) + filter.RemoveRecipientsByPVS( vecOrigin ); +#endif + + EmitSound_t ep; + ep.m_nChannel = CHAN_BODY; + ep.m_pSoundName = params.soundname; + ep.m_flVolume = fvol; + ep.m_SoundLevel = params.soundlevel; + ep.m_nFlags = 0; + ep.m_nPitch = params.pitch; + ep.m_pOrigin = &vecOrigin; + + EmitSound( filter, entindex(), ep ); +} + + +//========================== +// ANIMATION CODE +//========================== + + +// Below this many degrees, slow down turning rate linearly +#define FADE_TURN_DEGREES 45.0f +// After this, need to start turning feet +#define MAX_TORSO_ANGLE 90.0f +// Below this amount, don't play a turning animation/perform IK +#define MIN_TURN_ANGLE_REQUIRING_TURN_ANIMATION 15.0f + +static ConVar tf2_feetyawrunscale( "tf2_feetyawrunscale", "2", FCVAR_REPLICATED, "Multiplier on tf2_feetyawrate to allow turning faster when running." ); +extern ConVar sv_backspeed; +extern ConVar mp_feetyawrate; +extern ConVar mp_facefronttime; +extern ConVar mp_ik; + +CPlayerAnimState::CPlayerAnimState( CHL2MP_Player *outer ) + : m_pOuter( outer ) +{ + m_flGaitYaw = 0.0f; + m_flGoalFeetYaw = 0.0f; + m_flCurrentFeetYaw = 0.0f; + m_flCurrentTorsoYaw = 0.0f; + m_flLastYaw = 0.0f; + m_flLastTurnTime = 0.0f; + m_flTurnCorrectionTime = 0.0f; +}; + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPlayerAnimState::Update() +{ + m_angRender = GetOuter()->GetLocalAngles(); + + ComputePoseParam_BodyYaw(); + ComputePoseParam_BodyPitch(GetOuter()->GetModelPtr()); + ComputePoseParam_BodyLookYaw(); + + ComputePlaybackRate(); + +#ifdef CLIENT_DLL + GetOuter()->UpdateLookAt(); +#endif + +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPlayerAnimState::ComputePlaybackRate() +{ + // Determine ideal playback rate + Vector vel; + GetOuterAbsVelocity( vel ); + + float speed = vel.Length2D(); + + bool isMoving = ( speed > 0.5f ) ? true : false; + + float maxspeed = GetOuter()->GetSequenceGroundSpeed( GetOuter()->GetSequence() ); + + if ( isMoving && ( maxspeed > 0.0f ) ) + { + float flFactor = 1.0f; + + // Note this gets set back to 1.0 if sequence changes due to ResetSequenceInfo below + GetOuter()->SetPlaybackRate( ( speed * flFactor ) / maxspeed ); + + // BUG BUG: + // This stuff really should be m_flPlaybackRate = speed / m_flGroundSpeed + } + else + { + GetOuter()->SetPlaybackRate( 1.0f ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : CBasePlayer +//----------------------------------------------------------------------------- +CHL2MP_Player *CPlayerAnimState::GetOuter() +{ + return m_pOuter; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : dt - +//----------------------------------------------------------------------------- +void CPlayerAnimState::EstimateYaw( void ) +{ + float dt = gpGlobals->frametime; + + if ( !dt ) + { + return; + } + + Vector est_velocity; + QAngle angles; + + GetOuterAbsVelocity( est_velocity ); + + angles = GetOuter()->GetLocalAngles(); + + if ( est_velocity[1] == 0 && est_velocity[0] == 0 ) + { + float flYawDiff = angles[YAW] - m_flGaitYaw; + flYawDiff = flYawDiff - (int)(flYawDiff / 360) * 360; + if (flYawDiff > 180) + flYawDiff -= 360; + if (flYawDiff < -180) + flYawDiff += 360; + + if (dt < 0.25) + flYawDiff *= dt * 4; + else + flYawDiff *= dt; + + m_flGaitYaw += flYawDiff; + m_flGaitYaw = m_flGaitYaw - (int)(m_flGaitYaw / 360) * 360; + } + else + { + m_flGaitYaw = (atan2(est_velocity[1], est_velocity[0]) * 180 / M_PI); + + if (m_flGaitYaw > 180) + m_flGaitYaw = 180; + else if (m_flGaitYaw < -180) + m_flGaitYaw = -180; + } +} + +//----------------------------------------------------------------------------- +// Purpose: Override for backpeddling +// Input : dt - +//----------------------------------------------------------------------------- +void CPlayerAnimState::ComputePoseParam_BodyYaw( void ) +{ + int iYaw = GetOuter()->LookupPoseParameter( "move_yaw" ); + if ( iYaw < 0 ) + return; + + // view direction relative to movement + float flYaw; + + EstimateYaw(); + + QAngle angles = GetOuter()->GetLocalAngles(); + float ang = angles[ YAW ]; + if ( ang > 180.0f ) + { + ang -= 360.0f; + } + else if ( ang < -180.0f ) + { + ang += 360.0f; + } + + // calc side to side turning + flYaw = ang - m_flGaitYaw; + // Invert for mapping into 8way blend + flYaw = -flYaw; + flYaw = flYaw - (int)(flYaw / 360) * 360; + + if (flYaw < -180) + { + flYaw = flYaw + 360; + } + else if (flYaw > 180) + { + flYaw = flYaw - 360; + } + + GetOuter()->SetPoseParameter( iYaw, flYaw ); + +#ifndef CLIENT_DLL + //Adrian: Make the model's angle match the legs so the hitboxes match on both sides. + GetOuter()->SetLocalAngles( QAngle( GetOuter()->GetAnimEyeAngles().x, m_flCurrentFeetYaw, 0 ) ); +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPlayerAnimState::ComputePoseParam_BodyPitch( CStudioHdr *pStudioHdr ) +{ + // Get pitch from v_angle + float flPitch = GetOuter()->GetLocalAngles()[ PITCH ]; + + if ( flPitch > 180.0f ) + { + flPitch -= 360.0f; + } + flPitch = clamp( flPitch, -90, 90 ); + + QAngle absangles = GetOuter()->GetAbsAngles(); + absangles.x = 0.0f; + m_angRender = absangles; + + // See if we have a blender for pitch + GetOuter()->SetPoseParameter( pStudioHdr, "aim_pitch", flPitch ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : goal - +// maxrate - +// dt - +// current - +// Output : int +//----------------------------------------------------------------------------- +int CPlayerAnimState::ConvergeAngles( float goal,float maxrate, float dt, float& current ) +{ + int direction = TURN_NONE; + + float anglediff = goal - current; + float anglediffabs = fabs( anglediff ); + + anglediff = AngleNormalize( anglediff ); + + float scale = 1.0f; + if ( anglediffabs <= FADE_TURN_DEGREES ) + { + scale = anglediffabs / FADE_TURN_DEGREES; + // Always do at least a bit of the turn ( 1% ) + scale = clamp( scale, 0.01f, 1.0f ); + } + + float maxmove = maxrate * dt * scale; + + if ( fabs( anglediff ) < maxmove ) + { + current = goal; + } + else + { + if ( anglediff > 0 ) + { + current += maxmove; + direction = TURN_LEFT; + } + else + { + current -= maxmove; + direction = TURN_RIGHT; + } + } + + current = AngleNormalize( current ); + + return direction; +} + +void CPlayerAnimState::ComputePoseParam_BodyLookYaw( void ) +{ + QAngle absangles = GetOuter()->GetAbsAngles(); + absangles.y = AngleNormalize( absangles.y ); + m_angRender = absangles; + + // See if we even have a blender for pitch + int upper_body_yaw = GetOuter()->LookupPoseParameter( "aim_yaw" ); + if ( upper_body_yaw < 0 ) + { + return; + } + + // Assume upper and lower bodies are aligned and that we're not turning + float flGoalTorsoYaw = 0.0f; + int turning = TURN_NONE; + float turnrate = 360.0f; + + Vector vel; + + GetOuterAbsVelocity( vel ); + + bool isMoving = ( vel.Length() > 1.0f ) ? true : false; + + if ( !isMoving ) + { + // Just stopped moving, try and clamp feet + if ( m_flLastTurnTime <= 0.0f ) + { + m_flLastTurnTime = gpGlobals->curtime; + m_flLastYaw = GetOuter()->GetAnimEyeAngles().y; + // Snap feet to be perfectly aligned with torso/eyes + m_flGoalFeetYaw = GetOuter()->GetAnimEyeAngles().y; + m_flCurrentFeetYaw = m_flGoalFeetYaw; + m_nTurningInPlace = TURN_NONE; + } + + // If rotating in place, update stasis timer + if ( m_flLastYaw != GetOuter()->GetAnimEyeAngles().y ) + { + m_flLastTurnTime = gpGlobals->curtime; + m_flLastYaw = GetOuter()->GetAnimEyeAngles().y; + } + + if ( m_flGoalFeetYaw != m_flCurrentFeetYaw ) + { + m_flLastTurnTime = gpGlobals->curtime; + } + + turning = ConvergeAngles( m_flGoalFeetYaw, turnrate, gpGlobals->frametime, m_flCurrentFeetYaw ); + + QAngle eyeAngles = GetOuter()->GetAnimEyeAngles(); + QAngle vAngle = GetOuter()->GetLocalAngles(); + + // See how far off current feetyaw is from true yaw + float yawdelta = GetOuter()->GetAnimEyeAngles().y - m_flCurrentFeetYaw; + yawdelta = AngleNormalize( yawdelta ); + + bool rotated_too_far = false; + + float yawmagnitude = fabs( yawdelta ); + + // If too far, then need to turn in place + if ( yawmagnitude > 45 ) + { + rotated_too_far = true; + } + + // Standing still for a while, rotate feet around to face forward + // Or rotated too far + // FIXME: Play an in place turning animation + if ( rotated_too_far || + ( gpGlobals->curtime > m_flLastTurnTime + mp_facefronttime.GetFloat() ) ) + { + m_flGoalFeetYaw = GetOuter()->GetAnimEyeAngles().y; + m_flLastTurnTime = gpGlobals->curtime; + + /* float yd = m_flCurrentFeetYaw - m_flGoalFeetYaw; + if ( yd > 0 ) + { + m_nTurningInPlace = TURN_RIGHT; + } + else if ( yd < 0 ) + { + m_nTurningInPlace = TURN_LEFT; + } + else + { + m_nTurningInPlace = TURN_NONE; + } + + turning = ConvergeAngles( m_flGoalFeetYaw, turnrate, gpGlobals->frametime, m_flCurrentFeetYaw ); + yawdelta = GetOuter()->GetAnimEyeAngles().y - m_flCurrentFeetYaw;*/ + + } + + // Snap upper body into position since the delta is already smoothed for the feet + flGoalTorsoYaw = yawdelta; + m_flCurrentTorsoYaw = flGoalTorsoYaw; + } + else + { + m_flLastTurnTime = 0.0f; + m_nTurningInPlace = TURN_NONE; + m_flCurrentFeetYaw = m_flGoalFeetYaw = GetOuter()->GetAnimEyeAngles().y; + flGoalTorsoYaw = 0.0f; + m_flCurrentTorsoYaw = GetOuter()->GetAnimEyeAngles().y - m_flCurrentFeetYaw; + } + + + if ( turning == TURN_NONE ) + { + m_nTurningInPlace = turning; + } + + if ( m_nTurningInPlace != TURN_NONE ) + { + // If we're close to finishing the turn, then turn off the turning animation + if ( fabs( m_flCurrentFeetYaw - m_flGoalFeetYaw ) < MIN_TURN_ANGLE_REQUIRING_TURN_ANIMATION ) + { + m_nTurningInPlace = TURN_NONE; + } + } + + // Rotate entire body into position + absangles = GetOuter()->GetAbsAngles(); + absangles.y = m_flCurrentFeetYaw; + m_angRender = absangles; + + GetOuter()->SetPoseParameter( upper_body_yaw, clamp( m_flCurrentTorsoYaw, -60.0f, 60.0f ) ); + + /* + // FIXME: Adrian, what is this? + int body_yaw = GetOuter()->LookupPoseParameter( "body_yaw" ); + + if ( body_yaw >= 0 ) + { + GetOuter()->SetPoseParameter( body_yaw, 30 ); + } + */ + +} + + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : activity - +// Output : Activity +//----------------------------------------------------------------------------- +Activity CPlayerAnimState::BodyYawTranslateActivity( Activity activity ) +{ + // Not even standing still, sigh + if ( activity != ACT_IDLE ) + return activity; + + // Not turning + switch ( m_nTurningInPlace ) + { + default: + case TURN_NONE: + return activity; + /* + case TURN_RIGHT: + return ACT_TURNRIGHT45; + case TURN_LEFT: + return ACT_TURNLEFT45; + */ + case TURN_RIGHT: + case TURN_LEFT: + return mp_ik.GetBool() ? ACT_TURN : activity; + } + + Assert( 0 ); + return activity; +} + +const QAngle& CPlayerAnimState::GetRenderAngles() +{ + return m_angRender; +} + + +void CPlayerAnimState::GetOuterAbsVelocity( Vector& vel ) +{ +#if defined( CLIENT_DLL ) + GetOuter()->EstimateAbsVelocity( vel ); +#else + vel = GetOuter()->GetAbsVelocity(); +#endif +} diff --git a/game_shared/hl2mp/hl2mp_player_shared.h b/game_shared/hl2mp/hl2mp_player_shared.h index ef68d217..558ac3b1 100644 --- a/game_shared/hl2mp/hl2mp_player_shared.h +++ b/game_shared/hl2mp/hl2mp_player_shared.h @@ -95,3 +95,4 @@ private: }; #endif //HL2MP_PLAYER_SHARED_h + diff --git a/game_shared/hl2mp/weapon_crowbar.cpp b/game_shared/hl2mp/weapon_crowbar.cpp index e91b3a39..1b871c1c 100644 --- a/game_shared/hl2mp/weapon_crowbar.cpp +++ b/game_shared/hl2mp/weapon_crowbar.cpp @@ -1,261 +1,261 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: Crowbar - an old favorite -// -// $NoKeywords: $ -//=============================================================================// - -#include "cbase.h" -#include "weapon_hl2mpbasehlmpcombatweapon.h" -#include "gamerules.h" -#include "ammodef.h" -#include "mathlib.h" -#include "in_buttons.h" -#include "weapon_hl2mpbasebasebludgeon.h" -#include "vstdlib/random.h" -#include "npcevent.h" - -#if defined( CLIENT_DLL ) - #include "c_hl2mp_player.h" -#else - #include "hl2mp_player.h" - #include "ai_basenpc.h" -#endif - - -// memdbgon must be the last include file in a .cpp file!!! -#include "tier0/memdbgon.h" - -#define CROWBAR_RANGE 75.0f -#define CROWBAR_REFIRE 0.6f - - -#ifdef CLIENT_DLL -#define CWeaponCrowbar C_WeaponCrowbar -#endif - -//----------------------------------------------------------------------------- -// CWeaponCrowbar -//----------------------------------------------------------------------------- - -class CWeaponCrowbar : public CBaseHL2MPBludgeonWeapon -{ -public: - DECLARE_CLASS( CWeaponCrowbar, CBaseHL2MPBludgeonWeapon ); - - DECLARE_NETWORKCLASS(); - DECLARE_PREDICTABLE(); - -#ifndef CLIENT_DLL - DECLARE_ACTTABLE(); -#endif - - CWeaponCrowbar(); - - float GetRange( void ) { return CROWBAR_RANGE; } - float GetFireRate( void ) { return CROWBAR_REFIRE; } - - void AddViewKick( void ); - float GetDamageForActivity( Activity hitActivity ); - void SecondaryAttack( void ) { return; } - - void Drop( const Vector &vecVelocity ); - - - // Animation event -#ifndef CLIENT_DLL - virtual void Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatCharacter *pOperator ); - void HandleAnimEventMeleeHit( animevent_t *pEvent, CBaseCombatCharacter *pOperator ); - int WeaponMeleeAttack1Condition( float flDot, float flDist ); -#endif - - CWeaponCrowbar( const CWeaponCrowbar & ); - -private: - -}; - -//----------------------------------------------------------------------------- -// CWeaponCrowbar -//----------------------------------------------------------------------------- - -IMPLEMENT_NETWORKCLASS_ALIASED( WeaponCrowbar, DT_WeaponCrowbar ) - -BEGIN_NETWORK_TABLE( CWeaponCrowbar, DT_WeaponCrowbar ) -END_NETWORK_TABLE() - -BEGIN_PREDICTION_DATA( CWeaponCrowbar ) -END_PREDICTION_DATA() - -LINK_ENTITY_TO_CLASS( weapon_crowbar, CWeaponCrowbar ); -PRECACHE_WEAPON_REGISTER( weapon_crowbar ); - -#ifndef CLIENT_DLL - -acttable_t CWeaponCrowbar::m_acttable[] = -{ - { ACT_RANGE_ATTACK1, ACT_RANGE_ATTACK_SLAM, true }, - { ACT_HL2MP_IDLE, ACT_HL2MP_IDLE_MELEE, false }, - { ACT_HL2MP_RUN, ACT_HL2MP_RUN_MELEE, false }, - { ACT_HL2MP_IDLE_CROUCH, ACT_HL2MP_IDLE_CROUCH_MELEE, false }, - { ACT_HL2MP_WALK_CROUCH, ACT_HL2MP_WALK_CROUCH_MELEE, false }, - { ACT_HL2MP_GESTURE_RANGE_ATTACK, ACT_HL2MP_GESTURE_RANGE_ATTACK_MELEE, false }, - { ACT_HL2MP_GESTURE_RELOAD, ACT_HL2MP_GESTURE_RELOAD_MELEE, false }, - { ACT_HL2MP_JUMP, ACT_HL2MP_JUMP_MELEE, false }, -}; - -IMPLEMENT_ACTTABLE(CWeaponCrowbar); - -#endif - -//----------------------------------------------------------------------------- -// Constructor -//----------------------------------------------------------------------------- -CWeaponCrowbar::CWeaponCrowbar( void ) -{ -} - -//----------------------------------------------------------------------------- -// Purpose: Get the damage amount for the animation we're doing -// Input : hitActivity - currently played activity -// Output : Damage amount -//----------------------------------------------------------------------------- -float CWeaponCrowbar::GetDamageForActivity( Activity hitActivity ) -{ - return 25.0f; -} - -//----------------------------------------------------------------------------- -// Purpose: Add in a view kick for this weapon -//----------------------------------------------------------------------------- -void CWeaponCrowbar::AddViewKick( void ) -{ - CBasePlayer *pPlayer = ToBasePlayer( GetOwner() ); - - if ( pPlayer == NULL ) - return; - - QAngle punchAng; - - punchAng.x = random->RandomFloat( 1.0f, 2.0f ); - punchAng.y = random->RandomFloat( -2.0f, -1.0f ); - punchAng.z = 0.0f; - - pPlayer->ViewPunch( punchAng ); -} - - -#ifndef CLIENT_DLL -//----------------------------------------------------------------------------- -// Animation event handlers -//----------------------------------------------------------------------------- -void CWeaponCrowbar::HandleAnimEventMeleeHit( animevent_t *pEvent, CBaseCombatCharacter *pOperator ) -{ - // Trace up or down based on where the enemy is... - // But only if we're basically facing that direction - Vector vecDirection; - AngleVectors( GetAbsAngles(), &vecDirection ); - - Vector vecEnd; - VectorMA( pOperator->Weapon_ShootPosition(), 50, vecDirection, vecEnd ); - CBaseEntity *pHurt = pOperator->CheckTraceHullAttack( pOperator->Weapon_ShootPosition(), vecEnd, - Vector(-16,-16,-16), Vector(36,36,36), GetDamageForActivity( GetActivity() ), DMG_CLUB, 0.75 ); - - // did I hit someone? - if ( pHurt ) - { - // play sound - WeaponSound( MELEE_HIT ); - - // Fake a trace impact, so the effects work out like a player's crowbaw - trace_t traceHit; - UTIL_TraceLine( pOperator->Weapon_ShootPosition(), pHurt->GetAbsOrigin(), MASK_SHOT_HULL, pOperator, COLLISION_GROUP_NONE, &traceHit ); - ImpactEffect( traceHit ); - } - else - { - WeaponSound( MELEE_MISS ); - } -} - - -//----------------------------------------------------------------------------- -// Animation event -//----------------------------------------------------------------------------- -void CWeaponCrowbar::Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatCharacter *pOperator ) -{ - switch( pEvent->event ) - { - case EVENT_WEAPON_MELEE_HIT: - HandleAnimEventMeleeHit( pEvent, pOperator ); - break; - - default: - BaseClass::Operator_HandleAnimEvent( pEvent, pOperator ); - break; - } -} - -//----------------------------------------------------------------------------- -// Attempt to lead the target (needed because citizens can't hit manhacks with the crowbar!) -//----------------------------------------------------------------------------- -ConVar sk_crowbar_lead_time( "sk_crowbar_lead_time", "0.9" ); - -int CWeaponCrowbar::WeaponMeleeAttack1Condition( float flDot, float flDist ) -{ - // Attempt to lead the target (needed because citizens can't hit manhacks with the crowbar!) - CAI_BaseNPC *pNPC = GetOwner()->MyNPCPointer(); - CBaseEntity *pEnemy = pNPC->GetEnemy(); - if (!pEnemy) - return COND_NONE; - - Vector vecVelocity; - vecVelocity = pEnemy->GetSmoothedVelocity( ); - - // Project where the enemy will be in a little while - float dt = sk_crowbar_lead_time.GetFloat(); - dt += random->RandomFloat( -0.3f, 0.2f ); - if ( dt < 0.0f ) - dt = 0.0f; - - Vector vecExtrapolatedPos; - VectorMA( pEnemy->WorldSpaceCenter(), dt, vecVelocity, vecExtrapolatedPos ); - - Vector vecDelta; - VectorSubtract( vecExtrapolatedPos, pNPC->WorldSpaceCenter(), vecDelta ); - - if ( fabs( vecDelta.z ) > 70 ) - { - return COND_TOO_FAR_TO_ATTACK; - } - - Vector vecForward = pNPC->BodyDirection2D( ); - vecDelta.z = 0.0f; - float flExtrapolatedDist = Vector2DNormalize( vecDelta.AsVector2D() ); - if ((flDist > 64) && (flExtrapolatedDist > 64)) - { - return COND_TOO_FAR_TO_ATTACK; - } - - float flExtrapolatedDot = DotProduct2D( vecDelta.AsVector2D(), vecForward.AsVector2D() ); - if ((flDot < 0.7) && (flExtrapolatedDot < 0.7)) - { - return COND_NOT_FACING_ATTACK; - } - - return COND_CAN_MELEE_ATTACK1; -} - -#endif - - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CWeaponCrowbar::Drop( const Vector &vecVelocity ) -{ -#ifndef CLIENT_DLL - UTIL_Remove( this ); -#endif -} \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: Crowbar - an old favorite +// +// $NoKeywords: $ +//=============================================================================// + +#include "cbase.h" +#include "weapon_hl2mpbasehlmpcombatweapon.h" +#include "gamerules.h" +#include "ammodef.h" +#include "mathlib.h" +#include "in_buttons.h" +#include "weapon_hl2mpbasebasebludgeon.h" +#include "vstdlib/random.h" +#include "npcevent.h" + +#if defined( CLIENT_DLL ) + #include "c_hl2mp_player.h" +#else + #include "hl2mp_player.h" + #include "ai_basenpc.h" +#endif + + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +#define CROWBAR_RANGE 75.0f +#define CROWBAR_REFIRE 0.6f + + +#ifdef CLIENT_DLL +#define CWeaponCrowbar C_WeaponCrowbar +#endif + +//----------------------------------------------------------------------------- +// CWeaponCrowbar +//----------------------------------------------------------------------------- + +class CWeaponCrowbar : public CBaseHL2MPBludgeonWeapon +{ +public: + DECLARE_CLASS( CWeaponCrowbar, CBaseHL2MPBludgeonWeapon ); + + DECLARE_NETWORKCLASS(); + DECLARE_PREDICTABLE(); + +#ifndef CLIENT_DLL + DECLARE_ACTTABLE(); +#endif + + CWeaponCrowbar(); + + float GetRange( void ) { return CROWBAR_RANGE; } + float GetFireRate( void ) { return CROWBAR_REFIRE; } + + void AddViewKick( void ); + float GetDamageForActivity( Activity hitActivity ); + void SecondaryAttack( void ) { return; } + + void Drop( const Vector &vecVelocity ); + + + // Animation event +#ifndef CLIENT_DLL + virtual void Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatCharacter *pOperator ); + void HandleAnimEventMeleeHit( animevent_t *pEvent, CBaseCombatCharacter *pOperator ); + int WeaponMeleeAttack1Condition( float flDot, float flDist ); +#endif + + CWeaponCrowbar( const CWeaponCrowbar & ); + +private: + +}; + +//----------------------------------------------------------------------------- +// CWeaponCrowbar +//----------------------------------------------------------------------------- + +IMPLEMENT_NETWORKCLASS_ALIASED( WeaponCrowbar, DT_WeaponCrowbar ) + +BEGIN_NETWORK_TABLE( CWeaponCrowbar, DT_WeaponCrowbar ) +END_NETWORK_TABLE() + +BEGIN_PREDICTION_DATA( CWeaponCrowbar ) +END_PREDICTION_DATA() + +LINK_ENTITY_TO_CLASS( weapon_crowbar, CWeaponCrowbar ); +PRECACHE_WEAPON_REGISTER( weapon_crowbar ); + +#ifndef CLIENT_DLL + +acttable_t CWeaponCrowbar::m_acttable[] = +{ + { ACT_RANGE_ATTACK1, ACT_RANGE_ATTACK_SLAM, true }, + { ACT_HL2MP_IDLE, ACT_HL2MP_IDLE_MELEE, false }, + { ACT_HL2MP_RUN, ACT_HL2MP_RUN_MELEE, false }, + { ACT_HL2MP_IDLE_CROUCH, ACT_HL2MP_IDLE_CROUCH_MELEE, false }, + { ACT_HL2MP_WALK_CROUCH, ACT_HL2MP_WALK_CROUCH_MELEE, false }, + { ACT_HL2MP_GESTURE_RANGE_ATTACK, ACT_HL2MP_GESTURE_RANGE_ATTACK_MELEE, false }, + { ACT_HL2MP_GESTURE_RELOAD, ACT_HL2MP_GESTURE_RELOAD_MELEE, false }, + { ACT_HL2MP_JUMP, ACT_HL2MP_JUMP_MELEE, false }, +}; + +IMPLEMENT_ACTTABLE(CWeaponCrowbar); + +#endif + +//----------------------------------------------------------------------------- +// Constructor +//----------------------------------------------------------------------------- +CWeaponCrowbar::CWeaponCrowbar( void ) +{ +} + +//----------------------------------------------------------------------------- +// Purpose: Get the damage amount for the animation we're doing +// Input : hitActivity - currently played activity +// Output : Damage amount +//----------------------------------------------------------------------------- +float CWeaponCrowbar::GetDamageForActivity( Activity hitActivity ) +{ + return 25.0f; +} + +//----------------------------------------------------------------------------- +// Purpose: Add in a view kick for this weapon +//----------------------------------------------------------------------------- +void CWeaponCrowbar::AddViewKick( void ) +{ + CBasePlayer *pPlayer = ToBasePlayer( GetOwner() ); + + if ( pPlayer == NULL ) + return; + + QAngle punchAng; + + punchAng.x = random->RandomFloat( 1.0f, 2.0f ); + punchAng.y = random->RandomFloat( -2.0f, -1.0f ); + punchAng.z = 0.0f; + + pPlayer->ViewPunch( punchAng ); +} + + +#ifndef CLIENT_DLL +//----------------------------------------------------------------------------- +// Animation event handlers +//----------------------------------------------------------------------------- +void CWeaponCrowbar::HandleAnimEventMeleeHit( animevent_t *pEvent, CBaseCombatCharacter *pOperator ) +{ + // Trace up or down based on where the enemy is... + // But only if we're basically facing that direction + Vector vecDirection; + AngleVectors( GetAbsAngles(), &vecDirection ); + + Vector vecEnd; + VectorMA( pOperator->Weapon_ShootPosition(), 50, vecDirection, vecEnd ); + CBaseEntity *pHurt = pOperator->CheckTraceHullAttack( pOperator->Weapon_ShootPosition(), vecEnd, + Vector(-16,-16,-16), Vector(36,36,36), static_cast(GetDamageForActivity( GetActivity() )), DMG_CLUB, 0.75 ); + + // did I hit someone? + if ( pHurt ) + { + // play sound + WeaponSound( MELEE_HIT ); + + // Fake a trace impact, so the effects work out like a player's crowbaw + trace_t traceHit; + UTIL_TraceLine( pOperator->Weapon_ShootPosition(), pHurt->GetAbsOrigin(), MASK_SHOT_HULL, pOperator, COLLISION_GROUP_NONE, &traceHit ); + ImpactEffect( traceHit ); + } + else + { + WeaponSound( MELEE_MISS ); + } +} + + +//----------------------------------------------------------------------------- +// Animation event +//----------------------------------------------------------------------------- +void CWeaponCrowbar::Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatCharacter *pOperator ) +{ + switch( pEvent->event ) + { + case EVENT_WEAPON_MELEE_HIT: + HandleAnimEventMeleeHit( pEvent, pOperator ); + break; + + default: + BaseClass::Operator_HandleAnimEvent( pEvent, pOperator ); + break; + } +} + +//----------------------------------------------------------------------------- +// Attempt to lead the target (needed because citizens can't hit manhacks with the crowbar!) +//----------------------------------------------------------------------------- +ConVar sk_crowbar_lead_time( "sk_crowbar_lead_time", "0.9" ); + +int CWeaponCrowbar::WeaponMeleeAttack1Condition( float flDot, float flDist ) +{ + // Attempt to lead the target (needed because citizens can't hit manhacks with the crowbar!) + CAI_BaseNPC *pNPC = GetOwner()->MyNPCPointer(); + CBaseEntity *pEnemy = pNPC->GetEnemy(); + if (!pEnemy) + return COND_NONE; + + Vector vecVelocity; + vecVelocity = pEnemy->GetSmoothedVelocity( ); + + // Project where the enemy will be in a little while + float dt = sk_crowbar_lead_time.GetFloat(); + dt += random->RandomFloat( -0.3f, 0.2f ); + if ( dt < 0.0f ) + dt = 0.0f; + + Vector vecExtrapolatedPos; + VectorMA( pEnemy->WorldSpaceCenter(), dt, vecVelocity, vecExtrapolatedPos ); + + Vector vecDelta; + VectorSubtract( vecExtrapolatedPos, pNPC->WorldSpaceCenter(), vecDelta ); + + if ( fabs( vecDelta.z ) > 70 ) + { + return COND_TOO_FAR_TO_ATTACK; + } + + Vector vecForward = pNPC->BodyDirection2D( ); + vecDelta.z = 0.0f; + float flExtrapolatedDist = Vector2DNormalize( vecDelta.AsVector2D() ); + if ((flDist > 64) && (flExtrapolatedDist > 64)) + { + return COND_TOO_FAR_TO_ATTACK; + } + + float flExtrapolatedDot = DotProduct2D( vecDelta.AsVector2D(), vecForward.AsVector2D() ); + if ((flDot < 0.7) && (flExtrapolatedDot < 0.7)) + { + return COND_NOT_FACING_ATTACK; + } + + return COND_CAN_MELEE_ATTACK1; +} + +#endif + + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CWeaponCrowbar::Drop( const Vector &vecVelocity ) +{ +#ifndef CLIENT_DLL + UTIL_Remove( this ); +#endif +} diff --git a/game_shared/hl2mp/weapon_hl2mpbase.cpp b/game_shared/hl2mp/weapon_hl2mpbase.cpp index 1cec0965..45d2f8e2 100644 --- a/game_shared/hl2mp/weapon_hl2mpbase.cpp +++ b/game_shared/hl2mp/weapon_hl2mpbase.cpp @@ -45,7 +45,7 @@ bool IsAmmoType( int iAmmoType, const char *pAmmoName ) return GetAmmoDef()->Index( pAmmoName ) == iAmmoType; } -static const char * s_WeaponAliasInfo[] = +/*static const char * s_WeaponAliasInfo[] = { "none", // WEAPON_NONE = 0, @@ -53,7 +53,7 @@ static const char * s_WeaponAliasInfo[] = "shotgun", //WEAPON_AMERKNIFE, NULL, // end of list marker -}; +};*/ // ----------------------------------------------------------------------------- // diff --git a/game_shared/hl2mp/weapon_hl2mpbasebasebludgeon.cpp b/game_shared/hl2mp/weapon_hl2mpbasebasebludgeon.cpp index e13e3c8c..812bc4a5 100644 --- a/game_shared/hl2mp/weapon_hl2mpbasebasebludgeon.cpp +++ b/game_shared/hl2mp/weapon_hl2mpbasebasebludgeon.cpp @@ -361,3 +361,4 @@ void CBaseHL2MPBludgeonWeapon::Swing( int bIsSecondary ) m_flNextPrimaryAttack = gpGlobals->curtime + GetFireRate(); m_flNextSecondaryAttack = gpGlobals->curtime + SequenceDuration(); } + diff --git a/game_shared/hl2mp/weapon_hl2mpbasebasebludgeon.h b/game_shared/hl2mp/weapon_hl2mpbasebasebludgeon.h index e19bffd6..b4b3301e 100644 --- a/game_shared/hl2mp/weapon_hl2mpbasebasebludgeon.h +++ b/game_shared/hl2mp/weapon_hl2mpbasebasebludgeon.h @@ -1,66 +1,66 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: The class from which all bludgeon melee -// weapons are derived. -// -// $Workfile: $ -// $Date: $ -// $NoKeywords: $ -//=============================================================================// - -#include "weapon_hl2mpbasehlmpcombatweapon.h" - -#ifndef BASEBLUDGEONWEAPON_H -#define BASEBLUDGEONWEAPON_H - -#ifdef _WIN32 -#pragma once -#endif - - -#if defined( CLIENT_DLL ) -#define CBaseHL2MPBludgeonWeapon C_BaseHL2MPBludgeonWeapon -#endif - -//========================================================= -// CBaseHLBludgeonWeapon -//========================================================= -class CBaseHL2MPBludgeonWeapon : public CBaseHL2MPCombatWeapon -{ - DECLARE_CLASS( CBaseHL2MPBludgeonWeapon, CBaseHL2MPCombatWeapon ); -public: - CBaseHL2MPBludgeonWeapon(); - - DECLARE_NETWORKCLASS(); - DECLARE_PREDICTABLE(); - - virtual void Spawn( void ); - virtual void Precache( void ); - - //Attack functions - virtual void PrimaryAttack( void ); - virtual void SecondaryAttack( void ); - - virtual void ItemPostFrame( void ); - - //Functions to select animation sequences - virtual Activity GetPrimaryAttackActivity( void ) { return ACT_VM_HITCENTER; } - virtual Activity GetSecondaryAttackActivity( void ) { return ACT_VM_HITCENTER2; } - - virtual float GetFireRate( void ) { return 0.2f; } - virtual float GetRange( void ) { return 32.0f; } - virtual float GetDamageForActivity( Activity hitActivity ) { return 1.0f; } - - CBaseHL2MPBludgeonWeapon( const CBaseHL2MPBludgeonWeapon & ); - -protected: - virtual void ImpactEffect( trace_t &trace ); - -private: - bool ImpactWater( const Vector &start, const Vector &end ); - void Swing( int bIsSecondary ); - void Hit( trace_t &traceHit, Activity nHitActivity ); - Activity ChooseIntersectionPointAndActivity( trace_t &hitTrace, const Vector &mins, const Vector &maxs, CBasePlayer *pOwner ); -}; - -#endif \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: The class from which all bludgeon melee +// weapons are derived. +// +// $Workfile: $ +// $Date: $ +// $NoKeywords: $ +//=============================================================================// + +#include "weapon_hl2mpbasehlmpcombatweapon.h" + +#ifndef BASEBLUDGEONWEAPON_H +#define BASEBLUDGEONWEAPON_H + +#ifdef _WIN32 +#pragma once +#endif + + +#if defined( CLIENT_DLL ) +#define CBaseHL2MPBludgeonWeapon C_BaseHL2MPBludgeonWeapon +#endif + +//========================================================= +// CBaseHLBludgeonWeapon +//========================================================= +class CBaseHL2MPBludgeonWeapon : public CBaseHL2MPCombatWeapon +{ + DECLARE_CLASS( CBaseHL2MPBludgeonWeapon, CBaseHL2MPCombatWeapon ); +public: + CBaseHL2MPBludgeonWeapon(); + + DECLARE_NETWORKCLASS(); + DECLARE_PREDICTABLE(); + + virtual void Spawn( void ); + virtual void Precache( void ); + + //Attack functions + virtual void PrimaryAttack( void ); + virtual void SecondaryAttack( void ); + + virtual void ItemPostFrame( void ); + + //Functions to select animation sequences + virtual Activity GetPrimaryAttackActivity( void ) { return ACT_VM_HITCENTER; } + virtual Activity GetSecondaryAttackActivity( void ) { return ACT_VM_HITCENTER2; } + + virtual float GetFireRate( void ) { return 0.2f; } + virtual float GetRange( void ) { return 32.0f; } + virtual float GetDamageForActivity( Activity hitActivity ) { return 1.0f; } + + CBaseHL2MPBludgeonWeapon( const CBaseHL2MPBludgeonWeapon & ); + +protected: + virtual void ImpactEffect( trace_t &trace ); + +private: + bool ImpactWater( const Vector &start, const Vector &end ); + void Swing( int bIsSecondary ); + void Hit( trace_t &traceHit, Activity nHitActivity ); + Activity ChooseIntersectionPointAndActivity( trace_t &hitTrace, const Vector &mins, const Vector &maxs, CBasePlayer *pOwner ); +}; + +#endif diff --git a/game_shared/hl2mp/weapon_hl2mpbasehlmpcombatweapon.cpp b/game_shared/hl2mp/weapon_hl2mpbasehlmpcombatweapon.cpp index 5a23d029..b69e314a 100644 --- a/game_shared/hl2mp/weapon_hl2mpbasehlmpcombatweapon.cpp +++ b/game_shared/hl2mp/weapon_hl2mpbasehlmpcombatweapon.cpp @@ -1,416 +1,416 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -//=============================================================================// - -#include "cbase.h" -#include "weapon_hl2mpbasehlmpcombatweapon.h" - -#include "hl2mp_player_shared.h" - -// memdbgon must be the last include file in a .cpp file!!! -#include "tier0/memdbgon.h" - -LINK_ENTITY_TO_CLASS( basehl2mpcombatweapon, CBaseHL2MPCombatWeapon ); - -IMPLEMENT_NETWORKCLASS_ALIASED( BaseHL2MPCombatWeapon , DT_BaseHL2MPCombatWeapon ) - -BEGIN_NETWORK_TABLE( CBaseHL2MPCombatWeapon , DT_BaseHL2MPCombatWeapon ) -#if !defined( CLIENT_DLL ) -// SendPropInt( SENDINFO( m_bReflectViewModelAnimations ), 1, SPROP_UNSIGNED ), -#else -// RecvPropInt( RECVINFO( m_bReflectViewModelAnimations ) ), -#endif -END_NETWORK_TABLE() - - -#if !defined( CLIENT_DLL ) - -#include "globalstate.h" - -//--------------------------------------------------------- -// Save/Restore -//--------------------------------------------------------- -BEGIN_DATADESC( CBaseHL2MPCombatWeapon ) - - DEFINE_FIELD( m_bLowered, FIELD_BOOLEAN ), - DEFINE_FIELD( m_flRaiseTime, FIELD_TIME ), - DEFINE_FIELD( m_flHolsterTime, FIELD_TIME ), - -END_DATADESC() - -#endif - -BEGIN_PREDICTION_DATA( CBaseHL2MPCombatWeapon ) -END_PREDICTION_DATA() - -extern ConVar sk_auto_reload_time; - -CBaseHL2MPCombatWeapon::CBaseHL2MPCombatWeapon( void ) -{ - -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CBaseHL2MPCombatWeapon::ItemHolsterFrame( void ) -{ - BaseClass::ItemHolsterFrame(); - - // Must be player held - if ( GetOwner() && GetOwner()->IsPlayer() == false ) - return; - - // We can't be active - if ( GetOwner()->GetActiveWeapon() == this ) - return; - - // If it's been longer than three seconds, reload - if ( ( gpGlobals->curtime - m_flHolsterTime ) > sk_auto_reload_time.GetFloat() ) - { - // Just load the clip with no animations - FinishReload(); - m_flHolsterTime = gpGlobals->curtime; - } -} - -//----------------------------------------------------------------------------- -// Purpose: Drops the weapon into a lowered pose -// Output : Returns true on success, false on failure. -//----------------------------------------------------------------------------- -bool CBaseHL2MPCombatWeapon::Lower( void ) -{ - //Don't bother if we don't have the animation - if ( SelectWeightedSequence( ACT_VM_IDLE_LOWERED ) == ACTIVITY_NOT_AVAILABLE ) - return false; - - m_bLowered = true; - return true; -} - -//----------------------------------------------------------------------------- -// Purpose: Brings the weapon up to the ready position -// Output : Returns true on success, false on failure. -//----------------------------------------------------------------------------- -bool CBaseHL2MPCombatWeapon::Ready( void ) -{ - //Don't bother if we don't have the animation - if ( SelectWeightedSequence( ACT_VM_LOWERED_TO_IDLE ) == ACTIVITY_NOT_AVAILABLE ) - return false; - - m_bLowered = false; - m_flRaiseTime = gpGlobals->curtime + 0.5f; - return true; -} - -//----------------------------------------------------------------------------- -// Purpose: -// Output : Returns true on success, false on failure. -//----------------------------------------------------------------------------- -bool CBaseHL2MPCombatWeapon::Deploy( void ) -{ - // If we should be lowered, deploy in the lowered position - // We have to ask the player if the last time it checked, the weapon was lowered - if ( GetOwner() && GetOwner()->IsPlayer() ) - { - CHL2MP_Player *pPlayer = assert_cast( GetOwner() ); - if ( pPlayer->IsWeaponLowered() ) - { - if ( SelectWeightedSequence( ACT_VM_IDLE_LOWERED ) != ACTIVITY_NOT_AVAILABLE ) - { - if ( DefaultDeploy( (char*)GetViewModel(), (char*)GetWorldModel(), ACT_VM_IDLE_LOWERED, (char*)GetAnimPrefix() ) ) - { - m_bLowered = true; - - // Stomp the next attack time to fix the fact that the lower idles are long - pPlayer->SetNextAttack( gpGlobals->curtime + 1.0 ); - m_flNextPrimaryAttack = gpGlobals->curtime + 1.0; - m_flNextSecondaryAttack = gpGlobals->curtime + 1.0; - return true; - } - } - } - } - - m_bLowered = false; - return BaseClass::Deploy(); -} - -//----------------------------------------------------------------------------- -// Purpose: -// Output : Returns true on success, false on failure. -//----------------------------------------------------------------------------- -bool CBaseHL2MPCombatWeapon::Holster( CBaseCombatWeapon *pSwitchingTo ) -{ - if ( BaseClass::Holster( pSwitchingTo ) ) - { - SetWeaponVisible( false ); - m_flHolsterTime = gpGlobals->curtime; - return true; - } - - return false; -} - -//----------------------------------------------------------------------------- -// Purpose: -// Output : Returns true on success, false on failure. -//----------------------------------------------------------------------------- -bool CBaseHL2MPCombatWeapon::WeaponShouldBeLowered( void ) -{ - // Can't be in the middle of another animation - if ( GetIdealActivity() != ACT_VM_IDLE_LOWERED && GetIdealActivity() != ACT_VM_IDLE && - GetIdealActivity() != ACT_VM_IDLE_TO_LOWERED && GetIdealActivity() != ACT_VM_LOWERED_TO_IDLE ) - return false; - - if ( m_bLowered ) - return true; - -#if !defined( CLIENT_DLL ) - - if ( GlobalEntity_GetState( "friendly_encounter" ) == GLOBAL_ON ) - return true; - -#endif - - return false; -} - -//----------------------------------------------------------------------------- -// Purpose: Allows the weapon to choose proper weapon idle animation -//----------------------------------------------------------------------------- -void CBaseHL2MPCombatWeapon::WeaponIdle( void ) -{ - //See if we should idle high or low - if ( WeaponShouldBeLowered() ) - { - // Move to lowered position if we're not there yet - if ( GetActivity() != ACT_VM_IDLE_LOWERED && GetActivity() != ACT_VM_IDLE_TO_LOWERED - && GetActivity() != ACT_TRANSITION ) - { - SendWeaponAnim( ACT_VM_IDLE_LOWERED ); - } - else if ( HasWeaponIdleTimeElapsed() ) - { - // Keep idling low - SendWeaponAnim( ACT_VM_IDLE_LOWERED ); - } - } - else - { - // See if we need to raise immediately - if ( m_flRaiseTime < gpGlobals->curtime && GetActivity() == ACT_VM_IDLE_LOWERED ) - { - SendWeaponAnim( ACT_VM_IDLE ); - } - else if ( HasWeaponIdleTimeElapsed() ) - { - SendWeaponAnim( ACT_VM_IDLE ); - } - } -} - -#if defined( CLIENT_DLL ) - -#define HL2_BOB_CYCLE_MIN 1.0f -#define HL2_BOB_CYCLE_MAX 0.45f -#define HL2_BOB 0.002f -#define HL2_BOB_UP 0.5f - -extern float g_lateralBob; -extern float g_verticalBob; - -static ConVar cl_bobcycle( "cl_bobcycle","0.8" ); -static ConVar cl_bob( "cl_bob","0.002" ); -static ConVar cl_bobup( "cl_bobup","0.5" ); - -// Register these cvars if needed for easy tweaking -static ConVar v_iyaw_cycle( "v_iyaw_cycle", "2", FCVAR_REPLICATED | FCVAR_CHEAT ); -static ConVar v_iroll_cycle( "v_iroll_cycle", "0.5", FCVAR_REPLICATED | FCVAR_CHEAT ); -static ConVar v_ipitch_cycle( "v_ipitch_cycle", "1", FCVAR_REPLICATED | FCVAR_CHEAT ); -static ConVar v_iyaw_level( "v_iyaw_level", "0.3", FCVAR_REPLICATED | FCVAR_CHEAT ); -static ConVar v_iroll_level( "v_iroll_level", "0.1", FCVAR_REPLICATED | FCVAR_CHEAT ); -static ConVar v_ipitch_level( "v_ipitch_level", "0.3", FCVAR_REPLICATED | FCVAR_CHEAT ); - -//----------------------------------------------------------------------------- -// Purpose: -// Output : float -//----------------------------------------------------------------------------- -float CBaseHL2MPCombatWeapon::CalcViewmodelBob( void ) -{ - static float bobtime; - static float lastbobtime; - float cycle; - - CBasePlayer *player = ToBasePlayer( GetOwner() ); - //Assert( player ); - - //NOTENOTE: For now, let this cycle continue when in the air, because it snaps badly without it - - if ( ( !gpGlobals->frametime ) || ( player == NULL ) ) - { - //NOTENOTE: We don't use this return value in our case (need to restructure the calculation function setup!) - return 0.0f;// just use old value - } - - //Find the speed of the player - float speed = player->GetLocalVelocity().Length2D(); - - //FIXME: This maximum speed value must come from the server. - // MaxSpeed() is not sufficient for dealing with sprinting - jdw - - speed = clamp( speed, -320, 320 ); - - float bob_offset = RemapVal( speed, 0, 320, 0.0f, 1.0f ); - - bobtime += ( gpGlobals->curtime - lastbobtime ) * bob_offset; - lastbobtime = gpGlobals->curtime; - - //Calculate the vertical bob - cycle = bobtime - (int)(bobtime/HL2_BOB_CYCLE_MAX)*HL2_BOB_CYCLE_MAX; - cycle /= HL2_BOB_CYCLE_MAX; - - if ( cycle < HL2_BOB_UP ) - { - cycle = M_PI * cycle / HL2_BOB_UP; - } - else - { - cycle = M_PI + M_PI*(cycle-HL2_BOB_UP)/(1.0 - HL2_BOB_UP); - } - - g_verticalBob = speed*0.005f; - g_verticalBob = g_verticalBob*0.3 + g_verticalBob*0.7*sin(cycle); - - g_verticalBob = clamp( g_verticalBob, -7.0f, 4.0f ); - - //Calculate the lateral bob - cycle = bobtime - (int)(bobtime/HL2_BOB_CYCLE_MAX*2)*HL2_BOB_CYCLE_MAX*2; - cycle /= HL2_BOB_CYCLE_MAX*2; - - if ( cycle < HL2_BOB_UP ) - { - cycle = M_PI * cycle / HL2_BOB_UP; - } - else - { - cycle = M_PI + M_PI*(cycle-HL2_BOB_UP)/(1.0 - HL2_BOB_UP); - } - - g_lateralBob = speed*0.005f; - g_lateralBob = g_lateralBob*0.3 + g_lateralBob*0.7*sin(cycle); - g_lateralBob = clamp( g_lateralBob, -7.0f, 4.0f ); - - //NOTENOTE: We don't use this return value in our case (need to restructure the calculation function setup!) - return 0.0f; -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : &origin - -// &angles - -// viewmodelindex - -//----------------------------------------------------------------------------- -void CBaseHL2MPCombatWeapon::AddViewmodelBob( CBaseViewModel *viewmodel, Vector &origin, QAngle &angles ) -{ - Vector forward, right; - AngleVectors( angles, &forward, &right, NULL ); - - CalcViewmodelBob(); - - // Apply bob, but scaled down to 40% - VectorMA( origin, g_verticalBob * 0.1f, forward, origin ); - - // Z bob a bit more - origin[2] += g_verticalBob * 0.1f; - - // bob the angles - angles[ ROLL ] += g_verticalBob * 0.5f; - angles[ PITCH ] -= g_verticalBob * 0.4f; - - angles[ YAW ] -= g_lateralBob * 0.3f; - - VectorMA( origin, g_lateralBob * 0.8f, right, origin ); -} - -//----------------------------------------------------------------------------- -Vector CBaseHL2MPCombatWeapon::GetBulletSpread( WeaponProficiency_t proficiency ) -{ - return BaseClass::GetBulletSpread( proficiency ); -} - -//----------------------------------------------------------------------------- -float CBaseHL2MPCombatWeapon::GetSpreadBias( WeaponProficiency_t proficiency ) -{ - return BaseClass::GetSpreadBias( proficiency ); -} -//----------------------------------------------------------------------------- - -const WeaponProficiencyInfo_t *CBaseHL2MPCombatWeapon::GetProficiencyValues() -{ - return NULL; -} - -#else - -// Server stubs -float CBaseHL2MPCombatWeapon::CalcViewmodelBob( void ) -{ - return 0.0f; -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : &origin - -// &angles - -// viewmodelindex - -//----------------------------------------------------------------------------- -void CBaseHL2MPCombatWeapon::AddViewmodelBob( CBaseViewModel *viewmodel, Vector &origin, QAngle &angles ) -{ -} - - -//----------------------------------------------------------------------------- -Vector CBaseHL2MPCombatWeapon::GetBulletSpread( WeaponProficiency_t proficiency ) -{ - Vector baseSpread = BaseClass::GetBulletSpread( proficiency ); - - const WeaponProficiencyInfo_t *pProficiencyValues = GetProficiencyValues(); - float flModifier = (pProficiencyValues)[ proficiency ].spreadscale; - return ( baseSpread * flModifier ); -} - -//----------------------------------------------------------------------------- -float CBaseHL2MPCombatWeapon::GetSpreadBias( WeaponProficiency_t proficiency ) -{ - const WeaponProficiencyInfo_t *pProficiencyValues = GetProficiencyValues(); - return (pProficiencyValues)[ proficiency ].bias; -} - -//----------------------------------------------------------------------------- -const WeaponProficiencyInfo_t *CBaseHL2MPCombatWeapon::GetProficiencyValues() -{ - return GetDefaultProficiencyValues(); -} - -//----------------------------------------------------------------------------- -const WeaponProficiencyInfo_t *CBaseHL2MPCombatWeapon::GetDefaultProficiencyValues() -{ - // Weapon proficiency table. Keep this in sync with WeaponProficiency_t enum in the header!! - static WeaponProficiencyInfo_t g_BaseWeaponProficiencyTable[] = - { - { 2.50, 1.0 }, - { 2.00, 1.0 }, - { 1.50, 1.0 }, - { 1.25, 1.0 }, - { 1.00, 1.0 }, - }; - - COMPILE_TIME_ASSERT( ARRAYSIZE(g_BaseWeaponProficiencyTable) == WEAPON_PROFICIENCY_PERFECT + 1); - - return g_BaseWeaponProficiencyTable; -} - -#endif \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "cbase.h" +#include "weapon_hl2mpbasehlmpcombatweapon.h" + +#include "hl2mp_player_shared.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +LINK_ENTITY_TO_CLASS( basehl2mpcombatweapon, CBaseHL2MPCombatWeapon ); + +IMPLEMENT_NETWORKCLASS_ALIASED( BaseHL2MPCombatWeapon , DT_BaseHL2MPCombatWeapon ) + +BEGIN_NETWORK_TABLE( CBaseHL2MPCombatWeapon , DT_BaseHL2MPCombatWeapon ) +#if !defined( CLIENT_DLL ) +// SendPropInt( SENDINFO( m_bReflectViewModelAnimations ), 1, SPROP_UNSIGNED ), +#else +// RecvPropInt( RECVINFO( m_bReflectViewModelAnimations ) ), +#endif +END_NETWORK_TABLE() + + +#if !defined( CLIENT_DLL ) + +#include "globalstate.h" + +//--------------------------------------------------------- +// Save/Restore +//--------------------------------------------------------- +BEGIN_DATADESC( CBaseHL2MPCombatWeapon ) + + DEFINE_FIELD( m_bLowered, FIELD_BOOLEAN ), + DEFINE_FIELD( m_flRaiseTime, FIELD_TIME ), + DEFINE_FIELD( m_flHolsterTime, FIELD_TIME ), + +END_DATADESC() + +#endif + +BEGIN_PREDICTION_DATA( CBaseHL2MPCombatWeapon ) +END_PREDICTION_DATA() + +extern ConVar sk_auto_reload_time; + +CBaseHL2MPCombatWeapon::CBaseHL2MPCombatWeapon( void ) +{ + +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBaseHL2MPCombatWeapon::ItemHolsterFrame( void ) +{ + BaseClass::ItemHolsterFrame(); + + // Must be player held + if ( GetOwner() && GetOwner()->IsPlayer() == false ) + return; + + // We can't be active + if ( GetOwner()->GetActiveWeapon() == this ) + return; + + // If it's been longer than three seconds, reload + if ( ( gpGlobals->curtime - m_flHolsterTime ) > sk_auto_reload_time.GetFloat() ) + { + // Just load the clip with no animations + FinishReload(); + m_flHolsterTime = gpGlobals->curtime; + } +} + +//----------------------------------------------------------------------------- +// Purpose: Drops the weapon into a lowered pose +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CBaseHL2MPCombatWeapon::Lower( void ) +{ + //Don't bother if we don't have the animation + if ( SelectWeightedSequence( ACT_VM_IDLE_LOWERED ) == ACTIVITY_NOT_AVAILABLE ) + return false; + + m_bLowered = true; + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: Brings the weapon up to the ready position +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CBaseHL2MPCombatWeapon::Ready( void ) +{ + //Don't bother if we don't have the animation + if ( SelectWeightedSequence( ACT_VM_LOWERED_TO_IDLE ) == ACTIVITY_NOT_AVAILABLE ) + return false; + + m_bLowered = false; + m_flRaiseTime = gpGlobals->curtime + 0.5f; + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CBaseHL2MPCombatWeapon::Deploy( void ) +{ + // If we should be lowered, deploy in the lowered position + // We have to ask the player if the last time it checked, the weapon was lowered + if ( GetOwner() && GetOwner()->IsPlayer() ) + { + CHL2MP_Player *pPlayer = assert_cast( GetOwner() ); + if ( pPlayer->IsWeaponLowered() ) + { + if ( SelectWeightedSequence( ACT_VM_IDLE_LOWERED ) != ACTIVITY_NOT_AVAILABLE ) + { + if ( DefaultDeploy( (char*)GetViewModel(), (char*)GetWorldModel(), ACT_VM_IDLE_LOWERED, (char*)GetAnimPrefix() ) ) + { + m_bLowered = true; + + // Stomp the next attack time to fix the fact that the lower idles are long + pPlayer->SetNextAttack( gpGlobals->curtime + 1.0 ); + m_flNextPrimaryAttack = gpGlobals->curtime + 1.0; + m_flNextSecondaryAttack = gpGlobals->curtime + 1.0; + return true; + } + } + } + } + + m_bLowered = false; + return BaseClass::Deploy(); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CBaseHL2MPCombatWeapon::Holster( CBaseCombatWeapon *pSwitchingTo ) +{ + if ( BaseClass::Holster( pSwitchingTo ) ) + { + SetWeaponVisible( false ); + m_flHolsterTime = gpGlobals->curtime; + return true; + } + + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CBaseHL2MPCombatWeapon::WeaponShouldBeLowered( void ) +{ + // Can't be in the middle of another animation + if ( GetIdealActivity() != ACT_VM_IDLE_LOWERED && GetIdealActivity() != ACT_VM_IDLE && + GetIdealActivity() != ACT_VM_IDLE_TO_LOWERED && GetIdealActivity() != ACT_VM_LOWERED_TO_IDLE ) + return false; + + if ( m_bLowered ) + return true; + +#if !defined( CLIENT_DLL ) + + if ( GlobalEntity_GetState( "friendly_encounter" ) == GLOBAL_ON ) + return true; + +#endif + + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: Allows the weapon to choose proper weapon idle animation +//----------------------------------------------------------------------------- +void CBaseHL2MPCombatWeapon::WeaponIdle( void ) +{ + //See if we should idle high or low + if ( WeaponShouldBeLowered() ) + { + // Move to lowered position if we're not there yet + if ( GetActivity() != ACT_VM_IDLE_LOWERED && GetActivity() != ACT_VM_IDLE_TO_LOWERED + && GetActivity() != ACT_TRANSITION ) + { + SendWeaponAnim( ACT_VM_IDLE_LOWERED ); + } + else if ( HasWeaponIdleTimeElapsed() ) + { + // Keep idling low + SendWeaponAnim( ACT_VM_IDLE_LOWERED ); + } + } + else + { + // See if we need to raise immediately + if ( m_flRaiseTime < gpGlobals->curtime && GetActivity() == ACT_VM_IDLE_LOWERED ) + { + SendWeaponAnim( ACT_VM_IDLE ); + } + else if ( HasWeaponIdleTimeElapsed() ) + { + SendWeaponAnim( ACT_VM_IDLE ); + } + } +} + +#if defined( CLIENT_DLL ) + +#define HL2_BOB_CYCLE_MIN 1.0f +#define HL2_BOB_CYCLE_MAX 0.45f +#define HL2_BOB 0.002f +#define HL2_BOB_UP 0.5f + +extern float g_lateralBob; +extern float g_verticalBob; + +static ConVar cl_bobcycle( "cl_bobcycle","0.8" ); +static ConVar cl_bob( "cl_bob","0.002" ); +static ConVar cl_bobup( "cl_bobup","0.5" ); + +// Register these cvars if needed for easy tweaking +static ConVar v_iyaw_cycle( "v_iyaw_cycle", "2", FCVAR_REPLICATED | FCVAR_CHEAT ); +static ConVar v_iroll_cycle( "v_iroll_cycle", "0.5", FCVAR_REPLICATED | FCVAR_CHEAT ); +static ConVar v_ipitch_cycle( "v_ipitch_cycle", "1", FCVAR_REPLICATED | FCVAR_CHEAT ); +static ConVar v_iyaw_level( "v_iyaw_level", "0.3", FCVAR_REPLICATED | FCVAR_CHEAT ); +static ConVar v_iroll_level( "v_iroll_level", "0.1", FCVAR_REPLICATED | FCVAR_CHEAT ); +static ConVar v_ipitch_level( "v_ipitch_level", "0.3", FCVAR_REPLICATED | FCVAR_CHEAT ); + +//----------------------------------------------------------------------------- +// Purpose: +// Output : float +//----------------------------------------------------------------------------- +float CBaseHL2MPCombatWeapon::CalcViewmodelBob( void ) +{ + static float bobtime; + static float lastbobtime; + float cycle; + + CBasePlayer *player = ToBasePlayer( GetOwner() ); + //Assert( player ); + + //NOTENOTE: For now, let this cycle continue when in the air, because it snaps badly without it + + if ( ( !gpGlobals->frametime ) || ( player == NULL ) ) + { + //NOTENOTE: We don't use this return value in our case (need to restructure the calculation function setup!) + return 0.0f;// just use old value + } + + //Find the speed of the player + float speed = player->GetLocalVelocity().Length2D(); + + //FIXME: This maximum speed value must come from the server. + // MaxSpeed() is not sufficient for dealing with sprinting - jdw + + speed = clamp( speed, -320, 320 ); + + float bob_offset = RemapVal( speed, 0, 320, 0.0f, 1.0f ); + + bobtime += ( gpGlobals->curtime - lastbobtime ) * bob_offset; + lastbobtime = gpGlobals->curtime; + + //Calculate the vertical bob + cycle = bobtime - (int)(bobtime/HL2_BOB_CYCLE_MAX)*HL2_BOB_CYCLE_MAX; + cycle /= HL2_BOB_CYCLE_MAX; + + if ( cycle < HL2_BOB_UP ) + { + cycle = M_PI * cycle / HL2_BOB_UP; + } + else + { + cycle = M_PI + M_PI*(cycle-HL2_BOB_UP)/(1.0 - HL2_BOB_UP); + } + + g_verticalBob = speed*0.005f; + g_verticalBob = g_verticalBob*0.3 + g_verticalBob*0.7*sin(cycle); + + g_verticalBob = clamp( g_verticalBob, -7.0f, 4.0f ); + + //Calculate the lateral bob + cycle = bobtime - (int)(bobtime/HL2_BOB_CYCLE_MAX*2)*HL2_BOB_CYCLE_MAX*2; + cycle /= HL2_BOB_CYCLE_MAX*2; + + if ( cycle < HL2_BOB_UP ) + { + cycle = M_PI * cycle / HL2_BOB_UP; + } + else + { + cycle = M_PI + M_PI*(cycle-HL2_BOB_UP)/(1.0 - HL2_BOB_UP); + } + + g_lateralBob = speed*0.005f; + g_lateralBob = g_lateralBob*0.3 + g_lateralBob*0.7*sin(cycle); + g_lateralBob = clamp( g_lateralBob, -7.0f, 4.0f ); + + //NOTENOTE: We don't use this return value in our case (need to restructure the calculation function setup!) + return 0.0f; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : &origin - +// &angles - +// viewmodelindex - +//----------------------------------------------------------------------------- +void CBaseHL2MPCombatWeapon::AddViewmodelBob( CBaseViewModel *viewmodel, Vector &origin, QAngle &angles ) +{ + Vector forward, right; + AngleVectors( angles, &forward, &right, NULL ); + + CalcViewmodelBob(); + + // Apply bob, but scaled down to 40% + VectorMA( origin, g_verticalBob * 0.1f, forward, origin ); + + // Z bob a bit more + origin[2] += g_verticalBob * 0.1f; + + // bob the angles + angles[ ROLL ] += g_verticalBob * 0.5f; + angles[ PITCH ] -= g_verticalBob * 0.4f; + + angles[ YAW ] -= g_lateralBob * 0.3f; + + VectorMA( origin, g_lateralBob * 0.8f, right, origin ); +} + +//----------------------------------------------------------------------------- +Vector CBaseHL2MPCombatWeapon::GetBulletSpread( WeaponProficiency_t proficiency ) +{ + return BaseClass::GetBulletSpread( proficiency ); +} + +//----------------------------------------------------------------------------- +float CBaseHL2MPCombatWeapon::GetSpreadBias( WeaponProficiency_t proficiency ) +{ + return BaseClass::GetSpreadBias( proficiency ); +} +//----------------------------------------------------------------------------- + +const WeaponProficiencyInfo_t *CBaseHL2MPCombatWeapon::GetProficiencyValues() +{ + return NULL; +} + +#else + +// Server stubs +float CBaseHL2MPCombatWeapon::CalcViewmodelBob( void ) +{ + return 0.0f; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : &origin - +// &angles - +// viewmodelindex - +//----------------------------------------------------------------------------- +void CBaseHL2MPCombatWeapon::AddViewmodelBob( CBaseViewModel *viewmodel, Vector &origin, QAngle &angles ) +{ +} + + +//----------------------------------------------------------------------------- +Vector CBaseHL2MPCombatWeapon::GetBulletSpread( WeaponProficiency_t proficiency ) +{ + Vector baseSpread = BaseClass::GetBulletSpread( proficiency ); + + const WeaponProficiencyInfo_t *pProficiencyValues = GetProficiencyValues(); + float flModifier = (pProficiencyValues)[ proficiency ].spreadscale; + return ( baseSpread * flModifier ); +} + +//----------------------------------------------------------------------------- +float CBaseHL2MPCombatWeapon::GetSpreadBias( WeaponProficiency_t proficiency ) +{ + const WeaponProficiencyInfo_t *pProficiencyValues = GetProficiencyValues(); + return (pProficiencyValues)[ proficiency ].bias; +} + +//----------------------------------------------------------------------------- +const WeaponProficiencyInfo_t *CBaseHL2MPCombatWeapon::GetProficiencyValues() +{ + return GetDefaultProficiencyValues(); +} + +//----------------------------------------------------------------------------- +const WeaponProficiencyInfo_t *CBaseHL2MPCombatWeapon::GetDefaultProficiencyValues() +{ + // Weapon proficiency table. Keep this in sync with WeaponProficiency_t enum in the header!! + static WeaponProficiencyInfo_t g_BaseWeaponProficiencyTable[] = + { + { 2.50, 1.0 }, + { 2.00, 1.0 }, + { 1.50, 1.0 }, + { 1.25, 1.0 }, + { 1.00, 1.0 }, + }; + + COMPILE_TIME_ASSERT( ARRAYSIZE(g_BaseWeaponProficiencyTable) == WEAPON_PROFICIENCY_PERFECT + 1); + + return g_BaseWeaponProficiencyTable; +} + +#endif diff --git a/game_shared/hl2mp/weapon_physcannon.cpp b/game_shared/hl2mp/weapon_physcannon.cpp index 64e55d31..fcf1ed02 100644 --- a/game_shared/hl2mp/weapon_physcannon.cpp +++ b/game_shared/hl2mp/weapon_physcannon.cpp @@ -50,7 +50,7 @@ #define SPRITE_SCALE 128.0f -static const char *s_pWaitForUpgradeContext = "WaitForUpgrade"; +//static const char *s_pWaitForUpgradeContext = "WaitForUpgrade"; ConVar g_debug_physcannon( "g_debug_physcannon", "0", FCVAR_REPLICATED | FCVAR_CHEAT ); @@ -191,7 +191,7 @@ static QAngle AlignAngles( const QAngle &angles, float cosineAlignAngle ) } -static void TraceCollideAgainstBBox( const CPhysCollide *pCollide, const Vector &start, const Vector &end, const QAngle &angles, const Vector &boxOrigin, const Vector &mins, const Vector &maxs, trace_t *ptr ) +/*static void TraceCollideAgainstBBox( const CPhysCollide *pCollide, const Vector &start, const Vector &end, const QAngle &angles, const Vector &boxOrigin, const Vector &mins, const Vector &maxs, trace_t *ptr ) { physcollision->TraceBox( boxOrigin, boxOrigin + (start-end), mins, maxs, pCollide, start, angles, ptr ); @@ -202,7 +202,7 @@ static void TraceCollideAgainstBBox( const CPhysCollide *pCollide, const Vector ptr->plane.dist = -ptr->plane.dist; ptr->plane.normal *= -1; } -} +}*/ //----------------------------------------------------------------------------- // Purpose: Computes a local matrix for the player clamped to valid carry ranges diff --git a/game_shared/hl2mp/weapon_rpg.cpp b/game_shared/hl2mp/weapon_rpg.cpp index 39c2a7a3..189a235e 100644 --- a/game_shared/hl2mp/weapon_rpg.cpp +++ b/game_shared/hl2mp/weapon_rpg.cpp @@ -360,7 +360,7 @@ void CMissile::ShotDown( void ) void CMissile::DoExplosion( void ) { // Explode - ExplosionCreate( GetAbsOrigin(), GetAbsAngles(), GetOwnerEntity(), GetDamage(), GetDamage() * 2, + ExplosionCreate( GetAbsOrigin(), GetAbsAngles(), GetOwnerEntity(), static_cast(GetDamage()), static_cast(GetDamage() * 2), SF_ENVEXPLOSION_NOSPARKS | SF_ENVEXPLOSION_NODLIGHTS | SF_ENVEXPLOSION_NOSMOKE, 0.0f, this); } @@ -1037,7 +1037,7 @@ void CAPCMissile::DoExplosion( void ) else { ExplosionCreate( GetAbsOrigin(), GetAbsAngles(), GetOwnerEntity(), - APC_MISSILE_DAMAGE, 100, true, 20000 ); + static_cast(APC_MISSILE_DAMAGE), 100, true, 20000 ); } } diff --git a/game_shared/hl2mp/weapon_stunstick.cpp b/game_shared/hl2mp/weapon_stunstick.cpp index 16068e2b..47642d4a 100644 --- a/game_shared/hl2mp/weapon_stunstick.cpp +++ b/game_shared/hl2mp/weapon_stunstick.cpp @@ -1,896 +1,896 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: Stun Stick- beating stick with a zappy end -// -// $NoKeywords: $ -//=============================================================================// - -#include "cbase.h" -#include "npcevent.h" -#include "weapon_hl2mpbasebasebludgeon.h" -#include "IEffects.h" -#include "debugoverlay_shared.h" - -#ifndef CLIENT_DLL - #include "npc_metropolice.h" - #include "te_effect_dispatch.h" -#endif - -#ifdef CLIENT_DLL - - #include "iviewrender_beams.h" - #include "beam_shared.h" - #include "materialsystem/IMaterial.h" - #include "model_types.h" - #include "c_te_effect_dispatch.h" - #include "fx_quad.h" - #include "fx.h" - - extern void DrawHalo( IMaterial* pMaterial, const Vector &source, float scale, float const *color, float flHDRColorScale ); - extern void FormatViewModelAttachment( Vector &vOrigin, bool bInverse ); - -#endif - -// memdbgon must be the last include file in a .cpp file!!! -#include "tier0/memdbgon.h" - -extern ConVar metropolice_move_and_melee; - -#define STUNSTICK_RANGE 75.0f -#define STUNSTICK_REFIRE 0.8f -#define STUNSTICK_BEAM_MATERIAL "sprites/lgtning.vmt" -#define STUNSTICK_GLOW_MATERIAL "sprites/light_glow02_add" -#define STUNSTICK_GLOW_MATERIAL2 "effects/blueflare1" -#define STUNSTICK_GLOW_MATERIAL_NOZ "sprites/light_glow02_add_noz" - -#ifdef CLIENT_DLL -#define CWeaponStunStick C_WeaponStunStick -#endif - -class CWeaponStunStick : public CBaseHL2MPBludgeonWeapon -{ - DECLARE_CLASS( CWeaponStunStick, CBaseHL2MPBludgeonWeapon ); - -public: - - CWeaponStunStick(); - - DECLARE_NETWORKCLASS(); - DECLARE_PREDICTABLE(); - -#ifndef CLIENT_DLL - DECLARE_ACTTABLE(); -#endif - -#ifdef CLIENT_DLL - virtual int DrawModel( int flags ); - virtual void ClientThink( void ); - virtual void OnDataChanged( DataUpdateType_t updateType ); - virtual RenderGroup_t GetRenderGroup( void ); - virtual void ViewModelDrawn( C_BaseViewModel *pBaseViewModel ); - -#endif - - virtual void Precache(); - - void Spawn(); - - float GetRange( void ) { return STUNSTICK_RANGE; } - float GetFireRate( void ) { return STUNSTICK_REFIRE; } - - - bool Deploy( void ); - bool Holster( CBaseCombatWeapon *pSwitchingTo = NULL ); - - void Drop( const Vector &vecVelocity ); - void ImpactEffect( trace_t &traceHit ); - void SecondaryAttack( void ) {} - void SetStunState( bool state ); - bool GetStunState( void ); - -#ifndef CLIENT_DLL - void Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatCharacter *pOperator ); - int WeaponMeleeAttack1Condition( float flDot, float flDist ); -#endif - - float GetDamageForActivity( Activity hitActivity ); - - CWeaponStunStick( const CWeaponStunStick & ); - -private: - -#ifdef CLIENT_DLL - - #define NUM_BEAM_ATTACHMENTS 9 - - struct stunstickBeamInfo_t - { - int IDs[2]; // 0 - top, 1 - bottom - }; - - stunstickBeamInfo_t m_BeamAttachments[NUM_BEAM_ATTACHMENTS]; // Lookup for arc attachment points on the head of the stick - int m_BeamCenterAttachment; // "Core" of the effect (center of the head) - - void SetupAttachmentPoints( void ); - void DrawFirstPersonEffects( void ); - void DrawThirdPersonEffects( void ); - void DrawEffects( void ); - bool InSwing( void ); - - bool m_bSwungLastFrame; - - #define FADE_DURATION 0.25f - - float m_flFadeTime; - -#endif - - CNetworkVar( bool, m_bActive ); -}; - -//----------------------------------------------------------------------------- -// CWeaponStunStick -//----------------------------------------------------------------------------- -IMPLEMENT_NETWORKCLASS_ALIASED( WeaponStunStick, DT_WeaponStunStick ) - -BEGIN_NETWORK_TABLE( CWeaponStunStick, DT_WeaponStunStick ) -#ifdef CLIENT_DLL - RecvPropInt( RECVINFO( m_bActive ) ), -#else - SendPropInt( SENDINFO( m_bActive ), 1, SPROP_UNSIGNED ), -#endif - -END_NETWORK_TABLE() - -BEGIN_PREDICTION_DATA( CWeaponStunStick ) -END_PREDICTION_DATA() - -LINK_ENTITY_TO_CLASS( weapon_stunstick, CWeaponStunStick ); -PRECACHE_WEAPON_REGISTER( weapon_stunstick ); - - -#ifndef CLIENT_DLL - -acttable_t CWeaponStunStick::m_acttable[] = -{ - { ACT_RANGE_ATTACK1, ACT_RANGE_ATTACK_SLAM, true }, - { ACT_HL2MP_IDLE, ACT_HL2MP_IDLE_MELEE, false }, - { ACT_HL2MP_RUN, ACT_HL2MP_RUN_MELEE, false }, - { ACT_HL2MP_IDLE_CROUCH, ACT_HL2MP_IDLE_CROUCH_MELEE, false }, - { ACT_HL2MP_WALK_CROUCH, ACT_HL2MP_WALK_CROUCH_MELEE, false }, - { ACT_HL2MP_GESTURE_RANGE_ATTACK, ACT_HL2MP_GESTURE_RANGE_ATTACK_MELEE, false }, - { ACT_HL2MP_GESTURE_RELOAD, ACT_HL2MP_GESTURE_RELOAD_MELEE, false }, - { ACT_HL2MP_JUMP, ACT_HL2MP_JUMP_MELEE, false }, -}; - -IMPLEMENT_ACTTABLE(CWeaponStunStick); - -#endif - - -//----------------------------------------------------------------------------- -// Constructor -//----------------------------------------------------------------------------- -CWeaponStunStick::CWeaponStunStick( void ) -{ - // HACK: Don't call SetStunState because this tried to Emit a sound before - // any players are connected which is a bug - m_bActive = false; - -#ifdef CLIENT_DLL - m_bSwungLastFrame = false; - m_flFadeTime = FADE_DURATION; // Start off past the fade point -#endif -} - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -void CWeaponStunStick::Spawn() -{ - Precache(); - - BaseClass::Spawn(); - AddSolidFlags( FSOLID_NOT_STANDABLE ); -} - -void CWeaponStunStick::Precache() -{ - BaseClass::Precache(); - - PrecacheScriptSound( "Weapon_StunStick.Activate" ); - PrecacheScriptSound( "Weapon_StunStick.Deactivate" ); - - PrecacheModel( STUNSTICK_BEAM_MATERIAL ); -} - -//----------------------------------------------------------------------------- -// Purpose: Get the damage amount for the animation we're doing -// Input : hitActivity - currently played activity -// Output : Damage amount -//----------------------------------------------------------------------------- -float CWeaponStunStick::GetDamageForActivity( Activity hitActivity ) -{ - return 40.0f; -} - -//----------------------------------------------------------------------------- -// Attempt to lead the target (needed because citizens can't hit manhacks with the crowbar!) -//----------------------------------------------------------------------------- -extern ConVar sk_crowbar_lead_time; - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CWeaponStunStick::ImpactEffect( trace_t &traceHit ) -{ - -//#ifndef CLIENT_DLL - - CEffectData data; - - data.m_vNormal = traceHit.plane.normal; - data.m_vOrigin = traceHit.endpos + ( data.m_vNormal * 4.0f ); - - DispatchEffect( "StunstickImpact", data ); - -//#endif - - //FIXME: need new decals - UTIL_ImpactTrace( &traceHit, DMG_CLUB ); -} - -#ifndef CLIENT_DLL - - -int CWeaponStunStick::WeaponMeleeAttack1Condition( float flDot, float flDist ) -{ - // Attempt to lead the target (needed because citizens can't hit manhacks with the crowbar!) - CAI_BaseNPC *pNPC = GetOwner()->MyNPCPointer(); - CBaseEntity *pEnemy = pNPC->GetEnemy(); - if (!pEnemy) - return COND_NONE; - - Vector vecVelocity; - AngularImpulse angVelocity; - pEnemy->GetVelocity( &vecVelocity, &angVelocity ); - - // Project where the enemy will be in a little while, add some randomness so he doesn't always hit - float dt = sk_crowbar_lead_time.GetFloat(); - dt += random->RandomFloat( -0.3f, 0.2f ); - if ( dt < 0.0f ) - dt = 0.0f; - - Vector vecExtrapolatedPos; - VectorMA( pEnemy->WorldSpaceCenter(), dt, vecVelocity, vecExtrapolatedPos ); - - Vector vecDelta; - VectorSubtract( vecExtrapolatedPos, pNPC->WorldSpaceCenter(), vecDelta ); - - if ( fabs( vecDelta.z ) > 70 ) - { - return COND_TOO_FAR_TO_ATTACK; - } - - Vector vecForward = pNPC->BodyDirection2D( ); - vecDelta.z = 0.0f; - float flExtrapolatedDot = DotProduct2D( vecDelta.AsVector2D(), vecForward.AsVector2D() ); - if ((flDot < 0.7) && (flExtrapolatedDot < 0.7)) - { - return COND_NOT_FACING_ATTACK; - } - - float flExtrapolatedDist = Vector2DNormalize( vecDelta.AsVector2D() ); - - if( pEnemy->IsPlayer() ) - { - //Vector vecDir = pEnemy->GetSmoothedVelocity(); - //float flSpeed = VectorNormalize( vecDir ); - - // If player will be in front of me in one-half second, clock his arse. - Vector vecProjectEnemy = pEnemy->GetAbsOrigin() + (pEnemy->GetAbsVelocity() * 0.35); - Vector vecProjectMe = GetAbsOrigin(); - - if( (vecProjectMe - vecProjectEnemy).Length2D() <= 48.0f ) - { - return COND_CAN_MELEE_ATTACK1; - } - } -/* - if( metropolice_move_and_melee.GetBool() ) - { - if( pNPC->IsMoving() ) - { - flTargetDist *= 1.5f; - } - } -*/ - float flTargetDist = 48.0f; - if ((flDist > flTargetDist) && (flExtrapolatedDist > flTargetDist)) - { - return COND_TOO_FAR_TO_ATTACK; - } - - return COND_CAN_MELEE_ATTACK1; -} - - -void CWeaponStunStick::Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatCharacter *pOperator ) -{ - switch( pEvent->event ) - { - case EVENT_WEAPON_MELEE_HIT: - { - // Trace up or down based on where the enemy is... - // But only if we're basically facing that direction - Vector vecDirection; - AngleVectors( GetAbsAngles(), &vecDirection ); - - CBaseEntity *pEnemy = pOperator->MyNPCPointer() ? pOperator->MyNPCPointer()->GetEnemy() : NULL; - if ( pEnemy ) - { - Vector vecDelta; - VectorSubtract( pEnemy->WorldSpaceCenter(), pOperator->Weapon_ShootPosition(), vecDelta ); - VectorNormalize( vecDelta ); - - Vector2D vecDelta2D = vecDelta.AsVector2D(); - Vector2DNormalize( vecDelta2D ); - if ( DotProduct2D( vecDelta2D, vecDirection.AsVector2D() ) > 0.8f ) - { - vecDirection = vecDelta; - } - } - - Vector vecEnd; - VectorMA( pOperator->Weapon_ShootPosition(), 32, vecDirection, vecEnd ); - // Stretch the swing box down to catch low level physics objects - CBaseEntity *pHurt = pOperator->CheckTraceHullAttack( pOperator->Weapon_ShootPosition(), vecEnd, - Vector(-16,-16,-40), Vector(16,16,16), GetDamageForActivity( GetActivity() ), DMG_CLUB, 0.5f, false ); - - // did I hit someone? - if ( pHurt ) - { - // play sound - WeaponSound( MELEE_HIT ); - - CBasePlayer *pPlayer = ToBasePlayer( pHurt ); - - bool bFlashed = false; - - // Punch angles - if ( pPlayer != NULL && !(pPlayer->GetFlags() & FL_GODMODE) ) - { - float yawKick = random->RandomFloat( -48, -24 ); - - //Kick the player angles - pPlayer->ViewPunch( QAngle( -16, yawKick, 2 ) ); - - Vector dir = pHurt->GetAbsOrigin() - GetAbsOrigin(); - - // If the player's on my head, don't knock him up - if ( pPlayer->GetGroundEntity() == pOperator ) - { - dir = vecDirection; - dir.z = 0; - } - - VectorNormalize(dir); - - dir *= 500.0f; - - //If not on ground, then don't make them fly! - if ( !(pPlayer->GetFlags() & FL_ONGROUND ) ) - dir.z = 0.0f; - - //Push the target back - pHurt->ApplyAbsVelocityImpulse( dir ); - - if ( !bFlashed ) - { - color32 red = {128,0,0,128}; - UTIL_ScreenFade( pPlayer, red, 0.5f, 0.1f, FFADE_IN ); - } - - // Force the player to drop anyting they were holding - pPlayer->ForceDropOfCarriedPhysObjects(); - } - - // do effect? - } - else - { - WeaponSound( MELEE_MISS ); - } - } - break; - default: - BaseClass::Operator_HandleAnimEvent( pEvent, pOperator ); - break; - } -} - -#endif - -//----------------------------------------------------------------------------- -// Purpose: Sets the state of the stun stick -//----------------------------------------------------------------------------- -void CWeaponStunStick::SetStunState( bool state ) -{ - m_bActive = state; - - if ( m_bActive ) - { - //FIXME: START - Move to client-side - - Vector vecAttachment; - QAngle vecAttachmentAngles; - - GetAttachment( 1, vecAttachment, vecAttachmentAngles ); - g_pEffects->Sparks( vecAttachment ); - - //FIXME: END - Move to client-side - - EmitSound( "Weapon_StunStick.Activate" ); - } - else - { - EmitSound( "Weapon_StunStick.Deactivate" ); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -// Output : Returns true on success, false on failure. -//----------------------------------------------------------------------------- -bool CWeaponStunStick::Deploy( void ) -{ - SetStunState( true ); - - return BaseClass::Deploy(); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -bool CWeaponStunStick::Holster( CBaseCombatWeapon *pSwitchingTo ) -{ - if ( BaseClass::Holster( pSwitchingTo ) == false ) - return false; - - SetStunState( false ); - SetWeaponVisible( false ); - - return true; -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : &vecVelocity - -//----------------------------------------------------------------------------- -void CWeaponStunStick::Drop( const Vector &vecVelocity ) -{ - SetStunState( false ); - -#ifndef CLIENT_DLL - UTIL_Remove( this ); -#endif - -} - -//----------------------------------------------------------------------------- -// Purpose: -// Output : Returns true on success, false on failure. -//----------------------------------------------------------------------------- -bool CWeaponStunStick::GetStunState( void ) -{ - return m_bActive; -} - -#ifdef CLIENT_DLL - -//----------------------------------------------------------------------------- -// Purpose: Get the attachment point on a viewmodel that a base weapon is using -//----------------------------------------------------------------------------- -bool UTIL_GetWeaponAttachment( C_BaseCombatWeapon *pWeapon, int attachmentID, Vector &absOrigin, QAngle &absAngles ) -{ - // This is already correct in third-person - if ( pWeapon && pWeapon->IsCarriedByLocalPlayer() == false ) - { - return pWeapon->GetAttachment( attachmentID, absOrigin, absAngles ); - } - - // Otherwise we need to translate the attachment to the viewmodel's version and reformat it - CBasePlayer *pOwner = ToBasePlayer( pWeapon->GetOwner() ); - - if ( pOwner != NULL ) - { - int ret = pOwner->GetViewModel()->GetAttachment( attachmentID, absOrigin, absAngles ); - FormatViewModelAttachment( absOrigin, true ); - - return ret; - } - - // Wasn't found - return false; -} - -#define BEAM_ATTACH_CORE_NAME "sparkrear" - -//----------------------------------------------------------------------------- -// Purpose: Sets up the attachment point lookup for the model -//----------------------------------------------------------------------------- -void C_WeaponStunStick::SetupAttachmentPoints( void ) -{ - // Setup points for both types of views - if ( IsCarriedByLocalPlayer() ) - { - const char *szBeamAttachNamesTop[NUM_BEAM_ATTACHMENTS] = - { - "spark1a","spark2a","spark3a","spark4a", - "spark5a","spark6a","spark7a","spark8a", - "spark9a", - }; - - const char *szBeamAttachNamesBottom[NUM_BEAM_ATTACHMENTS] = - { - "spark1b","spark2b","spark3b","spark4b", - "spark5b","spark6b","spark7b","spark8b", - "spark9b", - }; - - // Lookup and store all connections - for ( int i = 0; i < NUM_BEAM_ATTACHMENTS; i++ ) - { - m_BeamAttachments[i].IDs[0] = LookupAttachment( szBeamAttachNamesTop[i] ); - m_BeamAttachments[i].IDs[1] = LookupAttachment( szBeamAttachNamesBottom[i] ); - } - - // Setup the center beam point - m_BeamCenterAttachment = LookupAttachment( BEAM_ATTACH_CORE_NAME ); - } - else - { - // Setup the center beam point - m_BeamCenterAttachment = 1; - } -} - -//----------------------------------------------------------------------------- -// Purpose: Draws the stunstick model (with extra effects) -//----------------------------------------------------------------------------- -int C_WeaponStunStick::DrawModel( int flags ) -{ - if ( ShouldDraw() == false ) - return 0; - - // Only render these on the transparent pass - if ( flags & STUDIO_TRANSPARENCY ) - { - DrawEffects(); - return 1; - } - - return BaseClass::DrawModel( flags ); -} - -//----------------------------------------------------------------------------- -// Purpose: Randomly adds extra effects -//----------------------------------------------------------------------------- -void C_WeaponStunStick::ClientThink( void ) -{ - if ( InSwing() == false ) - { - if ( m_bSwungLastFrame ) - { - // Start fading - m_flFadeTime = gpGlobals->curtime; - m_bSwungLastFrame = false; - } - - return; - } - - // Remember if we were swinging last frame - m_bSwungLastFrame = InSwing(); - - if ( IsEffectActive( EF_NODRAW ) ) - return; - - if ( IsCarriedByLocalPlayer() ) - { - // Update our effects - if ( gpGlobals->frametime != 0.0f && ( random->RandomInt( 0, 3 ) == 0 ) ) - { - Vector vecOrigin; - QAngle vecAngles; - - // Inner beams - BeamInfo_t beamInfo; - - int attachment = random->RandomInt( 0, 15 ); - - UTIL_GetWeaponAttachment( this, attachment, vecOrigin, vecAngles ); - ::FormatViewModelAttachment( vecOrigin, false ); - - CBasePlayer *pOwner = ToBasePlayer( GetOwner() ); - CBaseEntity *pBeamEnt = pOwner->GetViewModel(); - - beamInfo.m_vecStart = vec3_origin; - beamInfo.m_pStartEnt= pBeamEnt; - beamInfo.m_nStartAttachment = attachment; - - beamInfo.m_pEndEnt = NULL; - beamInfo.m_nEndAttachment = -1; - beamInfo.m_vecEnd = vecOrigin + RandomVector( -8, 8 ); - - beamInfo.m_pszModelName = STUNSTICK_BEAM_MATERIAL; - beamInfo.m_flHaloScale = 0.0f; - beamInfo.m_flLife = 0.05f; - beamInfo.m_flWidth = random->RandomFloat( 1.0f, 2.0f ); - beamInfo.m_flEndWidth = 0; - beamInfo.m_flFadeLength = 0.0f; - beamInfo.m_flAmplitude = random->RandomFloat( 16, 32 ); - beamInfo.m_flBrightness = 255.0; - beamInfo.m_flSpeed = 0.0; - beamInfo.m_nStartFrame = 0.0; - beamInfo.m_flFrameRate = 1.0f; - beamInfo.m_flRed = 255.0f;; - beamInfo.m_flGreen = 255.0f; - beamInfo.m_flBlue = 255.0f; - beamInfo.m_nSegments = 16; - beamInfo.m_bRenderable = true; - beamInfo.m_nFlags = 0; - - beams->CreateBeamEntPoint( beamInfo ); - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: Starts the client-side version thinking -//----------------------------------------------------------------------------- -void C_WeaponStunStick::OnDataChanged( DataUpdateType_t updateType ) -{ - BaseClass::OnDataChanged( updateType ); - if ( updateType == DATA_UPDATE_CREATED ) - { - SetNextClientThink( CLIENT_THINK_ALWAYS ); - SetupAttachmentPoints(); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Tells us we're always a translucent entity -//----------------------------------------------------------------------------- -RenderGroup_t C_WeaponStunStick::GetRenderGroup( void ) -{ - return RENDER_GROUP_TWOPASS; -} - -//----------------------------------------------------------------------------- -// Purpose: Tells us we're always a translucent entity -//----------------------------------------------------------------------------- -bool C_WeaponStunStick::InSwing( void ) -{ - int activity = GetActivity(); - - // FIXME: This is needed until the actual animation works - if ( IsCarriedByLocalPlayer() == false ) - return true; - - // These are the swing activities this weapon can play - if ( activity == GetPrimaryAttackActivity() || - activity == GetSecondaryAttackActivity() || - activity == ACT_VM_MISSCENTER || - activity == ACT_VM_MISSCENTER2 ) - return true; - - return false; -} - -//----------------------------------------------------------------------------- -// Purpose: Draw our special effects -//----------------------------------------------------------------------------- -void C_WeaponStunStick::DrawThirdPersonEffects( void ) -{ - Vector vecOrigin; - QAngle vecAngles; - float color[3]; - float scale; - - IMaterial *pMaterial = materials->FindMaterial( STUNSTICK_GLOW_MATERIAL, NULL, false ); - materials->Bind( pMaterial ); - - // Get bright when swung - if ( InSwing() ) - { - color[0] = color[1] = color[2] = 0.4f; - scale = 22.0f; - } - else - { - color[0] = color[1] = color[2] = 0.1f; - scale = 20.0f; - } - - // Draw an all encompassing glow around the entire head - UTIL_GetWeaponAttachment( this, m_BeamCenterAttachment, vecOrigin, vecAngles ); - DrawHalo( pMaterial, vecOrigin, scale, color ); - - if ( InSwing() ) - { - pMaterial = materials->FindMaterial( STUNSTICK_GLOW_MATERIAL2, NULL, false ); - materials->Bind( pMaterial ); - - color[0] = color[1] = color[2] = random->RandomFloat( 0.6f, 0.8f ); - scale = random->RandomFloat( 4.0f, 6.0f ); - - // Draw an all encompassing glow around the entire head - UTIL_GetWeaponAttachment( this, m_BeamCenterAttachment, vecOrigin, vecAngles ); - DrawHalo( pMaterial, vecOrigin, scale, color ); - - // Update our effects - if ( gpGlobals->frametime != 0.0f && ( random->RandomInt( 0, 5 ) == 0 ) ) - { - Vector vecOrigin; - QAngle vecAngles; - - GetAttachment( 1, vecOrigin, vecAngles ); - - Vector vForward; - AngleVectors( vecAngles, &vForward ); - - Vector vEnd = vecOrigin - vForward * 1.0f; - - // Inner beams - BeamInfo_t beamInfo; - - beamInfo.m_vecStart = vEnd; - Vector offset = RandomVector( -12, 8 ); - - offset += Vector(4,4,4); - beamInfo.m_vecEnd = vecOrigin + offset; - - beamInfo.m_pStartEnt= cl_entitylist->GetEnt( BEAMENT_ENTITY( entindex() ) ); - beamInfo.m_pEndEnt = cl_entitylist->GetEnt( BEAMENT_ENTITY( entindex() ) ); - beamInfo.m_nStartAttachment = 1; - beamInfo.m_nEndAttachment = -1; - - beamInfo.m_nType = TE_BEAMTESLA; - beamInfo.m_pszModelName = STUNSTICK_BEAM_MATERIAL; - beamInfo.m_flHaloScale = 0.0f; - beamInfo.m_flLife = 0.01f; - beamInfo.m_flWidth = random->RandomFloat( 1.0f, 3.0f ); - beamInfo.m_flEndWidth = 0; - beamInfo.m_flFadeLength = 0.0f; - beamInfo.m_flAmplitude = random->RandomFloat( 1, 2 ); - beamInfo.m_flBrightness = 255.0; - beamInfo.m_flSpeed = 0.0; - beamInfo.m_nStartFrame = 0.0; - beamInfo.m_flFrameRate = 1.0f; - beamInfo.m_flRed = 255.0f;; - beamInfo.m_flGreen = 255.0f; - beamInfo.m_flBlue = 255.0f; - beamInfo.m_nSegments = 16; - beamInfo.m_bRenderable = true; - beamInfo.m_nFlags = FBEAM_SHADEOUT; - - beams->CreateBeamPoints( beamInfo ); - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: Draw our special effects -//----------------------------------------------------------------------------- -void C_WeaponStunStick::DrawFirstPersonEffects( void ) -{ - Vector vecOrigin; - QAngle vecAngles; - float color[3]; - float scale; - - IMaterial *pMaterial = materials->FindMaterial( STUNSTICK_GLOW_MATERIAL_NOZ, NULL, false ); - materials->Bind( pMaterial ); - - // Find where we are in the fade - float fadeAmount = RemapValClamped( gpGlobals->curtime, m_flFadeTime, m_flFadeTime + FADE_DURATION, 1.0f, 0.1f ); - - // Get bright when swung - if ( InSwing() ) - { - color[0] = color[1] = color[2] = 0.4f; - scale = 22.0f; - } - else - { - color[0] = color[1] = color[2] = 0.4f * fadeAmount; - scale = 20.0f; - } - - if ( color[0] > 0.0f ) - { - // Draw an all encompassing glow around the entire head - UTIL_GetWeaponAttachment( this, m_BeamCenterAttachment, vecOrigin, vecAngles ); - DrawHalo( pMaterial, vecOrigin, scale, color ); - } - - // Draw bright points at each attachment location - for ( int i = 0; i < (NUM_BEAM_ATTACHMENTS*2)+1; i++ ) - { - if ( InSwing() ) - { - color[0] = color[1] = color[2] = random->RandomFloat( 0.05f, 0.5f ); - scale = random->RandomFloat( 4.0f, 5.0f ); - } - else - { - color[0] = color[1] = color[2] = random->RandomFloat( 0.05f, 0.5f ) * fadeAmount; - scale = random->RandomFloat( 4.0f, 5.0f ) * fadeAmount; - } - - if ( color[0] > 0.0f ) - { - UTIL_GetWeaponAttachment( this, i, vecOrigin, vecAngles ); - DrawHalo( pMaterial, vecOrigin, scale, color ); - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: Draw our special effects -//----------------------------------------------------------------------------- -void C_WeaponStunStick::DrawEffects( void ) -{ - if ( IsCarriedByLocalPlayer() ) - { - DrawFirstPersonEffects(); - } - else - { - DrawThirdPersonEffects(); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Viewmodel was drawn -//----------------------------------------------------------------------------- -void C_WeaponStunStick::ViewModelDrawn( C_BaseViewModel *pBaseViewModel ) -{ - // Don't bother when we're not deployed - if ( IsWeaponVisible() ) - { - // Do all our special effects - DrawEffects(); - } - - BaseClass::ViewModelDrawn( pBaseViewModel ); -} - -//----------------------------------------------------------------------------- -// Purpose: Draw a cheap glow quad at our impact point (with sparks) -//----------------------------------------------------------------------------- -void StunstickImpactCallback( const CEffectData &data ) -{ - float scale = random->RandomFloat( 16, 32 ); - - FX_AddQuad( data.m_vOrigin, - data.m_vNormal, - scale, - scale*2.0f, - 1.0f, - 1.0f, - 0.0f, - 0.0f, - random->RandomInt( 0, 360 ), - 0, - Vector( 1.0f, 1.0f, 1.0f ), - 0.1f, - "sprites/light_glow02_add", - 0 ); - - FX_Sparks( data.m_vOrigin, 1, 2, data.m_vNormal, 6, 64, 256 ); -} - -DECLARE_CLIENT_EFFECT( "StunstickImpact", StunstickImpactCallback ); - -#endif \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: Stun Stick- beating stick with a zappy end +// +// $NoKeywords: $ +//=============================================================================// + +#include "cbase.h" +#include "npcevent.h" +#include "weapon_hl2mpbasebasebludgeon.h" +#include "IEffects.h" +#include "debugoverlay_shared.h" + +#ifndef CLIENT_DLL + #include "npc_metropolice.h" + #include "te_effect_dispatch.h" +#endif + +#ifdef CLIENT_DLL + + #include "iviewrender_beams.h" + #include "beam_shared.h" + #include "materialsystem/IMaterial.h" + #include "model_types.h" + #include "c_te_effect_dispatch.h" + #include "fx_quad.h" + #include "fx.h" + + extern void DrawHalo( IMaterial* pMaterial, const Vector &source, float scale, float const *color, float flHDRColorScale ); + extern void FormatViewModelAttachment( Vector &vOrigin, bool bInverse ); + +#endif + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +extern ConVar metropolice_move_and_melee; + +#define STUNSTICK_RANGE 75.0f +#define STUNSTICK_REFIRE 0.8f +#define STUNSTICK_BEAM_MATERIAL "sprites/lgtning.vmt" +#define STUNSTICK_GLOW_MATERIAL "sprites/light_glow02_add" +#define STUNSTICK_GLOW_MATERIAL2 "effects/blueflare1" +#define STUNSTICK_GLOW_MATERIAL_NOZ "sprites/light_glow02_add_noz" + +#ifdef CLIENT_DLL +#define CWeaponStunStick C_WeaponStunStick +#endif + +class CWeaponStunStick : public CBaseHL2MPBludgeonWeapon +{ + DECLARE_CLASS( CWeaponStunStick, CBaseHL2MPBludgeonWeapon ); + +public: + + CWeaponStunStick(); + + DECLARE_NETWORKCLASS(); + DECLARE_PREDICTABLE(); + +#ifndef CLIENT_DLL + DECLARE_ACTTABLE(); +#endif + +#ifdef CLIENT_DLL + virtual int DrawModel( int flags ); + virtual void ClientThink( void ); + virtual void OnDataChanged( DataUpdateType_t updateType ); + virtual RenderGroup_t GetRenderGroup( void ); + virtual void ViewModelDrawn( C_BaseViewModel *pBaseViewModel ); + +#endif + + virtual void Precache(); + + void Spawn(); + + float GetRange( void ) { return STUNSTICK_RANGE; } + float GetFireRate( void ) { return STUNSTICK_REFIRE; } + + + bool Deploy( void ); + bool Holster( CBaseCombatWeapon *pSwitchingTo = NULL ); + + void Drop( const Vector &vecVelocity ); + void ImpactEffect( trace_t &traceHit ); + void SecondaryAttack( void ) {} + void SetStunState( bool state ); + bool GetStunState( void ); + +#ifndef CLIENT_DLL + void Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatCharacter *pOperator ); + int WeaponMeleeAttack1Condition( float flDot, float flDist ); +#endif + + float GetDamageForActivity( Activity hitActivity ); + + CWeaponStunStick( const CWeaponStunStick & ); + +private: + +#ifdef CLIENT_DLL + + #define NUM_BEAM_ATTACHMENTS 9 + + struct stunstickBeamInfo_t + { + int IDs[2]; // 0 - top, 1 - bottom + }; + + stunstickBeamInfo_t m_BeamAttachments[NUM_BEAM_ATTACHMENTS]; // Lookup for arc attachment points on the head of the stick + int m_BeamCenterAttachment; // "Core" of the effect (center of the head) + + void SetupAttachmentPoints( void ); + void DrawFirstPersonEffects( void ); + void DrawThirdPersonEffects( void ); + void DrawEffects( void ); + bool InSwing( void ); + + bool m_bSwungLastFrame; + + #define FADE_DURATION 0.25f + + float m_flFadeTime; + +#endif + + CNetworkVar( bool, m_bActive ); +}; + +//----------------------------------------------------------------------------- +// CWeaponStunStick +//----------------------------------------------------------------------------- +IMPLEMENT_NETWORKCLASS_ALIASED( WeaponStunStick, DT_WeaponStunStick ) + +BEGIN_NETWORK_TABLE( CWeaponStunStick, DT_WeaponStunStick ) +#ifdef CLIENT_DLL + RecvPropInt( RECVINFO( m_bActive ) ), +#else + SendPropInt( SENDINFO( m_bActive ), 1, SPROP_UNSIGNED ), +#endif + +END_NETWORK_TABLE() + +BEGIN_PREDICTION_DATA( CWeaponStunStick ) +END_PREDICTION_DATA() + +LINK_ENTITY_TO_CLASS( weapon_stunstick, CWeaponStunStick ); +PRECACHE_WEAPON_REGISTER( weapon_stunstick ); + + +#ifndef CLIENT_DLL + +acttable_t CWeaponStunStick::m_acttable[] = +{ + { ACT_RANGE_ATTACK1, ACT_RANGE_ATTACK_SLAM, true }, + { ACT_HL2MP_IDLE, ACT_HL2MP_IDLE_MELEE, false }, + { ACT_HL2MP_RUN, ACT_HL2MP_RUN_MELEE, false }, + { ACT_HL2MP_IDLE_CROUCH, ACT_HL2MP_IDLE_CROUCH_MELEE, false }, + { ACT_HL2MP_WALK_CROUCH, ACT_HL2MP_WALK_CROUCH_MELEE, false }, + { ACT_HL2MP_GESTURE_RANGE_ATTACK, ACT_HL2MP_GESTURE_RANGE_ATTACK_MELEE, false }, + { ACT_HL2MP_GESTURE_RELOAD, ACT_HL2MP_GESTURE_RELOAD_MELEE, false }, + { ACT_HL2MP_JUMP, ACT_HL2MP_JUMP_MELEE, false }, +}; + +IMPLEMENT_ACTTABLE(CWeaponStunStick); + +#endif + + +//----------------------------------------------------------------------------- +// Constructor +//----------------------------------------------------------------------------- +CWeaponStunStick::CWeaponStunStick( void ) +{ + // HACK: Don't call SetStunState because this tried to Emit a sound before + // any players are connected which is a bug + m_bActive = false; + +#ifdef CLIENT_DLL + m_bSwungLastFrame = false; + m_flFadeTime = FADE_DURATION; // Start off past the fade point +#endif +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CWeaponStunStick::Spawn() +{ + Precache(); + + BaseClass::Spawn(); + AddSolidFlags( FSOLID_NOT_STANDABLE ); +} + +void CWeaponStunStick::Precache() +{ + BaseClass::Precache(); + + PrecacheScriptSound( "Weapon_StunStick.Activate" ); + PrecacheScriptSound( "Weapon_StunStick.Deactivate" ); + + PrecacheModel( STUNSTICK_BEAM_MATERIAL ); +} + +//----------------------------------------------------------------------------- +// Purpose: Get the damage amount for the animation we're doing +// Input : hitActivity - currently played activity +// Output : Damage amount +//----------------------------------------------------------------------------- +float CWeaponStunStick::GetDamageForActivity( Activity hitActivity ) +{ + return 40.0f; +} + +//----------------------------------------------------------------------------- +// Attempt to lead the target (needed because citizens can't hit manhacks with the crowbar!) +//----------------------------------------------------------------------------- +extern ConVar sk_crowbar_lead_time; + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CWeaponStunStick::ImpactEffect( trace_t &traceHit ) +{ + +//#ifndef CLIENT_DLL + + CEffectData data; + + data.m_vNormal = traceHit.plane.normal; + data.m_vOrigin = traceHit.endpos + ( data.m_vNormal * 4.0f ); + + DispatchEffect( "StunstickImpact", data ); + +//#endif + + //FIXME: need new decals + UTIL_ImpactTrace( &traceHit, DMG_CLUB ); +} + +#ifndef CLIENT_DLL + + +int CWeaponStunStick::WeaponMeleeAttack1Condition( float flDot, float flDist ) +{ + // Attempt to lead the target (needed because citizens can't hit manhacks with the crowbar!) + CAI_BaseNPC *pNPC = GetOwner()->MyNPCPointer(); + CBaseEntity *pEnemy = pNPC->GetEnemy(); + if (!pEnemy) + return COND_NONE; + + Vector vecVelocity; + AngularImpulse angVelocity; + pEnemy->GetVelocity( &vecVelocity, &angVelocity ); + + // Project where the enemy will be in a little while, add some randomness so he doesn't always hit + float dt = sk_crowbar_lead_time.GetFloat(); + dt += random->RandomFloat( -0.3f, 0.2f ); + if ( dt < 0.0f ) + dt = 0.0f; + + Vector vecExtrapolatedPos; + VectorMA( pEnemy->WorldSpaceCenter(), dt, vecVelocity, vecExtrapolatedPos ); + + Vector vecDelta; + VectorSubtract( vecExtrapolatedPos, pNPC->WorldSpaceCenter(), vecDelta ); + + if ( fabs( vecDelta.z ) > 70 ) + { + return COND_TOO_FAR_TO_ATTACK; + } + + Vector vecForward = pNPC->BodyDirection2D( ); + vecDelta.z = 0.0f; + float flExtrapolatedDot = DotProduct2D( vecDelta.AsVector2D(), vecForward.AsVector2D() ); + if ((flDot < 0.7) && (flExtrapolatedDot < 0.7)) + { + return COND_NOT_FACING_ATTACK; + } + + float flExtrapolatedDist = Vector2DNormalize( vecDelta.AsVector2D() ); + + if( pEnemy->IsPlayer() ) + { + //Vector vecDir = pEnemy->GetSmoothedVelocity(); + //float flSpeed = VectorNormalize( vecDir ); + + // If player will be in front of me in one-half second, clock his arse. + Vector vecProjectEnemy = pEnemy->GetAbsOrigin() + (pEnemy->GetAbsVelocity() * 0.35); + Vector vecProjectMe = GetAbsOrigin(); + + if( (vecProjectMe - vecProjectEnemy).Length2D() <= 48.0f ) + { + return COND_CAN_MELEE_ATTACK1; + } + } +/* + if( metropolice_move_and_melee.GetBool() ) + { + if( pNPC->IsMoving() ) + { + flTargetDist *= 1.5f; + } + } +*/ + float flTargetDist = 48.0f; + if ((flDist > flTargetDist) && (flExtrapolatedDist > flTargetDist)) + { + return COND_TOO_FAR_TO_ATTACK; + } + + return COND_CAN_MELEE_ATTACK1; +} + + +void CWeaponStunStick::Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatCharacter *pOperator ) +{ + switch( pEvent->event ) + { + case EVENT_WEAPON_MELEE_HIT: + { + // Trace up or down based on where the enemy is... + // But only if we're basically facing that direction + Vector vecDirection; + AngleVectors( GetAbsAngles(), &vecDirection ); + + CBaseEntity *pEnemy = pOperator->MyNPCPointer() ? pOperator->MyNPCPointer()->GetEnemy() : NULL; + if ( pEnemy ) + { + Vector vecDelta; + VectorSubtract( pEnemy->WorldSpaceCenter(), pOperator->Weapon_ShootPosition(), vecDelta ); + VectorNormalize( vecDelta ); + + Vector2D vecDelta2D = vecDelta.AsVector2D(); + Vector2DNormalize( vecDelta2D ); + if ( DotProduct2D( vecDelta2D, vecDirection.AsVector2D() ) > 0.8f ) + { + vecDirection = vecDelta; + } + } + + Vector vecEnd; + VectorMA( pOperator->Weapon_ShootPosition(), 32, vecDirection, vecEnd ); + // Stretch the swing box down to catch low level physics objects + CBaseEntity *pHurt = pOperator->CheckTraceHullAttack( pOperator->Weapon_ShootPosition(), vecEnd, + Vector(-16,-16,-40), Vector(16,16,16), static_cast(GetDamageForActivity( GetActivity() )), DMG_CLUB, 0.5f, false ); + + // did I hit someone? + if ( pHurt ) + { + // play sound + WeaponSound( MELEE_HIT ); + + CBasePlayer *pPlayer = ToBasePlayer( pHurt ); + + bool bFlashed = false; + + // Punch angles + if ( pPlayer != NULL && !(pPlayer->GetFlags() & FL_GODMODE) ) + { + float yawKick = random->RandomFloat( -48, -24 ); + + //Kick the player angles + pPlayer->ViewPunch( QAngle( -16, yawKick, 2 ) ); + + Vector dir = pHurt->GetAbsOrigin() - GetAbsOrigin(); + + // If the player's on my head, don't knock him up + if ( pPlayer->GetGroundEntity() == pOperator ) + { + dir = vecDirection; + dir.z = 0; + } + + VectorNormalize(dir); + + dir *= 500.0f; + + //If not on ground, then don't make them fly! + if ( !(pPlayer->GetFlags() & FL_ONGROUND ) ) + dir.z = 0.0f; + + //Push the target back + pHurt->ApplyAbsVelocityImpulse( dir ); + + if ( !bFlashed ) + { + color32 red = {128,0,0,128}; + UTIL_ScreenFade( pPlayer, red, 0.5f, 0.1f, FFADE_IN ); + } + + // Force the player to drop anyting they were holding + pPlayer->ForceDropOfCarriedPhysObjects(); + } + + // do effect? + } + else + { + WeaponSound( MELEE_MISS ); + } + } + break; + default: + BaseClass::Operator_HandleAnimEvent( pEvent, pOperator ); + break; + } +} + +#endif + +//----------------------------------------------------------------------------- +// Purpose: Sets the state of the stun stick +//----------------------------------------------------------------------------- +void CWeaponStunStick::SetStunState( bool state ) +{ + m_bActive = state; + + if ( m_bActive ) + { + //FIXME: START - Move to client-side + + Vector vecAttachment; + QAngle vecAttachmentAngles; + + GetAttachment( 1, vecAttachment, vecAttachmentAngles ); + g_pEffects->Sparks( vecAttachment ); + + //FIXME: END - Move to client-side + + EmitSound( "Weapon_StunStick.Activate" ); + } + else + { + EmitSound( "Weapon_StunStick.Deactivate" ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CWeaponStunStick::Deploy( void ) +{ + SetStunState( true ); + + return BaseClass::Deploy(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CWeaponStunStick::Holster( CBaseCombatWeapon *pSwitchingTo ) +{ + if ( BaseClass::Holster( pSwitchingTo ) == false ) + return false; + + SetStunState( false ); + SetWeaponVisible( false ); + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : &vecVelocity - +//----------------------------------------------------------------------------- +void CWeaponStunStick::Drop( const Vector &vecVelocity ) +{ + SetStunState( false ); + +#ifndef CLIENT_DLL + UTIL_Remove( this ); +#endif + +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CWeaponStunStick::GetStunState( void ) +{ + return m_bActive; +} + +#ifdef CLIENT_DLL + +//----------------------------------------------------------------------------- +// Purpose: Get the attachment point on a viewmodel that a base weapon is using +//----------------------------------------------------------------------------- +bool UTIL_GetWeaponAttachment( C_BaseCombatWeapon *pWeapon, int attachmentID, Vector &absOrigin, QAngle &absAngles ) +{ + // This is already correct in third-person + if ( pWeapon && pWeapon->IsCarriedByLocalPlayer() == false ) + { + return pWeapon->GetAttachment( attachmentID, absOrigin, absAngles ); + } + + // Otherwise we need to translate the attachment to the viewmodel's version and reformat it + CBasePlayer *pOwner = ToBasePlayer( pWeapon->GetOwner() ); + + if ( pOwner != NULL ) + { + int ret = pOwner->GetViewModel()->GetAttachment( attachmentID, absOrigin, absAngles ); + FormatViewModelAttachment( absOrigin, true ); + + return ret; + } + + // Wasn't found + return false; +} + +#define BEAM_ATTACH_CORE_NAME "sparkrear" + +//----------------------------------------------------------------------------- +// Purpose: Sets up the attachment point lookup for the model +//----------------------------------------------------------------------------- +void C_WeaponStunStick::SetupAttachmentPoints( void ) +{ + // Setup points for both types of views + if ( IsCarriedByLocalPlayer() ) + { + const char *szBeamAttachNamesTop[NUM_BEAM_ATTACHMENTS] = + { + "spark1a","spark2a","spark3a","spark4a", + "spark5a","spark6a","spark7a","spark8a", + "spark9a", + }; + + const char *szBeamAttachNamesBottom[NUM_BEAM_ATTACHMENTS] = + { + "spark1b","spark2b","spark3b","spark4b", + "spark5b","spark6b","spark7b","spark8b", + "spark9b", + }; + + // Lookup and store all connections + for ( int i = 0; i < NUM_BEAM_ATTACHMENTS; i++ ) + { + m_BeamAttachments[i].IDs[0] = LookupAttachment( szBeamAttachNamesTop[i] ); + m_BeamAttachments[i].IDs[1] = LookupAttachment( szBeamAttachNamesBottom[i] ); + } + + // Setup the center beam point + m_BeamCenterAttachment = LookupAttachment( BEAM_ATTACH_CORE_NAME ); + } + else + { + // Setup the center beam point + m_BeamCenterAttachment = 1; + } +} + +//----------------------------------------------------------------------------- +// Purpose: Draws the stunstick model (with extra effects) +//----------------------------------------------------------------------------- +int C_WeaponStunStick::DrawModel( int flags ) +{ + if ( ShouldDraw() == false ) + return 0; + + // Only render these on the transparent pass + if ( flags & STUDIO_TRANSPARENCY ) + { + DrawEffects(); + return 1; + } + + return BaseClass::DrawModel( flags ); +} + +//----------------------------------------------------------------------------- +// Purpose: Randomly adds extra effects +//----------------------------------------------------------------------------- +void C_WeaponStunStick::ClientThink( void ) +{ + if ( InSwing() == false ) + { + if ( m_bSwungLastFrame ) + { + // Start fading + m_flFadeTime = gpGlobals->curtime; + m_bSwungLastFrame = false; + } + + return; + } + + // Remember if we were swinging last frame + m_bSwungLastFrame = InSwing(); + + if ( IsEffectActive( EF_NODRAW ) ) + return; + + if ( IsCarriedByLocalPlayer() ) + { + // Update our effects + if ( gpGlobals->frametime != 0.0f && ( random->RandomInt( 0, 3 ) == 0 ) ) + { + Vector vecOrigin; + QAngle vecAngles; + + // Inner beams + BeamInfo_t beamInfo; + + int attachment = random->RandomInt( 0, 15 ); + + UTIL_GetWeaponAttachment( this, attachment, vecOrigin, vecAngles ); + ::FormatViewModelAttachment( vecOrigin, false ); + + CBasePlayer *pOwner = ToBasePlayer( GetOwner() ); + CBaseEntity *pBeamEnt = pOwner->GetViewModel(); + + beamInfo.m_vecStart = vec3_origin; + beamInfo.m_pStartEnt= pBeamEnt; + beamInfo.m_nStartAttachment = attachment; + + beamInfo.m_pEndEnt = NULL; + beamInfo.m_nEndAttachment = -1; + beamInfo.m_vecEnd = vecOrigin + RandomVector( -8, 8 ); + + beamInfo.m_pszModelName = STUNSTICK_BEAM_MATERIAL; + beamInfo.m_flHaloScale = 0.0f; + beamInfo.m_flLife = 0.05f; + beamInfo.m_flWidth = random->RandomFloat( 1.0f, 2.0f ); + beamInfo.m_flEndWidth = 0; + beamInfo.m_flFadeLength = 0.0f; + beamInfo.m_flAmplitude = random->RandomFloat( 16, 32 ); + beamInfo.m_flBrightness = 255.0; + beamInfo.m_flSpeed = 0.0; + beamInfo.m_nStartFrame = 0.0; + beamInfo.m_flFrameRate = 1.0f; + beamInfo.m_flRed = 255.0f;; + beamInfo.m_flGreen = 255.0f; + beamInfo.m_flBlue = 255.0f; + beamInfo.m_nSegments = 16; + beamInfo.m_bRenderable = true; + beamInfo.m_nFlags = 0; + + beams->CreateBeamEntPoint( beamInfo ); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Starts the client-side version thinking +//----------------------------------------------------------------------------- +void C_WeaponStunStick::OnDataChanged( DataUpdateType_t updateType ) +{ + BaseClass::OnDataChanged( updateType ); + if ( updateType == DATA_UPDATE_CREATED ) + { + SetNextClientThink( CLIENT_THINK_ALWAYS ); + SetupAttachmentPoints(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Tells us we're always a translucent entity +//----------------------------------------------------------------------------- +RenderGroup_t C_WeaponStunStick::GetRenderGroup( void ) +{ + return RENDER_GROUP_TWOPASS; +} + +//----------------------------------------------------------------------------- +// Purpose: Tells us we're always a translucent entity +//----------------------------------------------------------------------------- +bool C_WeaponStunStick::InSwing( void ) +{ + int activity = GetActivity(); + + // FIXME: This is needed until the actual animation works + if ( IsCarriedByLocalPlayer() == false ) + return true; + + // These are the swing activities this weapon can play + if ( activity == GetPrimaryAttackActivity() || + activity == GetSecondaryAttackActivity() || + activity == ACT_VM_MISSCENTER || + activity == ACT_VM_MISSCENTER2 ) + return true; + + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: Draw our special effects +//----------------------------------------------------------------------------- +void C_WeaponStunStick::DrawThirdPersonEffects( void ) +{ + Vector vecOrigin; + QAngle vecAngles; + float color[3]; + float scale; + + IMaterial *pMaterial = materials->FindMaterial( STUNSTICK_GLOW_MATERIAL, NULL, false ); + materials->Bind( pMaterial ); + + // Get bright when swung + if ( InSwing() ) + { + color[0] = color[1] = color[2] = 0.4f; + scale = 22.0f; + } + else + { + color[0] = color[1] = color[2] = 0.1f; + scale = 20.0f; + } + + // Draw an all encompassing glow around the entire head + UTIL_GetWeaponAttachment( this, m_BeamCenterAttachment, vecOrigin, vecAngles ); + DrawHalo( pMaterial, vecOrigin, scale, color ); + + if ( InSwing() ) + { + pMaterial = materials->FindMaterial( STUNSTICK_GLOW_MATERIAL2, NULL, false ); + materials->Bind( pMaterial ); + + color[0] = color[1] = color[2] = random->RandomFloat( 0.6f, 0.8f ); + scale = random->RandomFloat( 4.0f, 6.0f ); + + // Draw an all encompassing glow around the entire head + UTIL_GetWeaponAttachment( this, m_BeamCenterAttachment, vecOrigin, vecAngles ); + DrawHalo( pMaterial, vecOrigin, scale, color ); + + // Update our effects + if ( gpGlobals->frametime != 0.0f && ( random->RandomInt( 0, 5 ) == 0 ) ) + { + Vector vecOrigin; + QAngle vecAngles; + + GetAttachment( 1, vecOrigin, vecAngles ); + + Vector vForward; + AngleVectors( vecAngles, &vForward ); + + Vector vEnd = vecOrigin - vForward * 1.0f; + + // Inner beams + BeamInfo_t beamInfo; + + beamInfo.m_vecStart = vEnd; + Vector offset = RandomVector( -12, 8 ); + + offset += Vector(4,4,4); + beamInfo.m_vecEnd = vecOrigin + offset; + + beamInfo.m_pStartEnt= cl_entitylist->GetEnt( BEAMENT_ENTITY( entindex() ) ); + beamInfo.m_pEndEnt = cl_entitylist->GetEnt( BEAMENT_ENTITY( entindex() ) ); + beamInfo.m_nStartAttachment = 1; + beamInfo.m_nEndAttachment = -1; + + beamInfo.m_nType = TE_BEAMTESLA; + beamInfo.m_pszModelName = STUNSTICK_BEAM_MATERIAL; + beamInfo.m_flHaloScale = 0.0f; + beamInfo.m_flLife = 0.01f; + beamInfo.m_flWidth = random->RandomFloat( 1.0f, 3.0f ); + beamInfo.m_flEndWidth = 0; + beamInfo.m_flFadeLength = 0.0f; + beamInfo.m_flAmplitude = random->RandomFloat( 1, 2 ); + beamInfo.m_flBrightness = 255.0; + beamInfo.m_flSpeed = 0.0; + beamInfo.m_nStartFrame = 0.0; + beamInfo.m_flFrameRate = 1.0f; + beamInfo.m_flRed = 255.0f;; + beamInfo.m_flGreen = 255.0f; + beamInfo.m_flBlue = 255.0f; + beamInfo.m_nSegments = 16; + beamInfo.m_bRenderable = true; + beamInfo.m_nFlags = FBEAM_SHADEOUT; + + beams->CreateBeamPoints( beamInfo ); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Draw our special effects +//----------------------------------------------------------------------------- +void C_WeaponStunStick::DrawFirstPersonEffects( void ) +{ + Vector vecOrigin; + QAngle vecAngles; + float color[3]; + float scale; + + IMaterial *pMaterial = materials->FindMaterial( STUNSTICK_GLOW_MATERIAL_NOZ, NULL, false ); + materials->Bind( pMaterial ); + + // Find where we are in the fade + float fadeAmount = RemapValClamped( gpGlobals->curtime, m_flFadeTime, m_flFadeTime + FADE_DURATION, 1.0f, 0.1f ); + + // Get bright when swung + if ( InSwing() ) + { + color[0] = color[1] = color[2] = 0.4f; + scale = 22.0f; + } + else + { + color[0] = color[1] = color[2] = 0.4f * fadeAmount; + scale = 20.0f; + } + + if ( color[0] > 0.0f ) + { + // Draw an all encompassing glow around the entire head + UTIL_GetWeaponAttachment( this, m_BeamCenterAttachment, vecOrigin, vecAngles ); + DrawHalo( pMaterial, vecOrigin, scale, color ); + } + + // Draw bright points at each attachment location + for ( int i = 0; i < (NUM_BEAM_ATTACHMENTS*2)+1; i++ ) + { + if ( InSwing() ) + { + color[0] = color[1] = color[2] = random->RandomFloat( 0.05f, 0.5f ); + scale = random->RandomFloat( 4.0f, 5.0f ); + } + else + { + color[0] = color[1] = color[2] = random->RandomFloat( 0.05f, 0.5f ) * fadeAmount; + scale = random->RandomFloat( 4.0f, 5.0f ) * fadeAmount; + } + + if ( color[0] > 0.0f ) + { + UTIL_GetWeaponAttachment( this, i, vecOrigin, vecAngles ); + DrawHalo( pMaterial, vecOrigin, scale, color ); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Draw our special effects +//----------------------------------------------------------------------------- +void C_WeaponStunStick::DrawEffects( void ) +{ + if ( IsCarriedByLocalPlayer() ) + { + DrawFirstPersonEffects(); + } + else + { + DrawThirdPersonEffects(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Viewmodel was drawn +//----------------------------------------------------------------------------- +void C_WeaponStunStick::ViewModelDrawn( C_BaseViewModel *pBaseViewModel ) +{ + // Don't bother when we're not deployed + if ( IsWeaponVisible() ) + { + // Do all our special effects + DrawEffects(); + } + + BaseClass::ViewModelDrawn( pBaseViewModel ); +} + +//----------------------------------------------------------------------------- +// Purpose: Draw a cheap glow quad at our impact point (with sparks) +//----------------------------------------------------------------------------- +void StunstickImpactCallback( const CEffectData &data ) +{ + float scale = random->RandomFloat( 16, 32 ); + + FX_AddQuad( data.m_vOrigin, + data.m_vNormal, + scale, + scale*2.0f, + 1.0f, + 1.0f, + 0.0f, + 0.0f, + random->RandomInt( 0, 360 ), + 0, + Vector( 1.0f, 1.0f, 1.0f ), + 0.1f, + "sprites/light_glow02_add", + 0 ); + + FX_Sparks( data.m_vOrigin, 1, 2, data.m_vNormal, 6, 64, 256 ); +} + +DECLARE_CLIENT_EFFECT( "StunstickImpact", StunstickImpactCallback ); + +#endif diff --git a/game_shared/in_buttons.h b/game_shared/in_buttons.h index 62c44e8d..3d462296 100644 --- a/game_shared/in_buttons.h +++ b/game_shared/in_buttons.h @@ -1,40 +1,40 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -// -//=============================================================================// -#ifndef IN_BUTTONS_H -#define IN_BUTTONS_H -#ifdef _WIN32 -#pragma once -#endif - -#define IN_ATTACK (1 << 0) -#define IN_JUMP (1 << 1) -#define IN_DUCK (1 << 2) -#define IN_FORWARD (1 << 3) -#define IN_BACK (1 << 4) -#define IN_USE (1 << 5) -#define IN_CANCEL (1 << 6) -#define IN_LEFT (1 << 7) -#define IN_RIGHT (1 << 8) -#define IN_MOVELEFT (1 << 9) -#define IN_MOVERIGHT (1 << 10) -#define IN_ATTACK2 (1 << 11) -#define IN_RUN (1 << 12) -#define IN_RELOAD (1 << 13) -#define IN_ALT1 (1 << 14) -#define IN_ALT2 (1 << 15) -#define IN_SCORE (1 << 16) // Used by client.dll for when scoreboard is held down -#define IN_SPEED (1 << 17) // Player is holding the speed key -#define IN_WALK (1 << 18) // Player holding walk key -#define IN_ZOOM (1 << 19) // Zoom key for HUD zoom -#define IN_WEAPON1 (1 << 20) // weapon defines these bits -#define IN_WEAPON2 (1 << 21) // weapon defines these bits -#define IN_BULLRUSH (1 << 22) -#define IN_GRENADE1 (1 << 23) // grenade 1 -#define IN_GRENADE2 (1 << 24) // grenade 2 - -#endif // IN_BUTTONS_H \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// +#ifndef IN_BUTTONS_H +#define IN_BUTTONS_H +#ifdef _WIN32 +#pragma once +#endif + +#define IN_ATTACK (1 << 0) +#define IN_JUMP (1 << 1) +#define IN_DUCK (1 << 2) +#define IN_FORWARD (1 << 3) +#define IN_BACK (1 << 4) +#define IN_USE (1 << 5) +#define IN_CANCEL (1 << 6) +#define IN_LEFT (1 << 7) +#define IN_RIGHT (1 << 8) +#define IN_MOVELEFT (1 << 9) +#define IN_MOVERIGHT (1 << 10) +#define IN_ATTACK2 (1 << 11) +#define IN_RUN (1 << 12) +#define IN_RELOAD (1 << 13) +#define IN_ALT1 (1 << 14) +#define IN_ALT2 (1 << 15) +#define IN_SCORE (1 << 16) // Used by client.dll for when scoreboard is held down +#define IN_SPEED (1 << 17) // Player is holding the speed key +#define IN_WALK (1 << 18) // Player holding walk key +#define IN_ZOOM (1 << 19) // Zoom key for HUD zoom +#define IN_WEAPON1 (1 << 20) // weapon defines these bits +#define IN_WEAPON2 (1 << 21) // weapon defines these bits +#define IN_BULLRUSH (1 << 22) +#define IN_GRENADE1 (1 << 23) // grenade 1 +#define IN_GRENADE2 (1 << 24) // grenade 2 + +#endif // IN_BUTTONS_H diff --git a/game_shared/mapdata_shared.h b/game_shared/mapdata_shared.h index 0f58447e..c7287ce4 100644 --- a/game_shared/mapdata_shared.h +++ b/game_shared/mapdata_shared.h @@ -1,33 +1,33 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -//=============================================================================// - -#ifndef MAPDATA_SHARED_H -#define MAPDATA_SHARED_H -#ifdef _WIN32 -#pragma once -#endif - -#include "interface.h" -#include "vector.h" - -#define INTERFACEVERSION_MAPDATA "MapData001" - -abstract_class IMapData -{ -public: - - // World data queries. - virtual void GetMapBounds( Vector &vecMins, Vector &vecMaxs ) = 0; - virtual void GetMapOrigin( Vector &vecOrigin ) = 0; - virtual void GetMapSize( Vector &vecSize ) = 0; - - // 3D Skybox data queries. - virtual void Get3DSkyboxOrigin( Vector &vecOrigin ) = 0; - virtual float Get3DSkyboxScale( void ) = 0; -}; - -#endif // MAPDATA_SHARED_H \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef MAPDATA_SHARED_H +#define MAPDATA_SHARED_H +#ifdef _WIN32 +#pragma once +#endif + +#include "interface.h" +#include "vector.h" + +#define INTERFACEVERSION_MAPDATA "MapData001" + +abstract_class IMapData +{ +public: + + // World data queries. + virtual void GetMapBounds( Vector &vecMins, Vector &vecMaxs ) = 0; + virtual void GetMapOrigin( Vector &vecOrigin ) = 0; + virtual void GetMapSize( Vector &vecSize ) = 0; + + // 3D Skybox data queries. + virtual void Get3DSkyboxOrigin( Vector &vecOrigin ) = 0; + virtual float Get3DSkyboxScale( void ) = 0; +}; + +#endif // MAPDATA_SHARED_H diff --git a/game_shared/mapentities_shared.cpp b/game_shared/mapentities_shared.cpp index b4d6eee0..d69d74c8 100644 --- a/game_shared/mapentities_shared.cpp +++ b/game_shared/mapentities_shared.cpp @@ -125,7 +125,7 @@ const char *MapEntity_ParseToken( const char *data, char *newToken ) for ( const char *c = s_BraceChars; *c; c++ ) { - s_BraceCharacters[*c] = true; + s_BraceCharacters[static_cast(*c)] = true; } } diff --git a/game_shared/physics_main_shared.cpp b/game_shared/physics_main_shared.cpp index 619ad8ef..79379157 100644 --- a/game_shared/physics_main_shared.cpp +++ b/game_shared/physics_main_shared.cpp @@ -52,7 +52,7 @@ public: CDataObjectAccessSystem() { - COMPILE_TIME_ASSERT( NUM_DATAOBJECT_TYPES <= MAX_ACCESSORS ); + COMPILE_TIME_ASSERT( static_cast(NUM_DATAOBJECT_TYPES) <= static_cast(MAX_ACCESSORS) ); Q_memset( m_Accessors, 0, sizeof( m_Accessors ) ); } diff --git a/game_shared/predictableid.cpp b/game_shared/predictableid.cpp index 1a6e3fe6..5706f464 100644 --- a/game_shared/predictableid.cpp +++ b/game_shared/predictableid.cpp @@ -109,7 +109,9 @@ void CPredictableId::ResetInstanceCounters( void ) //----------------------------------------------------------------------------- bool CPredictableId::IsActive( void ) const { - if ( *(const int *)&m_PredictableID == 0 ) + const void *predict = &m_PredictableID; + + if (*reinterpret_cast(predict) == 0) return false; return true; @@ -267,7 +269,8 @@ bool CPredictableId::GetAcknowledged( void ) const //----------------------------------------------------------------------------- int CPredictableId::GetRaw( void ) const { - return *(int *)&m_PredictableID; + const void *predict = &m_PredictableID; + return *reinterpret_cast(predict); } //----------------------------------------------------------------------------- @@ -276,7 +279,8 @@ int CPredictableId::GetRaw( void ) const //----------------------------------------------------------------------------- void CPredictableId::SetRaw( int raw ) { - *(int *)&m_PredictableID = raw; + void *predict = &m_PredictableID; + *reinterpret_cast(predict) = raw; } //----------------------------------------------------------------------------- diff --git a/game_shared/predicted_viewmodel.cpp b/game_shared/predicted_viewmodel.cpp index 38114d0e..e4324258 100644 --- a/game_shared/predicted_viewmodel.cpp +++ b/game_shared/predicted_viewmodel.cpp @@ -1,71 +1,71 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -//=============================================================================// -#include "cbase.h" -#include "predicted_viewmodel.h" - -// memdbgon must be the last include file in a .cpp file!!! -#include "tier0/memdbgon.h" - -LINK_ENTITY_TO_CLASS( predicted_viewmodel, CPredictedViewModel ); - -IMPLEMENT_NETWORKCLASS_ALIASED( PredictedViewModel, DT_PredictedViewModel ) - -BEGIN_NETWORK_TABLE( CPredictedViewModel, DT_PredictedViewModel ) -END_NETWORK_TABLE() - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -#ifdef CLIENT_DLL -CPredictedViewModel::CPredictedViewModel() : m_LagAnglesHistory("CPredictedViewModel::m_LagAnglesHistory") -{ - m_vLagAngles.Init(); - m_LagAnglesHistory.Setup( &m_vLagAngles, 0 ); -} -#else -CPredictedViewModel::CPredictedViewModel() -{ -} -#endif - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -CPredictedViewModel::~CPredictedViewModel() -{ -} - -#ifdef CLIENT_DLL -ConVar cl_wpn_sway_interp( "cl_wpn_sway_interp", "0.1", FCVAR_CLIENTDLL ); -ConVar cl_wpn_sway_scale( "cl_wpn_sway_scale", "1.0", FCVAR_CLIENTDLL ); -#endif - -void CPredictedViewModel::CalcViewModelLag( Vector& origin, QAngle& angles, QAngle& original_angles ) -{ - #ifdef CLIENT_DLL - // Calculate our drift - Vector forward, right, up; - AngleVectors( angles, &forward, &right, &up ); - - // Add an entry to the history. - m_vLagAngles = angles; - m_LagAnglesHistory.NoteChanged( gpGlobals->curtime, cl_wpn_sway_interp.GetFloat() ); - - // Interpolate back 100ms. - m_LagAnglesHistory.Interpolate( gpGlobals->curtime, cl_wpn_sway_interp.GetFloat() ); - - // Now take the 100ms angle difference and figure out how far the forward vector moved in local space. - Vector vLaggedForward; - QAngle angleDiff = m_vLagAngles - angles; - AngleVectors( -angleDiff, &vLaggedForward, 0, 0 ); - Vector vForwardDiff = Vector(1,0,0) - vLaggedForward; - - // Now offset the origin using that. - vForwardDiff *= cl_wpn_sway_scale.GetFloat(); - origin += forward*vForwardDiff.x + right*-vForwardDiff.y + up*vForwardDiff.z; - #endif -} \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// +#include "cbase.h" +#include "predicted_viewmodel.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +LINK_ENTITY_TO_CLASS( predicted_viewmodel, CPredictedViewModel ); + +IMPLEMENT_NETWORKCLASS_ALIASED( PredictedViewModel, DT_PredictedViewModel ) + +BEGIN_NETWORK_TABLE( CPredictedViewModel, DT_PredictedViewModel ) +END_NETWORK_TABLE() + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +#ifdef CLIENT_DLL +CPredictedViewModel::CPredictedViewModel() : m_LagAnglesHistory("CPredictedViewModel::m_LagAnglesHistory") +{ + m_vLagAngles.Init(); + m_LagAnglesHistory.Setup( &m_vLagAngles, 0 ); +} +#else +CPredictedViewModel::CPredictedViewModel() +{ +} +#endif + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CPredictedViewModel::~CPredictedViewModel() +{ +} + +#ifdef CLIENT_DLL +ConVar cl_wpn_sway_interp( "cl_wpn_sway_interp", "0.1", FCVAR_CLIENTDLL ); +ConVar cl_wpn_sway_scale( "cl_wpn_sway_scale", "1.0", FCVAR_CLIENTDLL ); +#endif + +void CPredictedViewModel::CalcViewModelLag( Vector& origin, QAngle& angles, QAngle& original_angles ) +{ + #ifdef CLIENT_DLL + // Calculate our drift + Vector forward, right, up; + AngleVectors( angles, &forward, &right, &up ); + + // Add an entry to the history. + m_vLagAngles = angles; + m_LagAnglesHistory.NoteChanged( gpGlobals->curtime, cl_wpn_sway_interp.GetFloat() ); + + // Interpolate back 100ms. + m_LagAnglesHistory.Interpolate( gpGlobals->curtime, cl_wpn_sway_interp.GetFloat() ); + + // Now take the 100ms angle difference and figure out how far the forward vector moved in local space. + Vector vLaggedForward; + QAngle angleDiff = m_vLagAngles - angles; + AngleVectors( -angleDiff, &vLaggedForward, 0, 0 ); + Vector vForwardDiff = Vector(1,0,0) - vLaggedForward; + + // Now offset the origin using that. + vForwardDiff *= cl_wpn_sway_scale.GetFloat(); + origin += forward*vForwardDiff.x + right*-vForwardDiff.y + up*vForwardDiff.z; + #endif +} diff --git a/game_shared/props_shared.cpp b/game_shared/props_shared.cpp index baefefb2..68bf1fbe 100644 --- a/game_shared/props_shared.cpp +++ b/game_shared/props_shared.cpp @@ -276,14 +276,14 @@ int CPropData::ParsePropFromKV( CBaseEntity *pProp, KeyValues *pSection, KeyValu } // Allow overriding of Block LOS - int iBlockLOS = pSection->GetFloat( "blockLOS", -1 ); + int iBlockLOS = pSection->GetInt( "blockLOS", -1 ); if ( iBlockLOS != -1 ) { pBreakableInterface->SetPropDataBlocksLOS( iBlockLOS != 0 ); } // Set whether AI can walk on this prop - int iIsWalkable = pSection->GetFloat( "AIWalkable", -1 ); + int iIsWalkable = pSection->GetInt( "AIWalkable", -1 ); if ( iIsWalkable != -1 ) { pBreakableInterface->SetPropDataIsAIWalkable( iIsWalkable != 0 ); @@ -380,7 +380,7 @@ int CPropData::ParsePropFromKV( CBaseEntity *pProp, KeyValues *pSection, KeyValu int iSmallest = SmallestAxis(vecSize); vecSize[iSmallest] = 1; float flVolume = vecSize.x * vecSize.y * vecSize.z; - int iMaxSize = floor( flVolume / (32.0*32.0) ); + int iMaxSize = static_cast(floor( flVolume / (32.0*32.0) )); pBreakableInterface->SetMaxBreakableSize( iMaxSize ); // Now parse our interactions @@ -520,7 +520,7 @@ class CBreakParser : public IVPhysicsKeyHandler { public: CBreakParser( float defaultBurstScale, int defaultCollisionGroup ) - : m_defaultBurstScale(defaultBurstScale), m_defaultCollisionGroup(defaultCollisionGroup) {} + : m_defaultCollisionGroup(defaultCollisionGroup), m_defaultBurstScale(defaultBurstScale) {} void ParseModelName( breakmodel_t *pModel, const char *pValue ) { @@ -744,7 +744,7 @@ const char *GetMassEquivalent(float flMass) { 7e24, "really freaking heavy" }, }; - for (int i = 0; i < sizeof(masstext) / sizeof(masstext[0]) - 1; i++) + for (size_t i = 0; i < sizeof(masstext) / sizeof(masstext[0]) - 1; i++) { if (flMass < masstext[i].flMass) { diff --git a/game_shared/rumble_shared.h b/game_shared/rumble_shared.h index 3e09e342..d331d20f 100644 --- a/game_shared/rumble_shared.h +++ b/game_shared/rumble_shared.h @@ -1,65 +1,65 @@ -//======= Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// -// -// Purpose: Shared code for XBox Rumble Effects -// -// $NoKeywords: $ -// -//=============================================================================// -#pragma once -#ifndef RUMBLE_SHARED_H -#define RUMBLE_SHARED_H - -#define RUMBLE_FLAGS_NONE 0x0000 -#define RUMBLE_FLAG_STOP 0x0001 // Stop any instance of this type of effect that's already playing. -#define RUMBLE_FLAG_LOOP 0x0002 // Make this effect loop. -#define RUMBLE_FLAG_RESTART 0x0004 // If this effect is already playing, restart it. -#define RUMBLE_FLAG_UPDATE_SCALE 0x0008 // Apply DATA to this effect if already playing, but don't restart. -#define RUMBLE_FLAG_ONLYONE 0x0010 // Don't play this effect if it is already playing. -#define RUMBLE_FLAG_RANDOM_AMPLITUDE 0x0020 // Amplitude scale will be randomly chosen. Between 10% and 100% -#define RUMBLE_FLAG_INITIAL_SCALE 0x0040 // Data is the initial scale to start this effect ( * 100 ) - -enum -{ -// DO NOT CHANGE THE ORDER OF ANY OF THESE ENUMS -// DO NOT INSERT ANY ITEMS - RUMBLE_INVALID = -1, - - RUMBLE_STOP_ALL = 0, // Cease all current rumbling effects. - - // Weapons - RUMBLE_PISTOL, - RUMBLE_357, - RUMBLE_SMG1, - RUMBLE_AR2, - RUMBLE_SHOTGUN_SINGLE, - RUMBLE_SHOTGUN_DOUBLE, - RUMBLE_AR2_ALT_FIRE, - -// YOU MAY INSERT/REARRANGE ITEMS FROM HERE DOWN, AS YOU SEE FIT - RUMBLE_RPG_MISSILE, - - RUMBLE_CROWBAR_SWING, - - // Vehicles - RUMBLE_AIRBOAT_GUN, - RUMBLE_JEEP_ENGINE_LOOP, - - RUMBLE_FLAT_LEFT, - RUMBLE_FLAT_RIGHT, - RUMBLE_FLAT_BOTH, - - // Damage - RUMBLE_DMG_LOW, - RUMBLE_DMG_MED, - RUMBLE_DMG_HIGH, - - RUMBLE_PHYSCANNON_OPEN, - RUMBLE_PHYSCANNON_PUNT, - RUMBLE_PHYSCANNON_LOW, - RUMBLE_PHYSCANNON_MEDIUM, - RUMBLE_PHYSCANNON_HIGH, - - NUM_RUMBLE_EFFECTS, // THIS MUST BE LAST!!! -}; - -#endif//RUMBLE_SHARED_H \ No newline at end of file +//======= Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: Shared code for XBox Rumble Effects +// +// $NoKeywords: $ +// +//=============================================================================// +#pragma once +#ifndef RUMBLE_SHARED_H +#define RUMBLE_SHARED_H + +#define RUMBLE_FLAGS_NONE 0x0000 +#define RUMBLE_FLAG_STOP 0x0001 // Stop any instance of this type of effect that's already playing. +#define RUMBLE_FLAG_LOOP 0x0002 // Make this effect loop. +#define RUMBLE_FLAG_RESTART 0x0004 // If this effect is already playing, restart it. +#define RUMBLE_FLAG_UPDATE_SCALE 0x0008 // Apply DATA to this effect if already playing, but don't restart. +#define RUMBLE_FLAG_ONLYONE 0x0010 // Don't play this effect if it is already playing. +#define RUMBLE_FLAG_RANDOM_AMPLITUDE 0x0020 // Amplitude scale will be randomly chosen. Between 10% and 100% +#define RUMBLE_FLAG_INITIAL_SCALE 0x0040 // Data is the initial scale to start this effect ( * 100 ) + +enum +{ +// DO NOT CHANGE THE ORDER OF ANY OF THESE ENUMS +// DO NOT INSERT ANY ITEMS + RUMBLE_INVALID = -1, + + RUMBLE_STOP_ALL = 0, // Cease all current rumbling effects. + + // Weapons + RUMBLE_PISTOL, + RUMBLE_357, + RUMBLE_SMG1, + RUMBLE_AR2, + RUMBLE_SHOTGUN_SINGLE, + RUMBLE_SHOTGUN_DOUBLE, + RUMBLE_AR2_ALT_FIRE, + +// YOU MAY INSERT/REARRANGE ITEMS FROM HERE DOWN, AS YOU SEE FIT + RUMBLE_RPG_MISSILE, + + RUMBLE_CROWBAR_SWING, + + // Vehicles + RUMBLE_AIRBOAT_GUN, + RUMBLE_JEEP_ENGINE_LOOP, + + RUMBLE_FLAT_LEFT, + RUMBLE_FLAT_RIGHT, + RUMBLE_FLAT_BOTH, + + // Damage + RUMBLE_DMG_LOW, + RUMBLE_DMG_MED, + RUMBLE_DMG_HIGH, + + RUMBLE_PHYSCANNON_OPEN, + RUMBLE_PHYSCANNON_PUNT, + RUMBLE_PHYSCANNON_LOW, + RUMBLE_PHYSCANNON_MEDIUM, + RUMBLE_PHYSCANNON_HIGH, + + NUM_RUMBLE_EFFECTS, // THIS MUST BE LAST!!! +}; + +#endif//RUMBLE_SHARED_H diff --git a/game_shared/saverestore.cpp b/game_shared/saverestore.cpp index 0315ad9b..342f8015 100644 --- a/game_shared/saverestore.cpp +++ b/game_shared/saverestore.cpp @@ -1,3379 +1,3379 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: Helper classes and functions for the save/restore system. -// -// $NoKeywords: $ -//=============================================================================// - -#include "cbase.h" -#include -#include "isaverestore.h" -#include "saverestore.h" -#include -#include "shake.h" -#include "decals.h" -#include "gamerules.h" -#include "bspfile.h" -#include "mathlib.h" -#include "engine/IEngineSound.h" -#include "saverestoretypes.h" -#include "saverestore_utlvector.h" -#include "model_types.h" -#include "igamesystem.h" -#include "interval.h" -#include "vphysics/object_hash.h" -#include "datacache/imdlcache.h" - -#if !defined( CLIENT_DLL ) - -#include "globalstate.h" -#include "entitylist.h" - -#else - -#include "gamestringpool.h" - -#endif - -// HACKHACK: Builds a global list of entities that were restored from all levels -#if !defined( CLIENT_DLL ) -void AddRestoredEntity( CBaseEntity *pEntity ); -#else -void AddRestoredEntity( C_BaseEntity *pEntity ); -#endif - - -// memdbgon must be the last include file in a .cpp file!!! -#include "tier0/memdbgon.h" - -#define MAX_ENTITYARRAY 1024 -#define ZERO_TIME ((FLT_MAX*-0.5)) -// A bit arbitrary, but unlikely to collide with any saved games... -#define TICK_NEVER_THINK_ENCODE ( INT_MAX - 3 ) - -ASSERT_INVARIANT( sizeof(EHandlePlaceholder_t) == sizeof(EHANDLE) ); - -//----------------------------------------------------------------------------- - -static int gSizes[FIELD_TYPECOUNT] = -{ - CDatamapFieldSizeDeducer::FieldSize(), - CDatamapFieldSizeDeducer::FieldSize(), - CDatamapFieldSizeDeducer::FieldSize(), - CDatamapFieldSizeDeducer::FieldSize(), - CDatamapFieldSizeDeducer::FieldSize(), - CDatamapFieldSizeDeducer::FieldSize(), - CDatamapFieldSizeDeducer::FieldSize(), - CDatamapFieldSizeDeducer::FieldSize(), - CDatamapFieldSizeDeducer::FieldSize(), - CDatamapFieldSizeDeducer::FieldSize(), - CDatamapFieldSizeDeducer::FieldSize(), - CDatamapFieldSizeDeducer::FieldSize(), - - CDatamapFieldSizeDeducer::FieldSize(), - CDatamapFieldSizeDeducer::FieldSize(), - CDatamapFieldSizeDeducer::FieldSize(), - - CDatamapFieldSizeDeducer::FieldSize(), - CDatamapFieldSizeDeducer::FieldSize(), - CDatamapFieldSizeDeducer::FieldSize(), - CDatamapFieldSizeDeducer::FieldSize(), - CDatamapFieldSizeDeducer::FieldSize(), - - CDatamapFieldSizeDeducer::FieldSize(), - CDatamapFieldSizeDeducer::FieldSize(), - CDatamapFieldSizeDeducer::FieldSize(), - CDatamapFieldSizeDeducer::FieldSize(), - CDatamapFieldSizeDeducer::FieldSize(), - CDatamapFieldSizeDeducer::FieldSize(), - CDatamapFieldSizeDeducer::FieldSize(), - CDatamapFieldSizeDeducer::FieldSize(), -}; - - -// helpers to offset worldspace matrices -static void VMatrixOffset( VMatrix &dest, const VMatrix &matrixIn, const Vector &offset ) -{ - dest = matrixIn; - dest.PostTranslate( offset ); -} - -static void Matrix3x4Offset( matrix3x4_t& dest, const matrix3x4_t& matrixIn, const Vector &offset ) -{ - MatrixCopy( matrixIn, dest ); - Vector out; - MatrixGetColumn( matrixIn, 3, out ); - out += offset; - MatrixSetColumn( out, 3, dest ); -} - -// This does the necessary casting / extract to grab a pointer to a member function as a void * -// UNDONE: Cast to BASEPTR or something else here? -#define EXTRACT_VOID_FUNCTIONPTR(x) (*(void **)(&(x))) -//----------------------------------------------------------------------------- -// Purpose: Search this datamap for the name of this member function -// This is used to save/restore function pointers (convert pointer to text) -// Input : *function - pointer to member function -// Output : const char * - function name -//----------------------------------------------------------------------------- -const char *UTIL_FunctionToName( datamap_t *pMap, void *function ) -{ - while ( pMap ) - { - for ( int i = 0; i < pMap->dataNumFields; i++ ) - { - if ( pMap->dataDesc[i].flags & FTYPEDESC_FUNCTIONTABLE ) - { - Assert( sizeof(pMap->dataDesc[i].inputFunc) == sizeof(void *) ); - void *pTest = EXTRACT_VOID_FUNCTIONPTR(pMap->dataDesc[i].inputFunc); - if ( pTest == function ) - return pMap->dataDesc[i].fieldName; - } - } - pMap = pMap->baseMap; - } - - return NULL; -} - -//----------------------------------------------------------------------------- -// Purpose: Search the datamap for a function named pName -// This is used to save/restore function pointers (convert text back to pointer) -// Input : *pName - name of the member function -//----------------------------------------------------------------------------- -void *UTIL_FunctionFromName( datamap_t *pMap, const char *pName ) -{ - while ( pMap ) - { - for ( int i = 0; i < pMap->dataNumFields; i++ ) - { - Assert( sizeof(pMap->dataDesc[i].inputFunc) == sizeof(void *) ); - - if ( pMap->dataDesc[i].flags & FTYPEDESC_FUNCTIONTABLE ) - { - if ( FStrEq( pName, pMap->dataDesc[i].fieldName ) ) - { - return EXTRACT_VOID_FUNCTIONPTR(pMap->dataDesc[i].inputFunc); - } - } - } - pMap = pMap->baseMap; - } - - Msg( "Failed to find function %s\n", pName ); - - return NULL; -} - -//----------------------------------------------------------------------------- -// -// CSave -// -//----------------------------------------------------------------------------- - -CSave::CSave( CSaveRestoreData *pdata ) - : m_pData(pdata), - m_pGameInfo( pdata ) -{ - m_BlockStartStack.EnsureCapacity( 32 ); - - // Logging. - m_hLogFile = NULL; -} - -//----------------------------------------------------------------------------- -// Purpose: Start logging save data. -//----------------------------------------------------------------------------- -void CSave::StartLogging( const char *pszLogName ) -{ - m_hLogFile = filesystem->Open( pszLogName, "w" ); -} - -//----------------------------------------------------------------------------- -// Purpose: Stop logging save data. -//----------------------------------------------------------------------------- -void CSave::EndLogging( void ) -{ - if ( m_hLogFile ) - { - filesystem->Close( m_hLogFile ); - } - m_hLogFile = NULL; -} - -//----------------------------------------------------------------------------- -// Purpose: Check to see if we are logging data. -//----------------------------------------------------------------------------- -bool CSave::IsLogging( void ) -{ - return ( m_hLogFile != NULL ); -} - -//----------------------------------------------------------------------------- -// Purpose: Log data. -//----------------------------------------------------------------------------- -void CSave::Log( const char *pName, fieldtype_t fieldType, void *value, int count ) -{ - // Check to see if we are logging. - if ( !IsLogging() ) - return; - - static char szBuf[1024]; - static char szTempBuf[256]; - - // Save the name. - Q_snprintf( szBuf, sizeof( szBuf ), "%s ", pName ); - - for ( int iCount = 0; iCount < count; ++iCount ) - { - switch ( fieldType ) - { - case FIELD_SHORT: - { - short *pValue = ( short* )( value ); - short nValue = pValue[iCount]; - Q_snprintf( szTempBuf, sizeof( szTempBuf ), "%d", nValue ); - Q_strncat( szBuf, szTempBuf, sizeof( szTempBuf ), COPY_ALL_CHARACTERS ); - break; - } - case FIELD_FLOAT: - { - float *pValue = ( float* )( value ); - float flValue = pValue[iCount]; - Q_snprintf( szTempBuf, sizeof( szTempBuf ), "%f", flValue ); - Q_strncat( szBuf, szTempBuf, sizeof( szTempBuf ), COPY_ALL_CHARACTERS ); - break; - } - case FIELD_BOOLEAN: - { - bool *pValue = ( bool* )( value ); - bool bValue = pValue[iCount]; - Q_snprintf( szTempBuf, sizeof( szTempBuf ), "%d", ( int )( bValue ) ); - Q_strncat( szBuf, szTempBuf, sizeof( szTempBuf ), COPY_ALL_CHARACTERS ); - break; - } - case FIELD_INTEGER: - { - int *pValue = ( int* )( value ); - int nValue = pValue[iCount]; - Q_snprintf( szTempBuf, sizeof( szTempBuf ), "%d", nValue ); - Q_strncat( szBuf, szTempBuf, sizeof( szTempBuf ), COPY_ALL_CHARACTERS ); - break; - } - case FIELD_STRING: - { - string_t *pValue = ( string_t* )( value ); - Q_snprintf( szTempBuf, sizeof( szTempBuf ), "%s", ( char* )STRING( *pValue ) ); - Q_strncat( szBuf, szTempBuf, sizeof( szTempBuf ), COPY_ALL_CHARACTERS ); - break; - } - case FIELD_VECTOR: - { - Vector *pValue = ( Vector* )( value ); - Vector vecValue = pValue[iCount]; - Q_snprintf( szTempBuf, sizeof( szTempBuf ), "(%f %f %f)", vecValue.x, vecValue.y, vecValue.z ); - Q_strncat( szBuf, szTempBuf, sizeof( szTempBuf ), COPY_ALL_CHARACTERS ); - break; - } - case FIELD_QUATERNION: - { - Quaternion *pValue = ( Quaternion* )( value ); - Quaternion q = pValue[iCount]; - Q_snprintf( szTempBuf, sizeof( szTempBuf ), "(%f %f %f %f)", q[0], q[1], q[2], q[3] ); - Q_strncat( szBuf, szTempBuf, sizeof( szTempBuf ), COPY_ALL_CHARACTERS ); - break; - } - case FIELD_CHARACTER: - { - char *pValue = ( char* )( value ); - char chValue = pValue[iCount]; - Q_snprintf( szTempBuf, sizeof( szTempBuf ), "%c", chValue ); - Q_strncat( szBuf, szTempBuf, sizeof( szTempBuf ), COPY_ALL_CHARACTERS ); - } - case FIELD_COLOR32: - { - byte *pValue = ( byte* )( value ); - byte *pColor = &pValue[iCount*4]; - Q_snprintf( szTempBuf, sizeof( szTempBuf ), "(%d %d %d %d)", ( int )pColor[0], ( int )pColor[1], ( int )pColor[2], ( int )pColor[3] ); - Q_strncat( szBuf, szTempBuf, sizeof( szTempBuf ), COPY_ALL_CHARACTERS ); - } - case FIELD_EMBEDDED: - case FIELD_CUSTOM: - default: - { - break; - } - } - - // Add space data. - if ( ( iCount + 1 ) != count ) - { - Q_strncpy( szTempBuf, " ", sizeof( szTempBuf ) ); - Q_strncat( szBuf, szTempBuf, sizeof( szTempBuf ), COPY_ALL_CHARACTERS ); - } - else - { - Q_strncpy( szTempBuf, "\n", sizeof( szTempBuf ) ); - Q_strncat( szBuf, szTempBuf, sizeof( szTempBuf ), COPY_ALL_CHARACTERS ); - } - } - - int nLength = strlen( szBuf ) + 1; - filesystem->Write( szBuf, nLength, m_hLogFile ); -} - -//------------------------------------- - -int CSave::GetWritePos() const -{ - return m_pData->GetCurPos(); -} - -//------------------------------------- - -void CSave::SetWritePos(int pos) -{ - m_pData->Seek(pos); -} - -//------------------------------------- - -void CSave::WriteShort( const short *value, int count ) -{ - BufferData( (const char *)value, sizeof(short) * count ); -} - -//------------------------------------- - -void CSave::WriteInt( const int *value, int count ) -{ - BufferData( (const char *)value, sizeof(int) * count ); -} - -//------------------------------------- - -void CSave::WriteBool( const bool *value, int count ) -{ - COMPILE_TIME_ASSERT( sizeof(bool) == sizeof(char) ); - BufferData( (const char *)value, sizeof(bool) * count ); -} - -//------------------------------------- - -void CSave::WriteFloat( const float *value, int count ) -{ - BufferData( (const char *)value, sizeof(float) * count ); -} - -//------------------------------------- - -void CSave::WriteData( const char *pdata , int size ) -{ - BufferData( pdata, size ); -} - -//------------------------------------- - -void CSave::WriteString( const char *pstring ) -{ - BufferData( pstring, strlen(pstring) + 1 ); -} - -//------------------------------------- - -void CSave::WriteString( const string_t *stringId, int count ) -{ - for ( int i = 0; i < count; i++ ) - { - const char *pString = STRING(stringId[i]); - BufferData( pString, strlen(pString)+1 ); - } -} - -//------------------------------------- - -void CSave::WriteVector( const Vector &value ) -{ - BufferData( (const char *)&value, sizeof(Vector) ); -} - -//------------------------------------- - -void CSave::WriteVector( const Vector *value, int count ) -{ - BufferData( (const char *)value, sizeof(Vector) * count ); -} - -void CSave::WriteQuaternion( const Quaternion &value ) -{ - BufferData( (const char *)&value, sizeof(Quaternion) ); -} - -//------------------------------------- - -void CSave::WriteQuaternion( const Quaternion *value, int count ) -{ - BufferData( (const char *)value, sizeof(Quaternion) * count ); -} - - -//------------------------------------- - -void CSave::WriteData( const char *pname, int size, const char *pdata ) -{ - BufferField( pname, size, pdata ); -} - -//------------------------------------- - -void CSave::WriteShort( const char *pname, const short *data, int count ) -{ - BufferField( pname, sizeof(short) * count, (const char *)data ); -} - -//------------------------------------- - -void CSave::WriteInt( const char *pname, const int *data, int count ) -{ - BufferField( pname, sizeof(int) * count, (const char *)data ); -} - -//------------------------------------- - -void CSave::WriteBool( const char *pname, const bool *data, int count ) -{ - COMPILE_TIME_ASSERT( sizeof(bool) == sizeof(char) ); - BufferField( pname, sizeof(bool) * count, (const char *)data ); -} - -//------------------------------------- - -void CSave::WriteFloat( const char *pname, const float *data, int count ) -{ - BufferField( pname, sizeof(float) * count, (const char *)data ); -} - -//------------------------------------- - -void CSave::WriteString( const char *pname, const char *pdata ) -{ - BufferField( pname, strlen(pdata) + 1, pdata ); -} - -//------------------------------------- - -void CSave::WriteString( const char *pname, const string_t *stringId, int count ) -{ - int i, size; - - size = 0; - for ( i = 0; i < count; i++ ) - size += strlen( STRING( stringId[i] ) ) + 1; - - WriteHeader( pname, size ); - WriteString( stringId, count ); -} - -//------------------------------------- - -void CSave::WriteVector( const char *pname, const Vector &value ) -{ - WriteVector( pname, &value, 1 ); -} - -//------------------------------------- - -void CSave::WriteVector( const char *pname, const Vector *value, int count ) -{ - WriteHeader( pname, sizeof(Vector) * count ); - BufferData( (const char *)value, sizeof(Vector) * count ); -} - -void CSave::WriteQuaternion( const char *pname, const Quaternion &value ) -{ - WriteQuaternion( pname, &value, 1 ); -} - -//------------------------------------- - -void CSave::WriteQuaternion( const char *pname, const Quaternion *value, int count ) -{ - WriteHeader( pname, sizeof(Quaternion) * count ); - BufferData( (const char *)value, sizeof(Quaternion) * count ); -} - - -//------------------------------------- - -void CSave::WriteVMatrix( const VMatrix *value, int count ) -{ - BufferData( (const char *)value, sizeof(VMatrix) * count ); -} - -//------------------------------------- - -void CSave::WriteVMatrix( const char *pname, const VMatrix *value, int count ) -{ - WriteHeader( pname, sizeof(VMatrix) * count ); - BufferData( (const char *)value, sizeof(VMatrix) * count ); -} - -//------------------------------------- - -void CSave::WriteVMatrixWorldspace( const VMatrix *value, int count ) -{ - for ( int i = 0; i < count; i++ ) - { - VMatrix tmp; - VMatrixOffset( tmp, value[i], -m_pGameInfo->GetLandmark() ); - BufferData( (const char *)&tmp, sizeof(VMatrix) ); - } -} - -//------------------------------------- - -void CSave::WriteVMatrixWorldspace( const char *pname, const VMatrix *value, int count ) -{ - WriteHeader( pname, sizeof(VMatrix) * count ); - WriteVMatrixWorldspace( value, count ); -} - -void CSave::WriteMatrix3x4Worldspace( const matrix3x4_t *value, int count ) -{ - Vector offset = -m_pGameInfo->GetLandmark(); - for ( int i = 0; i < count; i++ ) - { - matrix3x4_t tmp; - Matrix3x4Offset( tmp, value[i], offset ); - BufferData( (const char *)value, sizeof(matrix3x4_t) ); - } -} - -//------------------------------------- - -void CSave::WriteMatrix3x4Worldspace( const char *pname, const matrix3x4_t *value, int count ) -{ - WriteHeader( pname, sizeof(matrix3x4_t) * count ); - WriteMatrix3x4Worldspace( value, count ); -} - -void CSave::WriteInterval( const char *pname, const interval_t *value, int count ) -{ - WriteHeader( pname, sizeof( interval_t ) * count ); - WriteInterval( value, count ); -} - -void CSave::WriteInterval( const interval_t *value, int count ) -{ - BufferData( (const char *)value, count * sizeof( interval_t ) ); -} - -//------------------------------------- - -bool CSave::ShouldSaveField( const void *pData, typedescription_t *pField ) -{ - if ( !(pField->flags & FTYPEDESC_SAVE) || pField->fieldType == FIELD_VOID ) - return false; - - switch ( pField->fieldType ) - { - case FIELD_EMBEDDED: - { - if ( pField->flags & FTYPEDESC_PTR ) - { - AssertMsg( pField->fieldSize == 1, "Arrays of embedded pointer types presently unsupported by save/restore" ); - if ( pField->fieldSize != 1 ) - return false; - } - - AssertMsg( pField->td != NULL, "Embedded type appears to have not had type description implemented" ); - if ( pField->td == NULL ) - return false; - - if ( (pField->flags & FTYPEDESC_PTR) && !*((void **)pData) ) - return false; - - int nFieldCount = pField->fieldSize; - char *pTestData = (char *)( ( !(pField->flags & FTYPEDESC_PTR) ) ? pData : *((void **)pData) ); - while ( --nFieldCount >= 0 ) - { - typedescription_t *pTestField = pField->td->dataDesc; - typedescription_t *pLimit = pField->td->dataDesc + pField->td->dataNumFields; - - for ( ; pTestField < pLimit; ++pTestField ) - { - if ( ShouldSaveField( pTestData + pTestField->fieldOffset[ TD_OFFSET_NORMAL ], pTestField ) ) - return true; - } - - pTestData += pField->fieldSizeInBytes; - } - return false; - } - - case FIELD_CUSTOM: - { - // ask the data if it's empty - SaveRestoreFieldInfo_t fieldInfo = - { - const_cast(pData), - ((char *)pData) - pField->fieldOffset[ TD_OFFSET_NORMAL ], - pField - }; - if ( pField->pSaveRestoreOps->IsEmpty( fieldInfo ) ) - return false; - } - return true; - - case FIELD_EHANDLE: - { - if ( (pField->fieldSizeInBytes != pField->fieldSize * gSizes[pField->fieldType]) ) - { - Warning("WARNING! Field %s is using the wrong FIELD_ type!\nFix this or you'll see a crash.\n", pField->fieldName ); - Assert( 0 ); - } - - int *pEHandle = (int *)pData; - for ( int i = 0; i < pField->fieldSize; ++i, ++pEHandle ) - { - if ( (*pEHandle) != 0xFFFFFFFF ) - return true; - } - } - return false; - - default: - { - if ( (pField->fieldSizeInBytes != pField->fieldSize * gSizes[pField->fieldType]) ) - { - Warning("WARNING! Field %s is using the wrong FIELD_ type!\nFix this or you'll see a crash.\n", pField->fieldName ); - Assert( 0 ); - } - - // old byte-by-byte null check - if ( DataEmpty( (const char *)pData, pField->fieldSize * gSizes[pField->fieldType] ) ) - return false; - } - return true; - } -} - -//------------------------------------- -// Purpose: Writes all the fields that are client neutral. In the event of -// a librarization of save/restore, these would reside in the library -// - -bool CSave::WriteBasicField( const char *pname, void *pData, datamap_t *pRootMap, typedescription_t *pField ) -{ - switch( pField->fieldType ) - { - case FIELD_FLOAT: - WriteFloat( pField->fieldName, (float *)pData, pField->fieldSize ); - break; - - case FIELD_STRING: - WriteString( pField->fieldName, (string_t *)pData, pField->fieldSize ); - break; - - case FIELD_VECTOR: - WriteVector( pField->fieldName, (Vector *)pData, pField->fieldSize ); - break; - - case FIELD_QUATERNION: - WriteQuaternion( pField->fieldName, (Quaternion *)pData, pField->fieldSize ); - break; - - case FIELD_INTEGER: - WriteInt( pField->fieldName, (int *)pData, pField->fieldSize ); - break; - - case FIELD_BOOLEAN: - WriteBool( pField->fieldName, (bool *)pData, pField->fieldSize ); - break; - - case FIELD_SHORT: - WriteData( pField->fieldName, 2 * pField->fieldSize, ((char *)pData) ); - break; - - case FIELD_CHARACTER: - WriteData( pField->fieldName, pField->fieldSize, ((char *)pData) ); - break; - - case FIELD_COLOR32: - WriteData( pField->fieldName, 4*pField->fieldSize, (char *)pData ); - break; - - case FIELD_EMBEDDED: - { - AssertMsg( ( (pField->flags & FTYPEDESC_PTR) == 0 ) || (pField->fieldSize == 1), "Arrays of embedded pointer types presently unsupported by save/restore" ); - Assert( !(pField->flags & FTYPEDESC_PTR) || *((void **)pData) ); - int nFieldCount = pField->fieldSize; - char *pFieldData = (char *)( ( !(pField->flags & FTYPEDESC_PTR) ) ? pData : *((void **)pData) ); - - StartBlock( pField->fieldName ); - - while ( --nFieldCount >= 0 ) - { - WriteAll( pFieldData, pField->td ); - pFieldData += pField->fieldSizeInBytes; - } - - EndBlock(); - break; - } - - case FIELD_CUSTOM: - { - // Note it is up to the custom type implementor to handle arrays - StartBlock( pField->fieldName ); - - SaveRestoreFieldInfo_t fieldInfo = - { - pData, - ((char *)pData) - pField->fieldOffset[ TD_OFFSET_NORMAL ], - pField - }; - pField->pSaveRestoreOps->Save( fieldInfo, this ); - - EndBlock(); - break; - } - - default: - Warning( "Bad field type\n" ); - Assert(0); - return false; - } - - return true; -} - -//------------------------------------- - -bool CSave::WriteField( const char *pname, void *pData, datamap_t *pRootMap, typedescription_t *pField ) -{ -#ifdef _DEBUG - Log( pname, (fieldtype_t)pField->fieldType, pData, pField->fieldSize ); -#endif - - if ( pField->fieldType <= FIELD_CUSTOM ) - { - return WriteBasicField( pname, pData, pRootMap, pField ); - } - return WriteGameField( pname, pData, pRootMap, pField ); -} - -//------------------------------------- - -int CSave::CountFieldsToSave( const void *pBaseData, typedescription_t *pFields, int fieldCount ) -{ - int result = 0; - for ( int i = 0; i < fieldCount; i++ ) - { - if ( ShouldSaveField( (char *)pBaseData + pFields[i].fieldOffset[ TD_OFFSET_NORMAL ], &pFields[i] ) ) - result++; - } - return result; -} - -//------------------------------------- - -int CSave::WriteFields( const char *pname, const void *pBaseData, datamap_t *pRootMap, typedescription_t *pFields, int fieldCount ) -{ - int i, actualCount; - typedescription_t *pTest; - - // Empty fields will not be written, write out the actual number of fields to be written - actualCount = CountFieldsToSave( pBaseData, pFields, fieldCount ); - WriteInt( pname, &actualCount, 1 ); - - for ( i = 0; i < fieldCount; i++ ) - { - pTest = &pFields[ i ]; - - void *pOutputData = ( (char *)pBaseData + pTest->fieldOffset[ TD_OFFSET_NORMAL ] ); - if ( !ShouldSaveField( pOutputData, pTest ) ) - continue; - - if ( !WriteField( pname, pOutputData, pRootMap, pTest ) ) - break; - } - - return 1; -} - -//------------------------------------- -// Purpose: Recursively saves all the classes in an object, in reverse order (top down) -// Output : int 0 on failure, 1 on success - -int CSave::DoWriteAll( const void *pLeafObject, datamap_t *pLeafMap, datamap_t *pCurMap ) -{ - // save base classes first - if ( pCurMap->baseMap ) - { - int status = DoWriteAll( pLeafObject, pLeafMap, pCurMap->baseMap ); - if ( !status ) - return status; - } - - return WriteFields( pCurMap->dataClassName, pLeafObject, pLeafMap, pCurMap->dataDesc, pCurMap->dataNumFields ); -} - -//------------------------------------- - -void CSave::StartBlock( const char *pszBlockName ) -{ - WriteHeader( pszBlockName, 0 ); // placeholder - m_BlockStartStack.AddToTail( GetWritePos() ); -} - -//------------------------------------- - -void CSave::StartBlock() -{ - StartBlock( "" ); -} - -//------------------------------------- - -void CSave::EndBlock() -{ - int endPos = GetWritePos(); - int startPos = m_BlockStartStack[ m_BlockStartStack.Count() - 1 ]; - short sizeBlock = endPos - startPos; - - m_BlockStartStack.Remove( m_BlockStartStack.Count() - 1 ); - - // Move to the the location where the size of the block was written & rewrite the size - SetWritePos( startPos - sizeof(SaveRestoreRecordHeader_t) ); - BufferData( (const char *)&sizeBlock, sizeof(short) ); - - SetWritePos( endPos ); -} - -//------------------------------------- - -void CSave::BufferString( char *pdata, int len ) -{ - char c = 0; - - BufferData( pdata, len ); // Write the string - BufferData( &c, 1 ); // Write a null terminator -} - -//------------------------------------- - -int CSave::DataEmpty( const char *pdata, int size ) -{ - for ( int i = 0; i < size; i++ ) - { - if ( pdata[i] ) - return 0; - } - return 1; -} - -//------------------------------------- - -void CSave::BufferField( const char *pname, int size, const char *pdata ) -{ - WriteHeader( pname, size ); - BufferData( pdata, size ); -} - -//------------------------------------- - -void CSave::WriteHeader( const char *pname, int size ) -{ - short shortSize = size; - short hashvalue = m_pData->FindCreateSymbol( pname ); - if ( size > SHRT_MAX || size < 0 ) - { - Warning( "CSave::WriteHeader() size parameter exceeds 'short'!\n" ); - Assert(0); - } - - BufferData( (const char *)&shortSize, sizeof(short) ); - BufferData( (const char *)&hashvalue, sizeof(short) ); -} - -//------------------------------------- - -void CSave::BufferData( const char *pdata, int size ) -{ - if ( !m_pData ) - return; - - if ( !m_pData->Write( pdata, size ) ) - { - Warning( "Save/Restore overflow!\n" ); - Assert(0); - } -} - -//--------------------------------------------------------- -// -// Game centric save methods. -// -int CSave::EntityIndex( const edict_t *pentLookup ) -{ -#if !defined( CLIENT_DLL ) - if ( pentLookup == NULL ) - return -1; - return EntityIndex( CBaseEntity::Instance(pentLookup) ); -#else - Assert( !"CSave::EntityIndex( edict_t * ) not valid on client!" ); - return -1; -#endif -} - - -//------------------------------------- - -int CSave::EntityIndex( const CBaseEntity *pEntity ) -{ - if ( !m_pGameInfo || pEntity == NULL ) - return -1; - - int i; - entitytable_t *pTable; - - for ( i = 0; i < m_pGameInfo->NumEntities(); i++ ) - { - pTable = m_pGameInfo->GetEntityInfo( i ); - if ( pTable->hEnt == pEntity ) - return pTable->id; - } - return -1; -} - -//------------------------------------- - -int CSave::EntityFlagsSet( int entityIndex, int flags ) -{ - if ( !m_pGameInfo || entityIndex < 0 ) - return 0; - if ( entityIndex > m_pGameInfo->NumEntities() ) - return 0; - - m_pGameInfo->GetEntityInfo( entityIndex )->flags |= flags; - - return m_pGameInfo->GetEntityInfo( entityIndex )->flags; -} - -//------------------------------------- - -void CSave::WriteTime( const char *pname, const float *data, int count ) -{ - int i; - float tmp; - - WriteHeader( pname, sizeof(float) * count ); - for ( i = 0; i < count; i++ ) - { - // Always encode time as a delta from the current time so it can be re-based if loaded in a new level - // Times of 0 are never written to the file, so they will be restored as 0, not a relative time - Assert( data[i] != ZERO_TIME ); - - if ( data[i] == 0.0 ) - { - tmp = ZERO_TIME; - } - else if ( data[i] == INVALID_TIME || data[i] == FLT_MAX ) - { - tmp = data[i]; - } - else - { - tmp = data[i] - m_pGameInfo->GetBaseTime(); - if ( fabsf( tmp ) < 0.001 ) // never allow a time to become zero due to rebasing - tmp = 0.001; - } - - WriteData( (const char *)&tmp, sizeof(float) ); - } -} - -//------------------------------------- - -void CSave::WriteTime( const float *data, int count ) -{ - int i; - float tmp; - - for ( i = 0; i < count; i++ ) - { - // Always encode time as a delta from the current time so it can be re-based if loaded in a new level - // Times of 0 are never written to the file, so they will be restored as 0, not a relative time - if ( data[i] == 0.0 ) - { - tmp = ZERO_TIME; - } - else if ( data[i] == INVALID_TIME || data[i] == FLT_MAX ) - { - tmp = data[i]; - } - else - { - tmp = data[i] - m_pGameInfo->GetBaseTime(); - if ( fabsf( tmp ) < 0.001 ) // never allow a time to become zero due to rebasing - tmp = 0.001; - } - - WriteData( (const char *)&tmp, sizeof(float) ); - } -} - -void CSave::WriteTick( const char *pname, const int *data, int count ) -{ - WriteHeader( pname, sizeof(int) * count ); - WriteTick( data, count ); -} - -//------------------------------------- - -void CSave::WriteTick( const int *data, int count ) -{ - int i; - int tmp; - - int baseTick = TIME_TO_TICKS( m_pGameInfo->GetBaseTime() ); - - for ( i = 0; i < count; i++ ) - { - // Always encode time as a delta from the current time so it can be re-based if loaded in a new level - // Times of 0 are never written to the file, so they will be restored as 0, not a relative time - tmp = data[ i ]; - if ( data[ i ] == TICK_NEVER_THINK ) - { - tmp = TICK_NEVER_THINK_ENCODE; - } - else - { - // Rebase it... - tmp -= baseTick; - } - WriteData( (const char *)&tmp, sizeof(int) ); - } -} -//------------------------------------- - -void CSave::WritePositionVector( const char *pname, const Vector &value ) -{ - Vector tmp = value; - - if ( tmp != vec3_invalid ) - tmp -= m_pGameInfo->GetLandmark(); - - WriteVector( pname, tmp ); -} - -//------------------------------------- - -void CSave::WritePositionVector( const Vector &value ) -{ - Vector tmp = value; - - if ( tmp != vec3_invalid ) - tmp -= m_pGameInfo->GetLandmark(); - - WriteVector( tmp ); -} - -//------------------------------------- - -void CSave::WritePositionVector( const char *pname, const Vector *value, int count ) -{ - WriteHeader( pname, sizeof(Vector) * count ); - WritePositionVector( value, count ); -} - -//------------------------------------- - -void CSave::WritePositionVector( const Vector *value, int count ) -{ - int i; - Vector tmp; - for ( i = 0; i < count; i++ ) - { - Vector tmp = value[i]; - - if ( tmp != vec3_invalid ) - tmp -= m_pGameInfo->GetLandmark(); - - WriteData( (const char *)&tmp.x, sizeof(Vector) ); - } -} - -//------------------------------------- - -void CSave::WriteFunction( datamap_t *pRootMap, const char *pname, const int *data, int count ) -{ - AssertMsg( count == 1, "Arrays of functions not presently supported" ); - const char *functionName = UTIL_FunctionToName( pRootMap, (void *)(*data) ); - - if ( functionName ) - { - BufferField( pname, strlen(functionName) + 1, functionName ); - } - else - { - Warning( "Invalid function pointer in entity!\n" ); - Assert(0); - } -} - -//------------------------------------- - -void CSave::WriteEntityPtr( const char *pname, CBaseEntity **ppEntity, int count ) -{ - AssertMsg( count <= MAX_ENTITYARRAY, "Array of entities or ehandles exceeds limit supported by save/restore" ); - int entityArray[MAX_ENTITYARRAY]; - for ( int i = 0; i < count && i < MAX_ENTITYARRAY; i++ ) - { - entityArray[i] = EntityIndex( ppEntity[i] ); - } - WriteInt( pname, entityArray, count ); -} - -//------------------------------------- - -void CSave::WriteEntityPtr( CBaseEntity **ppEntity, int count ) -{ - AssertMsg( count <= MAX_ENTITYARRAY, "Array of entities or ehandles exceeds limit supported by save/restore" ); - int entityArray[MAX_ENTITYARRAY]; - for ( int i = 0; i < count && i < MAX_ENTITYARRAY; i++ ) - { - entityArray[i] = EntityIndex( ppEntity[i] ); - } - WriteInt( entityArray, count ); -} - -//------------------------------------- - -void CSave::WriteEdictPtr( const char *pname, edict_t **ppEdict, int count ) -{ - AssertMsg( count <= MAX_ENTITYARRAY, "Array of entities or ehandles exceeds limit supported by save/restore" ); - int entityArray[MAX_ENTITYARRAY]; - for ( int i = 0; i < count && i < MAX_ENTITYARRAY; i++ ) - { - entityArray[i] = EntityIndex( ppEdict[i] ); - } - WriteInt( pname, entityArray, count ); -} - -//------------------------------------- - -void CSave::WriteEdictPtr( edict_t **ppEdict, int count ) -{ - AssertMsg( count <= MAX_ENTITYARRAY, "Array of entities or ehandles exceeds limit supported by save/restore" ); - int entityArray[MAX_ENTITYARRAY]; - for ( int i = 0; i < count && i < MAX_ENTITYARRAY; i++ ) - { - entityArray[i] = EntityIndex( ppEdict[i] ); - } - WriteInt( entityArray, count ); -} - -//------------------------------------- - -void CSave::WriteEHandle( const char *pname, const EHANDLE *pEHandle, int count ) -{ - AssertMsg( count <= MAX_ENTITYARRAY, "Array of entities or ehandles exceeds limit supported by save/restore" ); - int entityArray[MAX_ENTITYARRAY]; - for ( int i = 0; i < count && i < MAX_ENTITYARRAY; i++ ) - { - entityArray[i] = EntityIndex( (CBaseEntity *)(const_cast(pEHandle)[i]) ); - } - WriteInt( pname, entityArray, count ); -} - -//------------------------------------- - -void CSave::WriteEHandle( const EHANDLE *pEHandle, int count ) -{ - AssertMsg( count <= MAX_ENTITYARRAY, "Array of entities or ehandles exceeds limit supported by save/restore" ); - int entityArray[MAX_ENTITYARRAY]; - for ( int i = 0; i < count && i < MAX_ENTITYARRAY; i++ ) - { - entityArray[i] = EntityIndex( (CBaseEntity *)(const_cast(pEHandle)[i]) ); - } - WriteInt( entityArray, count ); -} - -//------------------------------------- -// Purpose: Writes all the fields that are not client neutral. In the event of -// a librarization of save/restore, these would not reside in the library - -bool CSave::WriteGameField( const char *pname, void *pData, datamap_t *pRootMap, typedescription_t *pField ) -{ - switch( pField->fieldType ) - { - case FIELD_CLASSPTR: - WriteEntityPtr( pField->fieldName, (CBaseEntity **)pData, pField->fieldSize ); - break; - - case FIELD_EDICT: - WriteEdictPtr( pField->fieldName, (edict_t **)pData, pField->fieldSize ); - break; - - case FIELD_EHANDLE: - WriteEHandle( pField->fieldName, (EHANDLE *)pData, pField->fieldSize ); - break; - - case FIELD_POSITION_VECTOR: - WritePositionVector( pField->fieldName, (Vector *)pData, pField->fieldSize ); - break; - - case FIELD_TIME: - WriteTime( pField->fieldName, (float *)pData, pField->fieldSize ); - break; - - case FIELD_TICK: - WriteTick( pField->fieldName, (int *)pData, pField->fieldSize ); - break; - - case FIELD_MODELINDEX: - { - int nModelIndex = *(int*)pData; - string_t strModelName = NULL_STRING; - const model_t *pModel = modelinfo->GetModel( nModelIndex ); - if ( pModel ) - { - strModelName = AllocPooledString( modelinfo->GetModelName( pModel ) ); - } - WriteString( pField->fieldName, (string_t *)&strModelName, pField->fieldSize ); - } - break; - - case FIELD_MATERIALINDEX: - { - int nMateralIndex = *(int*)pData; - string_t strMaterialName = NULL_STRING; - const char *pMaterialName = GetMaterialNameFromIndex( nMateralIndex ); - if ( pMaterialName ) - { - strMaterialName = MAKE_STRING( pMaterialName ); - } - WriteString( pField->fieldName, (string_t *)&strMaterialName, pField->fieldSize ); - } - break; - - case FIELD_MODELNAME: - case FIELD_SOUNDNAME: - WriteString( pField->fieldName, (string_t *)pData, pField->fieldSize ); - break; - - // For now, just write the address out, we're not going to change memory while doing this yet! - case FIELD_FUNCTION: - WriteFunction( pRootMap, pField->fieldName, (int *)(char *)pData, pField->fieldSize ); - break; - - case FIELD_VMATRIX: - WriteVMatrix( pField->fieldName, (VMatrix *)pData, pField->fieldSize ); - break; - case FIELD_VMATRIX_WORLDSPACE: - WriteVMatrixWorldspace( pField->fieldName, (VMatrix *)pData, pField->fieldSize ); - break; - - case FIELD_MATRIX3X4_WORLDSPACE: - WriteMatrix3x4Worldspace( pField->fieldName, (const matrix3x4_t *)pData, pField->fieldSize ); - break; - - case FIELD_INTERVAL: - WriteInterval( pField->fieldName, (interval_t *)pData, pField->fieldSize ); - break; - - default: - Warning( "Bad field type\n" ); - Assert(0); - return false; - } - - return true; -} - -//----------------------------------------------------------------------------- -// -// CRestore -// -//----------------------------------------------------------------------------- - -CRestore::CRestore( CSaveRestoreData *pdata ) - : m_pData( pdata ), - m_pGameInfo( pdata ), - m_global( 0 ), - m_precache( true ) -{ - m_BlockEndStack.EnsureCapacity( 32 ); -} - -//------------------------------------- - -int CRestore::GetReadPos() const -{ - return m_pData->GetCurPos(); -} - -//------------------------------------- - -void CRestore::SetReadPos( int pos ) -{ - m_pData->Seek(pos); -} - -//------------------------------------- - -const char *CRestore::StringFromHeaderSymbol( int symbol ) -{ - const char *pszResult = m_pData->StringFromSymbol( symbol ); - return ( pszResult ) ? pszResult : ""; -} - -//------------------------------------- -// Purpose: Reads all the fields that are client neutral. In the event of -// a librarization of save/restore, these would reside in the library - -void CRestore::ReadBasicField( const SaveRestoreRecordHeader_t &header, void *pDest, datamap_t *pRootMap, typedescription_t *pField ) -{ - switch( pField->fieldType ) - { - case FIELD_FLOAT: - { - ReadFloat( (float *)pDest, pField->fieldSize, header.size ); - break; - } - case FIELD_STRING: - { - ReadString( (string_t *)pDest, pField->fieldSize, header.size ); - break; - } - - case FIELD_VECTOR: - { - ReadVector( (Vector *)pDest, pField->fieldSize, header.size ); - break; - } - - case FIELD_QUATERNION: - { - ReadQuaternion( (Quaternion *)pDest, pField->fieldSize, header.size ); - break; - } - - case FIELD_INTEGER: - { - ReadInt( (int *)pDest, pField->fieldSize, header.size ); - break; - } - - case FIELD_BOOLEAN: - { - ReadBool( (bool *)pDest, pField->fieldSize, header.size ); - break; - } - - case FIELD_SHORT: - { - ReadShort( (short *)pDest, pField->fieldSize, header.size ); - break; - } - - case FIELD_CHARACTER: - { - ReadData( (char *)pDest, pField->fieldSize, header.size ); - break; - } - - case FIELD_COLOR32: - { - COMPILE_TIME_ASSERT( sizeof(color32) == sizeof(int) ); - ReadInt( (int *)pDest, pField->fieldSize, header.size ); - break; - } - - case FIELD_EMBEDDED: - { - AssertMsg( (( pField->flags & FTYPEDESC_PTR ) == 0) || (pField->fieldSize == 1), "Arrays of embedded pointer types presently unsupported by save/restore" ); -#ifdef _DEBUG - int startPos = GetReadPos(); -#endif - if ( !(pField->flags & FTYPEDESC_PTR) || *((void **)pDest) ) - { - int nFieldCount = pField->fieldSize; - char *pFieldData = (char *)( ( !(pField->flags & FTYPEDESC_PTR) ) ? pDest : *((void **)pDest) ); - while ( --nFieldCount >= 0 ) - { - // No corresponding "block" (see write) as it was used as the header of the field - ReadAll( pFieldData, pField->td ); - pFieldData += pField->fieldSizeInBytes; - } - Assert( GetReadPos() - startPos == header.size ); - } - else - { - SetReadPos( GetReadPos() + header.size ); - Warning( "Attempted to restore FIELD_EMBEDDEDBYREF %s but there is no destination memory\n", pField->fieldName ); - } - break; - - } - case FIELD_CUSTOM: - { - // No corresponding "block" (see write) as it was used as the header of the field - int posNextField = GetReadPos() + header.size; - - SaveRestoreFieldInfo_t fieldInfo = - { - pDest, - ((char *)pDest) - pField->fieldOffset[ TD_OFFSET_NORMAL ], - pField - }; - - pField->pSaveRestoreOps->Restore( fieldInfo, this ); - - Assert( posNextField >= GetReadPos() ); - SetReadPos( posNextField ); - break; - } - - default: - Warning( "Bad field type\n" ); - Assert(0); - } -} - -//------------------------------------- - -void CRestore::ReadField( const SaveRestoreRecordHeader_t &header, void *pDest, datamap_t *pRootMap, typedescription_t *pField ) -{ - if ( pField->fieldType <= FIELD_CUSTOM ) - ReadBasicField( header, pDest, pRootMap, pField ); - else - ReadGameField( header, pDest, pRootMap, pField ); -} - -//------------------------------------- - -bool CRestore::ShouldReadField( typedescription_t *pField ) -{ - if ( (pField->flags & FTYPEDESC_SAVE) == 0 ) - return false; - - if ( m_global && (pField->flags & FTYPEDESC_GLOBAL) ) - return false; - - return true; -} - -//------------------------------------- - -typedescription_t *CRestore::FindField( const char *pszFieldName, typedescription_t *pFields, int fieldCount, int *pCookie ) -{ - int &fieldNumber = *pCookie; - if ( pszFieldName ) - { - typedescription_t *pTest; - - for ( int i = 0; i < fieldCount; i++ ) - { - pTest = &pFields[fieldNumber]; - - ++fieldNumber; - if ( fieldNumber == fieldCount ) - fieldNumber = 0; - - if ( stricmp( pTest->fieldName, pszFieldName ) == 0 ) - return pTest; - } - } - - fieldNumber = 0; - return NULL; -} - -//------------------------------------- - -bool CRestore::ShouldEmptyField( typedescription_t *pField ) -{ - // don't clear out fields that don't get saved, or that are handled specially - if ( !( pField->flags & FTYPEDESC_SAVE ) ) - return false; - - // Don't clear global fields - if ( m_global && (pField->flags & FTYPEDESC_GLOBAL) ) - return false; - - return true; -} - -//------------------------------------- - -void CRestore::EmptyFields( void *pBaseData, typedescription_t *pFields, int fieldCount ) -{ - int i; - for ( i = 0; i < fieldCount; i++ ) - { - typedescription_t *pField = &pFields[i]; - if ( !ShouldEmptyField( pField ) ) - continue; - - void *pFieldData = (char *)pBaseData + pField->fieldOffset[ TD_OFFSET_NORMAL ]; - switch( pField->fieldType ) - { - case FIELD_CUSTOM: - { - SaveRestoreFieldInfo_t fieldInfo = - { - pFieldData, - pBaseData, - pField - }; - pField->pSaveRestoreOps->MakeEmpty( fieldInfo ); - } - break; - - case FIELD_EMBEDDED: - { - if ( (pField->flags & FTYPEDESC_PTR) && !*((void **)pFieldData) ) - break; - - int nFieldCount = pField->fieldSize; - char *pFieldMemory = (char *)( ( !(pField->flags & FTYPEDESC_PTR) ) ? pFieldData : *((void **)pFieldData) ); - while ( --nFieldCount >= 0 ) - { - EmptyFields( pFieldMemory, pField->td->dataDesc, pField->td->dataNumFields ); - pFieldMemory += pField->fieldSizeInBytes; - } - } - break; - - default: - // NOTE: If you hit this assertion, you've got a bug where you're using - // the wrong field type for your field - if ( pField->fieldSizeInBytes != pField->fieldSize * gSizes[pField->fieldType] ) - { - Warning("WARNING! Field %s is using the wrong FIELD_ type!\nFix this or you'll see a crash.\n", pField->fieldName ); - Assert( 0 ); - } - memset( pFieldData, (pField->fieldType != FIELD_EHANDLE) ? 0 : 0xFF, pField->fieldSize * gSizes[pField->fieldType] ); - break; - } - } -} - -//------------------------------------- - -void CRestore::StartBlock( SaveRestoreRecordHeader_t *pHeader ) -{ - ReadHeader( pHeader ); - m_BlockEndStack.AddToTail( GetReadPos() + pHeader->size ); -} - -//------------------------------------- - -void CRestore::StartBlock( char szBlockName[] ) -{ - SaveRestoreRecordHeader_t header; - StartBlock( &header ); - Q_strncpy( szBlockName, StringFromHeaderSymbol( header.symbol ), SIZE_BLOCK_NAME_BUF ); -} - -//------------------------------------- - -void CRestore::StartBlock() -{ - char szBlockName[SIZE_BLOCK_NAME_BUF]; - StartBlock( szBlockName ); -} - -//------------------------------------- - -void CRestore::EndBlock() -{ - int endPos = m_BlockEndStack[ m_BlockEndStack.Count() - 1 ]; - m_BlockEndStack.Remove( m_BlockEndStack.Count() - 1 ); - SetReadPos( endPos ); -} - -//------------------------------------- - -int CRestore::ReadFields( const char *pname, void *pBaseData, datamap_t *pRootMap, typedescription_t *pFields, int fieldCount ) -{ - static int lastName = -1; - Verify( ReadShort() == sizeof(int) ); // First entry should be an int - int symName = m_pData->FindCreateSymbol(pname); - - // Check the struct name - int curSym = ReadShort(); - if ( curSym != symName ) // Field Set marker - { - const char *pLastName = m_pData->StringFromSymbol( lastName ); - const char *pCurName = m_pData->StringFromSymbol( curSym ); - Msg( "Expected %s found %s ( raw '%s' )! (prev: %s)\n", pname, pCurName, BufferPointer(), pLastName ); - Msg( "Field type name may have changed or inheritance graph changed, save file is suspect\n" ); - m_pData->Rewind( 2*sizeof(short) ); - return 0; - } - lastName = symName; - - // Clear out base data - EmptyFields( pBaseData, pFields, fieldCount ); - - // Skip over the struct name - int i; - int nFieldsSaved = ReadInt(); // Read field count - int searchCookie = 0; // Make searches faster, most data is read/written in the same order - SaveRestoreRecordHeader_t header; - - for ( i = 0; i < nFieldsSaved; i++ ) - { - ReadHeader( &header ); - - typedescription_t *pField = FindField( m_pData->StringFromSymbol( header.symbol ), pFields, fieldCount, &searchCookie); - if ( pField && ShouldReadField( pField ) ) - { - ReadField( header, ((char *)pBaseData + pField->fieldOffset[ TD_OFFSET_NORMAL ]), pRootMap, pField ); - } - else - { - BufferSkipBytes( header.size ); // Advance to next field - } - } - - return 1; -} - -//------------------------------------- - -void CRestore::ReadHeader( SaveRestoreRecordHeader_t *pheader ) -{ - if ( pheader != NULL ) - { - Assert( pheader!=NULL ); - pheader->size = ReadShort(); // Read field size - pheader->symbol = ReadShort(); // Read field name token - } - else - { - BufferSkipBytes( sizeof(short) * 2 ); - } -} - -//------------------------------------- - -short CRestore::ReadShort( void ) -{ - short tmp = 0; - - BufferReadBytes( (char *)&tmp, sizeof(short) ); - - return tmp; -} - -//------------------------------------- - -int CRestore::ReadInt( void ) -{ - int tmp = 0; - - BufferReadBytes( (char *)&tmp, sizeof(int) ); - - return tmp; -} - -//------------------------------------- -// Purpose: Recursively restores all the classes in an object, in reverse order (top down) -// Output : int 0 on failure, 1 on success - -int CRestore::DoReadAll( void *pLeafObject, datamap_t *pLeafMap, datamap_t *pCurMap ) -{ - // restore base classes first - if ( pCurMap->baseMap ) - { - int status = DoReadAll( pLeafObject, pLeafMap, pCurMap->baseMap ); - if ( !status ) - return status; - } - - return ReadFields( pCurMap->dataClassName, pLeafObject, pLeafMap, pCurMap->dataDesc, pCurMap->dataNumFields ); -} - -//------------------------------------- - -char *CRestore::BufferPointer( void ) -{ - if ( !m_pData ) - return NULL; - - return m_pData->AccessCurPos(); -} - -//------------------------------------- - -void CRestore::BufferReadBytes( char *pOutput, int size ) -{ - Assert( m_pData !=NULL ); - - if ( !m_pData || m_pData->BytesAvailable() == 0 ) - return; - - if ( !m_pData->Read( pOutput, size ) ) - { - Warning( "Restore underflow!\n" ); - Assert(0); - } -} - -//------------------------------------- - -void CRestore::BufferSkipBytes( int bytes ) -{ - BufferReadBytes( NULL, bytes ); -} - -//------------------------------------- - -int CRestore::ReadShort( short *pValue, int nElems, int nBytesAvailable ) -{ - return ReadSimple( pValue, nElems, nBytesAvailable ); -} - -//------------------------------------- - -int CRestore::ReadInt( int *pValue, int nElems, int nBytesAvailable ) -{ - return ReadSimple( pValue, nElems, nBytesAvailable ); -} - -//------------------------------------- - -int CRestore::ReadBool( bool *pValue, int nElems, int nBytesAvailable ) -{ - COMPILE_TIME_ASSERT( sizeof(bool) == sizeof(char) ); - return ReadSimple( pValue, nElems, nBytesAvailable ); -} - -//------------------------------------- - -int CRestore::ReadFloat( float *pValue, int nElems, int nBytesAvailable ) -{ - return ReadSimple( pValue, nElems, nBytesAvailable ); -} - -//------------------------------------- - -int CRestore::ReadData( char *pData, int size, int nBytesAvailable ) -{ - return ReadSimple( pData, size, nBytesAvailable ); -} - -//------------------------------------- - -void CRestore::ReadString( char *pDest, int nSizeDest, int nBytesAvailable ) -{ - const char *pString = BufferPointer(); - if ( !nBytesAvailable ) - nBytesAvailable = strlen( pString ) + 1; - BufferSkipBytes( nBytesAvailable ); - - Q_strncpy(pDest, pString, nSizeDest ); -} - -//------------------------------------- - -int CRestore::ReadString( string_t *pValue, int nElems, int nBytesAvailable ) -{ - AssertMsg( nBytesAvailable > 0, "CRestore::ReadString() implementation does not currently support unspecified bytes available"); - - int i; - char *pString = BufferPointer(); - char *pLimit = pString + nBytesAvailable; - for ( i = 0; i < nElems && pString < pLimit; i++ ) - { - if ( *((char *)pString) == 0 ) - pValue[i] = NULL_STRING; - else - pValue[i] = AllocPooledString( (char *)pString ); - - while (*pString) - pString++; - pString++; - } - - BufferSkipBytes( nBytesAvailable ); - - return i; -} - -//------------------------------------- - -int CRestore::ReadVector( Vector *pValue) -{ - BufferReadBytes( (char *)pValue, sizeof(Vector) ); - return 1; -} - -//------------------------------------- - -int CRestore::ReadVector( Vector *pValue, int nElems, int nBytesAvailable ) -{ - return ReadSimple( pValue, nElems, nBytesAvailable ); -} - -int CRestore::ReadQuaternion( Quaternion *pValue) -{ - BufferReadBytes( (char *)pValue, sizeof(Quaternion) ); - return 1; -} - -//------------------------------------- - -int CRestore::ReadQuaternion( Quaternion *pValue, int nElems, int nBytesAvailable ) -{ - return ReadSimple( pValue, nElems, nBytesAvailable ); -} - -//------------------------------------- -int CRestore::ReadVMatrix( VMatrix *pValue, int nElems, int nBytesAvailable ) -{ - return ReadSimple( pValue, nElems, nBytesAvailable ); -} - - -int CRestore::ReadVMatrixWorldspace( VMatrix *pValue, int nElems, int nBytesAvailable ) -{ - Vector basePosition = m_pGameInfo->GetLandmark(); - VMatrix tmp; - - for ( int i = 0; i < nElems; i++ ) - { - BufferReadBytes( (char *)&tmp, sizeof(float)*16 ); - - VMatrixOffset( pValue[i], tmp, basePosition ); - } - return nElems; -} - - -int CRestore::ReadMatrix3x4Worldspace( matrix3x4_t *pValue, int nElems, int nBytesAvailable ) -{ - Vector basePosition = m_pGameInfo->GetLandmark(); - matrix3x4_t tmp; - - for ( int i = 0; i < nElems; i++ ) - { - BufferReadBytes( (char *)&tmp, sizeof(matrix3x4_t) ); - - Matrix3x4Offset( pValue[i], tmp, basePosition ); - } - return nElems; -} - -int CRestore::ReadInterval( interval_t *interval, int count, int nBytesAvailable ) -{ - return ReadSimple( interval, count, nBytesAvailable ); -} - -//--------------------------------------------------------- -// -// Game centric restore methods -// - -CBaseEntity *CRestore::EntityFromIndex( int entityIndex ) -{ - if ( !m_pGameInfo || entityIndex < 0 ) - return NULL; - - int i; - entitytable_t *pTable; - - for ( i = 0; i < m_pGameInfo->NumEntities(); i++ ) - { - pTable = m_pGameInfo->GetEntityInfo( i ); - if ( pTable->id == entityIndex ) - return pTable->hEnt; - } - return NULL; -} - -//------------------------------------- - -int CRestore::ReadEntityPtr( CBaseEntity **ppEntity, int count, int nBytesAvailable ) -{ - AssertMsg( count <= MAX_ENTITYARRAY, "Array of entities or ehandles exceeds limit supported by save/restore" ); - int entityArray[MAX_ENTITYARRAY]; - - int nRead = ReadInt( entityArray, count, nBytesAvailable ); - - for ( int i = 0; i < nRead; i++ ) // nRead is never greater than count - { - ppEntity[i] = EntityFromIndex( entityArray[i] ); - } - - if ( nRead < count) - { - memset( &ppEntity[nRead], 0, ( count - nRead ) * sizeof(ppEntity[0]) ); - } - - return nRead; -} - -//------------------------------------- -int CRestore::ReadEdictPtr( edict_t **ppEdict, int count, int nBytesAvailable ) -{ -#if !defined( CLIENT_DLL ) - AssertMsg( count <= MAX_ENTITYARRAY, "Array of entities or ehandles exceeds limit supported by save/restore" ); - int entityArray[MAX_ENTITYARRAY]; - CBaseEntity *pEntity; - - int nRead = ReadInt( entityArray, count, nBytesAvailable ); - - for ( int i = 0; i < nRead; i++ ) // nRead is never greater than count - { - pEntity = EntityFromIndex( entityArray[i] ); - ppEdict[i] = (pEntity) ? pEntity->edict() : NULL; - } - - if ( nRead < count) - { - memset( &ppEdict[nRead], 0, ( count - nRead ) * sizeof(ppEdict[0]) ); - } - - return nRead; -#else - return 0; -#endif -} - - -//------------------------------------- - -int CRestore::ReadEHandle( EHANDLE *pEHandle, int count, int nBytesAvailable ) -{ - AssertMsg( count <= MAX_ENTITYARRAY, "Array of entities or ehandles exceeds limit supported by save/restore" ); - int entityArray[MAX_ENTITYARRAY]; - - int nRead = ReadInt( entityArray, count, nBytesAvailable ); - - for ( int i = 0; i < nRead; i++ ) // nRead is never greater than count - { - pEHandle[i] = EntityFromIndex( entityArray[i] ); - } - - if ( nRead < count) - { - memset( &pEHandle[nRead], 0xFF, ( count - nRead ) * sizeof(pEHandle[0]) ); - } - - return nRead; -} - -//------------------------------------- -// Purpose: Reads all the fields that are not client neutral. In the event of -// a librarization of save/restore, these would NOT reside in the library - -void CRestore::ReadGameField( const SaveRestoreRecordHeader_t &header, void *pDest, datamap_t *pRootMap, typedescription_t *pField ) -{ - switch( pField->fieldType ) - { - case FIELD_POSITION_VECTOR: - { - ReadPositionVector( (Vector *)pDest, pField->fieldSize, header.size ); - break; - } - - case FIELD_TIME: - { - ReadTime( (float *)pDest, pField->fieldSize, header.size ); - break; - } - - case FIELD_TICK: - { - ReadTick( (int *)pDest, pField->fieldSize, header.size ); - break; - } - - case FIELD_FUNCTION: - { - ReadFunction( pRootMap, (void **)pDest, pField->fieldSize, header.size ); - break; - } - - case FIELD_MODELINDEX: - { - int *pModelIndex = (int*)pDest; - string_t *pModelName = (string_t *)stackalloc( pField->fieldSize * sizeof(string_t) ); - int nRead = ReadString( pModelName, pField->fieldSize, header.size ); - - for ( int i = 0; i < nRead; i++ ) - { - if ( pModelName[i] == NULL_STRING ) - { - pModelIndex[i] = -1; - continue; - } - - pModelIndex[i] = modelinfo->GetModelIndex( STRING( pModelName[i] ) ); - -#if !defined( CLIENT_DLL ) - if ( m_precache ) - { - CBaseEntity::PrecacheModel( STRING( pModelName[i] ) ); - } -#endif - } - break; - } - - case FIELD_MATERIALINDEX: - { - int *pMaterialIndex = (int*)pDest; - string_t *pMaterialName = (string_t *)stackalloc( pField->fieldSize * sizeof(string_t) ); - int nRead = ReadString( pMaterialName, pField->fieldSize, header.size ); - - for ( int i = 0; i < nRead; i++ ) - { - if ( pMaterialName[i] == NULL_STRING ) - { - pMaterialIndex[i] = 0; - continue; - } - - pMaterialIndex[i] = GetMaterialIndex( STRING( pMaterialName[i] ) ); - -#if !defined( CLIENT_DLL ) - if ( m_precache ) - { - PrecacheMaterial( STRING( pMaterialName[i] ) ); - } -#endif - } - break; - } - - case FIELD_MODELNAME: - case FIELD_SOUNDNAME: - { - string_t *pStringDest = (string_t *)pDest; - int nRead = ReadString( pStringDest, pField->fieldSize, header.size ); - if ( m_precache ) - { - for ( int i = 0; i < nRead; i++ ) - { - if ( pStringDest[i] != NULL_STRING ) - { -#if !defined( CLIENT_DLL ) - if ( pField->fieldType == FIELD_MODELNAME ) - { - CBaseEntity::PrecacheModel( STRING( pStringDest[i] ) ); - } - else if ( pField->fieldType == FIELD_SOUNDNAME ) - { - CBaseEntity::PrecacheScriptSound( STRING( pStringDest[i] ) ); - } -#endif - } - } - } - break; - } - - case FIELD_CLASSPTR: - ReadEntityPtr( (CBaseEntity **)pDest, pField->fieldSize, header.size ); - break; - - case FIELD_EDICT: -#if !defined( CLIENT_DLL ) - ReadEdictPtr( (edict_t **)pDest, pField->fieldSize, header.size ); -#else - Assert( !"FIELD_EDICT not valid for client .dll" ); -#endif - break; - case FIELD_EHANDLE: - ReadEHandle( (EHANDLE *)pDest, pField->fieldSize, header.size ); - break; - - case FIELD_VMATRIX: - { - ReadVMatrix( (VMatrix *)pDest, pField->fieldSize, header.size ); - break; - } - - case FIELD_VMATRIX_WORLDSPACE: - ReadVMatrixWorldspace( (VMatrix *)pDest, pField->fieldSize, header.size ); - break; - - case FIELD_MATRIX3X4_WORLDSPACE: - ReadMatrix3x4Worldspace( (matrix3x4_t *)pDest, pField->fieldSize, header.size ); - break; - - case FIELD_INTERVAL: - ReadInterval( (interval_t *)pDest, pField->fieldSize, header.size ); - break; - - default: - Warning( "Bad field type\n" ); - Assert(0); - } -} - -//------------------------------------- - -int CRestore::ReadTime( float *pValue, int count, int nBytesAvailable ) -{ - float baseTime = m_pGameInfo->GetBaseTime(); - int nRead = ReadFloat( pValue, count, nBytesAvailable ); - - for ( int i = nRead - 1; i >= 0; i-- ) - { - if ( pValue[i] == ZERO_TIME ) - pValue[i] = 0.0; - else if ( pValue[i] != INVALID_TIME && pValue[i] != FLT_MAX ) - pValue[i] += baseTime; - } - - return nRead; -} - -int CRestore::ReadTick( int *pValue, int count, int nBytesAvailable ) -{ - // HACK HACK: Adding 0.1f here makes sure that all tick times read - // from .sav file which are near the basetime will end up just ahead of - // the base time, because we are restoring we'll have a slow frame of the - // max frametime of 0.1 seconds and that could otherwise cause all of our - // think times to get synchronized to each other... sigh. ywb... - int baseTick = TIME_TO_TICKS( m_pGameInfo->GetBaseTime() + 0.1f ); - int nRead = ReadInt( pValue, count, nBytesAvailable ); - - for ( int i = nRead - 1; i >= 0; i-- ) - { - if ( pValue[ i ] != TICK_NEVER_THINK_ENCODE ) - { - // Rebase it - pValue[i] += baseTick; - } - else - { - // Slam to -1 value - pValue[ i ] = TICK_NEVER_THINK; - } - } - - return nRead; -} - -//------------------------------------- - -int CRestore::ReadPositionVector( Vector *pValue ) -{ - return ReadPositionVector( pValue, 1, sizeof(Vector) ); -} - -//------------------------------------- - -int CRestore::ReadPositionVector( Vector *pValue, int count, int nBytesAvailable ) -{ - Vector basePosition = m_pGameInfo->GetLandmark(); - int nRead = ReadVector( pValue, count, nBytesAvailable ); - - for ( int i = nRead - 1; i >= 0; i-- ) - { - if ( pValue[i] != vec3_invalid ) - pValue[i] += basePosition; - } - - return nRead; -} - -//------------------------------------- - -int CRestore::ReadFunction( datamap_t *pMap, void **pValue, int count, int nBytesAvailable ) -{ - AssertMsg( nBytesAvailable > 0, "CRestore::ReadFunction() implementation does not currently support unspecified bytes available"); - - char *pszFunctionName = BufferPointer(); - BufferSkipBytes( nBytesAvailable ); - - AssertMsg( count == 1, "Arrays of functions not presently supported" ); - - if ( *pszFunctionName == 0 ) - *pValue = NULL; - else - *pValue = UTIL_FunctionFromName( pMap, pszFunctionName ); - - return 0; -} - -//----------------------------------------------------------------------------- -// -// Entity data saving routines -// -//----------------------------------------------------------------------------- - -BEGIN_SIMPLE_DATADESC(entitytable_t) - DEFINE_FIELD( id, FIELD_INTEGER ), - DEFINE_FIELD( edictindex, FIELD_INTEGER ), - DEFINE_FIELD( saveentityindex, FIELD_INTEGER ), -// DEFINE_FIELD( restoreentityindex, FIELD_INTEGER ), - // hEnt (not saved, this is the fixup) - DEFINE_FIELD( location, FIELD_INTEGER ), - DEFINE_FIELD( size, FIELD_INTEGER ), - DEFINE_FIELD( flags, FIELD_INTEGER ), - DEFINE_FIELD( classname, FIELD_STRING ), - DEFINE_FIELD( globalname, FIELD_STRING ), - DEFINE_FIELD( landmarkModelSpace, FIELD_VECTOR ), - DEFINE_FIELD( modelname, FIELD_STRING ), -END_DATADESC() - - -//----------------------------------------------------------------------------- -// Utilities entities can use when saving -//----------------------------------------------------------------------------- -class CEntitySaveUtils : public IEntitySaveUtils -{ -public: - // Call these in pre-save + post save - void PreSave(); - void PostSave(); - - // Methods of IEntitySaveUtils - virtual void AddLevelTransitionSaveDependency( CBaseEntity *pEntity1, CBaseEntity *pEntity2 ); - virtual int GetEntityDependencyCount( CBaseEntity *pEntity ); - virtual int GetEntityDependencies( CBaseEntity *pEntity, int nCount, CBaseEntity **ppEntList ); - -private: - IPhysicsObjectPairHash *m_pLevelAdjacencyDependencyHash; -}; - - -//----------------------------------------------------------------------------- -// Call these in pre-save + post save -//----------------------------------------------------------------------------- -void CEntitySaveUtils::PreSave() -{ - Assert( !m_pLevelAdjacencyDependencyHash ); - MEM_ALLOC_CREDIT(); - m_pLevelAdjacencyDependencyHash = physics->CreateObjectPairHash(); -} - -void CEntitySaveUtils::PostSave() -{ - physics->DestroyObjectPairHash( m_pLevelAdjacencyDependencyHash ); - m_pLevelAdjacencyDependencyHash = NULL; -} - - -//----------------------------------------------------------------------------- -// Gets the # of dependencies for a particular entity -//----------------------------------------------------------------------------- -int CEntitySaveUtils::GetEntityDependencyCount( CBaseEntity *pEntity ) -{ - return m_pLevelAdjacencyDependencyHash->GetPairCountForObject( pEntity ); -} - - -//----------------------------------------------------------------------------- -// Gets all dependencies for a particular entity -//----------------------------------------------------------------------------- -int CEntitySaveUtils::GetEntityDependencies( CBaseEntity *pEntity, int nCount, CBaseEntity **ppEntList ) -{ - return m_pLevelAdjacencyDependencyHash->GetPairListForObject( pEntity, nCount, (void**)ppEntList ); -} - - -//----------------------------------------------------------------------------- -// Methods of IEntitySaveUtils -//----------------------------------------------------------------------------- -void CEntitySaveUtils::AddLevelTransitionSaveDependency( CBaseEntity *pEntity1, CBaseEntity *pEntity2 ) -{ - if ( pEntity1 != pEntity2 ) - { - m_pLevelAdjacencyDependencyHash->AddObjectPair( pEntity1, pEntity2 ); - } -} - - -//----------------------------------------------------------------------------- -// Block handler for save/restore of entities -//----------------------------------------------------------------------------- -class CEntitySaveRestoreBlockHandler : public ISaveRestoreBlockHandler -{ -public: - const char *GetBlockName(); - void PreSave( CSaveRestoreData *pSaveData ); - void Save( ISave *pSave ); - void WriteSaveHeaders( ISave *pSave ); - virtual void PostSave(); - virtual void PreRestore(); - void ReadRestoreHeaders( IRestore *pRestore ); - - void Restore( IRestore *pRestore, bool createPlayers ); - virtual void PostRestore(); - - inline IEntitySaveUtils * GetEntitySaveUtils() { return &m_EntitySaveUtils; } - -private: - friend int CreateEntityTransitionList( CSaveRestoreData *pSaveData, int levelMask ); - bool SaveInitEntities( CSaveRestoreData *pSaveData ); - bool DoRestoreEntity( CBaseEntity *pEntity, IRestore *pRestore ); - Vector ModelSpaceLandmark( int modelIndex ); - int RestoreEntity( CBaseEntity *pEntity, IRestore *pRestore, entitytable_t *pEntInfo ); - -#if !defined( CLIENT_DLL ) - // Find the matching global entity. Spit out an error if the designer made entities of - // different classes with the same global name - CBaseEntity *FindGlobalEntity( string_t classname, string_t globalname ); - - int RestoreGlobalEntity( CBaseEntity *pEntity, CSaveRestoreData *pSaveData, entitytable_t *pEntInfo ); -#endif - -private: - CEntitySaveUtils m_EntitySaveUtils; -}; - - -//----------------------------------------------------------------------------- - -CEntitySaveRestoreBlockHandler g_EntitySaveRestoreBlockHandler; - -//------------------------------------- - -ISaveRestoreBlockHandler *GetEntitySaveRestoreBlockHandler() -{ - return &g_EntitySaveRestoreBlockHandler; -} - -IEntitySaveUtils *GetEntitySaveUtils() -{ - return g_EntitySaveRestoreBlockHandler.GetEntitySaveUtils(); -} - - -//----------------------------------------------------------------------------- -// Implementation of the block handler for save/restore of entities -//----------------------------------------------------------------------------- -const char *CEntitySaveRestoreBlockHandler::GetBlockName() -{ - return "Entities"; -} - -//--------------------------------- - -void CEntitySaveRestoreBlockHandler::PreSave( CSaveRestoreData *pSaveData ) -{ - IGameSystem::OnSaveAllSystems(); - - m_EntitySaveUtils.PreSave(); - - // Allow the entities to do some work - CBaseEntity *pEnt = NULL; -#if !defined( CLIENT_DLL ) - while ( (pEnt = gEntList.NextEnt( pEnt )) != NULL ) - { - pEnt->OnSave( &m_EntitySaveUtils ); - } -#else - // Do this because it'll force entities to figure out their origins, and that requires - // SetupBones in the case of aiments. - C_BaseAnimating::PushAllowBoneAccess( true, true ); - - int last = ClientEntityList().GetHighestEntityIndex(); - ClientEntityHandle_t iter = ClientEntityList().FirstHandle(); - - for ( int e = 0; e <= last; e++ ) - { - pEnt = ClientEntityList().GetBaseEntity( e ); - - if( !pEnt ) - continue; - - pEnt->OnSave(); - } - - while ( iter != ClientEntityList().InvalidHandle() ) - { - pEnt = ClientEntityList().GetBaseEntityFromHandle( iter ); - - if ( pEnt && pEnt->ObjectCaps() & FCAP_SAVE_NON_NETWORKABLE ) - { - pEnt->OnSave(); - } - - iter = ClientEntityList().NextHandle( iter ); - } - - - C_BaseAnimating::PopBoneAccess(); -#endif - SaveInitEntities( pSaveData ); -} - -//--------------------------------- - -void CEntitySaveRestoreBlockHandler::Save( ISave *pSave ) -{ - CGameSaveRestoreInfo *pSaveData = pSave->GetGameSaveRestoreInfo(); - - // write entity list that was previously built by SaveInitEntities() - for ( int i = 0; i < pSaveData->NumEntities(); i++ ) - { - entitytable_t *pEntInfo = pSaveData->GetEntityInfo( i ); - pEntInfo->location = pSave->GetWritePos(); - pEntInfo->size = 0; - - CBaseEntity *pEnt = pEntInfo->hEnt; - if ( pEnt && !( pEnt->ObjectCaps() & FCAP_DONT_SAVE ) ) - { - MDLCACHE_CRITICAL_SECTION(); -#if !defined( CLIENT_DLL ) - AssertMsg( !pEnt->edict() || ( pEnt->m_iClassname != NULL_STRING && - (STRING(pEnt->m_iClassname)[0] != 0) && - FStrEq( STRING(pEnt->m_iClassname), pEnt->GetClassname()) ), - "Saving entity with invalid classname" ); -#endif - - pSaveData->SetCurrentEntityContext( pEnt ); - pEnt->Save( *pSave ); - pSaveData->SetCurrentEntityContext( NULL ); - - pEntInfo->size = pSave->GetWritePos() - pEntInfo->location; // Size of entity block is data size written to block - - pEntInfo->classname = pEnt->m_iClassname; // Remember entity class for respawn - -#if !defined( CLIENT_DLL ) - pEntInfo->globalname = pEnt->m_iGlobalname; // remember global name - pEntInfo->landmarkModelSpace = ModelSpaceLandmark( pEnt->GetModelIndex() ); - int nEntIndex = pEnt->edict() ? ENTINDEX(pEnt->edict()) : -1; - bool bIsPlayer = ( ( nEntIndex >= 1 ) && ( nEntIndex <= gpGlobals->maxClients ) ) ? true : false; - if ( bIsPlayer ) - { - pEntInfo->flags |= FENTTABLE_PLAYER; - } -#endif - } - } -} - -//--------------------------------- - -void CEntitySaveRestoreBlockHandler::WriteSaveHeaders( ISave *pSave ) -{ - CGameSaveRestoreInfo *pSaveData = pSave->GetGameSaveRestoreInfo(); - - int nEntities = pSaveData->NumEntities(); - pSave->WriteInt( &nEntities ); - - for ( int i = 0; i < pSaveData->NumEntities(); i++ ) - pSave->WriteFields( "ETABLE", pSaveData->GetEntityInfo( i ), NULL, entitytable_t::m_DataMap.dataDesc, entitytable_t::m_DataMap.dataNumFields ); -} - -//--------------------------------- - -void CEntitySaveRestoreBlockHandler::PostSave() -{ - m_EntitySaveUtils.PostSave(); -} - -//--------------------------------- - -void CEntitySaveRestoreBlockHandler::PreRestore() -{ -} - -//--------------------------------- - -void CEntitySaveRestoreBlockHandler::ReadRestoreHeaders( IRestore *pRestore ) -{ - CGameSaveRestoreInfo *pSaveData = pRestore->GetGameSaveRestoreInfo(); - - int nEntities; - pRestore->ReadInt( &nEntities ); - - entitytable_t *pEntityTable = ( entitytable_t *)engine->SaveAllocMemory( (sizeof(entitytable_t) * nEntities), sizeof(char) ); - - pSaveData->InitEntityTable( pEntityTable, nEntities ); - - for ( int i = 0; i < pSaveData->NumEntities(); i++ ) - pRestore->ReadFields( "ETABLE", pSaveData->GetEntityInfo( i ), NULL, entitytable_t::m_DataMap.dataDesc, entitytable_t::m_DataMap.dataNumFields ); - -} - -//--------------------------------- - -#if !defined( CLIENT_DLL ) - -void CEntitySaveRestoreBlockHandler::Restore( IRestore *pRestore, bool createPlayers ) -{ - entitytable_t *pEntInfo; - CBaseEntity *pent; - - CGameSaveRestoreInfo *pSaveData = pRestore->GetGameSaveRestoreInfo(); - - bool restoredWorld = false; - - // Create entity list - int i; - for ( i = 0; i < pSaveData->NumEntities(); i++ ) - { - pEntInfo = pSaveData->GetEntityInfo( i ); - - if ( pEntInfo->classname != NULL_STRING && pEntInfo->size && !(pEntInfo->flags & FENTTABLE_REMOVED) ) - { - if ( pEntInfo->edictindex == 0 ) // worldspawn - { - Assert( i == 0 ); - pent = CreateEntityByName( STRING(pEntInfo->classname) ); - pRestore->SetReadPos( pEntInfo->location ); - if ( RestoreEntity( pent, pRestore, pEntInfo ) < 0 ) - { - pEntInfo->hEnt = NULL; - pEntInfo->restoreentityindex = -1; - UTIL_RemoveImmediate( pent ); - } - else - { - // force the entity to be relinked - AddRestoredEntity( pent ); - } - } - else if ( (pEntInfo->edictindex > 0) && (pEntInfo->edictindex <= gpGlobals->maxClients) ) - { - if ( !(pEntInfo->flags & FENTTABLE_PLAYER) ) - { - Warning( "ENTITY IS NOT A PLAYER: %d\n" , i ); - Assert(0); - } - - edict_t *ed = INDEXENT( pEntInfo->edictindex ); - - if ( ed && createPlayers ) - { - // create the player - pent = CBasePlayer::CreatePlayer( STRING(pEntInfo->classname), ed ); - } - else - pent = NULL; - } - else - { - pent = CreateEntityByName( STRING(pEntInfo->classname) ); - } - pEntInfo->hEnt = pent; - pEntInfo->restoreentityindex = pent ? pent->entindex() : - 1; - if ( pent && pEntInfo->restoreentityindex == 0 ) - { - if ( !FClassnameIs( pent, "worldspawn" ) ) - { - pEntInfo->restoreentityindex = -1; - } - } - - if ( pEntInfo->restoreentityindex == 0 ) - { - Assert( !restoredWorld ); - restoredWorld = true; - } - } - else - { - pEntInfo->hEnt = NULL; - pEntInfo->restoreentityindex = -1; - } - } - - // Now spawn entities - for ( i = 0; i < pSaveData->NumEntities(); i++ ) - { - pEntInfo = pSaveData->GetEntityInfo( i ); - if ( pEntInfo->edictindex != 0 ) - { - pent = pEntInfo->hEnt; - pRestore->SetReadPos( pEntInfo->location ); - if ( pent ) - { - if ( RestoreEntity( pent, pRestore, pEntInfo ) < 0 ) - { - pEntInfo->hEnt = NULL; - pEntInfo->restoreentityindex = -1; - UTIL_RemoveImmediate( pent ); - } - else - { - AddRestoredEntity( pent ); - } - } - } - } -} - -#else // CLIENT DLL VERSION - -void CEntitySaveRestoreBlockHandler::Restore( IRestore *pRestore, bool createPlayers ) -{ - entitytable_t *pEntInfo; - CBaseEntity *pent; - - CGameSaveRestoreInfo *pSaveData = pRestore->GetGameSaveRestoreInfo(); - - // Create entity list - int i; - bool restoredWorld = false; - - for ( i = 0; i < pSaveData->NumEntities(); i++ ) - { - pEntInfo = pSaveData->GetEntityInfo( i ); - pent = ClientEntityList().GetBaseEntity( pEntInfo->restoreentityindex ); - pEntInfo->hEnt = pent; - } - - // Blast saved data into entities - for ( i = 0; i < pSaveData->NumEntities(); i++ ) - { - pEntInfo = pSaveData->GetEntityInfo( i ); - - bool bRestoredCorrectly = false; - // FIXME, need to translate save spot to real index here using lookup table transmitted from server - //Assert( !"Need translation still" ); - if ( pEntInfo->restoreentityindex >= 0 ) - { - if ( pEntInfo->restoreentityindex == 0 ) - { - Assert( !restoredWorld ); - restoredWorld = true; - } - - pent = ClientEntityList().GetBaseEntity( pEntInfo->restoreentityindex ); - pRestore->SetReadPos( pEntInfo->location ); - if ( pent ) - { - if ( RestoreEntity( pent, pRestore, pEntInfo ) >= 0 ) - { - // Call the OnRestore method - AddRestoredEntity( pent ); - bRestoredCorrectly = true; - } - } - } - // BUGBUG: JAY: Disable ragdolls across transitions until PVS/solid check & client entity patch file are implemented - else if ( !pSaveData->levelInfo.fUseLandmark ) - { - if ( pEntInfo->classname != NULL_STRING ) - { - pent = CreateEntityByName( STRING(pEntInfo->classname) ); - pent->InitializeAsClientEntity( NULL, RENDER_GROUP_OPAQUE_ENTITY ); - - pRestore->SetReadPos( pEntInfo->location ); - - if ( pent ) - { - if ( RestoreEntity( pent, pRestore, pEntInfo ) >= 0 ) - { - pEntInfo->hEnt = pent; - AddRestoredEntity( pent ); - bRestoredCorrectly = true; - } - } - } - } - - if ( !bRestoredCorrectly ) - { - pEntInfo->hEnt = NULL; - pEntInfo->restoreentityindex = -1; - } - } - - // Note, server does this after local player connects fully - IGameSystem::OnRestoreAllSystems(); - - // Tell hud elements to modify behavior based on game restoration, if applicable - gHUD.OnRestore(); -} -#endif - -void CEntitySaveRestoreBlockHandler::PostRestore() -{ -} - -void SaveEntityOnTable( CBaseEntity *pEntity, CSaveRestoreData *pSaveData, int &iSlot ) -{ - entitytable_t *pEntInfo = pSaveData->GetEntityInfo( iSlot ); - pEntInfo->id = iSlot; -#if !defined( CLIENT_DLL ) - pEntInfo->edictindex = pEntity->RequiredEdictIndex(); -#else - pEntInfo->edictindex = -1; - pEntInfo->modelname = pEntity->GetModelName(); -#endif - pEntInfo->restoreentityindex = -1; - pEntInfo->saveentityindex = pEntity ? pEntity->entindex() : -1; - pEntInfo->hEnt = pEntity; - pEntInfo->flags = 0; - pEntInfo->location = 0; - pEntInfo->size = 0; - pEntInfo->classname = NULL_STRING; - - iSlot++; -} - - -//--------------------------------- - -bool CEntitySaveRestoreBlockHandler::SaveInitEntities( CSaveRestoreData *pSaveData ) -{ - int number_of_entities; - -#if !defined( CLIENT_DLL ) - number_of_entities = gEntList.NumberOfEntities(); -#else - number_of_entities = ClientEntityList().NumberOfEntities( true ); -#endif - entitytable_t *pEntityTable = ( entitytable_t *)engine->SaveAllocMemory( (sizeof(entitytable_t) * number_of_entities), sizeof(char) ); - pSaveData->InitEntityTable( pEntityTable, number_of_entities ); - - // build the table of entities - // this is used to turn pointers into savable indices - // build up ID numbers for each entity, for use in pointer conversions - // if an entity requires a certain edict number upon restore, save that as well - CBaseEntity *pEnt = NULL; - int i = 0; - -#if !defined( CLIENT_DLL ) - while ( (pEnt = gEntList.NextEnt( pEnt )) != NULL ) - { -#else - int last = ClientEntityList().GetHighestEntityIndex(); - - for ( int e = 0; e <= last; e++ ) - { - pEnt = ClientEntityList().GetBaseEntity( e ); - if( !pEnt ) - continue; -#endif - SaveEntityOnTable( pEnt, pSaveData, i ); - } - -#if defined( CLIENT_DLL ) - ClientEntityHandle_t iter = ClientEntityList().FirstHandle(); - - while ( iter != ClientEntityList().InvalidHandle() ) - { - pEnt = ClientEntityList().GetBaseEntityFromHandle( iter ); - - if ( pEnt && pEnt->ObjectCaps() & FCAP_SAVE_NON_NETWORKABLE ) - { - SaveEntityOnTable( pEnt, pSaveData, i ); - } - - iter = ClientEntityList().NextHandle( iter ); - } -#endif - - Assert( i == pSaveData->NumEntities() ); - return ( i == pSaveData->NumEntities() ); -} - -//--------------------------------- - -#if !defined( CLIENT_DLL ) - -// Find the matching global entity. Spit out an error if the designer made entities of -// different classes with the same global name -CBaseEntity *CEntitySaveRestoreBlockHandler::FindGlobalEntity( string_t classname, string_t globalname ) -{ - CBaseEntity *pReturn = NULL; - - while ( (pReturn = gEntList.NextEnt( pReturn )) != NULL ) - { - if ( FStrEq( STRING(pReturn->m_iGlobalname), STRING(globalname)) ) - break; - } - - if ( pReturn ) - { - if ( !FClassnameIs( pReturn, STRING(classname) ) ) - { - Warning( "Global entity found %s, wrong class %s [expects class %s]\n", STRING(globalname), STRING(pReturn->m_iClassname), classname ); - pReturn = NULL; - } - } - - return pReturn; -} - -#endif // !defined( CLIENT_DLL ) - -//--------------------------------- - -bool CEntitySaveRestoreBlockHandler::DoRestoreEntity( CBaseEntity *pEntity, IRestore *pRestore ) -{ - MDLCACHE_CRITICAL_SECTION(); - - EHANDLE hEntity; - - hEntity = pEntity; - - pRestore->GetGameSaveRestoreInfo()->SetCurrentEntityContext( pEntity ); - pEntity->Restore( *pRestore ); - pRestore->GetGameSaveRestoreInfo()->SetCurrentEntityContext( NULL ); - -#if !defined( CLIENT_DLL ) - if ( pEntity->ObjectCaps() & FCAP_MUST_SPAWN ) - { - pEntity->Spawn(); - } - else - { - pEntity->Precache( ); - } -#endif - - // Above calls may have resulted in self destruction - return ( hEntity != NULL ); -} - -//--------------------------------- -// Get a reference position in model space to compute -// changes in model space for global brush entities (designer models them in different coords!) -Vector CEntitySaveRestoreBlockHandler::ModelSpaceLandmark( int modelIndex ) -{ - const model_t *pModel = modelinfo->GetModel( modelIndex ); - if ( modelinfo->GetModelType( pModel ) != mod_brush ) - return vec3_origin; - - Vector mins, maxs; - modelinfo->GetModelBounds( pModel, mins, maxs ); - return mins; -} - - -int CEntitySaveRestoreBlockHandler::RestoreEntity( CBaseEntity *pEntity, IRestore *pRestore, entitytable_t *pEntInfo ) -{ - if ( !DoRestoreEntity( pEntity, pRestore ) ) - return 0; - -#if !defined( CLIENT_DLL ) - if ( pEntity->m_iGlobalname != NULL_STRING ) - { - int globalIndex = GlobalEntity_GetIndex( pEntity->m_iGlobalname ); - if ( globalIndex >= 0 ) - { - // Already dead? delete - if ( GlobalEntity_GetState( globalIndex ) == GLOBAL_DEAD ) - return -1; - else if ( !FStrEq( STRING(gpGlobals->mapname), GlobalEntity_GetMap(globalIndex) ) ) - { - pEntity->MakeDormant(); // Hasn't been moved to this level yet, wait but stay alive - } - // In this level & not dead, continue on as normal - } - else - { - Warning( "Global Entity %s (%s) not in table!!!\n", STRING(pEntity->m_iGlobalname), STRING(pEntity->m_iClassname) ); - // Spawned entities default to 'On' - GlobalEntity_Add( pEntity->m_iGlobalname, gpGlobals->mapname, GLOBAL_ON ); - } - } -#endif - - return 0; -} - -//--------------------------------- - -#if !defined( CLIENT_DLL ) - -int CEntitySaveRestoreBlockHandler::RestoreGlobalEntity( CBaseEntity *pEntity, CSaveRestoreData *pSaveData, entitytable_t *pEntInfo ) -{ - Vector oldOffset; - EHANDLE hEntitySafeHandle; - hEntitySafeHandle = pEntity; - - oldOffset.Init(); - CRestore restoreHelper( pSaveData ); - - string_t globalName = pEntInfo->globalname, className = pEntInfo->classname; - - // ------------------- - - int globalIndex = GlobalEntity_GetIndex( globalName ); - - // Don't overlay any instance of the global that isn't the latest - // pSaveData->szCurrentMapName is the level this entity is coming from - // pGlobal->levelName is the last level the global entity was active in. - // If they aren't the same, then this global update is out of date. - if ( !FStrEq( pSaveData->levelInfo.szCurrentMapName, GlobalEntity_GetMap(globalIndex) ) ) - { - return 0; - } - - // Compute the new global offset - CBaseEntity *pNewEntity = FindGlobalEntity( className, globalName ); - if ( pNewEntity ) - { -// Msg( "Overlay %s with %s\n", pNewEntity->GetClassname(), STRING(tmpEnt->classname) ); - // Tell the restore code we're overlaying a global entity from another level - restoreHelper.SetGlobalMode( 1 ); // Don't overwrite global fields - - pSaveData->modelSpaceOffset = pEntInfo->landmarkModelSpace - ModelSpaceLandmark( pNewEntity->GetModelIndex() ); - - UTIL_Remove( pEntity ); - pEntity = pNewEntity;// we're going to restore this data OVER the old entity - pEntInfo->hEnt = pEntity; - // HACKHACK: Do we need system-wide support for removing non-global spawn allocated resources? - pEntity->VPhysicsDestroyObject(); - Assert( pEntInfo->edictindex == -1 ); - // Update the global table to say that the global definition of this entity should come from this level - GlobalEntity_SetMap( globalIndex, gpGlobals->mapname ); - } - else - { - // This entity will be freed automatically by the engine-> If we don't do a restore on a matching entity (below) - // or call EntityUpdate() to move it to this level, we haven't changed global state at all. - DevMsg( "Warning: No match for global entity %s found in destination level\n", STRING(globalName) ); - return 0; - } - - if ( !DoRestoreEntity( pEntity, &restoreHelper ) ) - { - pEntity = NULL; - } - - // Is this an overriding global entity (coming over the transition) - pSaveData->modelSpaceOffset.Init(); - if ( pEntity ) - return 1; - return 0; -} - -#endif // !defined( CLIENT_DLL ) - - - -//----------------------------------------------------------------------------- - -CSaveRestoreData *SaveInit( int size ) -{ - CSaveRestoreData *pSaveData; - -#ifdef DISABLE_DEBUG_HISTORY - if ( size <= 0 ) - size = 2*1024*1024; // Reserve 2048K for now, UNDONE: Shrink this after compressing strings -#else - if ( size <= 0 ) - size = 3*1024*1024; // Reserve 3096K for now, UNDONE: Shrink this after compressing strings -#endif - - int numentities; - -#if !defined( CLIENT_DLL ) - numentities = gEntList.NumberOfEntities(); -#else - numentities = ClientEntityList().NumberOfEntities(); -#endif - - pSaveData = MakeSaveRestoreData(engine->SaveAllocMemory( sizeof(CSaveRestoreData) + (sizeof(entitytable_t) * numentities) + size, sizeof(char) )); - pSaveData->Init( (char *)(pSaveData + 1), size ); // skip the save structure - - const int nTokens = 0xfff; // Assume a maximum of 4K-1 symbol table entries(each of some length) - pSaveData->InitSymbolTable( (char **)engine->SaveAllocMemory( nTokens, sizeof( char * ) ), nTokens ); - - //--------------------------------- - - pSaveData->levelInfo.time = gpGlobals->curtime; // Use DLL time - pSaveData->levelInfo.vecLandmarkOffset = vec3_origin; - pSaveData->levelInfo.fUseLandmark = false; - pSaveData->levelInfo.connectionCount = 0; - - //--------------------------------- - - gpGlobals->pSaveData = pSaveData; - - return pSaveData; -} - - - -//----------------------------------------------------------------------------- -// -// ISaveRestoreBlockSet -// -// Purpose: Serves as holder for a group of sibling save sections. Takes -// care of iterating over them, making sure read points are -// queued up to the right spot (in case one section due to datadesc -// changes reads less than expected, or doesn't leave the -// read pointer at the right point), and ensuring the read pointer -// is at the end of the entire set when the set read is done. -//----------------------------------------------------------------------------- - -struct SaveRestoreBlockHeader_t -{ - char szName[MAX_BLOCK_NAME_LEN + 1]; - int locHeader; - int locBody; - - DECLARE_SIMPLE_DATADESC(); -}; - - -//------------------------------------- - -class CSaveRestoreBlockSet : public ISaveRestoreBlockSet -{ -public: - CSaveRestoreBlockSet( const char *pszName ) - { - Q_strncpy( m_Name, pszName, sizeof(m_Name) ); - } - - const char *GetBlockName() - { - return m_Name; - } - - //--------------------------------- - - void PreSave( CSaveRestoreData *pData ) - { - m_BlockHeaders.SetCount( m_Handlers.Count() ); - for ( int i = 0; i < m_Handlers.Count(); i++ ) - { - Q_strncpy( m_BlockHeaders[i].szName, m_Handlers[i]->GetBlockName(), MAX_BLOCK_NAME_LEN + 1 ); - m_Handlers[i]->PreSave( pData ); - } - } - - void Save( ISave *pSave ) - { - int base = pSave->GetWritePos(); - for ( int i = 0; i < m_Handlers.Count(); i++ ) - { - m_BlockHeaders[i].locBody = pSave->GetWritePos() - base; - m_Handlers[i]->Save( pSave ); - } - m_SizeBodies = pSave->GetWritePos() - base; - } - - void WriteSaveHeaders( ISave *pSave ) - { - int base = pSave->GetWritePos(); - - // - // Reserve space for a fully populated header - // - int dummyInt = -1; - CUtlVector dummyArr; - - dummyArr.SetCount( m_BlockHeaders.Count() ); - memset( &dummyArr[0], 0xff, dummyArr.Count() * sizeof(SaveRestoreBlockHeader_t) ); - - pSave->WriteInt( &dummyInt ); // size all headers - pSave->WriteInt( &dummyInt ); // size all bodies - SaveUtlVector( pSave, &dummyArr, FIELD_EMBEDDED ); - - // - // Write the data - // - for ( int i = 0; i < m_Handlers.Count(); i++ ) - { - m_BlockHeaders[i].locHeader = pSave->GetWritePos() - base; - m_Handlers[i]->WriteSaveHeaders( pSave ); - } - - m_SizeHeaders = pSave->GetWritePos() - base; - - // - // Write the actual header - // - int savedPos = pSave->GetWritePos(); - pSave->SetWritePos(base); - - pSave->WriteInt( &m_SizeHeaders ); - pSave->WriteInt( &m_SizeBodies ); - SaveUtlVector( pSave, &m_BlockHeaders, FIELD_EMBEDDED ); - - pSave->SetWritePos(savedPos); - } - - void PostSave() - { - for ( int i = 0; i < m_Handlers.Count(); i++ ) - { - m_Handlers[i]->PostSave(); - } - m_BlockHeaders.Purge(); - } - - //--------------------------------- - - void PreRestore() - { - for ( int i = 0; i < m_Handlers.Count(); i++ ) - { - m_Handlers[i]->PreRestore(); - } - } - - void ReadRestoreHeaders( IRestore *pRestore ) - { - int base = pRestore->GetReadPos(); - - pRestore->ReadInt( &m_SizeHeaders ); - pRestore->ReadInt( &m_SizeBodies ); - RestoreUtlVector( pRestore, &m_BlockHeaders, FIELD_EMBEDDED ); - - for ( int i = 0; i < m_Handlers.Count(); i++ ) - { - int location = GetBlockHeaderLoc( m_Handlers[i]->GetBlockName() ); - if ( location != -1 ) - { - pRestore->SetReadPos( base + location ); - m_Handlers[i]->ReadRestoreHeaders( pRestore ); - } - } - - pRestore->SetReadPos( base + m_SizeHeaders ); - } - - void CallBlockHandlerRestore( ISaveRestoreBlockHandler *pHandler, int baseFilePos, IRestore *pRestore, bool fCreatePlayers ) - { - int location = GetBlockBodyLoc( pHandler->GetBlockName() ); - if ( location != -1 ) - { - pRestore->SetReadPos( baseFilePos + location ); - pHandler->Restore( pRestore, fCreatePlayers ); - } - } - - void Restore( IRestore *pRestore, bool fCreatePlayers ) - { - int base = pRestore->GetReadPos(); - - for ( int i = 0; i < m_Handlers.Count(); i++ ) - { - CallBlockHandlerRestore( m_Handlers[i], base, pRestore, fCreatePlayers ); - } - pRestore->SetReadPos( base + m_SizeBodies ); - } - - void PostRestore() - { - for ( int i = 0; i < m_Handlers.Count(); i++ ) - { - m_Handlers[i]->PostRestore(); - } - m_BlockHeaders.Purge(); - } - - //--------------------------------- - - void AddBlockHandler( ISaveRestoreBlockHandler *pHandler ) - { - // Grody, but... while this class is still isolated in saverestore.cpp, this seems like a fine time to assert: - AssertMsg( pHandler == &g_EntitySaveRestoreBlockHandler || (m_Handlers.Count() >= 1 && m_Handlers[0] == &g_EntitySaveRestoreBlockHandler), "Expected entity save load to always be first" ); - - Assert( pHandler != this ); - m_Handlers.AddToTail( pHandler ); - } - - void RemoveBlockHandler( ISaveRestoreBlockHandler *pHandler ) - { - m_Handlers.FindAndRemove( pHandler ); - } - - //--------------------------------- - -private: - int GetBlockBodyLoc( const char *pszName ) - { - for ( int i = 0; i < m_BlockHeaders.Count(); i++ ) - { - if ( strcmp( m_BlockHeaders[i].szName, pszName ) == 0 ) - return m_BlockHeaders[i].locBody; - } - return -1; - } - - int GetBlockHeaderLoc( const char *pszName ) - { - for ( int i = 0; i < m_BlockHeaders.Count(); i++ ) - { - if ( strcmp( m_BlockHeaders[i].szName, pszName ) == 0 ) - return m_BlockHeaders[i].locHeader; - } - return -1; - } - - char m_Name[MAX_BLOCK_NAME_LEN + 1]; - CUtlVector m_Handlers; - - int m_SizeHeaders; - int m_SizeBodies; - CUtlVector m_BlockHeaders; -}; - -//------------------------------------- - -BEGIN_SIMPLE_DATADESC( SaveRestoreBlockHeader_t ) - DEFINE_ARRAY(szName, FIELD_CHARACTER, MAX_BLOCK_NAME_LEN + 1), - DEFINE_FIELD(locHeader, FIELD_INTEGER), - DEFINE_FIELD(locBody, FIELD_INTEGER), -END_DATADESC() - -//------------------------------------- - -CSaveRestoreBlockSet g_SaveRestoreBlockSet("Game"); -ISaveRestoreBlockSet *g_pGameSaveRestoreBlockSet = &g_SaveRestoreBlockSet; - -//============================================================================= -#if !defined( CLIENT_DLL ) - -//------------------------------------------------------------------------------ -// Creates all entities that lie in the transition list -//------------------------------------------------------------------------------ -void CreateEntitiesInTransitionList( CSaveRestoreData *pSaveData, int levelMask ) -{ - CBaseEntity *pent; - int i; - for ( i = 0; i < pSaveData->NumEntities(); i++ ) - { - entitytable_t *pEntInfo = pSaveData->GetEntityInfo( i ); - pEntInfo->hEnt = NULL; - - if ( pEntInfo->size == 0 || pEntInfo->edictindex == 0 ) - continue; - - if ( pEntInfo->classname == NULL_STRING ) - { - Warning( "Entity with data saved, but with no classname\n" ); - Assert(0); - continue; - } - - bool active = (pEntInfo->flags & levelMask) ? 1 : 0; - - // spawn players - pent = NULL; - if ( (pEntInfo->edictindex > 0) && (pEntInfo->edictindex <= gpGlobals->maxClients) ) - { - edict_t *ed = INDEXENT( pEntInfo->edictindex ); - - if ( active && ed && !ed->IsFree() ) - { - if ( !(pEntInfo->flags & FENTTABLE_PLAYER) ) - { - Warning( "ENTITY IS NOT A PLAYER: %d\n" , i ); - Assert(0); - } - - pent = CBasePlayer::CreatePlayer( STRING(pEntInfo->classname), ed ); - } - } - else if ( active ) - { - pent = CreateEntityByName( STRING(pEntInfo->classname) ); - } - - pEntInfo->hEnt = pent; - } -} - - -//----------------------------------------------------------------------------- -int CreateEntityTransitionList( CSaveRestoreData *pSaveData, int levelMask ) -{ - CBaseEntity *pent; - entitytable_t *pEntInfo; - - // Create entity list - CreateEntitiesInTransitionList( pSaveData, levelMask ); - - // Now spawn entities - CUtlVector checkList; - - int i; - int movedCount = 0; - for ( i = 0; i < pSaveData->NumEntities(); i++ ) - { - pEntInfo = pSaveData->GetEntityInfo( i ); - pent = pEntInfo->hEnt; -// pSaveData->currentIndex = i; - pSaveData->Seek( pEntInfo->location ); - - // clear this out - it must be set on a per-entity basis - pSaveData->modelSpaceOffset.Init(); - - if ( pent && (pEntInfo->flags & levelMask) ) // Screen out the player if he's not to be spawned - { - if ( pEntInfo->flags & FENTTABLE_GLOBAL ) - { - DevMsg( 2, "Merging changes for global: %s\n", STRING(pEntInfo->classname) ); - - // ------------------------------------------------------------------------- - // Pass the "global" flag to the DLL to indicate this entity should only override - // a matching entity, not be spawned - if ( g_EntitySaveRestoreBlockHandler.RestoreGlobalEntity( pent, pSaveData, pEntInfo ) > 0 ) - { - movedCount++; - pEntInfo->restoreentityindex = pEntInfo->hEnt.Get()->entindex(); - AddRestoredEntity( pEntInfo->hEnt.Get() ); - } - else - { - UTIL_RemoveImmediate( pEntInfo->hEnt.Get() ); - } - // ------------------------------------------------------------------------- - } - else - { - DevMsg( 2, "Transferring %s (%d)\n", STRING(pEntInfo->classname), pent->edict() ? ENTINDEX(pent->edict()) : -1 ); - CRestore restoreHelper( pSaveData ); - if ( g_EntitySaveRestoreBlockHandler.RestoreEntity( pent, &restoreHelper, pEntInfo ) < 0 ) - { - UTIL_RemoveImmediate( pent ); - } - else - { - // needs to be checked. Do this in a separate pass so that pointers & hierarchy can be traversed - checkList.AddToTail(i); - } - } - - // Remove any entities that were removed using UTIL_Remove() as a result of the above calls to UTIL_RemoveImmediate() - gEntList.CleanupDeleteList(); - } - } - - for ( i = checkList.Count()-1; i >= 0; --i ) - { - pEntInfo = pSaveData->GetEntityInfo( checkList[i] ); - pent = pEntInfo->hEnt; - - // NOTE: pent can be NULL because UTIL_RemoveImmediate (called below) removes all in hierarchy - if ( !pent ) - continue; - - MDLCACHE_CRITICAL_SECTION(); - - if ( !(pEntInfo->flags & FENTTABLE_PLAYER) && UTIL_EntityInSolid( pent ) ) - { - // this can happen during normal processing - PVS is just a guess, some map areas won't exist in the new map - DevMsg( 2, "Suppressing %s\n", STRING(pEntInfo->classname) ); - UTIL_RemoveImmediate( pent ); - // Remove any entities that were removed using UTIL_Remove() as a result of the above calls to UTIL_RemoveImmediate() - gEntList.CleanupDeleteList(); - } - else - { - movedCount++; - pEntInfo->flags = FENTTABLE_REMOVED; - pEntInfo->restoreentityindex = pent->entindex(); - AddRestoredEntity( pent ); - } - } - - return movedCount; -} -#endif +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: Helper classes and functions for the save/restore system. +// +// $NoKeywords: $ +//=============================================================================// + +#include "cbase.h" +#include +#include "isaverestore.h" +#include "saverestore.h" +#include +#include "shake.h" +#include "decals.h" +#include "gamerules.h" +#include "bspfile.h" +#include "mathlib.h" +#include "engine/IEngineSound.h" +#include "saverestoretypes.h" +#include "saverestore_utlvector.h" +#include "model_types.h" +#include "igamesystem.h" +#include "interval.h" +#include "vphysics/object_hash.h" +#include "datacache/imdlcache.h" + +#if !defined( CLIENT_DLL ) + +#include "globalstate.h" +#include "entitylist.h" + +#else + +#include "gamestringpool.h" + +#endif + +// HACKHACK: Builds a global list of entities that were restored from all levels +#if !defined( CLIENT_DLL ) +void AddRestoredEntity( CBaseEntity *pEntity ); +#else +void AddRestoredEntity( C_BaseEntity *pEntity ); +#endif + + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +#define MAX_ENTITYARRAY 1024 +#define ZERO_TIME ((FLT_MAX*-0.5)) +// A bit arbitrary, but unlikely to collide with any saved games... +#define TICK_NEVER_THINK_ENCODE ( INT_MAX - 3 ) + +ASSERT_INVARIANT( sizeof(EHandlePlaceholder_t) == sizeof(EHANDLE) ); + +//----------------------------------------------------------------------------- + +static int gSizes[FIELD_TYPECOUNT] = +{ + CDatamapFieldSizeDeducer::FieldSize(), + CDatamapFieldSizeDeducer::FieldSize(), + CDatamapFieldSizeDeducer::FieldSize(), + CDatamapFieldSizeDeducer::FieldSize(), + CDatamapFieldSizeDeducer::FieldSize(), + CDatamapFieldSizeDeducer::FieldSize(), + CDatamapFieldSizeDeducer::FieldSize(), + CDatamapFieldSizeDeducer::FieldSize(), + CDatamapFieldSizeDeducer::FieldSize(), + CDatamapFieldSizeDeducer::FieldSize(), + CDatamapFieldSizeDeducer::FieldSize(), + CDatamapFieldSizeDeducer::FieldSize(), + + CDatamapFieldSizeDeducer::FieldSize(), + CDatamapFieldSizeDeducer::FieldSize(), + CDatamapFieldSizeDeducer::FieldSize(), + + CDatamapFieldSizeDeducer::FieldSize(), + CDatamapFieldSizeDeducer::FieldSize(), + CDatamapFieldSizeDeducer::FieldSize(), + CDatamapFieldSizeDeducer::FieldSize(), + CDatamapFieldSizeDeducer::FieldSize(), + + CDatamapFieldSizeDeducer::FieldSize(), + CDatamapFieldSizeDeducer::FieldSize(), + CDatamapFieldSizeDeducer::FieldSize(), + CDatamapFieldSizeDeducer::FieldSize(), + CDatamapFieldSizeDeducer::FieldSize(), + CDatamapFieldSizeDeducer::FieldSize(), + CDatamapFieldSizeDeducer::FieldSize(), + CDatamapFieldSizeDeducer::FieldSize(), +}; + + +// helpers to offset worldspace matrices +static void VMatrixOffset( VMatrix &dest, const VMatrix &matrixIn, const Vector &offset ) +{ + dest = matrixIn; + dest.PostTranslate( offset ); +} + +static void Matrix3x4Offset( matrix3x4_t& dest, const matrix3x4_t& matrixIn, const Vector &offset ) +{ + MatrixCopy( matrixIn, dest ); + Vector out; + MatrixGetColumn( matrixIn, 3, out ); + out += offset; + MatrixSetColumn( out, 3, dest ); +} + +// This does the necessary casting / extract to grab a pointer to a member function as a void * +// UNDONE: Cast to BASEPTR or something else here? +#define EXTRACT_VOID_FUNCTIONPTR(x) (*(void **)(&(x))) +//----------------------------------------------------------------------------- +// Purpose: Search this datamap for the name of this member function +// This is used to save/restore function pointers (convert pointer to text) +// Input : *function - pointer to member function +// Output : const char * - function name +//----------------------------------------------------------------------------- +const char *UTIL_FunctionToName( datamap_t *pMap, void *function ) +{ + while ( pMap ) + { + for ( int i = 0; i < pMap->dataNumFields; i++ ) + { + if ( pMap->dataDesc[i].flags & FTYPEDESC_FUNCTIONTABLE ) + { + Assert( sizeof(pMap->dataDesc[i].inputFunc) == sizeof(void *) ); + void *pTest = EXTRACT_VOID_FUNCTIONPTR(pMap->dataDesc[i].inputFunc); + if ( pTest == function ) + return pMap->dataDesc[i].fieldName; + } + } + pMap = pMap->baseMap; + } + + return NULL; +} + +//----------------------------------------------------------------------------- +// Purpose: Search the datamap for a function named pName +// This is used to save/restore function pointers (convert text back to pointer) +// Input : *pName - name of the member function +//----------------------------------------------------------------------------- +void *UTIL_FunctionFromName( datamap_t *pMap, const char *pName ) +{ + while ( pMap ) + { + for ( int i = 0; i < pMap->dataNumFields; i++ ) + { + Assert( sizeof(pMap->dataDesc[i].inputFunc) == sizeof(void *) ); + + if ( pMap->dataDesc[i].flags & FTYPEDESC_FUNCTIONTABLE ) + { + if ( FStrEq( pName, pMap->dataDesc[i].fieldName ) ) + { + return EXTRACT_VOID_FUNCTIONPTR(pMap->dataDesc[i].inputFunc); + } + } + } + pMap = pMap->baseMap; + } + + Msg( "Failed to find function %s\n", pName ); + + return NULL; +} + +//----------------------------------------------------------------------------- +// +// CSave +// +//----------------------------------------------------------------------------- + +CSave::CSave( CSaveRestoreData *pdata ) + : m_pData(pdata), + m_pGameInfo( pdata ) +{ + m_BlockStartStack.EnsureCapacity( 32 ); + + // Logging. + m_hLogFile = NULL; +} + +//----------------------------------------------------------------------------- +// Purpose: Start logging save data. +//----------------------------------------------------------------------------- +void CSave::StartLogging( const char *pszLogName ) +{ + m_hLogFile = filesystem->Open( pszLogName, "w" ); +} + +//----------------------------------------------------------------------------- +// Purpose: Stop logging save data. +//----------------------------------------------------------------------------- +void CSave::EndLogging( void ) +{ + if ( m_hLogFile ) + { + filesystem->Close( m_hLogFile ); + } + m_hLogFile = NULL; +} + +//----------------------------------------------------------------------------- +// Purpose: Check to see if we are logging data. +//----------------------------------------------------------------------------- +bool CSave::IsLogging( void ) +{ + return ( m_hLogFile != NULL ); +} + +//----------------------------------------------------------------------------- +// Purpose: Log data. +//----------------------------------------------------------------------------- +void CSave::Log( const char *pName, fieldtype_t fieldType, void *value, int count ) +{ + // Check to see if we are logging. + if ( !IsLogging() ) + return; + + static char szBuf[1024]; + static char szTempBuf[256]; + + // Save the name. + Q_snprintf( szBuf, sizeof( szBuf ), "%s ", pName ); + + for ( int iCount = 0; iCount < count; ++iCount ) + { + switch ( fieldType ) + { + case FIELD_SHORT: + { + short *pValue = ( short* )( value ); + short nValue = pValue[iCount]; + Q_snprintf( szTempBuf, sizeof( szTempBuf ), "%d", nValue ); + Q_strncat( szBuf, szTempBuf, sizeof( szTempBuf ), COPY_ALL_CHARACTERS ); + break; + } + case FIELD_FLOAT: + { + float *pValue = ( float* )( value ); + float flValue = pValue[iCount]; + Q_snprintf( szTempBuf, sizeof( szTempBuf ), "%f", flValue ); + Q_strncat( szBuf, szTempBuf, sizeof( szTempBuf ), COPY_ALL_CHARACTERS ); + break; + } + case FIELD_BOOLEAN: + { + bool *pValue = ( bool* )( value ); + bool bValue = pValue[iCount]; + Q_snprintf( szTempBuf, sizeof( szTempBuf ), "%d", ( int )( bValue ) ); + Q_strncat( szBuf, szTempBuf, sizeof( szTempBuf ), COPY_ALL_CHARACTERS ); + break; + } + case FIELD_INTEGER: + { + int *pValue = ( int* )( value ); + int nValue = pValue[iCount]; + Q_snprintf( szTempBuf, sizeof( szTempBuf ), "%d", nValue ); + Q_strncat( szBuf, szTempBuf, sizeof( szTempBuf ), COPY_ALL_CHARACTERS ); + break; + } + case FIELD_STRING: + { + string_t *pValue = ( string_t* )( value ); + Q_snprintf( szTempBuf, sizeof( szTempBuf ), "%s", ( char* )STRING( *pValue ) ); + Q_strncat( szBuf, szTempBuf, sizeof( szTempBuf ), COPY_ALL_CHARACTERS ); + break; + } + case FIELD_VECTOR: + { + Vector *pValue = ( Vector* )( value ); + Vector vecValue = pValue[iCount]; + Q_snprintf( szTempBuf, sizeof( szTempBuf ), "(%f %f %f)", vecValue.x, vecValue.y, vecValue.z ); + Q_strncat( szBuf, szTempBuf, sizeof( szTempBuf ), COPY_ALL_CHARACTERS ); + break; + } + case FIELD_QUATERNION: + { + Quaternion *pValue = ( Quaternion* )( value ); + Quaternion q = pValue[iCount]; + Q_snprintf( szTempBuf, sizeof( szTempBuf ), "(%f %f %f %f)", q[0], q[1], q[2], q[3] ); + Q_strncat( szBuf, szTempBuf, sizeof( szTempBuf ), COPY_ALL_CHARACTERS ); + break; + } + case FIELD_CHARACTER: + { + char *pValue = ( char* )( value ); + char chValue = pValue[iCount]; + Q_snprintf( szTempBuf, sizeof( szTempBuf ), "%c", chValue ); + Q_strncat( szBuf, szTempBuf, sizeof( szTempBuf ), COPY_ALL_CHARACTERS ); + } + case FIELD_COLOR32: + { + byte *pValue = ( byte* )( value ); + byte *pColor = &pValue[iCount*4]; + Q_snprintf( szTempBuf, sizeof( szTempBuf ), "(%d %d %d %d)", ( int )pColor[0], ( int )pColor[1], ( int )pColor[2], ( int )pColor[3] ); + Q_strncat( szBuf, szTempBuf, sizeof( szTempBuf ), COPY_ALL_CHARACTERS ); + } + case FIELD_EMBEDDED: + case FIELD_CUSTOM: + default: + { + break; + } + } + + // Add space data. + if ( ( iCount + 1 ) != count ) + { + Q_strncpy( szTempBuf, " ", sizeof( szTempBuf ) ); + Q_strncat( szBuf, szTempBuf, sizeof( szTempBuf ), COPY_ALL_CHARACTERS ); + } + else + { + Q_strncpy( szTempBuf, "\n", sizeof( szTempBuf ) ); + Q_strncat( szBuf, szTempBuf, sizeof( szTempBuf ), COPY_ALL_CHARACTERS ); + } + } + + int nLength = strlen( szBuf ) + 1; + filesystem->Write( szBuf, nLength, m_hLogFile ); +} + +//------------------------------------- + +int CSave::GetWritePos() const +{ + return m_pData->GetCurPos(); +} + +//------------------------------------- + +void CSave::SetWritePos(int pos) +{ + m_pData->Seek(pos); +} + +//------------------------------------- + +void CSave::WriteShort( const short *value, int count ) +{ + BufferData( (const char *)value, sizeof(short) * count ); +} + +//------------------------------------- + +void CSave::WriteInt( const int *value, int count ) +{ + BufferData( (const char *)value, sizeof(int) * count ); +} + +//------------------------------------- + +void CSave::WriteBool( const bool *value, int count ) +{ + COMPILE_TIME_ASSERT( sizeof(bool) == sizeof(char) ); + BufferData( (const char *)value, sizeof(bool) * count ); +} + +//------------------------------------- + +void CSave::WriteFloat( const float *value, int count ) +{ + BufferData( (const char *)value, sizeof(float) * count ); +} + +//------------------------------------- + +void CSave::WriteData( const char *pdata , int size ) +{ + BufferData( pdata, size ); +} + +//------------------------------------- + +void CSave::WriteString( const char *pstring ) +{ + BufferData( pstring, strlen(pstring) + 1 ); +} + +//------------------------------------- + +void CSave::WriteString( const string_t *stringId, int count ) +{ + for ( int i = 0; i < count; i++ ) + { + const char *pString = STRING(stringId[i]); + BufferData( pString, strlen(pString)+1 ); + } +} + +//------------------------------------- + +void CSave::WriteVector( const Vector &value ) +{ + BufferData( (const char *)&value, sizeof(Vector) ); +} + +//------------------------------------- + +void CSave::WriteVector( const Vector *value, int count ) +{ + BufferData( (const char *)value, sizeof(Vector) * count ); +} + +void CSave::WriteQuaternion( const Quaternion &value ) +{ + BufferData( (const char *)&value, sizeof(Quaternion) ); +} + +//------------------------------------- + +void CSave::WriteQuaternion( const Quaternion *value, int count ) +{ + BufferData( (const char *)value, sizeof(Quaternion) * count ); +} + + +//------------------------------------- + +void CSave::WriteData( const char *pname, int size, const char *pdata ) +{ + BufferField( pname, size, pdata ); +} + +//------------------------------------- + +void CSave::WriteShort( const char *pname, const short *data, int count ) +{ + BufferField( pname, sizeof(short) * count, (const char *)data ); +} + +//------------------------------------- + +void CSave::WriteInt( const char *pname, const int *data, int count ) +{ + BufferField( pname, sizeof(int) * count, (const char *)data ); +} + +//------------------------------------- + +void CSave::WriteBool( const char *pname, const bool *data, int count ) +{ + COMPILE_TIME_ASSERT( sizeof(bool) == sizeof(char) ); + BufferField( pname, sizeof(bool) * count, (const char *)data ); +} + +//------------------------------------- + +void CSave::WriteFloat( const char *pname, const float *data, int count ) +{ + BufferField( pname, sizeof(float) * count, (const char *)data ); +} + +//------------------------------------- + +void CSave::WriteString( const char *pname, const char *pdata ) +{ + BufferField( pname, strlen(pdata) + 1, pdata ); +} + +//------------------------------------- + +void CSave::WriteString( const char *pname, const string_t *stringId, int count ) +{ + int i, size; + + size = 0; + for ( i = 0; i < count; i++ ) + size += strlen( STRING( stringId[i] ) ) + 1; + + WriteHeader( pname, size ); + WriteString( stringId, count ); +} + +//------------------------------------- + +void CSave::WriteVector( const char *pname, const Vector &value ) +{ + WriteVector( pname, &value, 1 ); +} + +//------------------------------------- + +void CSave::WriteVector( const char *pname, const Vector *value, int count ) +{ + WriteHeader( pname, sizeof(Vector) * count ); + BufferData( (const char *)value, sizeof(Vector) * count ); +} + +void CSave::WriteQuaternion( const char *pname, const Quaternion &value ) +{ + WriteQuaternion( pname, &value, 1 ); +} + +//------------------------------------- + +void CSave::WriteQuaternion( const char *pname, const Quaternion *value, int count ) +{ + WriteHeader( pname, sizeof(Quaternion) * count ); + BufferData( (const char *)value, sizeof(Quaternion) * count ); +} + + +//------------------------------------- + +void CSave::WriteVMatrix( const VMatrix *value, int count ) +{ + BufferData( (const char *)value, sizeof(VMatrix) * count ); +} + +//------------------------------------- + +void CSave::WriteVMatrix( const char *pname, const VMatrix *value, int count ) +{ + WriteHeader( pname, sizeof(VMatrix) * count ); + BufferData( (const char *)value, sizeof(VMatrix) * count ); +} + +//------------------------------------- + +void CSave::WriteVMatrixWorldspace( const VMatrix *value, int count ) +{ + for ( int i = 0; i < count; i++ ) + { + VMatrix tmp; + VMatrixOffset( tmp, value[i], -m_pGameInfo->GetLandmark() ); + BufferData( (const char *)&tmp, sizeof(VMatrix) ); + } +} + +//------------------------------------- + +void CSave::WriteVMatrixWorldspace( const char *pname, const VMatrix *value, int count ) +{ + WriteHeader( pname, sizeof(VMatrix) * count ); + WriteVMatrixWorldspace( value, count ); +} + +void CSave::WriteMatrix3x4Worldspace( const matrix3x4_t *value, int count ) +{ + Vector offset = -m_pGameInfo->GetLandmark(); + for ( int i = 0; i < count; i++ ) + { + matrix3x4_t tmp; + Matrix3x4Offset( tmp, value[i], offset ); + BufferData( (const char *)value, sizeof(matrix3x4_t) ); + } +} + +//------------------------------------- + +void CSave::WriteMatrix3x4Worldspace( const char *pname, const matrix3x4_t *value, int count ) +{ + WriteHeader( pname, sizeof(matrix3x4_t) * count ); + WriteMatrix3x4Worldspace( value, count ); +} + +void CSave::WriteInterval( const char *pname, const interval_t *value, int count ) +{ + WriteHeader( pname, sizeof( interval_t ) * count ); + WriteInterval( value, count ); +} + +void CSave::WriteInterval( const interval_t *value, int count ) +{ + BufferData( (const char *)value, count * sizeof( interval_t ) ); +} + +//------------------------------------- + +bool CSave::ShouldSaveField( const void *pData, typedescription_t *pField ) +{ + if ( !(pField->flags & FTYPEDESC_SAVE) || pField->fieldType == FIELD_VOID ) + return false; + + switch ( pField->fieldType ) + { + case FIELD_EMBEDDED: + { + if ( pField->flags & FTYPEDESC_PTR ) + { + AssertMsg( pField->fieldSize == 1, "Arrays of embedded pointer types presently unsupported by save/restore" ); + if ( pField->fieldSize != 1 ) + return false; + } + + AssertMsg( pField->td != NULL, "Embedded type appears to have not had type description implemented" ); + if ( pField->td == NULL ) + return false; + + if ( (pField->flags & FTYPEDESC_PTR) && !*((void **)pData) ) + return false; + + int nFieldCount = pField->fieldSize; + char *pTestData = (char *)( ( !(pField->flags & FTYPEDESC_PTR) ) ? pData : *((void **)pData) ); + while ( --nFieldCount >= 0 ) + { + typedescription_t *pTestField = pField->td->dataDesc; + typedescription_t *pLimit = pField->td->dataDesc + pField->td->dataNumFields; + + for ( ; pTestField < pLimit; ++pTestField ) + { + if ( ShouldSaveField( pTestData + pTestField->fieldOffset[ TD_OFFSET_NORMAL ], pTestField ) ) + return true; + } + + pTestData += pField->fieldSizeInBytes; + } + return false; + } + + case FIELD_CUSTOM: + { + // ask the data if it's empty + SaveRestoreFieldInfo_t fieldInfo = + { + const_cast(pData), + ((char *)pData) - pField->fieldOffset[ TD_OFFSET_NORMAL ], + pField + }; + if ( pField->pSaveRestoreOps->IsEmpty( fieldInfo ) ) + return false; + } + return true; + + case FIELD_EHANDLE: + { + if ( (pField->fieldSizeInBytes != pField->fieldSize * gSizes[pField->fieldType]) ) + { + Warning("WARNING! Field %s is using the wrong FIELD_ type!\nFix this or you'll see a crash.\n", pField->fieldName ); + Assert( 0 ); + } + + int *pEHandle = (int *)pData; + for ( int i = 0; i < static_cast(pField->fieldSize); ++i, ++pEHandle ) + { + if ( (*pEHandle) != static_cast(0xFFFFFFFF) ) + return true; + } + } + return false; + + default: + { + if ( (pField->fieldSizeInBytes != pField->fieldSize * gSizes[pField->fieldType]) ) + { + Warning("WARNING! Field %s is using the wrong FIELD_ type!\nFix this or you'll see a crash.\n", pField->fieldName ); + Assert( 0 ); + } + + // old byte-by-byte null check + if ( DataEmpty( (const char *)pData, pField->fieldSize * gSizes[pField->fieldType] ) ) + return false; + } + return true; + } +} + +//------------------------------------- +// Purpose: Writes all the fields that are client neutral. In the event of +// a librarization of save/restore, these would reside in the library +// + +bool CSave::WriteBasicField( const char *pname, void *pData, datamap_t *pRootMap, typedescription_t *pField ) +{ + switch( pField->fieldType ) + { + case FIELD_FLOAT: + WriteFloat( pField->fieldName, (float *)pData, pField->fieldSize ); + break; + + case FIELD_STRING: + WriteString( pField->fieldName, (string_t *)pData, pField->fieldSize ); + break; + + case FIELD_VECTOR: + WriteVector( pField->fieldName, (Vector *)pData, pField->fieldSize ); + break; + + case FIELD_QUATERNION: + WriteQuaternion( pField->fieldName, (Quaternion *)pData, pField->fieldSize ); + break; + + case FIELD_INTEGER: + WriteInt( pField->fieldName, (int *)pData, pField->fieldSize ); + break; + + case FIELD_BOOLEAN: + WriteBool( pField->fieldName, (bool *)pData, pField->fieldSize ); + break; + + case FIELD_SHORT: + WriteData( pField->fieldName, 2 * pField->fieldSize, ((char *)pData) ); + break; + + case FIELD_CHARACTER: + WriteData( pField->fieldName, pField->fieldSize, ((char *)pData) ); + break; + + case FIELD_COLOR32: + WriteData( pField->fieldName, 4*pField->fieldSize, (char *)pData ); + break; + + case FIELD_EMBEDDED: + { + AssertMsg( ( (pField->flags & FTYPEDESC_PTR) == 0 ) || (pField->fieldSize == 1), "Arrays of embedded pointer types presently unsupported by save/restore" ); + Assert( !(pField->flags & FTYPEDESC_PTR) || *((void **)pData) ); + int nFieldCount = pField->fieldSize; + char *pFieldData = (char *)( ( !(pField->flags & FTYPEDESC_PTR) ) ? pData : *((void **)pData) ); + + StartBlock( pField->fieldName ); + + while ( --nFieldCount >= 0 ) + { + WriteAll( pFieldData, pField->td ); + pFieldData += pField->fieldSizeInBytes; + } + + EndBlock(); + break; + } + + case FIELD_CUSTOM: + { + // Note it is up to the custom type implementor to handle arrays + StartBlock( pField->fieldName ); + + SaveRestoreFieldInfo_t fieldInfo = + { + pData, + ((char *)pData) - pField->fieldOffset[ TD_OFFSET_NORMAL ], + pField + }; + pField->pSaveRestoreOps->Save( fieldInfo, this ); + + EndBlock(); + break; + } + + default: + Warning( "Bad field type\n" ); + Assert(0); + return false; + } + + return true; +} + +//------------------------------------- + +bool CSave::WriteField( const char *pname, void *pData, datamap_t *pRootMap, typedescription_t *pField ) +{ +#ifdef _DEBUG + Log( pname, (fieldtype_t)pField->fieldType, pData, pField->fieldSize ); +#endif + + if ( pField->fieldType <= FIELD_CUSTOM ) + { + return WriteBasicField( pname, pData, pRootMap, pField ); + } + return WriteGameField( pname, pData, pRootMap, pField ); +} + +//------------------------------------- + +int CSave::CountFieldsToSave( const void *pBaseData, typedescription_t *pFields, int fieldCount ) +{ + int result = 0; + for ( int i = 0; i < fieldCount; i++ ) + { + if ( ShouldSaveField( (char *)pBaseData + pFields[i].fieldOffset[ TD_OFFSET_NORMAL ], &pFields[i] ) ) + result++; + } + return result; +} + +//------------------------------------- + +int CSave::WriteFields( const char *pname, const void *pBaseData, datamap_t *pRootMap, typedescription_t *pFields, int fieldCount ) +{ + int i, actualCount; + typedescription_t *pTest; + + // Empty fields will not be written, write out the actual number of fields to be written + actualCount = CountFieldsToSave( pBaseData, pFields, fieldCount ); + WriteInt( pname, &actualCount, 1 ); + + for ( i = 0; i < fieldCount; i++ ) + { + pTest = &pFields[ i ]; + + void *pOutputData = ( (char *)pBaseData + pTest->fieldOffset[ TD_OFFSET_NORMAL ] ); + if ( !ShouldSaveField( pOutputData, pTest ) ) + continue; + + if ( !WriteField( pname, pOutputData, pRootMap, pTest ) ) + break; + } + + return 1; +} + +//------------------------------------- +// Purpose: Recursively saves all the classes in an object, in reverse order (top down) +// Output : int 0 on failure, 1 on success + +int CSave::DoWriteAll( const void *pLeafObject, datamap_t *pLeafMap, datamap_t *pCurMap ) +{ + // save base classes first + if ( pCurMap->baseMap ) + { + int status = DoWriteAll( pLeafObject, pLeafMap, pCurMap->baseMap ); + if ( !status ) + return status; + } + + return WriteFields( pCurMap->dataClassName, pLeafObject, pLeafMap, pCurMap->dataDesc, pCurMap->dataNumFields ); +} + +//------------------------------------- + +void CSave::StartBlock( const char *pszBlockName ) +{ + WriteHeader( pszBlockName, 0 ); // placeholder + m_BlockStartStack.AddToTail( GetWritePos() ); +} + +//------------------------------------- + +void CSave::StartBlock() +{ + StartBlock( "" ); +} + +//------------------------------------- + +void CSave::EndBlock() +{ + int endPos = GetWritePos(); + int startPos = m_BlockStartStack[ m_BlockStartStack.Count() - 1 ]; + short sizeBlock = endPos - startPos; + + m_BlockStartStack.Remove( m_BlockStartStack.Count() - 1 ); + + // Move to the the location where the size of the block was written & rewrite the size + SetWritePos( startPos - sizeof(SaveRestoreRecordHeader_t) ); + BufferData( (const char *)&sizeBlock, sizeof(short) ); + + SetWritePos( endPos ); +} + +//------------------------------------- + +void CSave::BufferString( char *pdata, int len ) +{ + char c = 0; + + BufferData( pdata, len ); // Write the string + BufferData( &c, 1 ); // Write a null terminator +} + +//------------------------------------- + +int CSave::DataEmpty( const char *pdata, int size ) +{ + for ( int i = 0; i < size; i++ ) + { + if ( pdata[i] ) + return 0; + } + return 1; +} + +//------------------------------------- + +void CSave::BufferField( const char *pname, int size, const char *pdata ) +{ + WriteHeader( pname, size ); + BufferData( pdata, size ); +} + +//------------------------------------- + +void CSave::WriteHeader( const char *pname, int size ) +{ + short shortSize = size; + short hashvalue = m_pData->FindCreateSymbol( pname ); + if ( size > SHRT_MAX || size < 0 ) + { + Warning( "CSave::WriteHeader() size parameter exceeds 'short'!\n" ); + Assert(0); + } + + BufferData( (const char *)&shortSize, sizeof(short) ); + BufferData( (const char *)&hashvalue, sizeof(short) ); +} + +//------------------------------------- + +void CSave::BufferData( const char *pdata, int size ) +{ + if ( !m_pData ) + return; + + if ( !m_pData->Write( pdata, size ) ) + { + Warning( "Save/Restore overflow!\n" ); + Assert(0); + } +} + +//--------------------------------------------------------- +// +// Game centric save methods. +// +int CSave::EntityIndex( const edict_t *pentLookup ) +{ +#if !defined( CLIENT_DLL ) + if ( pentLookup == NULL ) + return -1; + return EntityIndex( CBaseEntity::Instance(pentLookup) ); +#else + Assert( !"CSave::EntityIndex( edict_t * ) not valid on client!" ); + return -1; +#endif +} + + +//------------------------------------- + +int CSave::EntityIndex( const CBaseEntity *pEntity ) +{ + if ( !m_pGameInfo || pEntity == NULL ) + return -1; + + int i; + entitytable_t *pTable; + + for ( i = 0; i < m_pGameInfo->NumEntities(); i++ ) + { + pTable = m_pGameInfo->GetEntityInfo( i ); + if ( pTable->hEnt == pEntity ) + return pTable->id; + } + return -1; +} + +//------------------------------------- + +int CSave::EntityFlagsSet( int entityIndex, int flags ) +{ + if ( !m_pGameInfo || entityIndex < 0 ) + return 0; + if ( entityIndex > m_pGameInfo->NumEntities() ) + return 0; + + m_pGameInfo->GetEntityInfo( entityIndex )->flags |= flags; + + return m_pGameInfo->GetEntityInfo( entityIndex )->flags; +} + +//------------------------------------- + +void CSave::WriteTime( const char *pname, const float *data, int count ) +{ + int i; + float tmp; + + WriteHeader( pname, sizeof(float) * count ); + for ( i = 0; i < count; i++ ) + { + // Always encode time as a delta from the current time so it can be re-based if loaded in a new level + // Times of 0 are never written to the file, so they will be restored as 0, not a relative time + Assert( data[i] != ZERO_TIME ); + + if ( data[i] == 0.0 ) + { + tmp = ZERO_TIME; + } + else if ( data[i] == INVALID_TIME || data[i] == FLT_MAX ) + { + tmp = data[i]; + } + else + { + tmp = data[i] - m_pGameInfo->GetBaseTime(); + if ( fabsf( tmp ) < 0.001 ) // never allow a time to become zero due to rebasing + tmp = 0.001; + } + + WriteData( (const char *)&tmp, sizeof(float) ); + } +} + +//------------------------------------- + +void CSave::WriteTime( const float *data, int count ) +{ + int i; + float tmp; + + for ( i = 0; i < count; i++ ) + { + // Always encode time as a delta from the current time so it can be re-based if loaded in a new level + // Times of 0 are never written to the file, so they will be restored as 0, not a relative time + if ( data[i] == 0.0 ) + { + tmp = ZERO_TIME; + } + else if ( data[i] == INVALID_TIME || data[i] == FLT_MAX ) + { + tmp = data[i]; + } + else + { + tmp = data[i] - m_pGameInfo->GetBaseTime(); + if ( fabsf( tmp ) < 0.001 ) // never allow a time to become zero due to rebasing + tmp = 0.001; + } + + WriteData( (const char *)&tmp, sizeof(float) ); + } +} + +void CSave::WriteTick( const char *pname, const int *data, int count ) +{ + WriteHeader( pname, sizeof(int) * count ); + WriteTick( data, count ); +} + +//------------------------------------- + +void CSave::WriteTick( const int *data, int count ) +{ + int i; + int tmp; + + int baseTick = TIME_TO_TICKS( m_pGameInfo->GetBaseTime() ); + + for ( i = 0; i < count; i++ ) + { + // Always encode time as a delta from the current time so it can be re-based if loaded in a new level + // Times of 0 are never written to the file, so they will be restored as 0, not a relative time + tmp = data[ i ]; + if ( data[ i ] == TICK_NEVER_THINK ) + { + tmp = TICK_NEVER_THINK_ENCODE; + } + else + { + // Rebase it... + tmp -= baseTick; + } + WriteData( (const char *)&tmp, sizeof(int) ); + } +} +//------------------------------------- + +void CSave::WritePositionVector( const char *pname, const Vector &value ) +{ + Vector tmp = value; + + if ( tmp != vec3_invalid ) + tmp -= m_pGameInfo->GetLandmark(); + + WriteVector( pname, tmp ); +} + +//------------------------------------- + +void CSave::WritePositionVector( const Vector &value ) +{ + Vector tmp = value; + + if ( tmp != vec3_invalid ) + tmp -= m_pGameInfo->GetLandmark(); + + WriteVector( tmp ); +} + +//------------------------------------- + +void CSave::WritePositionVector( const char *pname, const Vector *value, int count ) +{ + WriteHeader( pname, sizeof(Vector) * count ); + WritePositionVector( value, count ); +} + +//------------------------------------- + +void CSave::WritePositionVector( const Vector *value, int count ) +{ + int i; + Vector tmp; + for ( i = 0; i < count; i++ ) + { + Vector tmp = value[i]; + + if ( tmp != vec3_invalid ) + tmp -= m_pGameInfo->GetLandmark(); + + WriteData( (const char *)&tmp.x, sizeof(Vector) ); + } +} + +//------------------------------------- + +void CSave::WriteFunction( datamap_t *pRootMap, const char *pname, const int *data, int count ) +{ + AssertMsg( count == 1, "Arrays of functions not presently supported" ); + const char *functionName = UTIL_FunctionToName( pRootMap, (void *)(*data) ); + + if ( functionName ) + { + BufferField( pname, strlen(functionName) + 1, functionName ); + } + else + { + Warning( "Invalid function pointer in entity!\n" ); + Assert(0); + } +} + +//------------------------------------- + +void CSave::WriteEntityPtr( const char *pname, CBaseEntity **ppEntity, int count ) +{ + AssertMsg( count <= MAX_ENTITYARRAY, "Array of entities or ehandles exceeds limit supported by save/restore" ); + int entityArray[MAX_ENTITYARRAY]; + for ( int i = 0; i < count && i < MAX_ENTITYARRAY; i++ ) + { + entityArray[i] = EntityIndex( ppEntity[i] ); + } + WriteInt( pname, entityArray, count ); +} + +//------------------------------------- + +void CSave::WriteEntityPtr( CBaseEntity **ppEntity, int count ) +{ + AssertMsg( count <= MAX_ENTITYARRAY, "Array of entities or ehandles exceeds limit supported by save/restore" ); + int entityArray[MAX_ENTITYARRAY]; + for ( int i = 0; i < count && i < MAX_ENTITYARRAY; i++ ) + { + entityArray[i] = EntityIndex( ppEntity[i] ); + } + WriteInt( entityArray, count ); +} + +//------------------------------------- + +void CSave::WriteEdictPtr( const char *pname, edict_t **ppEdict, int count ) +{ + AssertMsg( count <= MAX_ENTITYARRAY, "Array of entities or ehandles exceeds limit supported by save/restore" ); + int entityArray[MAX_ENTITYARRAY]; + for ( int i = 0; i < count && i < MAX_ENTITYARRAY; i++ ) + { + entityArray[i] = EntityIndex( ppEdict[i] ); + } + WriteInt( pname, entityArray, count ); +} + +//------------------------------------- + +void CSave::WriteEdictPtr( edict_t **ppEdict, int count ) +{ + AssertMsg( count <= MAX_ENTITYARRAY, "Array of entities or ehandles exceeds limit supported by save/restore" ); + int entityArray[MAX_ENTITYARRAY]; + for ( int i = 0; i < count && i < MAX_ENTITYARRAY; i++ ) + { + entityArray[i] = EntityIndex( ppEdict[i] ); + } + WriteInt( entityArray, count ); +} + +//------------------------------------- + +void CSave::WriteEHandle( const char *pname, const EHANDLE *pEHandle, int count ) +{ + AssertMsg( count <= MAX_ENTITYARRAY, "Array of entities or ehandles exceeds limit supported by save/restore" ); + int entityArray[MAX_ENTITYARRAY]; + for ( int i = 0; i < count && i < MAX_ENTITYARRAY; i++ ) + { + entityArray[i] = EntityIndex( (CBaseEntity *)(const_cast(pEHandle)[i]) ); + } + WriteInt( pname, entityArray, count ); +} + +//------------------------------------- + +void CSave::WriteEHandle( const EHANDLE *pEHandle, int count ) +{ + AssertMsg( count <= MAX_ENTITYARRAY, "Array of entities or ehandles exceeds limit supported by save/restore" ); + int entityArray[MAX_ENTITYARRAY]; + for ( int i = 0; i < count && i < MAX_ENTITYARRAY; i++ ) + { + entityArray[i] = EntityIndex( (CBaseEntity *)(const_cast(pEHandle)[i]) ); + } + WriteInt( entityArray, count ); +} + +//------------------------------------- +// Purpose: Writes all the fields that are not client neutral. In the event of +// a librarization of save/restore, these would not reside in the library + +bool CSave::WriteGameField( const char *pname, void *pData, datamap_t *pRootMap, typedescription_t *pField ) +{ + switch( pField->fieldType ) + { + case FIELD_CLASSPTR: + WriteEntityPtr( pField->fieldName, (CBaseEntity **)pData, pField->fieldSize ); + break; + + case FIELD_EDICT: + WriteEdictPtr( pField->fieldName, (edict_t **)pData, pField->fieldSize ); + break; + + case FIELD_EHANDLE: + WriteEHandle( pField->fieldName, (EHANDLE *)pData, pField->fieldSize ); + break; + + case FIELD_POSITION_VECTOR: + WritePositionVector( pField->fieldName, (Vector *)pData, pField->fieldSize ); + break; + + case FIELD_TIME: + WriteTime( pField->fieldName, (float *)pData, pField->fieldSize ); + break; + + case FIELD_TICK: + WriteTick( pField->fieldName, (int *)pData, pField->fieldSize ); + break; + + case FIELD_MODELINDEX: + { + int nModelIndex = *(int*)pData; + string_t strModelName = NULL_STRING; + const model_t *pModel = modelinfo->GetModel( nModelIndex ); + if ( pModel ) + { + strModelName = AllocPooledString( modelinfo->GetModelName( pModel ) ); + } + WriteString( pField->fieldName, (string_t *)&strModelName, pField->fieldSize ); + } + break; + + case FIELD_MATERIALINDEX: + { + int nMateralIndex = *(int*)pData; + string_t strMaterialName = NULL_STRING; + const char *pMaterialName = GetMaterialNameFromIndex( nMateralIndex ); + if ( pMaterialName ) + { + strMaterialName = MAKE_STRING( pMaterialName ); + } + WriteString( pField->fieldName, (string_t *)&strMaterialName, pField->fieldSize ); + } + break; + + case FIELD_MODELNAME: + case FIELD_SOUNDNAME: + WriteString( pField->fieldName, (string_t *)pData, pField->fieldSize ); + break; + + // For now, just write the address out, we're not going to change memory while doing this yet! + case FIELD_FUNCTION: + WriteFunction( pRootMap, pField->fieldName, (int *)(char *)pData, pField->fieldSize ); + break; + + case FIELD_VMATRIX: + WriteVMatrix( pField->fieldName, (VMatrix *)pData, pField->fieldSize ); + break; + case FIELD_VMATRIX_WORLDSPACE: + WriteVMatrixWorldspace( pField->fieldName, (VMatrix *)pData, pField->fieldSize ); + break; + + case FIELD_MATRIX3X4_WORLDSPACE: + WriteMatrix3x4Worldspace( pField->fieldName, (const matrix3x4_t *)pData, pField->fieldSize ); + break; + + case FIELD_INTERVAL: + WriteInterval( pField->fieldName, (interval_t *)pData, pField->fieldSize ); + break; + + default: + Warning( "Bad field type\n" ); + Assert(0); + return false; + } + + return true; +} + +//----------------------------------------------------------------------------- +// +// CRestore +// +//----------------------------------------------------------------------------- + +CRestore::CRestore( CSaveRestoreData *pdata ) + : m_pData( pdata ), + m_pGameInfo( pdata ), + m_global( 0 ), + m_precache( true ) +{ + m_BlockEndStack.EnsureCapacity( 32 ); +} + +//------------------------------------- + +int CRestore::GetReadPos() const +{ + return m_pData->GetCurPos(); +} + +//------------------------------------- + +void CRestore::SetReadPos( int pos ) +{ + m_pData->Seek(pos); +} + +//------------------------------------- + +const char *CRestore::StringFromHeaderSymbol( int symbol ) +{ + const char *pszResult = m_pData->StringFromSymbol( symbol ); + return ( pszResult ) ? pszResult : ""; +} + +//------------------------------------- +// Purpose: Reads all the fields that are client neutral. In the event of +// a librarization of save/restore, these would reside in the library + +void CRestore::ReadBasicField( const SaveRestoreRecordHeader_t &header, void *pDest, datamap_t *pRootMap, typedescription_t *pField ) +{ + switch( pField->fieldType ) + { + case FIELD_FLOAT: + { + ReadFloat( (float *)pDest, pField->fieldSize, header.size ); + break; + } + case FIELD_STRING: + { + ReadString( (string_t *)pDest, pField->fieldSize, header.size ); + break; + } + + case FIELD_VECTOR: + { + ReadVector( (Vector *)pDest, pField->fieldSize, header.size ); + break; + } + + case FIELD_QUATERNION: + { + ReadQuaternion( (Quaternion *)pDest, pField->fieldSize, header.size ); + break; + } + + case FIELD_INTEGER: + { + ReadInt( (int *)pDest, pField->fieldSize, header.size ); + break; + } + + case FIELD_BOOLEAN: + { + ReadBool( (bool *)pDest, pField->fieldSize, header.size ); + break; + } + + case FIELD_SHORT: + { + ReadShort( (short *)pDest, pField->fieldSize, header.size ); + break; + } + + case FIELD_CHARACTER: + { + ReadData( (char *)pDest, pField->fieldSize, header.size ); + break; + } + + case FIELD_COLOR32: + { + COMPILE_TIME_ASSERT( sizeof(color32) == sizeof(int) ); + ReadInt( (int *)pDest, pField->fieldSize, header.size ); + break; + } + + case FIELD_EMBEDDED: + { + AssertMsg( (( pField->flags & FTYPEDESC_PTR ) == 0) || (pField->fieldSize == 1), "Arrays of embedded pointer types presently unsupported by save/restore" ); +#ifdef _DEBUG + int startPos = GetReadPos(); +#endif + if ( !(pField->flags & FTYPEDESC_PTR) || *((void **)pDest) ) + { + int nFieldCount = pField->fieldSize; + char *pFieldData = (char *)( ( !(pField->flags & FTYPEDESC_PTR) ) ? pDest : *((void **)pDest) ); + while ( --nFieldCount >= 0 ) + { + // No corresponding "block" (see write) as it was used as the header of the field + ReadAll( pFieldData, pField->td ); + pFieldData += pField->fieldSizeInBytes; + } + Assert( GetReadPos() - startPos == header.size ); + } + else + { + SetReadPos( GetReadPos() + header.size ); + Warning( "Attempted to restore FIELD_EMBEDDEDBYREF %s but there is no destination memory\n", pField->fieldName ); + } + break; + + } + case FIELD_CUSTOM: + { + // No corresponding "block" (see write) as it was used as the header of the field + int posNextField = GetReadPos() + header.size; + + SaveRestoreFieldInfo_t fieldInfo = + { + pDest, + ((char *)pDest) - pField->fieldOffset[ TD_OFFSET_NORMAL ], + pField + }; + + pField->pSaveRestoreOps->Restore( fieldInfo, this ); + + Assert( posNextField >= GetReadPos() ); + SetReadPos( posNextField ); + break; + } + + default: + Warning( "Bad field type\n" ); + Assert(0); + } +} + +//------------------------------------- + +void CRestore::ReadField( const SaveRestoreRecordHeader_t &header, void *pDest, datamap_t *pRootMap, typedescription_t *pField ) +{ + if ( pField->fieldType <= FIELD_CUSTOM ) + ReadBasicField( header, pDest, pRootMap, pField ); + else + ReadGameField( header, pDest, pRootMap, pField ); +} + +//------------------------------------- + +bool CRestore::ShouldReadField( typedescription_t *pField ) +{ + if ( (pField->flags & FTYPEDESC_SAVE) == 0 ) + return false; + + if ( m_global && (pField->flags & FTYPEDESC_GLOBAL) ) + return false; + + return true; +} + +//------------------------------------- + +typedescription_t *CRestore::FindField( const char *pszFieldName, typedescription_t *pFields, int fieldCount, int *pCookie ) +{ + int &fieldNumber = *pCookie; + if ( pszFieldName ) + { + typedescription_t *pTest; + + for ( int i = 0; i < fieldCount; i++ ) + { + pTest = &pFields[fieldNumber]; + + ++fieldNumber; + if ( fieldNumber == fieldCount ) + fieldNumber = 0; + + if ( stricmp( pTest->fieldName, pszFieldName ) == 0 ) + return pTest; + } + } + + fieldNumber = 0; + return NULL; +} + +//------------------------------------- + +bool CRestore::ShouldEmptyField( typedescription_t *pField ) +{ + // don't clear out fields that don't get saved, or that are handled specially + if ( !( pField->flags & FTYPEDESC_SAVE ) ) + return false; + + // Don't clear global fields + if ( m_global && (pField->flags & FTYPEDESC_GLOBAL) ) + return false; + + return true; +} + +//------------------------------------- + +void CRestore::EmptyFields( void *pBaseData, typedescription_t *pFields, int fieldCount ) +{ + int i; + for ( i = 0; i < fieldCount; i++ ) + { + typedescription_t *pField = &pFields[i]; + if ( !ShouldEmptyField( pField ) ) + continue; + + void *pFieldData = (char *)pBaseData + pField->fieldOffset[ TD_OFFSET_NORMAL ]; + switch( pField->fieldType ) + { + case FIELD_CUSTOM: + { + SaveRestoreFieldInfo_t fieldInfo = + { + pFieldData, + pBaseData, + pField + }; + pField->pSaveRestoreOps->MakeEmpty( fieldInfo ); + } + break; + + case FIELD_EMBEDDED: + { + if ( (pField->flags & FTYPEDESC_PTR) && !*((void **)pFieldData) ) + break; + + int nFieldCount = pField->fieldSize; + char *pFieldMemory = (char *)( ( !(pField->flags & FTYPEDESC_PTR) ) ? pFieldData : *((void **)pFieldData) ); + while ( --nFieldCount >= 0 ) + { + EmptyFields( pFieldMemory, pField->td->dataDesc, pField->td->dataNumFields ); + pFieldMemory += pField->fieldSizeInBytes; + } + } + break; + + default: + // NOTE: If you hit this assertion, you've got a bug where you're using + // the wrong field type for your field + if ( pField->fieldSizeInBytes != pField->fieldSize * gSizes[pField->fieldType] ) + { + Warning("WARNING! Field %s is using the wrong FIELD_ type!\nFix this or you'll see a crash.\n", pField->fieldName ); + Assert( 0 ); + } + memset( pFieldData, (pField->fieldType != FIELD_EHANDLE) ? 0 : 0xFF, pField->fieldSize * gSizes[pField->fieldType] ); + break; + } + } +} + +//------------------------------------- + +void CRestore::StartBlock( SaveRestoreRecordHeader_t *pHeader ) +{ + ReadHeader( pHeader ); + m_BlockEndStack.AddToTail( GetReadPos() + pHeader->size ); +} + +//------------------------------------- + +void CRestore::StartBlock( char szBlockName[] ) +{ + SaveRestoreRecordHeader_t header; + StartBlock( &header ); + Q_strncpy( szBlockName, StringFromHeaderSymbol( header.symbol ), SIZE_BLOCK_NAME_BUF ); +} + +//------------------------------------- + +void CRestore::StartBlock() +{ + char szBlockName[SIZE_BLOCK_NAME_BUF]; + StartBlock( szBlockName ); +} + +//------------------------------------- + +void CRestore::EndBlock() +{ + int endPos = m_BlockEndStack[ m_BlockEndStack.Count() - 1 ]; + m_BlockEndStack.Remove( m_BlockEndStack.Count() - 1 ); + SetReadPos( endPos ); +} + +//------------------------------------- + +int CRestore::ReadFields( const char *pname, void *pBaseData, datamap_t *pRootMap, typedescription_t *pFields, int fieldCount ) +{ + static int lastName = -1; + Verify( ReadShort() == sizeof(int) ); // First entry should be an int + int symName = m_pData->FindCreateSymbol(pname); + + // Check the struct name + int curSym = ReadShort(); + if ( curSym != symName ) // Field Set marker + { + const char *pLastName = m_pData->StringFromSymbol( lastName ); + const char *pCurName = m_pData->StringFromSymbol( curSym ); + Msg( "Expected %s found %s ( raw '%s' )! (prev: %s)\n", pname, pCurName, BufferPointer(), pLastName ); + Msg( "Field type name may have changed or inheritance graph changed, save file is suspect\n" ); + m_pData->Rewind( 2*sizeof(short) ); + return 0; + } + lastName = symName; + + // Clear out base data + EmptyFields( pBaseData, pFields, fieldCount ); + + // Skip over the struct name + int i; + int nFieldsSaved = ReadInt(); // Read field count + int searchCookie = 0; // Make searches faster, most data is read/written in the same order + SaveRestoreRecordHeader_t header; + + for ( i = 0; i < nFieldsSaved; i++ ) + { + ReadHeader( &header ); + + typedescription_t *pField = FindField( m_pData->StringFromSymbol( header.symbol ), pFields, fieldCount, &searchCookie); + if ( pField && ShouldReadField( pField ) ) + { + ReadField( header, ((char *)pBaseData + pField->fieldOffset[ TD_OFFSET_NORMAL ]), pRootMap, pField ); + } + else + { + BufferSkipBytes( header.size ); // Advance to next field + } + } + + return 1; +} + +//------------------------------------- + +void CRestore::ReadHeader( SaveRestoreRecordHeader_t *pheader ) +{ + if ( pheader != NULL ) + { + Assert( pheader!=NULL ); + pheader->size = ReadShort(); // Read field size + pheader->symbol = ReadShort(); // Read field name token + } + else + { + BufferSkipBytes( sizeof(short) * 2 ); + } +} + +//------------------------------------- + +short CRestore::ReadShort( void ) +{ + short tmp = 0; + + BufferReadBytes( (char *)&tmp, sizeof(short) ); + + return tmp; +} + +//------------------------------------- + +int CRestore::ReadInt( void ) +{ + int tmp = 0; + + BufferReadBytes( (char *)&tmp, sizeof(int) ); + + return tmp; +} + +//------------------------------------- +// Purpose: Recursively restores all the classes in an object, in reverse order (top down) +// Output : int 0 on failure, 1 on success + +int CRestore::DoReadAll( void *pLeafObject, datamap_t *pLeafMap, datamap_t *pCurMap ) +{ + // restore base classes first + if ( pCurMap->baseMap ) + { + int status = DoReadAll( pLeafObject, pLeafMap, pCurMap->baseMap ); + if ( !status ) + return status; + } + + return ReadFields( pCurMap->dataClassName, pLeafObject, pLeafMap, pCurMap->dataDesc, pCurMap->dataNumFields ); +} + +//------------------------------------- + +char *CRestore::BufferPointer( void ) +{ + if ( !m_pData ) + return NULL; + + return m_pData->AccessCurPos(); +} + +//------------------------------------- + +void CRestore::BufferReadBytes( char *pOutput, int size ) +{ + Assert( m_pData !=NULL ); + + if ( !m_pData || m_pData->BytesAvailable() == 0 ) + return; + + if ( !m_pData->Read( pOutput, size ) ) + { + Warning( "Restore underflow!\n" ); + Assert(0); + } +} + +//------------------------------------- + +void CRestore::BufferSkipBytes( int bytes ) +{ + BufferReadBytes( NULL, bytes ); +} + +//------------------------------------- + +int CRestore::ReadShort( short *pValue, int nElems, int nBytesAvailable ) +{ + return ReadSimple( pValue, nElems, nBytesAvailable ); +} + +//------------------------------------- + +int CRestore::ReadInt( int *pValue, int nElems, int nBytesAvailable ) +{ + return ReadSimple( pValue, nElems, nBytesAvailable ); +} + +//------------------------------------- + +int CRestore::ReadBool( bool *pValue, int nElems, int nBytesAvailable ) +{ + COMPILE_TIME_ASSERT( sizeof(bool) == sizeof(char) ); + return ReadSimple( pValue, nElems, nBytesAvailable ); +} + +//------------------------------------- + +int CRestore::ReadFloat( float *pValue, int nElems, int nBytesAvailable ) +{ + return ReadSimple( pValue, nElems, nBytesAvailable ); +} + +//------------------------------------- + +int CRestore::ReadData( char *pData, int size, int nBytesAvailable ) +{ + return ReadSimple( pData, size, nBytesAvailable ); +} + +//------------------------------------- + +void CRestore::ReadString( char *pDest, int nSizeDest, int nBytesAvailable ) +{ + const char *pString = BufferPointer(); + if ( !nBytesAvailable ) + nBytesAvailable = strlen( pString ) + 1; + BufferSkipBytes( nBytesAvailable ); + + Q_strncpy(pDest, pString, nSizeDest ); +} + +//------------------------------------- + +int CRestore::ReadString( string_t *pValue, int nElems, int nBytesAvailable ) +{ + AssertMsg( nBytesAvailable > 0, "CRestore::ReadString() implementation does not currently support unspecified bytes available"); + + int i; + char *pString = BufferPointer(); + char *pLimit = pString + nBytesAvailable; + for ( i = 0; i < nElems && pString < pLimit; i++ ) + { + if ( *((char *)pString) == 0 ) + pValue[i] = NULL_STRING; + else + pValue[i] = AllocPooledString( (char *)pString ); + + while (*pString) + pString++; + pString++; + } + + BufferSkipBytes( nBytesAvailable ); + + return i; +} + +//------------------------------------- + +int CRestore::ReadVector( Vector *pValue) +{ + BufferReadBytes( (char *)pValue, sizeof(Vector) ); + return 1; +} + +//------------------------------------- + +int CRestore::ReadVector( Vector *pValue, int nElems, int nBytesAvailable ) +{ + return ReadSimple( pValue, nElems, nBytesAvailable ); +} + +int CRestore::ReadQuaternion( Quaternion *pValue) +{ + BufferReadBytes( (char *)pValue, sizeof(Quaternion) ); + return 1; +} + +//------------------------------------- + +int CRestore::ReadQuaternion( Quaternion *pValue, int nElems, int nBytesAvailable ) +{ + return ReadSimple( pValue, nElems, nBytesAvailable ); +} + +//------------------------------------- +int CRestore::ReadVMatrix( VMatrix *pValue, int nElems, int nBytesAvailable ) +{ + return ReadSimple( pValue, nElems, nBytesAvailable ); +} + + +int CRestore::ReadVMatrixWorldspace( VMatrix *pValue, int nElems, int nBytesAvailable ) +{ + Vector basePosition = m_pGameInfo->GetLandmark(); + VMatrix tmp; + + for ( int i = 0; i < nElems; i++ ) + { + BufferReadBytes( (char *)&tmp, sizeof(float)*16 ); + + VMatrixOffset( pValue[i], tmp, basePosition ); + } + return nElems; +} + + +int CRestore::ReadMatrix3x4Worldspace( matrix3x4_t *pValue, int nElems, int nBytesAvailable ) +{ + Vector basePosition = m_pGameInfo->GetLandmark(); + matrix3x4_t tmp; + + for ( int i = 0; i < nElems; i++ ) + { + BufferReadBytes( (char *)&tmp, sizeof(matrix3x4_t) ); + + Matrix3x4Offset( pValue[i], tmp, basePosition ); + } + return nElems; +} + +int CRestore::ReadInterval( interval_t *interval, int count, int nBytesAvailable ) +{ + return ReadSimple( interval, count, nBytesAvailable ); +} + +//--------------------------------------------------------- +// +// Game centric restore methods +// + +CBaseEntity *CRestore::EntityFromIndex( int entityIndex ) +{ + if ( !m_pGameInfo || entityIndex < 0 ) + return NULL; + + int i; + entitytable_t *pTable; + + for ( i = 0; i < m_pGameInfo->NumEntities(); i++ ) + { + pTable = m_pGameInfo->GetEntityInfo( i ); + if ( pTable->id == entityIndex ) + return pTable->hEnt; + } + return NULL; +} + +//------------------------------------- + +int CRestore::ReadEntityPtr( CBaseEntity **ppEntity, int count, int nBytesAvailable ) +{ + AssertMsg( count <= MAX_ENTITYARRAY, "Array of entities or ehandles exceeds limit supported by save/restore" ); + int entityArray[MAX_ENTITYARRAY]; + + int nRead = ReadInt( entityArray, count, nBytesAvailable ); + + for ( int i = 0; i < nRead; i++ ) // nRead is never greater than count + { + ppEntity[i] = EntityFromIndex( entityArray[i] ); + } + + if ( nRead < count) + { + memset( &ppEntity[nRead], 0, ( count - nRead ) * sizeof(ppEntity[0]) ); + } + + return nRead; +} + +//------------------------------------- +int CRestore::ReadEdictPtr( edict_t **ppEdict, int count, int nBytesAvailable ) +{ +#if !defined( CLIENT_DLL ) + AssertMsg( count <= MAX_ENTITYARRAY, "Array of entities or ehandles exceeds limit supported by save/restore" ); + int entityArray[MAX_ENTITYARRAY]; + CBaseEntity *pEntity; + + int nRead = ReadInt( entityArray, count, nBytesAvailable ); + + for ( int i = 0; i < nRead; i++ ) // nRead is never greater than count + { + pEntity = EntityFromIndex( entityArray[i] ); + ppEdict[i] = (pEntity) ? pEntity->edict() : NULL; + } + + if ( nRead < count) + { + memset( &ppEdict[nRead], 0, ( count - nRead ) * sizeof(ppEdict[0]) ); + } + + return nRead; +#else + return 0; +#endif +} + + +//------------------------------------- + +int CRestore::ReadEHandle( EHANDLE *pEHandle, int count, int nBytesAvailable ) +{ + AssertMsg( count <= MAX_ENTITYARRAY, "Array of entities or ehandles exceeds limit supported by save/restore" ); + int entityArray[MAX_ENTITYARRAY]; + + int nRead = ReadInt( entityArray, count, nBytesAvailable ); + + for ( int i = 0; i < nRead; i++ ) // nRead is never greater than count + { + pEHandle[i] = EntityFromIndex( entityArray[i] ); + } + + if ( nRead < count) + { + memset( &pEHandle[nRead], 0xFF, ( count - nRead ) * sizeof(pEHandle[0]) ); + } + + return nRead; +} + +//------------------------------------- +// Purpose: Reads all the fields that are not client neutral. In the event of +// a librarization of save/restore, these would NOT reside in the library + +void CRestore::ReadGameField( const SaveRestoreRecordHeader_t &header, void *pDest, datamap_t *pRootMap, typedescription_t *pField ) +{ + switch( pField->fieldType ) + { + case FIELD_POSITION_VECTOR: + { + ReadPositionVector( (Vector *)pDest, pField->fieldSize, header.size ); + break; + } + + case FIELD_TIME: + { + ReadTime( (float *)pDest, pField->fieldSize, header.size ); + break; + } + + case FIELD_TICK: + { + ReadTick( (int *)pDest, pField->fieldSize, header.size ); + break; + } + + case FIELD_FUNCTION: + { + ReadFunction( pRootMap, (void **)pDest, pField->fieldSize, header.size ); + break; + } + + case FIELD_MODELINDEX: + { + int *pModelIndex = (int*)pDest; + string_t *pModelName = (string_t *)stackalloc( pField->fieldSize * sizeof(string_t) ); + int nRead = ReadString( pModelName, pField->fieldSize, header.size ); + + for ( int i = 0; i < nRead; i++ ) + { + if ( pModelName[i] == NULL_STRING ) + { + pModelIndex[i] = -1; + continue; + } + + pModelIndex[i] = modelinfo->GetModelIndex( STRING( pModelName[i] ) ); + +#if !defined( CLIENT_DLL ) + if ( m_precache ) + { + CBaseEntity::PrecacheModel( STRING( pModelName[i] ) ); + } +#endif + } + break; + } + + case FIELD_MATERIALINDEX: + { + int *pMaterialIndex = (int*)pDest; + string_t *pMaterialName = (string_t *)stackalloc( pField->fieldSize * sizeof(string_t) ); + int nRead = ReadString( pMaterialName, pField->fieldSize, header.size ); + + for ( int i = 0; i < nRead; i++ ) + { + if ( pMaterialName[i] == NULL_STRING ) + { + pMaterialIndex[i] = 0; + continue; + } + + pMaterialIndex[i] = GetMaterialIndex( STRING( pMaterialName[i] ) ); + +#if !defined( CLIENT_DLL ) + if ( m_precache ) + { + PrecacheMaterial( STRING( pMaterialName[i] ) ); + } +#endif + } + break; + } + + case FIELD_MODELNAME: + case FIELD_SOUNDNAME: + { + string_t *pStringDest = (string_t *)pDest; + int nRead = ReadString( pStringDest, pField->fieldSize, header.size ); + if ( m_precache ) + { + for ( int i = 0; i < nRead; i++ ) + { + if ( pStringDest[i] != NULL_STRING ) + { +#if !defined( CLIENT_DLL ) + if ( pField->fieldType == FIELD_MODELNAME ) + { + CBaseEntity::PrecacheModel( STRING( pStringDest[i] ) ); + } + else if ( pField->fieldType == FIELD_SOUNDNAME ) + { + CBaseEntity::PrecacheScriptSound( STRING( pStringDest[i] ) ); + } +#endif + } + } + } + break; + } + + case FIELD_CLASSPTR: + ReadEntityPtr( (CBaseEntity **)pDest, pField->fieldSize, header.size ); + break; + + case FIELD_EDICT: +#if !defined( CLIENT_DLL ) + ReadEdictPtr( (edict_t **)pDest, pField->fieldSize, header.size ); +#else + Assert( !"FIELD_EDICT not valid for client .dll" ); +#endif + break; + case FIELD_EHANDLE: + ReadEHandle( (EHANDLE *)pDest, pField->fieldSize, header.size ); + break; + + case FIELD_VMATRIX: + { + ReadVMatrix( (VMatrix *)pDest, pField->fieldSize, header.size ); + break; + } + + case FIELD_VMATRIX_WORLDSPACE: + ReadVMatrixWorldspace( (VMatrix *)pDest, pField->fieldSize, header.size ); + break; + + case FIELD_MATRIX3X4_WORLDSPACE: + ReadMatrix3x4Worldspace( (matrix3x4_t *)pDest, pField->fieldSize, header.size ); + break; + + case FIELD_INTERVAL: + ReadInterval( (interval_t *)pDest, pField->fieldSize, header.size ); + break; + + default: + Warning( "Bad field type\n" ); + Assert(0); + } +} + +//------------------------------------- + +int CRestore::ReadTime( float *pValue, int count, int nBytesAvailable ) +{ + float baseTime = m_pGameInfo->GetBaseTime(); + int nRead = ReadFloat( pValue, count, nBytesAvailable ); + + for ( int i = nRead - 1; i >= 0; i-- ) + { + if ( pValue[i] == ZERO_TIME ) + pValue[i] = 0.0; + else if ( pValue[i] != INVALID_TIME && pValue[i] != FLT_MAX ) + pValue[i] += baseTime; + } + + return nRead; +} + +int CRestore::ReadTick( int *pValue, int count, int nBytesAvailable ) +{ + // HACK HACK: Adding 0.1f here makes sure that all tick times read + // from .sav file which are near the basetime will end up just ahead of + // the base time, because we are restoring we'll have a slow frame of the + // max frametime of 0.1 seconds and that could otherwise cause all of our + // think times to get synchronized to each other... sigh. ywb... + int baseTick = TIME_TO_TICKS( m_pGameInfo->GetBaseTime() + 0.1f ); + int nRead = ReadInt( pValue, count, nBytesAvailable ); + + for ( int i = nRead - 1; i >= 0; i-- ) + { + if ( pValue[ i ] != TICK_NEVER_THINK_ENCODE ) + { + // Rebase it + pValue[i] += baseTick; + } + else + { + // Slam to -1 value + pValue[ i ] = TICK_NEVER_THINK; + } + } + + return nRead; +} + +//------------------------------------- + +int CRestore::ReadPositionVector( Vector *pValue ) +{ + return ReadPositionVector( pValue, 1, sizeof(Vector) ); +} + +//------------------------------------- + +int CRestore::ReadPositionVector( Vector *pValue, int count, int nBytesAvailable ) +{ + Vector basePosition = m_pGameInfo->GetLandmark(); + int nRead = ReadVector( pValue, count, nBytesAvailable ); + + for ( int i = nRead - 1; i >= 0; i-- ) + { + if ( pValue[i] != vec3_invalid ) + pValue[i] += basePosition; + } + + return nRead; +} + +//------------------------------------- + +int CRestore::ReadFunction( datamap_t *pMap, void **pValue, int count, int nBytesAvailable ) +{ + AssertMsg( nBytesAvailable > 0, "CRestore::ReadFunction() implementation does not currently support unspecified bytes available"); + + char *pszFunctionName = BufferPointer(); + BufferSkipBytes( nBytesAvailable ); + + AssertMsg( count == 1, "Arrays of functions not presently supported" ); + + if ( *pszFunctionName == 0 ) + *pValue = NULL; + else + *pValue = UTIL_FunctionFromName( pMap, pszFunctionName ); + + return 0; +} + +//----------------------------------------------------------------------------- +// +// Entity data saving routines +// +//----------------------------------------------------------------------------- + +BEGIN_SIMPLE_DATADESC(entitytable_t) + DEFINE_FIELD( id, FIELD_INTEGER ), + DEFINE_FIELD( edictindex, FIELD_INTEGER ), + DEFINE_FIELD( saveentityindex, FIELD_INTEGER ), +// DEFINE_FIELD( restoreentityindex, FIELD_INTEGER ), + // hEnt (not saved, this is the fixup) + DEFINE_FIELD( location, FIELD_INTEGER ), + DEFINE_FIELD( size, FIELD_INTEGER ), + DEFINE_FIELD( flags, FIELD_INTEGER ), + DEFINE_FIELD( classname, FIELD_STRING ), + DEFINE_FIELD( globalname, FIELD_STRING ), + DEFINE_FIELD( landmarkModelSpace, FIELD_VECTOR ), + DEFINE_FIELD( modelname, FIELD_STRING ), +END_DATADESC() + + +//----------------------------------------------------------------------------- +// Utilities entities can use when saving +//----------------------------------------------------------------------------- +class CEntitySaveUtils : public IEntitySaveUtils +{ +public: + // Call these in pre-save + post save + void PreSave(); + void PostSave(); + + // Methods of IEntitySaveUtils + virtual void AddLevelTransitionSaveDependency( CBaseEntity *pEntity1, CBaseEntity *pEntity2 ); + virtual int GetEntityDependencyCount( CBaseEntity *pEntity ); + virtual int GetEntityDependencies( CBaseEntity *pEntity, int nCount, CBaseEntity **ppEntList ); + +private: + IPhysicsObjectPairHash *m_pLevelAdjacencyDependencyHash; +}; + + +//----------------------------------------------------------------------------- +// Call these in pre-save + post save +//----------------------------------------------------------------------------- +void CEntitySaveUtils::PreSave() +{ + Assert( !m_pLevelAdjacencyDependencyHash ); + MEM_ALLOC_CREDIT(); + m_pLevelAdjacencyDependencyHash = physics->CreateObjectPairHash(); +} + +void CEntitySaveUtils::PostSave() +{ + physics->DestroyObjectPairHash( m_pLevelAdjacencyDependencyHash ); + m_pLevelAdjacencyDependencyHash = NULL; +} + + +//----------------------------------------------------------------------------- +// Gets the # of dependencies for a particular entity +//----------------------------------------------------------------------------- +int CEntitySaveUtils::GetEntityDependencyCount( CBaseEntity *pEntity ) +{ + return m_pLevelAdjacencyDependencyHash->GetPairCountForObject( pEntity ); +} + + +//----------------------------------------------------------------------------- +// Gets all dependencies for a particular entity +//----------------------------------------------------------------------------- +int CEntitySaveUtils::GetEntityDependencies( CBaseEntity *pEntity, int nCount, CBaseEntity **ppEntList ) +{ + return m_pLevelAdjacencyDependencyHash->GetPairListForObject( pEntity, nCount, (void**)ppEntList ); +} + + +//----------------------------------------------------------------------------- +// Methods of IEntitySaveUtils +//----------------------------------------------------------------------------- +void CEntitySaveUtils::AddLevelTransitionSaveDependency( CBaseEntity *pEntity1, CBaseEntity *pEntity2 ) +{ + if ( pEntity1 != pEntity2 ) + { + m_pLevelAdjacencyDependencyHash->AddObjectPair( pEntity1, pEntity2 ); + } +} + + +//----------------------------------------------------------------------------- +// Block handler for save/restore of entities +//----------------------------------------------------------------------------- +class CEntitySaveRestoreBlockHandler : public ISaveRestoreBlockHandler +{ +public: + const char *GetBlockName(); + void PreSave( CSaveRestoreData *pSaveData ); + void Save( ISave *pSave ); + void WriteSaveHeaders( ISave *pSave ); + virtual void PostSave(); + virtual void PreRestore(); + void ReadRestoreHeaders( IRestore *pRestore ); + + void Restore( IRestore *pRestore, bool createPlayers ); + virtual void PostRestore(); + + inline IEntitySaveUtils * GetEntitySaveUtils() { return &m_EntitySaveUtils; } + +private: + friend int CreateEntityTransitionList( CSaveRestoreData *pSaveData, int levelMask ); + bool SaveInitEntities( CSaveRestoreData *pSaveData ); + bool DoRestoreEntity( CBaseEntity *pEntity, IRestore *pRestore ); + Vector ModelSpaceLandmark( int modelIndex ); + int RestoreEntity( CBaseEntity *pEntity, IRestore *pRestore, entitytable_t *pEntInfo ); + +#if !defined( CLIENT_DLL ) + // Find the matching global entity. Spit out an error if the designer made entities of + // different classes with the same global name + CBaseEntity *FindGlobalEntity( string_t classname, string_t globalname ); + + int RestoreGlobalEntity( CBaseEntity *pEntity, CSaveRestoreData *pSaveData, entitytable_t *pEntInfo ); +#endif + +private: + CEntitySaveUtils m_EntitySaveUtils; +}; + + +//----------------------------------------------------------------------------- + +CEntitySaveRestoreBlockHandler g_EntitySaveRestoreBlockHandler; + +//------------------------------------- + +ISaveRestoreBlockHandler *GetEntitySaveRestoreBlockHandler() +{ + return &g_EntitySaveRestoreBlockHandler; +} + +IEntitySaveUtils *GetEntitySaveUtils() +{ + return g_EntitySaveRestoreBlockHandler.GetEntitySaveUtils(); +} + + +//----------------------------------------------------------------------------- +// Implementation of the block handler for save/restore of entities +//----------------------------------------------------------------------------- +const char *CEntitySaveRestoreBlockHandler::GetBlockName() +{ + return "Entities"; +} + +//--------------------------------- + +void CEntitySaveRestoreBlockHandler::PreSave( CSaveRestoreData *pSaveData ) +{ + IGameSystem::OnSaveAllSystems(); + + m_EntitySaveUtils.PreSave(); + + // Allow the entities to do some work + CBaseEntity *pEnt = NULL; +#if !defined( CLIENT_DLL ) + while ( (pEnt = gEntList.NextEnt( pEnt )) != NULL ) + { + pEnt->OnSave( &m_EntitySaveUtils ); + } +#else + // Do this because it'll force entities to figure out their origins, and that requires + // SetupBones in the case of aiments. + C_BaseAnimating::PushAllowBoneAccess( true, true ); + + int last = ClientEntityList().GetHighestEntityIndex(); + ClientEntityHandle_t iter = ClientEntityList().FirstHandle(); + + for ( int e = 0; e <= last; e++ ) + { + pEnt = ClientEntityList().GetBaseEntity( e ); + + if( !pEnt ) + continue; + + pEnt->OnSave(); + } + + while ( iter != ClientEntityList().InvalidHandle() ) + { + pEnt = ClientEntityList().GetBaseEntityFromHandle( iter ); + + if ( pEnt && pEnt->ObjectCaps() & FCAP_SAVE_NON_NETWORKABLE ) + { + pEnt->OnSave(); + } + + iter = ClientEntityList().NextHandle( iter ); + } + + + C_BaseAnimating::PopBoneAccess(); +#endif + SaveInitEntities( pSaveData ); +} + +//--------------------------------- + +void CEntitySaveRestoreBlockHandler::Save( ISave *pSave ) +{ + CGameSaveRestoreInfo *pSaveData = pSave->GetGameSaveRestoreInfo(); + + // write entity list that was previously built by SaveInitEntities() + for ( int i = 0; i < pSaveData->NumEntities(); i++ ) + { + entitytable_t *pEntInfo = pSaveData->GetEntityInfo( i ); + pEntInfo->location = pSave->GetWritePos(); + pEntInfo->size = 0; + + CBaseEntity *pEnt = pEntInfo->hEnt; + if ( pEnt && !( pEnt->ObjectCaps() & FCAP_DONT_SAVE ) ) + { + MDLCACHE_CRITICAL_SECTION(); +#if !defined( CLIENT_DLL ) + AssertMsg( !pEnt->edict() || ( pEnt->m_iClassname != NULL_STRING && + (STRING(pEnt->m_iClassname)[0] != 0) && + FStrEq( STRING(pEnt->m_iClassname), pEnt->GetClassname()) ), + "Saving entity with invalid classname" ); +#endif + + pSaveData->SetCurrentEntityContext( pEnt ); + pEnt->Save( *pSave ); + pSaveData->SetCurrentEntityContext( NULL ); + + pEntInfo->size = pSave->GetWritePos() - pEntInfo->location; // Size of entity block is data size written to block + + pEntInfo->classname = pEnt->m_iClassname; // Remember entity class for respawn + +#if !defined( CLIENT_DLL ) + pEntInfo->globalname = pEnt->m_iGlobalname; // remember global name + pEntInfo->landmarkModelSpace = ModelSpaceLandmark( pEnt->GetModelIndex() ); + int nEntIndex = pEnt->edict() ? ENTINDEX(pEnt->edict()) : -1; + bool bIsPlayer = ( ( nEntIndex >= 1 ) && ( nEntIndex <= gpGlobals->maxClients ) ) ? true : false; + if ( bIsPlayer ) + { + pEntInfo->flags |= FENTTABLE_PLAYER; + } +#endif + } + } +} + +//--------------------------------- + +void CEntitySaveRestoreBlockHandler::WriteSaveHeaders( ISave *pSave ) +{ + CGameSaveRestoreInfo *pSaveData = pSave->GetGameSaveRestoreInfo(); + + int nEntities = pSaveData->NumEntities(); + pSave->WriteInt( &nEntities ); + + for ( int i = 0; i < pSaveData->NumEntities(); i++ ) + pSave->WriteFields( "ETABLE", pSaveData->GetEntityInfo( i ), NULL, entitytable_t::m_DataMap.dataDesc, entitytable_t::m_DataMap.dataNumFields ); +} + +//--------------------------------- + +void CEntitySaveRestoreBlockHandler::PostSave() +{ + m_EntitySaveUtils.PostSave(); +} + +//--------------------------------- + +void CEntitySaveRestoreBlockHandler::PreRestore() +{ +} + +//--------------------------------- + +void CEntitySaveRestoreBlockHandler::ReadRestoreHeaders( IRestore *pRestore ) +{ + CGameSaveRestoreInfo *pSaveData = pRestore->GetGameSaveRestoreInfo(); + + int nEntities; + pRestore->ReadInt( &nEntities ); + + entitytable_t *pEntityTable = ( entitytable_t *)engine->SaveAllocMemory( (sizeof(entitytable_t) * nEntities), sizeof(char) ); + + pSaveData->InitEntityTable( pEntityTable, nEntities ); + + for ( int i = 0; i < pSaveData->NumEntities(); i++ ) + pRestore->ReadFields( "ETABLE", pSaveData->GetEntityInfo( i ), NULL, entitytable_t::m_DataMap.dataDesc, entitytable_t::m_DataMap.dataNumFields ); + +} + +//--------------------------------- + +#if !defined( CLIENT_DLL ) + +void CEntitySaveRestoreBlockHandler::Restore( IRestore *pRestore, bool createPlayers ) +{ + entitytable_t *pEntInfo; + CBaseEntity *pent; + + CGameSaveRestoreInfo *pSaveData = pRestore->GetGameSaveRestoreInfo(); + + bool restoredWorld = false; + + // Create entity list + int i; + for ( i = 0; i < pSaveData->NumEntities(); i++ ) + { + pEntInfo = pSaveData->GetEntityInfo( i ); + + if ( pEntInfo->classname != NULL_STRING && pEntInfo->size && !(pEntInfo->flags & FENTTABLE_REMOVED) ) + { + if ( pEntInfo->edictindex == 0 ) // worldspawn + { + Assert( i == 0 ); + pent = CreateEntityByName( STRING(pEntInfo->classname) ); + pRestore->SetReadPos( pEntInfo->location ); + if ( RestoreEntity( pent, pRestore, pEntInfo ) < 0 ) + { + pEntInfo->hEnt = NULL; + pEntInfo->restoreentityindex = -1; + UTIL_RemoveImmediate( pent ); + } + else + { + // force the entity to be relinked + AddRestoredEntity( pent ); + } + } + else if ( (pEntInfo->edictindex > 0) && (pEntInfo->edictindex <= gpGlobals->maxClients) ) + { + if ( !(pEntInfo->flags & FENTTABLE_PLAYER) ) + { + Warning( "ENTITY IS NOT A PLAYER: %d\n" , i ); + Assert(0); + } + + edict_t *ed = INDEXENT( pEntInfo->edictindex ); + + if ( ed && createPlayers ) + { + // create the player + pent = CBasePlayer::CreatePlayer( STRING(pEntInfo->classname), ed ); + } + else + pent = NULL; + } + else + { + pent = CreateEntityByName( STRING(pEntInfo->classname) ); + } + pEntInfo->hEnt = pent; + pEntInfo->restoreentityindex = pent ? pent->entindex() : - 1; + if ( pent && pEntInfo->restoreentityindex == 0 ) + { + if ( !FClassnameIs( pent, "worldspawn" ) ) + { + pEntInfo->restoreentityindex = -1; + } + } + + if ( pEntInfo->restoreentityindex == 0 ) + { + Assert( !restoredWorld ); + restoredWorld = true; + } + } + else + { + pEntInfo->hEnt = NULL; + pEntInfo->restoreentityindex = -1; + } + } + + // Now spawn entities + for ( i = 0; i < pSaveData->NumEntities(); i++ ) + { + pEntInfo = pSaveData->GetEntityInfo( i ); + if ( pEntInfo->edictindex != 0 ) + { + pent = pEntInfo->hEnt; + pRestore->SetReadPos( pEntInfo->location ); + if ( pent ) + { + if ( RestoreEntity( pent, pRestore, pEntInfo ) < 0 ) + { + pEntInfo->hEnt = NULL; + pEntInfo->restoreentityindex = -1; + UTIL_RemoveImmediate( pent ); + } + else + { + AddRestoredEntity( pent ); + } + } + } + } +} + +#else // CLIENT DLL VERSION + +void CEntitySaveRestoreBlockHandler::Restore( IRestore *pRestore, bool createPlayers ) +{ + entitytable_t *pEntInfo; + CBaseEntity *pent; + + CGameSaveRestoreInfo *pSaveData = pRestore->GetGameSaveRestoreInfo(); + + // Create entity list + int i; + bool restoredWorld = false; + + for ( i = 0; i < pSaveData->NumEntities(); i++ ) + { + pEntInfo = pSaveData->GetEntityInfo( i ); + pent = ClientEntityList().GetBaseEntity( pEntInfo->restoreentityindex ); + pEntInfo->hEnt = pent; + } + + // Blast saved data into entities + for ( i = 0; i < pSaveData->NumEntities(); i++ ) + { + pEntInfo = pSaveData->GetEntityInfo( i ); + + bool bRestoredCorrectly = false; + // FIXME, need to translate save spot to real index here using lookup table transmitted from server + //Assert( !"Need translation still" ); + if ( pEntInfo->restoreentityindex >= 0 ) + { + if ( pEntInfo->restoreentityindex == 0 ) + { + Assert( !restoredWorld ); + restoredWorld = true; + } + + pent = ClientEntityList().GetBaseEntity( pEntInfo->restoreentityindex ); + pRestore->SetReadPos( pEntInfo->location ); + if ( pent ) + { + if ( RestoreEntity( pent, pRestore, pEntInfo ) >= 0 ) + { + // Call the OnRestore method + AddRestoredEntity( pent ); + bRestoredCorrectly = true; + } + } + } + // BUGBUG: JAY: Disable ragdolls across transitions until PVS/solid check & client entity patch file are implemented + else if ( !pSaveData->levelInfo.fUseLandmark ) + { + if ( pEntInfo->classname != NULL_STRING ) + { + pent = CreateEntityByName( STRING(pEntInfo->classname) ); + pent->InitializeAsClientEntity( NULL, RENDER_GROUP_OPAQUE_ENTITY ); + + pRestore->SetReadPos( pEntInfo->location ); + + if ( pent ) + { + if ( RestoreEntity( pent, pRestore, pEntInfo ) >= 0 ) + { + pEntInfo->hEnt = pent; + AddRestoredEntity( pent ); + bRestoredCorrectly = true; + } + } + } + } + + if ( !bRestoredCorrectly ) + { + pEntInfo->hEnt = NULL; + pEntInfo->restoreentityindex = -1; + } + } + + // Note, server does this after local player connects fully + IGameSystem::OnRestoreAllSystems(); + + // Tell hud elements to modify behavior based on game restoration, if applicable + gHUD.OnRestore(); +} +#endif + +void CEntitySaveRestoreBlockHandler::PostRestore() +{ +} + +void SaveEntityOnTable( CBaseEntity *pEntity, CSaveRestoreData *pSaveData, int &iSlot ) +{ + entitytable_t *pEntInfo = pSaveData->GetEntityInfo( iSlot ); + pEntInfo->id = iSlot; +#if !defined( CLIENT_DLL ) + pEntInfo->edictindex = pEntity->RequiredEdictIndex(); +#else + pEntInfo->edictindex = -1; + pEntInfo->modelname = pEntity->GetModelName(); +#endif + pEntInfo->restoreentityindex = -1; + pEntInfo->saveentityindex = pEntity ? pEntity->entindex() : -1; + pEntInfo->hEnt = pEntity; + pEntInfo->flags = 0; + pEntInfo->location = 0; + pEntInfo->size = 0; + pEntInfo->classname = NULL_STRING; + + iSlot++; +} + + +//--------------------------------- + +bool CEntitySaveRestoreBlockHandler::SaveInitEntities( CSaveRestoreData *pSaveData ) +{ + int number_of_entities; + +#if !defined( CLIENT_DLL ) + number_of_entities = gEntList.NumberOfEntities(); +#else + number_of_entities = ClientEntityList().NumberOfEntities( true ); +#endif + entitytable_t *pEntityTable = ( entitytable_t *)engine->SaveAllocMemory( (sizeof(entitytable_t) * number_of_entities), sizeof(char) ); + pSaveData->InitEntityTable( pEntityTable, number_of_entities ); + + // build the table of entities + // this is used to turn pointers into savable indices + // build up ID numbers for each entity, for use in pointer conversions + // if an entity requires a certain edict number upon restore, save that as well + CBaseEntity *pEnt = NULL; + int i = 0; + +#if !defined( CLIENT_DLL ) + while ( (pEnt = gEntList.NextEnt( pEnt )) != NULL ) + { +#else + int last = ClientEntityList().GetHighestEntityIndex(); + + for ( int e = 0; e <= last; e++ ) + { + pEnt = ClientEntityList().GetBaseEntity( e ); + if( !pEnt ) + continue; +#endif + SaveEntityOnTable( pEnt, pSaveData, i ); + } + +#if defined( CLIENT_DLL ) + ClientEntityHandle_t iter = ClientEntityList().FirstHandle(); + + while ( iter != ClientEntityList().InvalidHandle() ) + { + pEnt = ClientEntityList().GetBaseEntityFromHandle( iter ); + + if ( pEnt && pEnt->ObjectCaps() & FCAP_SAVE_NON_NETWORKABLE ) + { + SaveEntityOnTable( pEnt, pSaveData, i ); + } + + iter = ClientEntityList().NextHandle( iter ); + } +#endif + + Assert( i == pSaveData->NumEntities() ); + return ( i == pSaveData->NumEntities() ); +} + +//--------------------------------- + +#if !defined( CLIENT_DLL ) + +// Find the matching global entity. Spit out an error if the designer made entities of +// different classes with the same global name +CBaseEntity *CEntitySaveRestoreBlockHandler::FindGlobalEntity( string_t classname, string_t globalname ) +{ + CBaseEntity *pReturn = NULL; + + while ( (pReturn = gEntList.NextEnt( pReturn )) != NULL ) + { + if ( FStrEq( STRING(pReturn->m_iGlobalname), STRING(globalname)) ) + break; + } + + if ( pReturn ) + { + if ( !FClassnameIs( pReturn, STRING(classname) ) ) + { + Warning( "Global entity found %s, wrong class %s [expects class %s]\n", STRING(globalname), STRING(pReturn->m_iClassname), STRING(classname) ); + pReturn = NULL; + } + } + + return pReturn; +} + +#endif // !defined( CLIENT_DLL ) + +//--------------------------------- + +bool CEntitySaveRestoreBlockHandler::DoRestoreEntity( CBaseEntity *pEntity, IRestore *pRestore ) +{ + MDLCACHE_CRITICAL_SECTION(); + + EHANDLE hEntity; + + hEntity = pEntity; + + pRestore->GetGameSaveRestoreInfo()->SetCurrentEntityContext( pEntity ); + pEntity->Restore( *pRestore ); + pRestore->GetGameSaveRestoreInfo()->SetCurrentEntityContext( NULL ); + +#if !defined( CLIENT_DLL ) + if ( pEntity->ObjectCaps() & FCAP_MUST_SPAWN ) + { + pEntity->Spawn(); + } + else + { + pEntity->Precache( ); + } +#endif + + // Above calls may have resulted in self destruction + return ( hEntity != NULL ); +} + +//--------------------------------- +// Get a reference position in model space to compute +// changes in model space for global brush entities (designer models them in different coords!) +Vector CEntitySaveRestoreBlockHandler::ModelSpaceLandmark( int modelIndex ) +{ + const model_t *pModel = modelinfo->GetModel( modelIndex ); + if ( modelinfo->GetModelType( pModel ) != mod_brush ) + return vec3_origin; + + Vector mins, maxs; + modelinfo->GetModelBounds( pModel, mins, maxs ); + return mins; +} + + +int CEntitySaveRestoreBlockHandler::RestoreEntity( CBaseEntity *pEntity, IRestore *pRestore, entitytable_t *pEntInfo ) +{ + if ( !DoRestoreEntity( pEntity, pRestore ) ) + return 0; + +#if !defined( CLIENT_DLL ) + if ( pEntity->m_iGlobalname != NULL_STRING ) + { + int globalIndex = GlobalEntity_GetIndex( pEntity->m_iGlobalname ); + if ( globalIndex >= 0 ) + { + // Already dead? delete + if ( GlobalEntity_GetState( globalIndex ) == GLOBAL_DEAD ) + return -1; + else if ( !FStrEq( STRING(gpGlobals->mapname), GlobalEntity_GetMap(globalIndex) ) ) + { + pEntity->MakeDormant(); // Hasn't been moved to this level yet, wait but stay alive + } + // In this level & not dead, continue on as normal + } + else + { + Warning( "Global Entity %s (%s) not in table!!!\n", STRING(pEntity->m_iGlobalname), STRING(pEntity->m_iClassname) ); + // Spawned entities default to 'On' + GlobalEntity_Add( pEntity->m_iGlobalname, gpGlobals->mapname, GLOBAL_ON ); + } + } +#endif + + return 0; +} + +//--------------------------------- + +#if !defined( CLIENT_DLL ) + +int CEntitySaveRestoreBlockHandler::RestoreGlobalEntity( CBaseEntity *pEntity, CSaveRestoreData *pSaveData, entitytable_t *pEntInfo ) +{ + Vector oldOffset; + EHANDLE hEntitySafeHandle; + hEntitySafeHandle = pEntity; + + oldOffset.Init(); + CRestore restoreHelper( pSaveData ); + + string_t globalName = pEntInfo->globalname, className = pEntInfo->classname; + + // ------------------- + + int globalIndex = GlobalEntity_GetIndex( globalName ); + + // Don't overlay any instance of the global that isn't the latest + // pSaveData->szCurrentMapName is the level this entity is coming from + // pGlobal->levelName is the last level the global entity was active in. + // If they aren't the same, then this global update is out of date. + if ( !FStrEq( pSaveData->levelInfo.szCurrentMapName, GlobalEntity_GetMap(globalIndex) ) ) + { + return 0; + } + + // Compute the new global offset + CBaseEntity *pNewEntity = FindGlobalEntity( className, globalName ); + if ( pNewEntity ) + { +// Msg( "Overlay %s with %s\n", pNewEntity->GetClassname(), STRING(tmpEnt->classname) ); + // Tell the restore code we're overlaying a global entity from another level + restoreHelper.SetGlobalMode( 1 ); // Don't overwrite global fields + + pSaveData->modelSpaceOffset = pEntInfo->landmarkModelSpace - ModelSpaceLandmark( pNewEntity->GetModelIndex() ); + + UTIL_Remove( pEntity ); + pEntity = pNewEntity;// we're going to restore this data OVER the old entity + pEntInfo->hEnt = pEntity; + // HACKHACK: Do we need system-wide support for removing non-global spawn allocated resources? + pEntity->VPhysicsDestroyObject(); + Assert( pEntInfo->edictindex == -1 ); + // Update the global table to say that the global definition of this entity should come from this level + GlobalEntity_SetMap( globalIndex, gpGlobals->mapname ); + } + else + { + // This entity will be freed automatically by the engine-> If we don't do a restore on a matching entity (below) + // or call EntityUpdate() to move it to this level, we haven't changed global state at all. + DevMsg( "Warning: No match for global entity %s found in destination level\n", STRING(globalName) ); + return 0; + } + + if ( !DoRestoreEntity( pEntity, &restoreHelper ) ) + { + pEntity = NULL; + } + + // Is this an overriding global entity (coming over the transition) + pSaveData->modelSpaceOffset.Init(); + if ( pEntity ) + return 1; + return 0; +} + +#endif // !defined( CLIENT_DLL ) + + + +//----------------------------------------------------------------------------- + +CSaveRestoreData *SaveInit( int size ) +{ + CSaveRestoreData *pSaveData; + +#ifdef DISABLE_DEBUG_HISTORY + if ( size <= 0 ) + size = 2*1024*1024; // Reserve 2048K for now, UNDONE: Shrink this after compressing strings +#else + if ( size <= 0 ) + size = 3*1024*1024; // Reserve 3096K for now, UNDONE: Shrink this after compressing strings +#endif + + int numentities; + +#if !defined( CLIENT_DLL ) + numentities = gEntList.NumberOfEntities(); +#else + numentities = ClientEntityList().NumberOfEntities(); +#endif + + pSaveData = MakeSaveRestoreData(engine->SaveAllocMemory( sizeof(CSaveRestoreData) + (sizeof(entitytable_t) * numentities) + size, sizeof(char) )); + pSaveData->Init( (char *)(pSaveData + 1), size ); // skip the save structure + + const int nTokens = 0xfff; // Assume a maximum of 4K-1 symbol table entries(each of some length) + pSaveData->InitSymbolTable( (char **)engine->SaveAllocMemory( nTokens, sizeof( char * ) ), nTokens ); + + //--------------------------------- + + pSaveData->levelInfo.time = gpGlobals->curtime; // Use DLL time + pSaveData->levelInfo.vecLandmarkOffset = vec3_origin; + pSaveData->levelInfo.fUseLandmark = false; + pSaveData->levelInfo.connectionCount = 0; + + //--------------------------------- + + gpGlobals->pSaveData = pSaveData; + + return pSaveData; +} + + + +//----------------------------------------------------------------------------- +// +// ISaveRestoreBlockSet +// +// Purpose: Serves as holder for a group of sibling save sections. Takes +// care of iterating over them, making sure read points are +// queued up to the right spot (in case one section due to datadesc +// changes reads less than expected, or doesn't leave the +// read pointer at the right point), and ensuring the read pointer +// is at the end of the entire set when the set read is done. +//----------------------------------------------------------------------------- + +struct SaveRestoreBlockHeader_t +{ + char szName[MAX_BLOCK_NAME_LEN + 1]; + int locHeader; + int locBody; + + DECLARE_SIMPLE_DATADESC(); +}; + + +//------------------------------------- + +class CSaveRestoreBlockSet : public ISaveRestoreBlockSet +{ +public: + CSaveRestoreBlockSet( const char *pszName ) + { + Q_strncpy( m_Name, pszName, sizeof(m_Name) ); + } + + const char *GetBlockName() + { + return m_Name; + } + + //--------------------------------- + + void PreSave( CSaveRestoreData *pData ) + { + m_BlockHeaders.SetCount( m_Handlers.Count() ); + for ( int i = 0; i < m_Handlers.Count(); i++ ) + { + Q_strncpy( m_BlockHeaders[i].szName, m_Handlers[i]->GetBlockName(), MAX_BLOCK_NAME_LEN + 1 ); + m_Handlers[i]->PreSave( pData ); + } + } + + void Save( ISave *pSave ) + { + int base = pSave->GetWritePos(); + for ( int i = 0; i < m_Handlers.Count(); i++ ) + { + m_BlockHeaders[i].locBody = pSave->GetWritePos() - base; + m_Handlers[i]->Save( pSave ); + } + m_SizeBodies = pSave->GetWritePos() - base; + } + + void WriteSaveHeaders( ISave *pSave ) + { + int base = pSave->GetWritePos(); + + // + // Reserve space for a fully populated header + // + int dummyInt = -1; + CUtlVector dummyArr; + + dummyArr.SetCount( m_BlockHeaders.Count() ); + memset( &dummyArr[0], 0xff, dummyArr.Count() * sizeof(SaveRestoreBlockHeader_t) ); + + pSave->WriteInt( &dummyInt ); // size all headers + pSave->WriteInt( &dummyInt ); // size all bodies + SaveUtlVector( pSave, &dummyArr, FIELD_EMBEDDED ); + + // + // Write the data + // + for ( int i = 0; i < m_Handlers.Count(); i++ ) + { + m_BlockHeaders[i].locHeader = pSave->GetWritePos() - base; + m_Handlers[i]->WriteSaveHeaders( pSave ); + } + + m_SizeHeaders = pSave->GetWritePos() - base; + + // + // Write the actual header + // + int savedPos = pSave->GetWritePos(); + pSave->SetWritePos(base); + + pSave->WriteInt( &m_SizeHeaders ); + pSave->WriteInt( &m_SizeBodies ); + SaveUtlVector( pSave, &m_BlockHeaders, FIELD_EMBEDDED ); + + pSave->SetWritePos(savedPos); + } + + void PostSave() + { + for ( int i = 0; i < m_Handlers.Count(); i++ ) + { + m_Handlers[i]->PostSave(); + } + m_BlockHeaders.Purge(); + } + + //--------------------------------- + + void PreRestore() + { + for ( int i = 0; i < m_Handlers.Count(); i++ ) + { + m_Handlers[i]->PreRestore(); + } + } + + void ReadRestoreHeaders( IRestore *pRestore ) + { + int base = pRestore->GetReadPos(); + + pRestore->ReadInt( &m_SizeHeaders ); + pRestore->ReadInt( &m_SizeBodies ); + RestoreUtlVector( pRestore, &m_BlockHeaders, FIELD_EMBEDDED ); + + for ( int i = 0; i < m_Handlers.Count(); i++ ) + { + int location = GetBlockHeaderLoc( m_Handlers[i]->GetBlockName() ); + if ( location != -1 ) + { + pRestore->SetReadPos( base + location ); + m_Handlers[i]->ReadRestoreHeaders( pRestore ); + } + } + + pRestore->SetReadPos( base + m_SizeHeaders ); + } + + void CallBlockHandlerRestore( ISaveRestoreBlockHandler *pHandler, int baseFilePos, IRestore *pRestore, bool fCreatePlayers ) + { + int location = GetBlockBodyLoc( pHandler->GetBlockName() ); + if ( location != -1 ) + { + pRestore->SetReadPos( baseFilePos + location ); + pHandler->Restore( pRestore, fCreatePlayers ); + } + } + + void Restore( IRestore *pRestore, bool fCreatePlayers ) + { + int base = pRestore->GetReadPos(); + + for ( int i = 0; i < m_Handlers.Count(); i++ ) + { + CallBlockHandlerRestore( m_Handlers[i], base, pRestore, fCreatePlayers ); + } + pRestore->SetReadPos( base + m_SizeBodies ); + } + + void PostRestore() + { + for ( int i = 0; i < m_Handlers.Count(); i++ ) + { + m_Handlers[i]->PostRestore(); + } + m_BlockHeaders.Purge(); + } + + //--------------------------------- + + void AddBlockHandler( ISaveRestoreBlockHandler *pHandler ) + { + // Grody, but... while this class is still isolated in saverestore.cpp, this seems like a fine time to assert: + AssertMsg( pHandler == &g_EntitySaveRestoreBlockHandler || (m_Handlers.Count() >= 1 && m_Handlers[0] == &g_EntitySaveRestoreBlockHandler), "Expected entity save load to always be first" ); + + Assert( pHandler != this ); + m_Handlers.AddToTail( pHandler ); + } + + void RemoveBlockHandler( ISaveRestoreBlockHandler *pHandler ) + { + m_Handlers.FindAndRemove( pHandler ); + } + + //--------------------------------- + +private: + int GetBlockBodyLoc( const char *pszName ) + { + for ( int i = 0; i < m_BlockHeaders.Count(); i++ ) + { + if ( strcmp( m_BlockHeaders[i].szName, pszName ) == 0 ) + return m_BlockHeaders[i].locBody; + } + return -1; + } + + int GetBlockHeaderLoc( const char *pszName ) + { + for ( int i = 0; i < m_BlockHeaders.Count(); i++ ) + { + if ( strcmp( m_BlockHeaders[i].szName, pszName ) == 0 ) + return m_BlockHeaders[i].locHeader; + } + return -1; + } + + char m_Name[MAX_BLOCK_NAME_LEN + 1]; + CUtlVector m_Handlers; + + int m_SizeHeaders; + int m_SizeBodies; + CUtlVector m_BlockHeaders; +}; + +//------------------------------------- + +BEGIN_SIMPLE_DATADESC( SaveRestoreBlockHeader_t ) + DEFINE_ARRAY(szName, FIELD_CHARACTER, MAX_BLOCK_NAME_LEN + 1), + DEFINE_FIELD(locHeader, FIELD_INTEGER), + DEFINE_FIELD(locBody, FIELD_INTEGER), +END_DATADESC() + +//------------------------------------- + +CSaveRestoreBlockSet g_SaveRestoreBlockSet("Game"); +ISaveRestoreBlockSet *g_pGameSaveRestoreBlockSet = &g_SaveRestoreBlockSet; + +//============================================================================= +#if !defined( CLIENT_DLL ) + +//------------------------------------------------------------------------------ +// Creates all entities that lie in the transition list +//------------------------------------------------------------------------------ +void CreateEntitiesInTransitionList( CSaveRestoreData *pSaveData, int levelMask ) +{ + CBaseEntity *pent; + int i; + for ( i = 0; i < pSaveData->NumEntities(); i++ ) + { + entitytable_t *pEntInfo = pSaveData->GetEntityInfo( i ); + pEntInfo->hEnt = NULL; + + if ( pEntInfo->size == 0 || pEntInfo->edictindex == 0 ) + continue; + + if ( pEntInfo->classname == NULL_STRING ) + { + Warning( "Entity with data saved, but with no classname\n" ); + Assert(0); + continue; + } + + bool active = (pEntInfo->flags & levelMask) ? 1 : 0; + + // spawn players + pent = NULL; + if ( (pEntInfo->edictindex > 0) && (pEntInfo->edictindex <= gpGlobals->maxClients) ) + { + edict_t *ed = INDEXENT( pEntInfo->edictindex ); + + if ( active && ed && !ed->IsFree() ) + { + if ( !(pEntInfo->flags & FENTTABLE_PLAYER) ) + { + Warning( "ENTITY IS NOT A PLAYER: %d\n" , i ); + Assert(0); + } + + pent = CBasePlayer::CreatePlayer( STRING(pEntInfo->classname), ed ); + } + } + else if ( active ) + { + pent = CreateEntityByName( STRING(pEntInfo->classname) ); + } + + pEntInfo->hEnt = pent; + } +} + + +//----------------------------------------------------------------------------- +int CreateEntityTransitionList( CSaveRestoreData *pSaveData, int levelMask ) +{ + CBaseEntity *pent; + entitytable_t *pEntInfo; + + // Create entity list + CreateEntitiesInTransitionList( pSaveData, levelMask ); + + // Now spawn entities + CUtlVector checkList; + + int i; + int movedCount = 0; + for ( i = 0; i < pSaveData->NumEntities(); i++ ) + { + pEntInfo = pSaveData->GetEntityInfo( i ); + pent = pEntInfo->hEnt; +// pSaveData->currentIndex = i; + pSaveData->Seek( pEntInfo->location ); + + // clear this out - it must be set on a per-entity basis + pSaveData->modelSpaceOffset.Init(); + + if ( pent && (pEntInfo->flags & levelMask) ) // Screen out the player if he's not to be spawned + { + if ( pEntInfo->flags & FENTTABLE_GLOBAL ) + { + DevMsg( 2, "Merging changes for global: %s\n", STRING(pEntInfo->classname) ); + + // ------------------------------------------------------------------------- + // Pass the "global" flag to the DLL to indicate this entity should only override + // a matching entity, not be spawned + if ( g_EntitySaveRestoreBlockHandler.RestoreGlobalEntity( pent, pSaveData, pEntInfo ) > 0 ) + { + movedCount++; + pEntInfo->restoreentityindex = pEntInfo->hEnt.Get()->entindex(); + AddRestoredEntity( pEntInfo->hEnt.Get() ); + } + else + { + UTIL_RemoveImmediate( pEntInfo->hEnt.Get() ); + } + // ------------------------------------------------------------------------- + } + else + { + DevMsg( 2, "Transferring %s (%d)\n", STRING(pEntInfo->classname), pent->edict() ? ENTINDEX(pent->edict()) : -1 ); + CRestore restoreHelper( pSaveData ); + if ( g_EntitySaveRestoreBlockHandler.RestoreEntity( pent, &restoreHelper, pEntInfo ) < 0 ) + { + UTIL_RemoveImmediate( pent ); + } + else + { + // needs to be checked. Do this in a separate pass so that pointers & hierarchy can be traversed + checkList.AddToTail(i); + } + } + + // Remove any entities that were removed using UTIL_Remove() as a result of the above calls to UTIL_RemoveImmediate() + gEntList.CleanupDeleteList(); + } + } + + for ( i = checkList.Count()-1; i >= 0; --i ) + { + pEntInfo = pSaveData->GetEntityInfo( checkList[i] ); + pent = pEntInfo->hEnt; + + // NOTE: pent can be NULL because UTIL_RemoveImmediate (called below) removes all in hierarchy + if ( !pent ) + continue; + + MDLCACHE_CRITICAL_SECTION(); + + if ( !(pEntInfo->flags & FENTTABLE_PLAYER) && UTIL_EntityInSolid( pent ) ) + { + // this can happen during normal processing - PVS is just a guess, some map areas won't exist in the new map + DevMsg( 2, "Suppressing %s\n", STRING(pEntInfo->classname) ); + UTIL_RemoveImmediate( pent ); + // Remove any entities that were removed using UTIL_Remove() as a result of the above calls to UTIL_RemoveImmediate() + gEntList.CleanupDeleteList(); + } + else + { + movedCount++; + pEntInfo->flags = FENTTABLE_REMOVED; + pEntInfo->restoreentityindex = pent->entindex(); + AddRestoredEntity( pent ); + } + } + + return movedCount; +} +#endif diff --git a/game_shared/sceneentity_shared.cpp b/game_shared/sceneentity_shared.cpp index 3afc85e4..a6b69c7d 100644 --- a/game_shared/sceneentity_shared.cpp +++ b/game_shared/sceneentity_shared.cpp @@ -1,129 +1,129 @@ -//====== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======= -// -// Purpose: -// -//============================================================================= - -#include "cbase.h" -#include "sceneentity_shared.h" - -// memdbgon must be the last include file in a .cpp file!!! -#include "tier0/memdbgon.h" - -static ConVar scene_print( "scene_print", "0", FCVAR_REPLICATED, "When playing back a scene, print timing and event info to console." ); -ConVar scene_clientflex( "scene_clientflex", "1", FCVAR_REPLICATED, "Do client side flex animation." ); - -IFileSystem *SceneFileSystem() -{ - return filesystem; -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *pFormat - -// ... - -// Output : static void -//----------------------------------------------------------------------------- -void Scene_Printf( const char *pFormat, ... ) -{ - int val = scene_print.GetInt(); - if ( !val ) - return; - - if ( val >= 2 ) - { - if ( CBaseEntity::IsServer() && val != 2 ) - { - return; - } - else if ( !CBaseEntity::IsServer() && val != 3 ) - { - return; - } - } - - va_list marker; - char msg[8192]; - - va_start(marker, pFormat); - Q_vsnprintf(msg, sizeof(msg), pFormat, marker); - va_end(marker); - - Msg( "%8.3f[%d] %s: %s", gpGlobals->curtime, gpGlobals->tickcount, CBaseEntity::IsServer() ? "sv" : "cl", msg ); -} - -//----------------------------------------------------------------------------- -// Purpose: -// Output : const char -//----------------------------------------------------------------------------- -const char *CSceneTokenProcessor::CurrentToken( void ) -{ - return m_szToken; -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : crossline - -// Output : Returns true on success, false on failure. -//----------------------------------------------------------------------------- -bool CSceneTokenProcessor::GetToken( bool crossline ) -{ - // NOTE: crossline is ignored here, may need to implement if needed - m_pBuffer = engine->ParseFile( m_pBuffer, m_szToken, sizeof( m_szToken ) ); - if ( strlen( m_szToken ) >= 0 ) - return true; - return false; -} - -//----------------------------------------------------------------------------- -// Purpose: -// Output : Returns true on success, false on failure. -//----------------------------------------------------------------------------- -bool CSceneTokenProcessor::TokenAvailable( void ) -{ - char const *search_p = m_pBuffer; - - while ( *search_p <= 32) - { - if (*search_p == '\n') - return false; - search_p++; - if ( !*search_p ) - return false; - - } - - if (*search_p == ';' || *search_p == '#' || // semicolon and # is comment field - (*search_p == '/' && *((search_p)+1) == '/')) // also make // a comment field - return false; - - return true; -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *fmt - -// ... - -//----------------------------------------------------------------------------- -void CSceneTokenProcessor::Error( const char *fmt, ... ) -{ - char string[ 2048 ]; - va_list argptr; - va_start( argptr, fmt ); - Q_vsnprintf( string, sizeof(string), fmt, argptr ); - va_end( argptr ); - - Warning( "%s", string ); - Assert(0); -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *buffer - -//----------------------------------------------------------------------------- -void CSceneTokenProcessor::SetBuffer( char *buffer ) -{ - m_pBuffer = buffer; -} - -CSceneTokenProcessor g_TokenProcessor; \ No newline at end of file +//====== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======= +// +// Purpose: +// +//============================================================================= + +#include "cbase.h" +#include "sceneentity_shared.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +static ConVar scene_print( "scene_print", "0", FCVAR_REPLICATED, "When playing back a scene, print timing and event info to console." ); +ConVar scene_clientflex( "scene_clientflex", "1", FCVAR_REPLICATED, "Do client side flex animation." ); + +IFileSystem *SceneFileSystem() +{ + return filesystem; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *pFormat - +// ... - +// Output : static void +//----------------------------------------------------------------------------- +void Scene_Printf( const char *pFormat, ... ) +{ + int val = scene_print.GetInt(); + if ( !val ) + return; + + if ( val >= 2 ) + { + if ( CBaseEntity::IsServer() && val != 2 ) + { + return; + } + else if ( !CBaseEntity::IsServer() && val != 3 ) + { + return; + } + } + + va_list marker; + char msg[8192]; + + va_start(marker, pFormat); + Q_vsnprintf(msg, sizeof(msg), pFormat, marker); + va_end(marker); + + Msg( "%8.3f[%d] %s: %s", gpGlobals->curtime, gpGlobals->tickcount, CBaseEntity::IsServer() ? "sv" : "cl", msg ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : const char +//----------------------------------------------------------------------------- +const char *CSceneTokenProcessor::CurrentToken( void ) +{ + return m_szToken; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : crossline - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CSceneTokenProcessor::GetToken( bool crossline ) +{ + // NOTE: crossline is ignored here, may need to implement if needed + m_pBuffer = engine->ParseFile( m_pBuffer, m_szToken, sizeof( m_szToken ) ); + if ( strlen( m_szToken ) >= 0 ) + return true; + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CSceneTokenProcessor::TokenAvailable( void ) +{ + char const *search_p = m_pBuffer; + + while ( *search_p <= 32) + { + if (*search_p == '\n') + return false; + search_p++; + if ( !*search_p ) + return false; + + } + + if (*search_p == ';' || *search_p == '#' || // semicolon and # is comment field + (*search_p == '/' && *((search_p)+1) == '/')) // also make // a comment field + return false; + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *fmt - +// ... - +//----------------------------------------------------------------------------- +void CSceneTokenProcessor::Error( const char *fmt, ... ) +{ + char string[ 2048 ]; + va_list argptr; + va_start( argptr, fmt ); + Q_vsnprintf( string, sizeof(string), fmt, argptr ); + va_end( argptr ); + + Warning( "%s", string ); + Assert(0); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *buffer - +//----------------------------------------------------------------------------- +void CSceneTokenProcessor::SetBuffer( char *buffer ) +{ + m_pBuffer = buffer; +} + +CSceneTokenProcessor g_TokenProcessor; diff --git a/game_shared/sdk/sdk_basegrenade_projectile.cpp b/game_shared/sdk/sdk_basegrenade_projectile.cpp index bf125658..5063e41b 100644 --- a/game_shared/sdk/sdk_basegrenade_projectile.cpp +++ b/game_shared/sdk/sdk_basegrenade_projectile.cpp @@ -120,7 +120,7 @@ END_NETWORK_TABLE() return; } - CSoundEnt::InsertSound ( SOUND_DANGER, GetAbsOrigin() + GetAbsVelocity() * 0.5, GetAbsVelocity().Length( ), 0.2 ); + CSoundEnt::InsertSound ( SOUND_DANGER, GetAbsOrigin() + GetAbsVelocity() * 0.5, static_cast(GetAbsVelocity().Length()), 0.2 ); SetNextThink( gpGlobals->curtime + 0.2 ); diff --git a/game_shared/sdk/sdk_gamerules.cpp b/game_shared/sdk/sdk_gamerules.cpp index 108c1ae2..0a65857d 100644 --- a/game_shared/sdk/sdk_gamerules.cpp +++ b/game_shared/sdk/sdk_gamerules.cpp @@ -154,7 +154,7 @@ IMPLEMENT_NETWORKCLASS_ALIASED( SDKGameRulesProxy, DT_SDKGameRulesProxy ) CSDKGameRules::CSDKGameRules() { // Create the team managers - for ( int i = 0; i < ARRAYSIZE( sTeamNames ); i++ ) + for ( int i = 0; i < static_cast(ARRAYSIZE( sTeamNames )); i++ ) { CTeam *pTeam = static_cast(CreateEntityByName( "sdk_team_manager" )); pTeam->Init( sTeamNames[i], i ); diff --git a/game_shared/sdk/sdk_playeranimstate.cpp b/game_shared/sdk/sdk_playeranimstate.cpp index 41faa598..09df257f 100644 --- a/game_shared/sdk/sdk_playeranimstate.cpp +++ b/game_shared/sdk/sdk_playeranimstate.cpp @@ -301,7 +301,7 @@ bool CSDKPlayerAnimState::IsOuterGrenadePrimed() } else { - return NULL; + return false; } } diff --git a/game_shared/sdk/weapon_sdkbase.cpp b/game_shared/sdk/weapon_sdkbase.cpp index d2434bbe..c0cf0283 100644 --- a/game_shared/sdk/weapon_sdkbase.cpp +++ b/game_shared/sdk/weapon_sdkbase.cpp @@ -1,163 +1,163 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -//=============================================================================// - -#include "cbase.h" -#include "in_buttons.h" -#include "takedamageinfo.h" -#include "weapon_sdkbase.h" -#include "ammodef.h" - - -#if defined( CLIENT_DLL ) - - #include "c_sdk_player.h" - -#else - - #include "sdk_player.h" - -#endif - - -// ----------------------------------------------------------------------------- // -// Global functions. -// ----------------------------------------------------------------------------- // - -//-------------------------------------------------------------------------------------------------------- -static const char * s_WeaponAliasInfo[] = -{ - "none", // WEAPON_NONE - "mp5", // WEAPON_MP5 - "shotgun", // WEAPON_SHOTGUN - "grenade", // WEAPON_GRENADE - NULL, // WEAPON_NONE -}; - -//-------------------------------------------------------------------------------------------------------- -// -// Given an alias, return the associated weapon ID -// -int AliasToWeaponID( const char *alias ) -{ - if (alias) - { - for( int i=0; s_WeaponAliasInfo[i] != NULL; ++i ) - if (!Q_stricmp( s_WeaponAliasInfo[i], alias )) - return i; - } - - return WEAPON_NONE; -} - -//-------------------------------------------------------------------------------------------------------- -// -// Given a weapon ID, return its alias -// -const char *WeaponIDToAlias( int id ) -{ - if ( (id >= WEAPON_MAX) || (id < 0) ) - return NULL; - - return s_WeaponAliasInfo[id]; -} - -// ----------------------------------------------------------------------------- // -// CWeaponSDKBase tables. -// ----------------------------------------------------------------------------- // - -IMPLEMENT_NETWORKCLASS_ALIASED( WeaponSDKBase, DT_WeaponSDKBase ) - -BEGIN_NETWORK_TABLE( CWeaponSDKBase, DT_WeaponSDKBase ) -#ifdef CLIENT_DLL - -#else - // world weapon models have no animations - SendPropExclude( "DT_AnimTimeMustBeFirst", "m_flAnimTime" ), - SendPropExclude( "DT_BaseAnimating", "m_nSequence" ), -#endif -END_NETWORK_TABLE() - -#ifdef CLIENT_DLL -BEGIN_PREDICTION_DATA( CWeaponSDKBase ) - DEFINE_PRED_FIELD( m_flTimeWeaponIdle, FIELD_FLOAT, FTYPEDESC_OVERRIDE | FTYPEDESC_NOERRORCHECK ), -END_PREDICTION_DATA() -#endif - -LINK_ENTITY_TO_CLASS( weapon_sdk_base, CWeaponSDKBase ); - - -#ifdef GAME_DLL - - BEGIN_DATADESC( CWeaponSDKBase ) - - // New weapon Think and Touch Functions go here.. - - END_DATADESC() - -#endif - -// ----------------------------------------------------------------------------- // -// CWeaponCSBase implementation. -// ----------------------------------------------------------------------------- // -CWeaponSDKBase::CWeaponSDKBase() -{ - SetPredictionEligible( true ); - - AddSolidFlags( FSOLID_TRIGGER ); // Nothing collides with these but it gets touches. -} - -const CSDKWeaponInfo &CWeaponSDKBase::GetSDKWpnData() const -{ - const FileWeaponInfo_t *pWeaponInfo = &GetWpnData(); - const CSDKWeaponInfo *pSDKInfo; - - #ifdef _DEBUG - pSDKInfo = dynamic_cast< const CSDKWeaponInfo* >( pWeaponInfo ); - Assert( pSDKInfo ); - #else - pSDKInfo = static_cast< const CSDKWeaponInfo* >( pWeaponInfo ); - #endif - - return *pSDKInfo; -} - -bool CWeaponSDKBase::PlayEmptySound() -{ - CPASAttenuationFilter filter( this ); - filter.UsePredictionRules(); - - EmitSound( filter, entindex(), "Default.ClipEmpty_Rifle" ); - - return 0; -} - -CSDKPlayer* CWeaponSDKBase::GetPlayerOwner() const -{ - return dynamic_cast< CSDKPlayer* >( GetOwner() ); -} - -#ifdef GAME_DLL - -void CWeaponSDKBase::SendReloadEvents() -{ - CSDKPlayer *pPlayer = dynamic_cast< CSDKPlayer* >( GetOwner() ); - if ( !pPlayer ) - return; - - // Send a message to any clients that have this entity to play the reload. - CPASFilter filter( pPlayer->GetAbsOrigin() ); - filter.RemoveRecipient( pPlayer ); - - UserMessageBegin( filter, "ReloadEffect" ); - WRITE_SHORT( pPlayer->entindex() ); - MessageEnd(); - - // Make the player play his reload animation. - pPlayer->DoAnimationEvent( PLAYERANIMEVENT_RELOAD ); -} - -#endif \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include "cbase.h" +#include "in_buttons.h" +#include "takedamageinfo.h" +#include "weapon_sdkbase.h" +#include "ammodef.h" + + +#if defined( CLIENT_DLL ) + + #include "c_sdk_player.h" + +#else + + #include "sdk_player.h" + +#endif + + +// ----------------------------------------------------------------------------- // +// Global functions. +// ----------------------------------------------------------------------------- // + +//-------------------------------------------------------------------------------------------------------- +static const char * s_WeaponAliasInfo[] = +{ + "none", // WEAPON_NONE + "mp5", // WEAPON_MP5 + "shotgun", // WEAPON_SHOTGUN + "grenade", // WEAPON_GRENADE + NULL, // WEAPON_NONE +}; + +//-------------------------------------------------------------------------------------------------------- +// +// Given an alias, return the associated weapon ID +// +int AliasToWeaponID( const char *alias ) +{ + if (alias) + { + for( int i=0; s_WeaponAliasInfo[i] != NULL; ++i ) + if (!Q_stricmp( s_WeaponAliasInfo[i], alias )) + return i; + } + + return WEAPON_NONE; +} + +//-------------------------------------------------------------------------------------------------------- +// +// Given a weapon ID, return its alias +// +const char *WeaponIDToAlias( int id ) +{ + if ( (id >= WEAPON_MAX) || (id < 0) ) + return NULL; + + return s_WeaponAliasInfo[id]; +} + +// ----------------------------------------------------------------------------- // +// CWeaponSDKBase tables. +// ----------------------------------------------------------------------------- // + +IMPLEMENT_NETWORKCLASS_ALIASED( WeaponSDKBase, DT_WeaponSDKBase ) + +BEGIN_NETWORK_TABLE( CWeaponSDKBase, DT_WeaponSDKBase ) +#ifdef CLIENT_DLL + +#else + // world weapon models have no animations + SendPropExclude( "DT_AnimTimeMustBeFirst", "m_flAnimTime" ), + SendPropExclude( "DT_BaseAnimating", "m_nSequence" ), +#endif +END_NETWORK_TABLE() + +#ifdef CLIENT_DLL +BEGIN_PREDICTION_DATA( CWeaponSDKBase ) + DEFINE_PRED_FIELD( m_flTimeWeaponIdle, FIELD_FLOAT, FTYPEDESC_OVERRIDE | FTYPEDESC_NOERRORCHECK ), +END_PREDICTION_DATA() +#endif + +LINK_ENTITY_TO_CLASS( weapon_sdk_base, CWeaponSDKBase ); + + +#ifdef GAME_DLL + + BEGIN_DATADESC( CWeaponSDKBase ) + + // New weapon Think and Touch Functions go here.. + + END_DATADESC() + +#endif + +// ----------------------------------------------------------------------------- // +// CWeaponCSBase implementation. +// ----------------------------------------------------------------------------- // +CWeaponSDKBase::CWeaponSDKBase() +{ + SetPredictionEligible( true ); + + AddSolidFlags( FSOLID_TRIGGER ); // Nothing collides with these but it gets touches. +} + +const CSDKWeaponInfo &CWeaponSDKBase::GetSDKWpnData() const +{ + const FileWeaponInfo_t *pWeaponInfo = &GetWpnData(); + const CSDKWeaponInfo *pSDKInfo; + + #ifdef _DEBUG + pSDKInfo = dynamic_cast< const CSDKWeaponInfo* >( pWeaponInfo ); + Assert( pSDKInfo ); + #else + pSDKInfo = static_cast< const CSDKWeaponInfo* >( pWeaponInfo ); + #endif + + return *pSDKInfo; +} + +bool CWeaponSDKBase::PlayEmptySound() +{ + CPASAttenuationFilter filter( this ); + filter.UsePredictionRules(); + + EmitSound( filter, entindex(), "Default.ClipEmpty_Rifle" ); + + return 0; +} + +CSDKPlayer* CWeaponSDKBase::GetPlayerOwner() const +{ + return dynamic_cast< CSDKPlayer* >( GetOwner() ); +} + +#ifdef GAME_DLL + +void CWeaponSDKBase::SendReloadEvents() +{ + CSDKPlayer *pPlayer = dynamic_cast< CSDKPlayer* >( GetOwner() ); + if ( !pPlayer ) + return; + + // Send a message to any clients that have this entity to play the reload. + CPASFilter filter( pPlayer->GetAbsOrigin() ); + filter.RemoveRecipient( pPlayer ); + + UserMessageBegin( filter, "ReloadEffect" ); + WRITE_SHORT( pPlayer->entindex() ); + MessageEnd(); + + // Make the player play his reload animation. + pPlayer->DoAnimationEvent( PLAYERANIMEVENT_RELOAD ); +} + +#endif diff --git a/game_shared/sheetsimulator.cpp b/game_shared/sheetsimulator.cpp index 1f986be8..79993f3b 100644 --- a/game_shared/sheetsimulator.cpp +++ b/game_shared/sheetsimulator.cpp @@ -398,7 +398,7 @@ void CSheetSimulator::InitPosition( int i ) // collision plane Vector delta; VectorSubtract( m_ControlPoints[i], m_Origin, delta ); - int maxdist = VectorNormalize( delta ); + int maxdist = static_cast(VectorNormalize( delta )); float dist = (m_pCollisionPlanes[i].dist - DotProduct( m_Origin, m_pCollisionPlanes[i].normal )) / DotProduct( delta, m_pCollisionPlanes[i].normal ); @@ -635,7 +635,7 @@ bool CIterativeSheetSimulator::Think( ) else { // Simulate it a bunch of times - Simulate(m_TimeStep, m_SubSteps); + Simulate(m_TimeStep, static_cast(m_SubSteps)); // Reset the collision point for collision detect m_CurrentCollisionPt = 0; diff --git a/game_shared/soundenvelope.cpp b/game_shared/soundenvelope.cpp index 6833ac89..2c3728eb 100644 --- a/game_shared/soundenvelope.cpp +++ b/game_shared/soundenvelope.cpp @@ -1041,7 +1041,7 @@ CSoundPatch *CSoundControllerImp::SoundCreate( IRecipientFilter& filter, int nEn CSoundPatch *pSound = new CSoundPatch; // FIXME: This is done so we don't have to futz with the public interface - EHANDLE hEnt = (nEntIndex != -1) ? g_pEntityList->GetNetworkableHandle( nEntIndex ) : NULL; + EHANDLE hEnt = (nEntIndex != -1) ? g_pEntityList->GetNetworkableHandle( nEntIndex ) : 0; pSound->Init( &filter, hEnt.Get(), CHAN_AUTO, pSoundName, SNDLVL_NORM ); return pSound; @@ -1051,7 +1051,7 @@ CSoundPatch *CSoundControllerImp::SoundCreate( IRecipientFilter& filter, int nEn const char *pSoundName, float attenuation ) { CSoundPatch *pSound = new CSoundPatch; - EHANDLE hEnt = (nEntIndex != -1) ? g_pEntityList->GetNetworkableHandle( nEntIndex ) : NULL; + EHANDLE hEnt = (nEntIndex != -1) ? g_pEntityList->GetNetworkableHandle( nEntIndex ) : 0; pSound->Init( &filter, hEnt.Get(), channel, pSoundName, ATTN_TO_SNDLVL( attenuation ) ); return pSound; @@ -1061,7 +1061,7 @@ CSoundPatch *CSoundControllerImp::SoundCreate( IRecipientFilter& filter, int nEn const char *pSoundName, soundlevel_t soundlevel ) { CSoundPatch *pSound = new CSoundPatch; - EHANDLE hEnt = (nEntIndex != -1) ? g_pEntityList->GetNetworkableHandle( nEntIndex ) : NULL; + EHANDLE hEnt = (nEntIndex != -1) ? g_pEntityList->GetNetworkableHandle( nEntIndex ) : 0; pSound->Init( &filter, hEnt.Get(), channel, pSoundName, soundlevel ); return pSound; @@ -1072,7 +1072,7 @@ CSoundPatch *CSoundControllerImp::SoundCreate( IRecipientFilter& filter, int nEn CSoundPatch *pSound = new CSoundPatch; // FIXME: This is done so we don't have to futz with the public interface - EHANDLE hEnt = (nEntIndex != -1) ? g_pEntityList->GetNetworkableHandle( nEntIndex ) : NULL; + EHANDLE hEnt = (nEntIndex != -1) ? g_pEntityList->GetNetworkableHandle( nEntIndex ) : 0; pSound->Init( &filter, hEnt.Get(), es.m_nChannel, es.m_pSoundName, es.m_SoundLevel ); pSound->ChangeVolume( es.m_flVolume, 0 ); pSound->ChangePitch( es.m_nPitch, 0 ); diff --git a/game_shared/usercmd.cpp b/game_shared/usercmd.cpp index 6d8bba6d..469769f1 100644 --- a/game_shared/usercmd.cpp +++ b/game_shared/usercmd.cpp @@ -77,7 +77,7 @@ void WriteUsercmd( bf_write *buf, CUserCmd *to, CUserCmd *from ) if ( to->forwardmove != from->forwardmove ) { buf->WriteOneBit( 1 ); - buf->WriteSBitLong( to->forwardmove, 16 ); + buf->WriteSBitLong( static_cast(to->forwardmove), 16 ); } else { @@ -87,7 +87,7 @@ void WriteUsercmd( bf_write *buf, CUserCmd *to, CUserCmd *from ) if ( to->sidemove != from->sidemove ) { buf->WriteOneBit( 1 ); - buf->WriteSBitLong( to->sidemove, 16 ); + buf->WriteSBitLong( static_cast(to->sidemove), 16 ); } else { @@ -97,7 +97,7 @@ void WriteUsercmd( bf_write *buf, CUserCmd *to, CUserCmd *from ) if ( to->upmove != from->upmove ) { buf->WriteOneBit( 1 ); - buf->WriteSBitLong( to->upmove, 16 ); + buf->WriteSBitLong( static_cast(to->upmove), 16 ); } else { diff --git a/game_shared/voice_gamemgr.cpp b/game_shared/voice_gamemgr.cpp index 6122d4cb..c3e132df 100644 --- a/game_shared/voice_gamemgr.cpp +++ b/game_shared/voice_gamemgr.cpp @@ -48,7 +48,7 @@ CVoiceGameMgr g_VoiceGameMgr; // ------------------------------------------------------------------------ // // Find a player with a case-insensitive name search. -static CBasePlayer* FindPlayerByName(const char *pTestName) +/*static CBasePlayer* FindPlayerByName(const char *pTestName) { for(int i=1; i <= gpGlobals->maxClients; i++) { @@ -68,7 +68,7 @@ static CBasePlayer* FindPlayerByName(const char *pTestName) } return NULL; -} +}*/ static void VoiceServerDebug( const char *pFmt, ... ) { @@ -161,8 +161,8 @@ bool CVoiceGameMgr::ClientCommand(CBasePlayer *pPlayer, const char *cmd) { for(int i=1; i < engine->Cmd_Argc(); i++) { - unsigned long mask = 0; - sscanf(engine->Cmd_Argv(i), "%x", &mask); + unsigned int mask = 0; + sscanf(engine->Cmd_Argv(i), "%X", &mask); if(i <= VOICE_MAX_PLAYERS_DW) { diff --git a/game_shared/weapon_parse.cpp b/game_shared/weapon_parse.cpp index eff1c08b..dbfc2c36 100644 --- a/game_shared/weapon_parse.cpp +++ b/game_shared/weapon_parse.cpp @@ -65,7 +65,7 @@ static CUtlDict< FileWeaponInfo_t*, unsigned short > m_WeaponInfoDatabase; #ifdef _DEBUG // used to track whether or not two weapons have been mistakenly assigned the wrong slot -bool g_bUsedWeaponSlots[MAX_WEAPON_SLOTS][MAX_WEAPON_POSITIONS] = { 0 }; +bool g_bUsedWeaponSlots[MAX_WEAPON_SLOTS][MAX_WEAPON_POSITIONS] = { {false} }; #endif @@ -109,7 +109,7 @@ static FileWeaponInfo_t gNullWeaponInfo; //----------------------------------------------------------------------------- FileWeaponInfo_t *GetFileWeaponInfoFromHandle( WEAPON_FILE_INFO_HANDLE handle ) { - if ( handle < 0 || handle >= m_WeaponInfoDatabase.Count() ) + if ( handle >= m_WeaponInfoDatabase.Count() ) { return &gNullWeaponInfo; } @@ -379,7 +379,7 @@ void FileWeaponInfo_t::Parse( KeyValues *pKeyValuesData, const char *szWeaponNam // Weapon scripts should use the flag names. iFlags = pKeyValuesData->GetInt( "item_flags", ITEM_FLAG_LIMITINWORLD ); - for ( int i=0; i < ARRAYSIZE( g_ItemFlags ); i++ ) + for ( size_t i = 0; i < ARRAYSIZE( g_ItemFlags ); i++ ) { int iVal = pKeyValuesData->GetInt( g_ItemFlags[i].m_pFlagName, -1 ); if ( iVal == 0 ) diff --git a/lib-vc7/public/choreoobjects.lib b/lib-vc7/public/choreoobjects.lib index 82af78f4..b44b5474 100644 Binary files a/lib-vc7/public/choreoobjects.lib and b/lib-vc7/public/choreoobjects.lib differ diff --git a/lib-vc7/public/vmpi.lib b/lib-vc7/public/vmpi.lib index 5a1be32e..5a062e62 100644 Binary files a/lib-vc7/public/vmpi.lib and b/lib-vc7/public/vmpi.lib differ diff --git a/lib/public/choreoobjects.lib b/lib/public/choreoobjects.lib index c5c3b7bb..d41a7b2f 100644 Binary files a/lib/public/choreoobjects.lib and b/lib/public/choreoobjects.lib differ diff --git a/lib/public/tier2.lib b/lib/public/tier2.lib index a59d89fb..7d799536 100644 Binary files a/lib/public/tier2.lib and b/lib/public/tier2.lib differ diff --git a/lib/public/vmpi.lib b/lib/public/vmpi.lib index 5a1be32e..d6cc1604 100644 Binary files a/lib/public/vmpi.lib and b/lib/public/vmpi.lib differ diff --git a/linux_sdk/Makefile b/linux_sdk/Makefile index b718f366..12bac042 100644 --- a/linux_sdk/Makefile +++ b/linux_sdk/Makefile @@ -11,40 +11,36 @@ NAME=server # the location of the vcproj that builds the mod -MOD_PROJ=../dlls/server_scratch-2003.vcproj +MOD_PROJ=../dlls/server_scratch-2005.vcproj # the name of the mod configuration (typically _) MOD_CONFIG=server_sdk_ReleaseSDKWin32 # the directory the base binaries (tier0_i486.so, etc) are located -#GAME_DIR=../../ -#GAME_DIR=~/valve/hl2bin/ -GAME_DIR=~/valve/steam +GAME_DIR=~/srcds # compiler options (gcc 3.4.1 or above is required) -CC=/usr/local/bin/gcc -CPLUS=/usr/local/bin/g++ -CLINK=/usr/local/bin/gcc -CPP_LIB="/usr/local/lib/libstdc++.a /usr/local/lib/libgcc_eh.a" +CC=/usr/bin/gcc +CPLUS=/usr/bin/g++ +CLINK=/usr/bin/gcc +CPP_LIB="/usr/lib/libstdc++.a /usr/lib/libgcc_eh.a" # put any compiler flags you want passed here USER_CFLAGS= # link flags for your mod, make sure to include any special libraries here -LDFLAGS="-lm -ldl $(GAME_DIR)/bin/tier0_i486.so $(GAME_DIR)/bin/vstdlib_i486.so mathlib_i486.a choreoobjects_i486.a tier1_i486.a" +LDFLAGS="-lm -ldl mathlib_i486.a choreoobjects_i486.a tier1_i486.a $(GAME_DIR)/bin/tier0_i486.so $(GAME_DIR)/bin/vstdlib_i486.so" # XERCES 2.6.0 or above ( http://xml.apache.org/xerces-c/ ) is used by the vcproj to makefile converter # it must be installed before being able to run this makefile -XERCES_INC_DIR=/home/alfred/tmp/xerces-c-src_2_6_0/include -XERCES_LIB_DIR=/home/alfred/tmp/xerces-c-src_2_6_0/lib -# if you have xerces installed already you should be able to use the two lines below -#XERCES_INC_DIR=/usr/include -#XERCES_LIB_DIR=/usr/lib +XERCES_INC_DIR=/usr/include +XERCES_LIB_DIR=/usr/lib ############################################################################# # Things below here shouldn't need to be altered ############################################################################# MAKE=make +AR="ar rvs" # the dir we want to put binaries we build into BUILD_DIR=. @@ -54,21 +50,25 @@ BUILD_OBJ_DIR=$(BUILD_DIR)/obj # the location of the source code SOURCE_DIR=.. +# Locations of static library projects +TIER1_PROJ=../tier1/tier1-2005.vcproj +MATHLIB_PROJ=../mathlib/mathlib-2005.vcproj +CHOREO_PROJ=../choreoobjects/choreoobjects-2005.vcproj + # the CPU target for the build, must be i486 for now ARCH=i486 ARCH_CFLAGS=-mtune=i686 -march=pentium3 -mmmx -O3 -# -fpermissive is so gcc 3.4.x doesn't complain about some template stuff -BASE_CFLAGS=-fpermissive -D_LINUX -DNDEBUG -Dstricmp=strcasecmp -D_stricmp=strcasecmp -D_strnicmp=strncasecmp -Dstrnicmp=strncasecmp -D_snprintf=snprintf -D_vsnprintf=vsnprintf -D_alloca=alloca -Dstrcmpi=strcasecmp +BASE_CFLAGS=-D_LINUX -DNDEBUG -Dstricmp=strcasecmp -D_stricmp=strcasecmp -D_strnicmp=strncasecmp -Dstrnicmp=strncasecmp -D_vsnprintf=vsnprintf -D_alloca=alloca -Dstrcmpi=strcasecmp -Wall -Wno-non-virtual-dtor -Wno-invalid-offsetof SHLIBEXT=so SHLIBCFLAGS=-fPIC SHLIBLDFLAGS=-shared -Wl,-Map,$@_map.txt -Wl #flags passed to the c compiler -CFLAGS="$(USER_CFLAGS) $(DEFINES) $(ARCH_CFLAGS) $(BASE_CFLAGS) -Usprintf=use_Q_snprintf_instead_of_sprintf -Ustrncpy=use_Q_strncpy_instead -UPROTECTED_THINGS_ENABLE" +CFLAGS="$(USER_CFLAGS) $(DEFINES) $(ARCH_CFLAGS) $(BASE_CFLAGS) -Usprintf -Ustrncpy -UPROTECTED_THINGS_ENABLE" # define list passed to make for the sub makefile -BASE_DEFINES=CC=$(CC) CPLUS=$(CPLUS) CPP_LIB=$(CPP_LIB) \ +BASE_DEFINES=CC=$(CC) AR=$(AR) CPLUS=$(CPLUS) CPP_LIB=$(CPP_LIB) \ BUILD_DIR=$(BUILD_DIR) BUILD_OBJ_DIR=$(BUILD_OBJ_DIR) \ SOURCE_DIR=$(SOURCE_DIR) SHLIBLDFLAGS=$(SHLIBLDFLAGS) SHLIBEXT=$(SHLIBEXT) \ CLINK=$(CLINK) CFLAGS=$(CFLAGS) LDFLAGS=$(LDFLAGS) \ @@ -76,18 +76,23 @@ BASE_DEFINES=CC=$(CC) CPLUS=$(CPLUS) CPP_LIB=$(CPP_LIB) \ XERCES_INC_DIR=$(XERCES_INC_DIR) XERCES_LIB_DIR=$(XERCES_LIB_DIR) # Project Makefile -MAKE_MOD=Makefile.mod +MAKE_MOD=Makefile.game MAKE_VCPM=Makefile.vcpm MAKE_PLUGIN=Makefile.plugin +MAKE_TIER1=Makefile.tier1 +MAKE_MATH=Makefile.mathlib +MAKE_CHOREO=Makefile.choreoobjects all: check vcpm mod check: if [ -z "$(CC)" ]; then echo "Compiler not defined."; exit; fi - if [ ! -d $(BUILD_DIR) ];then mkdir $(BUILD_DIR);fi + if [ ! -d $(BUILD_DIR) ];then mkdir -p $(BUILD_DIR);fi cd $(BUILD_DIR) vcpm: + if [ ! -f "tier0_i486.so" ]; then ln -s $(GAME_DIR)/bin/tier0_i486.so .; fi + if [ ! -f "vstdlib_i486.so" ]; then ln -s $(GAME_DIR)/bin/vstdlib_i486.so .; fi $(MAKE) -f $(MAKE_VCPM) $(BASE_DEFINES) mod: vcpm @@ -97,10 +102,23 @@ mod: vcpm $(MAKE) -f $(MAKE_MOD) $(BASE_DEFINES) plugin: + if [ ! -f "tier0_i486.so" ]; then ln -s $(GAME_DIR)/bin/tier0_i486.so .; fi + if [ ! -f "vstdlib_i486.so" ]; then ln -s $(GAME_DIR)/bin/vstdlib_i486.so .; fi $(MAKE) -f $(MAKE_PLUGIN) $(BASE_DEFINES) + +tier1: + $(MAKE) -f $(MAKE_TIER1) $(BASE_DEFINES) + +mathlib: + $(MAKE) -f $(MAKE_MATH) $(BASE_DEFINES) + +choreoobjects: + $(MAKE) -f $(MAKE_CHOREO) $(BASE_DEFINES) clean: - $(MAKE) -f $(MAKE_VCPM) $(BASE_DEFINES) clean - $(MAKE) -f $(MAKE_PLUGIN) $(BASE_DEFINES) clean - $(MAKE) -f $(MAKE_MOD) $(BASE_DEFINES) clean - + $(MAKE) -f $(MAKE_VCPM) $(BASE_DEFINES) clean + $(MAKE) -f $(MAKE_PLUGIN) $(BASE_DEFINES) clean + $(MAKE) -f $(MAKE_MOD) $(BASE_DEFINES) clean + $(MAKE) -f $(MAKE_TIER1) $(BASE_DEFINES) clean + $(MAKE) -f $(MAKE_MATH) $(BASE_DEFINES) clean + $(MAKE) -f $(MAKE_CHOREO) $(BASE_DEFINES) clean diff --git a/linux_sdk/Makefile.choreoobjects b/linux_sdk/Makefile.choreoobjects new file mode 100644 index 00000000..ab93231e --- /dev/null +++ b/linux_sdk/Makefile.choreoobjects @@ -0,0 +1,45 @@ +# +# Choreoobjects Static Library Makefile +# + +CHOREOOBJECTS_SRC_DIR=$(SOURCE_DIR)/choreoobjects +GAMESHARED_SRC_DIR=$(SOURCE_DIR)/game_shared +PUBLIC_SRC_DIR=$(SOURCE_DIR)/public +TIER1_PUBLIC_SRC_DIR=$(SOURCE_DIR)/public/tier1 + +CHOREOOBJECTS_OBJ_DIR=$(BUILD_OBJ_DIR)/choreoobjects_$(ARCH) + +# Extension of linux static library +override SHLIBEXT=a + +CFLAGS=$(BASE_CFLAGS) $(ARCH_CFLAGS) +#DEBUG = -g -ggdb +#CFLAGS+= $(DEBUG) + +INCLUDEDIRS=-I$(GAMESHARED_SRC_DIR) -I$(PUBLIC_SRC_DIR) -I$(TIER1_PUBLIC_SRC_DIR) -D_LIB -DCHOREOOBJECTS_STATIC_LIB + +DO_CC=$(CPLUS) $(INCLUDEDIRS) $(CFLAGS) -DARCH=$(ARCH) -o $@ -c $< + +##################################################################### + +CHOREOOBJECTS_OBJS= \ + $(CHOREOOBJECTS_OBJ_DIR)/choreoactor.o \ + $(CHOREOOBJECTS_OBJ_DIR)/choreochannel.o \ + $(CHOREOOBJECTS_OBJ_DIR)/choreoevent.o \ + $(CHOREOOBJECTS_OBJ_DIR)/choreoscene.o \ + +all: dirs choreoobjects_$(ARCH).$(SHLIBEXT) + +dirs: + -mkdir -p $(BUILD_OBJ_DIR) + -mkdir -p $(CHOREOOBJECTS_OBJ_DIR) + $(CHECK_DSP) $(SOURCE_DSP) + +choreoobjects_$(ARCH).$(SHLIBEXT): $(CHOREOOBJECTS_OBJS) + $(AR) $(BUILD_DIR)/$@ $(CHOREOOBJECTS_OBJS) + +$(CHOREOOBJECTS_OBJ_DIR)/%.o: $(CHOREOOBJECTS_SRC_DIR)/%.cpp + $(DO_CC) + +clean: + -rm -rf $(CHOREOOBJECTS_OBJ_DIR) diff --git a/linux_sdk/Makefile.game b/linux_sdk/Makefile.game index 80f8eebe..2a7cdad7 100644 --- a/linux_sdk/Makefile.game +++ b/linux_sdk/Makefile.game @@ -7,14 +7,14 @@ # PROJECT MAKEFILES ############################################################################# MAKE_FILE=Makefile.$(MOD_CONFIG) -include $(MAKE_FILE) +-include $(MAKE_FILE) ############################################################################# -# The compiler command lne for each src code file to compile +# The compiler command line for each src code file to compile ############################################################################# -DO_CC=$(CPLUS) -w $(INCLUDES) $(CFLAGS) -o $@ -c $< +DO_CC=$(CPLUS) $(INCLUDES) $(CFLAGS) -o $@ -c $< clean: - rm -rf obj/$(NAME) + rm -rf obj/$(NAME)_$(ARCH) rm -f $(NAME)_$(ARCH).$(SHLIBEXT) diff --git a/linux_sdk/Makefile.mathlib b/linux_sdk/Makefile.mathlib new file mode 100644 index 00000000..f1d8c413 --- /dev/null +++ b/linux_sdk/Makefile.mathlib @@ -0,0 +1,45 @@ +# +# Mathlin Static Library Makefile +# + +MATHLIB_SRC_DIR=$(SOURCE_DIR)/mathlib +PUBLIC_SRC_DIR=$(SOURCE_DIR)/public +MATHLIB_PUBLIC_SRC_DIR=$(SOURCE_DIR)/public/mathlib + +MATHLIB_OBJ_DIR=$(BUILD_OBJ_DIR)/mathlib_$(ARCH) + +# Extension of linux static library +override SHLIBEXT=a + +CFLAGS=$(BASE_CFLAGS) $(ARCH_CFLAGS) +#DEBUG = -g -ggdb +#CFLAGS+= $(DEBUG) + +INCLUDEDIRS=-I$(PUBLIC_SRC_DIR) -I$(MATHLIB_PUBLIC_SRC_DIR) -D_LIB + +DO_CC=$(CPLUS) $(INCLUDEDIRS) $(CFLAGS) -DARCH=$(ARCH) -o $@ -c $< + +##################################################################### + +MATHLIB_OBJS= \ + $(MATHLIB_OBJ_DIR)/halton.o \ + $(MATHLIB_OBJ_DIR)/lightdesc.o \ + $(MATHLIB_OBJ_DIR)/mathlib_base.o \ + $(MATHLIB_OBJ_DIR)/powsse.o \ + $(MATHLIB_OBJ_DIR)/sseconst.o \ + +all: dirs mathlib_$(ARCH).$(SHLIBEXT) + +dirs: + -mkdir -p $(BUILD_OBJ_DIR) + -mkdir -p $(MATHLIB_OBJ_DIR) + $(CHECK_DSP) $(SOURCE_DSP) + +mathlib_$(ARCH).$(SHLIBEXT): $(MATHLIB_OBJS) + $(AR) $(BUILD_DIR)/$@ $(MATHLIB_OBJS) + +$(MATHLIB_OBJ_DIR)/%.o: $(MATHLIB_SRC_DIR)/%.cpp + $(DO_CC) + +clean: + -rm -rf $(MATHLIB_OBJ_DIR) diff --git a/linux_sdk/Makefile.plugin b/linux_sdk/Makefile.plugin index 67589549..fff36d39 100644 --- a/linux_sdk/Makefile.plugin +++ b/linux_sdk/Makefile.plugin @@ -17,9 +17,9 @@ CFLAGS=$(BASE_CFLAGS) $(ARCH_CFLAGS) #DEBUG = -g -ggdb #CFLAGS+= $(DEBUG) -INCLUDEDIRS=-I$(PUBLIC_SRC_DIR) -I$(TIER1_PUBLIC_SRC_DIR) -Dstrcmpi=strcasecmp -D_alloca=alloca +INCLUDEDIRS=-I$(PUBLIC_SRC_DIR) -I$(TIER1_PUBLIC_SRC_DIR) -Dstrcmpi=strcasecmp -D_alloca=alloca -DO_CC=$(CPLUS) $(INCLUDEDIRS) -w $(CFLAGS) -DARCH=$(ARCH) -o $@ -c $< +DO_CC=$(CPLUS) $(INCLUDEDIRS) $(CFLAGS) -DARCH=$(ARCH) -o $@ -c $< ##################################################################### @@ -29,15 +29,15 @@ PLUGIN_OBJS = \ $(PLUGIN_OBJ_DIR)/serverplugin_bot.o \ TIER0_OBJS = \ - $(TIER0_OBJ_DIR)/memoverride-vc7.o \ + $(TIER0_OBJ_DIR)/memoverride.o \ all: dirs serverplugin_empty_$(ARCH).$(SHLIBEXT) dirs: - -mkdir $(BUILD_OBJ_DIR) - -mkdir $(PLUGIN_OBJ_DIR) - -mkdir $(PUBLIC_OBJ_DIR) - -mkdir $(TIER0_OBJ_DIR) + -mkdir -p $(BUILD_OBJ_DIR) + -mkdir -p $(PLUGIN_OBJ_DIR) + -mkdir -p $(PUBLIC_OBJ_DIR) + -mkdir -p $(TIER0_OBJ_DIR) $(CHECK_DSP) $(SOURCE_DSP) serverplugin_empty_$(ARCH).$(SHLIBEXT): $(PLUGIN_OBJS) $(TIER0_OBJS) @@ -51,4 +51,4 @@ $(TIER0_OBJ_DIR)/%.o: $(TIER0_PUBLIC_SRC_DIR)/%.cpp clean: -rm -rf $(PLUGIN_OBJ_DIR) - -rm -f plugin_$(ARCH).$(SHLIBEXT) + -rm -f serverplugin_empty_$(ARCH).$(SHLIBEXT) diff --git a/linux_sdk/Makefile.tier1 b/linux_sdk/Makefile.tier1 new file mode 100644 index 00000000..a0c08789 --- /dev/null +++ b/linux_sdk/Makefile.tier1 @@ -0,0 +1,66 @@ +# +# Tier1 Static Library Makefile +# + +TIER1_SRC_DIR=$(SOURCE_DIR)/tier1 +COMMON_SRC_DIR=$(SOURCE_DIR)/common +PUBLIC_SRC_DIR=$(SOURCE_DIR)/public +TIER1_PUBLIC_SRC_DIR=$(SOURCE_DIR)/public/tier1 + +TIER1_OBJ_DIR=$(BUILD_OBJ_DIR)/tier1_$(ARCH) + +# Extension of linux static library +override SHLIBEXT=a + +CFLAGS=$(BASE_CFLAGS) $(ARCH_CFLAGS) +#DEBUG = -g -ggdb +#CFLAGS+= $(DEBUG) + +INCLUDEDIRS=-I$(COMMON_SRC_DIR) -I$(PUBLIC_SRC_DIR) -I$(TIER1_PUBLIC_SRC_DIR) -D_LIB -DTIER1_STATIC_LIB + +DO_CC=$(CPLUS) $(INCLUDEDIRS) $(CFLAGS) -DARCH=$(ARCH) -o $@ -c $< + +##################################################################### + +TIER1_OBJS= \ + $(TIER1_OBJ_DIR)/bitbuf.o \ + $(TIER1_OBJ_DIR)/byteswap.o \ + $(TIER1_OBJ_DIR)/characterset.o \ + $(TIER1_OBJ_DIR)/checksum_crc.o \ + $(TIER1_OBJ_DIR)/checksum_md5.o \ + $(TIER1_OBJ_DIR)/convar.o \ + $(TIER1_OBJ_DIR)/datamanager.o \ + $(TIER1_OBJ_DIR)/diff.o \ + $(TIER1_OBJ_DIR)/generichash.o \ + $(TIER1_OBJ_DIR)/interface.o \ + $(TIER1_OBJ_DIR)/jobthread.o \ + $(TIER1_OBJ_DIR)/KeyValues.o \ + $(TIER1_OBJ_DIR)/mempool.o \ + $(TIER1_OBJ_DIR)/memstack.o \ + $(TIER1_OBJ_DIR)/NetAdr.o \ + $(TIER1_OBJ_DIR)/processor_detect.o \ + $(TIER1_OBJ_DIR)/rangecheckedvar.o \ + $(TIER1_OBJ_DIR)/stringpool.o \ + $(TIER1_OBJ_DIR)/strtools.o \ + $(TIER1_OBJ_DIR)/tier1.o \ + $(TIER1_OBJ_DIR)/tokenreader.o \ + $(TIER1_OBJ_DIR)/utlbuffer.o \ + $(TIER1_OBJ_DIR)/utlstring.o \ + $(TIER1_OBJ_DIR)/utlsymbol.o \ + $(TIER1_OBJ_DIR)/xboxstubs.o \ + +all: dirs tier1_$(ARCH).$(SHLIBEXT) + +dirs: + -mkdir -p $(BUILD_OBJ_DIR) + -mkdir -p $(TIER1_OBJ_DIR) + $(CHECK_DSP) $(SOURCE_DSP) + +tier1_$(ARCH).$(SHLIBEXT): $(TIER1_OBJS) + $(AR) $(BUILD_DIR)/$@ $(TIER1_OBJS) + +$(TIER1_OBJ_DIR)/%.o: $(TIER1_SRC_DIR)/%.cpp + $(DO_CC) + +clean: + -rm -rf $(TIER1_OBJ_DIR) diff --git a/linux_sdk/Makefile.vcpm b/linux_sdk/Makefile.vcpm index 10446a58..f85cf4dd 100644 --- a/linux_sdk/Makefile.vcpm +++ b/linux_sdk/Makefile.vcpm @@ -14,12 +14,12 @@ VCPM_OBJ_DIR=$(BUILD_OBJ_DIR)/vcpm TIER1_OBJ_DIR=$(BUILD_OBJ_DIR)/vcpm/public #we use custome CFLAGS because the base ones interfere with XERCES -CFLAGS= -w -fpermissive -D_LINUX -DNDEBUG -D_alloca=alloca -D_snprintf=snprintf -D_vsnprintf=vsnprintf $(ARCH_CFLAGS) +CFLAGS=-D_LINUX -DNDEBUG -D_alloca=alloca -D_snprintf=snprintf -D_vsnprintf=vsnprintf -Wall -Wno-non-virtual-dtor -Wno-invalid-offsetof -Werror $(ARCH_CFLAGS) #DEBUG = -g -ggdb #CFLAGS+= $(DEBUG) INCLUDEDIRS=-I$(PUBLIC_SRC_DIR) -I$(XERCES_INC_DIR) -I$(UTIL_COMMON_SRC_DIR) -I$(TIER1_PUBLIC_SRC_DIR) -LDFLAGS_VC=-lm -ldl -L$(XERCES_LIB_DIR) -lxerces-c $(GAME_DIR)/bin/tier0_i486.so $(GAME_DIR)/bin/vstdlib_i486.so +LDFLAGS_VC=-lm -ldl -L$(XERCES_LIB_DIR) -lxerces-c tier1_i486.a $(GAME_DIR)/bin/tier0_i486.so $(GAME_DIR)/bin/vstdlib_i486.so DO_CC=$(CPLUS) $(INCLUDEDIRS) -w $(CFLAGS) -DARCH=$(ARCH) -o $@ -c $< @@ -30,31 +30,18 @@ VCPM_OBJS = \ $(VCPM_OBJ_DIR)/vprojtomake.o \ $(VCPM_OBJ_DIR)/vcprojconvert.o \ -TIER1_OBJS = \ - $(TIER1_OBJ_DIR)/characterset.o \ - $(TIER1_OBJ_DIR)/interface.o \ - $(TIER1_OBJ_DIR)/generichash.o \ - $(TIER1_OBJ_DIR)/KeyValues.o \ - $(TIER1_OBJ_DIR)/stringpool.o \ - $(TIER1_OBJ_DIR)/utlbuffer.o \ - $(TIER1_OBJ_DIR)/utlsymbol.o \ - all: dirs vcpm dirs: - -mkdir $(BUILD_OBJ_DIR) - -mkdir $(VCPM_OBJ_DIR) - -mkdir $(TIER1_OBJ_DIR) + -mkdir -p $(BUILD_OBJ_DIR) + -mkdir -p $(VCPM_OBJ_DIR) -vcpm: $(VCPM_OBJS) $(TIER1_OBJS) - $(CLINK) $(DEBUG) -o $(BUILD_DIR)/$@ $(VCPM_OBJS) $(TIER1_OBJS) $(CPP_LIB) $(LDFLAGS_VC) +vcpm: $(VCPM_OBJS) + $(CLINK) $(DEBUG) -o $(BUILD_DIR)/$@ $(VCPM_OBJS) $(CPP_LIB) $(LDFLAGS_VC) $(VCPM_OBJ_DIR)/%.o: $(VCPM_SRC_DIR)/%.cpp $(DO_CC) -$(TIER1_OBJ_DIR)/%.o: $(TIER1_SRC_DIR)/%.cpp - $(DO_CC) -Dstricmp=strcasecmp -Dstrcmpi=strcasecmp - clean: -rm -rf $(VCPM_OBJ_DIR) -rm -f vcpm diff --git a/materialsystem/stdshaders/stdshader_dx8-2003.vcproj b/materialsystem/stdshaders/stdshader_dx8-2003.vcproj index 16ca7b37..3dfeebbe 100644 --- a/materialsystem/stdshaders/stdshader_dx8-2003.vcproj +++ b/materialsystem/stdshaders/stdshader_dx8-2003.vcproj @@ -42,12 +42,12 @@ CompileAs="0"/> + Outputs=""bin\game_shader_dx8.dll""/> + Outputs=""bin\game_shader_dx8.dll""/> + Outputs=""bin\game_shader_dx9.dll""/> + Outputs=""bin\game_shader_dx9.dll""/> (255 * pow ( i/255.f, g1 )); if (inf < 0) inf = 0; if (inf > 255) @@ -3035,7 +3043,7 @@ void BuildGammaTable( float gamma, float texGamma, float brightness, int overbri f = 0.125 + ((f - g3) / (1.0 - g3)) * 0.875; // convert linear space to desired gamma space - inf = 255 * pow ( f, g ); + inf = static_cast(255 * pow ( f, g )); if (inf < 0) inf = 0; @@ -3069,7 +3077,7 @@ void BuildGammaTable( float gamma, float texGamma, float brightness, int overbri for (i=0 ; i<1024 ; i++) { // convert from linear space (0..1) to nonlinear texture space (0..255) - lineartotexture[i] = pow( i / 1023.0, 1.0 / texGamma ) * 255; + lineartotexture[i] = static_cast(pow( i / 1023.0, 1.0 / texGamma ) * 255); } BuildExponentTable(); @@ -3186,7 +3194,7 @@ int LinearToTexture( float f ) { Assert( s_bMathlibInitialized ); int i; - i = f * 1023; // assume 0..1 range + i = static_cast(f * 1023); // assume 0..1 range if (i < 0) i = 0; if (i > 1023) @@ -3201,7 +3209,7 @@ int LinearToScreenGamma( float f ) { Assert( s_bMathlibInitialized ); int i; - i = f * 1023; // assume 0..1 range + i = static_cast(f * 1023); // assume 0..1 range if (i < 0) i = 0; if (i > 1023) @@ -4303,7 +4311,9 @@ void Hermite_SplineBasis( float t, float basis[4] ) //----------------------------------------------------------------------------- // BUG: the VectorSubtract()'s calls go away if the global optimizer is enabled +#ifdef _MSC_VER #pragma optimize( "g", off ) +#endif void Hermite_Spline( const Vector &p0, const Vector &p1, const Vector &p2, float t, Vector& output ) { @@ -4313,7 +4323,9 @@ void Hermite_Spline( const Vector &p0, const Vector &p1, const Vector &p2, float Hermite_Spline( p1, p2, e10, e21, t, output ); } +#ifdef _MSC_VER #pragma optimize( "", on ) +#endif float Hermite_Spline( float p0, float p1, float p2, float t ) { @@ -5060,7 +5072,9 @@ bool CalcLineToLineIntersectionSegment( return true; } +#ifdef _MSC_VER #pragma optimize( "", off ) +#endif // stuff from windows.h #ifndef _XBOX @@ -5071,8 +5085,9 @@ typedef unsigned int DWORD; #define EXCEPTION_EXECUTE_HANDLER 1 #endif - +#ifdef _MSC_VER #pragma optimize( "", on ) +#endif static bool s_b3DNowEnabled = false; static bool s_bMMXEnabled = false; diff --git a/notes.txt b/notes.txt new file mode 100644 index 00000000..14da7cd3 --- /dev/null +++ b/notes.txt @@ -0,0 +1,18 @@ +********* +* About * +********* +This version of the Source/Half-Life 2 SDK attempts to provide support for GCC 4.x and remove as many +warnings as possible when compiling under Windows or Linux. In addition, the -fpermissive option has +been removed from the Linux makefiles and -Wall has been added. + +********* +* Notes * +********* +1. The file "Makefile.mod" located in the linux_sdk directory has been renamed to "Makefile.game" in + order to avoid potential conflicts with the Modula-2 compiler. GNU Make may issue an error about the + file with its original name and try to execute m2c (the Modula-2 compiler program). + +2. Makefiles for choreoobjects_i486.a, mathlib_i486.a, and tier1_i486.a have been added in the linux_sdk + directory to ease compiling of these static libraries on Linux if it ever becomes necessary. + +3. The SDK does not currently compile with GCC 4.2. This will be addressed in the future. diff --git a/public/IceKey.cpp b/public/IceKey.cpp index 54ac5c46..e41e87d0 100644 --- a/public/IceKey.cpp +++ b/public/IceKey.cpp @@ -6,8 +6,9 @@ #include "IceKey.H" +#ifdef _MSC_VER #pragma warning(disable: 4244) - +#endif /* Structure of a single round subkey */ class IceSubkey { diff --git a/public/ScratchPadUtils.h b/public/ScratchPadUtils.h index 9f2e8cc2..ed47c65d 100644 --- a/public/ScratchPadUtils.h +++ b/public/ScratchPadUtils.h @@ -64,6 +64,7 @@ private: class CLineInfo { public: + CLineInfo() : m_flLastTime(0.0f), m_flLastValue(0.0f) { }; bool m_bFirst; float m_flLastTime; float m_flLastValue; diff --git a/public/SoundEmitterSystem/isoundemittersystembase.h b/public/SoundEmitterSystem/isoundemittersystembase.h index 0092bcde..7deb861d 100644 --- a/public/SoundEmitterSystem/isoundemittersystembase.h +++ b/public/SoundEmitterSystem/isoundemittersystembase.h @@ -106,8 +106,8 @@ struct sound_interval_t T range; interval_t &ToInterval( interval_t &dest ) const { dest.start = start; dest.range = range; return dest; } - void FromInterval( const interval_t &from ) { start = from.start; range = from.range; } - float Random() const { interval_t temp = { start, range }; return RandomInterval( temp ); } + void FromInterval( const interval_t &from ) { start = static_cast(from.start); range = static_cast(from.range); } + float Random() const { interval_t temp = { start, range }; return this->RandomInterval( temp ); } }; @@ -125,7 +125,7 @@ struct CSoundParametersInternal void CopyFrom( const CSoundParametersInternal& src ); - bool CSoundParametersInternal::operator == ( const CSoundParametersInternal& other ) const; + bool operator == ( const CSoundParametersInternal& other ) const; const char *VolumeToString( void ) const; const char *ChannelToString( void ) const; @@ -148,9 +148,9 @@ struct CSoundParametersInternal bool ShouldPreload() const { return m_bShouldPreload; } void SetChannel( int newChannel ) { channel = newChannel; } - void SetVolume( float start, float range = 0.0 ) { volume.start = start; volume.range = range; } - void SetPitch( float start, float range = 0.0 ) { pitch.start = start; pitch.range = range; } - void SetSoundLevel( float start, float range = 0.0 ) { soundlevel.start = start; soundlevel.range = range; } + void SetVolume( float start, float range = 0.0 ) { volume.start = static_cast(start); volume.range = static_cast(range); } + void SetPitch( float start, float range = 0.0 ) { pitch.start = static_cast(start); pitch.range = static_cast(range); } + void SetSoundLevel( float start, float range = 0.0 ) { soundlevel.start = static_cast(start); soundlevel.range = static_cast(range); } void SetDelayMsec( int delay ) { delay_msec = delay; } void SetShouldPreload( bool bShouldPreload ) { m_bShouldPreload = bShouldPreload; } void SetOnlyPlayToOwner( bool b ) { play_to_owner_only = b; } diff --git a/public/SoundEmitterSystem/isoundemittersystembasev1.h b/public/SoundEmitterSystem/isoundemittersystembasev1.h index d16158b2..5883a0ba 100644 --- a/public/SoundEmitterSystem/isoundemittersystembasev1.h +++ b/public/SoundEmitterSystem/isoundemittersystembasev1.h @@ -84,7 +84,7 @@ struct CSoundParametersInternal // CSoundParametersInternal( const CSoundParametersInternal& src ); bool CompareInterval( const interval_t& i1, const interval_t& i2 ) const; - bool CSoundParametersInternal::operator == ( const CSoundParametersInternal& other ) const; + bool operator == ( const CSoundParametersInternal& other ) const; const char *VolumeToString( void ); const char *ChannelToString( void ); diff --git a/public/UnicodeFileHelpers.cpp b/public/UnicodeFileHelpers.cpp index b01c1c03..dee10a10 100644 --- a/public/UnicodeFileHelpers.cpp +++ b/public/UnicodeFileHelpers.cpp @@ -1,240 +1,240 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -//=============================================================================// - -#include -#include -#include "UtlBuffer.h" - -// memdbgon must be the last include file in a .cpp file!!! -#include "tier0/memdbgon.h" - -//----------------------------------------------------------------------------- -// Purpose: Advances until non-whitespace hit -//----------------------------------------------------------------------------- -wchar_t *AdvanceOverWhitespace(wchar_t *Start) -{ - while (*Start != 0 && iswspace(*Start)) - { - Start++; - } - - return Start; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -wchar_t *ReadUnicodeToken(wchar_t *start, wchar_t *token, int tokenBufferSize, bool "ed) -{ - // skip over any whitespace - start = AdvanceOverWhitespace(start); - quoted = false; - *token = 0; - - if (!*start) - { - return start; - } - - // check to see if it's a quoted string - if (*start == '\"') - { - quoted = true; - // copy out the string until we hit an end quote - start++; - int count = 0; - while (*start && *start != '\"' && count < tokenBufferSize) - { - // check for special characters - if (*start == '\\' && *(start+1) == 'n') - { - start++; - *token = '\n'; - } - else if (*start == '\\' && *(start+1) == '\"') - { - start++; - *token = '\"'; - } - else - { - *token = *start; - } - - start++; - token++; - count++; - } - - if (*start == '\"') - { - start++; - } - } - else - { - // copy out the string until we hit a whitespace - int count = 0; - while (*start && !iswspace(*start) && count < tokenBufferSize) - { - // no checking for special characters if it's not a quoted string - *token = *start; - - start++; - token++; - count++; - } - } - - *token = 0; - return start; -} - -//----------------------------------------------------------------------------- -// Purpose: Same as above but no translation of \n -//----------------------------------------------------------------------------- -wchar_t *ReadUnicodeTokenNoSpecial(wchar_t *start, wchar_t *token, int tokenBufferSize, bool "ed) -{ - // skip over any whitespace - start = AdvanceOverWhitespace(start); - quoted = false; - *token = 0; - - if (!*start) - { - return start; - } - - // check to see if it's a quoted string - if (*start == '\"') - { - quoted = true; - // copy out the string until we hit an end quote - start++; - int count = 0; - while (*start && *start != '\"' && count < tokenBufferSize) - { - // check for special characters - /* - if (*start == '\\' && *(start+1) == 'n') - { - start++; - *token = '\n'; - } - else - */ - if (*start == '\\' && *(start+1) == '\"') - { - start++; - *token = '\"'; - } - else - { - *token = *start; - } - - start++; - token++; - count++; - } - - if (*start == '\"') - { - start++; - } - } - else - { - // copy out the string until we hit a whitespace - int count = 0; - while (*start && !iswspace(*start) && count < tokenBufferSize) - { - // no checking for special characters if it's not a quoted string - *token = *start; - - start++; - token++; - count++; - } - } - - *token = 0; - return start; -} - -//----------------------------------------------------------------------------- -// Purpose: Returns the first character after the next EOL characters -//----------------------------------------------------------------------------- -wchar_t *ReadToEndOfLine(wchar_t *start) -{ - if (!*start) - return start; - - while (*start) - { - if (*start == 0x0D || *start== 0x0A) - break; - start++; - } - - while (*start == 0x0D || *start== 0x0A) - start++; - - return start; -} - -//----------------------------------------------------------------------------- -// Purpose: file writing -//----------------------------------------------------------------------------- -void WriteUnicodeString(CUtlBuffer &buf, const wchar_t *string, bool addQuotes) -{ - if (addQuotes) - { - buf.PutUnsignedShort('\"'); - } - - for (const wchar_t *ws = string; *ws != 0; ws++) - { - // handle special characters - if (addQuotes && *ws == '\"') - { - buf.PutUnsignedShort('\\'); - } - // write the character - buf.PutUnsignedShort(*ws); - } - - if (addQuotes) - { - buf.PutUnsignedShort('\"'); - } -} - -//----------------------------------------------------------------------------- -// Purpose: file writing -//----------------------------------------------------------------------------- -void WriteAsciiStringAsUnicode(CUtlBuffer &buf, const char *string, bool addQuotes) -{ - if (addQuotes) - { - buf.PutUnsignedShort('\"'); - } - - for (const char *sz = string; *sz != 0; sz++) - { - // handle special characters - if (addQuotes && *sz == '\"') - { - buf.PutUnsignedShort('\\'); - } - buf.PutUnsignedShort(*sz); - } - - if (addQuotes) - { - buf.PutUnsignedShort('\"'); - } -} +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include +#include +#include "utlbuffer.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +//----------------------------------------------------------------------------- +// Purpose: Advances until non-whitespace hit +//----------------------------------------------------------------------------- +wchar_t *AdvanceOverWhitespace(wchar_t *Start) +{ + while (*Start != 0 && iswspace(*Start)) + { + Start++; + } + + return Start; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +wchar_t *ReadUnicodeToken(wchar_t *start, wchar_t *token, int tokenBufferSize, bool "ed) +{ + // skip over any whitespace + start = AdvanceOverWhitespace(start); + quoted = false; + *token = 0; + + if (!*start) + { + return start; + } + + // check to see if it's a quoted string + if (*start == '\"') + { + quoted = true; + // copy out the string until we hit an end quote + start++; + int count = 0; + while (*start && *start != '\"' && count < tokenBufferSize) + { + // check for special characters + if (*start == '\\' && *(start+1) == 'n') + { + start++; + *token = '\n'; + } + else if (*start == '\\' && *(start+1) == '\"') + { + start++; + *token = '\"'; + } + else + { + *token = *start; + } + + start++; + token++; + count++; + } + + if (*start == '\"') + { + start++; + } + } + else + { + // copy out the string until we hit a whitespace + int count = 0; + while (*start && !iswspace(*start) && count < tokenBufferSize) + { + // no checking for special characters if it's not a quoted string + *token = *start; + + start++; + token++; + count++; + } + } + + *token = 0; + return start; +} + +//----------------------------------------------------------------------------- +// Purpose: Same as above but no translation of \n +//----------------------------------------------------------------------------- +wchar_t *ReadUnicodeTokenNoSpecial(wchar_t *start, wchar_t *token, int tokenBufferSize, bool "ed) +{ + // skip over any whitespace + start = AdvanceOverWhitespace(start); + quoted = false; + *token = 0; + + if (!*start) + { + return start; + } + + // check to see if it's a quoted string + if (*start == '\"') + { + quoted = true; + // copy out the string until we hit an end quote + start++; + int count = 0; + while (*start && *start != '\"' && count < tokenBufferSize) + { + // check for special characters + /* + if (*start == '\\' && *(start+1) == 'n') + { + start++; + *token = '\n'; + } + else + */ + if (*start == '\\' && *(start+1) == '\"') + { + start++; + *token = '\"'; + } + else + { + *token = *start; + } + + start++; + token++; + count++; + } + + if (*start == '\"') + { + start++; + } + } + else + { + // copy out the string until we hit a whitespace + int count = 0; + while (*start && !iswspace(*start) && count < tokenBufferSize) + { + // no checking for special characters if it's not a quoted string + *token = *start; + + start++; + token++; + count++; + } + } + + *token = 0; + return start; +} + +//----------------------------------------------------------------------------- +// Purpose: Returns the first character after the next EOL characters +//----------------------------------------------------------------------------- +wchar_t *ReadToEndOfLine(wchar_t *start) +{ + if (!*start) + return start; + + while (*start) + { + if (*start == 0x0D || *start== 0x0A) + break; + start++; + } + + while (*start == 0x0D || *start== 0x0A) + start++; + + return start; +} + +//----------------------------------------------------------------------------- +// Purpose: file writing +//----------------------------------------------------------------------------- +void WriteUnicodeString(CUtlBuffer &buf, const wchar_t *string, bool addQuotes) +{ + if (addQuotes) + { + buf.PutUnsignedShort('\"'); + } + + for (const wchar_t *ws = string; *ws != 0; ws++) + { + // handle special characters + if (addQuotes && *ws == '\"') + { + buf.PutUnsignedShort('\\'); + } + // write the character + buf.PutUnsignedShort(*ws); + } + + if (addQuotes) + { + buf.PutUnsignedShort('\"'); + } +} + +//----------------------------------------------------------------------------- +// Purpose: file writing +//----------------------------------------------------------------------------- +void WriteAsciiStringAsUnicode(CUtlBuffer &buf, const char *string, bool addQuotes) +{ + if (addQuotes) + { + buf.PutUnsignedShort('\"'); + } + + for (const char *sz = string; *sz != 0; sz++) + { + // handle special characters + if (addQuotes && *sz == '\"') + { + buf.PutUnsignedShort('\\'); + } + buf.PutUnsignedShort(*sz); + } + + if (addQuotes) + { + buf.PutUnsignedShort('\"'); + } +} diff --git a/public/UtlCachedFileData.h b/public/UtlCachedFileData.h index 8c3a00b4..84594187 100644 --- a/public/UtlCachedFileData.h +++ b/public/UtlCachedFileData.h @@ -61,13 +61,13 @@ public: m_pszRepositoryFileName( repositoryFileName ), m_nVersion( version ), m_pfnMetaChecksum( checksumfunc ), - m_bDirty( false ), - m_bInitialized( false ), m_uCurrentMetaChecksum( 0u ), m_fileCheckType( fileCheckType ), m_bNeverCheckDisk( nevercheckdisk ), m_bReadOnly( readonly ), - m_bSaveManifest( savemanifest ) + m_bSaveManifest( savemanifest ), + m_bDirty( false ), + m_bInitialized( false ) { Assert( m_pszRepositoryFileName && m_pszRepositoryFileName[ 0 ] ); } diff --git a/public/anorms.cpp b/public/anorms.cpp index 7117785e..86db7aeb 100644 --- a/public/anorms.cpp +++ b/public/anorms.cpp @@ -1,181 +1,181 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -//=============================================================================// -#if !defined(_STATIC_LINKED) || defined(_SHARED_LIB) - - -#include "vector.h" -#include "anorms.h" - -// memdbgon must be the last include file in a .cpp file!!! -#include "tier0/memdbgon.h" - -Vector g_anorms[NUMVERTEXNORMALS] = -{ - Vector(-0.525731, 0.000000, 0.850651), - Vector(-0.442863, 0.238856, 0.864188), - Vector(-0.295242, 0.000000, 0.955423), - Vector(-0.309017, 0.500000, 0.809017), - Vector(-0.162460, 0.262866, 0.951056), - Vector(0.000000, 0.000000, 1.000000), - Vector(0.000000, 0.850651, 0.525731), - Vector(-0.147621, 0.716567, 0.681718), - Vector(0.147621, 0.716567, 0.681718), - Vector(0.000000, 0.525731, 0.850651), - Vector(0.309017, 0.500000, 0.809017), - Vector(0.525731, 0.000000, 0.850651), - Vector(0.295242, 0.000000, 0.955423), - Vector(0.442863, 0.238856, 0.864188), - Vector(0.162460, 0.262866, 0.951056), - Vector(-0.681718, 0.147621, 0.716567), - Vector(-0.809017, 0.309017, 0.500000), - Vector(-0.587785, 0.425325, 0.688191), - Vector(-0.850651, 0.525731, 0.000000), - Vector(-0.864188, 0.442863, 0.238856), - Vector(-0.716567, 0.681718, 0.147621), - Vector(-0.688191, 0.587785, 0.425325), - Vector(-0.500000, 0.809017, 0.309017), - Vector(-0.238856, 0.864188, 0.442863), - Vector(-0.425325, 0.688191, 0.587785), - Vector(-0.716567, 0.681718, -0.147621), - Vector(-0.500000, 0.809017, -0.309017), - Vector(-0.525731, 0.850651, 0.000000), - Vector(0.000000, 0.850651, -0.525731), - Vector(-0.238856, 0.864188, -0.442863), - Vector(0.000000, 0.955423, -0.295242), - Vector(-0.262866, 0.951056, -0.162460), - Vector(0.000000, 1.000000, 0.000000), - Vector(0.000000, 0.955423, 0.295242), - Vector(-0.262866, 0.951056, 0.162460), - Vector(0.238856, 0.864188, 0.442863), - Vector(0.262866, 0.951056, 0.162460), - Vector(0.500000, 0.809017, 0.309017), - Vector(0.238856, 0.864188, -0.442863), - Vector(0.262866, 0.951056, -0.162460), - Vector(0.500000, 0.809017, -0.309017), - Vector(0.850651, 0.525731, 0.000000), - Vector(0.716567, 0.681718, 0.147621), - Vector(0.716567, 0.681718, -0.147621), - Vector(0.525731, 0.850651, 0.000000), - Vector(0.425325, 0.688191, 0.587785), - Vector(0.864188, 0.442863, 0.238856), - Vector(0.688191, 0.587785, 0.425325), - Vector(0.809017, 0.309017, 0.500000), - Vector(0.681718, 0.147621, 0.716567), - Vector(0.587785, 0.425325, 0.688191), - Vector(0.955423, 0.295242, 0.000000), - Vector(1.000000, 0.000000, 0.000000), - Vector(0.951056, 0.162460, 0.262866), - Vector(0.850651, -0.525731, 0.000000), - Vector(0.955423, -0.295242, 0.000000), - Vector(0.864188, -0.442863, 0.238856), - Vector(0.951056, -0.162460, 0.262866), - Vector(0.809017, -0.309017, 0.500000), - Vector(0.681718, -0.147621, 0.716567), - Vector(0.850651, 0.000000, 0.525731), - Vector(0.864188, 0.442863, -0.238856), - Vector(0.809017, 0.309017, -0.500000), - Vector(0.951056, 0.162460, -0.262866), - Vector(0.525731, 0.000000, -0.850651), - Vector(0.681718, 0.147621, -0.716567), - Vector(0.681718, -0.147621, -0.716567), - Vector(0.850651, 0.000000, -0.525731), - Vector(0.809017, -0.309017, -0.500000), - Vector(0.864188, -0.442863, -0.238856), - Vector(0.951056, -0.162460, -0.262866), - Vector(0.147621, 0.716567, -0.681718), - Vector(0.309017, 0.500000, -0.809017), - Vector(0.425325, 0.688191, -0.587785), - Vector(0.442863, 0.238856, -0.864188), - Vector(0.587785, 0.425325, -0.688191), - Vector(0.688191, 0.587785, -0.425325), - Vector(-0.147621, 0.716567, -0.681718), - Vector(-0.309017, 0.500000, -0.809017), - Vector(0.000000, 0.525731, -0.850651), - Vector(-0.525731, 0.000000, -0.850651), - Vector(-0.442863, 0.238856, -0.864188), - Vector(-0.295242, 0.000000, -0.955423), - Vector(-0.162460, 0.262866, -0.951056), - Vector(0.000000, 0.000000, -1.000000), - Vector(0.295242, 0.000000, -0.955423), - Vector(0.162460, 0.262866, -0.951056), - Vector(-0.442863, -0.238856, -0.864188), - Vector(-0.309017, -0.500000, -0.809017), - Vector(-0.162460, -0.262866, -0.951056), - Vector(0.000000, -0.850651, -0.525731), - Vector(-0.147621, -0.716567, -0.681718), - Vector(0.147621, -0.716567, -0.681718), - Vector(0.000000, -0.525731, -0.850651), - Vector(0.309017, -0.500000, -0.809017), - Vector(0.442863, -0.238856, -0.864188), - Vector(0.162460, -0.262866, -0.951056), - Vector(0.238856, -0.864188, -0.442863), - Vector(0.500000, -0.809017, -0.309017), - Vector(0.425325, -0.688191, -0.587785), - Vector(0.716567, -0.681718, -0.147621), - Vector(0.688191, -0.587785, -0.425325), - Vector(0.587785, -0.425325, -0.688191), - Vector(0.000000, -0.955423, -0.295242), - Vector(0.000000, -1.000000, 0.000000), - Vector(0.262866, -0.951056, -0.162460), - Vector(0.000000, -0.850651, 0.525731), - Vector(0.000000, -0.955423, 0.295242), - Vector(0.238856, -0.864188, 0.442863), - Vector(0.262866, -0.951056, 0.162460), - Vector(0.500000, -0.809017, 0.309017), - Vector(0.716567, -0.681718, 0.147621), - Vector(0.525731, -0.850651, 0.000000), - Vector(-0.238856, -0.864188, -0.442863), - Vector(-0.500000, -0.809017, -0.309017), - Vector(-0.262866, -0.951056, -0.162460), - Vector(-0.850651, -0.525731, 0.000000), - Vector(-0.716567, -0.681718, -0.147621), - Vector(-0.716567, -0.681718, 0.147621), - Vector(-0.525731, -0.850651, 0.000000), - Vector(-0.500000, -0.809017, 0.309017), - Vector(-0.238856, -0.864188, 0.442863), - Vector(-0.262866, -0.951056, 0.162460), - Vector(-0.864188, -0.442863, 0.238856), - Vector(-0.809017, -0.309017, 0.500000), - Vector(-0.688191, -0.587785, 0.425325), - Vector(-0.681718, -0.147621, 0.716567), - Vector(-0.442863, -0.238856, 0.864188), - Vector(-0.587785, -0.425325, 0.688191), - Vector(-0.309017, -0.500000, 0.809017), - Vector(-0.147621, -0.716567, 0.681718), - Vector(-0.425325, -0.688191, 0.587785), - Vector(-0.162460, -0.262866, 0.951056), - Vector(0.442863, -0.238856, 0.864188), - Vector(0.162460, -0.262866, 0.951056), - Vector(0.309017, -0.500000, 0.809017), - Vector(0.147621, -0.716567, 0.681718), - Vector(0.000000, -0.525731, 0.850651), - Vector(0.425325, -0.688191, 0.587785), - Vector(0.587785, -0.425325, 0.688191), - Vector(0.688191, -0.587785, 0.425325), - Vector(-0.955423, 0.295242, 0.000000), - Vector(-0.951056, 0.162460, 0.262866), - Vector(-1.000000, 0.000000, 0.000000), - Vector(-0.850651, 0.000000, 0.525731), - Vector(-0.955423, -0.295242, 0.000000), - Vector(-0.951056, -0.162460, 0.262866), - Vector(-0.864188, 0.442863, -0.238856), - Vector(-0.951056, 0.162460, -0.262866), - Vector(-0.809017, 0.309017, -0.500000), - Vector(-0.864188, -0.442863, -0.238856), - Vector(-0.951056, -0.162460, -0.262866), - Vector(-0.809017, -0.309017, -0.500000), - Vector(-0.681718, 0.147621, -0.716567), - Vector(-0.681718, -0.147621, -0.716567), - Vector(-0.850651, 0.000000, -0.525731), - Vector(-0.688191, 0.587785, -0.425325), - Vector(-0.587785, 0.425325, -0.688191), - Vector(-0.425325, 0.688191, -0.587785), - Vector(-0.425325, -0.688191, -0.587785), - Vector(-0.587785, -0.425325, -0.688191), - Vector(-0.688191, -0.587785, -0.425325) -}; - -#endif // !_STATIC_LINKED || _SHARED_LIB \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// +#if !defined(_STATIC_LINKED) || defined(_SHARED_LIB) + + +#include "vector.h" +#include "anorms.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +Vector g_anorms[NUMVERTEXNORMALS] = +{ + Vector(-0.525731, 0.000000, 0.850651), + Vector(-0.442863, 0.238856, 0.864188), + Vector(-0.295242, 0.000000, 0.955423), + Vector(-0.309017, 0.500000, 0.809017), + Vector(-0.162460, 0.262866, 0.951056), + Vector(0.000000, 0.000000, 1.000000), + Vector(0.000000, 0.850651, 0.525731), + Vector(-0.147621, 0.716567, 0.681718), + Vector(0.147621, 0.716567, 0.681718), + Vector(0.000000, 0.525731, 0.850651), + Vector(0.309017, 0.500000, 0.809017), + Vector(0.525731, 0.000000, 0.850651), + Vector(0.295242, 0.000000, 0.955423), + Vector(0.442863, 0.238856, 0.864188), + Vector(0.162460, 0.262866, 0.951056), + Vector(-0.681718, 0.147621, 0.716567), + Vector(-0.809017, 0.309017, 0.500000), + Vector(-0.587785, 0.425325, 0.688191), + Vector(-0.850651, 0.525731, 0.000000), + Vector(-0.864188, 0.442863, 0.238856), + Vector(-0.716567, 0.681718, 0.147621), + Vector(-0.688191, 0.587785, 0.425325), + Vector(-0.500000, 0.809017, 0.309017), + Vector(-0.238856, 0.864188, 0.442863), + Vector(-0.425325, 0.688191, 0.587785), + Vector(-0.716567, 0.681718, -0.147621), + Vector(-0.500000, 0.809017, -0.309017), + Vector(-0.525731, 0.850651, 0.000000), + Vector(0.000000, 0.850651, -0.525731), + Vector(-0.238856, 0.864188, -0.442863), + Vector(0.000000, 0.955423, -0.295242), + Vector(-0.262866, 0.951056, -0.162460), + Vector(0.000000, 1.000000, 0.000000), + Vector(0.000000, 0.955423, 0.295242), + Vector(-0.262866, 0.951056, 0.162460), + Vector(0.238856, 0.864188, 0.442863), + Vector(0.262866, 0.951056, 0.162460), + Vector(0.500000, 0.809017, 0.309017), + Vector(0.238856, 0.864188, -0.442863), + Vector(0.262866, 0.951056, -0.162460), + Vector(0.500000, 0.809017, -0.309017), + Vector(0.850651, 0.525731, 0.000000), + Vector(0.716567, 0.681718, 0.147621), + Vector(0.716567, 0.681718, -0.147621), + Vector(0.525731, 0.850651, 0.000000), + Vector(0.425325, 0.688191, 0.587785), + Vector(0.864188, 0.442863, 0.238856), + Vector(0.688191, 0.587785, 0.425325), + Vector(0.809017, 0.309017, 0.500000), + Vector(0.681718, 0.147621, 0.716567), + Vector(0.587785, 0.425325, 0.688191), + Vector(0.955423, 0.295242, 0.000000), + Vector(1.000000, 0.000000, 0.000000), + Vector(0.951056, 0.162460, 0.262866), + Vector(0.850651, -0.525731, 0.000000), + Vector(0.955423, -0.295242, 0.000000), + Vector(0.864188, -0.442863, 0.238856), + Vector(0.951056, -0.162460, 0.262866), + Vector(0.809017, -0.309017, 0.500000), + Vector(0.681718, -0.147621, 0.716567), + Vector(0.850651, 0.000000, 0.525731), + Vector(0.864188, 0.442863, -0.238856), + Vector(0.809017, 0.309017, -0.500000), + Vector(0.951056, 0.162460, -0.262866), + Vector(0.525731, 0.000000, -0.850651), + Vector(0.681718, 0.147621, -0.716567), + Vector(0.681718, -0.147621, -0.716567), + Vector(0.850651, 0.000000, -0.525731), + Vector(0.809017, -0.309017, -0.500000), + Vector(0.864188, -0.442863, -0.238856), + Vector(0.951056, -0.162460, -0.262866), + Vector(0.147621, 0.716567, -0.681718), + Vector(0.309017, 0.500000, -0.809017), + Vector(0.425325, 0.688191, -0.587785), + Vector(0.442863, 0.238856, -0.864188), + Vector(0.587785, 0.425325, -0.688191), + Vector(0.688191, 0.587785, -0.425325), + Vector(-0.147621, 0.716567, -0.681718), + Vector(-0.309017, 0.500000, -0.809017), + Vector(0.000000, 0.525731, -0.850651), + Vector(-0.525731, 0.000000, -0.850651), + Vector(-0.442863, 0.238856, -0.864188), + Vector(-0.295242, 0.000000, -0.955423), + Vector(-0.162460, 0.262866, -0.951056), + Vector(0.000000, 0.000000, -1.000000), + Vector(0.295242, 0.000000, -0.955423), + Vector(0.162460, 0.262866, -0.951056), + Vector(-0.442863, -0.238856, -0.864188), + Vector(-0.309017, -0.500000, -0.809017), + Vector(-0.162460, -0.262866, -0.951056), + Vector(0.000000, -0.850651, -0.525731), + Vector(-0.147621, -0.716567, -0.681718), + Vector(0.147621, -0.716567, -0.681718), + Vector(0.000000, -0.525731, -0.850651), + Vector(0.309017, -0.500000, -0.809017), + Vector(0.442863, -0.238856, -0.864188), + Vector(0.162460, -0.262866, -0.951056), + Vector(0.238856, -0.864188, -0.442863), + Vector(0.500000, -0.809017, -0.309017), + Vector(0.425325, -0.688191, -0.587785), + Vector(0.716567, -0.681718, -0.147621), + Vector(0.688191, -0.587785, -0.425325), + Vector(0.587785, -0.425325, -0.688191), + Vector(0.000000, -0.955423, -0.295242), + Vector(0.000000, -1.000000, 0.000000), + Vector(0.262866, -0.951056, -0.162460), + Vector(0.000000, -0.850651, 0.525731), + Vector(0.000000, -0.955423, 0.295242), + Vector(0.238856, -0.864188, 0.442863), + Vector(0.262866, -0.951056, 0.162460), + Vector(0.500000, -0.809017, 0.309017), + Vector(0.716567, -0.681718, 0.147621), + Vector(0.525731, -0.850651, 0.000000), + Vector(-0.238856, -0.864188, -0.442863), + Vector(-0.500000, -0.809017, -0.309017), + Vector(-0.262866, -0.951056, -0.162460), + Vector(-0.850651, -0.525731, 0.000000), + Vector(-0.716567, -0.681718, -0.147621), + Vector(-0.716567, -0.681718, 0.147621), + Vector(-0.525731, -0.850651, 0.000000), + Vector(-0.500000, -0.809017, 0.309017), + Vector(-0.238856, -0.864188, 0.442863), + Vector(-0.262866, -0.951056, 0.162460), + Vector(-0.864188, -0.442863, 0.238856), + Vector(-0.809017, -0.309017, 0.500000), + Vector(-0.688191, -0.587785, 0.425325), + Vector(-0.681718, -0.147621, 0.716567), + Vector(-0.442863, -0.238856, 0.864188), + Vector(-0.587785, -0.425325, 0.688191), + Vector(-0.309017, -0.500000, 0.809017), + Vector(-0.147621, -0.716567, 0.681718), + Vector(-0.425325, -0.688191, 0.587785), + Vector(-0.162460, -0.262866, 0.951056), + Vector(0.442863, -0.238856, 0.864188), + Vector(0.162460, -0.262866, 0.951056), + Vector(0.309017, -0.500000, 0.809017), + Vector(0.147621, -0.716567, 0.681718), + Vector(0.000000, -0.525731, 0.850651), + Vector(0.425325, -0.688191, 0.587785), + Vector(0.587785, -0.425325, 0.688191), + Vector(0.688191, -0.587785, 0.425325), + Vector(-0.955423, 0.295242, 0.000000), + Vector(-0.951056, 0.162460, 0.262866), + Vector(-1.000000, 0.000000, 0.000000), + Vector(-0.850651, 0.000000, 0.525731), + Vector(-0.955423, -0.295242, 0.000000), + Vector(-0.951056, -0.162460, 0.262866), + Vector(-0.864188, 0.442863, -0.238856), + Vector(-0.951056, 0.162460, -0.262866), + Vector(-0.809017, 0.309017, -0.500000), + Vector(-0.864188, -0.442863, -0.238856), + Vector(-0.951056, -0.162460, -0.262866), + Vector(-0.809017, -0.309017, -0.500000), + Vector(-0.681718, 0.147621, -0.716567), + Vector(-0.681718, -0.147621, -0.716567), + Vector(-0.850651, 0.000000, -0.525731), + Vector(-0.688191, 0.587785, -0.425325), + Vector(-0.587785, 0.425325, -0.688191), + Vector(-0.425325, 0.688191, -0.587785), + Vector(-0.425325, -0.688191, -0.587785), + Vector(-0.587785, -0.425325, -0.688191), + Vector(-0.688191, -0.587785, -0.425325) +}; + +#endif // !_STATIC_LINKED || _SHARED_LIB diff --git a/public/bitmap/imageformat.h b/public/bitmap/imageformat.h index dd5129f6..b74e23b2 100644 --- a/public/bitmap/imageformat.h +++ b/public/bitmap/imageformat.h @@ -22,7 +22,9 @@ // don't bitch that inline functions aren't used!!!! +#ifdef _MSC_VER #pragma warning(disable : 4514) +#endif enum ImageFormat { @@ -266,8 +268,8 @@ enum struct ResampleInfo_t { - ResampleInfo_t() : m_flColorScale( 1.0f ), m_nFlags(0), m_flAlphaThreshhold(0.4f), - m_flAlphaHiFreqThreshhold(0.4f), m_nSrcDepth(1), m_nDestDepth(1) {} + ResampleInfo_t() : m_nSrcDepth(1), m_nDestDepth(1), m_flColorScale( 1.0f ), m_flAlphaThreshhold(0.4f), + m_flAlphaHiFreqThreshhold(0.4f), m_nFlags(0) {} unsigned char *m_pSrc; unsigned char *m_pDest; diff --git a/public/blockingudpsocket.cpp b/public/blockingudpsocket.cpp index e2c6ea95..dfb3f3fd 100644 --- a/public/blockingudpsocket.cpp +++ b/public/blockingudpsocket.cpp @@ -1,158 +1,158 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -//=============================================================================// - -#if !defined(_STATIC_LINKED) || defined(_SHARED_LIB) - - -#if defined(_WIN32) && !defined(_XBOX) -#include -#elif _LINUX -#define INVALID_SOCKET -1 -#define SOCKET_ERROR -1 -#include -#include -#include -#include -#define closesocket close -#elif defined(_XBOX) -#include "xbox/xbox_platform.h" -#include "xbox/xbox_win32stubs.h" -#endif - -#include "blockingudpsocket.h" -#include "tier0/vcrmode.h" - -class CBlockingUDPSocket::CImpl -{ -public: - struct sockaddr_in m_SocketIP; - fd_set m_FDSet; -}; - -CBlockingUDPSocket::CBlockingUDPSocket() : - m_cserIP(), - m_Socket( 0 ), - m_pImpl( new CImpl ) -{ - CreateSocket(); -} - -CBlockingUDPSocket::~CBlockingUDPSocket() -{ - delete m_pImpl; - closesocket( static_cast( m_Socket )); -} - -bool CBlockingUDPSocket::CreateSocket (void) -{ - struct sockaddr_in address; - - m_Socket = socket( PF_INET, SOCK_DGRAM, IPPROTO_UDP ); - if ( m_Socket == INVALID_SOCKET ) - { - return false; - } - - address = m_pImpl->m_SocketIP; - - if ( SOCKET_ERROR == bind( m_Socket, ( struct sockaddr * )&address, sizeof( address ) ) ) - { - return false; - } - -#ifdef _WIN32 - if ( m_pImpl->m_SocketIP.sin_addr.S_un.S_addr == INADDR_ANY ) - { - m_pImpl->m_SocketIP.sin_addr.S_un.S_addr = 0L; - } -#elif _LINUX - if ( m_pImpl->m_SocketIP.sin_addr.s_addr == INADDR_ANY ) - { - m_pImpl->m_SocketIP.sin_addr.s_addr = 0L; - } -#endif - - return true; -} - -bool CBlockingUDPSocket::WaitForMessage( float timeOutInSeconds ) -{ - struct timeval tv; - - FD_ZERO( &m_pImpl->m_FDSet ); - FD_SET( m_Socket, &m_pImpl->m_FDSet );//lint !e717 - - tv.tv_sec = (int)timeOutInSeconds; - float remainder = timeOutInSeconds - (int)timeOutInSeconds; - tv.tv_usec = (int)( remainder * 1000000 + 0.5f ); /* micro seconds */ - - if ( SOCKET_ERROR == select( ( int )m_Socket + 1, &m_pImpl->m_FDSet, NULL, NULL, &tv ) ) - { - return false; - } - - if ( FD_ISSET( m_Socket, &m_pImpl->m_FDSet) ) - { - return true; - } - - // Timed out - return false; -} - -unsigned int CBlockingUDPSocket::ReceiveSocketMessage( struct sockaddr_in *packet_from, unsigned char *buf, size_t bufsize ) -{ - memset( packet_from, 0, sizeof( *packet_from ) ); - - struct sockaddr fromaddress; - int fromlen = sizeof( fromaddress ); - - int packet_length = VCRHook_recvfrom - ( - m_Socket, - (char *)buf, - (int)bufsize, - 0, - &fromaddress, - &fromlen - ); - - if ( SOCKET_ERROR == packet_length ) - { - return 0; - } - - // In case it's parsed as a string - buf[ packet_length ] = 0; - - // Copy over the receive address - *packet_from = *( struct sockaddr_in * )&fromaddress; - - return ( unsigned int )packet_length; -} - -bool CBlockingUDPSocket::SendSocketMessage( const struct sockaddr_in & rRecipient, const unsigned char *buf, size_t bufsize ) -{ - // Send data - int bytesSent = sendto - ( - m_Socket, - (const char *)buf, - (int)bufsize, - 0, - reinterpret_cast< const sockaddr * >( &rRecipient ), - sizeof( rRecipient ) - ); - - if ( SOCKET_ERROR == bytesSent ) - { - return false; - } - - return true; -} - -#endif // !_STATIC_LINKED || _SHARED_LIB \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#if !defined(_STATIC_LINKED) || defined(_SHARED_LIB) + + +#if defined(_WIN32) && !defined(_XBOX) +#include +#elif _LINUX +#define INVALID_SOCKET -1 +#define SOCKET_ERROR -1 +#include +#include +#include +#include +#define closesocket close +#elif defined(_XBOX) +#include "xbox/xbox_platform.h" +#include "xbox/xbox_win32stubs.h" +#endif + +#include "blockingudpsocket.h" +#include "tier0/vcrmode.h" + +class CBlockingUDPSocket::CImpl +{ +public: + struct sockaddr_in m_SocketIP; + fd_set m_FDSet; +}; + +CBlockingUDPSocket::CBlockingUDPSocket() : + m_pImpl( new CImpl ), + m_cserIP(), + m_Socket( 0 ) +{ + CreateSocket(); +} + +CBlockingUDPSocket::~CBlockingUDPSocket() +{ + delete m_pImpl; + closesocket( static_cast( m_Socket )); +} + +bool CBlockingUDPSocket::CreateSocket (void) +{ + struct sockaddr_in address; + + m_Socket = socket( PF_INET, SOCK_DGRAM, IPPROTO_UDP ); + if ( m_Socket == INVALID_SOCKET ) + { + return false; + } + + address = m_pImpl->m_SocketIP; + + if ( SOCKET_ERROR == bind( m_Socket, ( struct sockaddr * )&address, sizeof( address ) ) ) + { + return false; + } + +#ifdef _WIN32 + if ( m_pImpl->m_SocketIP.sin_addr.S_un.S_addr == INADDR_ANY ) + { + m_pImpl->m_SocketIP.sin_addr.S_un.S_addr = 0L; + } +#elif _LINUX + if ( m_pImpl->m_SocketIP.sin_addr.s_addr == INADDR_ANY ) + { + m_pImpl->m_SocketIP.sin_addr.s_addr = 0L; + } +#endif + + return true; +} + +bool CBlockingUDPSocket::WaitForMessage( float timeOutInSeconds ) +{ + struct timeval tv; + + FD_ZERO( &m_pImpl->m_FDSet ); + FD_SET( m_Socket, &m_pImpl->m_FDSet );//lint !e717 + + tv.tv_sec = (int)timeOutInSeconds; + float remainder = timeOutInSeconds - (int)timeOutInSeconds; + tv.tv_usec = (int)( remainder * 1000000 + 0.5f ); /* micro seconds */ + + if ( SOCKET_ERROR == select( ( int )m_Socket + 1, &m_pImpl->m_FDSet, NULL, NULL, &tv ) ) + { + return false; + } + + if ( FD_ISSET( m_Socket, &m_pImpl->m_FDSet) ) + { + return true; + } + + // Timed out + return false; +} + +unsigned int CBlockingUDPSocket::ReceiveSocketMessage( struct sockaddr_in *packet_from, unsigned char *buf, size_t bufsize ) +{ + memset( packet_from, 0, sizeof( *packet_from ) ); + + struct sockaddr fromaddress; + int fromlen = sizeof( fromaddress ); + + int packet_length = VCRHook_recvfrom + ( + m_Socket, + (char *)buf, + (int)bufsize, + 0, + &fromaddress, + &fromlen + ); + + if ( SOCKET_ERROR == packet_length ) + { + return 0; + } + + // In case it's parsed as a string + buf[ packet_length ] = 0; + + // Copy over the receive address + *packet_from = *( struct sockaddr_in * )&fromaddress; + + return ( unsigned int )packet_length; +} + +bool CBlockingUDPSocket::SendSocketMessage( const struct sockaddr_in & rRecipient, const unsigned char *buf, size_t bufsize ) +{ + // Send data + int bytesSent = sendto + ( + m_Socket, + (const char *)buf, + (int)bufsize, + 0, + reinterpret_cast< const sockaddr * >( &rRecipient ), + sizeof( rRecipient ) + ); + + if ( SOCKET_ERROR == bytesSent ) + { + return false; + } + + return true; +} + +#endif // !_STATIC_LINKED || _SHARED_LIB diff --git a/public/blockingudpsocket.h b/public/blockingudpsocket.h index 2adb3383..b1dbdb8b 100644 --- a/public/blockingudpsocket.h +++ b/public/blockingudpsocket.h @@ -1,39 +1,39 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -//=============================================================================// - -#ifndef BLOCKINGUDPSOCKET_H -#define BLOCKINGUDPSOCKET_H -#ifdef _WIN32 -#pragma once -#endif - -#include "netadr.h" - -class CBlockingUDPSocket -{ -public: - explicit CBlockingUDPSocket(); - virtual ~CBlockingUDPSocket(); - - bool WaitForMessage( float timeOutInSeconds ); - unsigned int ReceiveSocketMessage( struct sockaddr_in *packet_from, unsigned char *buf, size_t bufsize ); - bool SendSocketMessage( const struct sockaddr_in& rRecipient, const unsigned char *buf, size_t bufsize ); - - bool IsValid() const { return m_Socket != 0; } - -protected: - bool CreateSocket (void); - - class CImpl; - CImpl *m_pImpl; - - netadr_t m_cserIP; - unsigned int m_Socket; - - -}; - -#endif // BLOCKINGUDPSOCKET_H +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#ifndef BLOCKINGUDPSOCKET_H +#define BLOCKINGUDPSOCKET_H +#ifdef _WIN32 +#pragma once +#endif + +#include "netadr.h" + +class CBlockingUDPSocket +{ +public: + explicit CBlockingUDPSocket(); + virtual ~CBlockingUDPSocket(); + + bool WaitForMessage( float timeOutInSeconds ); + unsigned int ReceiveSocketMessage( struct sockaddr_in *packet_from, unsigned char *buf, size_t bufsize ); + bool SendSocketMessage( const struct sockaddr_in& rRecipient, const unsigned char *buf, size_t bufsize ); + + bool IsValid() const { return m_Socket != 0; } + +protected: + bool CreateSocket (void); + + class CImpl; + CImpl *m_pImpl; + + netadr_t m_cserIP; + int m_Socket; + + +}; + +#endif // BLOCKINGUDPSOCKET_H diff --git a/public/bone_setup.cpp b/public/bone_setup.cpp index 6528f187..3724dd1e 100644 --- a/public/bone_setup.cpp +++ b/public/bone_setup.cpp @@ -1,4965 +1,4966 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -// -//=============================================================================// - -#include "tier0/dbg.h" -#include "mathlib.h" -#include "bone_setup.h" -#include - -#include "collisionutils.h" -#include "vstdlib/random.h" -#include "tier0/vprof.h" -#include "bone_accessor.h" -#include "bitvec.h" -#include "datamanager.h" -#include "convar.h" - -// memdbgon must be the last include file in a .cpp file!!! -#include "tier0/memdbgon.h" - -// ----------------------------------------------------------------- -CBoneCache *CBoneCache::CreateResource( const bonecacheparams_t ¶ms ) -{ - short studioToCachedIndex[MAXSTUDIOBONES]; - short cachedToStudioIndex[MAXSTUDIOBONES]; - int cachedBoneCount = 0; - for ( int i = 0; i < params.pStudioHdr->numbones(); i++ ) - { - // skip bones that aren't part of the boneMask (and aren't the root bone) - if (i != 0 && !(params.pStudioHdr->pBone(i)->flags & params.boneMask)) - { - studioToCachedIndex[i] = -1; - continue; - } - studioToCachedIndex[i] = cachedBoneCount; - cachedToStudioIndex[cachedBoneCount] = i; - cachedBoneCount++; - } - int tableSizeStudio = sizeof(short) * params.pStudioHdr->numbones(); - int tableSizeCached = sizeof(short) * cachedBoneCount; - int matrixSize = sizeof(matrix3x4_t) * cachedBoneCount; - int size = sizeof(CBoneCache) + tableSizeStudio + tableSizeCached + matrixSize; - - CBoneCache *pMem = (CBoneCache *)malloc( size ); - Construct( pMem ); - pMem->Init( params, size, studioToCachedIndex, cachedToStudioIndex, cachedBoneCount ); - return pMem; -} - -unsigned int CBoneCache::EstimatedSize( const bonecacheparams_t ¶ms ) -{ - // conservative estimate - max size - return params.pStudioHdr->numbones() * (sizeof(short) + sizeof(short) + sizeof(matrix3x4_t)); -} - -void CBoneCache::DestroyResource() -{ - free( this ); -} - - -CBoneCache::CBoneCache() -{ - m_size = 0; - m_cachedBoneCount = 0; -} - -void CBoneCache::Init( const bonecacheparams_t ¶ms, unsigned int size, short *pStudioToCached, short *pCachedToStudio, int cachedBoneCount ) -{ - m_cachedBoneCount = cachedBoneCount; - m_size = size; - m_timeValid = params.curtime; - m_boneMask = params.boneMask; - - int studioTableSize = params.pStudioHdr->numbones() * sizeof(short); - m_cachedToStudioOffset = studioTableSize; - memcpy( StudioToCached(), pStudioToCached, studioTableSize ); - - int cachedTableSize = cachedBoneCount * sizeof(short); - memcpy( CachedToStudio(), pCachedToStudio, cachedTableSize ); - - m_matrixOffset = m_cachedToStudioOffset + cachedTableSize; - - UpdateBones( params.pBoneToWorld, params.pStudioHdr->numbones(), params.curtime ); -} - -void CBoneCache::UpdateBones( const matrix3x4_t *pBoneToWorld, int numbones, float curtime ) -{ - matrix3x4_t *pBones = BoneArray(); - const short *pCachedToStudio = CachedToStudio(); - - for ( int i = 0; i < m_cachedBoneCount; i++ ) - { - int index = pCachedToStudio[i]; - MatrixCopy( pBoneToWorld[index], pBones[i] ); - } - m_timeValid = curtime; -} - -matrix3x4_t *CBoneCache::GetCachedBone( int studioIndex ) -{ - int cachedIndex = StudioToCached()[studioIndex]; - if ( cachedIndex >= 0 ) - { - return BoneArray() + cachedIndex; - } - return NULL; -} - -void CBoneCache::ReadCachedBones( matrix3x4_t *pBoneToWorld ) -{ - matrix3x4_t *pBones = BoneArray(); - const short *pCachedToStudio = CachedToStudio(); - for ( int i = 0; i < m_cachedBoneCount; i++ ) - { - MatrixCopy( pBones[i], pBoneToWorld[pCachedToStudio[i]] ); - } -} - -void CBoneCache::ReadCachedBonePointers( matrix3x4_t **bones, int numbones ) -{ - memset( bones, 0, sizeof(matrix3x4_t *) * numbones ); - matrix3x4_t *pBones = BoneArray(); - const short *pCachedToStudio = CachedToStudio(); - for ( int i = 0; i < m_cachedBoneCount; i++ ) - { - bones[pCachedToStudio[i]] = pBones + i; - } -} - -bool CBoneCache::IsValid( float time, float dt ) -{ - if ( time - m_timeValid <= dt ) - return true; - return false; -} - - -// private functions -matrix3x4_t *CBoneCache::BoneArray() -{ - return (matrix3x4_t *)( (char *)(this+1) + m_matrixOffset ); -} - -short *CBoneCache::StudioToCached() -{ - return (short *)( (char *)(this+1) ); -} - -short *CBoneCache::CachedToStudio() -{ - return (short *)( (char *)(this+1) + m_cachedToStudioOffset ); -} - -// Construct a singleton -static CDataManager g_StudioBoneCache( 16 * 1024L ); - -CBoneCache *Studio_GetBoneCache( memhandle_t cacheHandle ) -{ - return g_StudioBoneCache.GetResource_NoLock( cacheHandle ); -} - -memhandle_t Studio_CreateBoneCache( bonecacheparams_t ¶ms ) -{ - return g_StudioBoneCache.CreateResource( params ); -} - -void Studio_DestroyBoneCache( memhandle_t cacheHandle ) -{ - g_StudioBoneCache.DestroyResource( cacheHandle ); -} - -void Studio_InvalidateBoneCache( memhandle_t cacheHandle ) -{ - CBoneCache *pCache = g_StudioBoneCache.GetResource_NoLock( cacheHandle ); - if ( pCache ) - { - pCache->m_timeValid = -1.0f; - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- - -void BuildBoneChain( - const CStudioHdr *pStudioHdr, - const matrix3x4_t &rootxform, - const Vector pos[], - const Quaternion q[], - int iBone, - matrix3x4_t *pBoneToWorld ) -{ - CBoneBitList boneComputed; - BuildBoneChain( pStudioHdr, rootxform, pos, q, iBone, pBoneToWorld, boneComputed ); - return; -} - - - -//----------------------------------------------------------------------------- -// Purpose: return a sub frame rotation for a single bone -//----------------------------------------------------------------------------- - - -void ExtractAnimValue( int frame, - mstudioanimvalue_t *panimvalue, - float scale, - float &v1, float &v2 ) -{ - if (!panimvalue) - { - v1 = v2 = 0; - return; - } - - int k = frame; - - while (panimvalue->num.total <= k) - { - k -= panimvalue->num.total; - panimvalue += panimvalue->num.valid + 1; - if ( panimvalue->num.total == 0 ) - { - v1 = v2 = 0; - return; - } - } - // Bah, missing blend! - if (panimvalue->num.valid > k) - { - v1 = panimvalue[k+1].value * scale; - - if (panimvalue->num.valid > k + 1) - { - v2 = panimvalue[k+2].value * scale; - } - else - { - if (panimvalue->num.total > k + 1) - v2 = v1; - else - v2 = panimvalue[panimvalue->num.valid+2].value * scale; - } - } - else - { - v1 = panimvalue[panimvalue->num.valid].value * scale; - if (panimvalue->num.total > k + 1) - { - v2 = v1; - } - else - { - v2 = panimvalue[panimvalue->num.valid + 2].value * scale; - } - } -} - - -//----------------------------------------------------------------------------- -// Purpose: return a sub frame rotation for a single bone -//----------------------------------------------------------------------------- -void CalcBoneQuaternion( int frame, float s, - const mstudiobone_t *pbone, const mstudioanim_t *panim, Quaternion &q ) -{ - if (panim->flags & STUDIO_ANIM_RAWROT) - { - q = *(panim->pQuat()); - Assert( q.IsValid() ); - return; - } - else if (!(panim->flags & STUDIO_ANIM_ANIMROT)) - { - if (panim->flags & STUDIO_ANIM_DELTA) - { - q.Init( 0.0f, 0.0f, 0.0f, 1.0f ); - } - else - { - q = pbone->quat; - } - return; - } - - Quaternion q1, q2; - RadianEuler angle1, angle2; - mstudioanim_valueptr_t *pValuesPtr = panim->pRotV(); - - ExtractAnimValue( frame, pValuesPtr->pAnimvalue( 0 ), pbone->rotscale.x, angle1.x, angle2.x ); - ExtractAnimValue( frame, pValuesPtr->pAnimvalue( 1 ), pbone->rotscale.y, angle1.y, angle2.y ); - ExtractAnimValue( frame, pValuesPtr->pAnimvalue( 2 ), pbone->rotscale.z, angle1.z, angle2.z ); - - if (!(panim->flags & STUDIO_ANIM_DELTA)) - { - angle1.x = angle1.x + pbone->rot.x; - angle1.y = angle1.y + pbone->rot.y; - angle1.z = angle1.z + pbone->rot.z; - angle2.x = angle2.x + pbone->rot.x; - angle2.y = angle2.y + pbone->rot.y; - angle2.z = angle2.z + pbone->rot.z; - } - - Assert( angle1.IsValid() && angle2.IsValid() ); - if (angle1.x != angle2.x || angle1.y != angle2.y || angle1.z != angle2.z) - { - AngleQuaternion( angle1, q1 ); - AngleQuaternion( angle2, q2 ); - QuaternionBlend( q1, q2, s, q ); - } - else - { - AngleQuaternion( angle1, q ); - } - - Assert( q.IsValid() ); - - // align to unified bone - if (!(panim->flags & STUDIO_ANIM_DELTA) && (pbone->flags & BONE_FIXED_ALIGNMENT)) - { - QuaternionAlign( pbone->qAlignment, q, q ); - } -} - - -//----------------------------------------------------------------------------- -// Purpose: return a sub frame position for a single bone -//----------------------------------------------------------------------------- -void CalcBonePosition( int frame, float s, - const mstudiobone_t *pbone, const mstudioanim_t *panim, Vector &pos ) -{ - if (panim->flags & STUDIO_ANIM_RAWPOS) - { - pos = *(panim->pPos()); - Assert( pos.IsValid() ); - - return; - } - else if (!(panim->flags & STUDIO_ANIM_ANIMPOS)) - { - if (panim->flags & STUDIO_ANIM_DELTA) - { - pos.Init( 0.0f, 0.0f, 0.0f ); - } - else - { - pos = pbone->pos; - } - return; - } - - mstudioanim_valueptr_t *pPosV = panim->pPosV(); - int j; - float v1, v2; - - for (j = 0; j < 3; j++) - { - ExtractAnimValue( frame, pPosV->pAnimvalue( j ), pbone->posscale[j], v1, v2 ); - pos[j] = v1 * (1.0 - s) + v2 * s; - } - - if (!(panim->flags & STUDIO_ANIM_DELTA)) - { - pos.x = pos.x + pbone->pos.x; - pos.y = pos.y + pbone->pos.y; - pos.z = pos.z + pbone->pos.z; - } - - Assert( pos.IsValid() ); -} - - -void SetupSingleBoneMatrix( - CStudioHdr *pOwnerHdr, - int nSequence, - int iFrame, - int iBone, - matrix3x4_t &mBoneLocal ) -{ - mstudioseqdesc_t &seqdesc = pOwnerHdr->pSeqdesc( nSequence ); - mstudioanimdesc_t &animdesc = pOwnerHdr->pAnimdesc( seqdesc.anim( 0, 0 ) ); - mstudioanim_t *panim = animdesc.pAnim( ); - float s = 0; - mstudiobone_t *pbone = pOwnerHdr->pBone( iBone ); - - Quaternion boneQuat; - Vector bonePos; - - // search for bone - while (panim && panim->bone != iBone) - { - panim = panim->pNext(); - } - - // look up animation if found, if not, initialize - if (panim && seqdesc.weight(iBone) > 0) - { - CalcBoneQuaternion( iFrame, s, pbone, panim, boneQuat ); - CalcBonePosition ( iFrame, s, pbone, panim, bonePos ); - } - else if (animdesc.flags & STUDIO_DELTA) - { - boneQuat.Init( 0.0f, 0.0f, 0.0f, 1.0f ); - bonePos.Init( 0.0f, 0.0f, 0.0f ); - } - else - { - boneQuat = pbone->quat; - bonePos = pbone->pos; - } - - QuaternionMatrix( boneQuat, bonePos, mBoneLocal ); -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -static void CalcVirtualAnimation( virtualmodel_t *pVModel, const CStudioHdr *pStudioHdr, Vector *pos, Quaternion *q, - mstudioseqdesc_t &seqdesc, int sequence, int animation, - float cycle, int boneMask ) -{ - int i, j, k; - - const mstudiobone_t *pbone; - const virtualgroup_t *pSeqGroup; - const studiohdr_t *pSeqStudioHdr; - const mstudiobone_t *pSeqbone; - const mstudioanim_t *panim; - const studiohdr_t *pAnimStudioHdr; - const mstudiobone_t *pAnimbone; - const virtualgroup_t *pAnimGroup; - - pSeqGroup = pVModel->pSeqGroup( sequence ); - int baseanimation = pStudioHdr->iRelativeAnim( sequence, animation ); - mstudioanimdesc_t &animdesc = pStudioHdr->pAnimdesc( baseanimation ); - pSeqStudioHdr = pStudioHdr->pSeqStudioHdr( sequence ); - pSeqbone = pSeqStudioHdr->pBone( 0 ); - pAnimGroup = pVModel->pAnimGroup( baseanimation ); - panim = animdesc.pAnim( ); - pAnimStudioHdr = pStudioHdr->pAnimStudioHdr( baseanimation ); - pAnimbone = pAnimStudioHdr->pBone( 0 ); - - int iFrame; - float s; - - float fFrame = cycle * (animdesc.numframes - 1); - - iFrame = (int)fFrame; - s = (fFrame - iFrame); - - float *pweight = seqdesc.pBoneweight( 0 ); - pbone = pStudioHdr->pBone( 0 ); - - for (i = 0; i < pStudioHdr->numbones(); i++) - { - if (pbone[i].flags & boneMask) - { - int j = pSeqGroup->boneMap[i]; - if (j >= 0 && pweight[j] > 0.0f) - { - if (animdesc.flags & STUDIO_DELTA) - { - q[i].Init( 0.0f, 0.0f, 0.0f, 1.0f ); - pos[i].Init( 0.0f, 0.0f, 0.0f ); - } - else - { - q[i] = pSeqbone[j].quat; - pos[i] = pSeqbone[j].pos; - } - } - } - } - - // if the animation isn't available, look for the zero frame cache - if (!panim) - { - byte *pData = pAnimStudioHdr->pZeroframeCache( pVModel->m_anim[baseanimation].index ); - - if (pData) - { - // Msg("zeroframe %s\n", animdesc.pszName() ); - for (j = 0; j < pAnimStudioHdr->numbones; j++) - { - i = pAnimGroup->masterBone[j]; - - if (pAnimbone[j].flags & BONE_HAS_SAVEFRAME_POS) - { - if ((i >= 0) && (pbone[i].flags & boneMask)) - { - pos[i] = *(Vector48 *)pData; - } - pData += sizeof( Vector48 ); - } - if (!(animdesc.flags & STUDIO_DELTA) && (pAnimbone[j].flags & BONE_HAS_SAVEFRAME_ROT) != 0) - { - if ((i >= 0) && (pbone[i].flags & boneMask)) - { - q[i] = *(Quaternion32 *)pData; - } - pData += sizeof( Quaternion32 ); - } - } - return; - } - } - - // FIXME: change encoding so that bone -1 is never the case - while (panim && panim->bone < 255) - { - j = pAnimGroup->masterBone[panim->bone]; - if (j >= 0 && (pbone[j].flags & boneMask)) - { - k = pSeqGroup->boneMap[j]; - - if (k >= 0 && pweight[k] > 0.0f) - { - CalcBoneQuaternion( iFrame, s, &pAnimbone[panim->bone], panim, q[j] ); - CalcBonePosition ( iFrame, s, &pAnimbone[panim->bone], panim, pos[j] ); - } - } - panim = panim->pNext(); - } -} - - - - -static void CalcAnimation( const CStudioHdr *pStudioHdr, Vector *pos, Quaternion *q, - mstudioseqdesc_t &seqdesc, - int sequence, int animation, - float cycle, int boneMask ) -{ - int i; - - virtualmodel_t *pVModel = pStudioHdr->GetVirtualModel(); - - if (pVModel) - { - CalcVirtualAnimation( pVModel, pStudioHdr, pos, q, seqdesc, sequence, animation, cycle, boneMask ); - return; - } - - mstudioanimdesc_t &animdesc = pStudioHdr->pAnimdesc( animation ); - mstudiobone_t *pbone = pStudioHdr->pBone( 0 ); - mstudioanim_t *panim = animdesc.pAnim( ); - - int iFrame; - float s; - - float fFrame = cycle * (animdesc.numframes - 1); - - iFrame = (int)fFrame; - s = (fFrame - iFrame); - - float *pweight = seqdesc.pBoneweight( 0 ); - - // if the animation isn't available, look for the zero frame cache - if (!panim) - { - byte *pData = pStudioHdr->pZeroframeCache( animation ); - - if (pData) - { - // Msg("zeroframe %s\n", animdesc.pszName() ); - for (i = 0; i < pStudioHdr->numbones(); i++, pbone++, pweight++) - { - if (*pweight > 0 && (pbone->flags & boneMask)) - { - if (animdesc.flags & STUDIO_DELTA) - { - q[i].Init( 0.0f, 0.0f, 0.0f, 1.0f ); - pos[i].Init( 0.0f, 0.0f, 0.0f ); - } - else - { - q[i] = pbone->quat; - pos[i] = pbone->pos; - } - } - - if (pbone->flags & BONE_HAS_SAVEFRAME_POS) - { - if (*pweight > 0 && (pbone->flags & boneMask)) - { - pos[i] = *(Vector48 *)pData; - Assert( pos[i].IsValid() ); - } - pData += sizeof( Vector48 ); - } - if (!(animdesc.flags & STUDIO_DELTA) && (pbone->flags & BONE_HAS_SAVEFRAME_ROT) != 0) - { - if (*pweight > 0 && (pbone->flags & boneMask)) - { - q[i] = *(Quaternion32 *)pData; - Assert( q[i].IsValid() ); - } - pData += sizeof( Quaternion32 ); - } - } - return; - } - } - - // BUGBUG: the sequence, the anim, and the model can have all different bone mappings. - for (i = 0; i < pStudioHdr->numbones(); i++, pbone++, pweight++) - { - if (panim && panim->bone == i) - { - if (*pweight > 0 && (pbone->flags & boneMask)) - { - CalcBoneQuaternion( iFrame, s, pbone, panim, q[i] ); - CalcBonePosition ( iFrame, s, pbone, panim, pos[i] ); - } - panim = panim->pNext(); - } - else if (*pweight > 0 && (pbone->flags & boneMask)) - { - if (animdesc.flags & STUDIO_DELTA) - { - q[i].Init( 0.0f, 0.0f, 0.0f, 1.0f ); - pos[i].Init( 0.0f, 0.0f, 0.0f ); - } - else - { - q[i] = pbone->quat; - pos[i] = pbone->pos; - } - } - } -} - - - -// qt = ( s * p ) * q -void QuaternionSM( float s, const Quaternion &p, const Quaternion &q, Quaternion &qt ) -{ - Quaternion p1, q1; - - QuaternionScale( p, s, p1 ); - QuaternionMult( p1, q, q1 ); - QuaternionNormalize( q1 ); - qt[0] = q1[0]; - qt[1] = q1[1]; - qt[2] = q1[2]; - qt[3] = q1[3]; -} - -// qt = p * ( s * q ) -void QuaternionMA( const Quaternion &p, float s, const Quaternion &q, Quaternion &qt ) -{ - Quaternion p1, q1; - - QuaternionScale( q, s, q1 ); - QuaternionMult( p, q1, p1 ); - QuaternionNormalize( p1 ); - qt[0] = p1[0]; - qt[1] = p1[1]; - qt[2] = p1[2]; - qt[3] = p1[3]; -} - - -// qt = p * ( s * q ) -void QuaternionAccumulate( const Quaternion &p, float s, const Quaternion &q, Quaternion &qt ) -{ - Quaternion q2; - QuaternionAlign( p, q, q2 ); - - qt[0] = p[0] + s * q2[0]; - qt[1] = p[1] + s * q2[1]; - qt[2] = p[2] + s * q2[2]; - qt[3] = p[3] + s * q2[3]; -} - - - -//----------------------------------------------------------------------------- -// Purpose: blend together in world space q1,pos1 with q2,pos2. Return result in q1,pos1. -// 0 returns q1, pos1. 1 returns q2, pos2 -//----------------------------------------------------------------------------- - -void WorldSpaceSlerp( - const CStudioHdr *pStudioHdr, - Quaternion q1[MAXSTUDIOBONES], - Vector pos1[MAXSTUDIOBONES], - mstudioseqdesc_t &seqdesc, - int sequence, - const Quaternion q2[MAXSTUDIOBONES], - const Vector pos2[MAXSTUDIOBONES], - float s, - int boneMask ) -{ - int i, j; - float s1; // weight of parent for q2, pos2 - float s2; // weight for q2, pos2 - - // make fake root transform - matrix3x4_t rootXform; - SetIdentityMatrix( rootXform ); - - // matrices for q2, pos2 - static matrix3x4_t srcBoneToWorld[MAXSTUDIOBONES]; - CBoneBitList srcBoneComputed; - - static matrix3x4_t destBoneToWorld[MAXSTUDIOBONES]; - CBoneBitList destBoneComputed; - - static matrix3x4_t targetBoneToWorld[MAXSTUDIOBONES]; - CBoneBitList targetBoneComputed; - - virtualmodel_t *pVModel = pStudioHdr->GetVirtualModel(); - const virtualgroup_t *pSeqGroup = NULL; - if (pVModel) - { - pSeqGroup = pVModel->pSeqGroup( sequence ); - } - - mstudiobone_t *pbone = pStudioHdr->pBone( 0 ); - - for (i = 0; i < pStudioHdr->numbones(); i++) - { - // skip unused bones - if (!(pbone[i].flags & boneMask)) - { - continue; - } - - int n = pbone[i].parent; - s1 = 0.0; - if (pSeqGroup) - { - j = pSeqGroup->boneMap[i]; - if (j >= 0) - { - s2 = s * seqdesc.weight( j ); // blend in based on this bones weight - if (n != -1) - { - s1 = s * seqdesc.weight( pSeqGroup->boneMap[n] ); - } - } - else - { - s2 = 0.0; - } - } - else - { - s2 = s * seqdesc.weight( i ); // blend in based on this bones weight - if (n != -1) - { - s1 = s * seqdesc.weight( n ); - } - } - - if (s1 == 1.0 && s2 == 1.0) - { - pos1[i] = pos2[i]; - q1[i] = q2[i]; - } - else if (s2 > 0.0) - { - Quaternion srcQ, destQ; - Vector srcPos, destPos; - Quaternion targetQ; - Vector targetPos; - Vector tmp; - - BuildBoneChain( pStudioHdr, rootXform, pos1, q1, i, destBoneToWorld, destBoneComputed ); - BuildBoneChain( pStudioHdr, rootXform, pos2, q2, i, srcBoneToWorld, srcBoneComputed ); - - MatrixAngles( destBoneToWorld[i], destQ, destPos ); - MatrixAngles( srcBoneToWorld[i], srcQ, srcPos ); - - QuaternionSlerp( destQ, srcQ, s2, targetQ ); - AngleMatrix( targetQ, destPos, targetBoneToWorld[i] ); - - // back solve - if (n == -1) - { - MatrixAngles( targetBoneToWorld[i], q1[i], tmp ); - } - else - { - matrix3x4_t worldToBone; - MatrixInvert( targetBoneToWorld[n], worldToBone ); - - matrix3x4_t local; - ConcatTransforms( worldToBone, targetBoneToWorld[i], local ); - MatrixAngles( local, q1[i], tmp ); - - // blend bone lengths (local space) - pos1[i] = Lerp( s2, pos1[i], pos2[i] ); - } - } - } -} - - - -//----------------------------------------------------------------------------- -// Purpose: blend together q1,pos1 with q2,pos2. Return result in q1,pos1. -// 0 returns q1, pos1. 1 returns q2, pos2 -//----------------------------------------------------------------------------- -void SlerpBones( - const CStudioHdr *pStudioHdr, - Quaternion q1[MAXSTUDIOBONES], - Vector pos1[MAXSTUDIOBONES], - mstudioseqdesc_t &seqdesc, // source of q2 and pos2 - int sequence, - const Quaternion q2[MAXSTUDIOBONES], - const Vector pos2[MAXSTUDIOBONES], - float s, - int boneMask ) -{ - if (s <= 0.0f) - { - return; - } - else if (s > 1.0f) - { - s = 1.0f; - } - - if (seqdesc.flags & STUDIO_WORLD) - { - WorldSpaceSlerp( pStudioHdr, q1, pos1, seqdesc, sequence, q2, pos2, s, boneMask ); - return; - } - - int i, j; - Quaternion q3, q4; - float s1, s2; - - virtualmodel_t *pVModel = pStudioHdr->GetVirtualModel(); - const virtualgroup_t *pSeqGroup = NULL; - if (pVModel) - { - pSeqGroup = pVModel->pSeqGroup( sequence ); - } - - mstudiobone_t *pbone = pStudioHdr->pBone( 0 ); - - if (seqdesc.flags & STUDIO_DELTA) - { - for (i = 0; i < pStudioHdr->numbones(); i++) - { - // skip unused bones - if (!(pbone[i].flags & boneMask)) - { - continue; - } - - if (pSeqGroup) - { - j = pSeqGroup->boneMap[i]; - if (j >= 0) - { - s2 = s * seqdesc.weight( j ); // blend in based on this bones weight - } - else - { - s2 = 0.0; - } - } - else - { - s2 = s * seqdesc.weight( i ); // blend in based on this bones weight - } - - if (s2 > 0.0) - { - if (seqdesc.flags & STUDIO_POST) - { - QuaternionMA( q1[i], s2, q2[i], q1[i] ); - - // FIXME: are these correct? - pos1[i][0] = pos1[i][0] + pos2[i][0] * s2; - pos1[i][1] = pos1[i][1] + pos2[i][1] * s2; - pos1[i][2] = pos1[i][2] + pos2[i][2] * s2; - } - else - { - QuaternionSM( s2, q2[i], q1[i], q1[i] ); - - // FIXME: are these correct? - pos1[i][0] = pos1[i][0] + pos2[i][0] * s2; - pos1[i][1] = pos1[i][1] + pos2[i][1] * s2; - pos1[i][2] = pos1[i][2] + pos2[i][2] * s2; - } - } - } - } - else - { - for (i = 0; i < pStudioHdr->numbones(); i++) - { - // skip unused bones - if (!(pbone[i].flags & boneMask)) - { - continue; - } - - if (pSeqGroup) - { - j = pSeqGroup->boneMap[i]; - if (j >= 0) - { - s2 = s * seqdesc.weight( j ); // blend in based on this bones weight - } - else - { - s2 = 0.0; - } - } - else - { - s2 = s * seqdesc.weight( i ); // blend in based on this animations weights - } - if (s2 > 0.0) - { - s1 = 1.0 - s2; - - if (pbone[i].flags & BONE_FIXED_ALIGNMENT) - { - QuaternionSlerpNoAlign( q2[i], q1[i], s1, q3 ); - } - else - { - QuaternionSlerp( q2[i], q1[i], s1, q3 ); - } - q1[i][0] = q3[0]; - q1[i][1] = q3[1]; - q1[i][2] = q3[2]; - q1[i][3] = q3[3]; - pos1[i][0] = pos1[i][0] * s1 + pos2[i][0] * s2; - pos1[i][1] = pos1[i][1] * s1 + pos2[i][1] * s2; - pos1[i][2] = pos1[i][2] * s1 + pos2[i][2] * s2; - } - } - } -} - - - -//----------------------------------------------------------------------------- -// Purpose: Inter-animation blend. Assumes both types are identical. -// blend together q1,pos1 with q2,pos2. Return result in q1,pos1. -// 0 returns q1, pos1. 1 returns q2, pos2 -//----------------------------------------------------------------------------- -void BlendBones( - const CStudioHdr *pStudioHdr, - Quaternion q1[MAXSTUDIOBONES], - Vector pos1[MAXSTUDIOBONES], - mstudioseqdesc_t &seqdesc, - int sequence, - const Quaternion q2[MAXSTUDIOBONES], - const Vector pos2[MAXSTUDIOBONES], - float s, - int boneMask ) -{ - int i, j; - Quaternion q3; - - virtualmodel_t *pVModel = pStudioHdr->GetVirtualModel(); - const virtualgroup_t *pSeqGroup = NULL; - if (pVModel) - { - pSeqGroup = pVModel->pSeqGroup( sequence ); - } - - mstudiobone_t *pbone = pStudioHdr->pBone( 0 ); - - if (s <= 0) - { - Assert(0); // shouldn't have been called - return; - } - else if (s >= 1.0) - { - Assert(0); // shouldn't have been called - for (i = 0; i < pStudioHdr->numbones(); i++) - { - // skip unused bones - if (!(pbone[i].flags & boneMask)) - { - continue; - } - - if (pSeqGroup) - { - j = pSeqGroup->boneMap[i]; - } - else - { - j = i; - } - - if (j >= 0 && seqdesc.weight( j ) > 0.0) - { - q1[i] = q2[i]; - pos1[i] = pos2[i]; - } - } - return; - } - - float s2 = s; - float s1 = 1.0 - s2; - - for (i = 0; i < pStudioHdr->numbones(); i++) - { - // skip unused bones - if (!(pbone[i].flags & boneMask)) - { - continue; - } - - if (pSeqGroup) - { - j = pSeqGroup->boneMap[i]; - } - else - { - j = i; - } - - if (j >= 0 && seqdesc.weight( j ) > 0.0) - { - if (pbone[i].flags & BONE_FIXED_ALIGNMENT) - { - QuaternionBlendNoAlign( q2[i], q1[i], s1, q3 ); - } - else - { - QuaternionBlend( q2[i], q1[i], s1, q3 ); - } - q1[i][0] = q3[0]; - q1[i][1] = q3[1]; - q1[i][2] = q3[2]; - q1[i][3] = q3[3]; - pos1[i][0] = pos1[i][0] * s1 + pos2[i][0] * s2; - pos1[i][1] = pos1[i][1] * s1 + pos2[i][1] * s2; - pos1[i][2] = pos1[i][2] * s1 + pos2[i][2] * s2; - } - } -} - - - -//----------------------------------------------------------------------------- -// Purpose: Scale a set of bones. Must be of type delta -//----------------------------------------------------------------------------- -void ScaleBones( - const CStudioHdr *pStudioHdr, - Quaternion q1[MAXSTUDIOBONES], - Vector pos1[MAXSTUDIOBONES], - int sequence, - float s, - int boneMask ) -{ - int i, j; - Quaternion q3; - - mstudioseqdesc_t &seqdesc = pStudioHdr->pSeqdesc( sequence ); - - virtualmodel_t *pVModel = pStudioHdr->GetVirtualModel(); - const virtualgroup_t *pSeqGroup = NULL; - if (pVModel) - { - pSeqGroup = pVModel->pSeqGroup( sequence ); - } - - mstudiobone_t *pbone = pStudioHdr->pBone( 0 ); - - float s2 = s; - float s1 = 1.0 - s2; - - for (i = 0; i < pStudioHdr->numbones(); i++) - { - // skip unused bones - if (!(pbone[i].flags & boneMask)) - { - continue; - } - - if (pSeqGroup) - { - j = pSeqGroup->boneMap[i]; - } - else - { - j = i; - } - - if (j >= 0 && seqdesc.weight( j ) > 0.0) - { - QuaternionIdentityBlend( q1[i], s1, q1[i] ); - VectorScale( pos1[i], s2, pos1[i] ); - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: resolve a global pose parameter to the specific setting for this sequence -//----------------------------------------------------------------------------- -void Studio_LocalPoseParameter( const CStudioHdr *pStudioHdr, const float poseParameter[], mstudioseqdesc_t &seqdesc, int iSequence, int iLocalIndex, float &flSetting, int &index ) -{ - int iPose = pStudioHdr->GetSharedPoseParameter( iSequence, seqdesc.paramindex[iLocalIndex] ); - - if (iPose == -1) - { - flSetting = 0; - index = 0; - return; - } - - const mstudioposeparamdesc_t &Pose = pStudioHdr->pPoseParameter( iPose ); - - float flValue = poseParameter[iPose]; - - if (Pose.loop) - { - float wrap = (Pose.start + Pose.end) / 2.0 + Pose.loop / 2.0; - float shift = Pose.loop - wrap; - - flValue = flValue - Pose.loop * floor((flValue + shift) / Pose.loop); - } - - if (seqdesc.posekeyindex == 0) - { - float flLocalStart = ((float)seqdesc.paramstart[iLocalIndex] - Pose.start) / (Pose.end - Pose.start); - float flLocalEnd = ((float)seqdesc.paramend[iLocalIndex] - Pose.start) / (Pose.end - Pose.start); - - // convert into local range - flSetting = (flValue - flLocalStart) / (flLocalEnd - flLocalStart); - - // clamp. This shouldn't ever need to happen if it's looping. - if (flSetting < 0) - flSetting = 0; - if (flSetting > 1) - flSetting = 1; - - index = 0; - if (seqdesc.groupsize[iLocalIndex] > 2 ) - { - // estimate index - index = (int)(flSetting * (seqdesc.groupsize[iLocalIndex] - 1)); - if (index == seqdesc.groupsize[iLocalIndex] - 1) index = seqdesc.groupsize[iLocalIndex] - 2; - flSetting = flSetting * (seqdesc.groupsize[iLocalIndex] - 1) - index; - } - } - else - { - flValue = flValue * (Pose.end - Pose.start) + Pose.start; - index = 0; - - // FIXME: this needs to be 2D - // FIXME: this shouldn't be a linear search - - while (1) - { - flSetting = (flValue - seqdesc.poseKey( iLocalIndex, index )) / (seqdesc.poseKey( iLocalIndex, index + 1 ) - seqdesc.poseKey( iLocalIndex, index )); - /* - if (index > 0 && flSetting < 0.0) - { - index--; - continue; - } - else - */ - if (index < seqdesc.groupsize[iLocalIndex] - 2 && flSetting > 1.0) - { - index++; - continue; - } - break; - } - - // clamp. - if (flSetting < 0.0f) - flSetting = 0.0f; - if (flSetting > 1.0f) - flSetting = 1.0f; - } -} - -void Studio_CalcBoneToBoneTransform( const CStudioHdr *pStudioHdr, int inputBoneIndex, int outputBoneIndex, matrix3x4_t& matrixOut ) -{ - mstudiobone_t *pbone = pStudioHdr->pBone( inputBoneIndex ); - - matrix3x4_t inputToPose; - MatrixInvert( pbone->poseToBone, inputToPose ); - ConcatTransforms( pStudioHdr->pBone( outputBoneIndex )->poseToBone, inputToPose, matrixOut ); -} - -//----------------------------------------------------------------------------- -// Purpose: calculate a pose for a single sequence -//----------------------------------------------------------------------------- -void InitPose( - const CStudioHdr *pStudioHdr, - Vector pos[], - Quaternion q[] - ) -{ - for (int i = 0; i < pStudioHdr->numbones(); i++) - { - mstudiobone_t *pbone = pStudioHdr->pBone( i ); - - pos[i] = pbone->pos; - q[i] = pbone->quat; - } -} - - -inline bool PoseIsAllZeros( - const CStudioHdr *pStudioHdr, - int sequence, - mstudioseqdesc_t &seqdesc, - int i0, - int i1 - ) -{ - int baseanim; - - // remove "zero" positional blends - baseanim = pStudioHdr->iRelativeAnim( sequence, seqdesc.anim(i0 ,i1 ) ); - mstudioanimdesc_t &anim = pStudioHdr->pAnimdesc( baseanim ); - return (anim.flags & STUDIO_ALLZEROS) != 0; -} - - -//----------------------------------------------------------------------------- -// Purpose: calculate a pose for a single sequence -//----------------------------------------------------------------------------- -bool CalcPoseSingle( - const CStudioHdr *pStudioHdr, - Vector pos[], - Quaternion q[], - mstudioseqdesc_t &seqdesc, - int sequence, - float cycle, - const float poseParameter[], - int boneMask, - float flTime - ) -{ - ASSERT_NO_REENTRY(); - - static Vector pos2[MAXSTUDIOBONES]; - static Quaternion q2[MAXSTUDIOBONES]; - static Vector pos3[MAXSTUDIOBONES]; - static Quaternion q3[MAXSTUDIOBONES]; - - if (sequence >= pStudioHdr->GetNumSeq()) - { - sequence = 0; - seqdesc = pStudioHdr->pSeqdesc( sequence ); - } - - - int i0 = 0, i1 = 0; - float s0 = 0, s1 = 0; - - Studio_LocalPoseParameter( pStudioHdr, poseParameter, seqdesc, sequence, 0, s0, i0 ); - Studio_LocalPoseParameter( pStudioHdr, poseParameter, seqdesc, sequence, 1, s1, i1 ); - - - if (seqdesc.flags & STUDIO_REALTIME) - { - float cps = Studio_CPS( pStudioHdr, seqdesc, sequence, poseParameter ); - cycle = flTime * cps; - cycle = cycle - (int)cycle; - } - else if (cycle < 0 || cycle >= 1) - { - if (seqdesc.flags & STUDIO_LOOPING) - { - cycle = cycle - (int)cycle; - if (cycle < 0) cycle += 1; - } - else - { - cycle = max( 0.0, min( cycle, 0.9999 ) ); - } - } - - if (s0 < 0.001) - { - if (s1 < 0.001) - { - if (PoseIsAllZeros( pStudioHdr, sequence, seqdesc, i0, i1 )) - return false; - CalcAnimation( pStudioHdr, pos, q, seqdesc, sequence, seqdesc.anim( i0 , i1 ), cycle, boneMask ); - } - else if (s1 > 0.999) - { - CalcAnimation( pStudioHdr, pos, q, seqdesc, sequence, seqdesc.anim( i0 , i1+1 ), cycle, boneMask ); - } - else - { - CalcAnimation( pStudioHdr, pos, q, seqdesc, sequence, seqdesc.anim( i0 , i1 ), cycle, boneMask ); - CalcAnimation( pStudioHdr, pos2, q2, seqdesc, sequence, seqdesc.anim( i0 , i1+1 ), cycle, boneMask ); - BlendBones( pStudioHdr, q, pos, seqdesc, sequence, q2, pos2, s1, boneMask ); - } - } - else if (s0 > 0.999) - { - if (s1 < 0.001) - { - if (PoseIsAllZeros( pStudioHdr, sequence, seqdesc, i0+1, i1 )) - return false; - CalcAnimation( pStudioHdr, pos, q, seqdesc, sequence, seqdesc.anim( i0+1, i1 ), cycle, boneMask ); - } - else if (s1 > 0.999) - { - CalcAnimation( pStudioHdr, pos, q, seqdesc, sequence, seqdesc.anim( i0+1, i1+1 ), cycle, boneMask ); - } - else - { - CalcAnimation( pStudioHdr, pos, q, seqdesc, sequence, seqdesc.anim( i0+1, i1 ), cycle, boneMask ); - CalcAnimation( pStudioHdr, pos2, q2, seqdesc, sequence, seqdesc.anim( i0+1, i1+1 ), cycle, boneMask ); - BlendBones( pStudioHdr, q, pos, seqdesc, sequence, q2, pos2, s1, boneMask ); - } - } - else - { - if (s1 < 0.001) - { - if (PoseIsAllZeros( pStudioHdr, sequence, seqdesc, i0+1, i1 )) - { - CalcAnimation( pStudioHdr, pos, q, seqdesc, sequence, seqdesc.anim( i0 ,i1 ), cycle, boneMask ); - ScaleBones( pStudioHdr, q, pos, sequence, 1.0 - s0, boneMask ); - } - else if (PoseIsAllZeros( pStudioHdr, sequence, seqdesc, i0, i1 )) - { - CalcAnimation( pStudioHdr, pos, q, seqdesc, sequence, seqdesc.anim( i0+1 ,i1 ), cycle, boneMask ); - ScaleBones( pStudioHdr, q, pos, sequence, s0, boneMask ); - } - else - { - CalcAnimation( pStudioHdr, pos, q, seqdesc, sequence, seqdesc.anim( i0 ,i1 ), cycle, boneMask ); - CalcAnimation( pStudioHdr, pos2, q2, seqdesc, sequence, seqdesc.anim( i0+1,i1 ), cycle, boneMask ); - - BlendBones( pStudioHdr, q, pos, seqdesc, sequence, q2, pos2, s0, boneMask ); - } - } - else if (s1 > 0.999) - { - CalcAnimation( pStudioHdr, pos, q, seqdesc, sequence, seqdesc.anim( i0 ,i1+1 ), cycle, boneMask ); - CalcAnimation( pStudioHdr, pos2, q2, seqdesc, sequence, seqdesc.anim( i0+1,i1+1 ), cycle, boneMask ); - BlendBones( pStudioHdr, q, pos, seqdesc, sequence, q2, pos2, s0, boneMask ); - } - else - { - CalcAnimation( pStudioHdr, pos, q, seqdesc, sequence, seqdesc.anim( i0 ,i1 ), cycle, boneMask ); - CalcAnimation( pStudioHdr, pos2, q2, seqdesc, sequence, seqdesc.anim( i0+1,i1 ), cycle, boneMask ); - BlendBones( pStudioHdr, q, pos, seqdesc, sequence, q2, pos2, s0, boneMask ); - - CalcAnimation( pStudioHdr, pos2, q2, seqdesc, sequence, seqdesc.anim( i0 , i1+1), cycle, boneMask ); - CalcAnimation( pStudioHdr, pos3, q3, seqdesc, sequence, seqdesc.anim( i0+1, i1+1), cycle, boneMask ); - BlendBones( pStudioHdr, q2, pos2, seqdesc, sequence, q3, pos3, s0, boneMask ); - - BlendBones( pStudioHdr, q, pos, seqdesc, sequence, q2, pos2, s1, boneMask ); - } - } - - return true; -} - - - - -//----------------------------------------------------------------------------- -// Purpose: calculate a pose for a single sequence -// adds autolayers, runs local ik rukes -//----------------------------------------------------------------------------- -void AddSequenceLayers( - const CStudioHdr *pStudioHdr, - CIKContext *pIKContext, - Vector pos[], - Quaternion q[], - mstudioseqdesc_t &seqdesc, - int sequence, - float cycle, - const float poseParameter[], - int boneMask, - float flWeight, - float flTime - ) -{ - for (int i = 0; i < seqdesc.numautolayers; i++) - { - mstudioautolayer_t *pLayer = seqdesc.pAutolayer( i ); - - if (pLayer->flags & STUDIO_AL_LOCAL) - continue; - - float layerCycle = cycle; - float layerWeight = flWeight; - - if (pLayer->start != pLayer->end) - { - float s = 1.0; - float index; - - if (!(pLayer->flags & STUDIO_AL_POSE)) - { - index = cycle; - } - else - { - int iPose = pStudioHdr->GetSharedPoseParameter( pLayer->iSequence, pLayer->iPose ); - if (iPose != -1) - { - const mstudioposeparamdesc_t &Pose = pStudioHdr->pPoseParameter( iPose ); - index = poseParameter[ iPose ] * (Pose.end - Pose.start) + Pose.start; - } - else - { - index = 0; - } - } - - if (index < pLayer->start) - continue; - if (index >= pLayer->end) - continue; - - if (index < pLayer->peak && pLayer->start != pLayer->peak) - { - s = (index - pLayer->start) / (pLayer->peak - pLayer->start); - } - else if (index > pLayer->tail && pLayer->end != pLayer->tail) - { - s = (pLayer->end - index) / (pLayer->end - pLayer->tail); - } - - if (pLayer->flags & STUDIO_AL_SPLINE) - { - s = SimpleSpline( s ); - } - - if ((pLayer->flags & STUDIO_AL_XFADE) && (index > pLayer->tail)) - { - layerWeight = ( s * flWeight ) / ( 1 - flWeight + s * flWeight ); - } - else if (pLayer->flags & STUDIO_AL_NOBLEND) - { - layerWeight = s; - } - else - { - layerWeight = flWeight * s; - } - - if (!(pLayer->flags & STUDIO_AL_POSE)) - { - layerCycle = (cycle - pLayer->start) / (pLayer->end - pLayer->start); - } - } - - int iSequence = pStudioHdr->iRelativeSeq( sequence, pLayer->iSequence ); - AccumulatePose( pStudioHdr, pIKContext, pos, q, iSequence, layerCycle, poseParameter, boneMask, layerWeight, flTime ); - } -} - - -//----------------------------------------------------------------------------- -// Purpose: calculate a pose for a single sequence -// adds autolayers, runs local ik rukes -//----------------------------------------------------------------------------- -void AddLocalLayers( - const CStudioHdr *pStudioHdr, - CIKContext *pIKContext, - Vector pos[], - Quaternion q[], - mstudioseqdesc_t &seqdesc, - int sequence, - float cycle, - const float poseParameter[], - int boneMask, - float flWeight, - float flTime - ) -{ - if (!(seqdesc.flags & STUDIO_LOCAL)) - { - return; - } - - for (int i = 0; i < seqdesc.numautolayers; i++) - { - mstudioautolayer_t *pLayer = seqdesc.pAutolayer( i ); - - if (!(pLayer->flags & STUDIO_AL_LOCAL)) - continue; - - float layerCycle = cycle; - float layerWeight = flWeight; - - if (pLayer->start != pLayer->end) - { - float s = 1.0; - - if (cycle < pLayer->start) - continue; - if (cycle >= pLayer->end) - continue; - - if (cycle < pLayer->peak && pLayer->start != pLayer->peak) - { - s = (cycle - pLayer->start) / (pLayer->peak - pLayer->start); - } - else if (cycle > pLayer->tail && pLayer->end != pLayer->tail) - { - s = (pLayer->end - cycle) / (pLayer->end - pLayer->tail); - } - - if (pLayer->flags & STUDIO_AL_SPLINE) - { - s = SimpleSpline( s ); - } - - if ((pLayer->flags & STUDIO_AL_XFADE) && (cycle > pLayer->tail)) - { - layerWeight = ( s * flWeight ) / ( 1 - flWeight + s * flWeight ); - } - else if (pLayer->flags & STUDIO_AL_NOBLEND) - { - layerWeight = s; - } - else - { - layerWeight = flWeight * s; - } - - layerCycle = (cycle - pLayer->start) / (pLayer->end - pLayer->start); - } - - int iSequence = pStudioHdr->iRelativeSeq( sequence, pLayer->iSequence ); - AccumulatePose( pStudioHdr, pIKContext, pos, q, iSequence, layerCycle, poseParameter, boneMask, layerWeight, flTime ); - } -} - -//----------------------------------------------------------------------------- -// Purpose: calculate a pose for a single sequence -// adds autolayers, runs local ik rukes -//----------------------------------------------------------------------------- -void CalcPose( - const CStudioHdr *pStudioHdr, - CIKContext *pIKContext, - Vector pos[], - Quaternion q[], - int sequence, - float cycle, - const float poseParameter[], - int boneMask, - float flWeight, - float flTime - ) -{ - mstudioseqdesc_t &seqdesc = pStudioHdr->pSeqdesc( sequence ); - - Assert( flWeight >= 0.0f && flWeight <= 1.0f ); - // This shouldn't be necessary, but the Assert should help us catch whoever is screwing this up - flWeight = clamp( flWeight, 0.0f, 1.0f ); - - // add any IK locks to prevent numautolayers from moving extremities - CIKContext seq_ik; - if (seqdesc.numiklocks) - { - seq_ik.Init( pStudioHdr, vec3_angle, vec3_origin, 0.0, 0, boneMask ); // local space relative so absolute position doesn't mater - seq_ik.AddSequenceLocks( seqdesc, pos, q ); - } - - CalcPoseSingle( pStudioHdr, pos, q, seqdesc, sequence, cycle, poseParameter, boneMask, flTime ); - - if ( pIKContext ) - { - pIKContext->AddDependencies( seqdesc, sequence, cycle, poseParameter, flWeight ); - } - - AddSequenceLayers( pStudioHdr, pIKContext, pos, q, seqdesc, sequence, cycle, poseParameter, boneMask, flWeight, flTime ); - - if (seqdesc.numiklocks) - { - seq_ik.SolveSequenceLocks( seqdesc, pos, q ); - } -} - - -//----------------------------------------------------------------------------- -// Purpose: accumulate a pose for a single sequence on top of existing animation -// adds autolayers, runs local ik rukes -//----------------------------------------------------------------------------- -void AccumulatePose( - const CStudioHdr *pStudioHdr, - CIKContext *pIKContext, - Vector pos[], - Quaternion q[], - int sequence, - float cycle, - const float poseParameter[], - int boneMask, - float flWeight, - float flTime - ) -{ - Vector pos2[MAXSTUDIOBONES]; - Quaternion q2[MAXSTUDIOBONES]; - - Assert( flWeight >= 0.0f && flWeight <= 1.0f ); - // This shouldn't be necessary, but the Assert should help us catch whoever is screwing this up - flWeight = clamp( flWeight, 0.0f, 1.0f ); - - if ( sequence < 0 ) - return; - - mstudioseqdesc_t &seqdesc = pStudioHdr->pSeqdesc( sequence ); - - // add any IK locks to prevent extremities from moving - CIKContext seq_ik; - if (seqdesc.numiklocks) - { - seq_ik.Init( pStudioHdr, vec3_angle, vec3_origin, 0.0, 0, boneMask ); // local space relative so absolute position doesn't mater - seq_ik.AddSequenceLocks( seqdesc, pos, q ); - } - - if (seqdesc.flags & STUDIO_LOCAL) - { - InitPose( pStudioHdr, pos2, q2 ); - } - - if (CalcPoseSingle( pStudioHdr, pos2, q2, seqdesc, sequence, cycle, poseParameter, boneMask, flTime )) - { - // this weight is wrong, the IK rules won't composite at the correct intensity - AddLocalLayers( pStudioHdr, pIKContext, pos2, q2, seqdesc, sequence, cycle, poseParameter, boneMask, 1.0, flTime ); - SlerpBones( pStudioHdr, q, pos, seqdesc, sequence, q2, pos2, flWeight, boneMask ); - } - - if ( pIKContext ) - { - pIKContext->AddDependencies( seqdesc, sequence, cycle, poseParameter, flWeight ); - } - - AddSequenceLayers( pStudioHdr, pIKContext, pos, q, seqdesc, sequence, cycle, poseParameter, boneMask, flWeight, flTime ); - - if (seqdesc.numiklocks) - { - seq_ik.SolveSequenceLocks( seqdesc, pos, q ); - } -} - - -//----------------------------------------------------------------------------- -// Purpose: blend together q1,pos1 with q2,pos2. Return result in q1,pos1. -// 0 returns q1, pos1. 1 returns q2, pos2 -//----------------------------------------------------------------------------- -void CalcBoneAdj( - const CStudioHdr *pStudioHdr, - Vector pos[], - Quaternion q[], - const float controllers[], - int boneMask - ) -{ - int i, j, k; - float value; - mstudiobonecontroller_t *pbonecontroller; - Vector p0; - RadianEuler a0; - Quaternion q0; - - for (j = 0; j < pStudioHdr->numbonecontrollers(); j++) - { - pbonecontroller = pStudioHdr->pBonecontroller( j ); - k = pbonecontroller->bone; - - if (pStudioHdr->pBone( k )->flags & boneMask) - { - i = pbonecontroller->inputfield; - value = controllers[i]; - if (value < 0) value = 0; - if (value > 1.0) value = 1.0; - value = (1.0 - value) * pbonecontroller->start + value * pbonecontroller->end; - - switch(pbonecontroller->type & STUDIO_TYPES) - { - case STUDIO_XR: - a0.Init( value * (M_PI / 180.0), 0, 0 ); - AngleQuaternion( a0, q0 ); - QuaternionSM( 1.0, q0, q[k], q[k] ); - break; - case STUDIO_YR: - a0.Init( 0, value * (M_PI / 180.0), 0 ); - AngleQuaternion( a0, q0 ); - QuaternionSM( 1.0, q0, q[k], q[k] ); - break; - case STUDIO_ZR: - a0.Init( 0, 0, value * (M_PI / 180.0) ); - AngleQuaternion( a0, q0 ); - QuaternionSM( 1.0, q0, q[k], q[k] ); - break; - case STUDIO_X: - pos[k].x += value; - break; - case STUDIO_Y: - pos[k].y += value; - break; - case STUDIO_Z: - pos[k].z += value; - break; - } - } - } -} - - -void CalcBoneDerivatives( Vector &velocity, AngularImpulse &angVel, const matrix3x4_t &prev, const matrix3x4_t ¤t, float dt ) -{ - float scale = 1.0; - if ( dt > 0 ) - { - scale = 1.0 / dt; - } - - Vector endPosition, startPosition, deltaAxis; - QAngle endAngles, startAngles; - float deltaAngle; - - MatrixAngles( prev, startAngles, startPosition ); - MatrixAngles( current, endAngles, endPosition ); - - velocity.x = (endPosition.x - startPosition.x) * scale; - velocity.y = (endPosition.y - startPosition.y) * scale; - velocity.z = (endPosition.z - startPosition.z) * scale; - RotationDeltaAxisAngle( startAngles, endAngles, deltaAxis, deltaAngle ); - VectorScale( deltaAxis, (deltaAngle * scale), angVel ); -} - -void CalcBoneVelocityFromDerivative( const QAngle &vecAngles, Vector &velocity, AngularImpulse &angVel, const matrix3x4_t ¤t ) -{ - Vector vecLocalVelocity; - AngularImpulse LocalAngVel; - Quaternion q; - float angle; - MatrixAngles( current, q, vecLocalVelocity ); - QuaternionAxisAngle( q, LocalAngVel, angle ); - LocalAngVel *= angle; - - matrix3x4_t matAngles; - AngleMatrix( vecAngles, matAngles ); - VectorTransform( vecLocalVelocity, matAngles, velocity ); - VectorTransform( LocalAngVel, matAngles, angVel ); -} - - - - -class ik -{ -public: -//-------- SOLVE TWO LINK INVERSE KINEMATICS ------------- -// Author: Ken Perlin -// -// Given a two link joint from [0,0,0] to end effector position P, -// let link lengths be a and b, and let norm |P| = c. Clearly a+b <= c. -// -// Problem: find a "knee" position Q such that |Q| = a and |P-Q| = b. -// -// In the case of a point on the x axis R = [c,0,0], there is a -// closed form solution S = [d,e,0], where |S| = a and |R-S| = b: -// -// d2+e2 = a2 -- because |S| = a -// (c-d)2+e2 = b2 -- because |R-S| = b -// -// c2-2cd+d2+e2 = b2 -- combine the two equations -// c2-2cd = b2 - a2 -// c-2d = (b2-a2)/c -// d - c/2 = (a2-b2)/c / 2 -// -// d = (c + (a2-b2/c) / 2 -- to solve for d and e. -// e = sqrt(a2-d2) - - static float findD(float a, float b, float c) { - return (c + (a*a-b*b)/c) / 2; - } - static float findE(float a, float d) { return sqrt(a*a-d*d); } - -// This leads to a solution to the more general problem: -// -// (1) R = Mfwd(P) -- rotate P onto the x axis -// (2) Solve for S -// (3) Q = Minv(S) -- rotate back again - - static float Mfwd[3][3]; - static float Minv[3][3]; - - static bool solve(float A, float B, float const P[], float const D[], float Q[]) { - float R[3]; - defineM(P,D); - rot(Minv,P,R); - float r = length(R); - float d = findD(A,B,r); - float e = findE(A,d); - float S[3] = {d,e,0}; - rot(Mfwd,S,Q); - return d > (r - B) && d < A; - } - -// If "knee" position Q needs to be as close as possible to some point D, -// then choose M such that M(D) is in the y>0 half of the z=0 plane. -// -// Given that constraint, define the forward and inverse of M as follows: - - static void defineM(float const P[], float const D[]) { - float *X = Minv[0], *Y = Minv[1], *Z = Minv[2]; - -// Minv defines a coordinate system whose x axis contains P, so X = unit(P). - int i; - for (i = 0 ; i < 3 ; i++) - X[i] = P[i]; - normalize(X); - -// Its y axis is perpendicular to P, so Y = unit( E - X(E·X) ). - - float dDOTx = dot(D,X); - for (i = 0 ; i < 3 ; i++) - Y[i] = D[i] - dDOTx * X[i]; - normalize(Y); - -// Its z axis is perpendicular to both X and Y, so Z = X×Y. - - cross(X,Y,Z); - -// Mfwd = (Minv)T, since transposing inverts a rotation matrix. - - for (i = 0 ; i < 3 ; i++) { - Mfwd[i][0] = Minv[0][i]; - Mfwd[i][1] = Minv[1][i]; - Mfwd[i][2] = Minv[2][i]; - } - } - -//------------ GENERAL VECTOR MATH SUPPORT ----------- - - static float dot(float const a[], float const b[]) { return a[0]*b[0] + a[1]*b[1] + a[2]*b[2]; } - - static float length(float const v[]) { return sqrt( dot(v,v) ); } - - static void normalize(float v[]) { - float norm = length(v); - for (int i = 0 ; i < 3 ; i++) - v[i] /= norm; - } - - static void cross(float const a[], float const b[], float c[]) { - c[0] = a[1] * b[2] - a[2] * b[1]; - c[1] = a[2] * b[0] - a[0] * b[2]; - c[2] = a[0] * b[1] - a[1] * b[0]; - } - - static void rot(float const M[3][3], float const src[], float dst[]) { - for (int i = 0 ; i < 3 ; i++) - dst[i] = dot(M[i],src); - } -}; - -float ik::Mfwd[3][3]; -float ik::Minv[3][3]; - - - -//----------------------------------------------------------------------------- -// Purpose: visual debugging code -//----------------------------------------------------------------------------- -#if 1 -inline void debugLine(const Vector& origin, const Vector& dest, int r, int g, int b, bool noDepthTest, float duration) { }; -#else -extern void drawLine( const Vector &p1, const Vector &p2, int r = 0, int g = 0, int b = 1, bool noDepthTest = true, float duration = 0.1 ); -void debugLine(const Vector& origin, const Vector& dest, int r, int g, int b, bool noDepthTest, float duration) -{ - drawLine( origin, dest, r, g, b, noDepthTest, duration ); -} -#endif - - -//----------------------------------------------------------------------------- -// Purpose: for a 2 bone chain, find the IK solution and reset the matrices -//----------------------------------------------------------------------------- -bool Studio_SolveIK( mstudioikchain_t *pikchain, Vector &targetFoot, matrix3x4_t *pBoneToWorld ) -{ - if (pikchain->pLink(0)->kneeDir.LengthSqr() > 0.0) - { - Vector targetKneeDir, targetKneePos; - // FIXME: knee length should be as long as the legs - Vector tmp = pikchain->pLink( 0 )->kneeDir; - VectorRotate( tmp, pBoneToWorld[ pikchain->pLink( 0 )->bone ], targetKneeDir ); - MatrixPosition( pBoneToWorld[ pikchain->pLink( 1 )->bone ], targetKneePos ); - return Studio_SolveIK( pikchain->pLink( 0 )->bone, pikchain->pLink( 1 )->bone, pikchain->pLink( 2 )->bone, targetFoot, targetKneePos, targetKneeDir, pBoneToWorld ); - } - else - { - return Studio_SolveIK( pikchain->pLink( 0 )->bone, pikchain->pLink( 1 )->bone, pikchain->pLink( 2 )->bone, targetFoot, pBoneToWorld ); - } -} - - -#define KNEEMAX_EPSILON 0.9998 // (0.9998 is about 1 degree) - -//----------------------------------------------------------------------------- -// Purpose: Solve Knee position for a known hip and foot location, but no specific knee direction preference -//----------------------------------------------------------------------------- - -bool Studio_SolveIK( int iThigh, int iKnee, int iFoot, Vector &targetFoot, matrix3x4_t *pBoneToWorld ) -{ - Vector worldFoot, worldKnee, worldThigh; - - MatrixPosition( pBoneToWorld[ iThigh ], worldThigh ); - MatrixPosition( pBoneToWorld[ iKnee ], worldKnee ); - MatrixPosition( pBoneToWorld[ iFoot ], worldFoot ); - - //debugLine( worldThigh, worldKnee, 0, 0, 255, true, 0 ); - //debugLine( worldKnee, worldFoot, 0, 0, 255, true, 0 ); - - Vector ikFoot, ikKnee; - - ikFoot = targetFoot - worldThigh; - ikKnee = worldKnee - worldThigh; - - float l1 = (worldKnee-worldThigh).Length(); - float l2 = (worldFoot-worldKnee).Length(); - float l3 = (worldFoot-worldThigh).Length(); - - // leg too straight to figure out knee? - if (l3 > (l1 + l2) * KNEEMAX_EPSILON) - { - return false; - } - - Vector ikHalf = (worldFoot-worldThigh) * (l1 / l3); - - // FIXME: what to do when the knee completely straight? - Vector ikKneeDir = ikKnee - ikHalf; - VectorNormalize( ikKneeDir ); - - return Studio_SolveIK( iThigh, iKnee, iFoot, targetFoot, worldKnee, ikKneeDir, pBoneToWorld ); -} - -//----------------------------------------------------------------------------- -// Purpose: Realign the matrix so that its X axis points along the desired axis. -//----------------------------------------------------------------------------- -void Studio_AlignIKMatrix( matrix3x4_t &mMat, const Vector &vAlignTo ) -{ - Vector tmp1, tmp2, tmp3; - - // Column 0 (X) becomes the vector. - tmp1 = vAlignTo; - VectorNormalize( tmp1 ); - MatrixSetColumn( tmp1, 0, mMat ); - - // Column 1 (Y) is the cross of the vector and column 2 (Z). - MatrixGetColumn( mMat, 2, tmp3 ); - tmp2 = tmp3.Cross( tmp1 ); - VectorNormalize( tmp2 ); - // FIXME: check for X being too near to Z - MatrixSetColumn( tmp2, 1, mMat ); - - // Column 2 (Z) is the cross of columns 0 (X) and 1 (Y). - tmp3 = tmp1.Cross( tmp2 ); - MatrixSetColumn( tmp3, 2, mMat ); -} - - -//----------------------------------------------------------------------------- -// Purpose: Solve Knee position for a known hip and foot location, and a known knee direction -//----------------------------------------------------------------------------- - -bool Studio_SolveIK( int iThigh, int iKnee, int iFoot, Vector &targetFoot, Vector &targetKneePos, Vector &targetKneeDir, matrix3x4_t *pBoneToWorld ) -{ - Vector worldFoot, worldKnee, worldThigh; - - MatrixPosition( pBoneToWorld[ iThigh ], worldThigh ); - MatrixPosition( pBoneToWorld[ iKnee ], worldKnee ); - MatrixPosition( pBoneToWorld[ iFoot ], worldFoot ); - - //debugLine( worldThigh, worldKnee, 0, 0, 255, true, 0 ); - //debugLine( worldThigh, worldThigh + targetKneeDir, 0, 0, 255, true, 0 ); - // debugLine( worldKnee, targetKnee, 0, 0, 255, true, 0 ); - - Vector ikFoot, ikTargetKnee, ikKnee; - - ikFoot = targetFoot - worldThigh; - ikKnee = targetKneePos - worldThigh; - - float l1 = (worldKnee-worldThigh).Length(); - float l2 = (worldFoot-worldKnee).Length(); - - // exaggerate knee targets for legs that are nearly straight - // FIXME: should be configurable, and the ikKnee should be from the original animation, not modifed - float d = (targetFoot-worldThigh).Length() - min( l1, l2 ); - d = max( l1 + l2, d ); - // FIXME: too short knee directions cause trouble - d = d * 100; - - ikTargetKnee = ikKnee + targetKneeDir * d; - - // debugLine( worldKnee, worldThigh + ikTargetKnee, 0, 0, 255, true, 0 ); - - int color[3] = { 0, 255, 0 }; - - // too far away? (0.9998 is about 1 degree) - if (ikFoot.Length() > (l1 + l2) * KNEEMAX_EPSILON) - { - VectorNormalize( ikFoot ); - VectorScale( ikFoot, (l1 + l2) * KNEEMAX_EPSILON, ikFoot ); - color[0] = 255; color[1] = 0; color[2] = 0; - } - - // too close? - if (ikFoot.Length() < fabs(l1 - l2) * 1.05) - { - VectorNormalize( ikFoot ); - VectorScale( ikFoot, fabs(l1 - l2) * 1.05, ikFoot ); - } - - if (ik::solve( l1, l2, ikFoot.Base(), ikTargetKnee.Base(), ikKnee.Base() )) - { - matrix3x4_t& mWorldThigh = pBoneToWorld[ iThigh ]; - matrix3x4_t& mWorldKnee = pBoneToWorld[ iKnee ]; - matrix3x4_t& mWorldFoot = pBoneToWorld[ iFoot ]; - - //debugLine( worldThigh, ikKnee + worldThigh, 255, 0, 0, true, 0 ); - //debugLine( ikKnee + worldThigh, ikFoot + worldThigh, 255, 0, 0, true,0 ); - - // debugLine( worldThigh, ikKnee + worldThigh, color[0], color[1], color[2], true, 0 ); - // debugLine( ikKnee + worldThigh, ikFoot + worldThigh, color[0], color[1], color[2], true,0 ); - - - // build transformation matrix for thigh - Studio_AlignIKMatrix( mWorldThigh, ikKnee ); - Studio_AlignIKMatrix( mWorldKnee, ikFoot - ikKnee ); - - - mWorldKnee[0][3] = ikKnee.x + worldThigh.x; - mWorldKnee[1][3] = ikKnee.y + worldThigh.y; - mWorldKnee[2][3] = ikKnee.z + worldThigh.z; - - mWorldFoot[0][3] = ikFoot.x + worldThigh.x; - mWorldFoot[1][3] = ikFoot.y + worldThigh.y; - mWorldFoot[2][3] = ikFoot.z + worldThigh.z; - - return true; - } - else - { - /* - debugLine( worldThigh, worldThigh + ikKnee, 255, 0, 0, true, 0 ); - debugLine( worldThigh + ikKnee, worldThigh + ikFoot, 255, 0, 0, true, 0 ); - debugLine( worldThigh + ikFoot, worldThigh, 255, 0, 0, true, 0 ); - debugLine( worldThigh + ikKnee, worldThigh + ikTargetKnee, 255, 0, 0, true, 0 ); - */ - return false; - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- - -float Studio_IKRuleWeight( mstudioikrule_t &ikRule, const mstudioanimdesc_t *panim, float flCycle, int &iFrame, float &fraq ) -{ - if (ikRule.end > 1.0f && flCycle < ikRule.start) - { - flCycle = flCycle + 1.0f; - } - - float value = 0.0f; - fraq = (panim->numframes - 1) * (flCycle - ikRule.start) + ikRule.iStart; - iFrame = (int)fraq; - fraq = fraq - iFrame; - - if (flCycle < ikRule.start) - { - iFrame = ikRule.iStart; - fraq = 0.0f; - return 0.0f; - } - else if (flCycle < ikRule.peak ) - { - value = (flCycle - ikRule.start) / (ikRule.peak - ikRule.start); - } - else if (flCycle < ikRule.tail ) - { - return 1.0f; - } - else if (flCycle < ikRule.end ) - { - value = 1.0f - ((flCycle - ikRule.tail) / (ikRule.end - ikRule.tail)); - } - else - { - fraq = (panim->numframes - 1) * (ikRule.end - ikRule.start) + ikRule.iStart; - iFrame = (int)fraq; - fraq = fraq - iFrame; - } - return SimpleSpline( value ); -} - - -float Studio_IKRuleWeight( ikcontextikrule_t &ikRule, float flCycle ) -{ - if (ikRule.end > 1.0f && flCycle < ikRule.start) - { - flCycle = flCycle + 1.0f; - } - - float value = 0.0f; - if (flCycle < ikRule.start) - { - return 0.0f; - } - else if (flCycle < ikRule.peak ) - { - value = (flCycle - ikRule.start) / (ikRule.peak - ikRule.start); - } - else if (flCycle < ikRule.tail ) - { - return 1.0f; - } - else if (flCycle < ikRule.end ) - { - value = 1.0f - ((flCycle - ikRule.tail) / (ikRule.end - ikRule.tail)); - } - return 3.0f * value * value - 2.0f * value * value * value; -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- - -bool Studio_IKShouldLatch( ikcontextikrule_t &ikRule, float flCycle ) -{ - if (ikRule.end > 1.0f && flCycle < ikRule.start) - { - flCycle = flCycle + 1.0f; - } - - if (flCycle < ikRule.peak ) - { - return false; - } - else if (flCycle < ikRule.end ) - { - return true; - } - return false; -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- - -float Studio_IKTail( ikcontextikrule_t &ikRule, float flCycle ) -{ - if (ikRule.end > 1.0f && flCycle < ikRule.start) - { - flCycle = flCycle + 1.0f; - } - - if (flCycle <= ikRule.tail ) - { - return 0.0f; - } - else if (flCycle < ikRule.end ) - { - return ((flCycle - ikRule.tail) / (ikRule.end - ikRule.tail)); - } - return 0.0; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- - -bool Studio_IKAnimationError( const CStudioHdr *pStudioHdr, mstudioikrule_t *pRule, const mstudioanimdesc_t *panim, float flCycle, Vector &pos, Quaternion &q, float &flWeight ) -{ - float fraq; - int iFrame; - - flWeight = Studio_IKRuleWeight( *pRule, panim, flCycle, iFrame, fraq ); - Assert( fraq >= 0.0 && fraq < 1.0 ); - Assert( flWeight >= 0.0f && flWeight <= 1.0f ); - - // This shouldn't be necessary, but the Assert should help us catch whoever is screwing this up - flWeight = clamp( flWeight, 0.0f, 1.0f ); - - if (pRule->type != IK_GROUND && flWeight < 0.0001) - return false; - - mstudioikerror_t *pError = pRule->pError( iFrame ); - if (pError != NULL) - { - if (fraq < 0.001) - { - q = pError[0].q; - pos = pError[0].pos; - } - else - { - QuaternionBlend( pError[0].q, pError[1].q, fraq, q ); - pos = pError[0].pos * (1.0f - fraq) + pError[1].pos * fraq; - } - return true; - } - - mstudiocompressedikerror_t *pCompressed = pRule->pCompressedError(); - if (pCompressed != NULL) - { - iFrame = iFrame - pRule->iStart; - - Vector p1, p2; - ExtractAnimValue( iFrame, pCompressed->pAnimvalue( 0 ), pCompressed->scale[0], p1.x, p2.x ); - ExtractAnimValue( iFrame, pCompressed->pAnimvalue( 1 ), pCompressed->scale[1], p1.y, p2.y ); - ExtractAnimValue( iFrame, pCompressed->pAnimvalue( 2 ), pCompressed->scale[2], p1.z, p2.z ); - pos = p1 * (1 - fraq) + p2 * fraq; - - Quaternion q1, q2; - RadianEuler angle1, angle2; - ExtractAnimValue( iFrame, pCompressed->pAnimvalue( 3 ), pCompressed->scale[3], angle1.x, angle2.x ); - ExtractAnimValue( iFrame, pCompressed->pAnimvalue( 4 ), pCompressed->scale[4], angle1.y, angle2.y ); - ExtractAnimValue( iFrame, pCompressed->pAnimvalue( 5 ), pCompressed->scale[5], angle1.z, angle2.z ); - - if (angle1.x != angle2.x || angle1.y != angle2.y || angle1.z != angle2.z) - { - AngleQuaternion( angle1, q1 ); - AngleQuaternion( angle2, q2 ); - QuaternionBlend( q1, q2, fraq, q ); - } - else - { - AngleQuaternion( angle1, q ); - } - return true; - } - // no data, disable IK rule - Assert( 0 ); - flWeight = 0.0f; - return false; -} - -//----------------------------------------------------------------------------- -// Purpose: For a specific sequence:rule, find where it starts, stops, and what -// the estimated offset from the connection point is. -// return true if the rule is within bounds. -//----------------------------------------------------------------------------- - -bool Studio_IKSequenceError( const CStudioHdr *pStudioHdr, mstudioseqdesc_t &seqdesc, int iSequence, float flCycle, int iRule, const float poseParameter[], mstudioanimdesc_t *panim[4], float weight[4], ikcontextikrule_t &ikRule ) -{ - int i; - - memset( &ikRule, 0, sizeof(ikRule) ); - ikRule.start = ikRule.peak = ikRule.tail = ikRule.end = 0; - - - mstudioikrule_t *prevRule = NULL; - - // find overall influence - for (i = 0; i < 4; i++) - { - if (weight[i]) - { - if (iRule >= panim[i]->numikrules || panim[i]->numikrules != panim[0]->numikrules) - { - Assert( 0 ); - return false; - } - - mstudioikrule_t *pRule = panim[i]->pIKRule( iRule ); - if (pRule == NULL) - return false; - - float dt = 0.0; - if (prevRule != NULL) - { - if (pRule->start - prevRule->start > 0.5) - { - dt = -1.0; - } - else if (pRule->start - prevRule->start < -0.5) - { - dt = 1.0; - } - } - else - { - prevRule = pRule; - } - - ikRule.start += (pRule->start + dt) * weight[i]; - ikRule.peak += (pRule->peak + dt) * weight[i]; - ikRule.tail += (pRule->tail + dt) * weight[i]; - ikRule.end += (pRule->end + dt) * weight[i]; - } - } - if (ikRule.start > 1.0) - { - ikRule.start -= 1.0; - ikRule.peak -= 1.0; - ikRule.tail -= 1.0; - ikRule.end -= 1.0; - } - else if (ikRule.start < 0.0) - { - ikRule.start += 1.0; - ikRule.peak += 1.0; - ikRule.tail += 1.0; - ikRule.end += 1.0; - } - - ikRule.flWeight = Studio_IKRuleWeight( ikRule, flCycle ); - if (ikRule.flWeight <= 0.001f) - { - // go ahead and allow IK_GROUND rules a virtual looping section - if (panim[0]->pIKRule( iRule )->type == IK_GROUND && ikRule.end - ikRule.start > 0.75 ) - { - ikRule.flWeight = 0.001; - flCycle = ikRule.end - 0.001; - } - else - { - return false; - } - } - - Assert( ikRule.flWeight > 0.0f ); - - ikRule.pos.Init(); - ikRule.q.Init(); - - // FIXME: add "latched" value - ikRule.commit = Studio_IKShouldLatch( ikRule, flCycle ); - - // find target error - float total = 0.0f; - for (i = 0; i < 4; i++) - { - if (weight[i]) - { - Vector pos1; - Quaternion q1; - float w; - - mstudioikrule_t *pRule = panim[i]->pIKRule( iRule ); - if (pRule == NULL) - return false; - - ikRule.chain = pRule->chain; // FIXME: this is anim local - ikRule.bone = pRule->bone; // FIXME: this is anim local - ikRule.type = pRule->type; - ikRule.slot = pRule->slot; - - ikRule.height += pRule->height * weight[i]; - ikRule.floor += pRule->floor * weight[i]; - ikRule.radius += pRule->radius * weight[i]; - - // keep track of tail condition - ikRule.release += Studio_IKTail( ikRule, flCycle ) * weight[i]; - - // only check rules with error values - switch( ikRule.type ) - { - case IK_SELF: - case IK_WORLD: - case IK_GROUND: - case IK_ATTACHMENT: - { - int bResult = Studio_IKAnimationError( pStudioHdr, pRule, panim[i], flCycle, pos1, q1, w ); - - if (bResult) - { - ikRule.pos = ikRule.pos + pos1 * weight[i]; - QuaternionAccumulate( ikRule.q, weight[i], q1, ikRule.q ); - total += weight[i]; - } - } - break; - default: - total += weight[i]; - break; - } - - ikRule.latched = Studio_IKShouldLatch( ikRule, flCycle ) * ikRule.flWeight; - - if (ikRule.type == IK_ATTACHMENT) - { - ikRule.szLabel = pRule->pszAttachment(); - } - } - } - - if (total <= 0.0001f) - { - return false; - } - - if (total < 0.999f) - { - VectorScale( ikRule.pos, 1.0f / total, ikRule.pos ); - QuaternionScale( ikRule.q, 1.0f / total, ikRule.q ); - } - - if (ikRule.type == IK_SELF && ikRule.bone != -1) - { - // FIXME: this is anim local, not seq local! - ikRule.bone = pStudioHdr->RemapSeqBone( iSequence, ikRule.bone ); - if (ikRule.bone == -1) - return false; - } - - QuaternionNormalize( ikRule.q ); - return true; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- - - -CIKContext::CIKContext() -{ - m_target.EnsureCapacity( 12 ); // FIXME: this sucks, shouldn't it be grown? - m_iFramecounter = -1; - m_pStudioHdr = NULL; - m_flTime = -1.0f; - m_target.SetSize( 0 ); -} - - -void CIKContext::Init( const CStudioHdr *pStudioHdr, const QAngle &angles, const Vector &pos, float flTime, int iFramecounter, int boneMask ) -{ - m_pStudioHdr = pStudioHdr; - m_ikChainRule.RemoveAll(); // m_numikrules = 0; - if (pStudioHdr->numikchains()) - { - m_ikChainRule.SetSize( pStudioHdr->numikchains() ); - - // FIXME: Brutal hackery to prevent a crash - if (m_target.Count() == 0) - { - m_target.SetSize(12); - memset( m_target.Base(), 0, sizeof(m_target[0])*m_target.Count() ); - ClearTargets(); - } - - } - else - { - m_target.SetSize( 0 ); - } - AngleMatrix( angles, pos, m_rootxform ); - m_iFramecounter = iFramecounter; - m_flTime = flTime; - m_boneMask = boneMask; -} - -void CIKContext::AddDependencies( mstudioseqdesc_t &seqdesc, int iSequence, float flCycle, const float poseParameters[], float flWeight ) -{ - int i; - - if (seqdesc.numikrules == 0) - return; - - ikcontextikrule_t ikrule; - - Assert( flWeight >= 0.0f && flWeight <= 1.0f ); - // This shouldn't be necessary, but the Assert should help us catch whoever is screwing this up - flWeight = clamp( flWeight, 0.0f, 1.0f ); - - // unify this - if (seqdesc.flags & STUDIO_REALTIME) - { - float cps = Studio_CPS( m_pStudioHdr, seqdesc, iSequence, poseParameters ); - flCycle = m_flTime * cps; - flCycle = flCycle - (int)flCycle; - } - else if (flCycle < 0 || flCycle >= 1) - { - if (seqdesc.flags & STUDIO_LOOPING) - { - flCycle = flCycle - (int)flCycle; - if (flCycle < 0) flCycle += 1; - } - else - { - flCycle = max( 0.0, min( flCycle, 0.9999 ) ); - } - } - - mstudioanimdesc_t *panim[4]; - float weight[4]; - - Studio_SeqAnims( m_pStudioHdr, seqdesc, iSequence, poseParameters, panim, weight ); - - // FIXME: add proper number of rules!!! - for (i = 0; i < seqdesc.numikrules; i++) - { - if ( !Studio_IKSequenceError( m_pStudioHdr, seqdesc, iSequence, flCycle, i, poseParameters, panim, weight, ikrule ) ) - continue; - - // don't add rule if the bone isn't going to be calculated - int bone = m_pStudioHdr->pIKChain( ikrule.chain )->pLink( 2 )->bone; - if ( !(m_pStudioHdr->pBone( bone )->flags & m_boneMask)) - continue; - - // or if its relative bone isn't going to be calculated - if ( ikrule.bone >= 0 && !(m_pStudioHdr->pBone( ikrule.bone )->flags & m_boneMask)) - continue; - - // FIXME: Brutal hackery to prevent a crash - if (m_target.Count() == 0) - { - m_target.SetSize(12); - memset( m_target.Base(), 0, sizeof(m_target[0])*m_target.Count() ); - ClearTargets(); - } - - ikrule.flRuleWeight = flWeight; - - if (ikrule.flRuleWeight * ikrule.flWeight > 0.999) - { - if ( ikrule.type != IK_UNLATCH) - { - // clear out chain if rule is 100% - m_ikChainRule.Element( ikrule.chain ).RemoveAll( ); - if ( ikrule.type == IK_RELEASE) - { - continue; - } - } - } - - int nIndex = m_ikChainRule.Element( ikrule.chain ).AddToTail( ); - m_ikChainRule.Element( ikrule.chain ).Element( nIndex ) = ikrule; - } -} - - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- - -void CIKContext::AddAutoplayLocks( Vector pos[], Quaternion q[] ) -{ - // skip all array access if no autoplay locks. - if (m_pStudioHdr->GetNumIKAutoplayLocks() == 0) - { - return; - } - - static matrix3x4_t boneToWorld[MAXSTUDIOBONES]; - CBoneBitList boneComputed; - - int ikOffset = m_ikLock.AddMultipleToTail( m_pStudioHdr->GetNumIKAutoplayLocks() ); - memset( &m_ikLock[ikOffset], 0, sizeof(ikcontextikrule_t)*m_pStudioHdr->GetNumIKAutoplayLocks() ); - - for (int i = 0; i < m_pStudioHdr->GetNumIKAutoplayLocks(); i++) - { - const mstudioiklock_t &lock = m_pStudioHdr->pIKAutoplayLock( i ); - mstudioikchain_t *pchain = m_pStudioHdr->pIKChain( lock.chain ); - int bone = pchain->pLink( 2 )->bone; - - // don't bother with iklock if the bone isn't going to be calculated - if ( !(m_pStudioHdr->pBone( bone )->flags & m_boneMask)) - continue; - - // eval current ik'd bone - BuildBoneChain( pos, q, bone, boneToWorld, boneComputed ); - - ikcontextikrule_t &ikrule = m_ikLock[ i + ikOffset ]; - - ikrule.chain = lock.chain; - ikrule.slot = i; - ikrule.type = IK_WORLD; - - MatrixAngles( boneToWorld[bone], ikrule.q, ikrule.pos ); - - // save off current knee direction - if (pchain->pLink(0)->kneeDir.LengthSqr() > 0.0) - { - Vector tmp = pchain->pLink( 0 )->kneeDir; - VectorRotate( pchain->pLink( 0 )->kneeDir, boneToWorld[ pchain->pLink( 0 )->bone ], ikrule.kneeDir ); - MatrixPosition( boneToWorld[ pchain->pLink( 1 )->bone ], ikrule.kneePos ); - } - else - { - ikrule.kneeDir.Init( ); - } - } -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- - -void CIKContext::AddSequenceLocks( mstudioseqdesc_t &seqdesc, Vector pos[], Quaternion q[] ) -{ - if ( seqdesc.numiklocks == 0 ) - { - return; - } - - static matrix3x4_t boneToWorld[MAXSTUDIOBONES]; - CBoneBitList boneComputed; - - int ikOffset = m_ikLock.AddMultipleToTail( seqdesc.numiklocks ); - memset( &m_ikLock[ikOffset], 0, sizeof(ikcontextikrule_t) * seqdesc.numiklocks ); - - for (int i = 0; i < seqdesc.numiklocks; i++) - { - mstudioiklock_t *plock = seqdesc.pIKLock( i ); - mstudioikchain_t *pchain = m_pStudioHdr->pIKChain( plock->chain ); - int bone = pchain->pLink( 2 )->bone; - - // don't bother with iklock if the bone isn't going to be calculated - if ( !(m_pStudioHdr->pBone( bone )->flags & m_boneMask)) - continue; - - // eval current ik'd bone - BuildBoneChain( pos, q, bone, boneToWorld, boneComputed ); - - ikcontextikrule_t &ikrule = m_ikLock[i+ikOffset]; - ikrule.chain = i; - ikrule.slot = i; - ikrule.type = IK_WORLD; - - MatrixAngles( boneToWorld[bone], ikrule.q, ikrule.pos ); - - // save off current knee direction - if (pchain->pLink(0)->kneeDir.LengthSqr() > 0.0) - { - VectorRotate( pchain->pLink( 0 )->kneeDir, boneToWorld[ pchain->pLink( 0 )->bone ], ikrule.kneeDir ); - } - else - { - ikrule.kneeDir.Init( ); - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: build boneToWorld transforms for a specific bone -//----------------------------------------------------------------------------- -void CIKContext::BuildBoneChain( - const Vector pos[], - const Quaternion q[], - int iBone, - matrix3x4_t *pBoneToWorld, - CBoneBitList &boneComputed ) -{ - Assert( m_pStudioHdr->pBone( iBone )->flags & m_boneMask ); - ::BuildBoneChain( m_pStudioHdr, m_rootxform, pos, q, iBone, pBoneToWorld, boneComputed ); -} - - - -//----------------------------------------------------------------------------- -// Purpose: build boneToWorld transforms for a specific bone -//----------------------------------------------------------------------------- -void BuildBoneChain( - const CStudioHdr *pStudioHdr, - const matrix3x4_t &rootxform, - const Vector pos[], - const Quaternion q[], - int iBone, - matrix3x4_t *pBoneToWorld, - CBoneBitList &boneComputed ) -{ - if ( boneComputed.IsBoneMarked(iBone) ) - return; - - matrix3x4_t bonematrix; - QuaternionMatrix( q[iBone], pos[iBone], bonematrix ); - - int parent = pStudioHdr->pBone( iBone )->parent; - if (parent == -1) - { - ConcatTransforms( rootxform, bonematrix, pBoneToWorld[iBone] ); - } - else - { - // evil recursive!!! - BuildBoneChain( pStudioHdr, rootxform, pos, q, parent, pBoneToWorld, boneComputed ); - ConcatTransforms( pBoneToWorld[parent], bonematrix, pBoneToWorld[iBone]); - } - boneComputed.MarkBone(iBone); -} - - -//----------------------------------------------------------------------------- -// Purpose: turn a specific bones boneToWorld transform into a pos and q in parents bonespace -//----------------------------------------------------------------------------- -void SolveBone( - const CStudioHdr *pStudioHdr, - int iBone, - matrix3x4_t *pBoneToWorld, - Vector pos[], - Quaternion q[] - ) -{ - int iParent = pStudioHdr->pBone( iBone )->parent; - - matrix3x4_t worldToBone; - MatrixInvert( pBoneToWorld[iParent], worldToBone ); - - matrix3x4_t local; - ConcatTransforms( worldToBone, pBoneToWorld[iBone], local ); - - MatrixAngles( local, q[iBone], pos[iBone] ); -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- - -void CIKTarget::SetOwner( int entindex, const Vector &pos, const QAngle &angles ) -{ - latched.owner = entindex; - latched.absOrigin = pos; - latched.absAngles = angles; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- - -void CIKTarget::ClearOwner( void ) -{ - latched.owner = -1; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- - -int CIKTarget::GetOwner( void ) -{ - return latched.owner; -} - -//----------------------------------------------------------------------------- -// Purpose: update the latched IK values that are in a moving frame of reference -//----------------------------------------------------------------------------- - -void CIKTarget::UpdateOwner( int entindex, const Vector &pos, const QAngle &angles ) -{ - if (pos == latched.absOrigin && angles == latched.absAngles) - return; - - matrix3x4_t in, out; - AngleMatrix( angles, pos, in ); - AngleIMatrix( latched.absAngles, latched.absOrigin, out ); - - matrix3x4_t tmp1, tmp2; - QuaternionMatrix( latched.q, latched.pos, tmp1 ); - ConcatTransforms( out, tmp1, tmp2 ); - ConcatTransforms( in, tmp2, tmp1 ); - MatrixAngles( tmp1, latched.q, latched.pos ); -} - - -//----------------------------------------------------------------------------- -// Purpose: sets the ground position of an ik target -//----------------------------------------------------------------------------- - -void CIKTarget::SetPos( const Vector &pos ) -{ - est.pos = pos; -} - -//----------------------------------------------------------------------------- -// Purpose: sets the ground "identity" orientation of an ik target -//----------------------------------------------------------------------------- - -void CIKTarget::SetAngles( const QAngle &angles ) -{ - AngleQuaternion( angles, est.q ); -} - -//----------------------------------------------------------------------------- -// Purpose: sets the ground "identity" orientation of an ik target -//----------------------------------------------------------------------------- - -void CIKTarget::SetQuaternion( const Quaternion &q ) -{ - est.q = q; -} - -//----------------------------------------------------------------------------- -// Purpose: calculates a ground "identity" orientation based on the surface -// normal of the ground and the desired ground identity orientation -//----------------------------------------------------------------------------- - -void CIKTarget::SetNormal( const Vector &normal ) -{ - // recalculate foot angle based on slope of surface - matrix3x4_t m1; - Vector forward, right; - QuaternionMatrix( est.q, m1 ); - - MatrixGetColumn( m1, 1, right ); - forward = CrossProduct( right, normal ); - right = CrossProduct( normal, forward ); - MatrixSetColumn( forward, 0, m1 ); - MatrixSetColumn( right, 1, m1 ); - MatrixSetColumn( normal, 2, m1 ); - QAngle a1; - Vector p1; - MatrixAngles( m1, est.q, p1 ); -} - - -//----------------------------------------------------------------------------- -// Purpose: estimates the ground impact at the center location assuming a the edge of -// an Z axis aligned disc collided with it the surface. -//----------------------------------------------------------------------------- - -void CIKTarget::SetPosWithNormalOffset( const Vector &pos, const Vector &normal ) -{ - // assume it's a disc edge intersecting with the floor, so try to estimate the z location of the center - est.pos = pos; - if (normal.z > 0.9999) - { - return; - } - // clamp at 45 degrees - else if (normal.z > 0.707) - { - // tan == sin / cos - float tan = sqrt( 1 - normal.z * normal.z ) / normal.z; - est.pos.z = est.pos.z - est.radius * tan; - } - else - { - est.pos.z = est.pos.z - est.radius; - } -} - - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- - -bool CIKTarget::IsActive() -{ - return (est.flWeight > 0.0f); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- - -void CIKTarget::IKFailed( void ) -{ - latched.deltaPos.Init(); - latched.deltaQ.Init(); - latched.pos = ideal.pos; - latched.q = ideal.q; - est.latched = 0.0; - est.flWeight = 0.0; -} - - -//----------------------------------------------------------------------------- -// Purpose: Invalidate any IK locks. -//----------------------------------------------------------------------------- - -void CIKContext::ClearTargets( void ) -{ - int i; - for (i = 0; i < m_target.Count(); i++) - { - m_target[i].latched.iFramecounter = -9999; - } -} - -//----------------------------------------------------------------------------- -// Purpose: Run through the rules that survived and turn a specific bones boneToWorld -// transform into a pos and q in parents bonespace -//----------------------------------------------------------------------------- - -void CIKContext::UpdateTargets( Vector pos[], Quaternion q[], matrix3x4_t boneToWorld[], CBoneBitList &boneComputed ) -{ - int i, j; - - for (i = 0; i < m_target.Count(); i++) - { - m_target[i].est.flWeight = 0.0f; - m_target[i].est.latched = 1.0f; - m_target[i].est.release = 1.0f; - m_target[i].est.height = 0.0f; - m_target[i].est.floor = 0.0f; - m_target[i].est.radius = 0.0f; - m_target[i].offset.pos.Init(); - m_target[i].offset.q.Init(); - } - - AutoIKRelease( ); - - for (j = 0; j < m_ikChainRule.Count(); j++) - { - for (i = 0; i < m_ikChainRule.Element( j ).Count(); i++) - { - ikcontextikrule_t *pRule = &m_ikChainRule.Element( j ).Element( i ); - - // ikchainresult_t *pChainRule = &chainRule[ m_ikRule[i].chain ]; - - switch( pRule->type ) - { - case IK_ATTACHMENT: - case IK_GROUND: - // case IK_SELF: - { - matrix3x4_t footTarget; - CIKTarget *pTarget = &m_target[pRule->slot]; - pTarget->chain = pRule->chain; - pTarget->type = pRule->type; - - if (pRule->type == IK_ATTACHMENT) - { - pTarget->offset.pAttachmentName = pRule->szLabel; - } - else - { - pTarget->offset.pAttachmentName = NULL; - } - - if (pRule->flRuleWeight == 1.0f || pTarget->est.flWeight == 0.0f) - { - pTarget->offset.q = pRule->q; - pTarget->offset.pos = pRule->pos; - pTarget->est.height = pRule->height; - pTarget->est.floor = pRule->floor; - pTarget->est.radius = pRule->radius; - pTarget->est.latched = pRule->latched * pRule->flRuleWeight; - pTarget->est.release = pRule->release; - pTarget->est.flWeight = pRule->flWeight * pRule->flRuleWeight; - } - else - { - QuaternionSlerp( pTarget->offset.q, pRule->q, pRule->flRuleWeight, pTarget->offset.q ); - pTarget->offset.pos = Lerp( pRule->flRuleWeight, pTarget->offset.pos, pRule->pos ); - pTarget->est.height = Lerp( pRule->flRuleWeight, pTarget->est.height, pRule->height ); - pTarget->est.floor = Lerp( pRule->flRuleWeight, pTarget->est.floor, pRule->floor ); - pTarget->est.radius = Lerp( pRule->flRuleWeight, pTarget->est.radius, pRule->radius ); - //pTarget->est.latched = Lerp( pRule->flRuleWeight, pTarget->est.latched, pRule->latched ); - pTarget->est.latched = min( pTarget->est.latched, pRule->latched ); - pTarget->est.release = Lerp( pRule->flRuleWeight, pTarget->est.release, pRule->release ); - pTarget->est.flWeight = Lerp( pRule->flRuleWeight, pTarget->est.flWeight, pRule->flWeight ); - } - - if ( pRule->type == IK_GROUND ) - { - pTarget->latched.deltaPos.z = 0; - pTarget->est.pos.z = pTarget->est.floor + m_rootxform[2][3]; - } - } - break; - case IK_UNLATCH: - { - CIKTarget *pTarget = &m_target[pRule->slot]; - if (pRule->latched > 0.0) - pTarget->est.latched = 0.0; - else - pTarget->est.latched = min( pTarget->est.latched, 1.0f - pRule->flWeight ); - } - break; - case IK_RELEASE: - { - CIKTarget *pTarget = &m_target[pRule->slot]; - if (pRule->latched > 0.0) - pTarget->est.latched = 0.0; - else - pTarget->est.latched = min( pTarget->est.latched, 1.0f - pRule->flWeight ); - - pTarget->est.flWeight = (pTarget->est.flWeight) * (1 - pRule->flWeight * pRule->flRuleWeight); - } - break; - } - } - } - - for (i = 0; i < m_target.Count(); i++) - { - CIKTarget *pTarget = &m_target[i]; - if (pTarget->est.flWeight > 0.0) - { - mstudioikchain_t *pchain = m_pStudioHdr->pIKChain( pTarget->chain ); - // ikchainresult_t *pChainRule = &chainRule[ i ]; - int bone = pchain->pLink( 2 )->bone; - - // eval current ik'd bone - BuildBoneChain( pos, q, bone, boneToWorld, boneComputed ); - - // xform IK target error into world space - matrix3x4_t local; - matrix3x4_t worldFootpad; - QuaternionMatrix( pTarget->offset.q, pTarget->offset.pos, local ); - MatrixInvert( local, local ); - ConcatTransforms( boneToWorld[bone], local, worldFootpad ); - - if (pTarget->est.latched == 1.0) - { - pTarget->latched.bNeedsLatch = true; - } - else - { - pTarget->latched.bNeedsLatch = false; - } - - // disable latched position if it looks invalid - if (m_iFramecounter < 0 || pTarget->latched.iFramecounter < m_iFramecounter - 1 || pTarget->latched.iFramecounter > m_iFramecounter) - { - pTarget->latched.bHasLatch = false; - pTarget->latched.influence = 0.0; - } - pTarget->latched.iFramecounter = m_iFramecounter; - - // find ideal contact position - MatrixAngles( worldFootpad, pTarget->ideal.q, pTarget->ideal.pos ); - pTarget->est.q = pTarget->ideal.q; - pTarget->est.pos = pTarget->ideal.pos; - - float latched = pTarget->est.latched; - - if (pTarget->latched.bHasLatch) - { - if (pTarget->est.latched == 1.0) - { - // keep track of latch position error from ideal contact position - pTarget->latched.deltaPos = pTarget->latched.pos - pTarget->est.pos; - QuaternionSM( -1, pTarget->est.q, pTarget->latched.q, pTarget->latched.deltaQ ); - pTarget->est.q = pTarget->latched.q; - pTarget->est.pos = pTarget->latched.pos; - } - else if (pTarget->est.latched > 0.0) - { - // ramp out latch differences during decay phase of rule - if (latched > 0 && latched < pTarget->latched.influence) - { - // latching has decreased - float dt = pTarget->latched.influence - latched; - if (pTarget->latched.influence > 0.0) - dt = dt / pTarget->latched.influence; - - VectorScale( pTarget->latched.deltaPos, (1-dt), pTarget->latched.deltaPos ); - QuaternionScale( pTarget->latched.deltaQ, (1-dt), pTarget->latched.deltaQ ); - } - - // move ideal contact position by latched error factor - pTarget->est.pos = pTarget->est.pos + pTarget->latched.deltaPos; - QuaternionMA( pTarget->est.q, 1, pTarget->latched.deltaQ, pTarget->est.q ); - pTarget->latched.q = pTarget->est.q; - pTarget->latched.pos = pTarget->est.pos; - } - else - { - pTarget->latched.bHasLatch = false; - pTarget->latched.q = pTarget->est.q; - pTarget->latched.pos = pTarget->est.pos; - pTarget->latched.deltaPos.Init(); - pTarget->latched.deltaQ.Init(); - } - pTarget->latched.influence = latched; - } - - // check for illegal requests - Vector p1, p2, p3; - MatrixPosition( boneToWorld[pchain->pLink( 0 )->bone], p1 ); // hip - MatrixPosition( boneToWorld[pchain->pLink( 1 )->bone], p2 ); // knee - MatrixPosition( boneToWorld[pchain->pLink( 2 )->bone], p3 ); // foot - - float d1 = (p2 - p1).Length(); - float d2 = (p3 - p2).Length(); - - if (pTarget->latched.bHasLatch) - { - //float d3 = (p3 - p1).Length(); - float d4 = (p3 + pTarget->latched.deltaPos - p1).Length(); - - // unstick feet when distance is too great - if ((d4 < fabs( d1 - d2 ) || d4 * 0.95 > d1 + d2) && pTarget->est.latched > 0.2) - { - pTarget->error.flTime = m_flTime; - } - - // unstick feet when angle is too great - if (pTarget->est.latched > 0.2) - { - float d = fabs( pTarget->latched.deltaQ.w ) * 2.0f - 1.0f; // QuaternionDotProduct( pTarget->latched.q, pTarget->est.q ); - - // FIXME: cos(45), make property of chain - if (d < 0.707) - { - pTarget->error.flTime = m_flTime; - } - } - } - - Vector dt = pTarget->est.pos - p1; - VectorNormalize( dt ); - - pTarget->trace.p1 = p1 + dt * (fabs( d1 - d2 ) * 1.01); - pTarget->trace.p2 = p1 + dt * (d1 + d2) * 0.99; - pTarget->trace.p3 = p1 + Vector( 0, 0, -1 ) * (d1 + d2); - // pTarget->trace.endpos = pTarget->est.pos; - } - } -} - - -//----------------------------------------------------------------------------- -// Purpose: insert release rules if the ik rules were in error -//----------------------------------------------------------------------------- - -void CIKContext::AutoIKRelease( void ) -{ - int i; - - for (i = 0; i < m_target.Count(); i++) - { - CIKTarget *pTarget = &m_target[i]; - - float dt = m_flTime - pTarget->error.flTime; - if (pTarget->error.bInError || dt < 0.5) - { - if (!pTarget->error.bInError) - { - pTarget->error.ramp = 0.0; - pTarget->error.flErrorTime = pTarget->error.flTime; - pTarget->error.bInError = true; - } - - float ft = m_flTime - pTarget->error.flErrorTime; - if (dt < 0.25) - { - pTarget->error.ramp = min( pTarget->error.ramp + ft * 4.0, 1.0 ); - } - else - { - pTarget->error.ramp = max( pTarget->error.ramp - ft * 4.0, 0.0 ); - } - if (pTarget->error.ramp > 0.0) - { - ikcontextikrule_t ikrule; - - ikrule.chain = pTarget->chain; - ikrule.bone = 0; - ikrule.type = IK_RELEASE; - ikrule.slot = i; - ikrule.flWeight = SimpleSpline( pTarget->error.ramp ); - ikrule.flRuleWeight = 1.0; - ikrule.latched = dt < 0.25 ? 0.0 : ikrule.flWeight; - - // don't bother with AutoIKRelease if the bone isn't going to be calculated - // this code is crashing for some unknown reason. - if ( pTarget->chain >= 0 && pTarget->chain < m_pStudioHdr->numikchains()) - { - mstudioikchain_t *pchain = m_pStudioHdr->pIKChain( pTarget->chain ); - if (pchain != NULL) - { - int bone = pchain->pLink( 2 )->bone; - if (bone >= 0 && bone < m_pStudioHdr->numbones()) - { - mstudiobone_t *pBone = m_pStudioHdr->pBone( bone ); - if (pBone != NULL) - { - if ( !(pBone->flags & m_boneMask)) - { - pTarget->error.bInError = false; - continue; - } - /* - char buf[256]; - sprintf( buf, "dt %.4f ft %.4f weight %.4f latched %.4f\n", dt, ft, ikrule.flWeight, ikrule.latched ); - OutputDebugString( buf ); - */ - - int nIndex = m_ikChainRule.Element( ikrule.chain ).AddToTail( ); - m_ikChainRule.Element( ikrule.chain ).Element( nIndex ) = ikrule; - } - else - { - DevWarning( 1, "AutoIKRelease (%s) got a NULL pBone %d\n", m_pStudioHdr->name(), bone ); - } - } - else - { - DevWarning( 1, "AutoIKRelease (%s) got an out of range bone %d (%d)\n", m_pStudioHdr->name(), bone, m_pStudioHdr->numbones() ); - } - } - else - { - DevWarning( 1, "AutoIKRelease (%s) got a NULL pchain %d\n", m_pStudioHdr->name(), pTarget->chain ); - } - } - else - { - DevWarning( 1, "AutoIKRelease (%s) got an out of range chain %d (%d)\n", m_pStudioHdr->name(), pTarget->chain, m_pStudioHdr->numikchains()); - } - } - else - { - pTarget->error.bInError = false; - } - pTarget->error.flErrorTime = m_flTime; - } - } -} - - - -void CIKContext::SolveDependencies( Vector pos[], Quaternion q[], matrix3x4_t boneToWorld[], CBoneBitList &boneComputed ) -{ - ASSERT_NO_REENTRY(); - - matrix3x4_t worldTarget; - int i, j; - - ikchainresult_t chainResult[32]; // allocate!!! - - // init chain rules - for (i = 0; i < m_pStudioHdr->numikchains(); i++) - { - mstudioikchain_t *pchain = m_pStudioHdr->pIKChain( i ); - ikchainresult_t *pChainResult = &chainResult[ i ]; - int bone = pchain->pLink( 2 )->bone; - - pChainResult->target = -1; - pChainResult->flWeight = 0.0; - - // don't bother with chain if the bone isn't going to be calculated - if ( !(m_pStudioHdr->pBone( bone )->flags & m_boneMask)) - continue; - - // eval current ik'd bone - BuildBoneChain( pos, q, bone, boneToWorld, boneComputed ); - - MatrixAngles( boneToWorld[bone], pChainResult->q, pChainResult->pos ); - } - - for (j = 0; j < m_ikChainRule.Count(); j++) - { - for (i = 0; i < m_ikChainRule.Element( j ).Count(); i++) - { - ikcontextikrule_t *pRule = &m_ikChainRule.Element( j ).Element( i ); - ikchainresult_t *pChainResult = &chainResult[ pRule->chain ]; - pChainResult->target = -1; - - - switch( pRule->type ) - { - case IK_SELF: - { - // xform IK target error into world space - matrix3x4_t local; - QuaternionMatrix( pRule->q, pRule->pos, local ); - // eval target bone space - if (pRule->bone != -1) - { - BuildBoneChain( pos, q, pRule->bone, boneToWorld, boneComputed ); - ConcatTransforms( boneToWorld[pRule->bone], local, worldTarget ); - } - else - { - ConcatTransforms( m_rootxform, local, worldTarget ); - } - - float flWeight = pRule->flWeight * pRule->flRuleWeight; - pChainResult->flWeight = pChainResult->flWeight * (1 - flWeight) + flWeight; - - Vector p2; - Quaternion q2; - - // target p and q - MatrixAngles( worldTarget, q2, p2 ); - - // debugLine( pChainResult->pos, p2, 0, 0, 255, true, 0.1 ); - - // blend in position and angles - pChainResult->pos = pChainResult->pos * (1.0 - flWeight) + p2 * flWeight; - QuaternionSlerp( pChainResult->q, q2, flWeight, pChainResult->q ); - } - break; - case IK_WORLD: - Assert( 0 ); - break; - - case IK_ATTACHMENT: - break; - - case IK_GROUND: - break; - - case IK_RELEASE: - { - // move target back towards original location - float flWeight = pRule->flWeight * pRule->flRuleWeight; - mstudioikchain_t *pchain = m_pStudioHdr->pIKChain( pRule->chain ); - int bone = pchain->pLink( 2 )->bone; - - Vector p2; - Quaternion q2; - - BuildBoneChain( pos, q, bone, boneToWorld, boneComputed ); - MatrixAngles( boneToWorld[bone], q2, p2 ); - - // blend in position and angles - pChainResult->pos = pChainResult->pos * (1.0 - flWeight) + p2 * flWeight; - QuaternionSlerp( pChainResult->q, q2, flWeight, pChainResult->q ); - } - break; - case IK_UNLATCH: - { - /* - pChainResult->flWeight = pChainResult->flWeight * (1 - pRule->flWeight) + pRule->flWeight; - - pChainResult->pos = pChainResult->pos * (1.0 - pRule->flWeight ) + pChainResult->local.pos * pRule->flWeight; - QuaternionSlerp( pChainResult->q, pChainResult->local.q, pRule->flWeight, pChainResult->q ); - */ - } - break; - } - } - } - - for (i = 0; i < m_target.Count(); i++) - { - CIKTarget *pTarget = &m_target[i]; - - if (m_target[i].est.flWeight > 0.0) - { - matrix3x4_t worldFootpad; - matrix3x4_t local; - //mstudioikchain_t *pchain = m_pStudioHdr->pIKChain( m_target[i].chain ); - ikchainresult_t *pChainResult = &chainResult[ pTarget->chain ]; - - AngleMatrix(pTarget->offset.q, pTarget->offset.pos, local ); - - AngleMatrix( pTarget->est.q, pTarget->est.pos, worldFootpad ); - - ConcatTransforms( worldFootpad, local, worldTarget ); - - Vector p2; - Quaternion q2; - // target p and q - MatrixAngles( worldTarget, q2, p2 ); - // MatrixAngles( worldTarget, pChainResult->q, pChainResult->pos ); - - // blend in position and angles - pChainResult->flWeight = pTarget->est.flWeight; - pChainResult->pos = pChainResult->pos * (1.0 - pChainResult->flWeight ) + p2 * pChainResult->flWeight; - QuaternionSlerp( pChainResult->q, q2, pChainResult->flWeight, pChainResult->q ); - } - - if (pTarget->latched.bNeedsLatch) - { - // keep track of latch position - pTarget->latched.bHasLatch = true; - pTarget->latched.q = pTarget->est.q; - pTarget->latched.pos = pTarget->est.pos; - } - } - - for (i = 0; i < m_pStudioHdr->numikchains(); i++) - { - ikchainresult_t *pChainResult = &chainResult[ i ]; - mstudioikchain_t *pchain = m_pStudioHdr->pIKChain( i ); - - if (pChainResult->flWeight > 0.0) - { - Vector tmp; - MatrixPosition( boneToWorld[pchain->pLink( 2 )->bone], tmp ); - // debugLine( pChainResult->pos, tmp, 255, 255, 255, true, 0.1 ); - - // do exact IK solution - // FIXME: once per link! - if (Studio_SolveIK(pchain, pChainResult->pos, boneToWorld )) - { - Vector p3; - MatrixGetColumn( boneToWorld[pchain->pLink( 2 )->bone], 3, p3 ); - QuaternionMatrix( pChainResult->q, p3, boneToWorld[pchain->pLink( 2 )->bone] ); - - // rebuild chain - // FIXME: is this needed if everyone past this uses the boneToWorld array? - SolveBone( m_pStudioHdr, pchain->pLink( 2 )->bone, boneToWorld, pos, q ); - SolveBone( m_pStudioHdr, pchain->pLink( 1 )->bone, boneToWorld, pos, q ); - SolveBone( m_pStudioHdr, pchain->pLink( 0 )->bone, boneToWorld, pos, q ); - } - else - { - // FIXME: need to invalidate the targets that forced this... - if (pChainResult->target != -1) - { - CIKTarget *pTarget = &m_target[pChainResult->target]; - VectorScale( pTarget->latched.deltaPos, 0.8, pTarget->latched.deltaPos ); - QuaternionScale( pTarget->latched.deltaQ, 0.8, pTarget->latched.deltaQ ); - } - } - } - } - -#if 0 - Vector p1, p2, p3; - Quaternion q1, q2, q3; - - // current p and q - MatrixAngles( boneToWorld[bone], q1, p1 ); - - - // target p and q - MatrixAngles( worldTarget, q2, p2 ); - - // blend in position and angles - p3 = p1 * (1.0 - m_ikRule[i].flWeight ) + p2 * m_ikRule[i].flWeight; - - // do exact IK solution - // FIXME: once per link! - Studio_SolveIK(pchain, p3, boneToWorld ); - - // force angle (bad?) - QuaternionSlerp( q1, q2, m_ikRule[i].flWeight, q3 ); - MatrixGetColumn( boneToWorld[bone], 3, p3 ); - QuaternionMatrix( q3, p3, boneToWorld[bone] ); - - // rebuild chain - SolveBone( m_pStudioHdr, pchain->pLink( 2 )->bone, boneToWorld, pos, q ); - SolveBone( m_pStudioHdr, pchain->pLink( 1 )->bone, boneToWorld, pos, q ); - SolveBone( m_pStudioHdr, pchain->pLink( 0 )->bone, boneToWorld, pos, q ); -#endif -} - - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- - -void CIKContext::SolveAutoplayLocks( - Vector pos[], - Quaternion q[] - ) -{ - ASSERT_NO_REENTRY(); - - static matrix3x4_t boneToWorld[MAXSTUDIOBONES]; - CBoneBitList boneComputed; - int i; - - for (i = 0; i < m_ikLock.Count(); i++) - { - const mstudioiklock_t &lock = m_pStudioHdr->pIKAutoplayLock( i ); - mstudioikchain_t *pchain = m_pStudioHdr->pIKChain( lock.chain ); - int bone = pchain->pLink( 2 )->bone; - - // don't bother with iklock if the bone isn't going to be calculated - if ( !(m_pStudioHdr->pBone( bone )->flags & m_boneMask)) - continue; - - // eval current ik'd bone - BuildBoneChain( pos, q, bone, boneToWorld, boneComputed ); - - Vector p1, p2, p3; - Quaternion q2, q3; - - // current p and q - MatrixPosition( boneToWorld[bone], p1 ); - - // blend in position - p3 = p1 * (1.0 - lock.flPosWeight ) + m_ikLock[i].pos * lock.flPosWeight; - - // do exact IK solution - if (m_ikLock[i].kneeDir.LengthSqr() > 0) - { - Studio_SolveIK(pchain->pLink( 0 )->bone, pchain->pLink( 1 )->bone, pchain->pLink( 2 )->bone, p3, m_ikLock[i].kneePos, m_ikLock[i].kneeDir, boneToWorld ); - } - else - { - Studio_SolveIK(pchain, p3, boneToWorld ); - } - - // slam orientation - MatrixPosition( boneToWorld[bone], p3 ); - QuaternionMatrix( m_ikLock[i].q, p3, boneToWorld[bone] ); - - // rebuild chain - q2 = q[ bone ]; - SolveBone( m_pStudioHdr, pchain->pLink( 2 )->bone, boneToWorld, pos, q ); - QuaternionSlerp( q[bone], q2, lock.flLocalQWeight, q[bone] ); - - SolveBone( m_pStudioHdr, pchain->pLink( 1 )->bone, boneToWorld, pos, q ); - SolveBone( m_pStudioHdr, pchain->pLink( 0 )->bone, boneToWorld, pos, q ); - } -} - - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- - -void CIKContext::SolveSequenceLocks( - mstudioseqdesc_t &seqdesc, - Vector pos[], - Quaternion q[] - ) -{ - ASSERT_NO_REENTRY(); - - static matrix3x4_t boneToWorld[MAXSTUDIOBONES]; - CBoneBitList boneComputed; - int i; - - for (i = 0; i < m_ikLock.Count(); i++) - { - mstudioiklock_t *plock = seqdesc.pIKLock( i ); - mstudioikchain_t *pchain = m_pStudioHdr->pIKChain( plock->chain ); - int bone = pchain->pLink( 2 )->bone; - - // don't bother with iklock if the bone isn't going to be calculated - if ( !(m_pStudioHdr->pBone( bone )->flags & m_boneMask)) - continue; - - // eval current ik'd bone - BuildBoneChain( pos, q, bone, boneToWorld, boneComputed ); - - Vector p1, p2, p3; - Quaternion q2, q3; - - // current p and q - MatrixPosition( boneToWorld[bone], p1 ); - - // blend in position - p3 = p1 * (1.0 - plock->flPosWeight ) + m_ikLock[i].pos * plock->flPosWeight; - - // do exact IK solution - if (m_ikLock[i].kneeDir.LengthSqr() > 0) - { - Studio_SolveIK(pchain->pLink( 0 )->bone, pchain->pLink( 1 )->bone, pchain->pLink( 2 )->bone, p3, m_ikLock[i].kneePos, m_ikLock[i].kneeDir, boneToWorld ); - } - else - { - Studio_SolveIK(pchain, p3, boneToWorld ); - } - - // slam orientation - MatrixPosition( boneToWorld[bone], p3 ); - QuaternionMatrix( m_ikLock[i].q, p3, boneToWorld[bone] ); - - // rebuild chain - q2 = q[ bone ]; - SolveBone( m_pStudioHdr, pchain->pLink( 2 )->bone, boneToWorld, pos, q ); - QuaternionSlerp( q[bone], q2, plock->flLocalQWeight, q[bone] ); - - SolveBone( m_pStudioHdr, pchain->pLink( 1 )->bone, boneToWorld, pos, q ); - SolveBone( m_pStudioHdr, pchain->pLink( 0 )->bone, boneToWorld, pos, q ); - } -} - -//----------------------------------------------------------------------------- -// Purpose: run all animations that automatically play and are driven off of poseParameters -//----------------------------------------------------------------------------- -void CalcAutoplaySequences( - const CStudioHdr *pStudioHdr, - CIKContext *pIKContext, - Vector pos[], - Quaternion q[], - const float poseParameters[], - int boneMask, - float realTime - ) -{ - ASSERT_NO_REENTRY(); - - int i; - if ( pIKContext ) - { - pIKContext->AddAutoplayLocks( pos, q ); - } - - unsigned short *pList = NULL; - int count = pStudioHdr->GetAutoplayList( &pList ); - for (i = 0; i < count; i++) - { - int sequenceIndex = pList[i]; - mstudioseqdesc_t &seqdesc = pStudioHdr->pSeqdesc( sequenceIndex ); - if (seqdesc.flags & STUDIO_AUTOPLAY) - { - float cycle = 0; - float cps = Studio_CPS( pStudioHdr, seqdesc, sequenceIndex, poseParameters ); - cycle = realTime * cps; - cycle = cycle - (int)cycle; - - AccumulatePose( pStudioHdr, NULL, pos, q, sequenceIndex, cycle, poseParameters, boneMask, 1.0, realTime ); - } - } - - if ( pIKContext ) - { - pIKContext->SolveAutoplayLocks( pos, q ); - } -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void Studio_BuildMatrices( - const CStudioHdr *pStudioHdr, - const QAngle& angles, - const Vector& origin, - const Vector pos[], - const Quaternion q[], - int iBone, - matrix3x4_t bonetoworld[MAXSTUDIOBONES], - int boneMask - ) -{ - int i, j; - - int chain[MAXSTUDIOBONES]; - int chainlength = 0; - - if (iBone < -1 || iBone >= pStudioHdr->numbones()) - iBone = 0; - - mstudiobone_t *pbones = pStudioHdr->pBone( 0 ); - - // build list of what bones to use - if (iBone == -1) - { - // all bones - chainlength = pStudioHdr->numbones(); - for (i = 0; i < pStudioHdr->numbones(); i++) - { - chain[chainlength - i - 1] = i; - } - } - else - { - // only the parent bones - i = iBone; - while (i != -1) - { - chain[chainlength++] = i; - i = pbones[i].parent; - } - } - - matrix3x4_t bonematrix; - matrix3x4_t rotationmatrix; // model to world transformation - AngleMatrix( angles, origin, rotationmatrix); - - for (j = chainlength - 1; j >= 0; j--) - { - i = chain[j]; - if (pbones[i].flags & boneMask) - { - QuaternionMatrix( q[i], pos[i], bonematrix ); - - if (pbones[i].parent == -1) - { - ConcatTransforms (rotationmatrix, bonematrix, bonetoworld[i]); - } - else - { - ConcatTransforms (bonetoworld[pbones[i].parent], bonematrix, bonetoworld[i]); - } - } - } -} - - -//----------------------------------------------------------------------------- -// Purpose: look at single column vector of another bones local transformation -// and generate a procedural transformation based on how that column -// points down the 6 cardinal axis (all negative weights are clamped to 0). -//----------------------------------------------------------------------------- - -void DoAxisInterpBone( - mstudiobone_t *pbones, - int ibone, - CBoneAccessor &bonetoworld - ) -{ - matrix3x4_t bonematrix; - Vector control; - - mstudioaxisinterpbone_t *pProc = (mstudioaxisinterpbone_t *)pbones[ibone].pProcedure( ); - const matrix3x4_t &controlBone = bonetoworld.GetBone( pProc->control ); - if (pProc && pbones[pProc->control].parent != -1) - { - Vector tmp; - // pull out the control column - tmp.x = controlBone[0][pProc->axis]; - tmp.y = controlBone[1][pProc->axis]; - tmp.z = controlBone[2][pProc->axis]; - - // invert it back into parent's space. - VectorIRotate( tmp, bonetoworld.GetBone( pbones[pProc->control].parent ), control ); -#if 0 - matrix3x4_t tmpmatrix; - matrix3x4_t controlmatrix; - MatrixInvert( bonetoworld.GetBone( pbones[pProc->control].parent ), tmpmatrix ); - ConcatTransforms( tmpmatrix, bonetoworld.GetBone( pProc->control ), controlmatrix ); - - // pull out the control column - control.x = controlmatrix[0][pProc->axis]; - control.y = controlmatrix[1][pProc->axis]; - control.z = controlmatrix[2][pProc->axis]; -#endif - } - else - { - // pull out the control column - control.x = controlBone[0][pProc->axis]; - control.y = controlBone[1][pProc->axis]; - control.z = controlBone[2][pProc->axis]; - } - - Quaternion *q1, *q2, *q3; - Vector *p1, *p2, *p3; - - // find axial control inputs - float a1 = control.x; - float a2 = control.y; - float a3 = control.z; - if (a1 >= 0) - { - q1 = &pProc->quat[0]; - p1 = &pProc->pos[0]; - } - else - { - a1 = -a1; - q1 = &pProc->quat[1]; - p1 = &pProc->pos[1]; - } - - if (a2 >= 0) - { - q2 = &pProc->quat[2]; - p2 = &pProc->pos[2]; - } - else - { - a2 = -a2; - q2 = &pProc->quat[3]; - p2 = &pProc->pos[3]; - } - - if (a3 >= 0) - { - q3 = &pProc->quat[4]; - p3 = &pProc->pos[4]; - } - else - { - a3 = -a3; - q3 = &pProc->quat[5]; - p3 = &pProc->pos[5]; - } - - // do a three-way blend - Vector p; - Quaternion v, tmp; - if (a1 + a2 > 0) - { - float t = 1.0 / (a1 + a2 + a3); - // FIXME: do a proper 3-way Quat blend! - QuaternionSlerp( *q2, *q1, a1 / (a1 + a2), tmp ); - QuaternionSlerp( tmp, *q3, a3 * t, v ); - VectorScale( *p1, a1 * t, p ); - VectorMA( p, a2 * t, *p2, p ); - VectorMA( p, a3 * t, *p3, p ); - } - else - { - QuaternionSlerp( *q3, *q3, 0, v ); // ??? no quat copy? - p = *p3; - } - - QuaternionMatrix( v, p, bonematrix ); - - ConcatTransforms (bonetoworld.GetBone( pbones[ibone].parent ), bonematrix, bonetoworld.GetBoneForWrite( ibone )); -} - - - -//----------------------------------------------------------------------------- -// Purpose: Generate a procedural transformation based on how that another bones -// local transformation matches a set of target orientations. -//----------------------------------------------------------------------------- -void DoQuatInterpBone( - mstudiobone_t *pbones, - int ibone, - CBoneAccessor &bonetoworld - ) -{ - matrix3x4_t bonematrix; - Vector control; - - mstudioquatinterpbone_t *pProc = (mstudioquatinterpbone_t *)pbones[ibone].pProcedure( ); - if (pProc && pbones[pProc->control].parent != -1) - { - Quaternion src; - float weight[32]; - float scale = 0.0; - Quaternion quat; - Vector pos; - - matrix3x4_t tmpmatrix; - matrix3x4_t controlmatrix; - MatrixInvert( bonetoworld.GetBone( pbones[pProc->control].parent), tmpmatrix ); - ConcatTransforms( tmpmatrix, bonetoworld.GetBone( pProc->control ), controlmatrix ); - - MatrixAngles( controlmatrix, src, pos ); // FIXME: make a version without pos - - int i; - for (i = 0; i < pProc->numtriggers; i++) - { - float dot = fabs( QuaternionDotProduct( pProc->pTrigger( i )->trigger, src ) ); - // FIXME: a fast acos should be acceptable - dot = clamp( dot, -1, 1 ); - weight[i] = 1 - (2 * acos( dot ) * pProc->pTrigger( i )->inv_tolerance ); - weight[i] = max( 0, weight[i] ); - scale += weight[i]; - } - - if (scale <= 0.001) // EPSILON? - { - AngleMatrix( pProc->pTrigger( 0 )->quat, pProc->pTrigger( 0 )->pos, bonematrix ); - ConcatTransforms ( bonetoworld.GetBone( pbones[ibone].parent ), bonematrix, bonetoworld.GetBoneForWrite( ibone ) ); - return; - } - - scale = 1.0 / scale; - - quat.Init( 0, 0, 0, 0); - pos.Init( ); - - for (i = 0; i < pProc->numtriggers; i++) - { - if (weight[i]) - { - float s = weight[i] * scale; - mstudioquatinterpinfo_t *pTrigger = pProc->pTrigger( i ); - - QuaternionAlign( pTrigger->quat, quat, quat ); - - quat.x = quat.x + s * pTrigger->quat.x; - quat.y = quat.y + s * pTrigger->quat.y; - quat.z = quat.z + s * pTrigger->quat.z; - quat.w = quat.w + s * pTrigger->quat.w; - pos.x = pos.x + s * pTrigger->pos.x; - pos.y = pos.y + s * pTrigger->pos.y; - pos.z = pos.z + s * pTrigger->pos.z; - } - } - Assert( QuaternionNormalize( quat ) != 0); - QuaternionMatrix( quat, pos, bonematrix ); - } - - ConcatTransforms (bonetoworld.GetBone( pbones[ibone].parent ), bonematrix, bonetoworld.GetBoneForWrite( ibone )); -} - -/* - * This is for DoAimAtBone below, was just for testing, not needed in general - * but to turn it back on, uncomment this and the section in DoAimAtBone() below - * - -static ConVar aim_constraint( "aim_constraint", "1", FCVAR_REPLICATED, "Toggle Helper Bones" ); - -*/ - -//----------------------------------------------------------------------------- -// Purpose: Generate a procedural transformation so that one bone points at -// another point on the model -//----------------------------------------------------------------------------- -void DoAimAtBone( - mstudiobone_t *pBones, - int iBone, - CBoneAccessor &bonetoworld, - const CStudioHdr *pStudioHdr - ) -{ - mstudioaimatbone_t *pProc = (mstudioaimatbone_t *)pBones[iBone].pProcedure(); - - if ( !pProc ) - { - return; - } - - /* - * Uncomment this if the ConVar above is uncommented - * - - if ( !aim_constraint.GetBool() ) - { - // If the aim constraint is turned off then just copy the parent transform - // plus the offset value - - matrix3x4_t boneToWorldSpace; - MatrixCopy ( bonetoworld.GetBone( pProc->parent ), boneToWorldSpace ); - Vector boneWorldPosition; - VectorTransform( pProc->basepos, boneToWorldSpace, boneWorldPosition ); - MatrixSetColumn( boneWorldPosition, 3, boneToWorldSpace ); - MatrixCopy( boneToWorldSpace, bonetoworld.GetBoneForWrite( iBone ) ); - - return; - } - - */ - - // The world matrix of the bone to change - matrix3x4_t boneMatrix; - - // Guaranteed to be unit length - const Vector &userAimVector( pProc->aimvector ); - - // Guaranteed to be unit length - const Vector &userUpVector( pProc->upvector ); - - // Get to get position of bone but also for up reference - matrix3x4_t parentSpace; - MatrixCopy ( bonetoworld.GetBone( pProc->parent ), parentSpace ); - - // World space position of the bone to aim - Vector aimWorldPosition; - VectorTransform( pProc->basepos, parentSpace, aimWorldPosition ); - - // The worldspace matrix of the bone to aim at - matrix3x4_t aimAtSpace; - if ( pStudioHdr ) - { - // This means it's AIMATATTACH - const mstudioattachment_t &attachment( pStudioHdr->pAttachment( pProc->aim ) ); - ConcatTransforms( - bonetoworld.GetBone( attachment.localbone ), - attachment.local, - aimAtSpace ); - } - else - { - MatrixCopy( bonetoworld.GetBone( pProc->aim ), aimAtSpace ); - } - - Vector aimAtWorldPosition; - MatrixGetColumn( aimAtSpace, 3, aimAtWorldPosition ); - - Vector aimVector; - VectorSubtract( aimAtWorldPosition, aimWorldPosition, aimVector ); - VectorNormalizeFast( aimVector ); - - Vector axis; - CrossProduct( userAimVector, aimVector, axis ); - VectorNormalizeFast( axis ); - float angle( acosf( DotProduct( userAimVector, aimVector ) ) ); - Quaternion aimRotation; - AxisAngleQuaternion( axis, RAD2DEG( angle ), aimRotation ); - - if ( ( 1.0f - fabs( DotProduct( userUpVector, userAimVector ) ) ) > FLT_EPSILON ) - { - matrix3x4_t aimRotationMatrix; - QuaternionMatrix( aimRotation, aimRotationMatrix ); - - Vector tmpV; - - Vector tmp_pUp; - VectorRotate( userUpVector, aimRotationMatrix, tmp_pUp ); - VectorScale( aimVector, DotProduct( aimVector, tmp_pUp ), tmpV ); - Vector pUp; - VectorSubtract( tmp_pUp, tmpV, pUp ); - VectorNormalizeFast( pUp ); - - Vector tmp_pParentUp; - VectorRotate( userUpVector, parentSpace, tmp_pParentUp ); - VectorScale( aimVector, DotProduct( aimVector, tmp_pParentUp ), tmpV ); - Vector pParentUp; - VectorSubtract( tmp_pParentUp, tmpV, pParentUp ); - VectorNormalizeFast( pParentUp ); - - angle = acos( DotProduct( pUp, pParentUp ) ); - CrossProduct( pUp, pParentUp, axis ); - VectorNormalizeFast( axis ); - Quaternion upRotation; - AxisAngleQuaternion( axis, RAD2DEG( angle ), upRotation ); - - Quaternion boneRotation; - QuaternionMult( upRotation, aimRotation, boneRotation ); - QuaternionMatrix( boneRotation, aimWorldPosition, boneMatrix ); - } - else - { - QuaternionMatrix( aimRotation, aimWorldPosition, boneMatrix ); - } - - MatrixCopy( boneMatrix, bonetoworld.GetBoneForWrite( iBone ) ); -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- - -bool CalcProceduralBone( - const CStudioHdr *pStudioHdr, - int iBone, - CBoneAccessor &bonetoworld - ) -{ - mstudiobone_t *pbones = pStudioHdr->pBone( 0 ); - - if ( pbones[iBone].flags & BONE_ALWAYS_PROCEDURAL ) - { - switch( pbones[iBone].proctype ) - { - case STUDIO_PROC_AXISINTERP: - DoAxisInterpBone( pbones, iBone, bonetoworld ); - return true; - - case STUDIO_PROC_QUATINTERP: - DoQuatInterpBone( pbones, iBone, bonetoworld ); - return true; - - case STUDIO_PROC_AIMATBONE: - DoAimAtBone( pbones, iBone, bonetoworld, NULL ); - return true; - - case STUDIO_PROC_AIMATATTACH: - DoAimAtBone( pbones, iBone, bonetoworld, pStudioHdr ); - return true; - - default: - return false; - } - } - return false; -} - - - -//----------------------------------------------------------------------------- -// Purpose: Lookup a bone controller -//----------------------------------------------------------------------------- - - - -static mstudiobonecontroller_t* FindController( const CStudioHdr *pStudioHdr, int iController) -{ - // find first controller that matches the index - for (int i = 0; i < pStudioHdr->numbonecontrollers(); i++) - { - if (pStudioHdr->pBonecontroller( i )->inputfield == iController) - return pStudioHdr->pBonecontroller( i ); - } - - return NULL; -} - - -//----------------------------------------------------------------------------- -// Purpose: converts a ranged bone controller value into a 0..1 encoded value -// Output: ctlValue contains 0..1 encoding. -// returns clamped ranged value -//----------------------------------------------------------------------------- - -float Studio_SetController( const CStudioHdr *pStudioHdr, int iController, float flValue, float &ctlValue ) -{ - if (! pStudioHdr) - return flValue; - - mstudiobonecontroller_t *pbonecontroller = FindController(pStudioHdr, iController); - if(!pbonecontroller) - { - ctlValue = 0; - return flValue; - } - - // wrap 0..360 if it's a rotational controller - if (pbonecontroller->type & (STUDIO_XR | STUDIO_YR | STUDIO_ZR)) - { - // ugly hack, invert value if end < start - if (pbonecontroller->end < pbonecontroller->start) - flValue = -flValue; - - // does the controller not wrap? - if (pbonecontroller->start + 359.0 >= pbonecontroller->end) - { - if (flValue > ((pbonecontroller->start + pbonecontroller->end) / 2.0) + 180) - flValue = flValue - 360; - if (flValue < ((pbonecontroller->start + pbonecontroller->end) / 2.0) - 180) - flValue = flValue + 360; - } - else - { - if (flValue > 360) - flValue = flValue - (int)(flValue / 360.0) * 360.0; - else if (flValue < 0) - flValue = flValue + (int)((flValue / -360.0) + 1) * 360.0; - } - } - - ctlValue = (flValue - pbonecontroller->start) / (pbonecontroller->end - pbonecontroller->start); - if (ctlValue < 0) ctlValue = 0; - if (ctlValue > 1) ctlValue = 1; - - float flReturnVal = ((1.0 - ctlValue)*pbonecontroller->start + ctlValue *pbonecontroller->end); - - // ugly hack, invert value if a rotational controller and end < start - if (pbonecontroller->type & (STUDIO_XR | STUDIO_YR | STUDIO_ZR) && - pbonecontroller->end < pbonecontroller->start ) - { - flReturnVal *= -1; - } - - return flReturnVal; -} - - -//----------------------------------------------------------------------------- -// Purpose: converts a 0..1 encoded bone controller value into a ranged value -// Output: returns ranged value -//----------------------------------------------------------------------------- - -float Studio_GetController( const CStudioHdr *pStudioHdr, int iController, float ctlValue ) -{ - if (!pStudioHdr) - return 0.0; - - mstudiobonecontroller_t *pbonecontroller = FindController(pStudioHdr, iController); - if(!pbonecontroller) - return 0; - - return ctlValue * (pbonecontroller->end - pbonecontroller->start) + pbonecontroller->start; -} - - -//----------------------------------------------------------------------------- -// Purpose: converts a ranged pose parameter value into a 0..1 encoded value -// Output: ctlValue contains 0..1 encoding. -// returns clamped ranged value -//----------------------------------------------------------------------------- - -float Studio_SetPoseParameter( const CStudioHdr *pStudioHdr, int iParameter, float flValue, float &ctlValue ) -{ - if (iParameter < 0 || iParameter >= pStudioHdr->GetNumPoseParameters()) - { - return 0; - } - - const mstudioposeparamdesc_t &PoseParam = pStudioHdr->pPoseParameter( iParameter ); - - Assert( IsFinite( flValue ) ); - - if (PoseParam.loop) - { - float wrap = (PoseParam.start + PoseParam.end) / 2.0 + PoseParam.loop / 2.0; - float shift = PoseParam.loop - wrap; - - flValue = flValue - PoseParam.loop * floor((flValue + shift) / PoseParam.loop); - } - - ctlValue = (flValue - PoseParam.start) / (PoseParam.end - PoseParam.start); - - if (ctlValue < 0) ctlValue = 0; - if (ctlValue > 1) ctlValue = 1; - - Assert( IsFinite( ctlValue ) ); - - return ctlValue * (PoseParam.end - PoseParam.start) + PoseParam.start; -} - - -//----------------------------------------------------------------------------- -// Purpose: converts a 0..1 encoded pose parameter value into a ranged value -// Output: returns ranged value -//----------------------------------------------------------------------------- - -float Studio_GetPoseParameter( const CStudioHdr *pStudioHdr, int iParameter, float ctlValue ) -{ - if (iParameter < 0 || iParameter >= pStudioHdr->GetNumPoseParameters()) - { - return 0; - } - - const mstudioposeparamdesc_t &PoseParam = pStudioHdr->pPoseParameter( iParameter ); - - return ctlValue * (PoseParam.end - PoseParam.start) + PoseParam.start; -} - - -#pragma warning (disable : 4701) - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -static int ClipRayToHitbox( const Ray_t &ray, mstudiobbox_t *pbox, matrix3x4_t& matrix, trace_t &tr ) -{ - // scale by current t so hits shorten the ray and increase the likelihood of early outs - Vector delta2; - VectorScale( ray.m_Delta, (0.5f * tr.fraction), delta2 ); - - // OPTIMIZE: Store this in the box instead of computing it here - // compute center in local space - Vector boxextents; - boxextents.x = (pbox->bbmin.x + pbox->bbmax.x) * 0.5; - boxextents.y = (pbox->bbmin.y + pbox->bbmax.y) * 0.5; - boxextents.z = (pbox->bbmin.z + pbox->bbmax.z) * 0.5; - Vector boxCenter; - // transform to world space - VectorTransform( boxextents, matrix, boxCenter ); - // calc extents from local center - boxextents.x = pbox->bbmax.x - boxextents.x; - boxextents.y = pbox->bbmax.y - boxextents.y; - boxextents.z = pbox->bbmax.z - boxextents.z; - // OPTIMIZE: This is optimized for world space. If the transform is fast enough, it may make more - // sense to just xform and call UTIL_ClipToBox() instead. MEASURE THIS. - - // save the extents of the ray along - Vector extent, uextent; - Vector segmentCenter; - segmentCenter.x = ray.m_Start.x + delta2.x - boxCenter.x; - segmentCenter.y = ray.m_Start.y + delta2.y - boxCenter.y; - segmentCenter.z = ray.m_Start.z + delta2.z - boxCenter.z; - - extent.Init(); - - // check box axes for separation - for ( int j = 0; j < 3; j++ ) - { - extent[j] = delta2.x * matrix[0][j] + delta2.y * matrix[1][j] + delta2.z * matrix[2][j]; - uextent[j] = fabsf(extent[j]); - float coord = segmentCenter.x * matrix[0][j] + segmentCenter.y * matrix[1][j] + segmentCenter.z * matrix[2][j]; - coord = fabsf(coord); - - if ( coord > (boxextents[j] + uextent[j]) ) - return -1; - } - - // now check cross axes for separation - float tmp, cextent; - Vector cross; - CrossProduct( delta2, segmentCenter, cross ); - cextent = cross.x * matrix[0][0] + cross.y * matrix[1][0] + cross.z * matrix[2][0]; - cextent = fabsf(cextent); - tmp = boxextents[1]*uextent[2] + boxextents[2]*uextent[1]; - if ( cextent > tmp ) - return -1; - - cextent = cross.x * matrix[0][1] + cross.y * matrix[1][1] + cross.z * matrix[2][1]; - cextent = fabsf(cextent); - tmp = boxextents[0]*uextent[2] + boxextents[2]*uextent[0]; - if ( cextent > tmp ) - return -1; - - cextent = cross.x * matrix[0][2] + cross.y * matrix[1][2] + cross.z * matrix[2][2]; - cextent = fabsf(cextent); - tmp = boxextents[0]*uextent[1] + boxextents[1]*uextent[0]; - if ( cextent > tmp ) - return -1; - - // !!! We hit this box !!! compute intersection point and return - Vector start; - // Compute ray start in bone space - VectorITransform( ray.m_Start, matrix, start ); - // extent is delta2 in bone space, recompute delta in bone space - VectorScale( extent, 2, extent ); - - // delta was prescaled by the current t, so no need to see if this intersection - // is closer - trace_t boxTrace; - if ( !IntersectRayWithBox( start, extent, pbox->bbmin, pbox->bbmax, 0.0f, &boxTrace ) ) - return -1; - Assert( IsFinite(boxTrace.fraction) ); - tr.fraction *= boxTrace.fraction; - tr.startsolid = boxTrace.startsolid; - int hitside = boxTrace.plane.type; - if ( boxTrace.plane.normal[hitside] >= 0 ) - { - hitside += 3; - } - return hitside; -} - -#pragma warning (default : 4701) - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -bool SweepBoxToStudio( const Ray_t& ray, CStudioHdr *pStudioHdr, mstudiohitboxset_t *set, - matrix3x4_t **hitboxbones, int fContentsMask, trace_t &tr ) -{ - tr.fraction = 1.0; - tr.startsolid = false; - - // OPTIMIZE: Partition these? - Ray_t clippedRay = ray; - int hitbox = -1; - for ( int i = 0; i < set->numhitboxes; i++ ) - { - mstudiobbox_t *pbox = set->pHitbox(i); - - // Filter based on contents mask - int fBoneContents = pStudioHdr->pBone( pbox->bone )->contents; - if ( ( fBoneContents & fContentsMask ) == 0 ) - continue; - - trace_t obbTrace; - if ( IntersectRayWithOBB( clippedRay, *hitboxbones[pbox->bone], pbox->bbmin, pbox->bbmax, 0.0f, &obbTrace ) ) - { - tr.startpos = obbTrace.startpos; - tr.endpos = obbTrace.endpos; - tr.plane = obbTrace.plane; - tr.startsolid = obbTrace.startsolid; - tr.allsolid = obbTrace.allsolid; - - // This logic here is to shorten the ray each time to get more early outs - tr.fraction *= obbTrace.fraction; - clippedRay.m_Delta *= obbTrace.fraction; - hitbox = i; - if (tr.startsolid) - break; - } - } - - if ( hitbox >= 0 ) - { - tr.hitgroup = set->pHitbox(hitbox)->group; - tr.hitbox = hitbox; - tr.contents = pStudioHdr->pBone( set->pHitbox(hitbox)->bone )->contents | CONTENTS_HITBOX; - tr.physicsbone = pStudioHdr->pBone( set->pHitbox(hitbox)->bone )->physicsbone; - Assert( tr.physicsbone >= 0 ); - return true; - } - return false; -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -bool TraceToStudio( const Ray_t& ray, CStudioHdr *pStudioHdr, mstudiohitboxset_t *set, - matrix3x4_t **hitboxbones, int fContentsMask, trace_t &tr ) -{ - if ( !ray.m_IsRay ) - { - return SweepBoxToStudio( ray, pStudioHdr, set, hitboxbones, fContentsMask, tr ); - } - - tr.fraction = 1.0; - tr.startsolid = false; - - // no hit yet - int hitbox = -1; - int hitside = -1; - - // OPTIMIZE: Partition these? - for ( int i = 0; i < set->numhitboxes; i++ ) - { - mstudiobbox_t *pbox = set->pHitbox(i); - - // Filter based on contents mask - int fBoneContents = pStudioHdr->pBone( pbox->bone )->contents; - if ( ( fBoneContents & fContentsMask ) == 0 ) - continue; - - // columns are axes of the bones in world space, translation is in world space - matrix3x4_t& matrix = *hitboxbones[pbox->bone]; - - int side = ClipRayToHitbox( ray, pbox, matrix, tr ); - if ( side >= 0 ) - { - hitbox = i; - hitside = side; - } - } - - if ( hitbox >= 0 ) - { - mstudiobbox_t *pbox = set->pHitbox(hitbox); - VectorMA( ray.m_Start, tr.fraction, ray.m_Delta, tr.endpos ); - tr.hitgroup = set->pHitbox(hitbox)->group; - tr.hitbox = hitbox; - tr.contents = pStudioHdr->pBone( pbox->bone )->contents | CONTENTS_HITBOX; - tr.physicsbone = pStudioHdr->pBone( pbox->bone )->physicsbone; - Assert( tr.physicsbone >= 0 ); - matrix3x4_t& matrix = *hitboxbones[pbox->bone]; - if ( hitside >= 3 ) - { - hitside -= 3; - tr.plane.normal[0] = matrix[0][hitside]; - tr.plane.normal[1] = matrix[1][hitside]; - tr.plane.normal[2] = matrix[2][hitside]; - //tr.plane.dist = DotProduct( tr.plane.normal, Vector(matrix[0][3], matrix[1][3], matrix[2][3] ) ) + pbox->bbmax[hitside]; - } - else - { - tr.plane.normal[0] = -matrix[0][hitside]; - tr.plane.normal[1] = -matrix[1][hitside]; - tr.plane.normal[2] = -matrix[2][hitside]; - //tr.plane.dist = DotProduct( tr.plane.normal, Vector(matrix[0][3], matrix[1][3], matrix[2][3] ) ) - pbox->bbmin[hitside]; - } - // simpler plane constant equation - tr.plane.dist = DotProduct( tr.endpos, tr.plane.normal ); - tr.plane.type = 3; - return true; - } - return false; -} - - -//----------------------------------------------------------------------------- -// Purpose: returns array of animations and weightings for a sequence based on current pose parameters -//----------------------------------------------------------------------------- - -void Studio_SeqAnims( const CStudioHdr *pStudioHdr, mstudioseqdesc_t &seqdesc, int iSequence, const float poseParameter[], mstudioanimdesc_t *panim[4], float *weight ) -{ -#if _DEBUG - VPROF_INCREMENT_COUNTER("SEQ_ANIMS",1); -#endif - if (!pStudioHdr || iSequence >= pStudioHdr->GetNumSeq()) - { - weight[0] = weight[1] = weight[2] = weight[3] = 0.0; - return; - } - - int i0 = 0, i1 = 0; - float s0 = 0, s1 = 0; - - Studio_LocalPoseParameter( pStudioHdr, poseParameter, seqdesc, iSequence, 0, s0, i0 ); - Studio_LocalPoseParameter( pStudioHdr, poseParameter, seqdesc, iSequence, 1, s1, i1 ); - - panim[0] = &pStudioHdr->pAnimdesc( pStudioHdr->iRelativeAnim( iSequence, seqdesc.anim( i0 , i1 ) ) ); - weight[0] = (1 - s0) * (1 - s1); - - panim[1] = &pStudioHdr->pAnimdesc( pStudioHdr->iRelativeAnim( iSequence, seqdesc.anim( i0+1, i1 ) ) ); - weight[1] = (s0) * (1 - s1); - - panim[2] = &pStudioHdr->pAnimdesc( pStudioHdr->iRelativeAnim( iSequence, seqdesc.anim( i0 , i1+1 ) ) ); - weight[2] = (1 - s0) * (s1); - - panim[3] = &pStudioHdr->pAnimdesc( pStudioHdr->iRelativeAnim( iSequence, seqdesc.anim( i0+1, i1+1 ) ) ); - weight[3] = (s0) * (s1); - - Assert( weight[0] >= 0.0f && weight[1] >= 0.0f && weight[2] >= 0.0f && weight[3] >= 0.0f ); -} - -//----------------------------------------------------------------------------- -// Purpose: returns max frame number for a sequence -//----------------------------------------------------------------------------- - -int Studio_MaxFrame( const CStudioHdr *pStudioHdr, int iSequence, const float poseParameter[] ) -{ - mstudioanimdesc_t *panim[4]; - float weight[4]; - - mstudioseqdesc_t &seqdesc = pStudioHdr->pSeqdesc( iSequence ); - Studio_SeqAnims( pStudioHdr, seqdesc, iSequence, poseParameter, panim, weight ); - - float maxFrame = 0; - for (int i = 0; i < 4; i++) - { - if (weight[i] > 0) - { - maxFrame += panim[i]->numframes * weight[i]; - } - } - - if ( maxFrame > 1 ) - maxFrame -= 1; - - - // FIXME: why does the weights sometimes not exactly add it 1.0 and this sometimes rounds down? - return (maxFrame + 0.01); -} - - -//----------------------------------------------------------------------------- -// Purpose: returns frames per second of a sequence -//----------------------------------------------------------------------------- - -float Studio_FPS( const CStudioHdr *pStudioHdr, int iSequence, const float poseParameter[] ) -{ - mstudioanimdesc_t *panim[4]; - float weight[4]; - - mstudioseqdesc_t &seqdesc = pStudioHdr->pSeqdesc( iSequence ); - Studio_SeqAnims( pStudioHdr, seqdesc, iSequence, poseParameter, panim, weight ); - - float t = 0; - - for (int i = 0; i < 4; i++) - { - if (weight[i] > 0) - { - t += panim[i]->fps * weight[i]; - } - } - return t; -} - - -//----------------------------------------------------------------------------- -// Purpose: returns cycles per second of a sequence (cycles/second) -//----------------------------------------------------------------------------- - -float Studio_CPS( const CStudioHdr *pStudioHdr, mstudioseqdesc_t &seqdesc, int iSequence, const float poseParameter[] ) -{ - mstudioanimdesc_t *panim[4]; - float weight[4]; - - Studio_SeqAnims( pStudioHdr, seqdesc, iSequence, poseParameter, panim, weight ); - - float t = 0; - - for (int i = 0; i < 4; i++) - { - if (weight[i] > 0 && panim[i]->numframes > 1) - { - t += (panim[i]->fps / (panim[i]->numframes - 1)) * weight[i]; - } - } - return t; -} - -//----------------------------------------------------------------------------- -// Purpose: returns length (in seconds) of a sequence (seconds/cycle) -//----------------------------------------------------------------------------- - -float Studio_Duration( const CStudioHdr *pStudioHdr, int iSequence, const float poseParameter[] ) -{ - mstudioseqdesc_t &seqdesc = pStudioHdr->pSeqdesc( iSequence ); - float cps = Studio_CPS( pStudioHdr, seqdesc, iSequence, poseParameter ); - - if( cps == 0 ) - return 0.0f; - - return 1.0f/cps; -} - - -//----------------------------------------------------------------------------- -// Purpose: calculate changes in position and angle relative to the start of an animations cycle -// Output: updated position and angle, relative to the origin -// returns false if animation is not a movement animation -//----------------------------------------------------------------------------- - -bool Studio_AnimPosition( mstudioanimdesc_t *panim, float flCycle, Vector &vecPos, QAngle &vecAngle ) -{ - float prevframe = 0; - vecPos.Init( ); - vecAngle.Init( ); - - if (panim->nummovements == 0) - return false; - - int iLoops = 0; - if (flCycle > 1.0) - { - iLoops = (int)flCycle; - } - else if (flCycle < 0.0) - { - iLoops = (int)flCycle - 1; - } - flCycle = flCycle - iLoops; - - float flFrame = flCycle * (panim->numframes - 1); - - for (int i = 0; i < panim->nummovements; i++) - { - mstudiomovement_t *pmove = panim->pMovement( i ); - - if (pmove->endframe >= flFrame) - { - float f = (flFrame - prevframe) / (pmove->endframe - prevframe); - - float d = pmove->v0 * f + 0.5 * (pmove->v1 - pmove->v0) * f * f; - - vecPos = vecPos + d * pmove->vector; - vecAngle.y = vecAngle.y * (1 - f) + pmove->angle * f; - if (iLoops != 0) - { - mstudiomovement_t *pmove = panim->pMovement( panim->nummovements - 1 ); - vecPos = vecPos + iLoops * pmove->position; - vecAngle.y = vecAngle.y + iLoops * pmove->angle; - } - return true; - } - else - { - prevframe = pmove->endframe; - vecPos = pmove->position; - vecAngle.y = pmove->angle; - } - } - - return false; -} - - -//----------------------------------------------------------------------------- -// Purpose: calculate instantaneous velocity in ips at a given point -// in the animations cycle -// Output: velocity vector, relative to identity orientation -// returns false if animation is not a movement animation -//----------------------------------------------------------------------------- - -bool Studio_AnimVelocity( mstudioanimdesc_t *panim, float flCycle, Vector &vecVelocity ) -{ - float prevframe = 0; - - float flFrame = flCycle * (panim->numframes - 1); - flFrame = flFrame - (int)(flFrame / (panim->numframes - 1)); - - for (int i = 0; i < panim->nummovements; i++) - { - mstudiomovement_t *pmove = panim->pMovement( i ); - - if (pmove->endframe >= flFrame) - { - float f = (flFrame - prevframe) / (pmove->endframe - prevframe); - - float vel = pmove->v0 * (1 - f) + pmove->v1 * f; - // scale from per block to per sec velocity - vel = vel * panim->fps / (pmove->endframe - prevframe); - - vecVelocity = pmove->vector * vel; - return true; - } - else - { - prevframe = pmove->endframe; - } - } - return false; -} - - -//----------------------------------------------------------------------------- -// Purpose: calculate changes in position and angle between two points in an animation cycle -// Output: updated position and angle, relative to CycleFrom being at the origin -// returns false if animation is not a movement animation -//----------------------------------------------------------------------------- - -bool Studio_AnimMovement( mstudioanimdesc_t *panim, float flCycleFrom, float flCycleTo, Vector &deltaPos, QAngle &deltaAngle ) -{ - if (panim->nummovements == 0) - return false; - - Vector startPos; - QAngle startA; - Studio_AnimPosition( panim, flCycleFrom, startPos, startA ); - - Vector endPos; - QAngle endA; - Studio_AnimPosition( panim, flCycleTo, endPos, endA ); - - Vector tmp = endPos - startPos; - deltaAngle.y = endA.y - startA.y; - VectorYawRotate( tmp, -startA.y, deltaPos ); - - return true; -} - - -//----------------------------------------------------------------------------- -// Purpose: finds how much of an animation to play to move given linear distance -//----------------------------------------------------------------------------- - -float Studio_FindAnimDistance( mstudioanimdesc_t *panim, float flDist ) -{ - float prevframe = 0; - - if (flDist <= 0) - return 0.0; - - for (int i = 0; i < panim->nummovements; i++) - { - mstudiomovement_t *pmove = panim->pMovement( i ); - - float flMove = (pmove->v0 + pmove->v1) * 0.5; - - if (flMove >= flDist) - { - float root1, root2; - - // d = V0 * t + 1/2 (V1-V0) * t^2 - if (SolveQuadratic( 0.5 * (pmove->v1 - pmove->v0), pmove->v0, -flDist, root1, root2 )) - { - float cpf = 1.0 / (panim->numframes - 1); // cycles per frame - - return (prevframe + root1 * (pmove->endframe - prevframe)) * cpf; - } - return 0.0; - } - else - { - flDist -= flMove; - prevframe = pmove->endframe; - } - } - return 1.0; -} - - -//----------------------------------------------------------------------------- -// Purpose: calculate changes in position and angle between two points in a sequences cycle -// Output: updated position and angle, relative to CycleFrom being at the origin -// returns false if sequence is not a movement sequence -//----------------------------------------------------------------------------- - -bool Studio_SeqMovement( const CStudioHdr *pStudioHdr, int iSequence, float flCycleFrom, float flCycleTo, const float poseParameter[], Vector &deltaPos, QAngle &deltaAngles ) -{ - mstudioanimdesc_t *panim[4]; - float weight[4]; - - mstudioseqdesc_t &seqdesc = pStudioHdr->pSeqdesc( iSequence ); - - Studio_SeqAnims( pStudioHdr, seqdesc, iSequence, poseParameter, panim, weight ); - - deltaPos.Init( ); - deltaAngles.Init( ); - - bool found = false; - - for (int i = 0; i < 4; i++) - { - if (weight[i]) - { - Vector localPos; - QAngle localAngles; - - localPos.Init(); - localAngles.Init(); - - if (Studio_AnimMovement( panim[i], flCycleFrom, flCycleTo, localPos, localAngles )) - { - found = true; - deltaPos = deltaPos + localPos * weight[i]; - // FIXME: this makes no sense - deltaAngles = deltaAngles + localAngles * weight[i]; - } - else if (!(panim[i]->flags & STUDIO_DELTA) && panim[i]->nummovements == 0 && seqdesc.weight(0) > 0.0) - { - found = true; - } - } - } - return found; -} - - -//----------------------------------------------------------------------------- -// Purpose: calculate instantaneous velocity in ips at a given point in the sequence's cycle -// Output: velocity vector, relative to identity orientation -// returns false if sequence is not a movement sequence -//----------------------------------------------------------------------------- - -bool Studio_SeqVelocity( const CStudioHdr *pStudioHdr, int iSequence, float flCycle, const float poseParameter[], Vector &vecVelocity ) -{ - mstudioanimdesc_t *panim[4]; - float weight[4]; - - mstudioseqdesc_t &seqdesc = pStudioHdr->pSeqdesc( iSequence ); - Studio_SeqAnims( pStudioHdr, seqdesc, iSequence, poseParameter, panim, weight ); - - vecVelocity.Init( ); - - bool found = false; - - for (int i = 0; i < 4; i++) - { - if (weight[i]) - { - Vector vecLocalVelocity; - - if (Studio_AnimVelocity( panim[i], flCycle, vecLocalVelocity )) - { - vecVelocity = vecVelocity + vecLocalVelocity * weight[i]; - found = true; - } - } - } - return found; -} - -//----------------------------------------------------------------------------- -// Purpose: finds how much of an sequence to play to move given linear distance -//----------------------------------------------------------------------------- - -float Studio_FindSeqDistance( const CStudioHdr *pStudioHdr, int iSequence, const float poseParameter[], float flDist ) -{ - mstudioanimdesc_t *panim[4]; - float weight[4]; - - mstudioseqdesc_t &seqdesc = pStudioHdr->pSeqdesc( iSequence ); - Studio_SeqAnims( pStudioHdr, seqdesc, iSequence, poseParameter, panim, weight ); - - float flCycle = 0; - - for (int i = 0; i < 4; i++) - { - if (weight[i]) - { - float flLocalCycle = Studio_FindAnimDistance( panim[i], flDist ); - flCycle = flCycle + flLocalCycle * weight[i]; - } - } - return flCycle; -} - -//----------------------------------------------------------------------------- -// Purpose: lookup attachment by name -//----------------------------------------------------------------------------- - -int Studio_FindAttachment( const CStudioHdr *pStudioHdr, const char *pAttachmentName ) -{ - if ( pStudioHdr && pStudioHdr->SequencesAvailable() ) - { - // Extract the bone index from the name - for (int i = 0; i < pStudioHdr->GetNumAttachments(); i++) - { - if (!stricmp(pAttachmentName,pStudioHdr->pAttachment(i).pszName( ))) - { - return i; - } - } - } - - return -1; -} - -//----------------------------------------------------------------------------- -// Purpose: lookup attachments by substring. Randomly return one of the matching attachments. -//----------------------------------------------------------------------------- - -int Studio_FindRandomAttachment( const CStudioHdr *pStudioHdr, const char *pAttachmentName ) -{ - if ( pStudioHdr ) - { - // First move them all matching attachments into a list - CUtlVector matchingAttachments; - - // Extract the bone index from the name - for (int i = 0; i < pStudioHdr->GetNumAttachments(); i++) - { - if ( strstr( pStudioHdr->pAttachment(i).pszName(), pAttachmentName ) ) - { - matchingAttachments.AddToTail(i); - } - } - - // Then randomly return one of the attachments - if ( matchingAttachments.Size() > 0 ) - return matchingAttachments[ RandomInt( 0, matchingAttachments.Size()-1 ) ]; - } - - return -1; -} - -//----------------------------------------------------------------------------- -// Purpose: lookup bone by name -//----------------------------------------------------------------------------- - -int Studio_BoneIndexByName( const CStudioHdr *pStudioHdr, const char *pName ) -{ - // binary search for the bone matching pName - int start = 0, end = pStudioHdr->numbones()-1; - const byte *pBoneTable = pStudioHdr->GetBoneTableSortedByName(); - mstudiobone_t *pbones = pStudioHdr->pBone( 0 ); - while (start <= end) - { - int mid = (start + end) >> 1; - int cmp = Q_stricmp( pbones[pBoneTable[mid]].pszName(), pName ); - - if ( cmp < 0 ) - { - start = mid + 1; - } - else if ( cmp > 0 ) - { - end = mid - 1; - } - else - { - return pBoneTable[mid]; - } - } - return -1; -} - -const char *Studio_GetDefaultSurfaceProps( CStudioHdr *pstudiohdr ) -{ - return pstudiohdr->pszSurfaceProp(); -} - -float Studio_GetMass( CStudioHdr *pstudiohdr ) -{ - return pstudiohdr->mass(); -} - -//----------------------------------------------------------------------------- -// Purpose: return pointer to sequence key value buffer -//----------------------------------------------------------------------------- - -const char *Studio_GetKeyValueText( const CStudioHdr *pStudioHdr, int iSequence ) -{ - if (pStudioHdr && pStudioHdr->SequencesAvailable()) - { - if (iSequence >= 0 && iSequence < pStudioHdr->GetNumSeq()) - { - return pStudioHdr->pSeqdesc( iSequence ).KeyValueText(); - } - } - return NULL; -} - -bool Studio_PrefetchSequence( const CStudioHdr *pStudioHdr, int iSequence ) -{ - bool pendingload = false; - mstudioseqdesc_t &seqdesc = pStudioHdr->pSeqdesc( iSequence ); - int size0 = seqdesc.groupsize[ 0 ]; - int size1 = seqdesc.groupsize[ 1 ]; - for ( int i = 0; i < size0; ++i ) - { - for ( int j = 0; j < size1; ++j ) - { - mstudioanimdesc_t &animdesc = pStudioHdr->pAnimdesc( seqdesc.anim( i, j ) ); - mstudioanim_t *panim = animdesc.pAnim(); - if ( !panim ) - { - pendingload = true; - } - } - } - - // Everything for this sequence is resident? - return !pendingload; -} \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + +#include "tier0/dbg.h" +#include "mathlib.h" +#include "bone_setup.h" +#include + +#include "collisionutils.h" +#include "vstdlib/random.h" +#include "tier0/vprof.h" +#include "bone_accessor.h" +#include "bitvec.h" +#include "datamanager.h" +#include "convar.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +// ----------------------------------------------------------------- +CBoneCache *CBoneCache::CreateResource( const bonecacheparams_t ¶ms ) +{ + short studioToCachedIndex[MAXSTUDIOBONES]; + short cachedToStudioIndex[MAXSTUDIOBONES]; + int cachedBoneCount = 0; + for ( int i = 0; i < params.pStudioHdr->numbones(); i++ ) + { + // skip bones that aren't part of the boneMask (and aren't the root bone) + if (i != 0 && !(params.pStudioHdr->pBone(i)->flags & params.boneMask)) + { + studioToCachedIndex[i] = -1; + continue; + } + studioToCachedIndex[i] = cachedBoneCount; + cachedToStudioIndex[cachedBoneCount] = i; + cachedBoneCount++; + } + int tableSizeStudio = sizeof(short) * params.pStudioHdr->numbones(); + int tableSizeCached = sizeof(short) * cachedBoneCount; + int matrixSize = sizeof(matrix3x4_t) * cachedBoneCount; + int size = sizeof(CBoneCache) + tableSizeStudio + tableSizeCached + matrixSize; + + CBoneCache *pMem = (CBoneCache *)malloc( size ); + Construct( pMem ); + pMem->Init( params, size, studioToCachedIndex, cachedToStudioIndex, cachedBoneCount ); + return pMem; +} + +unsigned int CBoneCache::EstimatedSize( const bonecacheparams_t ¶ms ) +{ + // conservative estimate - max size + return params.pStudioHdr->numbones() * (sizeof(short) + sizeof(short) + sizeof(matrix3x4_t)); +} + +void CBoneCache::DestroyResource() +{ + free( this ); +} + + +CBoneCache::CBoneCache() +{ + m_size = 0; + m_cachedBoneCount = 0; +} + +void CBoneCache::Init( const bonecacheparams_t ¶ms, unsigned int size, short *pStudioToCached, short *pCachedToStudio, int cachedBoneCount ) +{ + m_cachedBoneCount = cachedBoneCount; + m_size = size; + m_timeValid = params.curtime; + m_boneMask = params.boneMask; + + int studioTableSize = params.pStudioHdr->numbones() * sizeof(short); + m_cachedToStudioOffset = studioTableSize; + memcpy( StudioToCached(), pStudioToCached, studioTableSize ); + + int cachedTableSize = cachedBoneCount * sizeof(short); + memcpy( CachedToStudio(), pCachedToStudio, cachedTableSize ); + + m_matrixOffset = m_cachedToStudioOffset + cachedTableSize; + + UpdateBones( params.pBoneToWorld, params.pStudioHdr->numbones(), params.curtime ); +} + +void CBoneCache::UpdateBones( const matrix3x4_t *pBoneToWorld, int numbones, float curtime ) +{ + matrix3x4_t *pBones = BoneArray(); + const short *pCachedToStudio = CachedToStudio(); + + for ( int i = 0; i < m_cachedBoneCount; i++ ) + { + int index = pCachedToStudio[i]; + MatrixCopy( pBoneToWorld[index], pBones[i] ); + } + m_timeValid = curtime; +} + +matrix3x4_t *CBoneCache::GetCachedBone( int studioIndex ) +{ + int cachedIndex = StudioToCached()[studioIndex]; + if ( cachedIndex >= 0 ) + { + return BoneArray() + cachedIndex; + } + return NULL; +} + +void CBoneCache::ReadCachedBones( matrix3x4_t *pBoneToWorld ) +{ + matrix3x4_t *pBones = BoneArray(); + const short *pCachedToStudio = CachedToStudio(); + for ( int i = 0; i < m_cachedBoneCount; i++ ) + { + MatrixCopy( pBones[i], pBoneToWorld[pCachedToStudio[i]] ); + } +} + +void CBoneCache::ReadCachedBonePointers( matrix3x4_t **bones, int numbones ) +{ + memset( bones, 0, sizeof(matrix3x4_t *) * numbones ); + matrix3x4_t *pBones = BoneArray(); + const short *pCachedToStudio = CachedToStudio(); + for ( int i = 0; i < m_cachedBoneCount; i++ ) + { + bones[pCachedToStudio[i]] = pBones + i; + } +} + +bool CBoneCache::IsValid( float time, float dt ) +{ + if ( time - m_timeValid <= dt ) + return true; + return false; +} + + +// private functions +matrix3x4_t *CBoneCache::BoneArray() +{ + return (matrix3x4_t *)( (char *)(this+1) + m_matrixOffset ); +} + +short *CBoneCache::StudioToCached() +{ + return (short *)( (char *)(this+1) ); +} + +short *CBoneCache::CachedToStudio() +{ + return (short *)( (char *)(this+1) + m_cachedToStudioOffset ); +} + +// Construct a singleton +static CDataManager g_StudioBoneCache( 16 * 1024L ); + +CBoneCache *Studio_GetBoneCache( memhandle_t cacheHandle ) +{ + return g_StudioBoneCache.GetResource_NoLock( cacheHandle ); +} + +memhandle_t Studio_CreateBoneCache( bonecacheparams_t ¶ms ) +{ + return g_StudioBoneCache.CreateResource( params ); +} + +void Studio_DestroyBoneCache( memhandle_t cacheHandle ) +{ + g_StudioBoneCache.DestroyResource( cacheHandle ); +} + +void Studio_InvalidateBoneCache( memhandle_t cacheHandle ) +{ + CBoneCache *pCache = g_StudioBoneCache.GetResource_NoLock( cacheHandle ); + if ( pCache ) + { + pCache->m_timeValid = -1.0f; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- + +void BuildBoneChain( + const CStudioHdr *pStudioHdr, + const matrix3x4_t &rootxform, + const Vector pos[], + const Quaternion q[], + int iBone, + matrix3x4_t *pBoneToWorld ) +{ + CBoneBitList boneComputed; + BuildBoneChain( pStudioHdr, rootxform, pos, q, iBone, pBoneToWorld, boneComputed ); + return; +} + + + +//----------------------------------------------------------------------------- +// Purpose: return a sub frame rotation for a single bone +//----------------------------------------------------------------------------- + + +void ExtractAnimValue( int frame, + mstudioanimvalue_t *panimvalue, + float scale, + float &v1, float &v2 ) +{ + if (!panimvalue) + { + v1 = v2 = 0; + return; + } + + int k = frame; + + while (panimvalue->num.total <= k) + { + k -= panimvalue->num.total; + panimvalue += panimvalue->num.valid + 1; + if ( panimvalue->num.total == 0 ) + { + v1 = v2 = 0; + return; + } + } + // Bah, missing blend! + if (panimvalue->num.valid > k) + { + v1 = panimvalue[k+1].value * scale; + + if (panimvalue->num.valid > k + 1) + { + v2 = panimvalue[k+2].value * scale; + } + else + { + if (panimvalue->num.total > k + 1) + v2 = v1; + else + v2 = panimvalue[panimvalue->num.valid+2].value * scale; + } + } + else + { + v1 = panimvalue[panimvalue->num.valid].value * scale; + if (panimvalue->num.total > k + 1) + { + v2 = v1; + } + else + { + v2 = panimvalue[panimvalue->num.valid + 2].value * scale; + } + } +} + + +//----------------------------------------------------------------------------- +// Purpose: return a sub frame rotation for a single bone +//----------------------------------------------------------------------------- +void CalcBoneQuaternion( int frame, float s, + const mstudiobone_t *pbone, const mstudioanim_t *panim, Quaternion &q ) +{ + if (panim->flags & STUDIO_ANIM_RAWROT) + { + q = *(panim->pQuat()); + Assert( q.IsValid() ); + return; + } + else if (!(panim->flags & STUDIO_ANIM_ANIMROT)) + { + if (panim->flags & STUDIO_ANIM_DELTA) + { + q.Init( 0.0f, 0.0f, 0.0f, 1.0f ); + } + else + { + q = pbone->quat; + } + return; + } + + Quaternion q1, q2; + RadianEuler angle1, angle2; + mstudioanim_valueptr_t *pValuesPtr = panim->pRotV(); + + ExtractAnimValue( frame, pValuesPtr->pAnimvalue( 0 ), pbone->rotscale.x, angle1.x, angle2.x ); + ExtractAnimValue( frame, pValuesPtr->pAnimvalue( 1 ), pbone->rotscale.y, angle1.y, angle2.y ); + ExtractAnimValue( frame, pValuesPtr->pAnimvalue( 2 ), pbone->rotscale.z, angle1.z, angle2.z ); + + if (!(panim->flags & STUDIO_ANIM_DELTA)) + { + angle1.x = angle1.x + pbone->rot.x; + angle1.y = angle1.y + pbone->rot.y; + angle1.z = angle1.z + pbone->rot.z; + angle2.x = angle2.x + pbone->rot.x; + angle2.y = angle2.y + pbone->rot.y; + angle2.z = angle2.z + pbone->rot.z; + } + + Assert( angle1.IsValid() && angle2.IsValid() ); + if (angle1.x != angle2.x || angle1.y != angle2.y || angle1.z != angle2.z) + { + AngleQuaternion( angle1, q1 ); + AngleQuaternion( angle2, q2 ); + QuaternionBlend( q1, q2, s, q ); + } + else + { + AngleQuaternion( angle1, q ); + } + + Assert( q.IsValid() ); + + // align to unified bone + if (!(panim->flags & STUDIO_ANIM_DELTA) && (pbone->flags & BONE_FIXED_ALIGNMENT)) + { + QuaternionAlign( pbone->qAlignment, q, q ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: return a sub frame position for a single bone +//----------------------------------------------------------------------------- +void CalcBonePosition( int frame, float s, + const mstudiobone_t *pbone, const mstudioanim_t *panim, Vector &pos ) +{ + if (panim->flags & STUDIO_ANIM_RAWPOS) + { + pos = *(panim->pPos()); + Assert( pos.IsValid() ); + + return; + } + else if (!(panim->flags & STUDIO_ANIM_ANIMPOS)) + { + if (panim->flags & STUDIO_ANIM_DELTA) + { + pos.Init( 0.0f, 0.0f, 0.0f ); + } + else + { + pos = pbone->pos; + } + return; + } + + mstudioanim_valueptr_t *pPosV = panim->pPosV(); + int j; + float v1, v2; + + for (j = 0; j < 3; j++) + { + ExtractAnimValue( frame, pPosV->pAnimvalue( j ), pbone->posscale[j], v1, v2 ); + pos[j] = v1 * (1.0 - s) + v2 * s; + } + + if (!(panim->flags & STUDIO_ANIM_DELTA)) + { + pos.x = pos.x + pbone->pos.x; + pos.y = pos.y + pbone->pos.y; + pos.z = pos.z + pbone->pos.z; + } + + Assert( pos.IsValid() ); +} + + +void SetupSingleBoneMatrix( + CStudioHdr *pOwnerHdr, + int nSequence, + int iFrame, + int iBone, + matrix3x4_t &mBoneLocal ) +{ + mstudioseqdesc_t &seqdesc = pOwnerHdr->pSeqdesc( nSequence ); + mstudioanimdesc_t &animdesc = pOwnerHdr->pAnimdesc( seqdesc.anim( 0, 0 ) ); + mstudioanim_t *panim = animdesc.pAnim( ); + float s = 0; + mstudiobone_t *pbone = pOwnerHdr->pBone( iBone ); + + Quaternion boneQuat; + Vector bonePos; + + // search for bone + while (panim && panim->bone != iBone) + { + panim = panim->pNext(); + } + + // look up animation if found, if not, initialize + if (panim && seqdesc.weight(iBone) > 0) + { + CalcBoneQuaternion( iFrame, s, pbone, panim, boneQuat ); + CalcBonePosition ( iFrame, s, pbone, panim, bonePos ); + } + else if (animdesc.flags & STUDIO_DELTA) + { + boneQuat.Init( 0.0f, 0.0f, 0.0f, 1.0f ); + bonePos.Init( 0.0f, 0.0f, 0.0f ); + } + else + { + boneQuat = pbone->quat; + bonePos = pbone->pos; + } + + QuaternionMatrix( boneQuat, bonePos, mBoneLocal ); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +static void CalcVirtualAnimation( virtualmodel_t *pVModel, const CStudioHdr *pStudioHdr, Vector *pos, Quaternion *q, + mstudioseqdesc_t &seqdesc, int sequence, int animation, + float cycle, int boneMask ) +{ + int i, j, k; + + const mstudiobone_t *pbone; + const virtualgroup_t *pSeqGroup; + const studiohdr_t *pSeqStudioHdr; + const mstudiobone_t *pSeqbone; + const mstudioanim_t *panim; + const studiohdr_t *pAnimStudioHdr; + const mstudiobone_t *pAnimbone; + const virtualgroup_t *pAnimGroup; + + pSeqGroup = pVModel->pSeqGroup( sequence ); + int baseanimation = pStudioHdr->iRelativeAnim( sequence, animation ); + mstudioanimdesc_t &animdesc = pStudioHdr->pAnimdesc( baseanimation ); + pSeqStudioHdr = pStudioHdr->pSeqStudioHdr( sequence ); + pSeqbone = pSeqStudioHdr->pBone( 0 ); + pAnimGroup = pVModel->pAnimGroup( baseanimation ); + panim = animdesc.pAnim( ); + pAnimStudioHdr = pStudioHdr->pAnimStudioHdr( baseanimation ); + pAnimbone = pAnimStudioHdr->pBone( 0 ); + + int iFrame; + float s; + + float fFrame = cycle * (animdesc.numframes - 1); + + iFrame = (int)fFrame; + s = (fFrame - iFrame); + + float *pweight = seqdesc.pBoneweight( 0 ); + pbone = pStudioHdr->pBone( 0 ); + + for (i = 0; i < pStudioHdr->numbones(); i++) + { + if (pbone[i].flags & boneMask) + { + int j = pSeqGroup->boneMap[i]; + if (j >= 0 && pweight[j] > 0.0f) + { + if (animdesc.flags & STUDIO_DELTA) + { + q[i].Init( 0.0f, 0.0f, 0.0f, 1.0f ); + pos[i].Init( 0.0f, 0.0f, 0.0f ); + } + else + { + q[i] = pSeqbone[j].quat; + pos[i] = pSeqbone[j].pos; + } + } + } + } + + // if the animation isn't available, look for the zero frame cache + if (!panim) + { + byte *pData = pAnimStudioHdr->pZeroframeCache( pVModel->m_anim[baseanimation].index ); + + if (pData) + { + // Msg("zeroframe %s\n", animdesc.pszName() ); + for (j = 0; j < pAnimStudioHdr->numbones; j++) + { + i = pAnimGroup->masterBone[j]; + + if (pAnimbone[j].flags & BONE_HAS_SAVEFRAME_POS) + { + if ((i >= 0) && (pbone[i].flags & boneMask)) + { + pos[i] = *(Vector48 *)pData; + } + pData += sizeof( Vector48 ); + } + if (!(animdesc.flags & STUDIO_DELTA) && (pAnimbone[j].flags & BONE_HAS_SAVEFRAME_ROT) != 0) + { + if ((i >= 0) && (pbone[i].flags & boneMask)) + { + q[i] = *(Quaternion32 *)pData; + } + pData += sizeof( Quaternion32 ); + } + } + return; + } + } + + // FIXME: change encoding so that bone -1 is never the case + while (panim && panim->bone < 255) + { + j = pAnimGroup->masterBone[panim->bone]; + if (j >= 0 && (pbone[j].flags & boneMask)) + { + k = pSeqGroup->boneMap[j]; + + if (k >= 0 && pweight[k] > 0.0f) + { + CalcBoneQuaternion( iFrame, s, &pAnimbone[panim->bone], panim, q[j] ); + CalcBonePosition ( iFrame, s, &pAnimbone[panim->bone], panim, pos[j] ); + } + } + panim = panim->pNext(); + } +} + + + + +static void CalcAnimation( const CStudioHdr *pStudioHdr, Vector *pos, Quaternion *q, + mstudioseqdesc_t &seqdesc, + int sequence, int animation, + float cycle, int boneMask ) +{ + int i; + + virtualmodel_t *pVModel = pStudioHdr->GetVirtualModel(); + + if (pVModel) + { + CalcVirtualAnimation( pVModel, pStudioHdr, pos, q, seqdesc, sequence, animation, cycle, boneMask ); + return; + } + + mstudioanimdesc_t &animdesc = pStudioHdr->pAnimdesc( animation ); + mstudiobone_t *pbone = pStudioHdr->pBone( 0 ); + mstudioanim_t *panim = animdesc.pAnim( ); + + int iFrame; + float s; + + float fFrame = cycle * (animdesc.numframes - 1); + + iFrame = (int)fFrame; + s = (fFrame - iFrame); + + float *pweight = seqdesc.pBoneweight( 0 ); + + // if the animation isn't available, look for the zero frame cache + if (!panim) + { + byte *pData = pStudioHdr->pZeroframeCache( animation ); + + if (pData) + { + // Msg("zeroframe %s\n", animdesc.pszName() ); + for (i = 0; i < pStudioHdr->numbones(); i++, pbone++, pweight++) + { + if (*pweight > 0 && (pbone->flags & boneMask)) + { + if (animdesc.flags & STUDIO_DELTA) + { + q[i].Init( 0.0f, 0.0f, 0.0f, 1.0f ); + pos[i].Init( 0.0f, 0.0f, 0.0f ); + } + else + { + q[i] = pbone->quat; + pos[i] = pbone->pos; + } + } + + if (pbone->flags & BONE_HAS_SAVEFRAME_POS) + { + if (*pweight > 0 && (pbone->flags & boneMask)) + { + pos[i] = *(Vector48 *)pData; + Assert( pos[i].IsValid() ); + } + pData += sizeof( Vector48 ); + } + if (!(animdesc.flags & STUDIO_DELTA) && (pbone->flags & BONE_HAS_SAVEFRAME_ROT) != 0) + { + if (*pweight > 0 && (pbone->flags & boneMask)) + { + q[i] = *(Quaternion32 *)pData; + Assert( q[i].IsValid() ); + } + pData += sizeof( Quaternion32 ); + } + } + return; + } + } + + // BUGBUG: the sequence, the anim, and the model can have all different bone mappings. + for (i = 0; i < pStudioHdr->numbones(); i++, pbone++, pweight++) + { + if (panim && panim->bone == i) + { + if (*pweight > 0 && (pbone->flags & boneMask)) + { + CalcBoneQuaternion( iFrame, s, pbone, panim, q[i] ); + CalcBonePosition ( iFrame, s, pbone, panim, pos[i] ); + } + panim = panim->pNext(); + } + else if (*pweight > 0 && (pbone->flags & boneMask)) + { + if (animdesc.flags & STUDIO_DELTA) + { + q[i].Init( 0.0f, 0.0f, 0.0f, 1.0f ); + pos[i].Init( 0.0f, 0.0f, 0.0f ); + } + else + { + q[i] = pbone->quat; + pos[i] = pbone->pos; + } + } + } +} + + + +// qt = ( s * p ) * q +void QuaternionSM( float s, const Quaternion &p, const Quaternion &q, Quaternion &qt ) +{ + Quaternion p1, q1; + + QuaternionScale( p, s, p1 ); + QuaternionMult( p1, q, q1 ); + QuaternionNormalize( q1 ); + qt[0] = q1[0]; + qt[1] = q1[1]; + qt[2] = q1[2]; + qt[3] = q1[3]; +} + +// qt = p * ( s * q ) +void QuaternionMA( const Quaternion &p, float s, const Quaternion &q, Quaternion &qt ) +{ + Quaternion p1, q1; + + QuaternionScale( q, s, q1 ); + QuaternionMult( p, q1, p1 ); + QuaternionNormalize( p1 ); + qt[0] = p1[0]; + qt[1] = p1[1]; + qt[2] = p1[2]; + qt[3] = p1[3]; +} + + +// qt = p * ( s * q ) +void QuaternionAccumulate( const Quaternion &p, float s, const Quaternion &q, Quaternion &qt ) +{ + Quaternion q2; + QuaternionAlign( p, q, q2 ); + + qt[0] = p[0] + s * q2[0]; + qt[1] = p[1] + s * q2[1]; + qt[2] = p[2] + s * q2[2]; + qt[3] = p[3] + s * q2[3]; +} + + + +//----------------------------------------------------------------------------- +// Purpose: blend together in world space q1,pos1 with q2,pos2. Return result in q1,pos1. +// 0 returns q1, pos1. 1 returns q2, pos2 +//----------------------------------------------------------------------------- + +void WorldSpaceSlerp( + const CStudioHdr *pStudioHdr, + Quaternion q1[MAXSTUDIOBONES], + Vector pos1[MAXSTUDIOBONES], + mstudioseqdesc_t &seqdesc, + int sequence, + const Quaternion q2[MAXSTUDIOBONES], + const Vector pos2[MAXSTUDIOBONES], + float s, + int boneMask ) +{ + int i, j; + float s1; // weight of parent for q2, pos2 + float s2; // weight for q2, pos2 + + // make fake root transform + matrix3x4_t rootXform; + SetIdentityMatrix( rootXform ); + + // matrices for q2, pos2 + static matrix3x4_t srcBoneToWorld[MAXSTUDIOBONES]; + CBoneBitList srcBoneComputed; + + static matrix3x4_t destBoneToWorld[MAXSTUDIOBONES]; + CBoneBitList destBoneComputed; + + static matrix3x4_t targetBoneToWorld[MAXSTUDIOBONES]; + CBoneBitList targetBoneComputed; + + virtualmodel_t *pVModel = pStudioHdr->GetVirtualModel(); + const virtualgroup_t *pSeqGroup = NULL; + if (pVModel) + { + pSeqGroup = pVModel->pSeqGroup( sequence ); + } + + mstudiobone_t *pbone = pStudioHdr->pBone( 0 ); + + for (i = 0; i < pStudioHdr->numbones(); i++) + { + // skip unused bones + if (!(pbone[i].flags & boneMask)) + { + continue; + } + + int n = pbone[i].parent; + s1 = 0.0; + if (pSeqGroup) + { + j = pSeqGroup->boneMap[i]; + if (j >= 0) + { + s2 = s * seqdesc.weight( j ); // blend in based on this bones weight + if (n != -1) + { + s1 = s * seqdesc.weight( pSeqGroup->boneMap[n] ); + } + } + else + { + s2 = 0.0; + } + } + else + { + s2 = s * seqdesc.weight( i ); // blend in based on this bones weight + if (n != -1) + { + s1 = s * seqdesc.weight( n ); + } + } + + if (s1 == 1.0 && s2 == 1.0) + { + pos1[i] = pos2[i]; + q1[i] = q2[i]; + } + else if (s2 > 0.0) + { + Quaternion srcQ, destQ; + Vector srcPos, destPos; + Quaternion targetQ; + Vector targetPos; + Vector tmp; + + BuildBoneChain( pStudioHdr, rootXform, pos1, q1, i, destBoneToWorld, destBoneComputed ); + BuildBoneChain( pStudioHdr, rootXform, pos2, q2, i, srcBoneToWorld, srcBoneComputed ); + + MatrixAngles( destBoneToWorld[i], destQ, destPos ); + MatrixAngles( srcBoneToWorld[i], srcQ, srcPos ); + + QuaternionSlerp( destQ, srcQ, s2, targetQ ); + AngleMatrix( targetQ, destPos, targetBoneToWorld[i] ); + + // back solve + if (n == -1) + { + MatrixAngles( targetBoneToWorld[i], q1[i], tmp ); + } + else + { + matrix3x4_t worldToBone; + MatrixInvert( targetBoneToWorld[n], worldToBone ); + + matrix3x4_t local; + ConcatTransforms( worldToBone, targetBoneToWorld[i], local ); + MatrixAngles( local, q1[i], tmp ); + + // blend bone lengths (local space) + pos1[i] = Lerp( s2, pos1[i], pos2[i] ); + } + } + } +} + + + +//----------------------------------------------------------------------------- +// Purpose: blend together q1,pos1 with q2,pos2. Return result in q1,pos1. +// 0 returns q1, pos1. 1 returns q2, pos2 +//----------------------------------------------------------------------------- +void SlerpBones( + const CStudioHdr *pStudioHdr, + Quaternion q1[MAXSTUDIOBONES], + Vector pos1[MAXSTUDIOBONES], + mstudioseqdesc_t &seqdesc, // source of q2 and pos2 + int sequence, + const Quaternion q2[MAXSTUDIOBONES], + const Vector pos2[MAXSTUDIOBONES], + float s, + int boneMask ) +{ + if (s <= 0.0f) + { + return; + } + else if (s > 1.0f) + { + s = 1.0f; + } + + if (seqdesc.flags & STUDIO_WORLD) + { + WorldSpaceSlerp( pStudioHdr, q1, pos1, seqdesc, sequence, q2, pos2, s, boneMask ); + return; + } + + int i, j; + Quaternion q3, q4; + float s1, s2; + + virtualmodel_t *pVModel = pStudioHdr->GetVirtualModel(); + const virtualgroup_t *pSeqGroup = NULL; + if (pVModel) + { + pSeqGroup = pVModel->pSeqGroup( sequence ); + } + + mstudiobone_t *pbone = pStudioHdr->pBone( 0 ); + + if (seqdesc.flags & STUDIO_DELTA) + { + for (i = 0; i < pStudioHdr->numbones(); i++) + { + // skip unused bones + if (!(pbone[i].flags & boneMask)) + { + continue; + } + + if (pSeqGroup) + { + j = pSeqGroup->boneMap[i]; + if (j >= 0) + { + s2 = s * seqdesc.weight( j ); // blend in based on this bones weight + } + else + { + s2 = 0.0; + } + } + else + { + s2 = s * seqdesc.weight( i ); // blend in based on this bones weight + } + + if (s2 > 0.0) + { + if (seqdesc.flags & STUDIO_POST) + { + QuaternionMA( q1[i], s2, q2[i], q1[i] ); + + // FIXME: are these correct? + pos1[i][0] = pos1[i][0] + pos2[i][0] * s2; + pos1[i][1] = pos1[i][1] + pos2[i][1] * s2; + pos1[i][2] = pos1[i][2] + pos2[i][2] * s2; + } + else + { + QuaternionSM( s2, q2[i], q1[i], q1[i] ); + + // FIXME: are these correct? + pos1[i][0] = pos1[i][0] + pos2[i][0] * s2; + pos1[i][1] = pos1[i][1] + pos2[i][1] * s2; + pos1[i][2] = pos1[i][2] + pos2[i][2] * s2; + } + } + } + } + else + { + for (i = 0; i < pStudioHdr->numbones(); i++) + { + // skip unused bones + if (!(pbone[i].flags & boneMask)) + { + continue; + } + + if (pSeqGroup) + { + j = pSeqGroup->boneMap[i]; + if (j >= 0) + { + s2 = s * seqdesc.weight( j ); // blend in based on this bones weight + } + else + { + s2 = 0.0; + } + } + else + { + s2 = s * seqdesc.weight( i ); // blend in based on this animations weights + } + if (s2 > 0.0) + { + s1 = 1.0 - s2; + + if (pbone[i].flags & BONE_FIXED_ALIGNMENT) + { + QuaternionSlerpNoAlign( q2[i], q1[i], s1, q3 ); + } + else + { + QuaternionSlerp( q2[i], q1[i], s1, q3 ); + } + q1[i][0] = q3[0]; + q1[i][1] = q3[1]; + q1[i][2] = q3[2]; + q1[i][3] = q3[3]; + pos1[i][0] = pos1[i][0] * s1 + pos2[i][0] * s2; + pos1[i][1] = pos1[i][1] * s1 + pos2[i][1] * s2; + pos1[i][2] = pos1[i][2] * s1 + pos2[i][2] * s2; + } + } + } +} + + + +//----------------------------------------------------------------------------- +// Purpose: Inter-animation blend. Assumes both types are identical. +// blend together q1,pos1 with q2,pos2. Return result in q1,pos1. +// 0 returns q1, pos1. 1 returns q2, pos2 +//----------------------------------------------------------------------------- +void BlendBones( + const CStudioHdr *pStudioHdr, + Quaternion q1[MAXSTUDIOBONES], + Vector pos1[MAXSTUDIOBONES], + mstudioseqdesc_t &seqdesc, + int sequence, + const Quaternion q2[MAXSTUDIOBONES], + const Vector pos2[MAXSTUDIOBONES], + float s, + int boneMask ) +{ + int i, j; + Quaternion q3; + + virtualmodel_t *pVModel = pStudioHdr->GetVirtualModel(); + const virtualgroup_t *pSeqGroup = NULL; + if (pVModel) + { + pSeqGroup = pVModel->pSeqGroup( sequence ); + } + + mstudiobone_t *pbone = pStudioHdr->pBone( 0 ); + + if (s <= 0) + { + Assert(0); // shouldn't have been called + return; + } + else if (s >= 1.0) + { + Assert(0); // shouldn't have been called + for (i = 0; i < pStudioHdr->numbones(); i++) + { + // skip unused bones + if (!(pbone[i].flags & boneMask)) + { + continue; + } + + if (pSeqGroup) + { + j = pSeqGroup->boneMap[i]; + } + else + { + j = i; + } + + if (j >= 0 && seqdesc.weight( j ) > 0.0) + { + q1[i] = q2[i]; + pos1[i] = pos2[i]; + } + } + return; + } + + float s2 = s; + float s1 = 1.0 - s2; + + for (i = 0; i < pStudioHdr->numbones(); i++) + { + // skip unused bones + if (!(pbone[i].flags & boneMask)) + { + continue; + } + + if (pSeqGroup) + { + j = pSeqGroup->boneMap[i]; + } + else + { + j = i; + } + + if (j >= 0 && seqdesc.weight( j ) > 0.0) + { + if (pbone[i].flags & BONE_FIXED_ALIGNMENT) + { + QuaternionBlendNoAlign( q2[i], q1[i], s1, q3 ); + } + else + { + QuaternionBlend( q2[i], q1[i], s1, q3 ); + } + q1[i][0] = q3[0]; + q1[i][1] = q3[1]; + q1[i][2] = q3[2]; + q1[i][3] = q3[3]; + pos1[i][0] = pos1[i][0] * s1 + pos2[i][0] * s2; + pos1[i][1] = pos1[i][1] * s1 + pos2[i][1] * s2; + pos1[i][2] = pos1[i][2] * s1 + pos2[i][2] * s2; + } + } +} + + + +//----------------------------------------------------------------------------- +// Purpose: Scale a set of bones. Must be of type delta +//----------------------------------------------------------------------------- +void ScaleBones( + const CStudioHdr *pStudioHdr, + Quaternion q1[MAXSTUDIOBONES], + Vector pos1[MAXSTUDIOBONES], + int sequence, + float s, + int boneMask ) +{ + int i, j; + Quaternion q3; + + mstudioseqdesc_t &seqdesc = pStudioHdr->pSeqdesc( sequence ); + + virtualmodel_t *pVModel = pStudioHdr->GetVirtualModel(); + const virtualgroup_t *pSeqGroup = NULL; + if (pVModel) + { + pSeqGroup = pVModel->pSeqGroup( sequence ); + } + + mstudiobone_t *pbone = pStudioHdr->pBone( 0 ); + + float s2 = s; + float s1 = 1.0 - s2; + + for (i = 0; i < pStudioHdr->numbones(); i++) + { + // skip unused bones + if (!(pbone[i].flags & boneMask)) + { + continue; + } + + if (pSeqGroup) + { + j = pSeqGroup->boneMap[i]; + } + else + { + j = i; + } + + if (j >= 0 && seqdesc.weight( j ) > 0.0) + { + QuaternionIdentityBlend( q1[i], s1, q1[i] ); + VectorScale( pos1[i], s2, pos1[i] ); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: resolve a global pose parameter to the specific setting for this sequence +//----------------------------------------------------------------------------- +void Studio_LocalPoseParameter( const CStudioHdr *pStudioHdr, const float poseParameter[], mstudioseqdesc_t &seqdesc, int iSequence, int iLocalIndex, float &flSetting, int &index ) +{ + int iPose = pStudioHdr->GetSharedPoseParameter( iSequence, seqdesc.paramindex[iLocalIndex] ); + + if (iPose == -1) + { + flSetting = 0; + index = 0; + return; + } + + const mstudioposeparamdesc_t &Pose = pStudioHdr->pPoseParameter( iPose ); + + float flValue = poseParameter[iPose]; + + if (Pose.loop) + { + float wrap = (Pose.start + Pose.end) / 2.0 + Pose.loop / 2.0; + float shift = Pose.loop - wrap; + + flValue = flValue - Pose.loop * floor((flValue + shift) / Pose.loop); + } + + if (seqdesc.posekeyindex == 0) + { + float flLocalStart = ((float)seqdesc.paramstart[iLocalIndex] - Pose.start) / (Pose.end - Pose.start); + float flLocalEnd = ((float)seqdesc.paramend[iLocalIndex] - Pose.start) / (Pose.end - Pose.start); + + // convert into local range + flSetting = (flValue - flLocalStart) / (flLocalEnd - flLocalStart); + + // clamp. This shouldn't ever need to happen if it's looping. + if (flSetting < 0) + flSetting = 0; + if (flSetting > 1) + flSetting = 1; + + index = 0; + if (seqdesc.groupsize[iLocalIndex] > 2 ) + { + // estimate index + index = (int)(flSetting * (seqdesc.groupsize[iLocalIndex] - 1)); + if (index == seqdesc.groupsize[iLocalIndex] - 1) index = seqdesc.groupsize[iLocalIndex] - 2; + flSetting = flSetting * (seqdesc.groupsize[iLocalIndex] - 1) - index; + } + } + else + { + flValue = flValue * (Pose.end - Pose.start) + Pose.start; + index = 0; + + // FIXME: this needs to be 2D + // FIXME: this shouldn't be a linear search + + while (1) + { + flSetting = (flValue - seqdesc.poseKey( iLocalIndex, index )) / (seqdesc.poseKey( iLocalIndex, index + 1 ) - seqdesc.poseKey( iLocalIndex, index )); + /* + if (index > 0 && flSetting < 0.0) + { + index--; + continue; + } + else + */ + if (index < seqdesc.groupsize[iLocalIndex] - 2 && flSetting > 1.0) + { + index++; + continue; + } + break; + } + + // clamp. + if (flSetting < 0.0f) + flSetting = 0.0f; + if (flSetting > 1.0f) + flSetting = 1.0f; + } +} + +void Studio_CalcBoneToBoneTransform( const CStudioHdr *pStudioHdr, int inputBoneIndex, int outputBoneIndex, matrix3x4_t& matrixOut ) +{ + mstudiobone_t *pbone = pStudioHdr->pBone( inputBoneIndex ); + + matrix3x4_t inputToPose; + MatrixInvert( pbone->poseToBone, inputToPose ); + ConcatTransforms( pStudioHdr->pBone( outputBoneIndex )->poseToBone, inputToPose, matrixOut ); +} + +//----------------------------------------------------------------------------- +// Purpose: calculate a pose for a single sequence +//----------------------------------------------------------------------------- +void InitPose( + const CStudioHdr *pStudioHdr, + Vector pos[], + Quaternion q[] + ) +{ + for (int i = 0; i < pStudioHdr->numbones(); i++) + { + mstudiobone_t *pbone = pStudioHdr->pBone( i ); + + pos[i] = pbone->pos; + q[i] = pbone->quat; + } +} + + +inline bool PoseIsAllZeros( + const CStudioHdr *pStudioHdr, + int sequence, + mstudioseqdesc_t &seqdesc, + int i0, + int i1 + ) +{ + int baseanim; + + // remove "zero" positional blends + baseanim = pStudioHdr->iRelativeAnim( sequence, seqdesc.anim(i0 ,i1 ) ); + mstudioanimdesc_t &anim = pStudioHdr->pAnimdesc( baseanim ); + return (anim.flags & STUDIO_ALLZEROS) != 0; +} + + +//----------------------------------------------------------------------------- +// Purpose: calculate a pose for a single sequence +//----------------------------------------------------------------------------- +bool CalcPoseSingle( + const CStudioHdr *pStudioHdr, + Vector pos[], + Quaternion q[], + mstudioseqdesc_t &seqdesc, + int sequence, + float cycle, + const float poseParameter[], + int boneMask, + float flTime + ) +{ + ASSERT_NO_REENTRY(); + + static Vector pos2[MAXSTUDIOBONES]; + static Quaternion q2[MAXSTUDIOBONES]; + static Vector pos3[MAXSTUDIOBONES]; + static Quaternion q3[MAXSTUDIOBONES]; + + if (sequence >= pStudioHdr->GetNumSeq()) + { + sequence = 0; + seqdesc = pStudioHdr->pSeqdesc( sequence ); + } + + + int i0 = 0, i1 = 0; + float s0 = 0, s1 = 0; + + Studio_LocalPoseParameter( pStudioHdr, poseParameter, seqdesc, sequence, 0, s0, i0 ); + Studio_LocalPoseParameter( pStudioHdr, poseParameter, seqdesc, sequence, 1, s1, i1 ); + + + if (seqdesc.flags & STUDIO_REALTIME) + { + float cps = Studio_CPS( pStudioHdr, seqdesc, sequence, poseParameter ); + cycle = flTime * cps; + cycle = cycle - (int)cycle; + } + else if (cycle < 0 || cycle >= 1) + { + if (seqdesc.flags & STUDIO_LOOPING) + { + cycle = cycle - (int)cycle; + if (cycle < 0) cycle += 1; + } + else + { + cycle = max( 0.0, min( cycle, 0.9999 ) ); + } + } + + if (s0 < 0.001) + { + if (s1 < 0.001) + { + if (PoseIsAllZeros( pStudioHdr, sequence, seqdesc, i0, i1 )) + return false; + CalcAnimation( pStudioHdr, pos, q, seqdesc, sequence, seqdesc.anim( i0 , i1 ), cycle, boneMask ); + } + else if (s1 > 0.999) + { + CalcAnimation( pStudioHdr, pos, q, seqdesc, sequence, seqdesc.anim( i0 , i1+1 ), cycle, boneMask ); + } + else + { + CalcAnimation( pStudioHdr, pos, q, seqdesc, sequence, seqdesc.anim( i0 , i1 ), cycle, boneMask ); + CalcAnimation( pStudioHdr, pos2, q2, seqdesc, sequence, seqdesc.anim( i0 , i1+1 ), cycle, boneMask ); + BlendBones( pStudioHdr, q, pos, seqdesc, sequence, q2, pos2, s1, boneMask ); + } + } + else if (s0 > 0.999) + { + if (s1 < 0.001) + { + if (PoseIsAllZeros( pStudioHdr, sequence, seqdesc, i0+1, i1 )) + return false; + CalcAnimation( pStudioHdr, pos, q, seqdesc, sequence, seqdesc.anim( i0+1, i1 ), cycle, boneMask ); + } + else if (s1 > 0.999) + { + CalcAnimation( pStudioHdr, pos, q, seqdesc, sequence, seqdesc.anim( i0+1, i1+1 ), cycle, boneMask ); + } + else + { + CalcAnimation( pStudioHdr, pos, q, seqdesc, sequence, seqdesc.anim( i0+1, i1 ), cycle, boneMask ); + CalcAnimation( pStudioHdr, pos2, q2, seqdesc, sequence, seqdesc.anim( i0+1, i1+1 ), cycle, boneMask ); + BlendBones( pStudioHdr, q, pos, seqdesc, sequence, q2, pos2, s1, boneMask ); + } + } + else + { + if (s1 < 0.001) + { + if (PoseIsAllZeros( pStudioHdr, sequence, seqdesc, i0+1, i1 )) + { + CalcAnimation( pStudioHdr, pos, q, seqdesc, sequence, seqdesc.anim( i0 ,i1 ), cycle, boneMask ); + ScaleBones( pStudioHdr, q, pos, sequence, 1.0 - s0, boneMask ); + } + else if (PoseIsAllZeros( pStudioHdr, sequence, seqdesc, i0, i1 )) + { + CalcAnimation( pStudioHdr, pos, q, seqdesc, sequence, seqdesc.anim( i0+1 ,i1 ), cycle, boneMask ); + ScaleBones( pStudioHdr, q, pos, sequence, s0, boneMask ); + } + else + { + CalcAnimation( pStudioHdr, pos, q, seqdesc, sequence, seqdesc.anim( i0 ,i1 ), cycle, boneMask ); + CalcAnimation( pStudioHdr, pos2, q2, seqdesc, sequence, seqdesc.anim( i0+1,i1 ), cycle, boneMask ); + + BlendBones( pStudioHdr, q, pos, seqdesc, sequence, q2, pos2, s0, boneMask ); + } + } + else if (s1 > 0.999) + { + CalcAnimation( pStudioHdr, pos, q, seqdesc, sequence, seqdesc.anim( i0 ,i1+1 ), cycle, boneMask ); + CalcAnimation( pStudioHdr, pos2, q2, seqdesc, sequence, seqdesc.anim( i0+1,i1+1 ), cycle, boneMask ); + BlendBones( pStudioHdr, q, pos, seqdesc, sequence, q2, pos2, s0, boneMask ); + } + else + { + CalcAnimation( pStudioHdr, pos, q, seqdesc, sequence, seqdesc.anim( i0 ,i1 ), cycle, boneMask ); + CalcAnimation( pStudioHdr, pos2, q2, seqdesc, sequence, seqdesc.anim( i0+1,i1 ), cycle, boneMask ); + BlendBones( pStudioHdr, q, pos, seqdesc, sequence, q2, pos2, s0, boneMask ); + + CalcAnimation( pStudioHdr, pos2, q2, seqdesc, sequence, seqdesc.anim( i0 , i1+1), cycle, boneMask ); + CalcAnimation( pStudioHdr, pos3, q3, seqdesc, sequence, seqdesc.anim( i0+1, i1+1), cycle, boneMask ); + BlendBones( pStudioHdr, q2, pos2, seqdesc, sequence, q3, pos3, s0, boneMask ); + + BlendBones( pStudioHdr, q, pos, seqdesc, sequence, q2, pos2, s1, boneMask ); + } + } + + return true; +} + + + + +//----------------------------------------------------------------------------- +// Purpose: calculate a pose for a single sequence +// adds autolayers, runs local ik rukes +//----------------------------------------------------------------------------- +void AddSequenceLayers( + const CStudioHdr *pStudioHdr, + CIKContext *pIKContext, + Vector pos[], + Quaternion q[], + mstudioseqdesc_t &seqdesc, + int sequence, + float cycle, + const float poseParameter[], + int boneMask, + float flWeight, + float flTime + ) +{ + for (int i = 0; i < seqdesc.numautolayers; i++) + { + mstudioautolayer_t *pLayer = seqdesc.pAutolayer( i ); + + if (pLayer->flags & STUDIO_AL_LOCAL) + continue; + + float layerCycle = cycle; + float layerWeight = flWeight; + + if (pLayer->start != pLayer->end) + { + float s = 1.0; + float index; + + if (!(pLayer->flags & STUDIO_AL_POSE)) + { + index = cycle; + } + else + { + int iPose = pStudioHdr->GetSharedPoseParameter( pLayer->iSequence, pLayer->iPose ); + if (iPose != -1) + { + const mstudioposeparamdesc_t &Pose = pStudioHdr->pPoseParameter( iPose ); + index = poseParameter[ iPose ] * (Pose.end - Pose.start) + Pose.start; + } + else + { + index = 0; + } + } + + if (index < pLayer->start) + continue; + if (index >= pLayer->end) + continue; + + if (index < pLayer->peak && pLayer->start != pLayer->peak) + { + s = (index - pLayer->start) / (pLayer->peak - pLayer->start); + } + else if (index > pLayer->tail && pLayer->end != pLayer->tail) + { + s = (pLayer->end - index) / (pLayer->end - pLayer->tail); + } + + if (pLayer->flags & STUDIO_AL_SPLINE) + { + s = SimpleSpline( s ); + } + + if ((pLayer->flags & STUDIO_AL_XFADE) && (index > pLayer->tail)) + { + layerWeight = ( s * flWeight ) / ( 1 - flWeight + s * flWeight ); + } + else if (pLayer->flags & STUDIO_AL_NOBLEND) + { + layerWeight = s; + } + else + { + layerWeight = flWeight * s; + } + + if (!(pLayer->flags & STUDIO_AL_POSE)) + { + layerCycle = (cycle - pLayer->start) / (pLayer->end - pLayer->start); + } + } + + int iSequence = pStudioHdr->iRelativeSeq( sequence, pLayer->iSequence ); + AccumulatePose( pStudioHdr, pIKContext, pos, q, iSequence, layerCycle, poseParameter, boneMask, layerWeight, flTime ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: calculate a pose for a single sequence +// adds autolayers, runs local ik rukes +//----------------------------------------------------------------------------- +void AddLocalLayers( + const CStudioHdr *pStudioHdr, + CIKContext *pIKContext, + Vector pos[], + Quaternion q[], + mstudioseqdesc_t &seqdesc, + int sequence, + float cycle, + const float poseParameter[], + int boneMask, + float flWeight, + float flTime + ) +{ + if (!(seqdesc.flags & STUDIO_LOCAL)) + { + return; + } + + for (int i = 0; i < seqdesc.numautolayers; i++) + { + mstudioautolayer_t *pLayer = seqdesc.pAutolayer( i ); + + if (!(pLayer->flags & STUDIO_AL_LOCAL)) + continue; + + float layerCycle = cycle; + float layerWeight = flWeight; + + if (pLayer->start != pLayer->end) + { + float s = 1.0; + + if (cycle < pLayer->start) + continue; + if (cycle >= pLayer->end) + continue; + + if (cycle < pLayer->peak && pLayer->start != pLayer->peak) + { + s = (cycle - pLayer->start) / (pLayer->peak - pLayer->start); + } + else if (cycle > pLayer->tail && pLayer->end != pLayer->tail) + { + s = (pLayer->end - cycle) / (pLayer->end - pLayer->tail); + } + + if (pLayer->flags & STUDIO_AL_SPLINE) + { + s = SimpleSpline( s ); + } + + if ((pLayer->flags & STUDIO_AL_XFADE) && (cycle > pLayer->tail)) + { + layerWeight = ( s * flWeight ) / ( 1 - flWeight + s * flWeight ); + } + else if (pLayer->flags & STUDIO_AL_NOBLEND) + { + layerWeight = s; + } + else + { + layerWeight = flWeight * s; + } + + layerCycle = (cycle - pLayer->start) / (pLayer->end - pLayer->start); + } + + int iSequence = pStudioHdr->iRelativeSeq( sequence, pLayer->iSequence ); + AccumulatePose( pStudioHdr, pIKContext, pos, q, iSequence, layerCycle, poseParameter, boneMask, layerWeight, flTime ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: calculate a pose for a single sequence +// adds autolayers, runs local ik rukes +//----------------------------------------------------------------------------- +void CalcPose( + const CStudioHdr *pStudioHdr, + CIKContext *pIKContext, + Vector pos[], + Quaternion q[], + int sequence, + float cycle, + const float poseParameter[], + int boneMask, + float flWeight, + float flTime + ) +{ + mstudioseqdesc_t &seqdesc = pStudioHdr->pSeqdesc( sequence ); + + Assert( flWeight >= 0.0f && flWeight <= 1.0f ); + // This shouldn't be necessary, but the Assert should help us catch whoever is screwing this up + flWeight = clamp( flWeight, 0.0f, 1.0f ); + + // add any IK locks to prevent numautolayers from moving extremities + CIKContext seq_ik; + if (seqdesc.numiklocks) + { + seq_ik.Init( pStudioHdr, vec3_angle, vec3_origin, 0.0, 0, boneMask ); // local space relative so absolute position doesn't mater + seq_ik.AddSequenceLocks( seqdesc, pos, q ); + } + + CalcPoseSingle( pStudioHdr, pos, q, seqdesc, sequence, cycle, poseParameter, boneMask, flTime ); + + if ( pIKContext ) + { + pIKContext->AddDependencies( seqdesc, sequence, cycle, poseParameter, flWeight ); + } + + AddSequenceLayers( pStudioHdr, pIKContext, pos, q, seqdesc, sequence, cycle, poseParameter, boneMask, flWeight, flTime ); + + if (seqdesc.numiklocks) + { + seq_ik.SolveSequenceLocks( seqdesc, pos, q ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: accumulate a pose for a single sequence on top of existing animation +// adds autolayers, runs local ik rukes +//----------------------------------------------------------------------------- +void AccumulatePose( + const CStudioHdr *pStudioHdr, + CIKContext *pIKContext, + Vector pos[], + Quaternion q[], + int sequence, + float cycle, + const float poseParameter[], + int boneMask, + float flWeight, + float flTime + ) +{ + Vector pos2[MAXSTUDIOBONES]; + Quaternion q2[MAXSTUDIOBONES]; + + Assert( flWeight >= 0.0f && flWeight <= 1.0f ); + // This shouldn't be necessary, but the Assert should help us catch whoever is screwing this up + flWeight = clamp( flWeight, 0.0f, 1.0f ); + + if ( sequence < 0 ) + return; + + mstudioseqdesc_t &seqdesc = pStudioHdr->pSeqdesc( sequence ); + + // add any IK locks to prevent extremities from moving + CIKContext seq_ik; + if (seqdesc.numiklocks) + { + seq_ik.Init( pStudioHdr, vec3_angle, vec3_origin, 0.0, 0, boneMask ); // local space relative so absolute position doesn't mater + seq_ik.AddSequenceLocks( seqdesc, pos, q ); + } + + if (seqdesc.flags & STUDIO_LOCAL) + { + InitPose( pStudioHdr, pos2, q2 ); + } + + if (CalcPoseSingle( pStudioHdr, pos2, q2, seqdesc, sequence, cycle, poseParameter, boneMask, flTime )) + { + // this weight is wrong, the IK rules won't composite at the correct intensity + AddLocalLayers( pStudioHdr, pIKContext, pos2, q2, seqdesc, sequence, cycle, poseParameter, boneMask, 1.0, flTime ); + SlerpBones( pStudioHdr, q, pos, seqdesc, sequence, q2, pos2, flWeight, boneMask ); + } + + if ( pIKContext ) + { + pIKContext->AddDependencies( seqdesc, sequence, cycle, poseParameter, flWeight ); + } + + AddSequenceLayers( pStudioHdr, pIKContext, pos, q, seqdesc, sequence, cycle, poseParameter, boneMask, flWeight, flTime ); + + if (seqdesc.numiklocks) + { + seq_ik.SolveSequenceLocks( seqdesc, pos, q ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: blend together q1,pos1 with q2,pos2. Return result in q1,pos1. +// 0 returns q1, pos1. 1 returns q2, pos2 +//----------------------------------------------------------------------------- +void CalcBoneAdj( + const CStudioHdr *pStudioHdr, + Vector pos[], + Quaternion q[], + const float controllers[], + int boneMask + ) +{ + int i, j, k; + float value; + mstudiobonecontroller_t *pbonecontroller; + Vector p0; + RadianEuler a0; + Quaternion q0; + + for (j = 0; j < pStudioHdr->numbonecontrollers(); j++) + { + pbonecontroller = pStudioHdr->pBonecontroller( j ); + k = pbonecontroller->bone; + + if (pStudioHdr->pBone( k )->flags & boneMask) + { + i = pbonecontroller->inputfield; + value = controllers[i]; + if (value < 0) value = 0; + if (value > 1.0) value = 1.0; + value = (1.0 - value) * pbonecontroller->start + value * pbonecontroller->end; + + switch(pbonecontroller->type & STUDIO_TYPES) + { + case STUDIO_XR: + a0.Init( value * (M_PI / 180.0), 0, 0 ); + AngleQuaternion( a0, q0 ); + QuaternionSM( 1.0, q0, q[k], q[k] ); + break; + case STUDIO_YR: + a0.Init( 0, value * (M_PI / 180.0), 0 ); + AngleQuaternion( a0, q0 ); + QuaternionSM( 1.0, q0, q[k], q[k] ); + break; + case STUDIO_ZR: + a0.Init( 0, 0, value * (M_PI / 180.0) ); + AngleQuaternion( a0, q0 ); + QuaternionSM( 1.0, q0, q[k], q[k] ); + break; + case STUDIO_X: + pos[k].x += value; + break; + case STUDIO_Y: + pos[k].y += value; + break; + case STUDIO_Z: + pos[k].z += value; + break; + } + } + } +} + + +void CalcBoneDerivatives( Vector &velocity, AngularImpulse &angVel, const matrix3x4_t &prev, const matrix3x4_t ¤t, float dt ) +{ + float scale = 1.0; + if ( dt > 0 ) + { + scale = 1.0 / dt; + } + + Vector endPosition, startPosition, deltaAxis; + QAngle endAngles, startAngles; + float deltaAngle; + + MatrixAngles( prev, startAngles, startPosition ); + MatrixAngles( current, endAngles, endPosition ); + + velocity.x = (endPosition.x - startPosition.x) * scale; + velocity.y = (endPosition.y - startPosition.y) * scale; + velocity.z = (endPosition.z - startPosition.z) * scale; + RotationDeltaAxisAngle( startAngles, endAngles, deltaAxis, deltaAngle ); + VectorScale( deltaAxis, (deltaAngle * scale), angVel ); +} + +void CalcBoneVelocityFromDerivative( const QAngle &vecAngles, Vector &velocity, AngularImpulse &angVel, const matrix3x4_t ¤t ) +{ + Vector vecLocalVelocity; + AngularImpulse LocalAngVel; + Quaternion q; + float angle; + MatrixAngles( current, q, vecLocalVelocity ); + QuaternionAxisAngle( q, LocalAngVel, angle ); + LocalAngVel *= angle; + + matrix3x4_t matAngles; + AngleMatrix( vecAngles, matAngles ); + VectorTransform( vecLocalVelocity, matAngles, velocity ); + VectorTransform( LocalAngVel, matAngles, angVel ); +} + + + + +class ik +{ +public: +//-------- SOLVE TWO LINK INVERSE KINEMATICS ------------- +// Author: Ken Perlin +// +// Given a two link joint from [0,0,0] to end effector position P, +// let link lengths be a and b, and let norm |P| = c. Clearly a+b <= c. +// +// Problem: find a "knee" position Q such that |Q| = a and |P-Q| = b. +// +// In the case of a point on the x axis R = [c,0,0], there is a +// closed form solution S = [d,e,0], where |S| = a and |R-S| = b: +// +// d2+e2 = a2 -- because |S| = a +// (c-d)2+e2 = b2 -- because |R-S| = b +// +// c2-2cd+d2+e2 = b2 -- combine the two equations +// c2-2cd = b2 - a2 +// c-2d = (b2-a2)/c +// d - c/2 = (a2-b2)/c / 2 +// +// d = (c + (a2-b2/c) / 2 -- to solve for d and e. +// e = sqrt(a2-d2) + + static float findD(float a, float b, float c) { + return (c + (a*a-b*b)/c) / 2; + } + static float findE(float a, float d) { return sqrt(a*a-d*d); } + +// This leads to a solution to the more general problem: +// +// (1) R = Mfwd(P) -- rotate P onto the x axis +// (2) Solve for S +// (3) Q = Minv(S) -- rotate back again + + static float Mfwd[3][3]; + static float Minv[3][3]; + + static bool solve(float A, float B, float const P[], float const D[], float Q[]) { + float R[3]; + defineM(P,D); + rot(Minv,P,R); + float r = length(R); + float d = findD(A,B,r); + float e = findE(A,d); + float S[3] = {d,e,0}; + rot(Mfwd,S,Q); + return d > (r - B) && d < A; + } + +// If "knee" position Q needs to be as close as possible to some point D, +// then choose M such that M(D) is in the y>0 half of the z=0 plane. +// +// Given that constraint, define the forward and inverse of M as follows: + + static void defineM(float const P[], float const D[]) { + float *X = Minv[0], *Y = Minv[1], *Z = Minv[2]; + +// Minv defines a coordinate system whose x axis contains P, so X = unit(P). + int i; + for (i = 0 ; i < 3 ; i++) + X[i] = P[i]; + normalize(X); + +// Its y axis is perpendicular to P, so Y = unit( E - X(E·X) ). + + float dDOTx = dot(D,X); + for (i = 0 ; i < 3 ; i++) + Y[i] = D[i] - dDOTx * X[i]; + normalize(Y); + +// Its z axis is perpendicular to both X and Y, so Z = X×Y. + + cross(X,Y,Z); + +// Mfwd = (Minv)T, since transposing inverts a rotation matrix. + + for (i = 0 ; i < 3 ; i++) { + Mfwd[i][0] = Minv[0][i]; + Mfwd[i][1] = Minv[1][i]; + Mfwd[i][2] = Minv[2][i]; + } + } + +//------------ GENERAL VECTOR MATH SUPPORT ----------- + + static float dot(float const a[], float const b[]) { return a[0]*b[0] + a[1]*b[1] + a[2]*b[2]; } + + static float length(float const v[]) { return sqrt( dot(v,v) ); } + + static void normalize(float v[]) { + float norm = length(v); + for (int i = 0 ; i < 3 ; i++) + v[i] /= norm; + } + + static void cross(float const a[], float const b[], float c[]) { + c[0] = a[1] * b[2] - a[2] * b[1]; + c[1] = a[2] * b[0] - a[0] * b[2]; + c[2] = a[0] * b[1] - a[1] * b[0]; + } + + static void rot(float const M[3][3], float const src[], float dst[]) { + for (int i = 0 ; i < 3 ; i++) + dst[i] = dot(M[i],src); + } +}; + +float ik::Mfwd[3][3]; +float ik::Minv[3][3]; + + + +//----------------------------------------------------------------------------- +// Purpose: visual debugging code +//----------------------------------------------------------------------------- +#if 1 +inline void debugLine(const Vector& origin, const Vector& dest, int r, int g, int b, bool noDepthTest, float duration) { }; +#else +extern void drawLine( const Vector &p1, const Vector &p2, int r = 0, int g = 0, int b = 1, bool noDepthTest = true, float duration = 0.1 ); +void debugLine(const Vector& origin, const Vector& dest, int r, int g, int b, bool noDepthTest, float duration) +{ + drawLine( origin, dest, r, g, b, noDepthTest, duration ); +} +#endif + + +//----------------------------------------------------------------------------- +// Purpose: for a 2 bone chain, find the IK solution and reset the matrices +//----------------------------------------------------------------------------- +bool Studio_SolveIK( mstudioikchain_t *pikchain, Vector &targetFoot, matrix3x4_t *pBoneToWorld ) +{ + if (pikchain->pLink(0)->kneeDir.LengthSqr() > 0.0) + { + Vector targetKneeDir, targetKneePos; + // FIXME: knee length should be as long as the legs + Vector tmp = pikchain->pLink( 0 )->kneeDir; + VectorRotate( tmp, pBoneToWorld[ pikchain->pLink( 0 )->bone ], targetKneeDir ); + MatrixPosition( pBoneToWorld[ pikchain->pLink( 1 )->bone ], targetKneePos ); + return Studio_SolveIK( pikchain->pLink( 0 )->bone, pikchain->pLink( 1 )->bone, pikchain->pLink( 2 )->bone, targetFoot, targetKneePos, targetKneeDir, pBoneToWorld ); + } + else + { + return Studio_SolveIK( pikchain->pLink( 0 )->bone, pikchain->pLink( 1 )->bone, pikchain->pLink( 2 )->bone, targetFoot, pBoneToWorld ); + } +} + + +#define KNEEMAX_EPSILON 0.9998 // (0.9998 is about 1 degree) + +//----------------------------------------------------------------------------- +// Purpose: Solve Knee position for a known hip and foot location, but no specific knee direction preference +//----------------------------------------------------------------------------- + +bool Studio_SolveIK( int iThigh, int iKnee, int iFoot, Vector &targetFoot, matrix3x4_t *pBoneToWorld ) +{ + Vector worldFoot, worldKnee, worldThigh; + + MatrixPosition( pBoneToWorld[ iThigh ], worldThigh ); + MatrixPosition( pBoneToWorld[ iKnee ], worldKnee ); + MatrixPosition( pBoneToWorld[ iFoot ], worldFoot ); + + //debugLine( worldThigh, worldKnee, 0, 0, 255, true, 0 ); + //debugLine( worldKnee, worldFoot, 0, 0, 255, true, 0 ); + + Vector ikFoot, ikKnee; + + ikFoot = targetFoot - worldThigh; + ikKnee = worldKnee - worldThigh; + + float l1 = (worldKnee-worldThigh).Length(); + float l2 = (worldFoot-worldKnee).Length(); + float l3 = (worldFoot-worldThigh).Length(); + + // leg too straight to figure out knee? + if (l3 > (l1 + l2) * KNEEMAX_EPSILON) + { + return false; + } + + Vector ikHalf = (worldFoot-worldThigh) * (l1 / l3); + + // FIXME: what to do when the knee completely straight? + Vector ikKneeDir = ikKnee - ikHalf; + VectorNormalize( ikKneeDir ); + + return Studio_SolveIK( iThigh, iKnee, iFoot, targetFoot, worldKnee, ikKneeDir, pBoneToWorld ); +} + +//----------------------------------------------------------------------------- +// Purpose: Realign the matrix so that its X axis points along the desired axis. +//----------------------------------------------------------------------------- +void Studio_AlignIKMatrix( matrix3x4_t &mMat, const Vector &vAlignTo ) +{ + Vector tmp1, tmp2, tmp3; + + // Column 0 (X) becomes the vector. + tmp1 = vAlignTo; + VectorNormalize( tmp1 ); + MatrixSetColumn( tmp1, 0, mMat ); + + // Column 1 (Y) is the cross of the vector and column 2 (Z). + MatrixGetColumn( mMat, 2, tmp3 ); + tmp2 = tmp3.Cross( tmp1 ); + VectorNormalize( tmp2 ); + // FIXME: check for X being too near to Z + MatrixSetColumn( tmp2, 1, mMat ); + + // Column 2 (Z) is the cross of columns 0 (X) and 1 (Y). + tmp3 = tmp1.Cross( tmp2 ); + MatrixSetColumn( tmp3, 2, mMat ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Solve Knee position for a known hip and foot location, and a known knee direction +//----------------------------------------------------------------------------- + +bool Studio_SolveIK( int iThigh, int iKnee, int iFoot, Vector &targetFoot, Vector &targetKneePos, Vector &targetKneeDir, matrix3x4_t *pBoneToWorld ) +{ + Vector worldFoot, worldKnee, worldThigh; + + MatrixPosition( pBoneToWorld[ iThigh ], worldThigh ); + MatrixPosition( pBoneToWorld[ iKnee ], worldKnee ); + MatrixPosition( pBoneToWorld[ iFoot ], worldFoot ); + + //debugLine( worldThigh, worldKnee, 0, 0, 255, true, 0 ); + //debugLine( worldThigh, worldThigh + targetKneeDir, 0, 0, 255, true, 0 ); + // debugLine( worldKnee, targetKnee, 0, 0, 255, true, 0 ); + + Vector ikFoot, ikTargetKnee, ikKnee; + + ikFoot = targetFoot - worldThigh; + ikKnee = targetKneePos - worldThigh; + + float l1 = (worldKnee-worldThigh).Length(); + float l2 = (worldFoot-worldKnee).Length(); + + // exaggerate knee targets for legs that are nearly straight + // FIXME: should be configurable, and the ikKnee should be from the original animation, not modifed + float d = (targetFoot-worldThigh).Length() - min( l1, l2 ); + d = max( l1 + l2, d ); + // FIXME: too short knee directions cause trouble + d = d * 100; + + ikTargetKnee = ikKnee + targetKneeDir * d; + + // debugLine( worldKnee, worldThigh + ikTargetKnee, 0, 0, 255, true, 0 ); + + int color[3] = { 0, 255, 0 }; + + // too far away? (0.9998 is about 1 degree) + if (ikFoot.Length() > (l1 + l2) * KNEEMAX_EPSILON) + { + VectorNormalize( ikFoot ); + VectorScale( ikFoot, (l1 + l2) * KNEEMAX_EPSILON, ikFoot ); + color[0] = 255; color[1] = 0; color[2] = 0; + } + + // too close? + if (ikFoot.Length() < fabs(l1 - l2) * 1.05) + { + VectorNormalize( ikFoot ); + VectorScale( ikFoot, fabs(l1 - l2) * 1.05, ikFoot ); + } + + if (ik::solve( l1, l2, ikFoot.Base(), ikTargetKnee.Base(), ikKnee.Base() )) + { + matrix3x4_t& mWorldThigh = pBoneToWorld[ iThigh ]; + matrix3x4_t& mWorldKnee = pBoneToWorld[ iKnee ]; + matrix3x4_t& mWorldFoot = pBoneToWorld[ iFoot ]; + + //debugLine( worldThigh, ikKnee + worldThigh, 255, 0, 0, true, 0 ); + //debugLine( ikKnee + worldThigh, ikFoot + worldThigh, 255, 0, 0, true,0 ); + + // debugLine( worldThigh, ikKnee + worldThigh, color[0], color[1], color[2], true, 0 ); + // debugLine( ikKnee + worldThigh, ikFoot + worldThigh, color[0], color[1], color[2], true,0 ); + + + // build transformation matrix for thigh + Studio_AlignIKMatrix( mWorldThigh, ikKnee ); + Studio_AlignIKMatrix( mWorldKnee, ikFoot - ikKnee ); + + + mWorldKnee[0][3] = ikKnee.x + worldThigh.x; + mWorldKnee[1][3] = ikKnee.y + worldThigh.y; + mWorldKnee[2][3] = ikKnee.z + worldThigh.z; + + mWorldFoot[0][3] = ikFoot.x + worldThigh.x; + mWorldFoot[1][3] = ikFoot.y + worldThigh.y; + mWorldFoot[2][3] = ikFoot.z + worldThigh.z; + + return true; + } + else + { + /* + debugLine( worldThigh, worldThigh + ikKnee, 255, 0, 0, true, 0 ); + debugLine( worldThigh + ikKnee, worldThigh + ikFoot, 255, 0, 0, true, 0 ); + debugLine( worldThigh + ikFoot, worldThigh, 255, 0, 0, true, 0 ); + debugLine( worldThigh + ikKnee, worldThigh + ikTargetKnee, 255, 0, 0, true, 0 ); + */ + return false; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- + +float Studio_IKRuleWeight( mstudioikrule_t &ikRule, const mstudioanimdesc_t *panim, float flCycle, int &iFrame, float &fraq ) +{ + if (ikRule.end > 1.0f && flCycle < ikRule.start) + { + flCycle = flCycle + 1.0f; + } + + float value = 0.0f; + fraq = (panim->numframes - 1) * (flCycle - ikRule.start) + ikRule.iStart; + iFrame = (int)fraq; + fraq = fraq - iFrame; + + if (flCycle < ikRule.start) + { + iFrame = ikRule.iStart; + fraq = 0.0f; + return 0.0f; + } + else if (flCycle < ikRule.peak ) + { + value = (flCycle - ikRule.start) / (ikRule.peak - ikRule.start); + } + else if (flCycle < ikRule.tail ) + { + return 1.0f; + } + else if (flCycle < ikRule.end ) + { + value = 1.0f - ((flCycle - ikRule.tail) / (ikRule.end - ikRule.tail)); + } + else + { + fraq = (panim->numframes - 1) * (ikRule.end - ikRule.start) + ikRule.iStart; + iFrame = (int)fraq; + fraq = fraq - iFrame; + } + return SimpleSpline( value ); +} + + +float Studio_IKRuleWeight( ikcontextikrule_t &ikRule, float flCycle ) +{ + if (ikRule.end > 1.0f && flCycle < ikRule.start) + { + flCycle = flCycle + 1.0f; + } + + float value = 0.0f; + if (flCycle < ikRule.start) + { + return 0.0f; + } + else if (flCycle < ikRule.peak ) + { + value = (flCycle - ikRule.start) / (ikRule.peak - ikRule.start); + } + else if (flCycle < ikRule.tail ) + { + return 1.0f; + } + else if (flCycle < ikRule.end ) + { + value = 1.0f - ((flCycle - ikRule.tail) / (ikRule.end - ikRule.tail)); + } + return 3.0f * value * value - 2.0f * value * value * value; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- + +bool Studio_IKShouldLatch( ikcontextikrule_t &ikRule, float flCycle ) +{ + if (ikRule.end > 1.0f && flCycle < ikRule.start) + { + flCycle = flCycle + 1.0f; + } + + if (flCycle < ikRule.peak ) + { + return false; + } + else if (flCycle < ikRule.end ) + { + return true; + } + return false; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- + +float Studio_IKTail( ikcontextikrule_t &ikRule, float flCycle ) +{ + if (ikRule.end > 1.0f && flCycle < ikRule.start) + { + flCycle = flCycle + 1.0f; + } + + if (flCycle <= ikRule.tail ) + { + return 0.0f; + } + else if (flCycle < ikRule.end ) + { + return ((flCycle - ikRule.tail) / (ikRule.end - ikRule.tail)); + } + return 0.0; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- + +bool Studio_IKAnimationError( const CStudioHdr *pStudioHdr, mstudioikrule_t *pRule, const mstudioanimdesc_t *panim, float flCycle, Vector &pos, Quaternion &q, float &flWeight ) +{ + float fraq; + int iFrame; + + flWeight = Studio_IKRuleWeight( *pRule, panim, flCycle, iFrame, fraq ); + Assert( fraq >= 0.0 && fraq < 1.0 ); + Assert( flWeight >= 0.0f && flWeight <= 1.0f ); + + // This shouldn't be necessary, but the Assert should help us catch whoever is screwing this up + flWeight = clamp( flWeight, 0.0f, 1.0f ); + + if (pRule->type != IK_GROUND && flWeight < 0.0001) + return false; + + mstudioikerror_t *pError = pRule->pError( iFrame ); + if (pError != NULL) + { + if (fraq < 0.001) + { + q = pError[0].q; + pos = pError[0].pos; + } + else + { + QuaternionBlend( pError[0].q, pError[1].q, fraq, q ); + pos = pError[0].pos * (1.0f - fraq) + pError[1].pos * fraq; + } + return true; + } + + mstudiocompressedikerror_t *pCompressed = pRule->pCompressedError(); + if (pCompressed != NULL) + { + iFrame = iFrame - pRule->iStart; + + Vector p1, p2; + ExtractAnimValue( iFrame, pCompressed->pAnimvalue( 0 ), pCompressed->scale[0], p1.x, p2.x ); + ExtractAnimValue( iFrame, pCompressed->pAnimvalue( 1 ), pCompressed->scale[1], p1.y, p2.y ); + ExtractAnimValue( iFrame, pCompressed->pAnimvalue( 2 ), pCompressed->scale[2], p1.z, p2.z ); + pos = p1 * (1 - fraq) + p2 * fraq; + + Quaternion q1, q2; + RadianEuler angle1, angle2; + ExtractAnimValue( iFrame, pCompressed->pAnimvalue( 3 ), pCompressed->scale[3], angle1.x, angle2.x ); + ExtractAnimValue( iFrame, pCompressed->pAnimvalue( 4 ), pCompressed->scale[4], angle1.y, angle2.y ); + ExtractAnimValue( iFrame, pCompressed->pAnimvalue( 5 ), pCompressed->scale[5], angle1.z, angle2.z ); + + if (angle1.x != angle2.x || angle1.y != angle2.y || angle1.z != angle2.z) + { + AngleQuaternion( angle1, q1 ); + AngleQuaternion( angle2, q2 ); + QuaternionBlend( q1, q2, fraq, q ); + } + else + { + AngleQuaternion( angle1, q ); + } + return true; + } + // no data, disable IK rule + Assert( 0 ); + flWeight = 0.0f; + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: For a specific sequence:rule, find where it starts, stops, and what +// the estimated offset from the connection point is. +// return true if the rule is within bounds. +//----------------------------------------------------------------------------- + +bool Studio_IKSequenceError( const CStudioHdr *pStudioHdr, mstudioseqdesc_t &seqdesc, int iSequence, float flCycle, int iRule, const float poseParameter[], mstudioanimdesc_t *panim[4], float weight[4], ikcontextikrule_t &ikRule ) +{ + int i; + + memset( &ikRule, 0, sizeof(ikRule) ); + ikRule.start = ikRule.peak = ikRule.tail = ikRule.end = 0; + + + mstudioikrule_t *prevRule = NULL; + + // find overall influence + for (i = 0; i < 4; i++) + { + if (weight[i]) + { + if (iRule >= panim[i]->numikrules || panim[i]->numikrules != panim[0]->numikrules) + { + Assert( 0 ); + return false; + } + + mstudioikrule_t *pRule = panim[i]->pIKRule( iRule ); + if (pRule == NULL) + return false; + + float dt = 0.0; + if (prevRule != NULL) + { + if (pRule->start - prevRule->start > 0.5) + { + dt = -1.0; + } + else if (pRule->start - prevRule->start < -0.5) + { + dt = 1.0; + } + } + else + { + prevRule = pRule; + } + + ikRule.start += (pRule->start + dt) * weight[i]; + ikRule.peak += (pRule->peak + dt) * weight[i]; + ikRule.tail += (pRule->tail + dt) * weight[i]; + ikRule.end += (pRule->end + dt) * weight[i]; + } + } + if (ikRule.start > 1.0) + { + ikRule.start -= 1.0; + ikRule.peak -= 1.0; + ikRule.tail -= 1.0; + ikRule.end -= 1.0; + } + else if (ikRule.start < 0.0) + { + ikRule.start += 1.0; + ikRule.peak += 1.0; + ikRule.tail += 1.0; + ikRule.end += 1.0; + } + + ikRule.flWeight = Studio_IKRuleWeight( ikRule, flCycle ); + if (ikRule.flWeight <= 0.001f) + { + // go ahead and allow IK_GROUND rules a virtual looping section + if (panim[0]->pIKRule( iRule )->type == IK_GROUND && ikRule.end - ikRule.start > 0.75 ) + { + ikRule.flWeight = 0.001; + flCycle = ikRule.end - 0.001; + } + else + { + return false; + } + } + + Assert( ikRule.flWeight > 0.0f ); + + ikRule.pos.Init(); + ikRule.q.Init(); + + // FIXME: add "latched" value + ikRule.commit = Studio_IKShouldLatch( ikRule, flCycle ); + + // find target error + float total = 0.0f; + for (i = 0; i < 4; i++) + { + if (weight[i]) + { + Vector pos1; + Quaternion q1; + float w; + + mstudioikrule_t *pRule = panim[i]->pIKRule( iRule ); + if (pRule == NULL) + return false; + + ikRule.chain = pRule->chain; // FIXME: this is anim local + ikRule.bone = pRule->bone; // FIXME: this is anim local + ikRule.type = pRule->type; + ikRule.slot = pRule->slot; + + ikRule.height += pRule->height * weight[i]; + ikRule.floor += pRule->floor * weight[i]; + ikRule.radius += pRule->radius * weight[i]; + + // keep track of tail condition + ikRule.release += Studio_IKTail( ikRule, flCycle ) * weight[i]; + + // only check rules with error values + switch( ikRule.type ) + { + case IK_SELF: + case IK_WORLD: + case IK_GROUND: + case IK_ATTACHMENT: + { + int bResult = Studio_IKAnimationError( pStudioHdr, pRule, panim[i], flCycle, pos1, q1, w ); + + if (bResult) + { + ikRule.pos = ikRule.pos + pos1 * weight[i]; + QuaternionAccumulate( ikRule.q, weight[i], q1, ikRule.q ); + total += weight[i]; + } + } + break; + default: + total += weight[i]; + break; + } + + ikRule.latched = Studio_IKShouldLatch( ikRule, flCycle ) * ikRule.flWeight; + + if (ikRule.type == IK_ATTACHMENT) + { + ikRule.szLabel = pRule->pszAttachment(); + } + } + } + + if (total <= 0.0001f) + { + return false; + } + + if (total < 0.999f) + { + VectorScale( ikRule.pos, 1.0f / total, ikRule.pos ); + QuaternionScale( ikRule.q, 1.0f / total, ikRule.q ); + } + + if (ikRule.type == IK_SELF && ikRule.bone != -1) + { + // FIXME: this is anim local, not seq local! + ikRule.bone = pStudioHdr->RemapSeqBone( iSequence, ikRule.bone ); + if (ikRule.bone == -1) + return false; + } + + QuaternionNormalize( ikRule.q ); + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- + + +CIKContext::CIKContext() +{ + m_target.EnsureCapacity( 12 ); // FIXME: this sucks, shouldn't it be grown? + m_iFramecounter = -1; + m_pStudioHdr = NULL; + m_flTime = -1.0f; + m_target.SetSize( 0 ); +} + + +void CIKContext::Init( const CStudioHdr *pStudioHdr, const QAngle &angles, const Vector &pos, float flTime, int iFramecounter, int boneMask ) +{ + m_pStudioHdr = pStudioHdr; + m_ikChainRule.RemoveAll(); // m_numikrules = 0; + if (pStudioHdr->numikchains()) + { + m_ikChainRule.SetSize( pStudioHdr->numikchains() ); + + // FIXME: Brutal hackery to prevent a crash + if (m_target.Count() == 0) + { + m_target.SetSize(12); + memset( m_target.Base(), 0, sizeof(m_target[0])*m_target.Count() ); + ClearTargets(); + } + + } + else + { + m_target.SetSize( 0 ); + } + AngleMatrix( angles, pos, m_rootxform ); + m_iFramecounter = iFramecounter; + m_flTime = flTime; + m_boneMask = boneMask; +} + +void CIKContext::AddDependencies( mstudioseqdesc_t &seqdesc, int iSequence, float flCycle, const float poseParameters[], float flWeight ) +{ + int i; + + if (seqdesc.numikrules == 0) + return; + + ikcontextikrule_t ikrule; + + Assert( flWeight >= 0.0f && flWeight <= 1.0f ); + // This shouldn't be necessary, but the Assert should help us catch whoever is screwing this up + flWeight = clamp( flWeight, 0.0f, 1.0f ); + + // unify this + if (seqdesc.flags & STUDIO_REALTIME) + { + float cps = Studio_CPS( m_pStudioHdr, seqdesc, iSequence, poseParameters ); + flCycle = m_flTime * cps; + flCycle = flCycle - (int)flCycle; + } + else if (flCycle < 0 || flCycle >= 1) + { + if (seqdesc.flags & STUDIO_LOOPING) + { + flCycle = flCycle - (int)flCycle; + if (flCycle < 0) flCycle += 1; + } + else + { + flCycle = max( 0.0, min( flCycle, 0.9999 ) ); + } + } + + mstudioanimdesc_t *panim[4]; + float weight[4]; + + Studio_SeqAnims( m_pStudioHdr, seqdesc, iSequence, poseParameters, panim, weight ); + + // FIXME: add proper number of rules!!! + for (i = 0; i < seqdesc.numikrules; i++) + { + if ( !Studio_IKSequenceError( m_pStudioHdr, seqdesc, iSequence, flCycle, i, poseParameters, panim, weight, ikrule ) ) + continue; + + // don't add rule if the bone isn't going to be calculated + int bone = m_pStudioHdr->pIKChain( ikrule.chain )->pLink( 2 )->bone; + if ( !(m_pStudioHdr->pBone( bone )->flags & m_boneMask)) + continue; + + // or if its relative bone isn't going to be calculated + if ( ikrule.bone >= 0 && !(m_pStudioHdr->pBone( ikrule.bone )->flags & m_boneMask)) + continue; + + // FIXME: Brutal hackery to prevent a crash + if (m_target.Count() == 0) + { + m_target.SetSize(12); + memset( m_target.Base(), 0, sizeof(m_target[0])*m_target.Count() ); + ClearTargets(); + } + + ikrule.flRuleWeight = flWeight; + + if (ikrule.flRuleWeight * ikrule.flWeight > 0.999) + { + if ( ikrule.type != IK_UNLATCH) + { + // clear out chain if rule is 100% + m_ikChainRule.Element( ikrule.chain ).RemoveAll( ); + if ( ikrule.type == IK_RELEASE) + { + continue; + } + } + } + + int nIndex = m_ikChainRule.Element( ikrule.chain ).AddToTail( ); + m_ikChainRule.Element( ikrule.chain ).Element( nIndex ) = ikrule; + } +} + + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- + +void CIKContext::AddAutoplayLocks( Vector pos[], Quaternion q[] ) +{ + // skip all array access if no autoplay locks. + if (m_pStudioHdr->GetNumIKAutoplayLocks() == 0) + { + return; + } + + static matrix3x4_t boneToWorld[MAXSTUDIOBONES]; + CBoneBitList boneComputed; + + int ikOffset = m_ikLock.AddMultipleToTail( m_pStudioHdr->GetNumIKAutoplayLocks() ); + memset( &m_ikLock[ikOffset], 0, sizeof(ikcontextikrule_t)*m_pStudioHdr->GetNumIKAutoplayLocks() ); + + for (int i = 0; i < m_pStudioHdr->GetNumIKAutoplayLocks(); i++) + { + const mstudioiklock_t &lock = m_pStudioHdr->pIKAutoplayLock( i ); + mstudioikchain_t *pchain = m_pStudioHdr->pIKChain( lock.chain ); + int bone = pchain->pLink( 2 )->bone; + + // don't bother with iklock if the bone isn't going to be calculated + if ( !(m_pStudioHdr->pBone( bone )->flags & m_boneMask)) + continue; + + // eval current ik'd bone + BuildBoneChain( pos, q, bone, boneToWorld, boneComputed ); + + ikcontextikrule_t &ikrule = m_ikLock[ i + ikOffset ]; + + ikrule.chain = lock.chain; + ikrule.slot = i; + ikrule.type = IK_WORLD; + + MatrixAngles( boneToWorld[bone], ikrule.q, ikrule.pos ); + + // save off current knee direction + if (pchain->pLink(0)->kneeDir.LengthSqr() > 0.0) + { + Vector tmp = pchain->pLink( 0 )->kneeDir; + VectorRotate( pchain->pLink( 0 )->kneeDir, boneToWorld[ pchain->pLink( 0 )->bone ], ikrule.kneeDir ); + MatrixPosition( boneToWorld[ pchain->pLink( 1 )->bone ], ikrule.kneePos ); + } + else + { + ikrule.kneeDir.Init( ); + } + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- + +void CIKContext::AddSequenceLocks( mstudioseqdesc_t &seqdesc, Vector pos[], Quaternion q[] ) +{ + if ( seqdesc.numiklocks == 0 ) + { + return; + } + + static matrix3x4_t boneToWorld[MAXSTUDIOBONES]; + CBoneBitList boneComputed; + + int ikOffset = m_ikLock.AddMultipleToTail( seqdesc.numiklocks ); + memset( &m_ikLock[ikOffset], 0, sizeof(ikcontextikrule_t) * seqdesc.numiklocks ); + + for (int i = 0; i < seqdesc.numiklocks; i++) + { + mstudioiklock_t *plock = seqdesc.pIKLock( i ); + mstudioikchain_t *pchain = m_pStudioHdr->pIKChain( plock->chain ); + int bone = pchain->pLink( 2 )->bone; + + // don't bother with iklock if the bone isn't going to be calculated + if ( !(m_pStudioHdr->pBone( bone )->flags & m_boneMask)) + continue; + + // eval current ik'd bone + BuildBoneChain( pos, q, bone, boneToWorld, boneComputed ); + + ikcontextikrule_t &ikrule = m_ikLock[i+ikOffset]; + ikrule.chain = i; + ikrule.slot = i; + ikrule.type = IK_WORLD; + + MatrixAngles( boneToWorld[bone], ikrule.q, ikrule.pos ); + + // save off current knee direction + if (pchain->pLink(0)->kneeDir.LengthSqr() > 0.0) + { + VectorRotate( pchain->pLink( 0 )->kneeDir, boneToWorld[ pchain->pLink( 0 )->bone ], ikrule.kneeDir ); + } + else + { + ikrule.kneeDir.Init( ); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: build boneToWorld transforms for a specific bone +//----------------------------------------------------------------------------- +void CIKContext::BuildBoneChain( + const Vector pos[], + const Quaternion q[], + int iBone, + matrix3x4_t *pBoneToWorld, + CBoneBitList &boneComputed ) +{ + Assert( m_pStudioHdr->pBone( iBone )->flags & m_boneMask ); + ::BuildBoneChain( m_pStudioHdr, m_rootxform, pos, q, iBone, pBoneToWorld, boneComputed ); +} + + + +//----------------------------------------------------------------------------- +// Purpose: build boneToWorld transforms for a specific bone +//----------------------------------------------------------------------------- +void BuildBoneChain( + const CStudioHdr *pStudioHdr, + const matrix3x4_t &rootxform, + const Vector pos[], + const Quaternion q[], + int iBone, + matrix3x4_t *pBoneToWorld, + CBoneBitList &boneComputed ) +{ + if ( boneComputed.IsBoneMarked(iBone) ) + return; + + matrix3x4_t bonematrix; + QuaternionMatrix( q[iBone], pos[iBone], bonematrix ); + + int parent = pStudioHdr->pBone( iBone )->parent; + if (parent == -1) + { + ConcatTransforms( rootxform, bonematrix, pBoneToWorld[iBone] ); + } + else + { + // evil recursive!!! + BuildBoneChain( pStudioHdr, rootxform, pos, q, parent, pBoneToWorld, boneComputed ); + ConcatTransforms( pBoneToWorld[parent], bonematrix, pBoneToWorld[iBone]); + } + boneComputed.MarkBone(iBone); +} + + +//----------------------------------------------------------------------------- +// Purpose: turn a specific bones boneToWorld transform into a pos and q in parents bonespace +//----------------------------------------------------------------------------- +void SolveBone( + const CStudioHdr *pStudioHdr, + int iBone, + matrix3x4_t *pBoneToWorld, + Vector pos[], + Quaternion q[] + ) +{ + int iParent = pStudioHdr->pBone( iBone )->parent; + + matrix3x4_t worldToBone; + MatrixInvert( pBoneToWorld[iParent], worldToBone ); + + matrix3x4_t local; + ConcatTransforms( worldToBone, pBoneToWorld[iBone], local ); + + MatrixAngles( local, q[iBone], pos[iBone] ); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- + +void CIKTarget::SetOwner( int entindex, const Vector &pos, const QAngle &angles ) +{ + latched.owner = entindex; + latched.absOrigin = pos; + latched.absAngles = angles; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- + +void CIKTarget::ClearOwner( void ) +{ + latched.owner = -1; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- + +int CIKTarget::GetOwner( void ) +{ + return latched.owner; +} + +//----------------------------------------------------------------------------- +// Purpose: update the latched IK values that are in a moving frame of reference +//----------------------------------------------------------------------------- + +void CIKTarget::UpdateOwner( int entindex, const Vector &pos, const QAngle &angles ) +{ + if (pos == latched.absOrigin && angles == latched.absAngles) + return; + + matrix3x4_t in, out; + AngleMatrix( angles, pos, in ); + AngleIMatrix( latched.absAngles, latched.absOrigin, out ); + + matrix3x4_t tmp1, tmp2; + QuaternionMatrix( latched.q, latched.pos, tmp1 ); + ConcatTransforms( out, tmp1, tmp2 ); + ConcatTransforms( in, tmp2, tmp1 ); + MatrixAngles( tmp1, latched.q, latched.pos ); +} + + +//----------------------------------------------------------------------------- +// Purpose: sets the ground position of an ik target +//----------------------------------------------------------------------------- + +void CIKTarget::SetPos( const Vector &pos ) +{ + est.pos = pos; +} + +//----------------------------------------------------------------------------- +// Purpose: sets the ground "identity" orientation of an ik target +//----------------------------------------------------------------------------- + +void CIKTarget::SetAngles( const QAngle &angles ) +{ + AngleQuaternion( angles, est.q ); +} + +//----------------------------------------------------------------------------- +// Purpose: sets the ground "identity" orientation of an ik target +//----------------------------------------------------------------------------- + +void CIKTarget::SetQuaternion( const Quaternion &q ) +{ + est.q = q; +} + +//----------------------------------------------------------------------------- +// Purpose: calculates a ground "identity" orientation based on the surface +// normal of the ground and the desired ground identity orientation +//----------------------------------------------------------------------------- + +void CIKTarget::SetNormal( const Vector &normal ) +{ + // recalculate foot angle based on slope of surface + matrix3x4_t m1; + Vector forward, right; + QuaternionMatrix( est.q, m1 ); + + MatrixGetColumn( m1, 1, right ); + forward = CrossProduct( right, normal ); + right = CrossProduct( normal, forward ); + MatrixSetColumn( forward, 0, m1 ); + MatrixSetColumn( right, 1, m1 ); + MatrixSetColumn( normal, 2, m1 ); + QAngle a1; + Vector p1; + MatrixAngles( m1, est.q, p1 ); +} + + +//----------------------------------------------------------------------------- +// Purpose: estimates the ground impact at the center location assuming a the edge of +// an Z axis aligned disc collided with it the surface. +//----------------------------------------------------------------------------- + +void CIKTarget::SetPosWithNormalOffset( const Vector &pos, const Vector &normal ) +{ + // assume it's a disc edge intersecting with the floor, so try to estimate the z location of the center + est.pos = pos; + if (normal.z > 0.9999) + { + return; + } + // clamp at 45 degrees + else if (normal.z > 0.707) + { + // tan == sin / cos + float tan = sqrt( 1 - normal.z * normal.z ) / normal.z; + est.pos.z = est.pos.z - est.radius * tan; + } + else + { + est.pos.z = est.pos.z - est.radius; + } +} + + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- + +bool CIKTarget::IsActive() +{ + return (est.flWeight > 0.0f); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- + +void CIKTarget::IKFailed( void ) +{ + latched.deltaPos.Init(); + latched.deltaQ.Init(); + latched.pos = ideal.pos; + latched.q = ideal.q; + est.latched = 0.0; + est.flWeight = 0.0; +} + + +//----------------------------------------------------------------------------- +// Purpose: Invalidate any IK locks. +//----------------------------------------------------------------------------- + +void CIKContext::ClearTargets( void ) +{ + int i; + for (i = 0; i < m_target.Count(); i++) + { + m_target[i].latched.iFramecounter = -9999; + } +} + +//----------------------------------------------------------------------------- +// Purpose: Run through the rules that survived and turn a specific bones boneToWorld +// transform into a pos and q in parents bonespace +//----------------------------------------------------------------------------- + +void CIKContext::UpdateTargets( Vector pos[], Quaternion q[], matrix3x4_t boneToWorld[], CBoneBitList &boneComputed ) +{ + int i, j; + + for (i = 0; i < m_target.Count(); i++) + { + m_target[i].est.flWeight = 0.0f; + m_target[i].est.latched = 1.0f; + m_target[i].est.release = 1.0f; + m_target[i].est.height = 0.0f; + m_target[i].est.floor = 0.0f; + m_target[i].est.radius = 0.0f; + m_target[i].offset.pos.Init(); + m_target[i].offset.q.Init(); + } + + AutoIKRelease( ); + + for (j = 0; j < m_ikChainRule.Count(); j++) + { + for (i = 0; i < m_ikChainRule.Element( j ).Count(); i++) + { + ikcontextikrule_t *pRule = &m_ikChainRule.Element( j ).Element( i ); + + // ikchainresult_t *pChainRule = &chainRule[ m_ikRule[i].chain ]; + + switch( pRule->type ) + { + case IK_ATTACHMENT: + case IK_GROUND: + // case IK_SELF: + { + matrix3x4_t footTarget; + CIKTarget *pTarget = &m_target[pRule->slot]; + pTarget->chain = pRule->chain; + pTarget->type = pRule->type; + + if (pRule->type == IK_ATTACHMENT) + { + pTarget->offset.pAttachmentName = pRule->szLabel; + } + else + { + pTarget->offset.pAttachmentName = NULL; + } + + if (pRule->flRuleWeight == 1.0f || pTarget->est.flWeight == 0.0f) + { + pTarget->offset.q = pRule->q; + pTarget->offset.pos = pRule->pos; + pTarget->est.height = pRule->height; + pTarget->est.floor = pRule->floor; + pTarget->est.radius = pRule->radius; + pTarget->est.latched = pRule->latched * pRule->flRuleWeight; + pTarget->est.release = pRule->release; + pTarget->est.flWeight = pRule->flWeight * pRule->flRuleWeight; + } + else + { + QuaternionSlerp( pTarget->offset.q, pRule->q, pRule->flRuleWeight, pTarget->offset.q ); + pTarget->offset.pos = Lerp( pRule->flRuleWeight, pTarget->offset.pos, pRule->pos ); + pTarget->est.height = Lerp( pRule->flRuleWeight, pTarget->est.height, pRule->height ); + pTarget->est.floor = Lerp( pRule->flRuleWeight, pTarget->est.floor, pRule->floor ); + pTarget->est.radius = Lerp( pRule->flRuleWeight, pTarget->est.radius, pRule->radius ); + //pTarget->est.latched = Lerp( pRule->flRuleWeight, pTarget->est.latched, pRule->latched ); + pTarget->est.latched = min( pTarget->est.latched, pRule->latched ); + pTarget->est.release = Lerp( pRule->flRuleWeight, pTarget->est.release, pRule->release ); + pTarget->est.flWeight = Lerp( pRule->flRuleWeight, pTarget->est.flWeight, pRule->flWeight ); + } + + if ( pRule->type == IK_GROUND ) + { + pTarget->latched.deltaPos.z = 0; + pTarget->est.pos.z = pTarget->est.floor + m_rootxform[2][3]; + } + } + break; + case IK_UNLATCH: + { + CIKTarget *pTarget = &m_target[pRule->slot]; + if (pRule->latched > 0.0) + pTarget->est.latched = 0.0; + else + pTarget->est.latched = min( pTarget->est.latched, 1.0f - pRule->flWeight ); + } + break; + case IK_RELEASE: + { + CIKTarget *pTarget = &m_target[pRule->slot]; + if (pRule->latched > 0.0) + pTarget->est.latched = 0.0; + else + pTarget->est.latched = min( pTarget->est.latched, 1.0f - pRule->flWeight ); + + pTarget->est.flWeight = (pTarget->est.flWeight) * (1 - pRule->flWeight * pRule->flRuleWeight); + } + break; + } + } + } + + for (i = 0; i < m_target.Count(); i++) + { + CIKTarget *pTarget = &m_target[i]; + if (pTarget->est.flWeight > 0.0) + { + mstudioikchain_t *pchain = m_pStudioHdr->pIKChain( pTarget->chain ); + // ikchainresult_t *pChainRule = &chainRule[ i ]; + int bone = pchain->pLink( 2 )->bone; + + // eval current ik'd bone + BuildBoneChain( pos, q, bone, boneToWorld, boneComputed ); + + // xform IK target error into world space + matrix3x4_t local; + matrix3x4_t worldFootpad; + QuaternionMatrix( pTarget->offset.q, pTarget->offset.pos, local ); + MatrixInvert( local, local ); + ConcatTransforms( boneToWorld[bone], local, worldFootpad ); + + if (pTarget->est.latched == 1.0) + { + pTarget->latched.bNeedsLatch = true; + } + else + { + pTarget->latched.bNeedsLatch = false; + } + + // disable latched position if it looks invalid + if (m_iFramecounter < 0 || pTarget->latched.iFramecounter < m_iFramecounter - 1 || pTarget->latched.iFramecounter > m_iFramecounter) + { + pTarget->latched.bHasLatch = false; + pTarget->latched.influence = 0.0; + } + pTarget->latched.iFramecounter = m_iFramecounter; + + // find ideal contact position + MatrixAngles( worldFootpad, pTarget->ideal.q, pTarget->ideal.pos ); + pTarget->est.q = pTarget->ideal.q; + pTarget->est.pos = pTarget->ideal.pos; + + float latched = pTarget->est.latched; + + if (pTarget->latched.bHasLatch) + { + if (pTarget->est.latched == 1.0) + { + // keep track of latch position error from ideal contact position + pTarget->latched.deltaPos = pTarget->latched.pos - pTarget->est.pos; + QuaternionSM( -1, pTarget->est.q, pTarget->latched.q, pTarget->latched.deltaQ ); + pTarget->est.q = pTarget->latched.q; + pTarget->est.pos = pTarget->latched.pos; + } + else if (pTarget->est.latched > 0.0) + { + // ramp out latch differences during decay phase of rule + if (latched > 0 && latched < pTarget->latched.influence) + { + // latching has decreased + float dt = pTarget->latched.influence - latched; + if (pTarget->latched.influence > 0.0) + dt = dt / pTarget->latched.influence; + + VectorScale( pTarget->latched.deltaPos, (1-dt), pTarget->latched.deltaPos ); + QuaternionScale( pTarget->latched.deltaQ, (1-dt), pTarget->latched.deltaQ ); + } + + // move ideal contact position by latched error factor + pTarget->est.pos = pTarget->est.pos + pTarget->latched.deltaPos; + QuaternionMA( pTarget->est.q, 1, pTarget->latched.deltaQ, pTarget->est.q ); + pTarget->latched.q = pTarget->est.q; + pTarget->latched.pos = pTarget->est.pos; + } + else + { + pTarget->latched.bHasLatch = false; + pTarget->latched.q = pTarget->est.q; + pTarget->latched.pos = pTarget->est.pos; + pTarget->latched.deltaPos.Init(); + pTarget->latched.deltaQ.Init(); + } + pTarget->latched.influence = latched; + } + + // check for illegal requests + Vector p1, p2, p3; + MatrixPosition( boneToWorld[pchain->pLink( 0 )->bone], p1 ); // hip + MatrixPosition( boneToWorld[pchain->pLink( 1 )->bone], p2 ); // knee + MatrixPosition( boneToWorld[pchain->pLink( 2 )->bone], p3 ); // foot + + float d1 = (p2 - p1).Length(); + float d2 = (p3 - p2).Length(); + + if (pTarget->latched.bHasLatch) + { + //float d3 = (p3 - p1).Length(); + float d4 = (p3 + pTarget->latched.deltaPos - p1).Length(); + + // unstick feet when distance is too great + if ((d4 < fabs( d1 - d2 ) || d4 * 0.95 > d1 + d2) && pTarget->est.latched > 0.2) + { + pTarget->error.flTime = m_flTime; + } + + // unstick feet when angle is too great + if (pTarget->est.latched > 0.2) + { + float d = fabs( pTarget->latched.deltaQ.w ) * 2.0f - 1.0f; // QuaternionDotProduct( pTarget->latched.q, pTarget->est.q ); + + // FIXME: cos(45), make property of chain + if (d < 0.707) + { + pTarget->error.flTime = m_flTime; + } + } + } + + Vector dt = pTarget->est.pos - p1; + VectorNormalize( dt ); + + pTarget->trace.p1 = p1 + dt * (fabs( d1 - d2 ) * 1.01); + pTarget->trace.p2 = p1 + dt * (d1 + d2) * 0.99; + pTarget->trace.p3 = p1 + Vector( 0, 0, -1 ) * (d1 + d2); + // pTarget->trace.endpos = pTarget->est.pos; + } + } +} + + +//----------------------------------------------------------------------------- +// Purpose: insert release rules if the ik rules were in error +//----------------------------------------------------------------------------- + +void CIKContext::AutoIKRelease( void ) +{ + int i; + + for (i = 0; i < m_target.Count(); i++) + { + CIKTarget *pTarget = &m_target[i]; + + float dt = m_flTime - pTarget->error.flTime; + if (pTarget->error.bInError || dt < 0.5) + { + if (!pTarget->error.bInError) + { + pTarget->error.ramp = 0.0; + pTarget->error.flErrorTime = pTarget->error.flTime; + pTarget->error.bInError = true; + } + + float ft = m_flTime - pTarget->error.flErrorTime; + if (dt < 0.25) + { + pTarget->error.ramp = min( pTarget->error.ramp + ft * 4.0, 1.0 ); + } + else + { + pTarget->error.ramp = max( pTarget->error.ramp - ft * 4.0, 0.0 ); + } + if (pTarget->error.ramp > 0.0) + { + ikcontextikrule_t ikrule; + + ikrule.chain = pTarget->chain; + ikrule.bone = 0; + ikrule.type = IK_RELEASE; + ikrule.slot = i; + ikrule.flWeight = SimpleSpline( pTarget->error.ramp ); + ikrule.flRuleWeight = 1.0; + ikrule.latched = dt < 0.25 ? 0.0 : ikrule.flWeight; + + // don't bother with AutoIKRelease if the bone isn't going to be calculated + // this code is crashing for some unknown reason. + if ( pTarget->chain >= 0 && pTarget->chain < m_pStudioHdr->numikchains()) + { + mstudioikchain_t *pchain = m_pStudioHdr->pIKChain( pTarget->chain ); + if (pchain != NULL) + { + int bone = pchain->pLink( 2 )->bone; + if (bone >= 0 && bone < m_pStudioHdr->numbones()) + { + mstudiobone_t *pBone = m_pStudioHdr->pBone( bone ); + if (pBone != NULL) + { + if ( !(pBone->flags & m_boneMask)) + { + pTarget->error.bInError = false; + continue; + } + /* + char buf[256]; + sprintf( buf, "dt %.4f ft %.4f weight %.4f latched %.4f\n", dt, ft, ikrule.flWeight, ikrule.latched ); + OutputDebugString( buf ); + */ + + int nIndex = m_ikChainRule.Element( ikrule.chain ).AddToTail( ); + m_ikChainRule.Element( ikrule.chain ).Element( nIndex ) = ikrule; + } + else + { + DevWarning( 1, "AutoIKRelease (%s) got a NULL pBone %d\n", m_pStudioHdr->name(), bone ); + } + } + else + { + DevWarning( 1, "AutoIKRelease (%s) got an out of range bone %d (%d)\n", m_pStudioHdr->name(), bone, m_pStudioHdr->numbones() ); + } + } + else + { + DevWarning( 1, "AutoIKRelease (%s) got a NULL pchain %d\n", m_pStudioHdr->name(), pTarget->chain ); + } + } + else + { + DevWarning( 1, "AutoIKRelease (%s) got an out of range chain %d (%d)\n", m_pStudioHdr->name(), pTarget->chain, m_pStudioHdr->numikchains()); + } + } + else + { + pTarget->error.bInError = false; + } + pTarget->error.flErrorTime = m_flTime; + } + } +} + + + +void CIKContext::SolveDependencies( Vector pos[], Quaternion q[], matrix3x4_t boneToWorld[], CBoneBitList &boneComputed ) +{ + ASSERT_NO_REENTRY(); + + matrix3x4_t worldTarget; + int i, j; + + ikchainresult_t chainResult[32]; // allocate!!! + + // init chain rules + for (i = 0; i < m_pStudioHdr->numikchains(); i++) + { + mstudioikchain_t *pchain = m_pStudioHdr->pIKChain( i ); + ikchainresult_t *pChainResult = &chainResult[ i ]; + int bone = pchain->pLink( 2 )->bone; + + pChainResult->target = -1; + pChainResult->flWeight = 0.0; + + // don't bother with chain if the bone isn't going to be calculated + if ( !(m_pStudioHdr->pBone( bone )->flags & m_boneMask)) + continue; + + // eval current ik'd bone + BuildBoneChain( pos, q, bone, boneToWorld, boneComputed ); + + MatrixAngles( boneToWorld[bone], pChainResult->q, pChainResult->pos ); + } + + for (j = 0; j < m_ikChainRule.Count(); j++) + { + for (i = 0; i < m_ikChainRule.Element( j ).Count(); i++) + { + ikcontextikrule_t *pRule = &m_ikChainRule.Element( j ).Element( i ); + ikchainresult_t *pChainResult = &chainResult[ pRule->chain ]; + pChainResult->target = -1; + + + switch( pRule->type ) + { + case IK_SELF: + { + // xform IK target error into world space + matrix3x4_t local; + QuaternionMatrix( pRule->q, pRule->pos, local ); + // eval target bone space + if (pRule->bone != -1) + { + BuildBoneChain( pos, q, pRule->bone, boneToWorld, boneComputed ); + ConcatTransforms( boneToWorld[pRule->bone], local, worldTarget ); + } + else + { + ConcatTransforms( m_rootxform, local, worldTarget ); + } + + float flWeight = pRule->flWeight * pRule->flRuleWeight; + pChainResult->flWeight = pChainResult->flWeight * (1 - flWeight) + flWeight; + + Vector p2; + Quaternion q2; + + // target p and q + MatrixAngles( worldTarget, q2, p2 ); + + // debugLine( pChainResult->pos, p2, 0, 0, 255, true, 0.1 ); + + // blend in position and angles + pChainResult->pos = pChainResult->pos * (1.0 - flWeight) + p2 * flWeight; + QuaternionSlerp( pChainResult->q, q2, flWeight, pChainResult->q ); + } + break; + case IK_WORLD: + Assert( 0 ); + break; + + case IK_ATTACHMENT: + break; + + case IK_GROUND: + break; + + case IK_RELEASE: + { + // move target back towards original location + float flWeight = pRule->flWeight * pRule->flRuleWeight; + mstudioikchain_t *pchain = m_pStudioHdr->pIKChain( pRule->chain ); + int bone = pchain->pLink( 2 )->bone; + + Vector p2; + Quaternion q2; + + BuildBoneChain( pos, q, bone, boneToWorld, boneComputed ); + MatrixAngles( boneToWorld[bone], q2, p2 ); + + // blend in position and angles + pChainResult->pos = pChainResult->pos * (1.0 - flWeight) + p2 * flWeight; + QuaternionSlerp( pChainResult->q, q2, flWeight, pChainResult->q ); + } + break; + case IK_UNLATCH: + { + /* + pChainResult->flWeight = pChainResult->flWeight * (1 - pRule->flWeight) + pRule->flWeight; + + pChainResult->pos = pChainResult->pos * (1.0 - pRule->flWeight ) + pChainResult->local.pos * pRule->flWeight; + QuaternionSlerp( pChainResult->q, pChainResult->local.q, pRule->flWeight, pChainResult->q ); + */ + } + break; + } + } + } + + for (i = 0; i < m_target.Count(); i++) + { + CIKTarget *pTarget = &m_target[i]; + + if (m_target[i].est.flWeight > 0.0) + { + matrix3x4_t worldFootpad; + matrix3x4_t local; + //mstudioikchain_t *pchain = m_pStudioHdr->pIKChain( m_target[i].chain ); + ikchainresult_t *pChainResult = &chainResult[ pTarget->chain ]; + + AngleMatrix(pTarget->offset.q, pTarget->offset.pos, local ); + + AngleMatrix( pTarget->est.q, pTarget->est.pos, worldFootpad ); + + ConcatTransforms( worldFootpad, local, worldTarget ); + + Vector p2; + Quaternion q2; + // target p and q + MatrixAngles( worldTarget, q2, p2 ); + // MatrixAngles( worldTarget, pChainResult->q, pChainResult->pos ); + + // blend in position and angles + pChainResult->flWeight = pTarget->est.flWeight; + pChainResult->pos = pChainResult->pos * (1.0 - pChainResult->flWeight ) + p2 * pChainResult->flWeight; + QuaternionSlerp( pChainResult->q, q2, pChainResult->flWeight, pChainResult->q ); + } + + if (pTarget->latched.bNeedsLatch) + { + // keep track of latch position + pTarget->latched.bHasLatch = true; + pTarget->latched.q = pTarget->est.q; + pTarget->latched.pos = pTarget->est.pos; + } + } + + for (i = 0; i < m_pStudioHdr->numikchains(); i++) + { + ikchainresult_t *pChainResult = &chainResult[ i ]; + mstudioikchain_t *pchain = m_pStudioHdr->pIKChain( i ); + + if (pChainResult->flWeight > 0.0) + { + Vector tmp; + MatrixPosition( boneToWorld[pchain->pLink( 2 )->bone], tmp ); + // debugLine( pChainResult->pos, tmp, 255, 255, 255, true, 0.1 ); + + // do exact IK solution + // FIXME: once per link! + if (Studio_SolveIK(pchain, pChainResult->pos, boneToWorld )) + { + Vector p3; + MatrixGetColumn( boneToWorld[pchain->pLink( 2 )->bone], 3, p3 ); + QuaternionMatrix( pChainResult->q, p3, boneToWorld[pchain->pLink( 2 )->bone] ); + + // rebuild chain + // FIXME: is this needed if everyone past this uses the boneToWorld array? + SolveBone( m_pStudioHdr, pchain->pLink( 2 )->bone, boneToWorld, pos, q ); + SolveBone( m_pStudioHdr, pchain->pLink( 1 )->bone, boneToWorld, pos, q ); + SolveBone( m_pStudioHdr, pchain->pLink( 0 )->bone, boneToWorld, pos, q ); + } + else + { + // FIXME: need to invalidate the targets that forced this... + if (pChainResult->target != -1) + { + CIKTarget *pTarget = &m_target[pChainResult->target]; + VectorScale( pTarget->latched.deltaPos, 0.8, pTarget->latched.deltaPos ); + QuaternionScale( pTarget->latched.deltaQ, 0.8, pTarget->latched.deltaQ ); + } + } + } + } + +#if 0 + Vector p1, p2, p3; + Quaternion q1, q2, q3; + + // current p and q + MatrixAngles( boneToWorld[bone], q1, p1 ); + + + // target p and q + MatrixAngles( worldTarget, q2, p2 ); + + // blend in position and angles + p3 = p1 * (1.0 - m_ikRule[i].flWeight ) + p2 * m_ikRule[i].flWeight; + + // do exact IK solution + // FIXME: once per link! + Studio_SolveIK(pchain, p3, boneToWorld ); + + // force angle (bad?) + QuaternionSlerp( q1, q2, m_ikRule[i].flWeight, q3 ); + MatrixGetColumn( boneToWorld[bone], 3, p3 ); + QuaternionMatrix( q3, p3, boneToWorld[bone] ); + + // rebuild chain + SolveBone( m_pStudioHdr, pchain->pLink( 2 )->bone, boneToWorld, pos, q ); + SolveBone( m_pStudioHdr, pchain->pLink( 1 )->bone, boneToWorld, pos, q ); + SolveBone( m_pStudioHdr, pchain->pLink( 0 )->bone, boneToWorld, pos, q ); +#endif +} + + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- + +void CIKContext::SolveAutoplayLocks( + Vector pos[], + Quaternion q[] + ) +{ + ASSERT_NO_REENTRY(); + + static matrix3x4_t boneToWorld[MAXSTUDIOBONES]; + CBoneBitList boneComputed; + int i; + + for (i = 0; i < m_ikLock.Count(); i++) + { + const mstudioiklock_t &lock = m_pStudioHdr->pIKAutoplayLock( i ); + mstudioikchain_t *pchain = m_pStudioHdr->pIKChain( lock.chain ); + int bone = pchain->pLink( 2 )->bone; + + // don't bother with iklock if the bone isn't going to be calculated + if ( !(m_pStudioHdr->pBone( bone )->flags & m_boneMask)) + continue; + + // eval current ik'd bone + BuildBoneChain( pos, q, bone, boneToWorld, boneComputed ); + + Vector p1, p2, p3; + Quaternion q2, q3; + + // current p and q + MatrixPosition( boneToWorld[bone], p1 ); + + // blend in position + p3 = p1 * (1.0 - lock.flPosWeight ) + m_ikLock[i].pos * lock.flPosWeight; + + // do exact IK solution + if (m_ikLock[i].kneeDir.LengthSqr() > 0) + { + Studio_SolveIK(pchain->pLink( 0 )->bone, pchain->pLink( 1 )->bone, pchain->pLink( 2 )->bone, p3, m_ikLock[i].kneePos, m_ikLock[i].kneeDir, boneToWorld ); + } + else + { + Studio_SolveIK(pchain, p3, boneToWorld ); + } + + // slam orientation + MatrixPosition( boneToWorld[bone], p3 ); + QuaternionMatrix( m_ikLock[i].q, p3, boneToWorld[bone] ); + + // rebuild chain + q2 = q[ bone ]; + SolveBone( m_pStudioHdr, pchain->pLink( 2 )->bone, boneToWorld, pos, q ); + QuaternionSlerp( q[bone], q2, lock.flLocalQWeight, q[bone] ); + + SolveBone( m_pStudioHdr, pchain->pLink( 1 )->bone, boneToWorld, pos, q ); + SolveBone( m_pStudioHdr, pchain->pLink( 0 )->bone, boneToWorld, pos, q ); + } +} + + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- + +void CIKContext::SolveSequenceLocks( + mstudioseqdesc_t &seqdesc, + Vector pos[], + Quaternion q[] + ) +{ + ASSERT_NO_REENTRY(); + + static matrix3x4_t boneToWorld[MAXSTUDIOBONES]; + CBoneBitList boneComputed; + int i; + + for (i = 0; i < m_ikLock.Count(); i++) + { + mstudioiklock_t *plock = seqdesc.pIKLock( i ); + mstudioikchain_t *pchain = m_pStudioHdr->pIKChain( plock->chain ); + int bone = pchain->pLink( 2 )->bone; + + // don't bother with iklock if the bone isn't going to be calculated + if ( !(m_pStudioHdr->pBone( bone )->flags & m_boneMask)) + continue; + + // eval current ik'd bone + BuildBoneChain( pos, q, bone, boneToWorld, boneComputed ); + + Vector p1, p2, p3; + Quaternion q2, q3; + + // current p and q + MatrixPosition( boneToWorld[bone], p1 ); + + // blend in position + p3 = p1 * (1.0 - plock->flPosWeight ) + m_ikLock[i].pos * plock->flPosWeight; + + // do exact IK solution + if (m_ikLock[i].kneeDir.LengthSqr() > 0) + { + Studio_SolveIK(pchain->pLink( 0 )->bone, pchain->pLink( 1 )->bone, pchain->pLink( 2 )->bone, p3, m_ikLock[i].kneePos, m_ikLock[i].kneeDir, boneToWorld ); + } + else + { + Studio_SolveIK(pchain, p3, boneToWorld ); + } + + // slam orientation + MatrixPosition( boneToWorld[bone], p3 ); + QuaternionMatrix( m_ikLock[i].q, p3, boneToWorld[bone] ); + + // rebuild chain + q2 = q[ bone ]; + SolveBone( m_pStudioHdr, pchain->pLink( 2 )->bone, boneToWorld, pos, q ); + QuaternionSlerp( q[bone], q2, plock->flLocalQWeight, q[bone] ); + + SolveBone( m_pStudioHdr, pchain->pLink( 1 )->bone, boneToWorld, pos, q ); + SolveBone( m_pStudioHdr, pchain->pLink( 0 )->bone, boneToWorld, pos, q ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: run all animations that automatically play and are driven off of poseParameters +//----------------------------------------------------------------------------- +void CalcAutoplaySequences( + const CStudioHdr *pStudioHdr, + CIKContext *pIKContext, + Vector pos[], + Quaternion q[], + const float poseParameters[], + int boneMask, + float realTime + ) +{ + ASSERT_NO_REENTRY(); + + int i; + if ( pIKContext ) + { + pIKContext->AddAutoplayLocks( pos, q ); + } + + unsigned short *pList = NULL; + int count = pStudioHdr->GetAutoplayList( &pList ); + for (i = 0; i < count; i++) + { + int sequenceIndex = pList[i]; + mstudioseqdesc_t &seqdesc = pStudioHdr->pSeqdesc( sequenceIndex ); + if (seqdesc.flags & STUDIO_AUTOPLAY) + { + float cycle = 0; + float cps = Studio_CPS( pStudioHdr, seqdesc, sequenceIndex, poseParameters ); + cycle = realTime * cps; + cycle = cycle - (int)cycle; + + AccumulatePose( pStudioHdr, NULL, pos, q, sequenceIndex, cycle, poseParameters, boneMask, 1.0, realTime ); + } + } + + if ( pIKContext ) + { + pIKContext->SolveAutoplayLocks( pos, q ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void Studio_BuildMatrices( + const CStudioHdr *pStudioHdr, + const QAngle& angles, + const Vector& origin, + const Vector pos[], + const Quaternion q[], + int iBone, + matrix3x4_t bonetoworld[MAXSTUDIOBONES], + int boneMask + ) +{ + int i, j; + + int chain[MAXSTUDIOBONES]; + int chainlength = 0; + + if (iBone < -1 || iBone >= pStudioHdr->numbones()) + iBone = 0; + + mstudiobone_t *pbones = pStudioHdr->pBone( 0 ); + + // build list of what bones to use + if (iBone == -1) + { + // all bones + chainlength = pStudioHdr->numbones(); + for (i = 0; i < pStudioHdr->numbones(); i++) + { + chain[chainlength - i - 1] = i; + } + } + else + { + // only the parent bones + i = iBone; + while (i != -1) + { + chain[chainlength++] = i; + i = pbones[i].parent; + } + } + + matrix3x4_t bonematrix; + matrix3x4_t rotationmatrix; // model to world transformation + AngleMatrix( angles, origin, rotationmatrix); + + for (j = chainlength - 1; j >= 0; j--) + { + i = chain[j]; + if (pbones[i].flags & boneMask) + { + QuaternionMatrix( q[i], pos[i], bonematrix ); + + if (pbones[i].parent == -1) + { + ConcatTransforms (rotationmatrix, bonematrix, bonetoworld[i]); + } + else + { + ConcatTransforms (bonetoworld[pbones[i].parent], bonematrix, bonetoworld[i]); + } + } + } +} + + +//----------------------------------------------------------------------------- +// Purpose: look at single column vector of another bones local transformation +// and generate a procedural transformation based on how that column +// points down the 6 cardinal axis (all negative weights are clamped to 0). +//----------------------------------------------------------------------------- + +void DoAxisInterpBone( + mstudiobone_t *pbones, + int ibone, + CBoneAccessor &bonetoworld + ) +{ + matrix3x4_t bonematrix; + Vector control; + + mstudioaxisinterpbone_t *pProc = (mstudioaxisinterpbone_t *)pbones[ibone].pProcedure( ); + const matrix3x4_t &controlBone = bonetoworld.GetBone( pProc->control ); + if (pProc && pbones[pProc->control].parent != -1) + { + Vector tmp; + // pull out the control column + tmp.x = controlBone[0][pProc->axis]; + tmp.y = controlBone[1][pProc->axis]; + tmp.z = controlBone[2][pProc->axis]; + + // invert it back into parent's space. + VectorIRotate( tmp, bonetoworld.GetBone( pbones[pProc->control].parent ), control ); +#if 0 + matrix3x4_t tmpmatrix; + matrix3x4_t controlmatrix; + MatrixInvert( bonetoworld.GetBone( pbones[pProc->control].parent ), tmpmatrix ); + ConcatTransforms( tmpmatrix, bonetoworld.GetBone( pProc->control ), controlmatrix ); + + // pull out the control column + control.x = controlmatrix[0][pProc->axis]; + control.y = controlmatrix[1][pProc->axis]; + control.z = controlmatrix[2][pProc->axis]; +#endif + } + else + { + // pull out the control column + control.x = controlBone[0][pProc->axis]; + control.y = controlBone[1][pProc->axis]; + control.z = controlBone[2][pProc->axis]; + } + + Quaternion *q1, *q2, *q3; + Vector *p1, *p2, *p3; + + // find axial control inputs + float a1 = control.x; + float a2 = control.y; + float a3 = control.z; + if (a1 >= 0) + { + q1 = &pProc->quat[0]; + p1 = &pProc->pos[0]; + } + else + { + a1 = -a1; + q1 = &pProc->quat[1]; + p1 = &pProc->pos[1]; + } + + if (a2 >= 0) + { + q2 = &pProc->quat[2]; + p2 = &pProc->pos[2]; + } + else + { + a2 = -a2; + q2 = &pProc->quat[3]; + p2 = &pProc->pos[3]; + } + + if (a3 >= 0) + { + q3 = &pProc->quat[4]; + p3 = &pProc->pos[4]; + } + else + { + a3 = -a3; + q3 = &pProc->quat[5]; + p3 = &pProc->pos[5]; + } + + // do a three-way blend + Vector p; + Quaternion v, tmp; + if (a1 + a2 > 0) + { + float t = 1.0 / (a1 + a2 + a3); + // FIXME: do a proper 3-way Quat blend! + QuaternionSlerp( *q2, *q1, a1 / (a1 + a2), tmp ); + QuaternionSlerp( tmp, *q3, a3 * t, v ); + VectorScale( *p1, a1 * t, p ); + VectorMA( p, a2 * t, *p2, p ); + VectorMA( p, a3 * t, *p3, p ); + } + else + { + QuaternionSlerp( *q3, *q3, 0, v ); // ??? no quat copy? + p = *p3; + } + + QuaternionMatrix( v, p, bonematrix ); + + ConcatTransforms (bonetoworld.GetBone( pbones[ibone].parent ), bonematrix, bonetoworld.GetBoneForWrite( ibone )); +} + + + +//----------------------------------------------------------------------------- +// Purpose: Generate a procedural transformation based on how that another bones +// local transformation matches a set of target orientations. +//----------------------------------------------------------------------------- +void DoQuatInterpBone( + mstudiobone_t *pbones, + int ibone, + CBoneAccessor &bonetoworld + ) +{ + matrix3x4_t bonematrix; + Vector control; + + mstudioquatinterpbone_t *pProc = (mstudioquatinterpbone_t *)pbones[ibone].pProcedure( ); + if (pProc && pbones[pProc->control].parent != -1) + { + Quaternion src; + float weight[32]; + float scale = 0.0; + Quaternion quat; + Vector pos; + + matrix3x4_t tmpmatrix; + matrix3x4_t controlmatrix; + MatrixInvert( bonetoworld.GetBone( pbones[pProc->control].parent), tmpmatrix ); + ConcatTransforms( tmpmatrix, bonetoworld.GetBone( pProc->control ), controlmatrix ); + + MatrixAngles( controlmatrix, src, pos ); // FIXME: make a version without pos + + int i; + for (i = 0; i < pProc->numtriggers; i++) + { + float dot = fabs( QuaternionDotProduct( pProc->pTrigger( i )->trigger, src ) ); + // FIXME: a fast acos should be acceptable + dot = clamp( dot, -1, 1 ); + weight[i] = 1 - (2 * acos( dot ) * pProc->pTrigger( i )->inv_tolerance ); + weight[i] = max( 0, weight[i] ); + scale += weight[i]; + } + + if (scale <= 0.001) // EPSILON? + { + AngleMatrix( pProc->pTrigger( 0 )->quat, pProc->pTrigger( 0 )->pos, bonematrix ); + ConcatTransforms ( bonetoworld.GetBone( pbones[ibone].parent ), bonematrix, bonetoworld.GetBoneForWrite( ibone ) ); + return; + } + + scale = 1.0 / scale; + + quat.Init( 0, 0, 0, 0); + pos.Init( ); + + for (i = 0; i < pProc->numtriggers; i++) + { + if (weight[i]) + { + float s = weight[i] * scale; + mstudioquatinterpinfo_t *pTrigger = pProc->pTrigger( i ); + + QuaternionAlign( pTrigger->quat, quat, quat ); + + quat.x = quat.x + s * pTrigger->quat.x; + quat.y = quat.y + s * pTrigger->quat.y; + quat.z = quat.z + s * pTrigger->quat.z; + quat.w = quat.w + s * pTrigger->quat.w; + pos.x = pos.x + s * pTrigger->pos.x; + pos.y = pos.y + s * pTrigger->pos.y; + pos.z = pos.z + s * pTrigger->pos.z; + } + } + Assert( QuaternionNormalize( quat ) != 0); + QuaternionMatrix( quat, pos, bonematrix ); + } + + ConcatTransforms (bonetoworld.GetBone( pbones[ibone].parent ), bonematrix, bonetoworld.GetBoneForWrite( ibone )); +} + +/* + * This is for DoAimAtBone below, was just for testing, not needed in general + * but to turn it back on, uncomment this and the section in DoAimAtBone() below + * + +static ConVar aim_constraint( "aim_constraint", "1", FCVAR_REPLICATED, "Toggle Helper Bones" ); + +*/ + +//----------------------------------------------------------------------------- +// Purpose: Generate a procedural transformation so that one bone points at +// another point on the model +//----------------------------------------------------------------------------- +void DoAimAtBone( + mstudiobone_t *pBones, + int iBone, + CBoneAccessor &bonetoworld, + const CStudioHdr *pStudioHdr + ) +{ + mstudioaimatbone_t *pProc = (mstudioaimatbone_t *)pBones[iBone].pProcedure(); + + if ( !pProc ) + { + return; + } + + /* + * Uncomment this if the ConVar above is uncommented + * + + if ( !aim_constraint.GetBool() ) + { + // If the aim constraint is turned off then just copy the parent transform + // plus the offset value + + matrix3x4_t boneToWorldSpace; + MatrixCopy ( bonetoworld.GetBone( pProc->parent ), boneToWorldSpace ); + Vector boneWorldPosition; + VectorTransform( pProc->basepos, boneToWorldSpace, boneWorldPosition ); + MatrixSetColumn( boneWorldPosition, 3, boneToWorldSpace ); + MatrixCopy( boneToWorldSpace, bonetoworld.GetBoneForWrite( iBone ) ); + + return; + } + + */ + + // The world matrix of the bone to change + matrix3x4_t boneMatrix; + + // Guaranteed to be unit length + const Vector &userAimVector( pProc->aimvector ); + + // Guaranteed to be unit length + const Vector &userUpVector( pProc->upvector ); + + // Get to get position of bone but also for up reference + matrix3x4_t parentSpace; + MatrixCopy ( bonetoworld.GetBone( pProc->parent ), parentSpace ); + + // World space position of the bone to aim + Vector aimWorldPosition; + VectorTransform( pProc->basepos, parentSpace, aimWorldPosition ); + + // The worldspace matrix of the bone to aim at + matrix3x4_t aimAtSpace; + if ( pStudioHdr ) + { + // This means it's AIMATATTACH + const mstudioattachment_t &attachment( pStudioHdr->pAttachment( pProc->aim ) ); + ConcatTransforms( + bonetoworld.GetBone( attachment.localbone ), + attachment.local, + aimAtSpace ); + } + else + { + MatrixCopy( bonetoworld.GetBone( pProc->aim ), aimAtSpace ); + } + + Vector aimAtWorldPosition; + MatrixGetColumn( aimAtSpace, 3, aimAtWorldPosition ); + + Vector aimVector; + VectorSubtract( aimAtWorldPosition, aimWorldPosition, aimVector ); + VectorNormalizeFast( aimVector ); + + Vector axis; + CrossProduct( userAimVector, aimVector, axis ); + VectorNormalizeFast( axis ); + float angle( acosf( DotProduct( userAimVector, aimVector ) ) ); + Quaternion aimRotation; + AxisAngleQuaternion( axis, RAD2DEG( angle ), aimRotation ); + + if ( ( 1.0f - fabs( DotProduct( userUpVector, userAimVector ) ) ) > FLT_EPSILON ) + { + matrix3x4_t aimRotationMatrix; + QuaternionMatrix( aimRotation, aimRotationMatrix ); + + Vector tmpV; + + Vector tmp_pUp; + VectorRotate( userUpVector, aimRotationMatrix, tmp_pUp ); + VectorScale( aimVector, DotProduct( aimVector, tmp_pUp ), tmpV ); + Vector pUp; + VectorSubtract( tmp_pUp, tmpV, pUp ); + VectorNormalizeFast( pUp ); + + Vector tmp_pParentUp; + VectorRotate( userUpVector, parentSpace, tmp_pParentUp ); + VectorScale( aimVector, DotProduct( aimVector, tmp_pParentUp ), tmpV ); + Vector pParentUp; + VectorSubtract( tmp_pParentUp, tmpV, pParentUp ); + VectorNormalizeFast( pParentUp ); + + angle = acos( DotProduct( pUp, pParentUp ) ); + CrossProduct( pUp, pParentUp, axis ); + VectorNormalizeFast( axis ); + Quaternion upRotation; + AxisAngleQuaternion( axis, RAD2DEG( angle ), upRotation ); + + Quaternion boneRotation; + QuaternionMult( upRotation, aimRotation, boneRotation ); + QuaternionMatrix( boneRotation, aimWorldPosition, boneMatrix ); + } + else + { + QuaternionMatrix( aimRotation, aimWorldPosition, boneMatrix ); + } + + MatrixCopy( boneMatrix, bonetoworld.GetBoneForWrite( iBone ) ); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- + +bool CalcProceduralBone( + const CStudioHdr *pStudioHdr, + int iBone, + CBoneAccessor &bonetoworld + ) +{ + mstudiobone_t *pbones = pStudioHdr->pBone( 0 ); + + if ( pbones[iBone].flags & BONE_ALWAYS_PROCEDURAL ) + { + switch( pbones[iBone].proctype ) + { + case STUDIO_PROC_AXISINTERP: + DoAxisInterpBone( pbones, iBone, bonetoworld ); + return true; + + case STUDIO_PROC_QUATINTERP: + DoQuatInterpBone( pbones, iBone, bonetoworld ); + return true; + + case STUDIO_PROC_AIMATBONE: + DoAimAtBone( pbones, iBone, bonetoworld, NULL ); + return true; + + case STUDIO_PROC_AIMATATTACH: + DoAimAtBone( pbones, iBone, bonetoworld, pStudioHdr ); + return true; + + default: + return false; + } + } + return false; +} + + + +//----------------------------------------------------------------------------- +// Purpose: Lookup a bone controller +//----------------------------------------------------------------------------- + + + +static mstudiobonecontroller_t* FindController( const CStudioHdr *pStudioHdr, int iController) +{ + // find first controller that matches the index + for (int i = 0; i < pStudioHdr->numbonecontrollers(); i++) + { + if (pStudioHdr->pBonecontroller( i )->inputfield == iController) + return pStudioHdr->pBonecontroller( i ); + } + + return NULL; +} + + +//----------------------------------------------------------------------------- +// Purpose: converts a ranged bone controller value into a 0..1 encoded value +// Output: ctlValue contains 0..1 encoding. +// returns clamped ranged value +//----------------------------------------------------------------------------- + +float Studio_SetController( const CStudioHdr *pStudioHdr, int iController, float flValue, float &ctlValue ) +{ + if (! pStudioHdr) + return flValue; + + mstudiobonecontroller_t *pbonecontroller = FindController(pStudioHdr, iController); + if(!pbonecontroller) + { + ctlValue = 0; + return flValue; + } + + // wrap 0..360 if it's a rotational controller + if (pbonecontroller->type & (STUDIO_XR | STUDIO_YR | STUDIO_ZR)) + { + // ugly hack, invert value if end < start + if (pbonecontroller->end < pbonecontroller->start) + flValue = -flValue; + + // does the controller not wrap? + if (pbonecontroller->start + 359.0 >= pbonecontroller->end) + { + if (flValue > ((pbonecontroller->start + pbonecontroller->end) / 2.0) + 180) + flValue = flValue - 360; + if (flValue < ((pbonecontroller->start + pbonecontroller->end) / 2.0) - 180) + flValue = flValue + 360; + } + else + { + if (flValue > 360) + flValue = flValue - (int)(flValue / 360.0) * 360.0; + else if (flValue < 0) + flValue = flValue + (int)((flValue / -360.0) + 1) * 360.0; + } + } + + ctlValue = (flValue - pbonecontroller->start) / (pbonecontroller->end - pbonecontroller->start); + if (ctlValue < 0) ctlValue = 0; + if (ctlValue > 1) ctlValue = 1; + + float flReturnVal = ((1.0 - ctlValue)*pbonecontroller->start + ctlValue *pbonecontroller->end); + + // ugly hack, invert value if a rotational controller and end < start + if (pbonecontroller->type & (STUDIO_XR | STUDIO_YR | STUDIO_ZR) && + pbonecontroller->end < pbonecontroller->start ) + { + flReturnVal *= -1; + } + + return flReturnVal; +} + + +//----------------------------------------------------------------------------- +// Purpose: converts a 0..1 encoded bone controller value into a ranged value +// Output: returns ranged value +//----------------------------------------------------------------------------- + +float Studio_GetController( const CStudioHdr *pStudioHdr, int iController, float ctlValue ) +{ + if (!pStudioHdr) + return 0.0; + + mstudiobonecontroller_t *pbonecontroller = FindController(pStudioHdr, iController); + if(!pbonecontroller) + return 0; + + return ctlValue * (pbonecontroller->end - pbonecontroller->start) + pbonecontroller->start; +} + + +//----------------------------------------------------------------------------- +// Purpose: converts a ranged pose parameter value into a 0..1 encoded value +// Output: ctlValue contains 0..1 encoding. +// returns clamped ranged value +//----------------------------------------------------------------------------- + +float Studio_SetPoseParameter( const CStudioHdr *pStudioHdr, int iParameter, float flValue, float &ctlValue ) +{ + if (iParameter < 0 || iParameter >= pStudioHdr->GetNumPoseParameters()) + { + return 0; + } + + const mstudioposeparamdesc_t &PoseParam = pStudioHdr->pPoseParameter( iParameter ); + + Assert( IsFinite( flValue ) ); + + if (PoseParam.loop) + { + float wrap = (PoseParam.start + PoseParam.end) / 2.0 + PoseParam.loop / 2.0; + float shift = PoseParam.loop - wrap; + + flValue = flValue - PoseParam.loop * floor((flValue + shift) / PoseParam.loop); + } + + ctlValue = (flValue - PoseParam.start) / (PoseParam.end - PoseParam.start); + + if (ctlValue < 0) ctlValue = 0; + if (ctlValue > 1) ctlValue = 1; + + Assert( IsFinite( ctlValue ) ); + + return ctlValue * (PoseParam.end - PoseParam.start) + PoseParam.start; +} + + +//----------------------------------------------------------------------------- +// Purpose: converts a 0..1 encoded pose parameter value into a ranged value +// Output: returns ranged value +//----------------------------------------------------------------------------- + +float Studio_GetPoseParameter( const CStudioHdr *pStudioHdr, int iParameter, float ctlValue ) +{ + if (iParameter < 0 || iParameter >= pStudioHdr->GetNumPoseParameters()) + { + return 0; + } + + const mstudioposeparamdesc_t &PoseParam = pStudioHdr->pPoseParameter( iParameter ); + + return ctlValue * (PoseParam.end - PoseParam.start) + PoseParam.start; +} + +#ifdef _MSC_VER +#pragma warning (disable : 4701) +#endif + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +static int ClipRayToHitbox( const Ray_t &ray, mstudiobbox_t *pbox, matrix3x4_t& matrix, trace_t &tr ) +{ + // scale by current t so hits shorten the ray and increase the likelihood of early outs + Vector delta2; + VectorScale( ray.m_Delta, (0.5f * tr.fraction), delta2 ); + + // OPTIMIZE: Store this in the box instead of computing it here + // compute center in local space + Vector boxextents; + boxextents.x = (pbox->bbmin.x + pbox->bbmax.x) * 0.5; + boxextents.y = (pbox->bbmin.y + pbox->bbmax.y) * 0.5; + boxextents.z = (pbox->bbmin.z + pbox->bbmax.z) * 0.5; + Vector boxCenter; + // transform to world space + VectorTransform( boxextents, matrix, boxCenter ); + // calc extents from local center + boxextents.x = pbox->bbmax.x - boxextents.x; + boxextents.y = pbox->bbmax.y - boxextents.y; + boxextents.z = pbox->bbmax.z - boxextents.z; + // OPTIMIZE: This is optimized for world space. If the transform is fast enough, it may make more + // sense to just xform and call UTIL_ClipToBox() instead. MEASURE THIS. + + // save the extents of the ray along + Vector extent, uextent; + Vector segmentCenter; + segmentCenter.x = ray.m_Start.x + delta2.x - boxCenter.x; + segmentCenter.y = ray.m_Start.y + delta2.y - boxCenter.y; + segmentCenter.z = ray.m_Start.z + delta2.z - boxCenter.z; + + extent.Init(); + + // check box axes for separation + for ( int j = 0; j < 3; j++ ) + { + extent[j] = delta2.x * matrix[0][j] + delta2.y * matrix[1][j] + delta2.z * matrix[2][j]; + uextent[j] = fabsf(extent[j]); + float coord = segmentCenter.x * matrix[0][j] + segmentCenter.y * matrix[1][j] + segmentCenter.z * matrix[2][j]; + coord = fabsf(coord); + + if ( coord > (boxextents[j] + uextent[j]) ) + return -1; + } + + // now check cross axes for separation + float tmp, cextent; + Vector cross; + CrossProduct( delta2, segmentCenter, cross ); + cextent = cross.x * matrix[0][0] + cross.y * matrix[1][0] + cross.z * matrix[2][0]; + cextent = fabsf(cextent); + tmp = boxextents[1]*uextent[2] + boxextents[2]*uextent[1]; + if ( cextent > tmp ) + return -1; + + cextent = cross.x * matrix[0][1] + cross.y * matrix[1][1] + cross.z * matrix[2][1]; + cextent = fabsf(cextent); + tmp = boxextents[0]*uextent[2] + boxextents[2]*uextent[0]; + if ( cextent > tmp ) + return -1; + + cextent = cross.x * matrix[0][2] + cross.y * matrix[1][2] + cross.z * matrix[2][2]; + cextent = fabsf(cextent); + tmp = boxextents[0]*uextent[1] + boxextents[1]*uextent[0]; + if ( cextent > tmp ) + return -1; + + // !!! We hit this box !!! compute intersection point and return + Vector start; + // Compute ray start in bone space + VectorITransform( ray.m_Start, matrix, start ); + // extent is delta2 in bone space, recompute delta in bone space + VectorScale( extent, 2, extent ); + + // delta was prescaled by the current t, so no need to see if this intersection + // is closer + trace_t boxTrace; + if ( !IntersectRayWithBox( start, extent, pbox->bbmin, pbox->bbmax, 0.0f, &boxTrace ) ) + return -1; + Assert( IsFinite(boxTrace.fraction) ); + tr.fraction *= boxTrace.fraction; + tr.startsolid = boxTrace.startsolid; + int hitside = boxTrace.plane.type; + if ( boxTrace.plane.normal[hitside] >= 0 ) + { + hitside += 3; + } + return hitside; +} + +#ifdef _MSC_VER +#pragma warning (default : 4701) +#endif + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool SweepBoxToStudio( const Ray_t& ray, CStudioHdr *pStudioHdr, mstudiohitboxset_t *set, + matrix3x4_t **hitboxbones, int fContentsMask, trace_t &tr ) +{ + tr.fraction = 1.0; + tr.startsolid = false; + + // OPTIMIZE: Partition these? + Ray_t clippedRay = ray; + int hitbox = -1; + for ( int i = 0; i < set->numhitboxes; i++ ) + { + mstudiobbox_t *pbox = set->pHitbox(i); + + // Filter based on contents mask + int fBoneContents = pStudioHdr->pBone( pbox->bone )->contents; + if ( ( fBoneContents & fContentsMask ) == 0 ) + continue; + + trace_t obbTrace; + if ( IntersectRayWithOBB( clippedRay, *hitboxbones[pbox->bone], pbox->bbmin, pbox->bbmax, 0.0f, &obbTrace ) ) + { + tr.startpos = obbTrace.startpos; + tr.endpos = obbTrace.endpos; + tr.plane = obbTrace.plane; + tr.startsolid = obbTrace.startsolid; + tr.allsolid = obbTrace.allsolid; + + // This logic here is to shorten the ray each time to get more early outs + tr.fraction *= obbTrace.fraction; + clippedRay.m_Delta *= obbTrace.fraction; + hitbox = i; + if (tr.startsolid) + break; + } + } + + if ( hitbox >= 0 ) + { + tr.hitgroup = set->pHitbox(hitbox)->group; + tr.hitbox = hitbox; + tr.contents = pStudioHdr->pBone( set->pHitbox(hitbox)->bone )->contents | CONTENTS_HITBOX; + tr.physicsbone = pStudioHdr->pBone( set->pHitbox(hitbox)->bone )->physicsbone; + Assert( tr.physicsbone >= 0 ); + return true; + } + return false; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool TraceToStudio( const Ray_t& ray, CStudioHdr *pStudioHdr, mstudiohitboxset_t *set, + matrix3x4_t **hitboxbones, int fContentsMask, trace_t &tr ) +{ + if ( !ray.m_IsRay ) + { + return SweepBoxToStudio( ray, pStudioHdr, set, hitboxbones, fContentsMask, tr ); + } + + tr.fraction = 1.0; + tr.startsolid = false; + + // no hit yet + int hitbox = -1; + int hitside = -1; + + // OPTIMIZE: Partition these? + for ( int i = 0; i < set->numhitboxes; i++ ) + { + mstudiobbox_t *pbox = set->pHitbox(i); + + // Filter based on contents mask + int fBoneContents = pStudioHdr->pBone( pbox->bone )->contents; + if ( ( fBoneContents & fContentsMask ) == 0 ) + continue; + + // columns are axes of the bones in world space, translation is in world space + matrix3x4_t& matrix = *hitboxbones[pbox->bone]; + + int side = ClipRayToHitbox( ray, pbox, matrix, tr ); + if ( side >= 0 ) + { + hitbox = i; + hitside = side; + } + } + + if ( hitbox >= 0 ) + { + mstudiobbox_t *pbox = set->pHitbox(hitbox); + VectorMA( ray.m_Start, tr.fraction, ray.m_Delta, tr.endpos ); + tr.hitgroup = set->pHitbox(hitbox)->group; + tr.hitbox = hitbox; + tr.contents = pStudioHdr->pBone( pbox->bone )->contents | CONTENTS_HITBOX; + tr.physicsbone = pStudioHdr->pBone( pbox->bone )->physicsbone; + Assert( tr.physicsbone >= 0 ); + matrix3x4_t& matrix = *hitboxbones[pbox->bone]; + if ( hitside >= 3 ) + { + hitside -= 3; + tr.plane.normal[0] = matrix[0][hitside]; + tr.plane.normal[1] = matrix[1][hitside]; + tr.plane.normal[2] = matrix[2][hitside]; + //tr.plane.dist = DotProduct( tr.plane.normal, Vector(matrix[0][3], matrix[1][3], matrix[2][3] ) ) + pbox->bbmax[hitside]; + } + else + { + tr.plane.normal[0] = -matrix[0][hitside]; + tr.plane.normal[1] = -matrix[1][hitside]; + tr.plane.normal[2] = -matrix[2][hitside]; + //tr.plane.dist = DotProduct( tr.plane.normal, Vector(matrix[0][3], matrix[1][3], matrix[2][3] ) ) - pbox->bbmin[hitside]; + } + // simpler plane constant equation + tr.plane.dist = DotProduct( tr.endpos, tr.plane.normal ); + tr.plane.type = 3; + return true; + } + return false; +} + + +//----------------------------------------------------------------------------- +// Purpose: returns array of animations and weightings for a sequence based on current pose parameters +//----------------------------------------------------------------------------- + +void Studio_SeqAnims( const CStudioHdr *pStudioHdr, mstudioseqdesc_t &seqdesc, int iSequence, const float poseParameter[], mstudioanimdesc_t *panim[4], float *weight ) +{ +#if _DEBUG + VPROF_INCREMENT_COUNTER("SEQ_ANIMS",1); +#endif + if (!pStudioHdr || iSequence >= pStudioHdr->GetNumSeq()) + { + weight[0] = weight[1] = weight[2] = weight[3] = 0.0; + return; + } + + int i0 = 0, i1 = 0; + float s0 = 0, s1 = 0; + + Studio_LocalPoseParameter( pStudioHdr, poseParameter, seqdesc, iSequence, 0, s0, i0 ); + Studio_LocalPoseParameter( pStudioHdr, poseParameter, seqdesc, iSequence, 1, s1, i1 ); + + panim[0] = &pStudioHdr->pAnimdesc( pStudioHdr->iRelativeAnim( iSequence, seqdesc.anim( i0 , i1 ) ) ); + weight[0] = (1 - s0) * (1 - s1); + + panim[1] = &pStudioHdr->pAnimdesc( pStudioHdr->iRelativeAnim( iSequence, seqdesc.anim( i0+1, i1 ) ) ); + weight[1] = (s0) * (1 - s1); + + panim[2] = &pStudioHdr->pAnimdesc( pStudioHdr->iRelativeAnim( iSequence, seqdesc.anim( i0 , i1+1 ) ) ); + weight[2] = (1 - s0) * (s1); + + panim[3] = &pStudioHdr->pAnimdesc( pStudioHdr->iRelativeAnim( iSequence, seqdesc.anim( i0+1, i1+1 ) ) ); + weight[3] = (s0) * (s1); + + Assert( weight[0] >= 0.0f && weight[1] >= 0.0f && weight[2] >= 0.0f && weight[3] >= 0.0f ); +} + +//----------------------------------------------------------------------------- +// Purpose: returns max frame number for a sequence +//----------------------------------------------------------------------------- + +int Studio_MaxFrame( const CStudioHdr *pStudioHdr, int iSequence, const float poseParameter[] ) +{ + mstudioanimdesc_t *panim[4]; + float weight[4]; + + mstudioseqdesc_t &seqdesc = pStudioHdr->pSeqdesc( iSequence ); + Studio_SeqAnims( pStudioHdr, seqdesc, iSequence, poseParameter, panim, weight ); + + float maxFrame = 0; + for (int i = 0; i < 4; i++) + { + if (weight[i] > 0) + { + maxFrame += panim[i]->numframes * weight[i]; + } + } + + if ( maxFrame > 1 ) + maxFrame -= 1; + + + // FIXME: why does the weights sometimes not exactly add it 1.0 and this sometimes rounds down? + return static_cast(maxFrame + 0.01); +} + + +//----------------------------------------------------------------------------- +// Purpose: returns frames per second of a sequence +//----------------------------------------------------------------------------- + +float Studio_FPS( const CStudioHdr *pStudioHdr, int iSequence, const float poseParameter[] ) +{ + mstudioanimdesc_t *panim[4]; + float weight[4]; + + mstudioseqdesc_t &seqdesc = pStudioHdr->pSeqdesc( iSequence ); + Studio_SeqAnims( pStudioHdr, seqdesc, iSequence, poseParameter, panim, weight ); + + float t = 0; + + for (int i = 0; i < 4; i++) + { + if (weight[i] > 0) + { + t += panim[i]->fps * weight[i]; + } + } + return t; +} + + +//----------------------------------------------------------------------------- +// Purpose: returns cycles per second of a sequence (cycles/second) +//----------------------------------------------------------------------------- + +float Studio_CPS( const CStudioHdr *pStudioHdr, mstudioseqdesc_t &seqdesc, int iSequence, const float poseParameter[] ) +{ + mstudioanimdesc_t *panim[4]; + float weight[4]; + + Studio_SeqAnims( pStudioHdr, seqdesc, iSequence, poseParameter, panim, weight ); + + float t = 0; + + for (int i = 0; i < 4; i++) + { + if (weight[i] > 0 && panim[i]->numframes > 1) + { + t += (panim[i]->fps / (panim[i]->numframes - 1)) * weight[i]; + } + } + return t; +} + +//----------------------------------------------------------------------------- +// Purpose: returns length (in seconds) of a sequence (seconds/cycle) +//----------------------------------------------------------------------------- + +float Studio_Duration( const CStudioHdr *pStudioHdr, int iSequence, const float poseParameter[] ) +{ + mstudioseqdesc_t &seqdesc = pStudioHdr->pSeqdesc( iSequence ); + float cps = Studio_CPS( pStudioHdr, seqdesc, iSequence, poseParameter ); + + if( cps == 0 ) + return 0.0f; + + return 1.0f/cps; +} + + +//----------------------------------------------------------------------------- +// Purpose: calculate changes in position and angle relative to the start of an animations cycle +// Output: updated position and angle, relative to the origin +// returns false if animation is not a movement animation +//----------------------------------------------------------------------------- + +bool Studio_AnimPosition( mstudioanimdesc_t *panim, float flCycle, Vector &vecPos, QAngle &vecAngle ) +{ + float prevframe = 0; + vecPos.Init( ); + vecAngle.Init( ); + + if (panim->nummovements == 0) + return false; + + int iLoops = 0; + if (flCycle > 1.0) + { + iLoops = (int)flCycle; + } + else if (flCycle < 0.0) + { + iLoops = (int)flCycle - 1; + } + flCycle = flCycle - iLoops; + + float flFrame = flCycle * (panim->numframes - 1); + + for (int i = 0; i < panim->nummovements; i++) + { + mstudiomovement_t *pmove = panim->pMovement( i ); + + if (pmove->endframe >= flFrame) + { + float f = (flFrame - prevframe) / (pmove->endframe - prevframe); + + float d = pmove->v0 * f + 0.5 * (pmove->v1 - pmove->v0) * f * f; + + vecPos = vecPos + d * pmove->vector; + vecAngle.y = vecAngle.y * (1 - f) + pmove->angle * f; + if (iLoops != 0) + { + mstudiomovement_t *pmove = panim->pMovement( panim->nummovements - 1 ); + vecPos = vecPos + iLoops * pmove->position; + vecAngle.y = vecAngle.y + iLoops * pmove->angle; + } + return true; + } + else + { + prevframe = pmove->endframe; + vecPos = pmove->position; + vecAngle.y = pmove->angle; + } + } + + return false; +} + + +//----------------------------------------------------------------------------- +// Purpose: calculate instantaneous velocity in ips at a given point +// in the animations cycle +// Output: velocity vector, relative to identity orientation +// returns false if animation is not a movement animation +//----------------------------------------------------------------------------- + +bool Studio_AnimVelocity( mstudioanimdesc_t *panim, float flCycle, Vector &vecVelocity ) +{ + float prevframe = 0; + + float flFrame = flCycle * (panim->numframes - 1); + flFrame = flFrame - (int)(flFrame / (panim->numframes - 1)); + + for (int i = 0; i < panim->nummovements; i++) + { + mstudiomovement_t *pmove = panim->pMovement( i ); + + if (pmove->endframe >= flFrame) + { + float f = (flFrame - prevframe) / (pmove->endframe - prevframe); + + float vel = pmove->v0 * (1 - f) + pmove->v1 * f; + // scale from per block to per sec velocity + vel = vel * panim->fps / (pmove->endframe - prevframe); + + vecVelocity = pmove->vector * vel; + return true; + } + else + { + prevframe = pmove->endframe; + } + } + return false; +} + + +//----------------------------------------------------------------------------- +// Purpose: calculate changes in position and angle between two points in an animation cycle +// Output: updated position and angle, relative to CycleFrom being at the origin +// returns false if animation is not a movement animation +//----------------------------------------------------------------------------- + +bool Studio_AnimMovement( mstudioanimdesc_t *panim, float flCycleFrom, float flCycleTo, Vector &deltaPos, QAngle &deltaAngle ) +{ + if (panim->nummovements == 0) + return false; + + Vector startPos; + QAngle startA; + Studio_AnimPosition( panim, flCycleFrom, startPos, startA ); + + Vector endPos; + QAngle endA; + Studio_AnimPosition( panim, flCycleTo, endPos, endA ); + + Vector tmp = endPos - startPos; + deltaAngle.y = endA.y - startA.y; + VectorYawRotate( tmp, -startA.y, deltaPos ); + + return true; +} + + +//----------------------------------------------------------------------------- +// Purpose: finds how much of an animation to play to move given linear distance +//----------------------------------------------------------------------------- + +float Studio_FindAnimDistance( mstudioanimdesc_t *panim, float flDist ) +{ + float prevframe = 0; + + if (flDist <= 0) + return 0.0; + + for (int i = 0; i < panim->nummovements; i++) + { + mstudiomovement_t *pmove = panim->pMovement( i ); + + float flMove = (pmove->v0 + pmove->v1) * 0.5; + + if (flMove >= flDist) + { + float root1, root2; + + // d = V0 * t + 1/2 (V1-V0) * t^2 + if (SolveQuadratic( 0.5 * (pmove->v1 - pmove->v0), pmove->v0, -flDist, root1, root2 )) + { + float cpf = 1.0 / (panim->numframes - 1); // cycles per frame + + return (prevframe + root1 * (pmove->endframe - prevframe)) * cpf; + } + return 0.0; + } + else + { + flDist -= flMove; + prevframe = pmove->endframe; + } + } + return 1.0; +} + + +//----------------------------------------------------------------------------- +// Purpose: calculate changes in position and angle between two points in a sequences cycle +// Output: updated position and angle, relative to CycleFrom being at the origin +// returns false if sequence is not a movement sequence +//----------------------------------------------------------------------------- + +bool Studio_SeqMovement( const CStudioHdr *pStudioHdr, int iSequence, float flCycleFrom, float flCycleTo, const float poseParameter[], Vector &deltaPos, QAngle &deltaAngles ) +{ + mstudioanimdesc_t *panim[4]; + float weight[4]; + + mstudioseqdesc_t &seqdesc = pStudioHdr->pSeqdesc( iSequence ); + + Studio_SeqAnims( pStudioHdr, seqdesc, iSequence, poseParameter, panim, weight ); + + deltaPos.Init( ); + deltaAngles.Init( ); + + bool found = false; + + for (int i = 0; i < 4; i++) + { + if (weight[i]) + { + Vector localPos; + QAngle localAngles; + + localPos.Init(); + localAngles.Init(); + + if (Studio_AnimMovement( panim[i], flCycleFrom, flCycleTo, localPos, localAngles )) + { + found = true; + deltaPos = deltaPos + localPos * weight[i]; + // FIXME: this makes no sense + deltaAngles = deltaAngles + localAngles * weight[i]; + } + else if (!(panim[i]->flags & STUDIO_DELTA) && panim[i]->nummovements == 0 && seqdesc.weight(0) > 0.0) + { + found = true; + } + } + } + return found; +} + + +//----------------------------------------------------------------------------- +// Purpose: calculate instantaneous velocity in ips at a given point in the sequence's cycle +// Output: velocity vector, relative to identity orientation +// returns false if sequence is not a movement sequence +//----------------------------------------------------------------------------- + +bool Studio_SeqVelocity( const CStudioHdr *pStudioHdr, int iSequence, float flCycle, const float poseParameter[], Vector &vecVelocity ) +{ + mstudioanimdesc_t *panim[4]; + float weight[4]; + + mstudioseqdesc_t &seqdesc = pStudioHdr->pSeqdesc( iSequence ); + Studio_SeqAnims( pStudioHdr, seqdesc, iSequence, poseParameter, panim, weight ); + + vecVelocity.Init( ); + + bool found = false; + + for (int i = 0; i < 4; i++) + { + if (weight[i]) + { + Vector vecLocalVelocity; + + if (Studio_AnimVelocity( panim[i], flCycle, vecLocalVelocity )) + { + vecVelocity = vecVelocity + vecLocalVelocity * weight[i]; + found = true; + } + } + } + return found; +} + +//----------------------------------------------------------------------------- +// Purpose: finds how much of an sequence to play to move given linear distance +//----------------------------------------------------------------------------- + +float Studio_FindSeqDistance( const CStudioHdr *pStudioHdr, int iSequence, const float poseParameter[], float flDist ) +{ + mstudioanimdesc_t *panim[4]; + float weight[4]; + + mstudioseqdesc_t &seqdesc = pStudioHdr->pSeqdesc( iSequence ); + Studio_SeqAnims( pStudioHdr, seqdesc, iSequence, poseParameter, panim, weight ); + + float flCycle = 0; + + for (int i = 0; i < 4; i++) + { + if (weight[i]) + { + float flLocalCycle = Studio_FindAnimDistance( panim[i], flDist ); + flCycle = flCycle + flLocalCycle * weight[i]; + } + } + return flCycle; +} + +//----------------------------------------------------------------------------- +// Purpose: lookup attachment by name +//----------------------------------------------------------------------------- + +int Studio_FindAttachment( const CStudioHdr *pStudioHdr, const char *pAttachmentName ) +{ + if ( pStudioHdr && pStudioHdr->SequencesAvailable() ) + { + // Extract the bone index from the name + for (int i = 0; i < pStudioHdr->GetNumAttachments(); i++) + { + if (!stricmp(pAttachmentName,pStudioHdr->pAttachment(i).pszName( ))) + { + return i; + } + } + } + + return -1; +} + +//----------------------------------------------------------------------------- +// Purpose: lookup attachments by substring. Randomly return one of the matching attachments. +//----------------------------------------------------------------------------- + +int Studio_FindRandomAttachment( const CStudioHdr *pStudioHdr, const char *pAttachmentName ) +{ + if ( pStudioHdr ) + { + // First move them all matching attachments into a list + CUtlVector matchingAttachments; + + // Extract the bone index from the name + for (int i = 0; i < pStudioHdr->GetNumAttachments(); i++) + { + if ( strstr( pStudioHdr->pAttachment(i).pszName(), pAttachmentName ) ) + { + matchingAttachments.AddToTail(i); + } + } + + // Then randomly return one of the attachments + if ( matchingAttachments.Size() > 0 ) + return matchingAttachments[ RandomInt( 0, matchingAttachments.Size()-1 ) ]; + } + + return -1; +} + +//----------------------------------------------------------------------------- +// Purpose: lookup bone by name +//----------------------------------------------------------------------------- + +int Studio_BoneIndexByName( const CStudioHdr *pStudioHdr, const char *pName ) +{ + // binary search for the bone matching pName + int start = 0, end = pStudioHdr->numbones()-1; + const byte *pBoneTable = pStudioHdr->GetBoneTableSortedByName(); + mstudiobone_t *pbones = pStudioHdr->pBone( 0 ); + while (start <= end) + { + int mid = (start + end) >> 1; + int cmp = Q_stricmp( pbones[pBoneTable[mid]].pszName(), pName ); + + if ( cmp < 0 ) + { + start = mid + 1; + } + else if ( cmp > 0 ) + { + end = mid - 1; + } + else + { + return pBoneTable[mid]; + } + } + return -1; +} + +const char *Studio_GetDefaultSurfaceProps( CStudioHdr *pstudiohdr ) +{ + return pstudiohdr->pszSurfaceProp(); +} + +float Studio_GetMass( CStudioHdr *pstudiohdr ) +{ + return pstudiohdr->mass(); +} + +//----------------------------------------------------------------------------- +// Purpose: return pointer to sequence key value buffer +//----------------------------------------------------------------------------- + +const char *Studio_GetKeyValueText( const CStudioHdr *pStudioHdr, int iSequence ) +{ + if (pStudioHdr && pStudioHdr->SequencesAvailable()) + { + if (iSequence >= 0 && iSequence < pStudioHdr->GetNumSeq()) + { + return pStudioHdr->pSeqdesc( iSequence ).KeyValueText(); + } + } + return NULL; +} + +bool Studio_PrefetchSequence( const CStudioHdr *pStudioHdr, int iSequence ) +{ + bool pendingload = false; + mstudioseqdesc_t &seqdesc = pStudioHdr->pSeqdesc( iSequence ); + int size0 = seqdesc.groupsize[ 0 ]; + int size1 = seqdesc.groupsize[ 1 ]; + for ( int i = 0; i < size0; ++i ) + { + for ( int j = 0; j < size1; ++j ) + { + mstudioanimdesc_t &animdesc = pStudioHdr->pAnimdesc( seqdesc.anim( i, j ) ); + mstudioanim_t *panim = animdesc.pAnim(); + if ( !panim ) + { + pendingload = true; + } + } + } + + // Everything for this sequence is resident? + return !pendingload; +} diff --git a/public/bspfile.h b/public/bspfile.h index c1b68580..92e2f188 100644 --- a/public/bspfile.h +++ b/public/bspfile.h @@ -916,7 +916,7 @@ public: inline void doverlay_t::SetFaceCount( unsigned short count ) { - Assert( count >= 0 && (count & OVERLAY_RENDER_ORDER_MASK) == 0 ); + Assert( (count & OVERLAY_RENDER_ORDER_MASK) == 0 ); m_nFaceCountAndRenderOrder &= OVERLAY_RENDER_ORDER_MASK; m_nFaceCountAndRenderOrder |= (count & ~OVERLAY_RENDER_ORDER_MASK); } @@ -928,7 +928,7 @@ inline unsigned short doverlay_t::GetFaceCount() const inline void doverlay_t::SetRenderOrder( unsigned short order ) { - Assert( order >= 0 && order < OVERLAY_NUM_RENDER_ORDERS ); + Assert( order < OVERLAY_NUM_RENDER_ORDERS ); m_nFaceCountAndRenderOrder &= ~OVERLAY_RENDER_ORDER_MASK; m_nFaceCountAndRenderOrder |= (order << (16 - OVERLAY_RENDER_ORDER_NUM_BITS)); // leave 2 bits for render order. } @@ -969,7 +969,7 @@ public: inline void dwateroverlay_t::SetFaceCount( unsigned short count ) { - Assert( count >= 0 && (count & WATEROVERLAY_RENDER_ORDER_MASK) == 0 ); + Assert((count & WATEROVERLAY_RENDER_ORDER_MASK) == 0 ); m_nFaceCountAndRenderOrder &= WATEROVERLAY_RENDER_ORDER_MASK; m_nFaceCountAndRenderOrder |= (count & ~WATEROVERLAY_RENDER_ORDER_MASK); } @@ -981,7 +981,7 @@ inline unsigned short dwateroverlay_t::GetFaceCount() const inline void dwateroverlay_t::SetRenderOrder( unsigned short order ) { - Assert( order >= 0 && order < WATEROVERLAY_NUM_RENDER_ORDERS ); + Assert( order < WATEROVERLAY_NUM_RENDER_ORDERS ); m_nFaceCountAndRenderOrder &= ~WATEROVERLAY_RENDER_ORDER_MASK; m_nFaceCountAndRenderOrder |= ( order << ( 16 - WATEROVERLAY_RENDER_ORDER_NUM_BITS ) ); // leave 2 bits for render order. } diff --git a/public/builddisp.cpp b/public/builddisp.cpp index caac04f7..8f3fe2f9 100644 --- a/public/builddisp.cpp +++ b/public/builddisp.cpp @@ -1,2995 +1,2998 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $Workfile: $ -// $Date: $ -// $NoKeywords: $ -//=============================================================================// - -//#include -#include -#include -#include "builddisp.h" -#include "collisionutils.h" -#include "vstdlib/strtools.h" -#include "tier0/dbg.h" - -// memdbgon must be the last include file in a .cpp file!!! -#include "tier0/memdbgon.h" - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -bool CalcBarycentricCooefs( Vector const &v0, Vector const &v1, Vector const &v2, - Vector const &pt, float &c0, float &c1, float &c2 ) -{ - Vector vSeg0, vSeg1, vCross; - vSeg0 = v1 - v0; - vSeg1 = v2 - v0; - - // get the area of the triangle - vCross = vSeg0.Cross( vSeg1 ); - float totalArea = vCross.Length() * 0.5f; - float ooTotalArea = totalArea ? 1.0f / totalArea : 0.0f; - - // get the area for cooeficient 0 (pt, v1, v2) - vSeg0 = v1 - pt; - vSeg1 = v2 - pt; - vCross = vSeg0.Cross( vSeg1 ); - float subArea = vCross.Length() * 0.5f; - c0 = subArea * ooTotalArea; - - // get the area for cooeficient 1 (v0, pt, v2) - vSeg0 = v2 - pt; - vSeg1 = v0 - pt; - vCross = vSeg0.Cross( vSeg1 ); - subArea = vCross.Length() * 0.5f; - c1 = subArea * ooTotalArea; - - // get the area for cooeficient 2 (v0, v1, pt) - vSeg0 = v0 - pt; - vSeg1 = v1 - pt; - vCross = vSeg0.Cross( vSeg1 ); - subArea = vCross.Length() * 0.5f; - c2 = subArea * ooTotalArea; - - float cTotal = c0 + c1 + c2; - if ( FloatMakePositive( 1.0f - cTotal ) < 1e-3 ) - return true; - - return false; -} - -// For some reason, the global optimizer screws up the recursion here. disable the global optimizations to fix this. -// IN VC++ 6.0 -#pragma optimize( "g", off ) - -CCoreDispSurface::CCoreDispSurface() -{ - Init(); -} - - -//============================================================================= -// -// CDispSurface Functions -// - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -void CCoreDispSurface::Init( void ) -{ - m_Index = -1; - - m_PointCount = 0; - int i; - for( i = 0; i < QUAD_POINT_COUNT; i++ ) - { - VectorClear( m_Points[i] ); - VectorClear( m_Normals[i] ); - Vector2DClear( m_TexCoords[i] ); - - for( int j = 0; j < NUM_BUMP_VECTS+1; j++ ) - { - Vector2DClear( m_LuxelCoords[i][j] ); - } - - m_Alphas[i] = 1.0f; - } - - m_PointStartIndex = -1; - VectorClear( m_PointStart ); - VectorClear( sAxis ); - VectorClear( tAxis ); - - for( i = 0; i < 4; i++ ) - { - m_EdgeNeighbors[i].SetInvalid(); - m_CornerNeighbors[i].SetInvalid(); - } - - m_Flags = 0; - m_Contents = 0; -} - - -void CCoreDispSurface::SetNeighborData( const CDispNeighbor edgeNeighbors[4], const CDispCornerNeighbors cornerNeighbors[4] ) -{ - for ( int i=0; i < 4; i++ ) - { - m_EdgeNeighbors[i] = edgeNeighbors[i]; - m_CornerNeighbors[i] = cornerNeighbors[i]; - } -} - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -void CCoreDispSurface::GeneratePointStartIndexFromMappingAxes( Vector const &sAxis, Vector const &tAxis ) -{ - if( m_PointStartIndex != -1 ) - return; - - int numIndices = 0; - int indices[4]; - int offsetIndex; - - // - // project all points on to the v-axis first and find the minimum - // - float minValue = DotProduct( tAxis, m_Points[0] ); - indices[numIndices] = 0; - numIndices++; - - int i; - for( i = 1; i < m_PointCount; i++ ) - { - float value = DotProduct( tAxis, m_Points[i] ); - float delta = ( value - minValue ); - delta = FloatMakePositive( delta ); - if( delta < 0.1 ) - { - indices[numIndices] = i; - numIndices++; - } - else if( value < minValue ) - { - minValue = value; - indices[0] = i; - numIndices = 1; - } - } - - // - // break ties with the u-axis projection - // - minValue = DotProduct( sAxis, m_Points[indices[0]] ); - offsetIndex = indices[0]; - - for( i = 1; i < numIndices; i++ ) - { - float value = DotProduct( sAxis, m_Points[indices[i]] ); - if( ( value < minValue ) ) - { - minValue = value; - offsetIndex = indices[i]; - } - } - - m_PointStartIndex = offsetIndex; -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -int CCoreDispSurface::GenerateSurfPointStartIndex( void ) -{ - // - // get the minimum surface component values - // - Vector bMin; - VectorFill( bMin, 99999.0f ); - - int i; - for( i = 0; i < QUAD_POINT_COUNT; i++ ) - { - for( int j = 0; j < 3; j++ ) - { - if( m_Points[i][j] < bMin[j] ) - { - bMin[j] = m_Points[i][j]; - } - } - } - - // - // find the point closest to the minimum, that is the start point - // - int minIndex = -1; - float minDistance = 999999999.0f; - for( i = 0; i < QUAD_POINT_COUNT; i++ ) - { - Vector segment; - segment = m_Points[i] - bMin; - float distanceSq = segment.LengthSqr(); - if( distanceSq < minDistance ) - { - minDistance = distanceSq; - minIndex = i; - } - } - - m_PointStartIndex = minIndex; - - return minIndex; -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -int CCoreDispSurface::FindSurfPointStartIndex( void ) -{ - if( m_PointStartIndex != -1 ) - return m_PointStartIndex; - - int minIndex = -1; - float minDistance = 999999999.0f; - - for( int i = 0; i < QUAD_POINT_COUNT; i++ ) - { - Vector segment; - VectorSubtract( m_PointStart, m_Points[i], segment ); - float distanceSq = segment.LengthSqr(); - if( distanceSq < minDistance ) - { - minDistance = distanceSq; - minIndex = i; - } - } - - m_PointStartIndex = minIndex; - - return minIndex; -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -void CCoreDispSurface::AdjustSurfPointData( void ) -{ - Vector tmpPoints[4]; - Vector tmpNormals[4]; - Vector2D tmpTexCoords[4]; - Vector2D tmpLuxelCoords[4][4]; - float tmpAlphas[4]; - - int i; - for( i = 0; i < QUAD_POINT_COUNT; i++ ) - { - VectorCopy( m_Points[i], tmpPoints[i] ); - VectorCopy( m_Normals[i], tmpNormals[i] ); - Vector2DCopy( m_TexCoords[i], tmpTexCoords[i] ); - - for( int j = 0; j < ( NUM_BUMP_VECTS + 1 ); j++ ) - { - Vector2DCopy( m_LuxelCoords[j][i], tmpLuxelCoords[j][i] ); - } - - tmpAlphas[i] = m_Alphas[i]; - } - - for( i = 0; i < QUAD_POINT_COUNT; i++ ) - { - VectorCopy( tmpPoints[(i+m_PointStartIndex)%4], m_Points[i] ); - VectorCopy( tmpNormals[(i+m_PointStartIndex)%4], m_Normals[i] ); - Vector2DCopy( tmpTexCoords[(i+m_PointStartIndex)%4], m_TexCoords[i] ); - -// for( int j = 0; j < ( NUM_BUMP_VECTS + 1 ); j++ ) -// { -// Vector2DCopy( tmpLuxelCoords[j][(i+m_PointStartIndex)%4], m_LuxelCoords[j][i] ); -// } - - m_Alphas[i] = tmpAlphas[i]; - } -} - - -//============================================================================= -// -// CDispNode Functions -// - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -void CCoreDispNode::Init( void ) -{ - VectorClear( m_BBox[0] ); - VectorClear( m_BBox[1] ); - - m_ErrorTerm = 0.0f; - - m_VertIndex = -1; - - int j; - for( j = 0; j < MAX_NEIGHBOR_NODE_COUNT; j++ ) - { - m_NeighborVertIndices[j] = -1; - } - - for( j = 0; j < MAX_SURF_AT_NODE_COUNT; j++ ) - { - VectorClear( m_SurfBBoxes[j][0] ); - VectorClear( m_SurfBBoxes[j][1] ); - VectorClear( m_SurfPlanes[j].normal ); - m_SurfPlanes[j].dist = 0.0f; - } -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -void GetDispNodeTriVerts( CCoreDispInfo *pDisp, int nodeIndex, int triIndex, Vector& v1, Vector& v2, Vector& v3 ) -{ - // get the node - CCoreDispNode *pNode = pDisp->GetNode( nodeIndex ); - - switch( triIndex ) - { - case 0: - { - pDisp->GetVert( pNode->GetNeighborVertIndex( 4 ), v1 ); - pDisp->GetVert( pNode->GetNeighborVertIndex( 0 ), v2 ); - pDisp->GetVert( pNode->GetNeighborVertIndex( 3 ), v3 ); - return; - } - case 1: - { - pDisp->GetVert( pNode->GetNeighborVertIndex( 3 ), v1 ); - pDisp->GetVert( pNode->GetNeighborVertIndex( 0 ), v2 ); - pDisp->GetVert( pNode->GetCenterVertIndex(), v3 ); - return; - } - case 2: - { - pDisp->GetVert( pNode->GetNeighborVertIndex( 3 ), v1 ); - pDisp->GetVert( pNode->GetCenterVertIndex(), v2 ); - pDisp->GetVert( pNode->GetNeighborVertIndex( 5 ), v3 ); - return; - } - case 3: - { - pDisp->GetVert( pNode->GetNeighborVertIndex( 5 ), v1 ); - pDisp->GetVert( pNode->GetCenterVertIndex(), v2 ); - pDisp->GetVert( pNode->GetNeighborVertIndex( 2 ), v3 ); - return; - } - case 4: - { - pDisp->GetVert( pNode->GetNeighborVertIndex( 0 ), v1 ); - pDisp->GetVert( pNode->GetNeighborVertIndex( 6 ), v2 ); - pDisp->GetVert( pNode->GetCenterVertIndex(), v3 ); - return; - } - case 5: - { - pDisp->GetVert( pNode->GetCenterVertIndex(), v1 ); - pDisp->GetVert( pNode->GetNeighborVertIndex( 6 ), v2 ); - pDisp->GetVert( pNode->GetNeighborVertIndex( 1 ), v3 ); - return; - } - case 6: - { - pDisp->GetVert( pNode->GetCenterVertIndex(), v1 ); - pDisp->GetVert( pNode->GetNeighborVertIndex( 1 ), v2 ); - pDisp->GetVert( pNode->GetNeighborVertIndex( 2 ), v3 ); - return; - } - case 7: - { - pDisp->GetVert( pNode->GetNeighborVertIndex( 2 ), v1 ); - pDisp->GetVert( pNode->GetNeighborVertIndex( 1 ), v2 ); - pDisp->GetVert( pNode->GetNeighborVertIndex( 7 ), v3 ); - return; - } - default: { return; } - } -} - - -//============================================================================= -// -// CCoreDispInfo Functions -// - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -CCoreDispInfo::CCoreDispInfo() -{ - m_pVerts = NULL; - m_RenderIndices = NULL; - m_Nodes = NULL; - m_pTris = NULL; - - // initialize the base surface data - m_Surf.Init(); - - // - // initialize the disp info - // - m_Power = 0; - m_Elevation = 0.0f; - m_RenderIndexCount = 0; - m_RenderCounter = 0; - m_bTouched = false; - - m_pNext = NULL; - - m_ppListBase = NULL; - m_ListSize = 0; - m_nListIndex = -1; -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -CCoreDispInfo::~CCoreDispInfo() -{ - if (m_pVerts) - delete [] m_pVerts; - if (m_RenderIndices) - delete [] m_RenderIndices; - if (m_Nodes) - delete [] m_Nodes; - if (m_pTris) - delete [] m_pTris; -} - - -#if 0 -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -void CCoreDispInfo::InitSurf( int parentIndex, Vector points[4], Vector normals[4], - Vector2D texCoords[4], Vector2D lightCoords[4][4], int contents, int flags, - bool bGenerateSurfPointStart, Vector& startPoint, - bool bHasMappingAxes, Vector& uAxis, Vector& vAxis ) -{ - // save the "parent" index - m_Surf.m_Index = parentIndex; - - // - // save the surface points and point normals, texture coordinates, and - // lightmap coordinates - // - m_Surf.m_PointCount = CSurface::QUAD_POINT_COUNT; - for( int i = 0; i < CSurface::QUAD_POINT_COUNT; i++ ) - { - VectorCopy( points[i], m_Surf.m_Points[i] ); - - if( normals ) - { - VectorCopy( normals[i], m_Surf.m_pVerts[i].m_Normal ); - } - - if( texCoords ) - { - Vector2DCopy( texCoords[i], m_Surf.m_TexCoords[i] ); - } - - if( lightCoords ) - { - Assert( NUM_BUMP_VECTS == 3 ); - Vector2DCopy( lightCoords[0][i], m_Surf.m_LightCoords[i][0] ); - Vector2DCopy( lightCoords[1][i], m_Surf.m_LightCoords[i][1] ); - Vector2DCopy( lightCoords[2][i], m_Surf.m_LightCoords[i][2] ); - Vector2DCopy( lightCoords[3][i], m_Surf.m_LightCoords[i][3] ); - } - } - - // save the starting point - if( startPoint ) - { - VectorCopy( startPoint, m_Surf.m_PointStart ); - } - - // - // save the surface contents and flags - // - m_Contents = contents; - m_Flags = flags; - - // - // adjust surface points, texture coordinates, etc.... - // - if( bHasMappingAxes && ( m_Surf.m_PointStartIndex == -1 ) ) - { - GeneratePointStartIndexFromMappingAxes( uAxis, vAxis ); - } - else - { - // - // adjust the surf data - // - if( bGenerateSurfPointStart ) - { - GenerateSurfPointStartIndex(); - } - else - { - FindSurfPointStartIndex(); - } - } - - AdjustSurfPointData(); -} -#endif - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -void CCoreDispInfo::InitDispInfo( int power, int minTess, float smoothingAngle, - float *alphas, Vector *dispVectorField, float *dispDistances ) -{ - Assert( power >= MIN_MAP_DISP_POWER && power <= MAX_MAP_DISP_POWER ); - - // - // general displacement data - // - m_Power = power; - - if ( ( minTess & 0x80000000 ) != 0 ) - { - // If the high bit is set, this represents FLAGS (SURF_NOPHYSICS_COLL, etc.) flags. - int nFlags = minTess; - nFlags &= ~0x80000000; - GetSurface()->SetFlags( nFlags ); - } - - // Allocate + initialize verts - int size = GetSize(); - m_pVerts = new CoreDispVert_t[size]; - - int nIndexCount = size * 2 * 3; - m_RenderIndices = new unsigned short[nIndexCount]; - - int nNodeCount = GetNodeCount(power); - m_Nodes = new CCoreDispNode[nNodeCount]; - - int i; - for( i = 0; i < size; i++ ) - { - m_pVerts[i].m_FieldVector.Init(); - m_pVerts[i].m_SubdivPos.Init(); - m_pVerts[i].m_SubdivNormal.Init(); - - m_pVerts[i].m_FieldDistance = 0.0f; - - m_pVerts[i].m_Vert.Init(); - m_pVerts[i].m_FlatVert.Init(); - m_pVerts[i].m_Normal.Init(); - m_pVerts[i].m_TangentS.Init(); - m_pVerts[i].m_TangentT.Init(); - m_pVerts[i].m_TexCoord.Init(); - - for( int j = 0; j < ( NUM_BUMP_VECTS + 1 ); j++ ) - { - m_pVerts[i].m_LuxelCoords[j].Init(); - } - - m_pVerts[i].m_Alpha = 0.0f; - } - - for( i = 0; i < nIndexCount; i++ ) - { - m_RenderIndices[i] = 0; - } - - for( i = 0; i < nNodeCount; i++ ) - { - m_Nodes[i].Init(); - } - - // - // save the displacement vector field and distances within the field - // offset have been combined with fieldvectors at this point!!! - // - if (alphas && dispVectorField && dispDistances) - { - for( i = 0; i < size; i++ ) - { - VectorCopy( dispVectorField[i], m_pVerts[i].m_FieldVector ); - m_pVerts[i].m_FieldDistance = dispDistances[i]; - m_pVerts[i].m_Alpha = alphas[i]; - } - } - - // Init triangle information. - int nTriCount = GetTriCount(); - if ( nTriCount != 0 ) - { - m_pTris = new CoreDispTri_t[nTriCount]; - if ( m_pTris ) - { - InitTris(); - } - } -} - - -void CCoreDispInfo::InitDispInfo( int power, int minTess, float smoothingAngle, const CDispVert *pVerts, - const CDispTri *pTris ) -{ - Vector vectors[MAX_DISPVERTS]; - float dists[MAX_DISPVERTS]; - float alphas[MAX_DISPVERTS]; - - int nVerts = NUM_DISP_POWER_VERTS( power ); - for ( int i=0; i < nVerts; i++ ) - { - vectors[i] = pVerts[i].m_vVector; - dists[i] = pVerts[i].m_flDist; - alphas[i] = pVerts[i].m_flAlpha; - } - - InitDispInfo( power, minTess, smoothingAngle, alphas, vectors, dists ); - - int nTris = NUM_DISP_POWER_TRIS( power ); - for ( int iTri = 0; iTri < nTris; ++iTri ) - { - m_pTris[iTri].m_uiTags = pTris[iTri].m_uiTags; - } -} - - -void CCoreDispInfo::SetDispUtilsHelperInfo( CCoreDispInfo **ppListBase, int listSize ) -{ - m_ppListBase = ppListBase; - m_ListSize = listSize; -} - -const CPowerInfo* CCoreDispInfo::GetPowerInfo() const -{ - return ::GetPowerInfo( GetPower() ); -} - -CDispNeighbor* CCoreDispInfo::GetEdgeNeighbor( int index ) -{ - return GetSurface()->GetEdgeNeighbor( index ); -} - -CDispCornerNeighbors* CCoreDispInfo::GetCornerNeighbors( int index ) -{ - return GetSurface()->GetCornerNeighbors( index ); -} - -CDispUtilsHelper* CCoreDispInfo::GetDispUtilsByIndex( int index ) -{ - Assert( m_ppListBase ); - return index == 0xFFFF ? 0 : m_ppListBase[index]; -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -void CCoreDispInfo::BuildTriTLtoBR( int ndx ) -{ - // get width and height of displacement maps - int nWidth = ( ( 1 << m_Power ) + 1 ); - - m_RenderIndices[m_RenderIndexCount] = ndx; - m_RenderIndices[m_RenderIndexCount+1] = ndx + nWidth; - m_RenderIndices[m_RenderIndexCount+2] = ndx + 1; - m_RenderIndexCount += 3; - - m_RenderIndices[m_RenderIndexCount] = ndx + 1; - m_RenderIndices[m_RenderIndexCount+1] = ndx + nWidth; - m_RenderIndices[m_RenderIndexCount+2] = ndx + nWidth + 1; - m_RenderIndexCount += 3; -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -void CCoreDispInfo::BuildTriBLtoTR( int ndx ) -{ - // get width and height of displacement maps - int nWidth = ( ( 1 << m_Power ) + 1 ); - - m_RenderIndices[m_RenderIndexCount] = ndx; - m_RenderIndices[m_RenderIndexCount+1] = ndx + nWidth; - m_RenderIndices[m_RenderIndexCount+2] = ndx + nWidth + 1; - m_RenderIndexCount += 3; - - m_RenderIndices[m_RenderIndexCount] = ndx; - m_RenderIndices[m_RenderIndexCount+1] = ndx + nWidth + 1; - m_RenderIndices[m_RenderIndexCount+2] = ndx + 1; - m_RenderIndexCount += 3; -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -void CCoreDispInfo::GenerateCollisionSurface( void ) -{ - // get width and height of displacement maps - int nWidth = ( ( 1 << m_Power ) + 1 ); - int nHeight = ( ( 1 << m_Power ) + 1 ); - - // - // generate a fan tesselated (at quadtree node) rendering index list - // - m_RenderIndexCount = 0; - for ( int iV = 0; iV < ( nHeight - 1 ); iV++ ) - { - for ( int iU = 0; iU < ( nWidth - 1 ); iU++ ) - { - int ndx = ( iV * nWidth ) + iU; - - // test whether or not the index is odd - bool bOdd = ( ( ndx %2 ) == 1 ); - - // Top Left to Bottom Right - if( bOdd ) - { - BuildTriTLtoBR( ndx ); - } - // Bottom Left to Top Right - else - { - BuildTriBLtoTR( ndx ); - } - } - } -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -void CCoreDispInfo::GenerateCollisionData( void ) -{ - GenerateCollisionSurface(); -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -void CCoreDispInfo::CalcTriSurfPlanes( int nodeIndex, int indices[8][3] ) -{ - // - // calculate plane info for each face - // - for( int i = 0; i < 8; i++ ) - { - Vector v[3]; - VectorCopy( m_pVerts[indices[i][0]].m_Vert, v[0] ); - VectorCopy( m_pVerts[indices[i][1]].m_Vert, v[1] ); - VectorCopy( m_pVerts[indices[i][2]].m_Vert, v[2] ); - - Vector seg[2]; - VectorSubtract( v[1], v[0], seg[0] ); - VectorSubtract( v[2], v[0], seg[1] ); - - Vector normal; - CrossProduct( seg[1], seg[0], normal ); - VectorNormalize( normal ); - float dist = DotProduct( v[0], normal ); - - m_Nodes[nodeIndex].SetTriPlane( i, normal, dist ); - } -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -void CCoreDispInfo::CalcRayBoundingBoxes( int nodeIndex, int indices[8][3] ) -{ - Vector triMin, triMax; - - for( int i = 0; i < 4; i++ ) - { - triMin[0] = triMax[0] = m_pVerts[indices[(i*2)][0]].m_Vert[0]; - triMin[1] = triMax[1] = m_pVerts[indices[(i*2)][0]].m_Vert[1]; - triMin[2] = triMax[2] = m_pVerts[indices[(i*2)][0]].m_Vert[2]; - - for( int j = 0; j < 3; j++ ) - { - // - // minimum - // - if( triMin[0] > m_pVerts[indices[(i*2)][j]].m_Vert[0] ) - triMin[0] = m_pVerts[indices[(i*2)][j]].m_Vert[0]; - if( triMin[0] > m_pVerts[indices[(i*2+1)][j]].m_Vert[0] ) - triMin[0] = m_pVerts[indices[(i*2+1)][j]].m_Vert[0]; - - if( triMin[1] > m_pVerts[indices[(i*2)][j]].m_Vert[1] ) - triMin[1] = m_pVerts[indices[(i*2)][j]].m_Vert[1]; - if( triMin[1] > m_pVerts[indices[(i*2+1)][j]].m_Vert[1] ) - triMin[1] = m_pVerts[indices[(i*2+1)][j]].m_Vert[1]; - - if( triMin[2] > m_pVerts[indices[(i*2)][j]].m_Vert[2] ) - triMin[2] = m_pVerts[indices[(i*2)][j]].m_Vert[2]; - if( triMin[2] > m_pVerts[indices[(i*2+1)][j]].m_Vert[2] ) - triMin[2] = m_pVerts[indices[(i*2+1)][j]].m_Vert[2]; - - // - // maximum - // - if( triMax[0] < m_pVerts[indices[(i*2)][j]].m_Vert[0] ) - triMax[0] = m_pVerts[indices[(i*2)][j]].m_Vert[0]; - if( triMax[0] < m_pVerts[indices[(i*2+1)][j]].m_Vert[0] ) - triMax[0] = m_pVerts[indices[(i*2+1)][j]].m_Vert[0]; - - if( triMax[1] < m_pVerts[indices[(i*2)][j]].m_Vert[1] ) - triMax[1] = m_pVerts[indices[(i*2)][j]].m_Vert[1]; - if( triMax[1] < m_pVerts[indices[(i*2+1)][j]].m_Vert[1] ) - triMax[1] = m_pVerts[indices[(i*2+1)][j]].m_Vert[1]; - - if( triMax[2] < m_pVerts[indices[(i*2)][j]].m_Vert[2] ) - triMax[2] = m_pVerts[indices[(i*2)][j]].m_Vert[2]; - if( triMax[2] < m_pVerts[indices[(i*2+1)][j]].m_Vert[2] ) - triMax[2] = m_pVerts[indices[(i*2+1)][j]].m_Vert[2]; - } - - m_Nodes[nodeIndex].SetRayBoundingBox( i, triMin, triMax ); - } -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -void CCoreDispInfo::CalcTriSurfBoundingBoxes( int nodeIndex, int indices[8][3] ) -{ - Vector triMin, triMax; - - for( int i = 0; i < 8; i++ ) - { - m_Nodes[nodeIndex].GetTriBoundingBox( i, triMin, triMax ); - - for( int j = 0; j < 3; j++ ) - { - // - // minimum - // - if( triMin[0] > m_pVerts[indices[i][j]].m_Vert[0] ) - triMin[0] = m_pVerts[indices[i][j]].m_Vert[0]; - - if( triMin[1] > m_pVerts[indices[i][j]].m_Vert[1] ) - triMin[1] = m_pVerts[indices[i][j]].m_Vert[1]; - - if( triMin[2] > m_pVerts[indices[i][j]].m_Vert[2] ) - triMin[2] = m_pVerts[indices[i][j]].m_Vert[2]; - - // - // maximum - // - if( triMax[0] < m_pVerts[indices[i][j]].m_Vert[0] ) - triMax[0] = m_pVerts[indices[i][j]].m_Vert[0]; - - if( triMax[1] < m_pVerts[indices[i][j]].m_Vert[1] ) - triMax[1] = m_pVerts[indices[i][j]].m_Vert[1]; - - if( triMax[2] < m_pVerts[indices[i][j]].m_Vert[2] ) - triMax[2] = m_pVerts[indices[i][j]].m_Vert[2]; - } - - m_Nodes[nodeIndex].SetTriBoundingBox( i, triMin, triMax ); - } -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -void CCoreDispInfo::CalcTriSurfIndices( int nodeIndex, int indices[8][3] ) -{ - indices[0][0] = m_Nodes[nodeIndex].GetNeighborVertIndex( 4 ); - indices[0][1] = m_Nodes[nodeIndex].GetNeighborVertIndex( 0 ); - indices[0][2] = m_Nodes[nodeIndex].GetNeighborVertIndex( 3 ); - - indices[1][0] = m_Nodes[nodeIndex].GetNeighborVertIndex( 3 ); - indices[1][1] = m_Nodes[nodeIndex].GetNeighborVertIndex( 0 ); - indices[1][2] = m_Nodes[nodeIndex].GetCenterVertIndex(); - - indices[2][0] = m_Nodes[nodeIndex].GetNeighborVertIndex( 3 ); - indices[2][1] = m_Nodes[nodeIndex].GetCenterVertIndex(); - indices[2][2] = m_Nodes[nodeIndex].GetNeighborVertIndex( 5 ); - - indices[3][0] = m_Nodes[nodeIndex].GetNeighborVertIndex( 5 ); - indices[3][1] = m_Nodes[nodeIndex].GetCenterVertIndex(); - indices[3][2] = m_Nodes[nodeIndex].GetNeighborVertIndex( 2 ); - - indices[4][0] = m_Nodes[nodeIndex].GetNeighborVertIndex( 0 ); - indices[4][1] = m_Nodes[nodeIndex].GetNeighborVertIndex( 6 ); - indices[4][2] = m_Nodes[nodeIndex].GetCenterVertIndex(); - - indices[5][0] = m_Nodes[nodeIndex].GetCenterVertIndex(); - indices[5][1] = m_Nodes[nodeIndex].GetNeighborVertIndex( 6 ); - indices[5][2] = m_Nodes[nodeIndex].GetNeighborVertIndex( 1 ); - - indices[6][0] = m_Nodes[nodeIndex].GetCenterVertIndex(); - indices[6][1] = m_Nodes[nodeIndex].GetNeighborVertIndex( 1 ); - indices[6][2] = m_Nodes[nodeIndex].GetNeighborVertIndex( 2 ); - - indices[7][0] = m_Nodes[nodeIndex].GetNeighborVertIndex( 2 ); - indices[7][1] = m_Nodes[nodeIndex].GetNeighborVertIndex( 1 ); - indices[7][2] = m_Nodes[nodeIndex].GetNeighborVertIndex( 7 ); -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -void CCoreDispInfo::CalcTriSurfInfoAtNode( int nodeIndex ) -{ - int indices[8][3]; - - CalcTriSurfIndices( nodeIndex, indices ); - CalcTriSurfBoundingBoxes( nodeIndex, indices ); - CalcRayBoundingBoxes( nodeIndex, indices ); - CalcTriSurfPlanes( nodeIndex, indices ); -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -void CCoreDispInfo::CalcMinMaxBoundingBoxAtNode( int nodeIndex, Vector& bMin, Vector& bMax ) -{ - // get the child node index - int childNodeIndex = GetNodeChild( m_Power, nodeIndex, 4 ); - - // get initial bounding box values - m_Nodes[childNodeIndex].GetBoundingBox( bMin, bMax ); - - Vector nodeMin, nodeMax; - for( int i = 1, j = 5; i < 4; i++, j++ ) - { - // - // get the child node bounding box - // - childNodeIndex = GetNodeChild( m_Power, nodeIndex, j ); - m_Nodes[childNodeIndex].GetBoundingBox( nodeMin, nodeMax ); - - // minimum - if( bMin[0] > nodeMin[0] ) - bMin[0] = nodeMin[0]; - - if( bMin[1] > nodeMin[1] ) - bMin[1] = nodeMin[1]; - - if( bMin[2] > nodeMin[2] ) - bMin[2] = nodeMin[2]; - - // maximum - if( bMax[0] < nodeMax[0] ) - bMax[0] = nodeMax[0]; - - if( bMax[1] < nodeMax[1] ) - bMax[1] = nodeMax[1]; - - if( bMax[2] < nodeMax[2] ) - bMax[2] = nodeMax[2]; - } -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -void CCoreDispInfo::CalcBoundingBoxAtNode( int nodeIndex ) -{ - Vector bMin, bMax; - - // - // initialize the minimum and maximum values for the bounding box - // - int level = GetNodeLevel( nodeIndex ); - - int vertIndex = m_Nodes[nodeIndex].GetCenterVertIndex(); - if( level == m_Power ) - { - VectorCopy( m_pVerts[vertIndex].m_Vert, bMin ); - VectorCopy( m_pVerts[vertIndex].m_Vert, bMax ); - } - else - { - CalcMinMaxBoundingBoxAtNode( nodeIndex, bMin, bMax ); - - if( bMin[0] > m_pVerts[vertIndex].m_Vert[0] ) - bMin[0] = m_pVerts[vertIndex].m_Vert[0]; - - if( bMin[1] > m_pVerts[vertIndex].m_Vert[1] ) - bMin[1] = m_pVerts[vertIndex].m_Vert[1]; - - if( bMin[2] > m_pVerts[vertIndex].m_Vert[2] ) - bMin[2] = m_pVerts[vertIndex].m_Vert[2]; - - - if( bMax[0] < m_pVerts[vertIndex].m_Vert[0] ) - bMax[0] = m_pVerts[vertIndex].m_Vert[0]; - - if( bMax[1] < m_pVerts[vertIndex].m_Vert[1] ) - bMax[1] = m_pVerts[vertIndex].m_Vert[1]; - - if( bMax[2] < m_pVerts[vertIndex].m_Vert[2] ) - bMax[2] = m_pVerts[vertIndex].m_Vert[2]; - } - - for( int i = 0; i < 8; i++ ) - { - int neighborVertIndex = m_Nodes[nodeIndex].GetNeighborVertIndex( i ); - - // - // minimum - // - if( bMin[0] > m_pVerts[neighborVertIndex].m_Vert[0] ) - bMin[0] = m_pVerts[neighborVertIndex].m_Vert[0]; - - if( bMin[1] > m_pVerts[neighborVertIndex].m_Vert[1] ) - bMin[1] = m_pVerts[neighborVertIndex].m_Vert[1]; - - if( bMin[2] > m_pVerts[neighborVertIndex].m_Vert[2] ) - bMin[2] = m_pVerts[neighborVertIndex].m_Vert[2]; - - // - // maximum - // - if( bMax[0] < m_pVerts[neighborVertIndex].m_Vert[0] ) - bMax[0] = m_pVerts[neighborVertIndex].m_Vert[0]; - - if( bMax[1] < m_pVerts[neighborVertIndex].m_Vert[1] ) - bMax[1] = m_pVerts[neighborVertIndex].m_Vert[1]; - - if( bMax[2] < m_pVerts[neighborVertIndex].m_Vert[2] ) - bMax[2] = m_pVerts[neighborVertIndex].m_Vert[2]; - } - - m_Nodes[nodeIndex].SetBoundingBox( bMin, bMax ); -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -float CCoreDispInfo::GetMaxErrorFromChildren( int nodeIndex, int level ) -{ - // - // check for children nodes - // - if( level == m_Power ) - return 0.0f; - - // - // get the child's error term and save the greatest error -- SW, SE, NW, NE - // - float errorTerm = 0.0f; - for( int i = 4; i < 8; i++ ) - { - int childIndex = GetNodeChild( m_Power, nodeIndex, i ); - - float nodeErrorTerm = m_Nodes[childIndex].GetErrorTerm(); - if( errorTerm < nodeErrorTerm ) - { - errorTerm = nodeErrorTerm; - } - } - - return errorTerm; -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -void CCoreDispInfo::CalcErrorTermAtNode( int nodeIndex, int level ) -{ - if( level == m_Power ) - return; - - // - // get the vertex indices - // - int neighborVertIndices[9]; - for( int i = 0; i < 8; i++ ) - { - neighborVertIndices[i] = m_Nodes[nodeIndex].GetNeighborVertIndex( i ); - } - neighborVertIndices[8] = m_Nodes[nodeIndex].GetCenterVertIndex(); - - - // - // calculate the error terms - // - Vector segment; - Vector v; - - VectorAdd( m_pVerts[neighborVertIndices[5]].m_Vert, m_pVerts[neighborVertIndices[4]].m_Vert, v ); - VectorScale( v, 0.5f, v ); - VectorSubtract( m_pVerts[neighborVertIndices[0]].m_Vert, v, segment ); - float errorTerm = ( float )VectorLength( segment ); - - VectorAdd( m_pVerts[neighborVertIndices[5]].m_Vert, m_pVerts[neighborVertIndices[6]].m_Vert, v ); - VectorScale( v, 0.5f, v ); - VectorSubtract( m_pVerts[neighborVertIndices[1]].m_Vert, v, segment ); - if( errorTerm < ( float )VectorLength( segment ) ) - errorTerm = ( float )VectorLength( segment ); - - VectorAdd( m_pVerts[neighborVertIndices[6]].m_Vert, m_pVerts[neighborVertIndices[7]].m_Vert, v ); - VectorScale( v, 0.5f, v ); - VectorSubtract( m_pVerts[neighborVertIndices[2]].m_Vert, v, segment ); - if( errorTerm < ( float )VectorLength( segment ) ) - errorTerm = ( float )VectorLength( segment ); - - VectorAdd( m_pVerts[neighborVertIndices[7]].m_Vert, m_pVerts[neighborVertIndices[4]].m_Vert, v ); - VectorScale( v, 0.5f, v ); - VectorSubtract( m_pVerts[neighborVertIndices[3]].m_Vert, v, segment ); - if( errorTerm < ( float )VectorLength( segment ) ) - errorTerm = ( float )VectorLength( segment ); - - VectorAdd( m_pVerts[neighborVertIndices[4]].m_Vert, m_pVerts[neighborVertIndices[6]].m_Vert, v ); - VectorScale( v, 0.5f, v ); - VectorSubtract( m_pVerts[neighborVertIndices[8]].m_Vert, v, segment ); - if( errorTerm < ( float )VectorLength( segment ) ) - errorTerm = ( float )VectorLength( segment ); - - VectorAdd( m_pVerts[neighborVertIndices[5]].m_Vert, m_pVerts[neighborVertIndices[7]].m_Vert, v ); - VectorScale( v, 0.5f, v ); - VectorSubtract( m_pVerts[neighborVertIndices[8]].m_Vert, v, segment ); - if( errorTerm < ( float )VectorLength( segment ) ) - errorTerm = ( float )VectorLength( segment ); - - // - // add the max child's error term - // - errorTerm += GetMaxErrorFromChildren( nodeIndex, level ); - - // set the error term - m_Nodes[nodeIndex].SetErrorTerm( errorTerm ); -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -void CCoreDispInfo::CalcNeighborVertIndicesAtNode( int nodeIndex, int level ) -{ - // calculate the shift in direction in the matrix - int shift = ( 1 << ( m_Power - level ) ); - - // calculate the width, height of the displacement surface (are uniform) - int extent = ( ( 1 << m_Power ) + 1 ); - - // - // get the neighbor vertex indices (defining the surface at the node level) - // - for( int direction = 0; direction < 8; direction++ ) - { - // - // get the parent vertex index in component form - // - int posX = m_Nodes[nodeIndex].GetCenterVertIndex() % extent; - int posY = m_Nodes[nodeIndex].GetCenterVertIndex() / extent; - - // - // calculate the neighboring vertex indices for surface rendering - // - bool bError = false; - switch( direction ) - { - case WEST: { posX -= shift; break; } - case NORTH: { posY += shift; break; } - case EAST: { posX += shift; break; } - case SOUTH: { posY -= shift; break; } - case SOUTHWEST: { posX -= shift; posY -= shift; break; } - case SOUTHEAST: { posX += shift; posY -= shift; break; } - case NORTHWEST: { posX -= shift; posY += shift; break; } - case NORTHEAST: { posX += shift; posY += shift; break; } - default: { bError = true; break; } - } - - if( bError ) - { - m_Nodes[nodeIndex].SetNeighborVertIndex( direction, -99999 ); - } - else - { - m_Nodes[nodeIndex].SetNeighborVertIndex( direction, ( ( posY * extent ) + posX ) ); - } - } -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -void CCoreDispInfo::CalcNodeInfo( int nodeIndex, int terminationLevel ) -{ - // get the level of the current node - int level = GetNodeLevel( nodeIndex ); - - // - // get the node data at the termination level - // - if( level == terminationLevel ) - { - // get the neighbor vertex indices (used to create surface at node level) - CalcNeighborVertIndicesAtNode( nodeIndex, level ); - - // get the neighbor node indices - //CalcNeighborNodeIndicesAtNode( nodeIndex, level ); - - // calculate the error term at the node - CalcErrorTermAtNode( nodeIndex, level ); - - // calcluate the axial-aligned bounding box at the node - CalcBoundingBoxAtNode( nodeIndex ); - - // calculate the triangular surface info at the node - CalcTriSurfInfoAtNode( nodeIndex ); - - return; - } - - // - // continue recursion (down to nodes "children") - // - for( int i = 4; i < 8; i++ ) - { - int childIndex = GetNodeChild( m_Power, nodeIndex, i ); - CalcNodeInfo( childIndex, terminationLevel ); - } -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -int CCoreDispInfo::GetNodeVertIndexFromParentIndex( int level, int parentVertIndex, int direction ) -{ - // calculate the "shift" - int shift = ( 1 << ( m_Power - ( level + 1 ) ) ); - - // calculate the width and height of displacement (is uniform) - int extent = ( ( 1 << m_Power ) + 1 ); - - // get the parent vertex index in component form - int posX = parentVertIndex % extent; - int posY = parentVertIndex / extent; - - // - // calculate the child index based on the parent index and child - // direction - // - switch( direction ) - { - case SOUTHWEST: { posX -= shift; posY -= shift; break; } - case SOUTHEAST: { posX += shift; posY -= shift; break; } - case NORTHWEST: { posX -= shift; posY += shift; break; } - case NORTHEAST: { posX += shift; posY += shift; break; } - default: return -99999; - } - - // return the child vertex index - return ( ( posY * extent ) + posX ); -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -void CCoreDispInfo::CalcVertIndicesAtNodes( int nodeIndex ) -{ - // - // check for recursion termination ( node level = power ) - // - int level = GetNodeLevel( nodeIndex ); - if( level == m_Power ) - return; - - // - // get the children indices - SW, SE, NW, NE - // - int childIndices[4]; - int i, j; - for( i = 0, j = 4; i < 4; i++, j++ ) - { - childIndices[i] = GetNodeChild( m_Power, nodeIndex, j ); - int centerIndex = GetNodeVertIndexFromParentIndex( level, m_Nodes[nodeIndex].GetCenterVertIndex(), j ); - m_Nodes[childIndices[i]].SetCenterVertIndex( centerIndex ); - } - - // - // calculate the children's node vertex indices - // - for( i = 0; i < 4; i++ ) - { - CalcVertIndicesAtNodes( childIndices[i] ); - } -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -void CCoreDispInfo::GenerateLODTree( void ) -{ - // - // calculate the displacement surface's vertex index at each quad-tree node - // centroid - // - int size = GetSize(); - int initialIndex = ( ( size - 1 ) >> 1 ); - m_Nodes[0].SetCenterVertIndex( initialIndex ); - CalcVertIndicesAtNodes( 0 ); - - // - // calculate the error terms, bounding boxes, and neighboring vertex indices - // at each node - // - for( int i = m_Power; i > 0; i-- ) - { - CalcNodeInfo( 0, i ); - } -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -void CCoreDispInfo::CalcDispSurfCoords( bool bLightMap, int lightmapID ) -{ - // - // get base surface texture coords - // - Vector2D texCoords[4]; - Vector2D luxelCoords[4]; - CCoreDispSurface *pSurf = GetSurface(); - - int i; - for( i = 0; i < 4; i++ ) - { - pSurf->GetTexCoord( i, texCoords[i] ); - pSurf->GetLuxelCoord( lightmapID, i, luxelCoords[i] ); - } - - // - // get images width and intervals along the edge - // - int postSpacing = GetPostSpacing(); - float ooInt = ( 1.0f / ( float )( postSpacing - 1 ) ); - - // - // calculate the parallel edge intervals - // - Vector2D edgeInt[2]; - if( !bLightMap ) - { - Vector2DSubtract( texCoords[1], texCoords[0], edgeInt[0] ); - Vector2DSubtract( texCoords[2], texCoords[3], edgeInt[1] ); - } - else - { - Vector2DSubtract( luxelCoords[1], luxelCoords[0], edgeInt[0] ); - Vector2DSubtract( luxelCoords[2], luxelCoords[3], edgeInt[1] ); - } - Vector2DMultiply( edgeInt[0], ooInt, edgeInt[0] ); - Vector2DMultiply( edgeInt[1], ooInt, edgeInt[1] ); - - // - // calculate the displacement points - // - for( i = 0; i < postSpacing; i++ ) - { - // - // position along parallel edges (start and end for a perpendicular segment) - // - Vector2D endPts[2]; - Vector2DMultiply( edgeInt[0], ( float )i, endPts[0] ); - Vector2DMultiply( edgeInt[1], ( float )i, endPts[1] ); - if( !bLightMap ) - { - Vector2DAdd( endPts[0], texCoords[0], endPts[0] ); - Vector2DAdd( endPts[1], texCoords[3], endPts[1] ); - } - else - { - Vector2DAdd( endPts[0], luxelCoords[0], endPts[0] ); - Vector2DAdd( endPts[1], luxelCoords[3], endPts[1] ); - } - - // - // interval length for perpendicular edge - // - Vector2D seg, segInt; - Vector2DSubtract( endPts[1], endPts[0], seg ); - Vector2DMultiply( seg, ooInt, segInt ); - - // - // calculate the material (texture or light) coordinate at each point - // - for( int j = 0; j < postSpacing; j++ ) - { - Vector2DMultiply( segInt, ( float )j, seg ); - - if( !bLightMap ) - { - Vector2DAdd( endPts[0], seg, m_pVerts[i*postSpacing+j].m_TexCoord ); - } - else - { - Vector2DAdd( endPts[0], seg, m_pVerts[i*postSpacing+j].m_LuxelCoords[lightmapID] ); - } - } - } -} - -#if 0 -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -void CCoreDispInfo::CalcDispSurfAlphas( void ) -{ - // - // get images width and intervals along the edge - // - int postSpacing = GetPostSpacing(); - float ooInt = ( 1.0f / ( float )( postSpacing - 1 ) ); - - // - // calculate the parallel edge intervals - // - float edgeInt[2]; - edgeInt[0] = m_Surf.m_Alpha[1] - m_Surf.m_Alpha[0]; - edgeInt[1] = m_Surf.m_Alpha[2] - m_Surf.m_Alpha[3]; - edgeInt[0] *= ooInt; - edgeInt[1] *= ooInt; - - // - // calculate the displacement points - // - for( int i = 0; i < postSpacing; i++ ) - { - // - // position along parallel edges (start and end for a perpendicular segment) - // - float endValues[2]; - - endValues[0] = edgeInt[0] * ( float )i; - endValues[1] = edgeInt[1] * ( float )i; - endValues[0] += m_Surf.m_Alpha[0]; - endValues[1] += m_Surf.m_Alpha[3]; - - // - // interval length for perpendicular edge - // - float seg, segInt; - seg = endValues[1] - endValues[0]; - segInt = seg * ooInt; - - // - // calculate the alpha value at each point - // - for( int j = 0; j < postSpacing; j++ ) - { - seg = segInt * ( float )j; - m_Alphas[i*postSpacing+j] = endValues[0] + seg; - } - } -} -#endif - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -void CCoreDispInfo::GenerateDispSurfTangentSpaces( void ) -{ - // - // get texture axes from base surface - // - CCoreDispSurface *pSurf = GetSurface(); - Vector sAxis, tAxis; - pSurf->GetSAxis( sAxis ); - pSurf->GetTAxis( tAxis ); - - // - // calculate the tangent spaces - // - int size = GetSize(); - for( int i = 0; i < size; i++ ) - { - // - // create the axes - normals, tangents, and binormals - // - VectorCopy( tAxis, m_pVerts[i].m_TangentT ); - VectorNormalize( m_pVerts[i].m_TangentT ); - CrossProduct( m_pVerts[i].m_Normal, m_pVerts[i].m_TangentT, m_pVerts[i].m_TangentS ); - VectorNormalize( m_pVerts[i].m_TangentS ); - CrossProduct( m_pVerts[i].m_TangentS, m_pVerts[i].m_Normal, m_pVerts[i].m_TangentT ); - VectorNormalize( m_pVerts[i].m_TangentT ); - - Vector tmpVect; - Vector planeNormal; - pSurf->GetNormal( planeNormal ); - CrossProduct( sAxis, tAxis, tmpVect ); - if( DotProduct( planeNormal, tmpVect ) > 0.0f ) - { - VectorScale( m_pVerts[i].m_TangentS, -1.0f, m_pVerts[i].m_TangentS ); - } - } -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -void CCoreDispInfo::CalcNormalFromEdges( int indexRow, int indexCol, bool bIsEdge[4], - Vector& normal ) -{ - // get the post spacing (size/interval of displacement surface) - int postSpacing = ( ( 1 << m_Power ) + 1 ); - - // initialize the normal accumulator - counter - Vector accumNormal; - int normalCount = 0; - - VectorClear( accumNormal ); - - Vector tmpVect[2]; - Vector tmpNormal; - - // - // check quadrant I (posX, posY) - // - if( bIsEdge[1] && bIsEdge[2] ) - { - // tri i - VectorSubtract( m_pVerts[(indexCol+1)*postSpacing+indexRow].m_Vert, m_pVerts[indexCol*postSpacing+indexRow].m_Vert, tmpVect[0] ); - VectorSubtract( m_pVerts[indexCol*postSpacing+(indexRow+1)].m_Vert, m_pVerts[indexCol*postSpacing+indexRow].m_Vert, tmpVect[1] ); - CrossProduct( tmpVect[1], tmpVect[0], tmpNormal ); - VectorNormalize( tmpNormal ); - VectorAdd( accumNormal, tmpNormal, accumNormal ); - normalCount++; - - // tri 2 - VectorSubtract( m_pVerts[(indexCol+1)*postSpacing+indexRow].m_Vert, m_pVerts[indexCol*postSpacing+(indexRow+1)].m_Vert, tmpVect[0] ); - VectorSubtract( m_pVerts[(indexCol+1)*postSpacing+(indexRow+1)].m_Vert, m_pVerts[indexCol*postSpacing+(indexRow+1)].m_Vert, tmpVect[1] ); - CrossProduct( tmpVect[1], tmpVect[0], tmpNormal ); - VectorNormalize( tmpNormal ); - VectorAdd( accumNormal, tmpNormal, accumNormal ); - normalCount++; - } - - // - // check quadrant II (negX, posY) - // - if( bIsEdge[0] && bIsEdge[1] ) - { - // tri i - VectorSubtract( m_pVerts[(indexCol+1)*postSpacing+(indexRow-1)].m_Vert, m_pVerts[indexCol*postSpacing+(indexRow-1)].m_Vert, tmpVect[0] ); - VectorSubtract( m_pVerts[indexCol*postSpacing+indexRow].m_Vert, m_pVerts[indexCol*postSpacing+(indexRow-1)].m_Vert, tmpVect[1] ); - CrossProduct( tmpVect[1], tmpVect[0], tmpNormal ); - VectorNormalize( tmpNormal ); - VectorAdd( accumNormal, tmpNormal, accumNormal ); - normalCount++; - - // tri 2 - VectorSubtract( m_pVerts[(indexCol+1)*postSpacing+(indexRow-1)].m_Vert, m_pVerts[indexCol*postSpacing+indexRow].m_Vert, tmpVect[0] ); - VectorSubtract( m_pVerts[(indexCol+1)*postSpacing+indexRow].m_Vert, m_pVerts[indexCol*postSpacing+indexRow].m_Vert, tmpVect[1] ); - CrossProduct( tmpVect[1], tmpVect[0], tmpNormal ); - VectorNormalize( tmpNormal ); - VectorAdd( accumNormal, tmpNormal, accumNormal ); - normalCount++; - } - - // - // check quadrant III (negX, negY) - // - if( bIsEdge[0] && bIsEdge[3] ) - { - // tri i - VectorSubtract( m_pVerts[indexCol*postSpacing+(indexRow-1)].m_Vert, m_pVerts[(indexCol-1)*postSpacing+(indexRow-1)].m_Vert, tmpVect[0] ); - VectorSubtract( m_pVerts[(indexCol-1)*postSpacing+indexRow].m_Vert, m_pVerts[(indexCol-1)*postSpacing+(indexRow-1)].m_Vert, tmpVect[1] ); - CrossProduct( tmpVect[1], tmpVect[0], tmpNormal ); - VectorNormalize( tmpNormal ); - VectorAdd( accumNormal, tmpNormal, accumNormal ); - normalCount++; - - // tri 2 - VectorSubtract( m_pVerts[indexCol*postSpacing+(indexRow-1)].m_Vert, m_pVerts[(indexCol-1)*postSpacing+indexRow].m_Vert, tmpVect[0] ); - VectorSubtract( m_pVerts[indexCol*postSpacing+indexRow].m_Vert, m_pVerts[(indexCol-1)*postSpacing+indexRow].m_Vert, tmpVect[1] ); - CrossProduct( tmpVect[1], tmpVect[0], tmpNormal ); - VectorNormalize( tmpNormal ); - VectorAdd( accumNormal, tmpNormal, accumNormal ); - normalCount++; - } - - // - // check quadrant IV (posX, negY) - // - if( bIsEdge[2] && bIsEdge[3] ) - { - // tri i - VectorSubtract( m_pVerts[indexCol*postSpacing+indexRow].m_Vert, m_pVerts[(indexCol-1)*postSpacing+indexRow].m_Vert, tmpVect[0] ); - VectorSubtract( m_pVerts[(indexCol-1)*postSpacing+(indexRow+1)].m_Vert, m_pVerts[(indexCol-1)*postSpacing+indexRow].m_Vert, tmpVect[1] ); - CrossProduct( tmpVect[1], tmpVect[0], tmpNormal ); - VectorNormalize( tmpNormal ); - VectorAdd( accumNormal, tmpNormal, accumNormal ); - normalCount++; - - // tri 2 - VectorSubtract( m_pVerts[indexCol*postSpacing+indexRow].m_Vert, m_pVerts[(indexCol-1)*postSpacing+(indexRow+1)].m_Vert, tmpVect[0] ); - VectorSubtract( m_pVerts[indexCol*postSpacing+(indexRow+1)].m_Vert, m_pVerts[(indexCol-1)*postSpacing+(indexRow+1)].m_Vert, tmpVect[1] ); - CrossProduct( tmpVect[1], tmpVect[0], tmpNormal ); - VectorNormalize( tmpNormal ); - VectorAdd( accumNormal, tmpNormal, accumNormal ); - normalCount++; - } - - VectorScale( accumNormal, ( 1.0f / ( float )normalCount ), normal ); -} - - -//----------------------------------------------------------------------------- -// Purpose: This function determines if edges exist in each of the directions -// off of the given point (given in component form). We know ahead of -// time that there are only 4 possibilities. -// -// 1 "directions" -// 0 + 2 -// 3 -// -// Input: indexRow - row position -// indexCol - col position -// direction - the direction (edge) currently being evaluated -// postSpacing - the number of intervals in the row and col directions -// Output: the edge existed? (true/false) -//----------------------------------------------------------------------------- -bool CCoreDispInfo::DoesEdgeExist( int indexRow, int indexCol, int direction, int postSpacing ) -{ - switch( direction ) - { - case 0: - // left edge - if( ( indexRow - 1 ) < 0 ) - return false; - return true; - case 1: - // top edge - if( ( indexCol + 1 ) > ( postSpacing - 1 ) ) - return false; - return true; - case 2: - // right edge - if( ( indexRow + 1 ) > ( postSpacing - 1 ) ) - return false; - return true; - case 3: - // bottom edge - if( ( indexCol - 1 ) < 0 ) - return false; - return true; - default: - return false; - } -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -void CCoreDispInfo::GenerateDispSurfNormals( void ) -{ - // get the post spacing (size/interval of displacement surface) - int postSpacing = GetPostSpacing(); - - // - // generate the normals at each displacement surface vertex - // - for( int i = 0; i < postSpacing; i++ ) - { - for( int j = 0; j < postSpacing; j++ ) - { - bool bIsEdge[4]; - - // edges - for( int k = 0; k < 4; k++ ) - { - bIsEdge[k] = DoesEdgeExist( j, i, k, postSpacing ); - } - - Vector normal; - CalcNormalFromEdges( j, i, bIsEdge, normal ); - - // save generated normal - VectorCopy( normal, m_pVerts[i*postSpacing+j].m_Normal ); - } - } -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -void CCoreDispInfo::GenerateDispSurf( void ) -{ - int i; - CCoreDispSurface *pSurf = GetSurface(); - Vector points[4]; - for( i = 0; i < 4; i++ ) - { - pSurf->GetPoint( i, points[i] ); - } - - // - // get the spacing (interval = width/height, are equal because it is uniform) along the edge - // - int postSpacing = GetPostSpacing(); - float ooInt = 1.0f / ( float )( postSpacing - 1 ); - - // - // calculate the opposite edge intervals - // - Vector edgeInt[2]; - VectorSubtract( points[1], points[0], edgeInt[0] ); - VectorScale( edgeInt[0], ooInt, edgeInt[0] ); - VectorSubtract( points[2], points[3], edgeInt[1] ); - VectorScale( edgeInt[1], ooInt, edgeInt[1] ); - - Vector elevNormal; - elevNormal.Init(); - if( m_Elevation != 0.0f ) - { - pSurf->GetNormal( elevNormal ); - VectorScale( elevNormal, m_Elevation, elevNormal ); - } - - // - // calculate the displaced vertices - // - for( i = 0; i < postSpacing; i++ ) - { - // - // calculate segment interval between opposite edges - // - Vector endPts[2]; - VectorScale( edgeInt[0], ( float )i, endPts[0] ); - VectorAdd( endPts[0], points[0], endPts[0] ); - VectorScale( edgeInt[1], ( float )i, endPts[1] ); - VectorAdd( endPts[1], points[3], endPts[1] ); - - Vector seg, segInt; - VectorSubtract( endPts[1], endPts[0], seg ); - VectorScale( seg, ooInt, segInt ); - - // - // calculate the surface vertices - // - for( int j = 0; j < postSpacing; j++ ) - { - int ndx = i * postSpacing + j; - - CoreDispVert_t *pVert = &m_pVerts[ndx]; - - // calculate the flat surface position -- saved separately - pVert->m_FlatVert = endPts[0] + ( segInt * ( float )j ); - - // start with the base surface position - pVert->m_Vert = pVert->m_FlatVert; - - // add the elevation vector -- if it exists - if( m_Elevation != 0.0f ) - { - pVert->m_Vert += elevNormal; - } - - // add the subdivision surface position - pVert->m_Vert += pVert->m_SubdivPos; - - // add the displacement field direction(normalized) and distance - pVert->m_Vert += pVert->m_FieldVector * pVert->m_FieldDistance; - } - } -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -//bool CCoreDispInfo::Create( int creationFlags ) -bool CCoreDispInfo::Create( void ) -{ - // sanity check - CCoreDispSurface *pSurf = GetSurface(); - if( pSurf->GetPointCount() != 4 ) - return false; - - // generate the displacement surface - GenerateDispSurf(); - - GenerateDispSurfNormals(); - - GenerateDispSurfTangentSpaces(); - - CalcDispSurfCoords( false, 0 ); - - for( int bumpID = 0; bumpID < ( NUM_BUMP_VECTS + 1 ); bumpID++ ) - { - CalcDispSurfCoords( true, bumpID ); - } - - GenerateLODTree(); - - GenerateCollisionData(); - - CreateTris(); - - return true; -} - -//----------------------------------------------------------------------------- -// Purpose: Create a displacement surface without generating the LOD for it. -//----------------------------------------------------------------------------- -bool CCoreDispInfo::CreateWithoutLOD( void ) -{ - // sanity check - CCoreDispSurface *pSurf = GetSurface(); - if( pSurf->GetPointCount() != 4 ) - return false; - - GenerateDispSurf(); - - GenerateDispSurfNormals(); - - GenerateDispSurfTangentSpaces(); - - CalcDispSurfCoords( false, 0 ); - - for( int bumpID = 0; bumpID < ( NUM_BUMP_VECTS + 1 ); bumpID++ ) - { - CalcDispSurfCoords( true, bumpID ); - } - GenerateCollisionData(); - - CreateTris(); - - return true; -} - - -// -// Node Functions (friend functions) -// - -//----------------------------------------------------------------------------- -// should make this more programatic and extensible! -//----------------------------------------------------------------------------- -int GetNodeLevel( int index ) -{ - // root - if( index == 0 ) - return 1; - - // [1...4] - if( index < 5 ) - return 2; - - // [5....20] - if( index < 21 ) - return 3; - - // [21....84] - if( index < 85 ) - return 4; - - // error!!! - return -1; -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -int GetNodeCount( int power ) -{ - return ( ( 1 << ( power << 1 ) ) / 3 ); -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -int GetNodeParent( int index ) -{ - // ( index - 1 ) / 4 - return ( ( index - 1 ) >> 2 ); -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -int GetNodeChild( int power, int index, int direction ) -{ - // ( index * 4 ) + direction - return ( ( index << 2 ) + ( direction - 3 ) ); -} - - -//----------------------------------------------------------------------------- -// Purpose: This function calculates the neighbor node index given the base -// node and direction of the neighbor node in the tree. -// Input: power - the size in one dimension of the displacement map (2^power + 1 ) -// index - the "base" node index -// direction - the direction of the neighbor { W = 1, N = 2, E = 3, S = 4 } -// Output: returns the index of the neighbor node -//----------------------------------------------------------------------------- -int GetNodeNeighborNode( int power, int index, int direction, int level ) -{ - // adjust the index to range [0...?] - int minNodeIndex = GetNodeMinNodeAtLevel( level ); - - // get node extent (uniform: height = width) - int nodeExtent = ( 1 << ( level - 1 ) ); - - // - // get node's component positions in quad-tree - // - int posX, posY; - GetComponentsFromNodeIndex( ( index - minNodeIndex ), &posX, &posY ); - - // - // find the neighbor in the "direction" - // - switch( direction ) - { - case CCoreDispInfo::WEST: - { - if( ( posX - 1 ) < 0 ) - { - return -( CCoreDispInfo::WEST + 1 ); - } - else - { - return ( GetNodeIndexFromComponents( ( posX - 1 ), posY ) + minNodeIndex ); - } - } - case CCoreDispInfo::NORTH: - { - if( ( posY + 1 ) == nodeExtent ) - { - return -( CCoreDispInfo::NORTH + 1 ); - } - else - { - return ( GetNodeIndexFromComponents( posX, ( posY + 1 ) ) + minNodeIndex ); - } - } - case CCoreDispInfo::EAST: - { - if( ( posX + 1 ) == nodeExtent ) - { - return -( CCoreDispInfo::EAST + 1 ); - } - else - { - return ( GetNodeIndexFromComponents( ( posX + 1 ), posY ) + minNodeIndex ); - } - } - case CCoreDispInfo::SOUTH: - { - if( ( posY - 1 ) < 0 ) - { - return -( CCoreDispInfo::SOUTH + 1 ); - } - else - { - return ( GetNodeIndexFromComponents( posX, ( posY - 1 ) ) + minNodeIndex ); - } - } - default: - { - return -99999; - } - } -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -int GetNodeNeighborNodeFromNeighborSurf( int power, int index, int direction, int level, int neighborOrient ) -{ - // adjust the index to range [0...?] - int minNodeIndex = GetNodeMinNodeAtLevel( level ); - - // get node extent (uniform: height = width) - int nodeExtent = ( 1 << ( level - 1 ) ); - - // - // get node's component positions in quad-tree - // - int posX, posY; - GetComponentsFromNodeIndex( ( index - minNodeIndex ), &posX, &posY ); - - switch( direction ) - { - case CCoreDispInfo::WEST: - { - switch( neighborOrient ) - { - case CCoreDispInfo::WEST: return -( ( GetNodeIndexFromComponents( posX, ( ( nodeExtent - 1 ) - posY ) ) ) + minNodeIndex ); - case CCoreDispInfo::NORTH: return -( ( GetNodeIndexFromComponents( ( nodeExtent - 1 ) - posY, ( nodeExtent - 1 ) ) ) + minNodeIndex ); - case CCoreDispInfo::EAST: return -( ( GetNodeIndexFromComponents( ( nodeExtent - 1 ), posY ) ) + minNodeIndex ); - case CCoreDispInfo::SOUTH: return -( ( GetNodeIndexFromComponents( posY, posX ) ) + minNodeIndex ); - default: return -99999; - } - } - case CCoreDispInfo::NORTH: - { - switch( neighborOrient ) - { - case CCoreDispInfo::WEST: return -( ( GetNodeIndexFromComponents( ( ( nodeExtent - 1 ) - posY ), ( ( nodeExtent - 1 ) - posX ) ) ) + minNodeIndex ); - case CCoreDispInfo::NORTH: return -( ( GetNodeIndexFromComponents( ( ( nodeExtent - 1 ) - posX ), posY ) ) + minNodeIndex ); - case CCoreDispInfo::EAST: return -( ( GetNodeIndexFromComponents( posY, posX ) ) + minNodeIndex ); - case CCoreDispInfo::SOUTH: return -( ( GetNodeIndexFromComponents( posX, ( ( nodeExtent - 1 ) - posY ) ) ) + minNodeIndex ); - default: return -99999; - } - } - case CCoreDispInfo::EAST: - { - switch( neighborOrient ) - { - case CCoreDispInfo::WEST: return -( ( GetNodeIndexFromComponents( ( ( nodeExtent - 1 ) - posX ), posY ) ) + minNodeIndex ); - case CCoreDispInfo::NORTH: return -( ( GetNodeIndexFromComponents( posY, posX ) ) + minNodeIndex ); - case CCoreDispInfo::EAST: return -( ( GetNodeIndexFromComponents( posX, ( ( nodeExtent - 1 ) - posY ) ) ) + minNodeIndex ); - case CCoreDispInfo::SOUTH: return -( ( GetNodeIndexFromComponents( ( ( nodeExtent - 1 ) - posY ), ( ( nodeExtent - 1 ) - posX ) ) ) + minNodeIndex ); - default: return -99999; - } - } - case CCoreDispInfo::SOUTH: - { - switch( neighborOrient ) - { - case CCoreDispInfo::WEST: return -( ( GetNodeIndexFromComponents( posY, posX ) ) + minNodeIndex ); - case CCoreDispInfo::NORTH: return -( ( GetNodeIndexFromComponents( posX, ( nodeExtent - 1 ) ) ) + minNodeIndex ); - case CCoreDispInfo::EAST: return -( ( GetNodeIndexFromComponents( ( nodeExtent - 1 ), ( ( nodeExtent - 1 ) - posX ) ) ) + minNodeIndex ); - case CCoreDispInfo::SOUTH: return -( ( GetNodeIndexFromComponents( ( ( nodeExtent - 1 ) - posX ), posY ) ) + minNodeIndex ); - default: return -99999; - } - } - default: - { - return -99999; - } - } -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -int GetNodeMinNodeAtLevel( int level ) -{ - switch( level ) - { - case 1: return 0; - case 2: return 1; - case 3: return 5; - case 4: return 21; - default: return -99999; - } -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -void GetComponentsFromNodeIndex( int index, int *x, int *y ) -{ - *x = 0; - *y = 0; - - for( int shift = 0; index != 0; shift++ ) - { - *x |= ( index & 1 ) << shift; - index >>= 1; - - *y |= ( index & 1 ) << shift; - index >>= 1; - } -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -int GetNodeIndexFromComponents( int x, int y ) -{ - int index = 0; - - // Interleave bits from the x and y values to create the index: - - int shift; - for( shift = 0; x != 0; shift += 2, x >>= 1 ) - { - index |= ( x & 1 ) << shift; - } - - for( shift = 1; y != 0; shift += 2, y >>= 1 ) - { - index |= ( y & 1 ) << shift; - } - - return index; -} - - -// Turn the optimizer back on -#pragma optimize( "", on ) - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -void CCoreDispInfo::GetPositionOnSurface( float u, float v, Vector &vPos, - Vector *pNormal, float *pAlpha ) -{ - Vector2D dispUV( u, v ); - DispUVToSurf( dispUV, vPos, pNormal, pAlpha ); -} - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -void CCoreDispInfo::BaseFacePlaneToDispUV( Vector const &planePt, Vector2D &dispUV ) -{ - // Get the base surface points. - CCoreDispSurface *pSurf = GetSurface(); - Vector vecPoints[4]; - for( int iPoint = 0; iPoint < 4; ++iPoint ) - { - pSurf->GetPoint( iPoint, vecPoints[iPoint] ); - } - - PointInQuadToBarycentric( vecPoints[0], vecPoints[3], vecPoints[2], vecPoints[1], planePt, dispUV ); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CCoreDispInfo::DispUVToSurf_TriTLToBR_1( const Vector &vecIntersectPoint, - int nSnapU, int nNextU, int nSnapV, int nNextV, - Vector &vecPoint, Vector *pNormal, float *pAlpha, - bool bBackup ) -{ - int nWidth = GetWidth(); - - int nIndices[3]; - nIndices[0] = nNextV * nWidth + nSnapU; - nIndices[1] = nNextV * nWidth + nNextU; - nIndices[2] = nSnapV * nWidth + nNextU; - - Vector vecFlatVerts[3], vecVerts[3]; - float flAlphas[3]; - for ( int iVert = 0; iVert < 3; ++iVert ) - { - vecFlatVerts[iVert] = m_pVerts[nIndices[iVert]].m_FlatVert; - vecVerts[iVert] = m_pVerts[nIndices[iVert]].m_Vert; - flAlphas[iVert] = m_pVerts[nIndices[iVert]].m_Alpha; - } - - if ( nSnapU == nNextU ) - { - if ( nSnapV == nNextV ) - { - vecPoint = vecVerts[0]; - *pAlpha = flAlphas[0]; - } - else - { - float flFrac = ( vecIntersectPoint - vecFlatVerts[0] ).Length() / ( vecFlatVerts[2] - vecFlatVerts[0] ).Length(); - vecPoint = vecVerts[0] + ( flFrac * ( vecVerts[2] - vecVerts[0] ) ); - - if ( pAlpha ) - { - *pAlpha = flAlphas[0] + ( flFrac * ( flAlphas[2] - flAlphas[0] ) ); - } - } - - if( pNormal ) - { - Vector edgeU = vecVerts[0] - vecVerts[1]; - Vector edgeV = vecVerts[2] - vecVerts[1]; - *pNormal = CrossProduct( edgeU, edgeV ); - VectorNormalize( *pNormal ); - } - } - else if ( nSnapV == nNextV ) - { - if ( nSnapU == nNextU ) - { - vecPoint = vecVerts[0]; - *pAlpha = flAlphas[0]; - } - else - { - float flFrac = ( vecIntersectPoint - vecFlatVerts[0] ).Length() / ( vecFlatVerts[2] - vecFlatVerts[0] ).Length(); - vecPoint = vecVerts[0] + ( flFrac * ( vecVerts[2] - vecVerts[0] ) ); - - if ( pAlpha ) - { - *pAlpha = flAlphas[0] + ( flFrac * ( flAlphas[2] - flAlphas[0] ) ); - } - } - - if( pNormal ) - { - Vector edgeU = vecVerts[0] - vecVerts[1]; - Vector edgeV = vecVerts[2] - vecVerts[1]; - *pNormal = CrossProduct( edgeU, edgeV ); - VectorNormalize( *pNormal ); - } - } - else - { - float flCfs[3]; - if ( CalcBarycentricCooefs( vecFlatVerts[0], vecFlatVerts[1], vecFlatVerts[2], vecIntersectPoint, flCfs[0], flCfs[1], flCfs[2] ) ) - { - vecPoint = ( vecVerts[0] * flCfs[0] ) + ( vecVerts[1] * flCfs[1] ) + ( vecVerts[2] * flCfs[2] ); - - if( pAlpha ) - { - *pAlpha = ( flAlphas[0] * flCfs[0] ) + ( flAlphas[1] * flCfs[1] ) + ( flAlphas[2] * flCfs[2] ); - } - - if( pNormal ) - { - Vector edgeU = vecVerts[0] - vecVerts[1]; - Vector edgeV = vecVerts[2] - vecVerts[1]; - *pNormal = CrossProduct( edgeU, edgeV ); - VectorNormalize( *pNormal ); - } - } - else - { - if ( !bBackup ) - { - DispUVToSurf_TriTLToBR_2( vecIntersectPoint, nSnapU, nNextU, nSnapV, nNextV, vecPoint, pNormal, pAlpha, true ); - } - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CCoreDispInfo::DispUVToSurf_TriTLToBR_2( const Vector &vecIntersectPoint, - int nSnapU, int nNextU, int nSnapV, int nNextV, - Vector &vecPoint, Vector *pNormal, float *pAlpha, - bool bBackup ) -{ - int nWidth = GetWidth(); - - int nIndices[3]; - nIndices[0] = nSnapV * nWidth + nSnapU; - nIndices[1] = nNextV * nWidth + nSnapU; - nIndices[2] = nSnapV * nWidth + nNextU; - - Vector vecFlatVerts[3], vecVerts[3]; - float flAlphas[3]; - for ( int iVert = 0; iVert < 3; ++iVert ) - { - vecFlatVerts[iVert] = m_pVerts[nIndices[iVert]].m_FlatVert; - vecVerts[iVert] = m_pVerts[nIndices[iVert]].m_Vert; - flAlphas[iVert] = m_pVerts[nIndices[iVert]].m_Alpha; - } - - if ( nSnapU == nNextU ) - { - if ( nSnapV == nNextV ) - { - vecPoint = vecVerts[0]; - *pAlpha = flAlphas[0]; - } - else - { - float flFrac = ( vecIntersectPoint - vecFlatVerts[0] ).Length() / ( vecFlatVerts[1] - vecFlatVerts[0] ).Length(); - vecPoint = vecVerts[0] + ( flFrac * ( vecVerts[1] - vecVerts[0] ) ); - - if ( pAlpha ) - { - *pAlpha = flAlphas[0] + ( flFrac * ( flAlphas[1] - flAlphas[0] ) ); - } - } - - if( pNormal ) - { - Vector edgeU = vecVerts[2] - vecVerts[0]; - Vector edgeV = vecVerts[1] - vecVerts[0]; - *pNormal = CrossProduct( edgeU, edgeV ); - VectorNormalize( *pNormal ); - } - } - else if ( nSnapV == nNextV ) - { - if ( nSnapU == nNextU ) - { - vecPoint = vecVerts[0]; - *pAlpha = flAlphas[0]; - } - else - { - float flFrac = ( vecIntersectPoint - vecFlatVerts[0] ).Length() / ( vecFlatVerts[2] - vecFlatVerts[0] ).Length(); - vecPoint = vecVerts[0] + ( flFrac * ( vecVerts[2] - vecVerts[0] ) ); - - if ( pAlpha ) - { - *pAlpha = flAlphas[0] + ( flFrac * ( flAlphas[2] - flAlphas[0] ) ); - } - } - - if( pNormal ) - { - Vector edgeU = vecVerts[2] - vecVerts[0]; - Vector edgeV = vecVerts[1] - vecVerts[0]; - *pNormal = CrossProduct( edgeU, edgeV ); - VectorNormalize( *pNormal ); - } - } - else - { - float flCfs[3]; - if ( CalcBarycentricCooefs( vecFlatVerts[0], vecFlatVerts[1], vecFlatVerts[2], vecIntersectPoint, flCfs[0], flCfs[1], flCfs[2] ) ) - { - vecPoint = ( vecVerts[0] * flCfs[0] ) + ( vecVerts[1] * flCfs[1] ) + ( vecVerts[2] * flCfs[2] ); - - if( pAlpha ) - { - *pAlpha = ( flAlphas[0] * flCfs[0] ) + ( flAlphas[1] * flCfs[1] ) + ( flAlphas[2] * flCfs[2] ); - } - - if( pNormal ) - { - Vector edgeU = vecVerts[2] - vecVerts[0]; - Vector edgeV = vecVerts[1] - vecVerts[0]; - *pNormal = CrossProduct( edgeU, edgeV ); - VectorNormalize( *pNormal ); - } - } - else - { - if ( !bBackup ) - { - DispUVToSurf_TriTLToBR_1( vecIntersectPoint, nSnapU, nNextU, nSnapV, nNextV, vecPoint, pNormal, pAlpha, true ); - } - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CCoreDispInfo::DispUVToSurf_TriTLToBR( Vector &vecPoint, Vector *pNormal, float *pAlpha, - float flU, float flV, const Vector &vecIntersectPoint ) -{ - const float TRIEDGE_EPSILON = 0.00001f; - - int nWidth = GetWidth(); - int nHeight = GetHeight(); - - int nSnapU = static_cast( flU ); - int nSnapV = static_cast( flV ); - int nNextU = nSnapU + 1; - int nNextV = nSnapV + 1; - if ( nNextU == nWidth) { --nNextU; } - if ( nNextV == nHeight ) { --nNextV; } - - float flFracU = flU - static_cast( nSnapU ); - float flFracV = flV - static_cast( nSnapV ); - - if ( ( flFracU + flFracV ) >= ( 1.0f + TRIEDGE_EPSILON ) ) - { - DispUVToSurf_TriTLToBR_1( vecIntersectPoint, nSnapU, nNextU, nSnapV, nNextV, vecPoint, pNormal, pAlpha, false ); - } - else - { - DispUVToSurf_TriTLToBR_2( vecIntersectPoint, nSnapU, nNextU, nSnapV, nNextV, vecPoint, pNormal, pAlpha, false ); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CCoreDispInfo::DispUVToSurf_TriBLToTR_1( const Vector &vecIntersectPoint, - int nSnapU, int nNextU, int nSnapV, int nNextV, - Vector &vecPoint, Vector *pNormal, float *pAlpha, - bool bBackup ) -{ - int nWidth = GetWidth(); - - int nIndices[3]; - nIndices[0] = nSnapV * nWidth + nSnapU; - nIndices[1] = nNextV * nWidth + nSnapU; - nIndices[2] = nNextV * nWidth + nNextU; - - Vector vecFlatVerts[3], vecVerts[3]; - float flAlphas[3]; - for ( int iVert = 0; iVert < 3; ++iVert ) - { - vecFlatVerts[iVert] = m_pVerts[nIndices[iVert]].m_FlatVert; - vecVerts[iVert] = m_pVerts[nIndices[iVert]].m_Vert; - flAlphas[iVert] = m_pVerts[nIndices[iVert]].m_Alpha; - } - - if ( nSnapU == nNextU ) - { - if ( nSnapV == nNextV ) - { - vecPoint = vecVerts[0]; - *pAlpha = flAlphas[0]; - } - else - { - float flFrac = ( vecIntersectPoint - vecFlatVerts[0] ).Length() / ( vecFlatVerts[2] - vecFlatVerts[0] ).Length(); - vecPoint = vecVerts[0] + ( flFrac * ( vecVerts[2] - vecVerts[0] ) ); - - if ( pAlpha ) - { - *pAlpha = flAlphas[0] + ( flFrac * ( flAlphas[2] - flAlphas[0] ) ); - } - } - - if( pNormal ) - { - Vector edgeU = vecVerts[2] - vecVerts[1]; - Vector edgeV = vecVerts[0] - vecVerts[1]; - *pNormal = CrossProduct( edgeU, edgeV ); - VectorNormalize( *pNormal ); - } - } - else if ( nSnapV == nNextV ) - { - if ( nSnapU == nNextU ) - { - vecPoint = vecVerts[0]; - *pAlpha = flAlphas[0]; - } - else - { - float flFrac = ( vecIntersectPoint - vecFlatVerts[0] ).Length() / ( vecFlatVerts[2] - vecFlatVerts[0] ).Length(); - vecPoint = vecVerts[0] + ( flFrac * ( vecVerts[2] - vecVerts[0] ) ); - - if ( pAlpha ) - { - *pAlpha = flAlphas[0] + ( flFrac * ( flAlphas[2] - flAlphas[0] ) ); - } - } - - if( pNormal ) - { - Vector edgeU = vecVerts[2] - vecVerts[1]; - Vector edgeV = vecVerts[0] - vecVerts[1]; - *pNormal = CrossProduct( edgeV, edgeU ); - VectorNormalize( *pNormal ); - } - } - else - { - float flCfs[3]; - if ( CalcBarycentricCooefs( vecFlatVerts[0], vecFlatVerts[1], vecFlatVerts[2], vecIntersectPoint, flCfs[0], flCfs[1], flCfs[2] ) ) - { - vecPoint = ( vecVerts[0] * flCfs[0] ) + ( vecVerts[1] * flCfs[1] ) + ( vecVerts[2] * flCfs[2] ); - - if( pAlpha ) - { - *pAlpha = ( flAlphas[0] * flCfs[0] ) + ( flAlphas[1] * flCfs[1] ) + ( flAlphas[2] * flCfs[2] ); - } - - if( pNormal ) - { - Vector edgeU = vecVerts[2] - vecVerts[1]; - Vector edgeV = vecVerts[0] - vecVerts[1]; - *pNormal = CrossProduct( edgeV, edgeU ); - VectorNormalize( *pNormal ); - } - } - else - { - if ( !bBackup ) - { - DispUVToSurf_TriBLToTR_2( vecIntersectPoint, nSnapU, nNextU, nSnapV, nNextV, vecPoint, pNormal, pAlpha, true ); - } - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CCoreDispInfo::DispUVToSurf_TriBLToTR_2( const Vector &vecIntersectPoint, - int nSnapU, int nNextU, int nSnapV, int nNextV, - Vector &vecPoint, Vector *pNormal, float *pAlpha, - bool bBackup ) -{ - int nWidth = GetWidth(); - - int nIndices[3]; - nIndices[0] = nSnapV * nWidth + nSnapU; - nIndices[1] = nNextV * nWidth + nNextU; - nIndices[2] = nSnapV * nWidth + nNextU; - - Vector vecFlatVerts[3], vecVerts[3]; - float flAlphas[3]; - for ( int iVert = 0; iVert < 3; ++iVert ) - { - vecFlatVerts[iVert] = m_pVerts[nIndices[iVert]].m_FlatVert; - vecVerts[iVert] = m_pVerts[nIndices[iVert]].m_Vert; - flAlphas[iVert] = m_pVerts[nIndices[iVert]].m_Alpha; - } - - if ( nSnapU == nNextU ) - { - if ( nSnapV == nNextV ) - { - vecPoint = vecVerts[0]; - *pAlpha = flAlphas[0]; - } - else - { - float flFrac = ( vecIntersectPoint - vecFlatVerts[0] ).Length() / ( vecFlatVerts[1] - vecFlatVerts[0] ).Length(); - vecPoint = vecVerts[0] + ( flFrac * ( vecVerts[1] - vecVerts[0] ) ); - - if ( pAlpha ) - { - *pAlpha = flAlphas[0] + ( flFrac * ( flAlphas[1] - flAlphas[0] ) ); - } - } - - if( pNormal ) - { - Vector edgeU = vecVerts[0] - vecVerts[2]; - Vector edgeV = vecVerts[1] - vecVerts[2]; - *pNormal = CrossProduct( edgeV, edgeU ); - VectorNormalize( *pNormal ); - } - } - else if ( nSnapV == nNextV ) - { - if ( nSnapU == nNextU ) - { - vecPoint = vecVerts[0]; - *pAlpha = flAlphas[0]; - } - else - { - float flFrac = ( vecIntersectPoint - vecFlatVerts[0] ).Length() / ( vecFlatVerts[2] - vecFlatVerts[0] ).Length(); - vecPoint = vecVerts[0] + ( flFrac * ( vecVerts[2] - vecVerts[0] ) ); - - if ( pAlpha ) - { - *pAlpha = flAlphas[0] + ( flFrac * ( flAlphas[2] - flAlphas[0] ) ); - } - } - - if( pNormal ) - { - Vector edgeU = vecVerts[0] - vecVerts[2]; - Vector edgeV = vecVerts[1] - vecVerts[2]; - *pNormal = CrossProduct( edgeV, edgeU ); - VectorNormalize( *pNormal ); - } - } - else - { - float flCfs[3]; - if ( CalcBarycentricCooefs( vecFlatVerts[0], vecFlatVerts[1], vecFlatVerts[2], vecIntersectPoint, flCfs[0], flCfs[1], flCfs[2] ) ) - { - vecPoint = ( vecVerts[0] * flCfs[0] ) + ( vecVerts[1] * flCfs[1] ) + ( vecVerts[2] * flCfs[2] ); - - if( pAlpha ) - { - *pAlpha = ( flAlphas[0] * flCfs[0] ) + ( flAlphas[1] * flCfs[1] ) + ( flAlphas[2] * flCfs[2] ); - } - - if( pNormal ) - { - Vector edgeU = vecVerts[0] - vecVerts[2]; - Vector edgeV = vecVerts[1] - vecVerts[2]; - *pNormal = CrossProduct( edgeV, edgeU ); - VectorNormalize( *pNormal ); - } - } - else - { - if ( !bBackup ) - { - DispUVToSurf_TriBLToTR_1( vecIntersectPoint, nSnapU, nNextU, nSnapV, nNextV, vecPoint, pNormal, pAlpha, true ); - } - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CCoreDispInfo::DispUVToSurf_TriBLToTR( Vector &vecPoint, Vector *pNormal, float *pAlpha, - float flU, float flV, const Vector &vecIntersectPoint ) -{ - int nWidth = GetWidth(); - int nHeight = GetHeight(); - - int nSnapU = static_cast( flU ); - int nSnapV = static_cast( flV ); - int nNextU = nSnapU + 1; - int nNextV = nSnapV + 1; - if ( nNextU == nWidth) { --nNextU; } - if ( nNextV == nHeight ) { --nNextV; } - - float flFracU = flU - static_cast( nSnapU ); - float flFracV = flV - static_cast( nSnapV ); - - if( flFracU < flFracV ) - { - DispUVToSurf_TriBLToTR_1( vecIntersectPoint, nSnapU, nNextU, nSnapV, nNextV, vecPoint, pNormal, pAlpha, false ); - } - else - { - DispUVToSurf_TriBLToTR_2( vecIntersectPoint, nSnapU, nNextU, nSnapV, nNextV, vecPoint, pNormal, pAlpha, false ); - } -} - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -void CCoreDispInfo::DispUVToSurf( Vector2D const &dispUV, Vector &vecPoint, - Vector *pNormal, float *pAlpha ) -{ - // Check to see that the point is on the surface. - if ( dispUV.x < 0.0f || dispUV.x > 1.0f || dispUV.y < 0.0f || dispUV.y > 1.0f ) - return; - - // Get the base surface points. - Vector vecIntersectPoint; - CCoreDispSurface *pSurf = GetSurface(); - PointInQuadFromBarycentric( pSurf->GetPoint( 0 ), pSurf->GetPoint( 3 ), pSurf->GetPoint( 2 ), pSurf->GetPoint( 1 ), dispUV, vecIntersectPoint ); - - // Get the displacement power. - int nWidth = GetWidth(); - int nHeight = GetHeight(); - - // Scale the U, V coordinates to the displacement grid size. - float flU = dispUV.x * ( static_cast( nWidth ) - 1.000001f ); - float flV = dispUV.y * ( static_cast( nHeight ) - 1.000001f ); - - // Find the base U, V. - int nSnapU = static_cast( flU ); - int nSnapV = static_cast( flV ); - - // Use this to get the triangle orientation. - bool bOdd = ( ( ( nSnapV * nWidth ) + nSnapU ) % 2 == 1 ); - - // Top Left to Bottom Right - if( bOdd ) - { - DispUVToSurf_TriTLToBR( vecPoint, pNormal, pAlpha, flU, flV, vecIntersectPoint ); - } - // Bottom Left to Top Right - else - { - DispUVToSurf_TriBLToTR( vecPoint, pNormal, pAlpha, flU, flV, vecIntersectPoint ); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Create bounding boxes around pairs of triangles (in a grid-like) -// fashion; used for culling -//----------------------------------------------------------------------------- -void CCoreDispInfo::CreateBoundingBoxes( CoreDispBBox_t *pBBox, int count ) -{ - // - // Initialize the bounding boxes. - // - int iBox; - for( iBox = 0; iBox < count; ++iBox ) - { - pBBox[iBox].vMin.Init( FLT_MAX, FLT_MAX, FLT_MAX ); - pBBox[iBox].vMax.Init( FLT_MIN, FLT_MIN, FLT_MIN ); - } - - // Get the width and height of the displacement surface. - int nHeight = GetHeight(); - int nWidth = GetWidth(); - - // Find bounding box of every two consecutive triangles - iBox = 0; - int nIndex = 0; - for( int iHgt = 0; iHgt < ( nHeight - 1 ); ++iHgt ) - { - for( int iWid = 0; iWid < ( nWidth - 1 ); ++iWid ) - { - for( int iPoint = 0; iPoint < 4; ++iPoint ) - { - switch( iPoint ) - { - case 0: { nIndex = ( nHeight * iHgt ) + iWid; break; } - case 1: { nIndex = ( nHeight * ( iHgt + 1 ) ) + iWid; break; } - case 2: { nIndex = ( nHeight * ( iHgt + 1 ) ) + ( iWid + 1 ); break; } - case 3: { nIndex = ( nHeight * iHgt ) + ( iWid + 1 ); break; } - default: { break; } - } - - Vector vecPoint; - GetVert( nIndex, vecPoint ); - if( vecPoint[0] < pBBox[iBox].vMin[0] ) { pBBox[iBox].vMin[0] = vecPoint[0]; } - if( vecPoint[1] < pBBox[iBox].vMin[1] ) { pBBox[iBox].vMin[1] = vecPoint[1]; } - if( vecPoint[2] < pBBox[iBox].vMin[2] ) { pBBox[iBox].vMin[2] = vecPoint[2]; } - - if( vecPoint[0] > pBBox[iBox].vMax[0] ) { pBBox[iBox].vMax[0] = vecPoint[0]; } - if( vecPoint[1] > pBBox[iBox].vMax[1] ) { pBBox[iBox].vMax[1] = vecPoint[1]; } - if( vecPoint[2] > pBBox[iBox].vMax[2] ) { pBBox[iBox].vMax[2] = vecPoint[2]; } - } - - iBox++; - } - } - - // Verify. - Assert( iBox == count ); - - // Bloat. - for ( iBox = 0; iBox < count; ++iBox ) - { - for( int iAxis = 0; iAxis < 3; ++iAxis ) - { - pBBox[iBox].vMin[iAxis] -= 1.0f; - pBBox[iBox].vMax[iAxis] += 1.0f; - } - } -} - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -inline bool PointInDispBBox( CoreDispBBox_t *pBox, const Vector &vecPoint ) -{ - // Check to see if point lies in box - if( ( vecPoint.x < pBox->vMin.x ) || ( vecPoint.x > pBox->vMax.x ) ) - return false; - - if( ( vecPoint.y < pBox->vMin.y ) || ( vecPoint.y > pBox->vMax.y ) ) - return false; - - if( ( vecPoint.z < pBox->vMin.z ) || ( vecPoint.z > pBox->vMax.z ) ) - return false; - - return true; -} - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -void CCoreDispInfo::GetTriangleIndicesForDispBBox( int nIndex, int nTris[2][3] ) -{ - // Test to see whether or not the index is odd. - bool bOdd = ( ( nIndex % 2 ) == 1 ); - - int nWidth = GetWidth(); - - // Tris for TLtoBR - if ( bOdd ) - { - nTris[0][0] = nIndex; - nTris[0][1] = nIndex + nWidth; - nTris[0][2] = nIndex + 1; - - nTris[1][0] = nIndex + 1; - nTris[1][1] = nIndex + nWidth; - nTris[1][2] = nIndex + nWidth + 1; - } - // Tris for BLtoTR - else - { - nTris[0][0] = nIndex; - nTris[0][1] = nIndex + nWidth; - nTris[0][2] = nIndex + nWidth + 1; - - nTris[1][0] = nIndex; - nTris[1][1] = nIndex + nWidth + 1; - nTris[1][2] = nIndex + 1; - } -} - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -bool CCoreDispInfo::SurfToBaseFacePlane( Vector const &surfPt, Vector &planePt ) -{ - // Create bounding boxes - int nBoxCount = ( GetHeight() - 1 ) * ( GetWidth() - 1 ); - CoreDispBBox_t *pBBox = new CoreDispBBox_t[nBoxCount]; - CreateBoundingBoxes( pBBox, nBoxCount ); - - // Use the boxes as a first-pass culling mechanism. - for( int iBox = 0; iBox < nBoxCount; ++iBox ) - { - // Get the current displacement triangle-pair bounding-box. - CoreDispBBox_t *pBox = &pBBox[iBox]; - if( !pBox ) - continue; - - // Check the point against the current displacement bounding-box. - if ( !PointInDispBBox( pBox, surfPt ) ) - continue; - - // Point lies within the bounding box. - int nIndex = iBox + ( iBox / ( GetWidth() - 1 ) ); - - // Get the triangle coordinates for this box. - int aTris[2][3]; - GetTriangleIndicesForDispBBox( nIndex, aTris ); - - // Barycentrically test the triangles on the displacement surface. - Vector vecPoints[3]; - for ( int iTri = 0; iTri < 2; ++iTri ) - { - for ( int iVert = 0; iVert < 3; ++iVert ) - { - GetVert( aTris[iTri][iVert], vecPoints[iVert] ); - } - - float c[3]; - if ( CalcBarycentricCooefs( vecPoints[0], vecPoints[1], vecPoints[2], surfPt, c[0], c[1], c[2] ) ) - { - Vector vecFlatPoints[3]; - for ( int iVert = 0; iVert < 3; ++iVert ) - { - GetFlatVert( aTris[iTri][iVert], vecFlatPoints[iVert] ); - } - - planePt = ( vecFlatPoints[0] * c[0] ) + ( vecFlatPoints[1] * c[1] ) + ( vecFlatPoints[2] * c[2] ); - - // Delete temporary memory. - delete [] pBBox; - return true; - } - } - } - - // Delete temporary memory - delete [] pBBox; - return false; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -int CCoreDispInfo::GetTriCount( void ) -{ - return ( ( GetHeight() - 1 ) * ( GetWidth() -1 ) * 2 ); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CCoreDispInfo::GetTriIndices( int iTri, unsigned short &v1, unsigned short &v2, unsigned short &v3 ) -{ - // Verify we have the correct data (only build when collision data is built). - if ( !m_pTris || ( iTri < 0 ) || ( iTri >= GetTriCount() ) ) - { - Assert( iTri >= 0 ); - Assert( iTri < GetTriCount() ); - Assert( m_pTris ); - return; - } - - CoreDispTri_t *pTri = &m_pTris[iTri]; - v1 = pTri->m_iIndex[0]; - v2 = pTri->m_iIndex[1]; - v3 = pTri->m_iIndex[2]; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CCoreDispInfo::SetTriIndices( int iTri, unsigned short v1, unsigned short v2, unsigned short v3 ) -{ - // Verify we have the correct data (only build when collision data is built). - if ( !m_pTris || ( iTri < 0 ) || ( iTri >= GetTriCount() ) ) - { - Assert( iTri >= 0 ); - Assert( iTri < GetTriCount() ); - Assert( m_pTris ); - return; - } - - CoreDispTri_t *pTri = &m_pTris[iTri]; - pTri->m_iIndex[0] = v1; - pTri->m_iIndex[1] = v2; - pTri->m_iIndex[2] = v3; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CCoreDispInfo::GetTriPos( int iTri, Vector &v1, Vector &v2, Vector &v3 ) -{ - // Verify we have the correct data (only build when collision data is built). - if ( !m_pTris || ( iTri < 0 ) || ( iTri >= GetTriCount() ) ) - { - Assert( iTri >= 0 ); - Assert( iTri < GetTriCount() ); - Assert( m_pTris ); - return; - } - - CoreDispTri_t *pTri = &m_pTris[iTri]; - v1 = m_pVerts[pTri->m_iIndex[0]].m_Vert; - v2 = m_pVerts[pTri->m_iIndex[1]].m_Vert; - v3 = m_pVerts[pTri->m_iIndex[2]].m_Vert; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CCoreDispInfo::InitTris( void ) -{ - // Verify we have the correct data (only build when collision data is built). - if ( !m_pTris ) - { - Assert( m_pTris ); - return; - } - - int nTriCount = GetTriCount(); - for ( int iTri = 0; iTri < nTriCount; ++iTri ) - { - m_pTris[iTri].m_uiTags = 0; - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CCoreDispInfo::CreateTris( void ) -{ - // Verify we have the correct data (only build when collision data is built). - if ( !m_pTris ) - { - Assert( m_pTris ); - return; - } - - // Extra sanity check if wanted! - Assert( GetTriCount() == ( m_RenderIndexCount / 3 ) ); - - int nTriCount = GetTriCount(); - for ( int iTri = 0, iRender = 0; iTri < nTriCount; ++iTri, iRender += 3 ) - { - m_pTris[iTri].m_iIndex[0] = m_RenderIndices[iRender]; - m_pTris[iTri].m_iIndex[1] = m_RenderIndices[iRender+1]; - m_pTris[iTri].m_iIndex[2] = m_RenderIndices[iRender+2]; - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -bool CCoreDispInfo::IsTriWalkable( int iTri ) -{ - if ( IsTriTag( iTri, COREDISPTRI_TAG_FORCE_WALKABLE_BIT ) ) - { - return IsTriTag( iTri, COREDISPTRI_TAG_FORCE_WALKABLE_VAL ); - } - - return IsTriTag( iTri, COREDISPTRI_TAG_WALKABLE ); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -bool CCoreDispInfo::IsTriBuildable( int iTri ) -{ - if ( IsTriTag( iTri, COREDISPTRI_TAG_FORCE_BUILDABLE_BIT ) ) - { - return IsTriTag( iTri, COREDISPTRI_TAG_FORCE_BUILDABLE_VAL ); - } - - return IsTriTag( iTri, COREDISPTRI_TAG_BUILDABLE ); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CCoreDispInfo::Position_Update( int iVert, Vector vecPos ) -{ - Vector vSPos, vFlat; - GetFlatVert( iVert, vFlat ); - GetSubdivPosition( iVert, vSPos ); - - Vector vSeg; - vSeg = vecPos - vFlat; - vSeg -= vSPos; - - // Subtract out the elevation. - float elev = GetElevation(); - if( elev != 0.0 ) - { - Vector vNormal; - GetSurface()->GetNormal( vNormal ); - vNormal *= elev; - - vSeg -= vNormal; - } - - float flDistance = VectorNormalize( vSeg ); - - SetFieldVector( iVert, vSeg ); - SetFieldDistance( iVert, flDistance ); -} +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $Workfile: $ +// $Date: $ +// $NoKeywords: $ +//=============================================================================// + +//#include +#include +#include +#include "builddisp.h" +#include "collisionutils.h" +#include "vstdlib/strtools.h" +#include "tier0/dbg.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +bool CalcBarycentricCooefs( Vector const &v0, Vector const &v1, Vector const &v2, + Vector const &pt, float &c0, float &c1, float &c2 ) +{ + Vector vSeg0, vSeg1, vCross; + vSeg0 = v1 - v0; + vSeg1 = v2 - v0; + + // get the area of the triangle + vCross = vSeg0.Cross( vSeg1 ); + float totalArea = vCross.Length() * 0.5f; + float ooTotalArea = totalArea ? 1.0f / totalArea : 0.0f; + + // get the area for cooeficient 0 (pt, v1, v2) + vSeg0 = v1 - pt; + vSeg1 = v2 - pt; + vCross = vSeg0.Cross( vSeg1 ); + float subArea = vCross.Length() * 0.5f; + c0 = subArea * ooTotalArea; + + // get the area for cooeficient 1 (v0, pt, v2) + vSeg0 = v2 - pt; + vSeg1 = v0 - pt; + vCross = vSeg0.Cross( vSeg1 ); + subArea = vCross.Length() * 0.5f; + c1 = subArea * ooTotalArea; + + // get the area for cooeficient 2 (v0, v1, pt) + vSeg0 = v0 - pt; + vSeg1 = v1 - pt; + vCross = vSeg0.Cross( vSeg1 ); + subArea = vCross.Length() * 0.5f; + c2 = subArea * ooTotalArea; + + float cTotal = c0 + c1 + c2; + if ( FloatMakePositive( 1.0f - cTotal ) < 1e-3 ) + return true; + + return false; +} + +// For some reason, the global optimizer screws up the recursion here. disable the global optimizations to fix this. +// IN VC++ 6.0 +#ifdef _MSC_VER +#pragma optimize( "g", off ) +#endif + +// +// Node Functions (friend functions) +// + +//----------------------------------------------------------------------------- +// should make this more programatic and extensible! +//----------------------------------------------------------------------------- +int GetNodeLevel( int index ) +{ + // root + if( index == 0 ) + return 1; + + // [1...4] + if( index < 5 ) + return 2; + + // [5....20] + if( index < 21 ) + return 3; + + // [21....84] + if( index < 85 ) + return 4; + + // error!!! + return -1; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +int GetNodeCount( int power ) +{ + return ( ( 1 << ( power << 1 ) ) / 3 ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +int GetNodeParent( int index ) +{ + // ( index - 1 ) / 4 + return ( ( index - 1 ) >> 2 ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +int GetNodeChild( int power, int index, int direction ) +{ + // ( index * 4 ) + direction + return ( ( index << 2 ) + ( direction - 3 ) ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +int GetNodeMinNodeAtLevel( int level ) +{ + switch( level ) + { + case 1: return 0; + case 2: return 1; + case 3: return 5; + case 4: return 21; + default: return -99999; + } +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void GetComponentsFromNodeIndex( int index, int *x, int *y ) +{ + *x = 0; + *y = 0; + + for( int shift = 0; index != 0; shift++ ) + { + *x |= ( index & 1 ) << shift; + index >>= 1; + + *y |= ( index & 1 ) << shift; + index >>= 1; + } +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +int GetNodeIndexFromComponents( int x, int y ) +{ + int index = 0; + + // Interleave bits from the x and y values to create the index: + + int shift; + for( shift = 0; x != 0; shift += 2, x >>= 1 ) + { + index |= ( x & 1 ) << shift; + } + + for( shift = 1; y != 0; shift += 2, y >>= 1 ) + { + index |= ( y & 1 ) << shift; + } + + return index; +} + + +//----------------------------------------------------------------------------- +// Purpose: This function calculates the neighbor node index given the base +// node and direction of the neighbor node in the tree. +// Input: power - the size in one dimension of the displacement map (2^power + 1 ) +// index - the "base" node index +// direction - the direction of the neighbor { W = 1, N = 2, E = 3, S = 4 } +// Output: returns the index of the neighbor node +//----------------------------------------------------------------------------- +int GetNodeNeighborNode( int power, int index, int direction, int level ) +{ + // adjust the index to range [0...?] + int minNodeIndex = GetNodeMinNodeAtLevel( level ); + + // get node extent (uniform: height = width) + int nodeExtent = ( 1 << ( level - 1 ) ); + + // + // get node's component positions in quad-tree + // + int posX, posY; + GetComponentsFromNodeIndex( ( index - minNodeIndex ), &posX, &posY ); + + // + // find the neighbor in the "direction" + // + switch( direction ) + { + case CCoreDispInfo::WEST: + { + if( ( posX - 1 ) < 0 ) + { + return -( CCoreDispInfo::WEST + 1 ); + } + else + { + return ( GetNodeIndexFromComponents( ( posX - 1 ), posY ) + minNodeIndex ); + } + } + case CCoreDispInfo::NORTH: + { + if( ( posY + 1 ) == nodeExtent ) + { + return -( CCoreDispInfo::NORTH + 1 ); + } + else + { + return ( GetNodeIndexFromComponents( posX, ( posY + 1 ) ) + minNodeIndex ); + } + } + case CCoreDispInfo::EAST: + { + if( ( posX + 1 ) == nodeExtent ) + { + return -( CCoreDispInfo::EAST + 1 ); + } + else + { + return ( GetNodeIndexFromComponents( ( posX + 1 ), posY ) + minNodeIndex ); + } + } + case CCoreDispInfo::SOUTH: + { + if( ( posY - 1 ) < 0 ) + { + return -( CCoreDispInfo::SOUTH + 1 ); + } + else + { + return ( GetNodeIndexFromComponents( posX, ( posY - 1 ) ) + minNodeIndex ); + } + } + default: + { + return -99999; + } + } +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +int GetNodeNeighborNodeFromNeighborSurf( int power, int index, int direction, int level, int neighborOrient ) +{ + // adjust the index to range [0...?] + int minNodeIndex = GetNodeMinNodeAtLevel( level ); + + // get node extent (uniform: height = width) + int nodeExtent = ( 1 << ( level - 1 ) ); + + // + // get node's component positions in quad-tree + // + int posX, posY; + GetComponentsFromNodeIndex( ( index - minNodeIndex ), &posX, &posY ); + + switch( direction ) + { + case CCoreDispInfo::WEST: + { + switch( neighborOrient ) + { + case CCoreDispInfo::WEST: return -( ( GetNodeIndexFromComponents( posX, ( ( nodeExtent - 1 ) - posY ) ) ) + minNodeIndex ); + case CCoreDispInfo::NORTH: return -( ( GetNodeIndexFromComponents( ( nodeExtent - 1 ) - posY, ( nodeExtent - 1 ) ) ) + minNodeIndex ); + case CCoreDispInfo::EAST: return -( ( GetNodeIndexFromComponents( ( nodeExtent - 1 ), posY ) ) + minNodeIndex ); + case CCoreDispInfo::SOUTH: return -( ( GetNodeIndexFromComponents( posY, posX ) ) + minNodeIndex ); + default: return -99999; + } + } + case CCoreDispInfo::NORTH: + { + switch( neighborOrient ) + { + case CCoreDispInfo::WEST: return -( ( GetNodeIndexFromComponents( ( ( nodeExtent - 1 ) - posY ), ( ( nodeExtent - 1 ) - posX ) ) ) + minNodeIndex ); + case CCoreDispInfo::NORTH: return -( ( GetNodeIndexFromComponents( ( ( nodeExtent - 1 ) - posX ), posY ) ) + minNodeIndex ); + case CCoreDispInfo::EAST: return -( ( GetNodeIndexFromComponents( posY, posX ) ) + minNodeIndex ); + case CCoreDispInfo::SOUTH: return -( ( GetNodeIndexFromComponents( posX, ( ( nodeExtent - 1 ) - posY ) ) ) + minNodeIndex ); + default: return -99999; + } + } + case CCoreDispInfo::EAST: + { + switch( neighborOrient ) + { + case CCoreDispInfo::WEST: return -( ( GetNodeIndexFromComponents( ( ( nodeExtent - 1 ) - posX ), posY ) ) + minNodeIndex ); + case CCoreDispInfo::NORTH: return -( ( GetNodeIndexFromComponents( posY, posX ) ) + minNodeIndex ); + case CCoreDispInfo::EAST: return -( ( GetNodeIndexFromComponents( posX, ( ( nodeExtent - 1 ) - posY ) ) ) + minNodeIndex ); + case CCoreDispInfo::SOUTH: return -( ( GetNodeIndexFromComponents( ( ( nodeExtent - 1 ) - posY ), ( ( nodeExtent - 1 ) - posX ) ) ) + minNodeIndex ); + default: return -99999; + } + } + case CCoreDispInfo::SOUTH: + { + switch( neighborOrient ) + { + case CCoreDispInfo::WEST: return -( ( GetNodeIndexFromComponents( posY, posX ) ) + minNodeIndex ); + case CCoreDispInfo::NORTH: return -( ( GetNodeIndexFromComponents( posX, ( nodeExtent - 1 ) ) ) + minNodeIndex ); + case CCoreDispInfo::EAST: return -( ( GetNodeIndexFromComponents( ( nodeExtent - 1 ), ( ( nodeExtent - 1 ) - posX ) ) ) + minNodeIndex ); + case CCoreDispInfo::SOUTH: return -( ( GetNodeIndexFromComponents( ( ( nodeExtent - 1 ) - posX ), posY ) ) + minNodeIndex ); + default: return -99999; + } + } + default: + { + return -99999; + } + } +} + + +CCoreDispSurface::CCoreDispSurface() +{ + Init(); +} + + +//============================================================================= +// +// CDispSurface Functions +// + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CCoreDispSurface::Init( void ) +{ + m_Index = -1; + + m_PointCount = 0; + int i; + for( i = 0; i < QUAD_POINT_COUNT; i++ ) + { + VectorClear( m_Points[i] ); + VectorClear( m_Normals[i] ); + Vector2DClear( m_TexCoords[i] ); + + for( int j = 0; j < NUM_BUMP_VECTS+1; j++ ) + { + Vector2DClear( m_LuxelCoords[i][j] ); + } + + m_Alphas[i] = 1.0f; + } + + m_PointStartIndex = -1; + VectorClear( m_PointStart ); + VectorClear( sAxis ); + VectorClear( tAxis ); + + for( i = 0; i < 4; i++ ) + { + m_EdgeNeighbors[i].SetInvalid(); + m_CornerNeighbors[i].SetInvalid(); + } + + m_Flags = 0; + m_Contents = 0; +} + + +void CCoreDispSurface::SetNeighborData( const CDispNeighbor edgeNeighbors[4], const CDispCornerNeighbors cornerNeighbors[4] ) +{ + for ( int i=0; i < 4; i++ ) + { + m_EdgeNeighbors[i] = edgeNeighbors[i]; + m_CornerNeighbors[i] = cornerNeighbors[i]; + } +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CCoreDispSurface::GeneratePointStartIndexFromMappingAxes( Vector const &sAxis, Vector const &tAxis ) +{ + if( m_PointStartIndex != -1 ) + return; + + int numIndices = 0; + int indices[4]; + int offsetIndex; + + // + // project all points on to the v-axis first and find the minimum + // + float minValue = DotProduct( tAxis, m_Points[0] ); + indices[numIndices] = 0; + numIndices++; + + int i; + for( i = 1; i < m_PointCount; i++ ) + { + float value = DotProduct( tAxis, m_Points[i] ); + float delta = ( value - minValue ); + delta = FloatMakePositive( delta ); + if( delta < 0.1 ) + { + indices[numIndices] = i; + numIndices++; + } + else if( value < minValue ) + { + minValue = value; + indices[0] = i; + numIndices = 1; + } + } + + // + // break ties with the u-axis projection + // + minValue = DotProduct( sAxis, m_Points[indices[0]] ); + offsetIndex = indices[0]; + + for( i = 1; i < numIndices; i++ ) + { + float value = DotProduct( sAxis, m_Points[indices[i]] ); + if( ( value < minValue ) ) + { + minValue = value; + offsetIndex = indices[i]; + } + } + + m_PointStartIndex = offsetIndex; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +int CCoreDispSurface::GenerateSurfPointStartIndex( void ) +{ + // + // get the minimum surface component values + // + Vector bMin; + VectorFill( bMin, 99999.0f ); + + int i; + for( i = 0; i < QUAD_POINT_COUNT; i++ ) + { + for( int j = 0; j < 3; j++ ) + { + if( m_Points[i][j] < bMin[j] ) + { + bMin[j] = m_Points[i][j]; + } + } + } + + // + // find the point closest to the minimum, that is the start point + // + int minIndex = -1; + float minDistance = 999999999.0f; + for( i = 0; i < QUAD_POINT_COUNT; i++ ) + { + Vector segment; + segment = m_Points[i] - bMin; + float distanceSq = segment.LengthSqr(); + if( distanceSq < minDistance ) + { + minDistance = distanceSq; + minIndex = i; + } + } + + m_PointStartIndex = minIndex; + + return minIndex; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +int CCoreDispSurface::FindSurfPointStartIndex( void ) +{ + if( m_PointStartIndex != -1 ) + return m_PointStartIndex; + + int minIndex = -1; + float minDistance = 999999999.0f; + + for( int i = 0; i < QUAD_POINT_COUNT; i++ ) + { + Vector segment; + VectorSubtract( m_PointStart, m_Points[i], segment ); + float distanceSq = segment.LengthSqr(); + if( distanceSq < minDistance ) + { + minDistance = distanceSq; + minIndex = i; + } + } + + m_PointStartIndex = minIndex; + + return minIndex; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CCoreDispSurface::AdjustSurfPointData( void ) +{ + Vector tmpPoints[4]; + Vector tmpNormals[4]; + Vector2D tmpTexCoords[4]; + Vector2D tmpLuxelCoords[4][4]; + float tmpAlphas[4]; + + int i; + for( i = 0; i < QUAD_POINT_COUNT; i++ ) + { + VectorCopy( m_Points[i], tmpPoints[i] ); + VectorCopy( m_Normals[i], tmpNormals[i] ); + Vector2DCopy( m_TexCoords[i], tmpTexCoords[i] ); + + for( int j = 0; j < ( NUM_BUMP_VECTS + 1 ); j++ ) + { + Vector2DCopy( m_LuxelCoords[j][i], tmpLuxelCoords[j][i] ); + } + + tmpAlphas[i] = m_Alphas[i]; + } + + for( i = 0; i < QUAD_POINT_COUNT; i++ ) + { + VectorCopy( tmpPoints[(i+m_PointStartIndex)%4], m_Points[i] ); + VectorCopy( tmpNormals[(i+m_PointStartIndex)%4], m_Normals[i] ); + Vector2DCopy( tmpTexCoords[(i+m_PointStartIndex)%4], m_TexCoords[i] ); + +// for( int j = 0; j < ( NUM_BUMP_VECTS + 1 ); j++ ) +// { +// Vector2DCopy( tmpLuxelCoords[j][(i+m_PointStartIndex)%4], m_LuxelCoords[j][i] ); +// } + + m_Alphas[i] = tmpAlphas[i]; + } +} + + +//============================================================================= +// +// CDispNode Functions +// + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CCoreDispNode::Init( void ) +{ + VectorClear( m_BBox[0] ); + VectorClear( m_BBox[1] ); + + m_ErrorTerm = 0.0f; + + m_VertIndex = -1; + + int j; + for( j = 0; j < MAX_NEIGHBOR_NODE_COUNT; j++ ) + { + m_NeighborVertIndices[j] = -1; + } + + for( j = 0; j < MAX_SURF_AT_NODE_COUNT; j++ ) + { + VectorClear( m_SurfBBoxes[j][0] ); + VectorClear( m_SurfBBoxes[j][1] ); + VectorClear( m_SurfPlanes[j].normal ); + m_SurfPlanes[j].dist = 0.0f; + } +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void GetDispNodeTriVerts( CCoreDispInfo *pDisp, int nodeIndex, int triIndex, Vector& v1, Vector& v2, Vector& v3 ) +{ + // get the node + CCoreDispNode *pNode = pDisp->GetNode( nodeIndex ); + + switch( triIndex ) + { + case 0: + { + pDisp->GetVert( pNode->GetNeighborVertIndex( 4 ), v1 ); + pDisp->GetVert( pNode->GetNeighborVertIndex( 0 ), v2 ); + pDisp->GetVert( pNode->GetNeighborVertIndex( 3 ), v3 ); + return; + } + case 1: + { + pDisp->GetVert( pNode->GetNeighborVertIndex( 3 ), v1 ); + pDisp->GetVert( pNode->GetNeighborVertIndex( 0 ), v2 ); + pDisp->GetVert( pNode->GetCenterVertIndex(), v3 ); + return; + } + case 2: + { + pDisp->GetVert( pNode->GetNeighborVertIndex( 3 ), v1 ); + pDisp->GetVert( pNode->GetCenterVertIndex(), v2 ); + pDisp->GetVert( pNode->GetNeighborVertIndex( 5 ), v3 ); + return; + } + case 3: + { + pDisp->GetVert( pNode->GetNeighborVertIndex( 5 ), v1 ); + pDisp->GetVert( pNode->GetCenterVertIndex(), v2 ); + pDisp->GetVert( pNode->GetNeighborVertIndex( 2 ), v3 ); + return; + } + case 4: + { + pDisp->GetVert( pNode->GetNeighborVertIndex( 0 ), v1 ); + pDisp->GetVert( pNode->GetNeighborVertIndex( 6 ), v2 ); + pDisp->GetVert( pNode->GetCenterVertIndex(), v3 ); + return; + } + case 5: + { + pDisp->GetVert( pNode->GetCenterVertIndex(), v1 ); + pDisp->GetVert( pNode->GetNeighborVertIndex( 6 ), v2 ); + pDisp->GetVert( pNode->GetNeighborVertIndex( 1 ), v3 ); + return; + } + case 6: + { + pDisp->GetVert( pNode->GetCenterVertIndex(), v1 ); + pDisp->GetVert( pNode->GetNeighborVertIndex( 1 ), v2 ); + pDisp->GetVert( pNode->GetNeighborVertIndex( 2 ), v3 ); + return; + } + case 7: + { + pDisp->GetVert( pNode->GetNeighborVertIndex( 2 ), v1 ); + pDisp->GetVert( pNode->GetNeighborVertIndex( 1 ), v2 ); + pDisp->GetVert( pNode->GetNeighborVertIndex( 7 ), v3 ); + return; + } + default: { return; } + } +} + + +//============================================================================= +// +// CCoreDispInfo Functions +// + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +CCoreDispInfo::CCoreDispInfo() +{ + m_pVerts = NULL; + m_RenderIndices = NULL; + m_Nodes = NULL; + m_pTris = NULL; + + // initialize the base surface data + m_Surf.Init(); + + // + // initialize the disp info + // + m_Power = 0; + m_Elevation = 0.0f; + m_RenderIndexCount = 0; + m_RenderCounter = 0; + m_bTouched = false; + + m_pNext = NULL; + + m_ppListBase = NULL; + m_ListSize = 0; + m_nListIndex = -1; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +CCoreDispInfo::~CCoreDispInfo() +{ + if (m_pVerts) + delete [] m_pVerts; + if (m_RenderIndices) + delete [] m_RenderIndices; + if (m_Nodes) + delete [] m_Nodes; + if (m_pTris) + delete [] m_pTris; +} + + +#if 0 +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CCoreDispInfo::InitSurf( int parentIndex, Vector points[4], Vector normals[4], + Vector2D texCoords[4], Vector2D lightCoords[4][4], int contents, int flags, + bool bGenerateSurfPointStart, Vector& startPoint, + bool bHasMappingAxes, Vector& uAxis, Vector& vAxis ) +{ + // save the "parent" index + m_Surf.m_Index = parentIndex; + + // + // save the surface points and point normals, texture coordinates, and + // lightmap coordinates + // + m_Surf.m_PointCount = CSurface::QUAD_POINT_COUNT; + for( int i = 0; i < CSurface::QUAD_POINT_COUNT; i++ ) + { + VectorCopy( points[i], m_Surf.m_Points[i] ); + + if( normals ) + { + VectorCopy( normals[i], m_Surf.m_pVerts[i].m_Normal ); + } + + if( texCoords ) + { + Vector2DCopy( texCoords[i], m_Surf.m_TexCoords[i] ); + } + + if( lightCoords ) + { + Assert( NUM_BUMP_VECTS == 3 ); + Vector2DCopy( lightCoords[0][i], m_Surf.m_LightCoords[i][0] ); + Vector2DCopy( lightCoords[1][i], m_Surf.m_LightCoords[i][1] ); + Vector2DCopy( lightCoords[2][i], m_Surf.m_LightCoords[i][2] ); + Vector2DCopy( lightCoords[3][i], m_Surf.m_LightCoords[i][3] ); + } + } + + // save the starting point + if( startPoint ) + { + VectorCopy( startPoint, m_Surf.m_PointStart ); + } + + // + // save the surface contents and flags + // + m_Contents = contents; + m_Flags = flags; + + // + // adjust surface points, texture coordinates, etc.... + // + if( bHasMappingAxes && ( m_Surf.m_PointStartIndex == -1 ) ) + { + GeneratePointStartIndexFromMappingAxes( uAxis, vAxis ); + } + else + { + // + // adjust the surf data + // + if( bGenerateSurfPointStart ) + { + GenerateSurfPointStartIndex(); + } + else + { + FindSurfPointStartIndex(); + } + } + + AdjustSurfPointData(); +} +#endif + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CCoreDispInfo::InitDispInfo( int power, int minTess, float smoothingAngle, + float *alphas, Vector *dispVectorField, float *dispDistances ) +{ + Assert( power >= MIN_MAP_DISP_POWER && power <= MAX_MAP_DISP_POWER ); + + // + // general displacement data + // + m_Power = power; + + if ( ( minTess & 0x80000000 ) != 0 ) + { + // If the high bit is set, this represents FLAGS (SURF_NOPHYSICS_COLL, etc.) flags. + int nFlags = minTess; + nFlags &= ~0x80000000; + GetSurface()->SetFlags( nFlags ); + } + + // Allocate + initialize verts + int size = GetSize(); + m_pVerts = new CoreDispVert_t[size]; + + int nIndexCount = size * 2 * 3; + m_RenderIndices = new unsigned short[nIndexCount]; + + int nNodeCount = GetNodeCount(power); + m_Nodes = new CCoreDispNode[nNodeCount]; + + int i; + for( i = 0; i < size; i++ ) + { + m_pVerts[i].m_FieldVector.Init(); + m_pVerts[i].m_SubdivPos.Init(); + m_pVerts[i].m_SubdivNormal.Init(); + + m_pVerts[i].m_FieldDistance = 0.0f; + + m_pVerts[i].m_Vert.Init(); + m_pVerts[i].m_FlatVert.Init(); + m_pVerts[i].m_Normal.Init(); + m_pVerts[i].m_TangentS.Init(); + m_pVerts[i].m_TangentT.Init(); + m_pVerts[i].m_TexCoord.Init(); + + for( int j = 0; j < ( NUM_BUMP_VECTS + 1 ); j++ ) + { + m_pVerts[i].m_LuxelCoords[j].Init(); + } + + m_pVerts[i].m_Alpha = 0.0f; + } + + for( i = 0; i < nIndexCount; i++ ) + { + m_RenderIndices[i] = 0; + } + + for( i = 0; i < nNodeCount; i++ ) + { + m_Nodes[i].Init(); + } + + // + // save the displacement vector field and distances within the field + // offset have been combined with fieldvectors at this point!!! + // + if (alphas && dispVectorField && dispDistances) + { + for( i = 0; i < size; i++ ) + { + VectorCopy( dispVectorField[i], m_pVerts[i].m_FieldVector ); + m_pVerts[i].m_FieldDistance = dispDistances[i]; + m_pVerts[i].m_Alpha = alphas[i]; + } + } + + // Init triangle information. + int nTriCount = GetTriCount(); + if ( nTriCount != 0 ) + { + m_pTris = new CoreDispTri_t[nTriCount]; + if ( m_pTris ) + { + InitTris(); + } + } +} + + +void CCoreDispInfo::InitDispInfo( int power, int minTess, float smoothingAngle, const CDispVert *pVerts, + const CDispTri *pTris ) +{ + Vector vectors[MAX_DISPVERTS]; + float dists[MAX_DISPVERTS]; + float alphas[MAX_DISPVERTS]; + + int nVerts = NUM_DISP_POWER_VERTS( power ); + for ( int i=0; i < nVerts; i++ ) + { + vectors[i] = pVerts[i].m_vVector; + dists[i] = pVerts[i].m_flDist; + alphas[i] = pVerts[i].m_flAlpha; + } + + InitDispInfo( power, minTess, smoothingAngle, alphas, vectors, dists ); + + int nTris = NUM_DISP_POWER_TRIS( power ); + for ( int iTri = 0; iTri < nTris; ++iTri ) + { + m_pTris[iTri].m_uiTags = pTris[iTri].m_uiTags; + } +} + + +void CCoreDispInfo::SetDispUtilsHelperInfo( CCoreDispInfo **ppListBase, int listSize ) +{ + m_ppListBase = ppListBase; + m_ListSize = listSize; +} + +const CPowerInfo* CCoreDispInfo::GetPowerInfo() const +{ + return ::GetPowerInfo( GetPower() ); +} + +CDispNeighbor* CCoreDispInfo::GetEdgeNeighbor( int index ) +{ + return GetSurface()->GetEdgeNeighbor( index ); +} + +CDispCornerNeighbors* CCoreDispInfo::GetCornerNeighbors( int index ) +{ + return GetSurface()->GetCornerNeighbors( index ); +} + +CDispUtilsHelper* CCoreDispInfo::GetDispUtilsByIndex( int index ) +{ + Assert( m_ppListBase ); + return index == 0xFFFF ? 0 : m_ppListBase[index]; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CCoreDispInfo::BuildTriTLtoBR( int ndx ) +{ + // get width and height of displacement maps + int nWidth = ( ( 1 << m_Power ) + 1 ); + + m_RenderIndices[m_RenderIndexCount] = ndx; + m_RenderIndices[m_RenderIndexCount+1] = ndx + nWidth; + m_RenderIndices[m_RenderIndexCount+2] = ndx + 1; + m_RenderIndexCount += 3; + + m_RenderIndices[m_RenderIndexCount] = ndx + 1; + m_RenderIndices[m_RenderIndexCount+1] = ndx + nWidth; + m_RenderIndices[m_RenderIndexCount+2] = ndx + nWidth + 1; + m_RenderIndexCount += 3; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CCoreDispInfo::BuildTriBLtoTR( int ndx ) +{ + // get width and height of displacement maps + int nWidth = ( ( 1 << m_Power ) + 1 ); + + m_RenderIndices[m_RenderIndexCount] = ndx; + m_RenderIndices[m_RenderIndexCount+1] = ndx + nWidth; + m_RenderIndices[m_RenderIndexCount+2] = ndx + nWidth + 1; + m_RenderIndexCount += 3; + + m_RenderIndices[m_RenderIndexCount] = ndx; + m_RenderIndices[m_RenderIndexCount+1] = ndx + nWidth + 1; + m_RenderIndices[m_RenderIndexCount+2] = ndx + 1; + m_RenderIndexCount += 3; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CCoreDispInfo::GenerateCollisionSurface( void ) +{ + // get width and height of displacement maps + int nWidth = ( ( 1 << m_Power ) + 1 ); + int nHeight = ( ( 1 << m_Power ) + 1 ); + + // + // generate a fan tesselated (at quadtree node) rendering index list + // + m_RenderIndexCount = 0; + for ( int iV = 0; iV < ( nHeight - 1 ); iV++ ) + { + for ( int iU = 0; iU < ( nWidth - 1 ); iU++ ) + { + int ndx = ( iV * nWidth ) + iU; + + // test whether or not the index is odd + bool bOdd = ( ( ndx %2 ) == 1 ); + + // Top Left to Bottom Right + if( bOdd ) + { + BuildTriTLtoBR( ndx ); + } + // Bottom Left to Top Right + else + { + BuildTriBLtoTR( ndx ); + } + } + } +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CCoreDispInfo::GenerateCollisionData( void ) +{ + GenerateCollisionSurface(); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CCoreDispInfo::CalcTriSurfPlanes( int nodeIndex, int indices[8][3] ) +{ + // + // calculate plane info for each face + // + for( int i = 0; i < 8; i++ ) + { + Vector v[3]; + VectorCopy( m_pVerts[indices[i][0]].m_Vert, v[0] ); + VectorCopy( m_pVerts[indices[i][1]].m_Vert, v[1] ); + VectorCopy( m_pVerts[indices[i][2]].m_Vert, v[2] ); + + Vector seg[2]; + VectorSubtract( v[1], v[0], seg[0] ); + VectorSubtract( v[2], v[0], seg[1] ); + + Vector normal; + CrossProduct( seg[1], seg[0], normal ); + VectorNormalize( normal ); + float dist = DotProduct( v[0], normal ); + + m_Nodes[nodeIndex].SetTriPlane( i, normal, dist ); + } +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CCoreDispInfo::CalcRayBoundingBoxes( int nodeIndex, int indices[8][3] ) +{ + Vector triMin, triMax; + + for( int i = 0; i < 4; i++ ) + { + triMin[0] = triMax[0] = m_pVerts[indices[(i*2)][0]].m_Vert[0]; + triMin[1] = triMax[1] = m_pVerts[indices[(i*2)][0]].m_Vert[1]; + triMin[2] = triMax[2] = m_pVerts[indices[(i*2)][0]].m_Vert[2]; + + for( int j = 0; j < 3; j++ ) + { + // + // minimum + // + if( triMin[0] > m_pVerts[indices[(i*2)][j]].m_Vert[0] ) + triMin[0] = m_pVerts[indices[(i*2)][j]].m_Vert[0]; + if( triMin[0] > m_pVerts[indices[(i*2+1)][j]].m_Vert[0] ) + triMin[0] = m_pVerts[indices[(i*2+1)][j]].m_Vert[0]; + + if( triMin[1] > m_pVerts[indices[(i*2)][j]].m_Vert[1] ) + triMin[1] = m_pVerts[indices[(i*2)][j]].m_Vert[1]; + if( triMin[1] > m_pVerts[indices[(i*2+1)][j]].m_Vert[1] ) + triMin[1] = m_pVerts[indices[(i*2+1)][j]].m_Vert[1]; + + if( triMin[2] > m_pVerts[indices[(i*2)][j]].m_Vert[2] ) + triMin[2] = m_pVerts[indices[(i*2)][j]].m_Vert[2]; + if( triMin[2] > m_pVerts[indices[(i*2+1)][j]].m_Vert[2] ) + triMin[2] = m_pVerts[indices[(i*2+1)][j]].m_Vert[2]; + + // + // maximum + // + if( triMax[0] < m_pVerts[indices[(i*2)][j]].m_Vert[0] ) + triMax[0] = m_pVerts[indices[(i*2)][j]].m_Vert[0]; + if( triMax[0] < m_pVerts[indices[(i*2+1)][j]].m_Vert[0] ) + triMax[0] = m_pVerts[indices[(i*2+1)][j]].m_Vert[0]; + + if( triMax[1] < m_pVerts[indices[(i*2)][j]].m_Vert[1] ) + triMax[1] = m_pVerts[indices[(i*2)][j]].m_Vert[1]; + if( triMax[1] < m_pVerts[indices[(i*2+1)][j]].m_Vert[1] ) + triMax[1] = m_pVerts[indices[(i*2+1)][j]].m_Vert[1]; + + if( triMax[2] < m_pVerts[indices[(i*2)][j]].m_Vert[2] ) + triMax[2] = m_pVerts[indices[(i*2)][j]].m_Vert[2]; + if( triMax[2] < m_pVerts[indices[(i*2+1)][j]].m_Vert[2] ) + triMax[2] = m_pVerts[indices[(i*2+1)][j]].m_Vert[2]; + } + + m_Nodes[nodeIndex].SetRayBoundingBox( i, triMin, triMax ); + } +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CCoreDispInfo::CalcTriSurfBoundingBoxes( int nodeIndex, int indices[8][3] ) +{ + Vector triMin, triMax; + + for( int i = 0; i < 8; i++ ) + { + m_Nodes[nodeIndex].GetTriBoundingBox( i, triMin, triMax ); + + for( int j = 0; j < 3; j++ ) + { + // + // minimum + // + if( triMin[0] > m_pVerts[indices[i][j]].m_Vert[0] ) + triMin[0] = m_pVerts[indices[i][j]].m_Vert[0]; + + if( triMin[1] > m_pVerts[indices[i][j]].m_Vert[1] ) + triMin[1] = m_pVerts[indices[i][j]].m_Vert[1]; + + if( triMin[2] > m_pVerts[indices[i][j]].m_Vert[2] ) + triMin[2] = m_pVerts[indices[i][j]].m_Vert[2]; + + // + // maximum + // + if( triMax[0] < m_pVerts[indices[i][j]].m_Vert[0] ) + triMax[0] = m_pVerts[indices[i][j]].m_Vert[0]; + + if( triMax[1] < m_pVerts[indices[i][j]].m_Vert[1] ) + triMax[1] = m_pVerts[indices[i][j]].m_Vert[1]; + + if( triMax[2] < m_pVerts[indices[i][j]].m_Vert[2] ) + triMax[2] = m_pVerts[indices[i][j]].m_Vert[2]; + } + + m_Nodes[nodeIndex].SetTriBoundingBox( i, triMin, triMax ); + } +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CCoreDispInfo::CalcTriSurfIndices( int nodeIndex, int indices[8][3] ) +{ + indices[0][0] = m_Nodes[nodeIndex].GetNeighborVertIndex( 4 ); + indices[0][1] = m_Nodes[nodeIndex].GetNeighborVertIndex( 0 ); + indices[0][2] = m_Nodes[nodeIndex].GetNeighborVertIndex( 3 ); + + indices[1][0] = m_Nodes[nodeIndex].GetNeighborVertIndex( 3 ); + indices[1][1] = m_Nodes[nodeIndex].GetNeighborVertIndex( 0 ); + indices[1][2] = m_Nodes[nodeIndex].GetCenterVertIndex(); + + indices[2][0] = m_Nodes[nodeIndex].GetNeighborVertIndex( 3 ); + indices[2][1] = m_Nodes[nodeIndex].GetCenterVertIndex(); + indices[2][2] = m_Nodes[nodeIndex].GetNeighborVertIndex( 5 ); + + indices[3][0] = m_Nodes[nodeIndex].GetNeighborVertIndex( 5 ); + indices[3][1] = m_Nodes[nodeIndex].GetCenterVertIndex(); + indices[3][2] = m_Nodes[nodeIndex].GetNeighborVertIndex( 2 ); + + indices[4][0] = m_Nodes[nodeIndex].GetNeighborVertIndex( 0 ); + indices[4][1] = m_Nodes[nodeIndex].GetNeighborVertIndex( 6 ); + indices[4][2] = m_Nodes[nodeIndex].GetCenterVertIndex(); + + indices[5][0] = m_Nodes[nodeIndex].GetCenterVertIndex(); + indices[5][1] = m_Nodes[nodeIndex].GetNeighborVertIndex( 6 ); + indices[5][2] = m_Nodes[nodeIndex].GetNeighborVertIndex( 1 ); + + indices[6][0] = m_Nodes[nodeIndex].GetCenterVertIndex(); + indices[6][1] = m_Nodes[nodeIndex].GetNeighborVertIndex( 1 ); + indices[6][2] = m_Nodes[nodeIndex].GetNeighborVertIndex( 2 ); + + indices[7][0] = m_Nodes[nodeIndex].GetNeighborVertIndex( 2 ); + indices[7][1] = m_Nodes[nodeIndex].GetNeighborVertIndex( 1 ); + indices[7][2] = m_Nodes[nodeIndex].GetNeighborVertIndex( 7 ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CCoreDispInfo::CalcTriSurfInfoAtNode( int nodeIndex ) +{ + int indices[8][3]; + + CalcTriSurfIndices( nodeIndex, indices ); + CalcTriSurfBoundingBoxes( nodeIndex, indices ); + CalcRayBoundingBoxes( nodeIndex, indices ); + CalcTriSurfPlanes( nodeIndex, indices ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CCoreDispInfo::CalcMinMaxBoundingBoxAtNode( int nodeIndex, Vector& bMin, Vector& bMax ) +{ + // get the child node index + int childNodeIndex = GetNodeChild( m_Power, nodeIndex, 4 ); + + // get initial bounding box values + m_Nodes[childNodeIndex].GetBoundingBox( bMin, bMax ); + + Vector nodeMin, nodeMax; + for( int i = 1, j = 5; i < 4; i++, j++ ) + { + // + // get the child node bounding box + // + childNodeIndex = GetNodeChild( m_Power, nodeIndex, j ); + m_Nodes[childNodeIndex].GetBoundingBox( nodeMin, nodeMax ); + + // minimum + if( bMin[0] > nodeMin[0] ) + bMin[0] = nodeMin[0]; + + if( bMin[1] > nodeMin[1] ) + bMin[1] = nodeMin[1]; + + if( bMin[2] > nodeMin[2] ) + bMin[2] = nodeMin[2]; + + // maximum + if( bMax[0] < nodeMax[0] ) + bMax[0] = nodeMax[0]; + + if( bMax[1] < nodeMax[1] ) + bMax[1] = nodeMax[1]; + + if( bMax[2] < nodeMax[2] ) + bMax[2] = nodeMax[2]; + } +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CCoreDispInfo::CalcBoundingBoxAtNode( int nodeIndex ) +{ + Vector bMin, bMax; + + // + // initialize the minimum and maximum values for the bounding box + // + int level = GetNodeLevel( nodeIndex ); + + int vertIndex = m_Nodes[nodeIndex].GetCenterVertIndex(); + if( level == m_Power ) + { + VectorCopy( m_pVerts[vertIndex].m_Vert, bMin ); + VectorCopy( m_pVerts[vertIndex].m_Vert, bMax ); + } + else + { + CalcMinMaxBoundingBoxAtNode( nodeIndex, bMin, bMax ); + + if( bMin[0] > m_pVerts[vertIndex].m_Vert[0] ) + bMin[0] = m_pVerts[vertIndex].m_Vert[0]; + + if( bMin[1] > m_pVerts[vertIndex].m_Vert[1] ) + bMin[1] = m_pVerts[vertIndex].m_Vert[1]; + + if( bMin[2] > m_pVerts[vertIndex].m_Vert[2] ) + bMin[2] = m_pVerts[vertIndex].m_Vert[2]; + + + if( bMax[0] < m_pVerts[vertIndex].m_Vert[0] ) + bMax[0] = m_pVerts[vertIndex].m_Vert[0]; + + if( bMax[1] < m_pVerts[vertIndex].m_Vert[1] ) + bMax[1] = m_pVerts[vertIndex].m_Vert[1]; + + if( bMax[2] < m_pVerts[vertIndex].m_Vert[2] ) + bMax[2] = m_pVerts[vertIndex].m_Vert[2]; + } + + for( int i = 0; i < 8; i++ ) + { + int neighborVertIndex = m_Nodes[nodeIndex].GetNeighborVertIndex( i ); + + // + // minimum + // + if( bMin[0] > m_pVerts[neighborVertIndex].m_Vert[0] ) + bMin[0] = m_pVerts[neighborVertIndex].m_Vert[0]; + + if( bMin[1] > m_pVerts[neighborVertIndex].m_Vert[1] ) + bMin[1] = m_pVerts[neighborVertIndex].m_Vert[1]; + + if( bMin[2] > m_pVerts[neighborVertIndex].m_Vert[2] ) + bMin[2] = m_pVerts[neighborVertIndex].m_Vert[2]; + + // + // maximum + // + if( bMax[0] < m_pVerts[neighborVertIndex].m_Vert[0] ) + bMax[0] = m_pVerts[neighborVertIndex].m_Vert[0]; + + if( bMax[1] < m_pVerts[neighborVertIndex].m_Vert[1] ) + bMax[1] = m_pVerts[neighborVertIndex].m_Vert[1]; + + if( bMax[2] < m_pVerts[neighborVertIndex].m_Vert[2] ) + bMax[2] = m_pVerts[neighborVertIndex].m_Vert[2]; + } + + m_Nodes[nodeIndex].SetBoundingBox( bMin, bMax ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +float CCoreDispInfo::GetMaxErrorFromChildren( int nodeIndex, int level ) +{ + // + // check for children nodes + // + if( level == m_Power ) + return 0.0f; + + // + // get the child's error term and save the greatest error -- SW, SE, NW, NE + // + float errorTerm = 0.0f; + for( int i = 4; i < 8; i++ ) + { + int childIndex = GetNodeChild( m_Power, nodeIndex, i ); + + float nodeErrorTerm = m_Nodes[childIndex].GetErrorTerm(); + if( errorTerm < nodeErrorTerm ) + { + errorTerm = nodeErrorTerm; + } + } + + return errorTerm; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CCoreDispInfo::CalcErrorTermAtNode( int nodeIndex, int level ) +{ + if( level == m_Power ) + return; + + // + // get the vertex indices + // + int neighborVertIndices[9]; + for( int i = 0; i < 8; i++ ) + { + neighborVertIndices[i] = m_Nodes[nodeIndex].GetNeighborVertIndex( i ); + } + neighborVertIndices[8] = m_Nodes[nodeIndex].GetCenterVertIndex(); + + + // + // calculate the error terms + // + Vector segment; + Vector v; + + VectorAdd( m_pVerts[neighborVertIndices[5]].m_Vert, m_pVerts[neighborVertIndices[4]].m_Vert, v ); + VectorScale( v, 0.5f, v ); + VectorSubtract( m_pVerts[neighborVertIndices[0]].m_Vert, v, segment ); + float errorTerm = ( float )VectorLength( segment ); + + VectorAdd( m_pVerts[neighborVertIndices[5]].m_Vert, m_pVerts[neighborVertIndices[6]].m_Vert, v ); + VectorScale( v, 0.5f, v ); + VectorSubtract( m_pVerts[neighborVertIndices[1]].m_Vert, v, segment ); + if( errorTerm < ( float )VectorLength( segment ) ) + errorTerm = ( float )VectorLength( segment ); + + VectorAdd( m_pVerts[neighborVertIndices[6]].m_Vert, m_pVerts[neighborVertIndices[7]].m_Vert, v ); + VectorScale( v, 0.5f, v ); + VectorSubtract( m_pVerts[neighborVertIndices[2]].m_Vert, v, segment ); + if( errorTerm < ( float )VectorLength( segment ) ) + errorTerm = ( float )VectorLength( segment ); + + VectorAdd( m_pVerts[neighborVertIndices[7]].m_Vert, m_pVerts[neighborVertIndices[4]].m_Vert, v ); + VectorScale( v, 0.5f, v ); + VectorSubtract( m_pVerts[neighborVertIndices[3]].m_Vert, v, segment ); + if( errorTerm < ( float )VectorLength( segment ) ) + errorTerm = ( float )VectorLength( segment ); + + VectorAdd( m_pVerts[neighborVertIndices[4]].m_Vert, m_pVerts[neighborVertIndices[6]].m_Vert, v ); + VectorScale( v, 0.5f, v ); + VectorSubtract( m_pVerts[neighborVertIndices[8]].m_Vert, v, segment ); + if( errorTerm < ( float )VectorLength( segment ) ) + errorTerm = ( float )VectorLength( segment ); + + VectorAdd( m_pVerts[neighborVertIndices[5]].m_Vert, m_pVerts[neighborVertIndices[7]].m_Vert, v ); + VectorScale( v, 0.5f, v ); + VectorSubtract( m_pVerts[neighborVertIndices[8]].m_Vert, v, segment ); + if( errorTerm < ( float )VectorLength( segment ) ) + errorTerm = ( float )VectorLength( segment ); + + // + // add the max child's error term + // + errorTerm += GetMaxErrorFromChildren( nodeIndex, level ); + + // set the error term + m_Nodes[nodeIndex].SetErrorTerm( errorTerm ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CCoreDispInfo::CalcNeighborVertIndicesAtNode( int nodeIndex, int level ) +{ + // calculate the shift in direction in the matrix + int shift = ( 1 << ( m_Power - level ) ); + + // calculate the width, height of the displacement surface (are uniform) + int extent = ( ( 1 << m_Power ) + 1 ); + + // + // get the neighbor vertex indices (defining the surface at the node level) + // + for( int direction = 0; direction < 8; direction++ ) + { + // + // get the parent vertex index in component form + // + int posX = m_Nodes[nodeIndex].GetCenterVertIndex() % extent; + int posY = m_Nodes[nodeIndex].GetCenterVertIndex() / extent; + + // + // calculate the neighboring vertex indices for surface rendering + // + bool bError = false; + switch( direction ) + { + case WEST: { posX -= shift; break; } + case NORTH: { posY += shift; break; } + case EAST: { posX += shift; break; } + case SOUTH: { posY -= shift; break; } + case SOUTHWEST: { posX -= shift; posY -= shift; break; } + case SOUTHEAST: { posX += shift; posY -= shift; break; } + case NORTHWEST: { posX -= shift; posY += shift; break; } + case NORTHEAST: { posX += shift; posY += shift; break; } + default: { bError = true; break; } + } + + if( bError ) + { + m_Nodes[nodeIndex].SetNeighborVertIndex( direction, -99999 ); + } + else + { + m_Nodes[nodeIndex].SetNeighborVertIndex( direction, ( ( posY * extent ) + posX ) ); + } + } +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CCoreDispInfo::CalcNodeInfo( int nodeIndex, int terminationLevel ) +{ + // get the level of the current node + int level = GetNodeLevel( nodeIndex ); + + // + // get the node data at the termination level + // + if( level == terminationLevel ) + { + // get the neighbor vertex indices (used to create surface at node level) + CalcNeighborVertIndicesAtNode( nodeIndex, level ); + + // get the neighbor node indices + //CalcNeighborNodeIndicesAtNode( nodeIndex, level ); + + // calculate the error term at the node + CalcErrorTermAtNode( nodeIndex, level ); + + // calcluate the axial-aligned bounding box at the node + CalcBoundingBoxAtNode( nodeIndex ); + + // calculate the triangular surface info at the node + CalcTriSurfInfoAtNode( nodeIndex ); + + return; + } + + // + // continue recursion (down to nodes "children") + // + for( int i = 4; i < 8; i++ ) + { + int childIndex = GetNodeChild( m_Power, nodeIndex, i ); + CalcNodeInfo( childIndex, terminationLevel ); + } +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +int CCoreDispInfo::GetNodeVertIndexFromParentIndex( int level, int parentVertIndex, int direction ) +{ + // calculate the "shift" + int shift = ( 1 << ( m_Power - ( level + 1 ) ) ); + + // calculate the width and height of displacement (is uniform) + int extent = ( ( 1 << m_Power ) + 1 ); + + // get the parent vertex index in component form + int posX = parentVertIndex % extent; + int posY = parentVertIndex / extent; + + // + // calculate the child index based on the parent index and child + // direction + // + switch( direction ) + { + case SOUTHWEST: { posX -= shift; posY -= shift; break; } + case SOUTHEAST: { posX += shift; posY -= shift; break; } + case NORTHWEST: { posX -= shift; posY += shift; break; } + case NORTHEAST: { posX += shift; posY += shift; break; } + default: return -99999; + } + + // return the child vertex index + return ( ( posY * extent ) + posX ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CCoreDispInfo::CalcVertIndicesAtNodes( int nodeIndex ) +{ + // + // check for recursion termination ( node level = power ) + // + int level = GetNodeLevel( nodeIndex ); + if( level == m_Power ) + return; + + // + // get the children indices - SW, SE, NW, NE + // + int childIndices[4]; + int i, j; + for( i = 0, j = 4; i < 4; i++, j++ ) + { + childIndices[i] = GetNodeChild( m_Power, nodeIndex, j ); + int centerIndex = GetNodeVertIndexFromParentIndex( level, m_Nodes[nodeIndex].GetCenterVertIndex(), j ); + m_Nodes[childIndices[i]].SetCenterVertIndex( centerIndex ); + } + + // + // calculate the children's node vertex indices + // + for( i = 0; i < 4; i++ ) + { + CalcVertIndicesAtNodes( childIndices[i] ); + } +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CCoreDispInfo::GenerateLODTree( void ) +{ + // + // calculate the displacement surface's vertex index at each quad-tree node + // centroid + // + int size = GetSize(); + int initialIndex = ( ( size - 1 ) >> 1 ); + m_Nodes[0].SetCenterVertIndex( initialIndex ); + CalcVertIndicesAtNodes( 0 ); + + // + // calculate the error terms, bounding boxes, and neighboring vertex indices + // at each node + // + for( int i = m_Power; i > 0; i-- ) + { + CalcNodeInfo( 0, i ); + } +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CCoreDispInfo::CalcDispSurfCoords( bool bLightMap, int lightmapID ) +{ + // + // get base surface texture coords + // + Vector2D texCoords[4]; + Vector2D luxelCoords[4]; + CCoreDispSurface *pSurf = GetSurface(); + + int i; + for( i = 0; i < 4; i++ ) + { + pSurf->GetTexCoord( i, texCoords[i] ); + pSurf->GetLuxelCoord( lightmapID, i, luxelCoords[i] ); + } + + // + // get images width and intervals along the edge + // + int postSpacing = GetPostSpacing(); + float ooInt = ( 1.0f / ( float )( postSpacing - 1 ) ); + + // + // calculate the parallel edge intervals + // + Vector2D edgeInt[2]; + if( !bLightMap ) + { + Vector2DSubtract( texCoords[1], texCoords[0], edgeInt[0] ); + Vector2DSubtract( texCoords[2], texCoords[3], edgeInt[1] ); + } + else + { + Vector2DSubtract( luxelCoords[1], luxelCoords[0], edgeInt[0] ); + Vector2DSubtract( luxelCoords[2], luxelCoords[3], edgeInt[1] ); + } + Vector2DMultiply( edgeInt[0], ooInt, edgeInt[0] ); + Vector2DMultiply( edgeInt[1], ooInt, edgeInt[1] ); + + // + // calculate the displacement points + // + for( i = 0; i < postSpacing; i++ ) + { + // + // position along parallel edges (start and end for a perpendicular segment) + // + Vector2D endPts[2]; + Vector2DMultiply( edgeInt[0], ( float )i, endPts[0] ); + Vector2DMultiply( edgeInt[1], ( float )i, endPts[1] ); + if( !bLightMap ) + { + Vector2DAdd( endPts[0], texCoords[0], endPts[0] ); + Vector2DAdd( endPts[1], texCoords[3], endPts[1] ); + } + else + { + Vector2DAdd( endPts[0], luxelCoords[0], endPts[0] ); + Vector2DAdd( endPts[1], luxelCoords[3], endPts[1] ); + } + + // + // interval length for perpendicular edge + // + Vector2D seg, segInt; + Vector2DSubtract( endPts[1], endPts[0], seg ); + Vector2DMultiply( seg, ooInt, segInt ); + + // + // calculate the material (texture or light) coordinate at each point + // + for( int j = 0; j < postSpacing; j++ ) + { + Vector2DMultiply( segInt, ( float )j, seg ); + + if( !bLightMap ) + { + Vector2DAdd( endPts[0], seg, m_pVerts[i*postSpacing+j].m_TexCoord ); + } + else + { + Vector2DAdd( endPts[0], seg, m_pVerts[i*postSpacing+j].m_LuxelCoords[lightmapID] ); + } + } + } +} + +#if 0 +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CCoreDispInfo::CalcDispSurfAlphas( void ) +{ + // + // get images width and intervals along the edge + // + int postSpacing = GetPostSpacing(); + float ooInt = ( 1.0f / ( float )( postSpacing - 1 ) ); + + // + // calculate the parallel edge intervals + // + float edgeInt[2]; + edgeInt[0] = m_Surf.m_Alpha[1] - m_Surf.m_Alpha[0]; + edgeInt[1] = m_Surf.m_Alpha[2] - m_Surf.m_Alpha[3]; + edgeInt[0] *= ooInt; + edgeInt[1] *= ooInt; + + // + // calculate the displacement points + // + for( int i = 0; i < postSpacing; i++ ) + { + // + // position along parallel edges (start and end for a perpendicular segment) + // + float endValues[2]; + + endValues[0] = edgeInt[0] * ( float )i; + endValues[1] = edgeInt[1] * ( float )i; + endValues[0] += m_Surf.m_Alpha[0]; + endValues[1] += m_Surf.m_Alpha[3]; + + // + // interval length for perpendicular edge + // + float seg, segInt; + seg = endValues[1] - endValues[0]; + segInt = seg * ooInt; + + // + // calculate the alpha value at each point + // + for( int j = 0; j < postSpacing; j++ ) + { + seg = segInt * ( float )j; + m_Alphas[i*postSpacing+j] = endValues[0] + seg; + } + } +} +#endif + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CCoreDispInfo::GenerateDispSurfTangentSpaces( void ) +{ + // + // get texture axes from base surface + // + CCoreDispSurface *pSurf = GetSurface(); + Vector sAxis, tAxis; + pSurf->GetSAxis( sAxis ); + pSurf->GetTAxis( tAxis ); + + // + // calculate the tangent spaces + // + int size = GetSize(); + for( int i = 0; i < size; i++ ) + { + // + // create the axes - normals, tangents, and binormals + // + VectorCopy( tAxis, m_pVerts[i].m_TangentT ); + VectorNormalize( m_pVerts[i].m_TangentT ); + CrossProduct( m_pVerts[i].m_Normal, m_pVerts[i].m_TangentT, m_pVerts[i].m_TangentS ); + VectorNormalize( m_pVerts[i].m_TangentS ); + CrossProduct( m_pVerts[i].m_TangentS, m_pVerts[i].m_Normal, m_pVerts[i].m_TangentT ); + VectorNormalize( m_pVerts[i].m_TangentT ); + + Vector tmpVect; + Vector planeNormal; + pSurf->GetNormal( planeNormal ); + CrossProduct( sAxis, tAxis, tmpVect ); + if( DotProduct( planeNormal, tmpVect ) > 0.0f ) + { + VectorScale( m_pVerts[i].m_TangentS, -1.0f, m_pVerts[i].m_TangentS ); + } + } +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CCoreDispInfo::CalcNormalFromEdges( int indexRow, int indexCol, bool bIsEdge[4], + Vector& normal ) +{ + // get the post spacing (size/interval of displacement surface) + int postSpacing = ( ( 1 << m_Power ) + 1 ); + + // initialize the normal accumulator - counter + Vector accumNormal; + int normalCount = 0; + + VectorClear( accumNormal ); + + Vector tmpVect[2]; + Vector tmpNormal; + + // + // check quadrant I (posX, posY) + // + if( bIsEdge[1] && bIsEdge[2] ) + { + // tri i + VectorSubtract( m_pVerts[(indexCol+1)*postSpacing+indexRow].m_Vert, m_pVerts[indexCol*postSpacing+indexRow].m_Vert, tmpVect[0] ); + VectorSubtract( m_pVerts[indexCol*postSpacing+(indexRow+1)].m_Vert, m_pVerts[indexCol*postSpacing+indexRow].m_Vert, tmpVect[1] ); + CrossProduct( tmpVect[1], tmpVect[0], tmpNormal ); + VectorNormalize( tmpNormal ); + VectorAdd( accumNormal, tmpNormal, accumNormal ); + normalCount++; + + // tri 2 + VectorSubtract( m_pVerts[(indexCol+1)*postSpacing+indexRow].m_Vert, m_pVerts[indexCol*postSpacing+(indexRow+1)].m_Vert, tmpVect[0] ); + VectorSubtract( m_pVerts[(indexCol+1)*postSpacing+(indexRow+1)].m_Vert, m_pVerts[indexCol*postSpacing+(indexRow+1)].m_Vert, tmpVect[1] ); + CrossProduct( tmpVect[1], tmpVect[0], tmpNormal ); + VectorNormalize( tmpNormal ); + VectorAdd( accumNormal, tmpNormal, accumNormal ); + normalCount++; + } + + // + // check quadrant II (negX, posY) + // + if( bIsEdge[0] && bIsEdge[1] ) + { + // tri i + VectorSubtract( m_pVerts[(indexCol+1)*postSpacing+(indexRow-1)].m_Vert, m_pVerts[indexCol*postSpacing+(indexRow-1)].m_Vert, tmpVect[0] ); + VectorSubtract( m_pVerts[indexCol*postSpacing+indexRow].m_Vert, m_pVerts[indexCol*postSpacing+(indexRow-1)].m_Vert, tmpVect[1] ); + CrossProduct( tmpVect[1], tmpVect[0], tmpNormal ); + VectorNormalize( tmpNormal ); + VectorAdd( accumNormal, tmpNormal, accumNormal ); + normalCount++; + + // tri 2 + VectorSubtract( m_pVerts[(indexCol+1)*postSpacing+(indexRow-1)].m_Vert, m_pVerts[indexCol*postSpacing+indexRow].m_Vert, tmpVect[0] ); + VectorSubtract( m_pVerts[(indexCol+1)*postSpacing+indexRow].m_Vert, m_pVerts[indexCol*postSpacing+indexRow].m_Vert, tmpVect[1] ); + CrossProduct( tmpVect[1], tmpVect[0], tmpNormal ); + VectorNormalize( tmpNormal ); + VectorAdd( accumNormal, tmpNormal, accumNormal ); + normalCount++; + } + + // + // check quadrant III (negX, negY) + // + if( bIsEdge[0] && bIsEdge[3] ) + { + // tri i + VectorSubtract( m_pVerts[indexCol*postSpacing+(indexRow-1)].m_Vert, m_pVerts[(indexCol-1)*postSpacing+(indexRow-1)].m_Vert, tmpVect[0] ); + VectorSubtract( m_pVerts[(indexCol-1)*postSpacing+indexRow].m_Vert, m_pVerts[(indexCol-1)*postSpacing+(indexRow-1)].m_Vert, tmpVect[1] ); + CrossProduct( tmpVect[1], tmpVect[0], tmpNormal ); + VectorNormalize( tmpNormal ); + VectorAdd( accumNormal, tmpNormal, accumNormal ); + normalCount++; + + // tri 2 + VectorSubtract( m_pVerts[indexCol*postSpacing+(indexRow-1)].m_Vert, m_pVerts[(indexCol-1)*postSpacing+indexRow].m_Vert, tmpVect[0] ); + VectorSubtract( m_pVerts[indexCol*postSpacing+indexRow].m_Vert, m_pVerts[(indexCol-1)*postSpacing+indexRow].m_Vert, tmpVect[1] ); + CrossProduct( tmpVect[1], tmpVect[0], tmpNormal ); + VectorNormalize( tmpNormal ); + VectorAdd( accumNormal, tmpNormal, accumNormal ); + normalCount++; + } + + // + // check quadrant IV (posX, negY) + // + if( bIsEdge[2] && bIsEdge[3] ) + { + // tri i + VectorSubtract( m_pVerts[indexCol*postSpacing+indexRow].m_Vert, m_pVerts[(indexCol-1)*postSpacing+indexRow].m_Vert, tmpVect[0] ); + VectorSubtract( m_pVerts[(indexCol-1)*postSpacing+(indexRow+1)].m_Vert, m_pVerts[(indexCol-1)*postSpacing+indexRow].m_Vert, tmpVect[1] ); + CrossProduct( tmpVect[1], tmpVect[0], tmpNormal ); + VectorNormalize( tmpNormal ); + VectorAdd( accumNormal, tmpNormal, accumNormal ); + normalCount++; + + // tri 2 + VectorSubtract( m_pVerts[indexCol*postSpacing+indexRow].m_Vert, m_pVerts[(indexCol-1)*postSpacing+(indexRow+1)].m_Vert, tmpVect[0] ); + VectorSubtract( m_pVerts[indexCol*postSpacing+(indexRow+1)].m_Vert, m_pVerts[(indexCol-1)*postSpacing+(indexRow+1)].m_Vert, tmpVect[1] ); + CrossProduct( tmpVect[1], tmpVect[0], tmpNormal ); + VectorNormalize( tmpNormal ); + VectorAdd( accumNormal, tmpNormal, accumNormal ); + normalCount++; + } + + VectorScale( accumNormal, ( 1.0f / ( float )normalCount ), normal ); +} + + +//----------------------------------------------------------------------------- +// Purpose: This function determines if edges exist in each of the directions +// off of the given point (given in component form). We know ahead of +// time that there are only 4 possibilities. +// +// 1 "directions" +// 0 + 2 +// 3 +// +// Input: indexRow - row position +// indexCol - col position +// direction - the direction (edge) currently being evaluated +// postSpacing - the number of intervals in the row and col directions +// Output: the edge existed? (true/false) +//----------------------------------------------------------------------------- +bool CCoreDispInfo::DoesEdgeExist( int indexRow, int indexCol, int direction, int postSpacing ) +{ + switch( direction ) + { + case 0: + // left edge + if( ( indexRow - 1 ) < 0 ) + return false; + return true; + case 1: + // top edge + if( ( indexCol + 1 ) > ( postSpacing - 1 ) ) + return false; + return true; + case 2: + // right edge + if( ( indexRow + 1 ) > ( postSpacing - 1 ) ) + return false; + return true; + case 3: + // bottom edge + if( ( indexCol - 1 ) < 0 ) + return false; + return true; + default: + return false; + } +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CCoreDispInfo::GenerateDispSurfNormals( void ) +{ + // get the post spacing (size/interval of displacement surface) + int postSpacing = GetPostSpacing(); + + // + // generate the normals at each displacement surface vertex + // + for( int i = 0; i < postSpacing; i++ ) + { + for( int j = 0; j < postSpacing; j++ ) + { + bool bIsEdge[4]; + + // edges + for( int k = 0; k < 4; k++ ) + { + bIsEdge[k] = DoesEdgeExist( j, i, k, postSpacing ); + } + + Vector normal; + CalcNormalFromEdges( j, i, bIsEdge, normal ); + + // save generated normal + VectorCopy( normal, m_pVerts[i*postSpacing+j].m_Normal ); + } + } +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CCoreDispInfo::GenerateDispSurf( void ) +{ + int i; + CCoreDispSurface *pSurf = GetSurface(); + Vector points[4]; + for( i = 0; i < 4; i++ ) + { + pSurf->GetPoint( i, points[i] ); + } + + // + // get the spacing (interval = width/height, are equal because it is uniform) along the edge + // + int postSpacing = GetPostSpacing(); + float ooInt = 1.0f / ( float )( postSpacing - 1 ); + + // + // calculate the opposite edge intervals + // + Vector edgeInt[2]; + VectorSubtract( points[1], points[0], edgeInt[0] ); + VectorScale( edgeInt[0], ooInt, edgeInt[0] ); + VectorSubtract( points[2], points[3], edgeInt[1] ); + VectorScale( edgeInt[1], ooInt, edgeInt[1] ); + + Vector elevNormal; + elevNormal.Init(); + if( m_Elevation != 0.0f ) + { + pSurf->GetNormal( elevNormal ); + VectorScale( elevNormal, m_Elevation, elevNormal ); + } + + // + // calculate the displaced vertices + // + for( i = 0; i < postSpacing; i++ ) + { + // + // calculate segment interval between opposite edges + // + Vector endPts[2]; + VectorScale( edgeInt[0], ( float )i, endPts[0] ); + VectorAdd( endPts[0], points[0], endPts[0] ); + VectorScale( edgeInt[1], ( float )i, endPts[1] ); + VectorAdd( endPts[1], points[3], endPts[1] ); + + Vector seg, segInt; + VectorSubtract( endPts[1], endPts[0], seg ); + VectorScale( seg, ooInt, segInt ); + + // + // calculate the surface vertices + // + for( int j = 0; j < postSpacing; j++ ) + { + int ndx = i * postSpacing + j; + + CoreDispVert_t *pVert = &m_pVerts[ndx]; + + // calculate the flat surface position -- saved separately + pVert->m_FlatVert = endPts[0] + ( segInt * ( float )j ); + + // start with the base surface position + pVert->m_Vert = pVert->m_FlatVert; + + // add the elevation vector -- if it exists + if( m_Elevation != 0.0f ) + { + pVert->m_Vert += elevNormal; + } + + // add the subdivision surface position + pVert->m_Vert += pVert->m_SubdivPos; + + // add the displacement field direction(normalized) and distance + pVert->m_Vert += pVert->m_FieldVector * pVert->m_FieldDistance; + } + } +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//bool CCoreDispInfo::Create( int creationFlags ) +bool CCoreDispInfo::Create( void ) +{ + // sanity check + CCoreDispSurface *pSurf = GetSurface(); + if( pSurf->GetPointCount() != 4 ) + return false; + + // generate the displacement surface + GenerateDispSurf(); + + GenerateDispSurfNormals(); + + GenerateDispSurfTangentSpaces(); + + CalcDispSurfCoords( false, 0 ); + + for( int bumpID = 0; bumpID < ( NUM_BUMP_VECTS + 1 ); bumpID++ ) + { + CalcDispSurfCoords( true, bumpID ); + } + + GenerateLODTree(); + + GenerateCollisionData(); + + CreateTris(); + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: Create a displacement surface without generating the LOD for it. +//----------------------------------------------------------------------------- +bool CCoreDispInfo::CreateWithoutLOD( void ) +{ + // sanity check + CCoreDispSurface *pSurf = GetSurface(); + if( pSurf->GetPointCount() != 4 ) + return false; + + GenerateDispSurf(); + + GenerateDispSurfNormals(); + + GenerateDispSurfTangentSpaces(); + + CalcDispSurfCoords( false, 0 ); + + for( int bumpID = 0; bumpID < ( NUM_BUMP_VECTS + 1 ); bumpID++ ) + { + CalcDispSurfCoords( true, bumpID ); + } + GenerateCollisionData(); + + CreateTris(); + + return true; +} + + +// Turn the optimizer back on +#ifdef _MSC_VER +#pragma optimize( "", on ) +#endif + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CCoreDispInfo::GetPositionOnSurface( float u, float v, Vector &vPos, + Vector *pNormal, float *pAlpha ) +{ + Vector2D dispUV( u, v ); + DispUVToSurf( dispUV, vPos, pNormal, pAlpha ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CCoreDispInfo::BaseFacePlaneToDispUV( Vector const &planePt, Vector2D &dispUV ) +{ + // Get the base surface points. + CCoreDispSurface *pSurf = GetSurface(); + Vector vecPoints[4]; + for( int iPoint = 0; iPoint < 4; ++iPoint ) + { + pSurf->GetPoint( iPoint, vecPoints[iPoint] ); + } + + PointInQuadToBarycentric( vecPoints[0], vecPoints[3], vecPoints[2], vecPoints[1], planePt, dispUV ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CCoreDispInfo::DispUVToSurf_TriTLToBR_1( const Vector &vecIntersectPoint, + int nSnapU, int nNextU, int nSnapV, int nNextV, + Vector &vecPoint, Vector *pNormal, float *pAlpha, + bool bBackup ) +{ + int nWidth = GetWidth(); + + int nIndices[3]; + nIndices[0] = nNextV * nWidth + nSnapU; + nIndices[1] = nNextV * nWidth + nNextU; + nIndices[2] = nSnapV * nWidth + nNextU; + + Vector vecFlatVerts[3], vecVerts[3]; + float flAlphas[3]; + for ( int iVert = 0; iVert < 3; ++iVert ) + { + vecFlatVerts[iVert] = m_pVerts[nIndices[iVert]].m_FlatVert; + vecVerts[iVert] = m_pVerts[nIndices[iVert]].m_Vert; + flAlphas[iVert] = m_pVerts[nIndices[iVert]].m_Alpha; + } + + if ( nSnapU == nNextU ) + { + if ( nSnapV == nNextV ) + { + vecPoint = vecVerts[0]; + *pAlpha = flAlphas[0]; + } + else + { + float flFrac = ( vecIntersectPoint - vecFlatVerts[0] ).Length() / ( vecFlatVerts[2] - vecFlatVerts[0] ).Length(); + vecPoint = vecVerts[0] + ( flFrac * ( vecVerts[2] - vecVerts[0] ) ); + + if ( pAlpha ) + { + *pAlpha = flAlphas[0] + ( flFrac * ( flAlphas[2] - flAlphas[0] ) ); + } + } + + if( pNormal ) + { + Vector edgeU = vecVerts[0] - vecVerts[1]; + Vector edgeV = vecVerts[2] - vecVerts[1]; + *pNormal = CrossProduct( edgeU, edgeV ); + VectorNormalize( *pNormal ); + } + } + else if ( nSnapV == nNextV ) + { + if ( nSnapU == nNextU ) + { + vecPoint = vecVerts[0]; + *pAlpha = flAlphas[0]; + } + else + { + float flFrac = ( vecIntersectPoint - vecFlatVerts[0] ).Length() / ( vecFlatVerts[2] - vecFlatVerts[0] ).Length(); + vecPoint = vecVerts[0] + ( flFrac * ( vecVerts[2] - vecVerts[0] ) ); + + if ( pAlpha ) + { + *pAlpha = flAlphas[0] + ( flFrac * ( flAlphas[2] - flAlphas[0] ) ); + } + } + + if( pNormal ) + { + Vector edgeU = vecVerts[0] - vecVerts[1]; + Vector edgeV = vecVerts[2] - vecVerts[1]; + *pNormal = CrossProduct( edgeU, edgeV ); + VectorNormalize( *pNormal ); + } + } + else + { + float flCfs[3]; + if ( CalcBarycentricCooefs( vecFlatVerts[0], vecFlatVerts[1], vecFlatVerts[2], vecIntersectPoint, flCfs[0], flCfs[1], flCfs[2] ) ) + { + vecPoint = ( vecVerts[0] * flCfs[0] ) + ( vecVerts[1] * flCfs[1] ) + ( vecVerts[2] * flCfs[2] ); + + if( pAlpha ) + { + *pAlpha = ( flAlphas[0] * flCfs[0] ) + ( flAlphas[1] * flCfs[1] ) + ( flAlphas[2] * flCfs[2] ); + } + + if( pNormal ) + { + Vector edgeU = vecVerts[0] - vecVerts[1]; + Vector edgeV = vecVerts[2] - vecVerts[1]; + *pNormal = CrossProduct( edgeU, edgeV ); + VectorNormalize( *pNormal ); + } + } + else + { + if ( !bBackup ) + { + DispUVToSurf_TriTLToBR_2( vecIntersectPoint, nSnapU, nNextU, nSnapV, nNextV, vecPoint, pNormal, pAlpha, true ); + } + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CCoreDispInfo::DispUVToSurf_TriTLToBR_2( const Vector &vecIntersectPoint, + int nSnapU, int nNextU, int nSnapV, int nNextV, + Vector &vecPoint, Vector *pNormal, float *pAlpha, + bool bBackup ) +{ + int nWidth = GetWidth(); + + int nIndices[3]; + nIndices[0] = nSnapV * nWidth + nSnapU; + nIndices[1] = nNextV * nWidth + nSnapU; + nIndices[2] = nSnapV * nWidth + nNextU; + + Vector vecFlatVerts[3], vecVerts[3]; + float flAlphas[3]; + for ( int iVert = 0; iVert < 3; ++iVert ) + { + vecFlatVerts[iVert] = m_pVerts[nIndices[iVert]].m_FlatVert; + vecVerts[iVert] = m_pVerts[nIndices[iVert]].m_Vert; + flAlphas[iVert] = m_pVerts[nIndices[iVert]].m_Alpha; + } + + if ( nSnapU == nNextU ) + { + if ( nSnapV == nNextV ) + { + vecPoint = vecVerts[0]; + *pAlpha = flAlphas[0]; + } + else + { + float flFrac = ( vecIntersectPoint - vecFlatVerts[0] ).Length() / ( vecFlatVerts[1] - vecFlatVerts[0] ).Length(); + vecPoint = vecVerts[0] + ( flFrac * ( vecVerts[1] - vecVerts[0] ) ); + + if ( pAlpha ) + { + *pAlpha = flAlphas[0] + ( flFrac * ( flAlphas[1] - flAlphas[0] ) ); + } + } + + if( pNormal ) + { + Vector edgeU = vecVerts[2] - vecVerts[0]; + Vector edgeV = vecVerts[1] - vecVerts[0]; + *pNormal = CrossProduct( edgeU, edgeV ); + VectorNormalize( *pNormal ); + } + } + else if ( nSnapV == nNextV ) + { + if ( nSnapU == nNextU ) + { + vecPoint = vecVerts[0]; + *pAlpha = flAlphas[0]; + } + else + { + float flFrac = ( vecIntersectPoint - vecFlatVerts[0] ).Length() / ( vecFlatVerts[2] - vecFlatVerts[0] ).Length(); + vecPoint = vecVerts[0] + ( flFrac * ( vecVerts[2] - vecVerts[0] ) ); + + if ( pAlpha ) + { + *pAlpha = flAlphas[0] + ( flFrac * ( flAlphas[2] - flAlphas[0] ) ); + } + } + + if( pNormal ) + { + Vector edgeU = vecVerts[2] - vecVerts[0]; + Vector edgeV = vecVerts[1] - vecVerts[0]; + *pNormal = CrossProduct( edgeU, edgeV ); + VectorNormalize( *pNormal ); + } + } + else + { + float flCfs[3]; + if ( CalcBarycentricCooefs( vecFlatVerts[0], vecFlatVerts[1], vecFlatVerts[2], vecIntersectPoint, flCfs[0], flCfs[1], flCfs[2] ) ) + { + vecPoint = ( vecVerts[0] * flCfs[0] ) + ( vecVerts[1] * flCfs[1] ) + ( vecVerts[2] * flCfs[2] ); + + if( pAlpha ) + { + *pAlpha = ( flAlphas[0] * flCfs[0] ) + ( flAlphas[1] * flCfs[1] ) + ( flAlphas[2] * flCfs[2] ); + } + + if( pNormal ) + { + Vector edgeU = vecVerts[2] - vecVerts[0]; + Vector edgeV = vecVerts[1] - vecVerts[0]; + *pNormal = CrossProduct( edgeU, edgeV ); + VectorNormalize( *pNormal ); + } + } + else + { + if ( !bBackup ) + { + DispUVToSurf_TriTLToBR_1( vecIntersectPoint, nSnapU, nNextU, nSnapV, nNextV, vecPoint, pNormal, pAlpha, true ); + } + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CCoreDispInfo::DispUVToSurf_TriTLToBR( Vector &vecPoint, Vector *pNormal, float *pAlpha, + float flU, float flV, const Vector &vecIntersectPoint ) +{ + const float TRIEDGE_EPSILON = 0.00001f; + + int nWidth = GetWidth(); + int nHeight = GetHeight(); + + int nSnapU = static_cast( flU ); + int nSnapV = static_cast( flV ); + int nNextU = nSnapU + 1; + int nNextV = nSnapV + 1; + if ( nNextU == nWidth) { --nNextU; } + if ( nNextV == nHeight ) { --nNextV; } + + float flFracU = flU - static_cast( nSnapU ); + float flFracV = flV - static_cast( nSnapV ); + + if ( ( flFracU + flFracV ) >= ( 1.0f + TRIEDGE_EPSILON ) ) + { + DispUVToSurf_TriTLToBR_1( vecIntersectPoint, nSnapU, nNextU, nSnapV, nNextV, vecPoint, pNormal, pAlpha, false ); + } + else + { + DispUVToSurf_TriTLToBR_2( vecIntersectPoint, nSnapU, nNextU, nSnapV, nNextV, vecPoint, pNormal, pAlpha, false ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CCoreDispInfo::DispUVToSurf_TriBLToTR_1( const Vector &vecIntersectPoint, + int nSnapU, int nNextU, int nSnapV, int nNextV, + Vector &vecPoint, Vector *pNormal, float *pAlpha, + bool bBackup ) +{ + int nWidth = GetWidth(); + + int nIndices[3]; + nIndices[0] = nSnapV * nWidth + nSnapU; + nIndices[1] = nNextV * nWidth + nSnapU; + nIndices[2] = nNextV * nWidth + nNextU; + + Vector vecFlatVerts[3], vecVerts[3]; + float flAlphas[3]; + for ( int iVert = 0; iVert < 3; ++iVert ) + { + vecFlatVerts[iVert] = m_pVerts[nIndices[iVert]].m_FlatVert; + vecVerts[iVert] = m_pVerts[nIndices[iVert]].m_Vert; + flAlphas[iVert] = m_pVerts[nIndices[iVert]].m_Alpha; + } + + if ( nSnapU == nNextU ) + { + if ( nSnapV == nNextV ) + { + vecPoint = vecVerts[0]; + *pAlpha = flAlphas[0]; + } + else + { + float flFrac = ( vecIntersectPoint - vecFlatVerts[0] ).Length() / ( vecFlatVerts[2] - vecFlatVerts[0] ).Length(); + vecPoint = vecVerts[0] + ( flFrac * ( vecVerts[2] - vecVerts[0] ) ); + + if ( pAlpha ) + { + *pAlpha = flAlphas[0] + ( flFrac * ( flAlphas[2] - flAlphas[0] ) ); + } + } + + if( pNormal ) + { + Vector edgeU = vecVerts[2] - vecVerts[1]; + Vector edgeV = vecVerts[0] - vecVerts[1]; + *pNormal = CrossProduct( edgeU, edgeV ); + VectorNormalize( *pNormal ); + } + } + else if ( nSnapV == nNextV ) + { + if ( nSnapU == nNextU ) + { + vecPoint = vecVerts[0]; + *pAlpha = flAlphas[0]; + } + else + { + float flFrac = ( vecIntersectPoint - vecFlatVerts[0] ).Length() / ( vecFlatVerts[2] - vecFlatVerts[0] ).Length(); + vecPoint = vecVerts[0] + ( flFrac * ( vecVerts[2] - vecVerts[0] ) ); + + if ( pAlpha ) + { + *pAlpha = flAlphas[0] + ( flFrac * ( flAlphas[2] - flAlphas[0] ) ); + } + } + + if( pNormal ) + { + Vector edgeU = vecVerts[2] - vecVerts[1]; + Vector edgeV = vecVerts[0] - vecVerts[1]; + *pNormal = CrossProduct( edgeV, edgeU ); + VectorNormalize( *pNormal ); + } + } + else + { + float flCfs[3]; + if ( CalcBarycentricCooefs( vecFlatVerts[0], vecFlatVerts[1], vecFlatVerts[2], vecIntersectPoint, flCfs[0], flCfs[1], flCfs[2] ) ) + { + vecPoint = ( vecVerts[0] * flCfs[0] ) + ( vecVerts[1] * flCfs[1] ) + ( vecVerts[2] * flCfs[2] ); + + if( pAlpha ) + { + *pAlpha = ( flAlphas[0] * flCfs[0] ) + ( flAlphas[1] * flCfs[1] ) + ( flAlphas[2] * flCfs[2] ); + } + + if( pNormal ) + { + Vector edgeU = vecVerts[2] - vecVerts[1]; + Vector edgeV = vecVerts[0] - vecVerts[1]; + *pNormal = CrossProduct( edgeV, edgeU ); + VectorNormalize( *pNormal ); + } + } + else + { + if ( !bBackup ) + { + DispUVToSurf_TriBLToTR_2( vecIntersectPoint, nSnapU, nNextU, nSnapV, nNextV, vecPoint, pNormal, pAlpha, true ); + } + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CCoreDispInfo::DispUVToSurf_TriBLToTR_2( const Vector &vecIntersectPoint, + int nSnapU, int nNextU, int nSnapV, int nNextV, + Vector &vecPoint, Vector *pNormal, float *pAlpha, + bool bBackup ) +{ + int nWidth = GetWidth(); + + int nIndices[3]; + nIndices[0] = nSnapV * nWidth + nSnapU; + nIndices[1] = nNextV * nWidth + nNextU; + nIndices[2] = nSnapV * nWidth + nNextU; + + Vector vecFlatVerts[3], vecVerts[3]; + float flAlphas[3]; + for ( int iVert = 0; iVert < 3; ++iVert ) + { + vecFlatVerts[iVert] = m_pVerts[nIndices[iVert]].m_FlatVert; + vecVerts[iVert] = m_pVerts[nIndices[iVert]].m_Vert; + flAlphas[iVert] = m_pVerts[nIndices[iVert]].m_Alpha; + } + + if ( nSnapU == nNextU ) + { + if ( nSnapV == nNextV ) + { + vecPoint = vecVerts[0]; + *pAlpha = flAlphas[0]; + } + else + { + float flFrac = ( vecIntersectPoint - vecFlatVerts[0] ).Length() / ( vecFlatVerts[1] - vecFlatVerts[0] ).Length(); + vecPoint = vecVerts[0] + ( flFrac * ( vecVerts[1] - vecVerts[0] ) ); + + if ( pAlpha ) + { + *pAlpha = flAlphas[0] + ( flFrac * ( flAlphas[1] - flAlphas[0] ) ); + } + } + + if( pNormal ) + { + Vector edgeU = vecVerts[0] - vecVerts[2]; + Vector edgeV = vecVerts[1] - vecVerts[2]; + *pNormal = CrossProduct( edgeV, edgeU ); + VectorNormalize( *pNormal ); + } + } + else if ( nSnapV == nNextV ) + { + if ( nSnapU == nNextU ) + { + vecPoint = vecVerts[0]; + *pAlpha = flAlphas[0]; + } + else + { + float flFrac = ( vecIntersectPoint - vecFlatVerts[0] ).Length() / ( vecFlatVerts[2] - vecFlatVerts[0] ).Length(); + vecPoint = vecVerts[0] + ( flFrac * ( vecVerts[2] - vecVerts[0] ) ); + + if ( pAlpha ) + { + *pAlpha = flAlphas[0] + ( flFrac * ( flAlphas[2] - flAlphas[0] ) ); + } + } + + if( pNormal ) + { + Vector edgeU = vecVerts[0] - vecVerts[2]; + Vector edgeV = vecVerts[1] - vecVerts[2]; + *pNormal = CrossProduct( edgeV, edgeU ); + VectorNormalize( *pNormal ); + } + } + else + { + float flCfs[3]; + if ( CalcBarycentricCooefs( vecFlatVerts[0], vecFlatVerts[1], vecFlatVerts[2], vecIntersectPoint, flCfs[0], flCfs[1], flCfs[2] ) ) + { + vecPoint = ( vecVerts[0] * flCfs[0] ) + ( vecVerts[1] * flCfs[1] ) + ( vecVerts[2] * flCfs[2] ); + + if( pAlpha ) + { + *pAlpha = ( flAlphas[0] * flCfs[0] ) + ( flAlphas[1] * flCfs[1] ) + ( flAlphas[2] * flCfs[2] ); + } + + if( pNormal ) + { + Vector edgeU = vecVerts[0] - vecVerts[2]; + Vector edgeV = vecVerts[1] - vecVerts[2]; + *pNormal = CrossProduct( edgeV, edgeU ); + VectorNormalize( *pNormal ); + } + } + else + { + if ( !bBackup ) + { + DispUVToSurf_TriBLToTR_1( vecIntersectPoint, nSnapU, nNextU, nSnapV, nNextV, vecPoint, pNormal, pAlpha, true ); + } + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CCoreDispInfo::DispUVToSurf_TriBLToTR( Vector &vecPoint, Vector *pNormal, float *pAlpha, + float flU, float flV, const Vector &vecIntersectPoint ) +{ + int nWidth = GetWidth(); + int nHeight = GetHeight(); + + int nSnapU = static_cast( flU ); + int nSnapV = static_cast( flV ); + int nNextU = nSnapU + 1; + int nNextV = nSnapV + 1; + if ( nNextU == nWidth) { --nNextU; } + if ( nNextV == nHeight ) { --nNextV; } + + float flFracU = flU - static_cast( nSnapU ); + float flFracV = flV - static_cast( nSnapV ); + + if( flFracU < flFracV ) + { + DispUVToSurf_TriBLToTR_1( vecIntersectPoint, nSnapU, nNextU, nSnapV, nNextV, vecPoint, pNormal, pAlpha, false ); + } + else + { + DispUVToSurf_TriBLToTR_2( vecIntersectPoint, nSnapU, nNextU, nSnapV, nNextV, vecPoint, pNormal, pAlpha, false ); + } +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CCoreDispInfo::DispUVToSurf( Vector2D const &dispUV, Vector &vecPoint, + Vector *pNormal, float *pAlpha ) +{ + // Check to see that the point is on the surface. + if ( dispUV.x < 0.0f || dispUV.x > 1.0f || dispUV.y < 0.0f || dispUV.y > 1.0f ) + return; + + // Get the base surface points. + Vector vecIntersectPoint; + CCoreDispSurface *pSurf = GetSurface(); + PointInQuadFromBarycentric( pSurf->GetPoint( 0 ), pSurf->GetPoint( 3 ), pSurf->GetPoint( 2 ), pSurf->GetPoint( 1 ), dispUV, vecIntersectPoint ); + + // Get the displacement power. + int nWidth = GetWidth(); + int nHeight = GetHeight(); + + // Scale the U, V coordinates to the displacement grid size. + float flU = dispUV.x * ( static_cast( nWidth ) - 1.000001f ); + float flV = dispUV.y * ( static_cast( nHeight ) - 1.000001f ); + + // Find the base U, V. + int nSnapU = static_cast( flU ); + int nSnapV = static_cast( flV ); + + // Use this to get the triangle orientation. + bool bOdd = ( ( ( nSnapV * nWidth ) + nSnapU ) % 2 == 1 ); + + // Top Left to Bottom Right + if( bOdd ) + { + DispUVToSurf_TriTLToBR( vecPoint, pNormal, pAlpha, flU, flV, vecIntersectPoint ); + } + // Bottom Left to Top Right + else + { + DispUVToSurf_TriBLToTR( vecPoint, pNormal, pAlpha, flU, flV, vecIntersectPoint ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Create bounding boxes around pairs of triangles (in a grid-like) +// fashion; used for culling +//----------------------------------------------------------------------------- +void CCoreDispInfo::CreateBoundingBoxes( CoreDispBBox_t *pBBox, int count ) +{ + // + // Initialize the bounding boxes. + // + int iBox; + for( iBox = 0; iBox < count; ++iBox ) + { + pBBox[iBox].vMin.Init( FLT_MAX, FLT_MAX, FLT_MAX ); + pBBox[iBox].vMax.Init( FLT_MIN, FLT_MIN, FLT_MIN ); + } + + // Get the width and height of the displacement surface. + int nHeight = GetHeight(); + int nWidth = GetWidth(); + + // Find bounding box of every two consecutive triangles + iBox = 0; + int nIndex = 0; + for( int iHgt = 0; iHgt < ( nHeight - 1 ); ++iHgt ) + { + for( int iWid = 0; iWid < ( nWidth - 1 ); ++iWid ) + { + for( int iPoint = 0; iPoint < 4; ++iPoint ) + { + switch( iPoint ) + { + case 0: { nIndex = ( nHeight * iHgt ) + iWid; break; } + case 1: { nIndex = ( nHeight * ( iHgt + 1 ) ) + iWid; break; } + case 2: { nIndex = ( nHeight * ( iHgt + 1 ) ) + ( iWid + 1 ); break; } + case 3: { nIndex = ( nHeight * iHgt ) + ( iWid + 1 ); break; } + default: { break; } + } + + Vector vecPoint; + GetVert( nIndex, vecPoint ); + if( vecPoint[0] < pBBox[iBox].vMin[0] ) { pBBox[iBox].vMin[0] = vecPoint[0]; } + if( vecPoint[1] < pBBox[iBox].vMin[1] ) { pBBox[iBox].vMin[1] = vecPoint[1]; } + if( vecPoint[2] < pBBox[iBox].vMin[2] ) { pBBox[iBox].vMin[2] = vecPoint[2]; } + + if( vecPoint[0] > pBBox[iBox].vMax[0] ) { pBBox[iBox].vMax[0] = vecPoint[0]; } + if( vecPoint[1] > pBBox[iBox].vMax[1] ) { pBBox[iBox].vMax[1] = vecPoint[1]; } + if( vecPoint[2] > pBBox[iBox].vMax[2] ) { pBBox[iBox].vMax[2] = vecPoint[2]; } + } + + iBox++; + } + } + + // Verify. + Assert( iBox == count ); + + // Bloat. + for ( iBox = 0; iBox < count; ++iBox ) + { + for( int iAxis = 0; iAxis < 3; ++iAxis ) + { + pBBox[iBox].vMin[iAxis] -= 1.0f; + pBBox[iBox].vMax[iAxis] += 1.0f; + } + } +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline bool PointInDispBBox( CoreDispBBox_t *pBox, const Vector &vecPoint ) +{ + // Check to see if point lies in box + if( ( vecPoint.x < pBox->vMin.x ) || ( vecPoint.x > pBox->vMax.x ) ) + return false; + + if( ( vecPoint.y < pBox->vMin.y ) || ( vecPoint.y > pBox->vMax.y ) ) + return false; + + if( ( vecPoint.z < pBox->vMin.z ) || ( vecPoint.z > pBox->vMax.z ) ) + return false; + + return true; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CCoreDispInfo::GetTriangleIndicesForDispBBox( int nIndex, int nTris[2][3] ) +{ + // Test to see whether or not the index is odd. + bool bOdd = ( ( nIndex % 2 ) == 1 ); + + int nWidth = GetWidth(); + + // Tris for TLtoBR + if ( bOdd ) + { + nTris[0][0] = nIndex; + nTris[0][1] = nIndex + nWidth; + nTris[0][2] = nIndex + 1; + + nTris[1][0] = nIndex + 1; + nTris[1][1] = nIndex + nWidth; + nTris[1][2] = nIndex + nWidth + 1; + } + // Tris for BLtoTR + else + { + nTris[0][0] = nIndex; + nTris[0][1] = nIndex + nWidth; + nTris[0][2] = nIndex + nWidth + 1; + + nTris[1][0] = nIndex; + nTris[1][1] = nIndex + nWidth + 1; + nTris[1][2] = nIndex + 1; + } +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +bool CCoreDispInfo::SurfToBaseFacePlane( Vector const &surfPt, Vector &planePt ) +{ + // Create bounding boxes + int nBoxCount = ( GetHeight() - 1 ) * ( GetWidth() - 1 ); + CoreDispBBox_t *pBBox = new CoreDispBBox_t[nBoxCount]; + CreateBoundingBoxes( pBBox, nBoxCount ); + + // Use the boxes as a first-pass culling mechanism. + for( int iBox = 0; iBox < nBoxCount; ++iBox ) + { + // Get the current displacement triangle-pair bounding-box. + CoreDispBBox_t *pBox = &pBBox[iBox]; + if( !pBox ) + continue; + + // Check the point against the current displacement bounding-box. + if ( !PointInDispBBox( pBox, surfPt ) ) + continue; + + // Point lies within the bounding box. + int nIndex = iBox + ( iBox / ( GetWidth() - 1 ) ); + + // Get the triangle coordinates for this box. + int aTris[2][3]; + GetTriangleIndicesForDispBBox( nIndex, aTris ); + + // Barycentrically test the triangles on the displacement surface. + Vector vecPoints[3]; + for ( int iTri = 0; iTri < 2; ++iTri ) + { + for ( int iVert = 0; iVert < 3; ++iVert ) + { + GetVert( aTris[iTri][iVert], vecPoints[iVert] ); + } + + float c[3]; + if ( CalcBarycentricCooefs( vecPoints[0], vecPoints[1], vecPoints[2], surfPt, c[0], c[1], c[2] ) ) + { + Vector vecFlatPoints[3]; + for ( int iVert = 0; iVert < 3; ++iVert ) + { + GetFlatVert( aTris[iTri][iVert], vecFlatPoints[iVert] ); + } + + planePt = ( vecFlatPoints[0] * c[0] ) + ( vecFlatPoints[1] * c[1] ) + ( vecFlatPoints[2] * c[2] ); + + // Delete temporary memory. + delete [] pBBox; + return true; + } + } + } + + // Delete temporary memory + delete [] pBBox; + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +int CCoreDispInfo::GetTriCount( void ) +{ + return ( ( GetHeight() - 1 ) * ( GetWidth() -1 ) * 2 ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CCoreDispInfo::GetTriIndices( int iTri, unsigned short &v1, unsigned short &v2, unsigned short &v3 ) +{ + // Verify we have the correct data (only build when collision data is built). + if ( !m_pTris || ( iTri < 0 ) || ( iTri >= GetTriCount() ) ) + { + Assert( iTri >= 0 ); + Assert( iTri < GetTriCount() ); + Assert( m_pTris ); + return; + } + + CoreDispTri_t *pTri = &m_pTris[iTri]; + v1 = pTri->m_iIndex[0]; + v2 = pTri->m_iIndex[1]; + v3 = pTri->m_iIndex[2]; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CCoreDispInfo::SetTriIndices( int iTri, unsigned short v1, unsigned short v2, unsigned short v3 ) +{ + // Verify we have the correct data (only build when collision data is built). + if ( !m_pTris || ( iTri < 0 ) || ( iTri >= GetTriCount() ) ) + { + Assert( iTri >= 0 ); + Assert( iTri < GetTriCount() ); + Assert( m_pTris ); + return; + } + + CoreDispTri_t *pTri = &m_pTris[iTri]; + pTri->m_iIndex[0] = v1; + pTri->m_iIndex[1] = v2; + pTri->m_iIndex[2] = v3; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CCoreDispInfo::GetTriPos( int iTri, Vector &v1, Vector &v2, Vector &v3 ) +{ + // Verify we have the correct data (only build when collision data is built). + if ( !m_pTris || ( iTri < 0 ) || ( iTri >= GetTriCount() ) ) + { + Assert( iTri >= 0 ); + Assert( iTri < GetTriCount() ); + Assert( m_pTris ); + return; + } + + CoreDispTri_t *pTri = &m_pTris[iTri]; + v1 = m_pVerts[pTri->m_iIndex[0]].m_Vert; + v2 = m_pVerts[pTri->m_iIndex[1]].m_Vert; + v3 = m_pVerts[pTri->m_iIndex[2]].m_Vert; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CCoreDispInfo::InitTris( void ) +{ + // Verify we have the correct data (only build when collision data is built). + if ( !m_pTris ) + { + Assert( m_pTris ); + return; + } + + int nTriCount = GetTriCount(); + for ( int iTri = 0; iTri < nTriCount; ++iTri ) + { + m_pTris[iTri].m_uiTags = 0; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CCoreDispInfo::CreateTris( void ) +{ + // Verify we have the correct data (only build when collision data is built). + if ( !m_pTris ) + { + Assert( m_pTris ); + return; + } + + // Extra sanity check if wanted! + Assert( GetTriCount() == ( m_RenderIndexCount / 3 ) ); + + int nTriCount = GetTriCount(); + for ( int iTri = 0, iRender = 0; iTri < nTriCount; ++iTri, iRender += 3 ) + { + m_pTris[iTri].m_iIndex[0] = m_RenderIndices[iRender]; + m_pTris[iTri].m_iIndex[1] = m_RenderIndices[iRender+1]; + m_pTris[iTri].m_iIndex[2] = m_RenderIndices[iRender+2]; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CCoreDispInfo::IsTriWalkable( int iTri ) +{ + if ( IsTriTag( iTri, COREDISPTRI_TAG_FORCE_WALKABLE_BIT ) ) + { + return IsTriTag( iTri, COREDISPTRI_TAG_FORCE_WALKABLE_VAL ); + } + + return IsTriTag( iTri, COREDISPTRI_TAG_WALKABLE ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CCoreDispInfo::IsTriBuildable( int iTri ) +{ + if ( IsTriTag( iTri, COREDISPTRI_TAG_FORCE_BUILDABLE_BIT ) ) + { + return IsTriTag( iTri, COREDISPTRI_TAG_FORCE_BUILDABLE_VAL ); + } + + return IsTriTag( iTri, COREDISPTRI_TAG_BUILDABLE ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CCoreDispInfo::Position_Update( int iVert, Vector vecPos ) +{ + Vector vSPos, vFlat; + GetFlatVert( iVert, vFlat ); + GetSubdivPosition( iVert, vSPos ); + + Vector vSeg; + vSeg = vecPos - vFlat; + vSeg -= vSPos; + + // Subtract out the elevation. + float elev = GetElevation(); + if( elev != 0.0 ) + { + Vector vNormal; + GetSurface()->GetNormal( vNormal ); + vNormal *= elev; + + vSeg -= vNormal; + } + + float flDistance = VectorNormalize( vSeg ); + + SetFieldVector( iVert, vSeg ); + SetFieldDistance( iVert, flDistance ); +} diff --git a/public/builddisp.h b/public/builddisp.h index 120fc757..a134146a 100644 --- a/public/builddisp.h +++ b/public/builddisp.h @@ -1,1432 +1,1432 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $Workfile: $ -// $Date: $ -// $NoKeywords: $ -//=============================================================================// - -#ifndef BUILDDISP_H -#define BUILDDISP_H - -#ifdef _WIN32 -#pragma once -#endif - -#include "commonmacros.h" -#include "tier0/dbg.h" -#include "bspfile.h" -#include "mathlib.h" -#include "bumpvects.h" -#include "disp_common.h" -#include "bitvec.h" - -#define DISP_ALPHA_PROP_DELTA 382.5f - -class CCoreDispInfo; - -struct CoreDispBBox_t -{ - Vector vMin, vMax; -}; - -//========================================================================= -// -// Surface Class - interfacing class (fill in with MapFace, dface_t, and -// msurface_t) -// -class CCoreDispSurface -{ -public: - - enum { QUAD_POINT_COUNT = 4 }; - enum { MAX_CORNER_NEIGHBOR_COUNT = 16 }; - - CCoreDispSurface(); - - //========================================================================= - // - // initialization - // - void Init( void ); - - //========================================================================= - // - // parent surface id - index to CMapFace, dface_t, or msurface_t - // - inline void SetHandle( int handle ); - inline int GetHandle( void ); - - //========================================================================= - // - // vertex data - pos, normal, texture, lightmap, alpha, etc... - // - inline void SetPointCount( int count ); - inline int GetPointCount( void ) const; - - inline void SetPoint( int index, Vector const &pt ); - inline void GetPoint( int index, Vector& pt ) const; - inline Vector const& GetPoint( int index ) const; - - inline void SetPointNormal( int index, Vector const &normal ); - inline void GetPointNormal( int index, Vector &normal ); - inline void SetTexCoord( int index, Vector2D const& texCoord ); - inline void GetTexCoord( int index, Vector2D& texCoord ) const; - - inline void SetLuxelCoord( int bumpIndex, int index, Vector2D const& luxelCoord ); - inline void GetLuxelCoord( int bumpIndex, int index, Vector2D& luxelCoord ) const; - inline void SetLuxelCoords( int bumpIndex, Vector2D const coords[4] ); - inline void GetLuxelCoords( int bumpIndex, Vector2D coords[4] ) const; - - inline void SetAlpha( int index, float alpha ); - inline float GetAlpha( int const index ) const; - - //========================================================================= - // - // utils - // - inline void GetNormal( Vector& normal ); - inline void SetFlags( int flag ); - inline int GetFlags( void ); - inline void SetContents( int contents ); - inline int GetContents( void ); - - //========================================================================= - // - // create utils (texture axis not use anymore but here to support older maps) - // - inline void SetSAxis( Vector const &axis ); - inline void GetSAxis( Vector &axis ); - inline void SetTAxis( Vector const &axis ); - inline void GetTAxis( Vector &axis ); - - inline void SetPointStartIndex( int index ); - inline int GetPointStartIndex( void ); - inline void SetPointStart( Vector const &pt ); - inline void GetPointStart( Vector &pt ); - - // Used by the tools to set the neighbor data from the BSP file. - void SetNeighborData( const CDispNeighbor edgeNeighbors[4], const CDispCornerNeighbors cornerNeighbors[4] ); - - void GeneratePointStartIndexFromMappingAxes( Vector const &sAxis, Vector const &tAxis ); - int GenerateSurfPointStartIndex( void ); - int FindSurfPointStartIndex( void ); - void AdjustSurfPointData( void ); - - // Indexed by CORNER_ defines. - CDispCornerNeighbors* GetCornerNeighbors( int iCorner ) { Assert( iCorner >= 0 && iCorner < ARRAYSIZE( m_CornerNeighbors ) ); return &m_CornerNeighbors[iCorner]; } - const CDispCornerNeighbors* GetCornerNeighbors( int iCorner ) const { Assert( iCorner >= 0 && iCorner < ARRAYSIZE( m_CornerNeighbors ) ); return &m_CornerNeighbors[iCorner]; } - - // Indexed by CORNER_ defines. - int GetCornerNeighborCount( int iCorner ) const { return GetCornerNeighbors( iCorner )->m_nNeighbors; } - int GetCornerNeighbor( int iCorner, int iNeighbor ) const { Assert( iNeighbor >= 0 && iNeighbor < GetCornerNeighbors(iCorner)->m_nNeighbors ); return GetCornerNeighbors( iCorner )->m_Neighbors[iNeighbor]; } - - CDispNeighbor* GetEdgeNeighbor( int iEdge ) { Assert( iEdge >= 0 && iEdge < ARRAYSIZE( m_EdgeNeighbors ) ); return &m_EdgeNeighbors[iEdge]; } - const CDispNeighbor* GetEdgeNeighbor( int iEdge ) const { Assert( iEdge >= 0 && iEdge < ARRAYSIZE( m_EdgeNeighbors ) ); return &m_EdgeNeighbors[iEdge]; } - - -protected: - - int m_Index; // parent face (CMapFace, dface_t, msurface_t) index "handle" - - int m_PointCount; // number of points in the face (should be 4!) - Vector m_Points[QUAD_POINT_COUNT]; // points - Vector m_Normals[QUAD_POINT_COUNT]; // normals at points - Vector2D m_TexCoords[QUAD_POINT_COUNT]; // texture coordinates at points - Vector2D m_LuxelCoords[NUM_BUMP_VECTS+1][QUAD_POINT_COUNT]; // lightmap coordinates at points - float m_Alphas[QUAD_POINT_COUNT]; // alpha at points - - // Straight from the BSP file. - CDispNeighbor m_EdgeNeighbors[4]; - CDispCornerNeighbors m_CornerNeighbors[4]; - - int m_Flags; // surface flags - inherited from the "parent" face - int m_Contents; // contents flags - inherited from the "parent" face - - Vector sAxis; // used to generate start disp orientation (old method) - Vector tAxis; // used to generate start disp orientation (old method) - int m_PointStartIndex; // index to the starting point -- for saving starting point - Vector m_PointStart; // starting point used to determine the orientation of the displacement map on the surface -}; - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -inline void CCoreDispSurface::SetHandle( int handle ) -{ - m_Index = handle; -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -inline int CCoreDispSurface::GetHandle( void ) -{ - return m_Index; -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -inline void CCoreDispSurface::SetPointCount( int count ) -{ - // quad only -- currently! - if( count != 4 ) - return; - m_PointCount = count; -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -inline int CCoreDispSurface::GetPointCount( void ) const -{ - return m_PointCount; -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -inline void CCoreDispSurface::SetPoint( int index, Vector const &pt ) -{ - Assert( index >= 0 ); - Assert( index < QUAD_POINT_COUNT ); - VectorCopy( pt, m_Points[index] ); -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -inline void CCoreDispSurface::GetPoint( int index, Vector &pt ) const -{ - Assert( index >= 0 ); - Assert( index < QUAD_POINT_COUNT ); - VectorCopy( m_Points[index], pt ); -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -inline Vector const& CCoreDispSurface::GetPoint( int index ) const -{ - Assert( index >= 0 ); - Assert( index < QUAD_POINT_COUNT ); - return m_Points[index]; -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -inline void CCoreDispSurface::SetPointNormal( int index, Vector const &normal ) -{ - Assert( index >= 0 ); - Assert( index < QUAD_POINT_COUNT ); - VectorCopy( normal, m_Normals[index] ); -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -inline void CCoreDispSurface::GetPointNormal( int index, Vector& normal ) -{ - Assert( index >= 0 ); - Assert( index < QUAD_POINT_COUNT ); - VectorCopy( m_Normals[index], normal ); -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -inline void CCoreDispSurface::SetTexCoord( int index, Vector2D const& texCoord ) -{ - Assert( index >= 0 ); - Assert( index < QUAD_POINT_COUNT ); - Vector2DCopy( texCoord, m_TexCoords[index] ); -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -inline void CCoreDispSurface::GetTexCoord( int index, Vector2D& texCoord ) const -{ - Assert( index >= 0 ); - Assert( index < QUAD_POINT_COUNT ); - Vector2DCopy( m_TexCoords[index], texCoord ); -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -inline void CCoreDispSurface::SetLuxelCoord( int bumpIndex, int index, Vector2D const& luxelCoord ) -{ - Assert( index >= 0 ); - Assert( index < QUAD_POINT_COUNT ); - Assert( bumpIndex >= 0 ); - Assert( bumpIndex < NUM_BUMP_VECTS + 1 ); - Vector2DCopy( luxelCoord, m_LuxelCoords[bumpIndex][index] ); -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -inline void CCoreDispSurface::GetLuxelCoord( int bumpIndex, int index, Vector2D& luxelCoord ) const -{ - Assert( index >= 0 ); - Assert( index < QUAD_POINT_COUNT ); - Assert( bumpIndex >= 0 ); - Assert( bumpIndex < NUM_BUMP_VECTS + 1 ); - Vector2DCopy( m_LuxelCoords[bumpIndex][index], luxelCoord ); -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -inline void CCoreDispSurface::SetLuxelCoords( int bumpIndex, Vector2D const luxelCoords[4] ) -{ - Assert( bumpIndex >= 0 ); - Assert( bumpIndex < NUM_BUMP_VECTS + 1 ); - for( int i=0; i < 4; i++ ) - Vector2DCopy( luxelCoords[i], m_LuxelCoords[bumpIndex][i] ); -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -inline void CCoreDispSurface::GetLuxelCoords( int bumpIndex, Vector2D luxelCoords[4] ) const -{ - Assert( bumpIndex >= 0 ); - Assert( bumpIndex < NUM_BUMP_VECTS + 1 ); - for( int i=0; i < 4; i++ ) - Vector2DCopy( m_LuxelCoords[bumpIndex][i], luxelCoords[i] ); -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -inline void CCoreDispSurface::SetAlpha( int index, float alpha ) -{ - Assert( index >= 0 ); - Assert( index < QUAD_POINT_COUNT ); - m_Alphas[index] = alpha; -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -inline float CCoreDispSurface::GetAlpha( int const index ) const -{ - Assert( index >= 0 ); - Assert( index < QUAD_POINT_COUNT ); - return m_Alphas[index]; -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -inline void CCoreDispSurface::SetFlags( int flag ) -{ - m_Flags = flag; -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -inline int CCoreDispSurface::GetFlags( void ) -{ - return m_Flags; -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -inline void CCoreDispSurface::SetContents( int contents ) -{ - m_Contents = contents; -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -inline int CCoreDispSurface::GetContents( void ) -{ - return m_Contents; -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -inline void CCoreDispSurface::SetSAxis( Vector const &axis ) -{ - VectorCopy( axis, sAxis ); -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -inline void CCoreDispSurface::GetSAxis( Vector& axis ) -{ - VectorCopy( sAxis, axis ); -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -inline void CCoreDispSurface::SetTAxis( Vector const &axis ) -{ - VectorCopy( axis, tAxis ); -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -inline void CCoreDispSurface::GetTAxis( Vector& axis ) -{ - VectorCopy( tAxis, axis ); -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -inline void CCoreDispSurface::SetPointStartIndex( int index ) -{ - Assert( index >= 0 ); - Assert( index < QUAD_POINT_COUNT ); - m_PointStartIndex = index; -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -inline int CCoreDispSurface::GetPointStartIndex( void ) -{ - return m_PointStartIndex; -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -inline void CCoreDispSurface::SetPointStart( Vector const& pt ) -{ - VectorCopy( pt, m_PointStart ); -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -inline void CCoreDispSurface::GetPointStart( Vector& pt ) -{ - VectorCopy( m_PointStart, pt ); -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -inline void CCoreDispSurface::GetNormal( Vector& normal ) -{ - // - // calculate the displacement surface normal - // - Vector tmp[2]; - VectorSubtract( m_Points[1], m_Points[0], tmp[0] ); - VectorSubtract( m_Points[3], m_Points[0], tmp[1] ); - CrossProduct( tmp[1], tmp[0], normal ); - VectorNormalize( normal ); -} - - -//========================================================================= -// -// Node Class (for displacement quad-tree) -// -class CCoreDispNode -{ -public: - - enum { MAX_NEIGHBOR_NODE_COUNT = 4 }; - enum { MAX_NEIGHBOR_VERT_COUNT = 8 }; - enum { MAX_SURF_AT_NODE_COUNT = 8 }; - - //========================================================================= - // - // Initialization - // - void Init( void ); - - //========================================================================= - // - // - // - inline void SetBoundingBox( Vector const& bMin, Vector const& bMax ); - inline void GetBoundingBox( Vector& bMin, Vector& bMax ); - - inline void SetErrorTerm( float errorTerm ); - inline float GetErrorTerm( void ); - - inline void SetNeighborNodeIndex( int dir, int index ); - inline int GetNeighborNodeIndex( int dir ); - - inline void SetCenterVertIndex( int index ); - inline int GetCenterVertIndex( void ); - inline void SetNeighborVertIndex( int dir, int index ); - inline int GetNeighborVertIndex( int dir ); - - inline void SetTriBoundingBox( int index, Vector const& bMin, Vector const& bMax ); - inline void GetTriBoundingBox( int index, Vector& bMin, Vector& bMax ); - inline void SetTriPlane( int index, Vector const& normal, float dist ); - inline void GetTriPlane( int index, cplane_t *plane ); - - inline void SetRayBoundingBox( int index, Vector const& bMin, Vector const& bMax ); - inline void GetRayBoundingBox( int index, Vector& bMin, Vector& bMax ); - - //========================================================================= - // - // Node Functions (friend functions) - // - friend int GetNodeLevel( int index ); - friend int GetNodeCount( int power ); - friend int GetNodeParent( int index ); - friend int GetNodeChild( int power, int index, int direction ); - friend int GetNodeNeighborNode( int power, int index, int direction, int level ); - friend int GetNodeNeighborNodeFromNeighborSurf( int power, int index, int direction, int level, int neighborOrient ); - friend int GetNodeMinNodeAtLevel( int level ); - - friend void GetDispNodeTriVerts( CCoreDispInfo *pDisp, int nodeIndex, int triIndex, float *v1, float *v2, float *v3 ); - - friend void GetComponentsFromNodeIndex( int index, int *x, int *y ); - friend int GetNodeIndexFromComponents( int x, int y ); - -protected: - - Vector m_BBox[2]; // displacement node bounding box (take into account size of children) - float m_ErrorTerm; // LOD error term (the "precision" of the representation of the surface at this node's level) - int m_VertIndex; // the node's vertex index (center vertex of node) - int m_NeighborVertIndices[MAX_NEIGHBOR_VERT_COUNT]; // all other vertex indices in node (maximally creates 8 trianglar surfaces) - Vector m_SurfBBoxes[MAX_SURF_AT_NODE_COUNT][2]; // surface bounding boxes - old method - cplane_t m_SurfPlanes[MAX_SURF_AT_NODE_COUNT]; // surface plane info - old method - - Vector m_RayBBoxes[4][2]; // bounding boxes for ray traces -}; - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -inline void CCoreDispNode::SetBoundingBox( Vector const& bMin, Vector const& bMax ) -{ - VectorCopy( bMin, m_BBox[0] ); - VectorCopy( bMax, m_BBox[1] ); -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -inline void CCoreDispNode::GetBoundingBox( Vector& bMin, Vector& bMax ) -{ - VectorCopy( m_BBox[0], bMin ); - VectorCopy( m_BBox[1], bMax ); -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -inline void CCoreDispNode::SetErrorTerm( float errorTerm ) -{ - m_ErrorTerm = errorTerm; -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -inline float CCoreDispNode::GetErrorTerm( void ) -{ - return m_ErrorTerm; -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -inline void CCoreDispNode::SetCenterVertIndex( int index ) -{ - m_VertIndex = index; -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -inline int CCoreDispNode::GetCenterVertIndex( void ) -{ - return m_VertIndex; -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -inline void CCoreDispNode::SetNeighborVertIndex( int dir, int index ) -{ - Assert( dir >= 0 ); - Assert( dir < MAX_NEIGHBOR_VERT_COUNT ); - m_NeighborVertIndices[dir] = index; -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -inline int CCoreDispNode::GetNeighborVertIndex( int dir ) -{ - Assert( dir >= 0 ); - Assert( dir < MAX_NEIGHBOR_VERT_COUNT ); - return m_NeighborVertIndices[dir]; -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -inline void CCoreDispNode::SetTriBoundingBox( int index, Vector const& bMin, Vector const& bMax ) -{ - Assert( index >= 0 ); - Assert( index < MAX_SURF_AT_NODE_COUNT ); - VectorCopy( bMin, m_SurfBBoxes[index][0] ); - VectorCopy( bMax, m_SurfBBoxes[index][1] ); -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -inline void CCoreDispNode::GetTriBoundingBox( int index, Vector& bMin, Vector& bMax ) -{ - Assert( index >= 0 ); - Assert( index < MAX_SURF_AT_NODE_COUNT ); - VectorCopy( m_SurfBBoxes[index][0], bMin ); - VectorCopy( m_SurfBBoxes[index][1], bMax ); -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -inline void CCoreDispNode::SetTriPlane( int index, Vector const &normal, float dist ) -{ - Assert( index >= 0 ); - Assert( index < MAX_SURF_AT_NODE_COUNT ); - VectorCopy( normal, m_SurfPlanes[index].normal ); - m_SurfPlanes[index].dist = dist; -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -inline void CCoreDispNode::GetTriPlane( int index, cplane_t *plane ) -{ - Assert( index >= 0 ); - Assert( index < MAX_SURF_AT_NODE_COUNT ); - VectorCopy( m_SurfPlanes[index].normal, plane->normal ); - plane->dist = m_SurfPlanes[index].dist; -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -inline void CCoreDispNode::SetRayBoundingBox( int index, Vector const &bMin, Vector const &bMax ) -{ - Assert( index >= 0 ); - Assert( index < 4 ); - VectorCopy( bMin, m_RayBBoxes[index][0] ); - VectorCopy( bMax, m_RayBBoxes[index][1] ); -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -inline void CCoreDispNode::GetRayBoundingBox( int index, Vector& bMin, Vector& bMax ) -{ - Assert( index >= 0 ); - Assert( index < 4 ); - VectorCopy( m_RayBBoxes[index][0], bMin ); - VectorCopy( m_RayBBoxes[index][1], bMax ); -} - - -//============================================================================= -// -// CCoreInfoBuilder - the primary data necessay to derive a displacement surface -// used by WorldCraft (CMapFace, CMapDisp), VRAD (dface_t, ddispinto_t), -// and the engine (msurface_t, CDispInfo) -// - -struct CoreDispVert_t -{ - Vector m_FieldVector; // displacement vector field - float m_FieldDistance; // the distances along the displacement vector normal - - Vector m_SubdivNormal; - Vector m_SubdivPos; // used the create curvature of displacements - - // generated displacement surface data - Vector m_Vert; // displacement surface vertices - Vector m_FlatVert; - Vector m_Normal; // displacement surface normals - Vector m_TangentS; // use in calculating the tangent space axes - Vector m_TangentT; // use in calculating the tangent space axes - Vector2D m_TexCoord; // displacement surface texture coordinates - Vector2D m_LuxelCoords[NUM_BUMP_VECTS+1]; // displacement surface lightmap coordinates - - // additional per-vertex data - float m_Alpha; // displacement alpha values (per displacement vertex) -}; - -// New, need to use this at the node level -#define COREDISPTRI_TAG_WALKABLE (1<<0) -#define COREDISPTRI_TAG_FORCE_WALKABLE_BIT (1<<1) -#define COREDISPTRI_TAG_FORCE_WALKABLE_VAL (1<<2) -#define COREDISPTRI_TAG_BUILDABLE (1<<3) -#define COREDISPTRI_TAG_FORCE_BUILDABLE_BIT (1<<4) -#define COREDISPTRI_TAG_FORCE_BUILDABLE_VAL (1<<5) - -struct CoreDispTri_t -{ - unsigned short m_iIndex[3]; // the three indices that make up a triangle - unsigned short m_uiTags; // walkable, buildable, etc. -}; - -class CCoreDispInfo : public CDispUtilsHelper -{ -public: - - // - // tree and displacement surface directions - // - enum { WEST = 0, - NORTH = 1, - EAST = 2, - SOUTH = 3, - SOUTHWEST = 4, - SOUTHEAST = 5, - NORTHWEST = 6, - NORTHEAST = 7 }; - -#if 0 - // - // building parameters - // - enum { BUILD_NORMALS = 0x1, - BUILD_TEXCOORDS = 0x2, - BUILD_LIGHTCOORDS = 0x4, - BUILD_LODTREE = 0x8, - BUILD_COLLISION = 0x10, - BUILD_TANGENTSPACE = 0x20 }; -#endif - - // - // surface info flags - // - enum { SURF_BUMPED = 0x1, - SURF_NOPHYSICS_COLL = 0x2, - SURF_NOHULL_COLL = 0x4, - SURF_NORAY_COLL = 0x8 }; - - enum { MAX_DISP_POWER = MAX_MAP_DISP_POWER }; - enum { MAX_VERT_COUNT = MAX_DISPVERTS }; - enum { MAX_NODE_COUNT = 85 }; - - -// Convert from a CDispUtilsHelper. -public: - - static CCoreDispInfo* FromDispUtils( CDispUtilsHelper *p ) { return (CCoreDispInfo*)p; } - - -// CDispUtilsHelper implementation. -public: - - virtual CDispNeighbor* GetEdgeNeighbor( int index ); - virtual CDispCornerNeighbors* GetCornerNeighbors( int index ); - virtual const CPowerInfo* GetPowerInfo() const; - virtual CDispUtilsHelper* GetDispUtilsByIndex( int index ); - - -public: - - //========================================================================= - // - // Creation/Destruction - // - CCoreDispInfo(); - ~CCoreDispInfo(); - - void InitSurf( int parentIndex, Vector points[4], Vector normals[4], - Vector2D texCoords[4], Vector2D lightCoords[4][4], int contents, int flags, - bool bGenerateSurfPointStart, Vector& startPoint, - bool bHasMappingAxes, Vector& uAxis, Vector& vAxis ); - - void InitDispInfo( int power, int minTess, float smoothingAngle, - float *alphas, Vector *dispVectorField, float *dispDistances ); - - // This just unpacks the contents of the verts into arrays and calls InitDispInfo. - void InitDispInfo( int power, int minTess, float smoothingAngle, const CDispVert *pVerts, const CDispTri *pTris ); - -// bool Create( int creationFlags ); - bool Create( void ); - bool CreateWithoutLOD( void ); - - //========================================================================= - // - // Parameter "Wrappers" - // - CCoreDispSurface* GetSurface() { return &m_Surf; } - const CCoreDispSurface* GetSurface() const { return &m_Surf; } - - inline CCoreDispNode *GetNode( int index ); - - inline void SetPower( int power ); - inline int GetPower( void ) const; - inline int GetPostSpacing( void ); - inline int GetWidth( void ); - inline int GetHeight( void ); - inline int GetSize( void ) const; - - // Use this disp as a CDispUtils. - void SetDispUtilsHelperInfo( CCoreDispInfo **ppListBase, int listSize ); - - void SetNeighborData( const CDispNeighbor edgeNeighbors[4], const CDispCornerNeighbors cornerNeighbors[4] ) { GetSurface()->SetNeighborData( edgeNeighbors, cornerNeighbors ); } - - // Get a corner point. Indexed by the CORNER_ defines. - const CVertIndex& GetCornerPointIndex( int index ) const { return GetPowerInfo()->GetCornerPointIndex( index ); } - const Vector& GetCornerPoint( int index ) const { return GetVert( VertIndexToInt( GetCornerPointIndex( index ) ) ); } - - inline void SetVert( int index, Vector const& vert ); - inline void GetVert( int index, Vector& vert ) const; - - inline const Vector& GetVert( int index ) const; - inline const Vector& GetVert( const CVertIndex &index ) const; - - inline void GetFlatVert( int index, Vector& vert ) const; - inline void SetFlatVert( int index, const Vector &vert ); - - inline void GetNormal( int index, Vector& normal ) const; - inline const Vector& GetNormal( int index ) const; - inline const Vector& GetNormal( const CVertIndex &index ) const; - inline void SetNormal( int index, Vector const& normal ); - inline void SetNormal( const CVertIndex &index, Vector const& normal ); - - inline void GetTangentS( int index, Vector& tangentS ) const; - inline void GetTangentT( int index, Vector& tangentT ) const; - inline void SetTexCoord( int index, Vector2D const& texCoord ); - inline void GetTexCoord( int index, Vector2D& texCoord ) const; - - inline void SetLuxelCoord( int bumpIndex, int index, Vector2D const& luxelCoord ); - inline void GetLuxelCoord( int bumpIndex, int index, Vector2D& luxelCoord ) const; - - inline void SetAlpha( int index, float alpha ); - inline float GetAlpha( int index ); - - int GetTriCount( void ); - void GetTriIndices( int iTri, unsigned short &v1, unsigned short &v2, unsigned short &v3 ); - void SetTriIndices( int iTri, unsigned short v1, unsigned short v2, unsigned short v3 ); - void GetTriPos( int iTri, Vector &v1, Vector &v2, Vector &v3 ); - inline void SetTriTag( int iTri, unsigned short nTag ) { m_pTris[iTri].m_uiTags |= nTag; } - inline void ResetTriTag( int iTri, unsigned short nTag ) { m_pTris[iTri].m_uiTags &= ~nTag; } - inline void ToggleTriTag( int iTri, unsigned short nTag ) { m_pTris[iTri].m_uiTags ^= nTag; } - inline bool IsTriTag( int iTri, unsigned short nTag ) { return ( ( m_pTris[iTri].m_uiTags & nTag ) != 0 ); } - inline unsigned short GetTriTagValue( int iTri ) { return m_pTris[iTri].m_uiTags; } - inline void SetTriTagValue( int iTri, unsigned short nVal ) { m_pTris[iTri].m_uiTags = nVal; } - - bool IsTriWalkable( int iTri ); - bool IsTriBuildable( int iTri ); - - inline void SetElevation( float elevation ); - inline float GetElevation( void ); - - inline void ResetFieldVectors( void ); - inline void SetFieldVector( int index, Vector const &v ); - inline void GetFieldVector( int index, Vector& v ); - inline void ResetFieldDistances( void ); - inline void SetFieldDistance( int index, float dist ); - inline float GetFieldDistance( int index ); - - inline void ResetSubdivPositions( void ); - inline void SetSubdivPosition( int ndx, Vector const &v ); - inline void GetSubdivPosition( int ndx, Vector& v ); - - inline void ResetSubdivNormals( void ); - inline void SetSubdivNormal( int ndx, Vector const &v ); - inline void GetSubdivNormal( int ndx, Vector &v ); - - inline void SetRenderIndexCount( int count ); - inline int GetRenderIndexCount( void ); - inline void SetRenderIndex( int index, int triIndex ); - inline int GetRenderIndex( int index ); - - inline CoreDispVert_t *GetDispVert( int iVert ) { return &m_pVerts[iVert]; } - inline CoreDispVert_t *GetDispVertList(); - inline unsigned short *GetRenderIndexList( void ); - - inline void SetTouched( bool touched ); - inline bool IsTouched( void ); - - void CalcDispSurfCoords( bool bLightMap, int lightmapID ); - void GetPositionOnSurface( float u, float v, Vector &vPos, Vector *pNormal, float *pAlpha ); - - void DispUVToSurf( Vector2D const &dispUV, Vector &vecPoint, Vector *pNormal, float *pAlpha ); - void BaseFacePlaneToDispUV( Vector const &planePt, Vector2D &dispUV ); - bool SurfToBaseFacePlane( Vector const &surfPt, Vector &planePt ); - - const CDispCornerNeighbors* GetCornerNeighbors( int iCorner ) const { return GetSurface()->GetCornerNeighbors( iCorner ); } - const CDispNeighbor* GetEdgeNeighbor( int iEdge ) const { return GetSurface()->GetEdgeNeighbor( iEdge ); } - - void SetListIndex( int nIndex ) { m_nListIndex = nIndex; } - int GetListIndex( void ) { return m_nListIndex; } - - CBitVec& GetAllowedVerts() { return m_AllowedVerts; } - const CBitVec& GetAllowedVerts() const { return m_AllowedVerts; } - void AllowedVerts_Clear( void ) { m_AllowedVerts.SetAll(); } - int AllowedVerts_GetNumDWords() const { return m_AllowedVerts.GetNumDWords(); } - unsigned long AllowedVerts_GetDWord(int i) const { return m_AllowedVerts.GetDWord( i ); } - void AllowedVerts_SetDWord(int i, unsigned long val) { m_AllowedVerts.SetDWord( i, val ); } - - - void Position_Update( int iVert, Vector vecPos ); - - //========================================================================= - // - // friend functions - // - friend void SmoothNeighboringDispSurfNormals( CCoreDispInfo **ppCoreDispInfoList, int listSize ); - -private: - // be changed to match the paint normal next pass) - // LOD/collision node data - CCoreDispNode *m_Nodes; // LOD quad-tree nodes - - float m_Elevation; // distance along the subdivision normal (should - - // defines the size of the displacement surface - int m_Power; // "size" of the displacement map - - // base surface data - CCoreDispSurface m_Surf; // surface containing displacement data - // be changed to match the paint normal next pass) - // Vertex data.. - CoreDispVert_t *m_pVerts; - - // Triangle data.. - CoreDispTri_t *m_pTris; - - // render specific data - int m_RenderIndexCount; // number of indices used in rendering - unsigned short *m_RenderIndices; // rendering index list (list of triangles) - int m_RenderCounter; // counter to verify surfaces are renderered/collided with only once per frame - - // utility data - bool m_bTouched; // touched flag - CCoreDispInfo *m_pNext; // used for chaining - - // The list that this disp is in (used for CDispUtils::IHelper implementation). - CCoreDispInfo **m_ppListBase; - int m_ListSize; - - CBitVec m_AllowedVerts; // Built in VBSP. Defines which verts are allowed to exist based on what the neighbors are. - - int m_nListIndex; - - //========================================================================= - // - // Creation Functions - // - - void GenerateDispSurf( void ); - void GenerateDispSurfNormals( void ); - void GenerateDispSurfTangentSpaces( void ); - bool DoesEdgeExist( int indexRow, int indexCol, int direction, int postSpacing ); - void CalcNormalFromEdges( int indexRow, int indexCol, bool bIsEdge[4], Vector& normal ); - void CalcDispSurfAlphas( void ); - void GenerateLODTree( void ); - void CalcVertIndicesAtNodes( int nodeIndex ); - int GetNodeVertIndexFromParentIndex( int level, int parentVertIndex, int direction ); - void CalcNodeInfo( int nodeIndex, int terminationLevel ); - void CalcNeighborVertIndicesAtNode( int nodeIndex, int level ); - void CalcNeighborNodeIndicesAtNode( int nodeIndex, int level ); - void CalcErrorTermAtNode( int nodeIndex, int level ); - float GetMaxErrorFromChildren( int nodeIndex, int level ); - void CalcBoundingBoxAtNode( int nodeIndex ); - void CalcMinMaxBoundingBoxAtNode( int nodeIndex, Vector& bMin, Vector& bMax ); - void CalcTriSurfInfoAtNode( int nodeIndex ); - void CalcTriSurfIndices( int nodeIndex, int indices[8][3] ); - void CalcTriSurfBoundingBoxes( int nodeIndex, int indices[8][3] ); - void CalcRayBoundingBoxes( int nodeIndex, int indices[8][3] ); - void CalcTriSurfPlanes( int nodeIndex, int indices[8][3] ); - void GenerateCollisionData( void ); - void GenerateCollisionSurface( void ); - - void CreateBoundingBoxes( CoreDispBBox_t *pBBox, int count ); - - void DispUVToSurf_TriTLToBR( Vector &vecPoint, Vector *pNormal, float *pAlpha, float flU, float flV, const Vector &vecIntersectPoint ); - void DispUVToSurf_TriBLToTR( Vector &vecPoint, Vector *pNormal, float *pAlpha, float flU, float flV, const Vector &vecIntersectPoint ); - void DispUVToSurf_TriTLToBR_1( const Vector &vecIntersectPoint, int nSnapU, int nNextU, int nSnapV, int nNextV, Vector &vecPoint, Vector *pNormal, float *pAlpha, bool bBackup ); - void DispUVToSurf_TriTLToBR_2( const Vector &vecIntersectPoint, int nSnapU, int nNextU, int nSnapV, int nNextV, Vector &vecPoint, Vector *pNormal, float *pAlpha, bool bBackup ); - void DispUVToSurf_TriBLToTR_1( const Vector &vecIntersectPoint, int nSnapU, int nNextU, int nSnapV, int nNextV, Vector &vecPoint, Vector *pNormal, float *pAlpha, bool bBackup ); - void DispUVToSurf_TriBLToTR_2( const Vector &vecIntersectPoint, int nSnapU, int nNextU, int nSnapV, int nNextV, Vector &vecPoint, Vector *pNormal, float *pAlpha, bool bBackup ); - - void GetTriangleIndicesForDispBBox( int nIndex, int nTris[2][3] ); - - void BuildTriTLtoBR( int ndx ); - void BuildTriBLtoTR( int ndx ); - - void InitTris( void ); - void CreateTris( void ); -}; - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -inline void CCoreDispInfo::SetPower( int power ) -{ - m_Power = power; -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -inline int CCoreDispInfo::GetPower( void ) const -{ - return m_Power; -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -inline int CCoreDispInfo::GetPostSpacing( void ) -{ - return ( ( 1 << m_Power ) + 1 ); -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -inline int CCoreDispInfo::GetWidth( void ) -{ - return ( ( 1 << m_Power ) + 1 ); -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -inline int CCoreDispInfo::GetHeight( void ) -{ - return ( ( 1 << m_Power ) + 1 ); -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -inline int CCoreDispInfo::GetSize( void ) const -{ - return ( ( ( 1 << m_Power ) + 1 ) * ( ( 1 << m_Power ) + 1 ) ); -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -inline void CCoreDispInfo::SetVert( int index, Vector const &vert ) -{ - Assert( index >= 0 ); - Assert( index < MAX_VERT_COUNT ); - VectorCopy( vert, m_pVerts[index].m_Vert ); -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -inline void CCoreDispInfo::GetVert( int index, Vector& vert ) const -{ - Assert( index >= 0 ); - Assert( index < MAX_VERT_COUNT ); - VectorCopy( m_pVerts[index].m_Vert, vert ); -} - - -inline const Vector& CCoreDispInfo::GetVert( int index ) const -{ - Assert( index >= 0 ); - Assert( index < MAX_VERT_COUNT ); - return m_pVerts[index].m_Vert; -} - -inline const Vector& CCoreDispInfo::GetVert( const CVertIndex &index ) const -{ - return GetVert( VertIndexToInt( index ) ); -} - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -inline void CCoreDispInfo::GetFlatVert( int index, Vector& vert ) const -{ - Assert( index >= 0 ); - Assert( index < MAX_VERT_COUNT ); - VectorCopy( m_pVerts[index].m_FlatVert, vert ); -} - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -inline void CCoreDispInfo::SetFlatVert( int index, const Vector &vert ) -{ - Assert( index >= 0 ); - Assert( index < MAX_VERT_COUNT ); - VectorCopy( vert, m_pVerts[index].m_FlatVert ); -} - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -inline void CCoreDispInfo::SetNormal( int index, Vector const &normal ) -{ - Assert( index >= 0 ); - Assert( index < MAX_VERT_COUNT ); - VectorCopy( normal, m_pVerts[index].m_Normal ); -} - - -inline void CCoreDispInfo::SetNormal( const CVertIndex &index, Vector const &normal ) -{ - SetNormal( VertIndexToInt( index ), normal ); -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -inline void CCoreDispInfo::GetNormal( int index, Vector& normal ) const -{ - Assert( index >= 0 ); - Assert( index < MAX_VERT_COUNT ); - VectorCopy( m_pVerts[index].m_Normal, normal ); -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -inline const Vector& CCoreDispInfo::GetNormal( int index ) const -{ - Assert( index >= 0 ); - Assert( index < MAX_VERT_COUNT ); - return m_pVerts[index].m_Normal; -} - - -inline const Vector& CCoreDispInfo::GetNormal( const CVertIndex &index ) const -{ - return GetNormal( VertIndexToInt( index ) ); -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -inline void CCoreDispInfo::GetTangentS( int index, Vector& tangentS ) const -{ - Assert( index >= 0 ); - Assert( index < GetSize() ); - VectorCopy( m_pVerts[index].m_TangentS, tangentS ); -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -inline void CCoreDispInfo::GetTangentT( int index, Vector& tangentT ) const -{ - Assert( index >= 0 ); - Assert( index < GetSize() ); - VectorCopy( m_pVerts[index].m_TangentT, tangentT ); -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -inline void CCoreDispInfo::SetTexCoord( int index, Vector2D const& texCoord ) -{ - Assert( index >= 0 ); - Assert( index < GetSize() ); - Vector2DCopy( texCoord, m_pVerts[index].m_TexCoord ); -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -inline void CCoreDispInfo::GetTexCoord( int index, Vector2D& texCoord ) const -{ - Assert( index >= 0 ); - Assert( index < GetSize() ); - Vector2DCopy( m_pVerts[index].m_TexCoord, texCoord ); -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -inline void CCoreDispInfo::SetLuxelCoord( int bumpIndex, int index, Vector2D const& luxelCoord ) -{ - Assert( index >= 0 ); - Assert( index < GetSize() ); - Assert( bumpIndex >= 0 ); - Assert( bumpIndex < NUM_BUMP_VECTS + 1 ); - Vector2DCopy( luxelCoord, m_pVerts[index].m_LuxelCoords[bumpIndex] ); -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -inline void CCoreDispInfo::GetLuxelCoord( int bumpIndex, int index, Vector2D& luxelCoord ) const -{ - Assert( index >= 0 ); - Assert( index < MAX_VERT_COUNT ); - Assert( bumpIndex >= 0 ); - Assert( bumpIndex < NUM_BUMP_VECTS + 1 ); - Vector2DCopy( m_pVerts[index].m_LuxelCoords[bumpIndex], luxelCoord ); -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -inline void CCoreDispInfo::SetAlpha( int index, float alpha ) -{ - Assert( index >= 0 ); - Assert( index < MAX_VERT_COUNT ); - m_pVerts[index].m_Alpha = alpha; -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -inline float CCoreDispInfo::GetAlpha( int index ) -{ - Assert( index >= 0 ); - Assert( index < MAX_VERT_COUNT ); - return m_pVerts[index].m_Alpha; -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -inline void CCoreDispInfo::SetElevation( float elevation ) -{ - m_Elevation = elevation; -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -inline float CCoreDispInfo::GetElevation( void ) -{ - return m_Elevation; -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -inline void CCoreDispInfo::ResetFieldVectors( void ) -{ -// Vector normal; -// m_Surf.GetNormal( normal ); - - int size = GetSize(); - for( int i = 0; i < size; i++ ) - { - m_pVerts[i].m_FieldVector.Init(); -// m_FieldVectors[i] = normal; - } -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -inline void CCoreDispInfo::SetFieldVector( int index, Vector const &v ) -{ - Assert( index >= 0 ); - Assert( index < MAX_VERT_COUNT ); - VectorCopy( v, m_pVerts[index].m_FieldVector ); -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -inline void CCoreDispInfo::GetFieldVector( int index, Vector& v ) -{ - Assert( index >= 0 ); - Assert( index < MAX_VERT_COUNT ); - VectorCopy( m_pVerts[index].m_FieldVector, v ); -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -inline void CCoreDispInfo::ResetSubdivPositions( void ) -{ - int size = GetSize(); - for( int i = 0; i < size; i++ ) - { - m_pVerts[i].m_SubdivPos.Init(); - } -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -inline void CCoreDispInfo::SetSubdivPosition( int ndx, Vector const &v ) -{ - Assert( ndx >= 0 ); - Assert( ndx < MAX_VERT_COUNT ); - m_pVerts[ndx].m_SubdivPos = v; -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -inline void CCoreDispInfo::GetSubdivPosition( int ndx, Vector& v ) -{ - Assert( ndx >= 0 ); - Assert( ndx < MAX_VERT_COUNT ); - v = m_pVerts[ndx].m_SubdivPos; -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -inline void CCoreDispInfo::ResetSubdivNormals( void ) -{ - Vector normal; - m_Surf.GetNormal( normal ); - - int size = GetSize(); - for( int i = 0; i < size; i++ ) - { - m_pVerts[i].m_SubdivNormal = normal; - } -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -inline void CCoreDispInfo::SetSubdivNormal( int ndx, Vector const &v ) -{ - Assert( ndx >= 0 ); - Assert( ndx < MAX_VERT_COUNT ); - m_pVerts[ndx].m_SubdivNormal = v; -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -inline void CCoreDispInfo::GetSubdivNormal( int ndx, Vector &v ) -{ - Assert( ndx >= 0 ); - Assert( ndx < MAX_VERT_COUNT ); - v = m_pVerts[ndx].m_SubdivNormal; -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -inline void CCoreDispInfo::ResetFieldDistances( void ) -{ - int size = GetSize(); - for( int i = 0; i < size; i++ ) - { - m_pVerts[i].m_FieldDistance = 0.0f; - } -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -inline void CCoreDispInfo::SetFieldDistance( int index, float dist ) -{ - Assert( index >= 0 ); - Assert( index < GetSize() ); - m_pVerts[index].m_FieldDistance = dist; -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -inline float CCoreDispInfo::GetFieldDistance( int index ) -{ - Assert( index >= 0 ); - Assert( index < GetSize() ); - return m_pVerts[index].m_FieldDistance; -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -inline void CCoreDispInfo::SetRenderIndexCount( int count ) -{ - m_RenderIndexCount = count; -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -inline int CCoreDispInfo::GetRenderIndexCount( void ) -{ - return m_RenderIndexCount; -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -inline void CCoreDispInfo::SetRenderIndex( int index, int triIndex ) -{ - Assert( index >= 0 ); - Assert( index < ( MAX_VERT_COUNT*2*3) ); - m_RenderIndices[index] = triIndex; -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -inline int CCoreDispInfo::GetRenderIndex( int index ) -{ - Assert( index >= 0 ); - Assert( index < ( MAX_VERT_COUNT*2*3) ); - return m_RenderIndices[index]; -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -inline CoreDispVert_t *CCoreDispInfo::GetDispVertList() -{ - return m_pVerts; -} - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -inline unsigned short *CCoreDispInfo::GetRenderIndexList( void ) -{ - return &m_RenderIndices[0]; -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -inline void CCoreDispInfo::SetTouched( bool touched ) -{ - m_bTouched = touched; -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -inline bool CCoreDispInfo::IsTouched( void ) -{ - return m_bTouched; -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -inline CCoreDispNode *CCoreDispInfo::GetNode( int index ) -{ - Assert( index >= 0 ); - Assert( index < MAX_NODE_COUNT ); - return &m_Nodes[index]; -} - -bool CalcBarycentricCooefs( Vector const &v0, Vector const &v1, Vector const &v2, - Vector const &pt, float &c0, float &c1, float &c2 ); - -#endif // BUILDDISP_H +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $Workfile: $ +// $Date: $ +// $NoKeywords: $ +//=============================================================================// + +#ifndef BUILDDISP_H +#define BUILDDISP_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "commonmacros.h" +#include "tier0/dbg.h" +#include "bspfile.h" +#include "mathlib.h" +#include "bumpvects.h" +#include "disp_common.h" +#include "bitvec.h" + +#define DISP_ALPHA_PROP_DELTA 382.5f + +class CCoreDispInfo; + +struct CoreDispBBox_t +{ + Vector vMin, vMax; +}; + +//========================================================================= +// +// Surface Class - interfacing class (fill in with MapFace, dface_t, and +// msurface_t) +// +class CCoreDispSurface +{ +public: + + enum { QUAD_POINT_COUNT = 4 }; + enum { MAX_CORNER_NEIGHBOR_COUNT = 16 }; + + CCoreDispSurface(); + + //========================================================================= + // + // initialization + // + void Init( void ); + + //========================================================================= + // + // parent surface id - index to CMapFace, dface_t, or msurface_t + // + inline void SetHandle( int handle ); + inline int GetHandle( void ); + + //========================================================================= + // + // vertex data - pos, normal, texture, lightmap, alpha, etc... + // + inline void SetPointCount( int count ); + inline int GetPointCount( void ) const; + + inline void SetPoint( int index, Vector const &pt ); + inline void GetPoint( int index, Vector& pt ) const; + inline Vector const& GetPoint( int index ) const; + + inline void SetPointNormal( int index, Vector const &normal ); + inline void GetPointNormal( int index, Vector &normal ); + inline void SetTexCoord( int index, Vector2D const& texCoord ); + inline void GetTexCoord( int index, Vector2D& texCoord ) const; + + inline void SetLuxelCoord( int bumpIndex, int index, Vector2D const& luxelCoord ); + inline void GetLuxelCoord( int bumpIndex, int index, Vector2D& luxelCoord ) const; + inline void SetLuxelCoords( int bumpIndex, Vector2D const coords[4] ); + inline void GetLuxelCoords( int bumpIndex, Vector2D coords[4] ) const; + + inline void SetAlpha( int index, float alpha ); + inline float GetAlpha( int const index ) const; + + //========================================================================= + // + // utils + // + inline void GetNormal( Vector& normal ); + inline void SetFlags( int flag ); + inline int GetFlags( void ); + inline void SetContents( int contents ); + inline int GetContents( void ); + + //========================================================================= + // + // create utils (texture axis not use anymore but here to support older maps) + // + inline void SetSAxis( Vector const &axis ); + inline void GetSAxis( Vector &axis ); + inline void SetTAxis( Vector const &axis ); + inline void GetTAxis( Vector &axis ); + + inline void SetPointStartIndex( int index ); + inline int GetPointStartIndex( void ); + inline void SetPointStart( Vector const &pt ); + inline void GetPointStart( Vector &pt ); + + // Used by the tools to set the neighbor data from the BSP file. + void SetNeighborData( const CDispNeighbor edgeNeighbors[4], const CDispCornerNeighbors cornerNeighbors[4] ); + + void GeneratePointStartIndexFromMappingAxes( Vector const &sAxis, Vector const &tAxis ); + int GenerateSurfPointStartIndex( void ); + int FindSurfPointStartIndex( void ); + void AdjustSurfPointData( void ); + + // Indexed by CORNER_ defines. + CDispCornerNeighbors* GetCornerNeighbors( int iCorner ) { Assert( iCorner >= 0 && iCorner < ARRAYSIZE( m_CornerNeighbors ) ); return &m_CornerNeighbors[iCorner]; } + const CDispCornerNeighbors* GetCornerNeighbors( int iCorner ) const { Assert( iCorner >= 0 && iCorner < ARRAYSIZE( m_CornerNeighbors ) ); return &m_CornerNeighbors[iCorner]; } + + // Indexed by CORNER_ defines. + int GetCornerNeighborCount( int iCorner ) const { return GetCornerNeighbors( iCorner )->m_nNeighbors; } + int GetCornerNeighbor( int iCorner, int iNeighbor ) const { Assert( iNeighbor >= 0 && iNeighbor < GetCornerNeighbors(iCorner)->m_nNeighbors ); return GetCornerNeighbors( iCorner )->m_Neighbors[iNeighbor]; } + + CDispNeighbor* GetEdgeNeighbor( int iEdge ) { Assert( iEdge >= 0 && iEdge < ARRAYSIZE( m_EdgeNeighbors ) ); return &m_EdgeNeighbors[iEdge]; } + const CDispNeighbor* GetEdgeNeighbor( int iEdge ) const { Assert( iEdge >= 0 && iEdge < ARRAYSIZE( m_EdgeNeighbors ) ); return &m_EdgeNeighbors[iEdge]; } + + +protected: + + int m_Index; // parent face (CMapFace, dface_t, msurface_t) index "handle" + + int m_PointCount; // number of points in the face (should be 4!) + Vector m_Points[QUAD_POINT_COUNT]; // points + Vector m_Normals[QUAD_POINT_COUNT]; // normals at points + Vector2D m_TexCoords[QUAD_POINT_COUNT]; // texture coordinates at points + Vector2D m_LuxelCoords[NUM_BUMP_VECTS+1][QUAD_POINT_COUNT]; // lightmap coordinates at points + float m_Alphas[QUAD_POINT_COUNT]; // alpha at points + + // Straight from the BSP file. + CDispNeighbor m_EdgeNeighbors[4]; + CDispCornerNeighbors m_CornerNeighbors[4]; + + int m_Flags; // surface flags - inherited from the "parent" face + int m_Contents; // contents flags - inherited from the "parent" face + + Vector sAxis; // used to generate start disp orientation (old method) + Vector tAxis; // used to generate start disp orientation (old method) + int m_PointStartIndex; // index to the starting point -- for saving starting point + Vector m_PointStart; // starting point used to determine the orientation of the displacement map on the surface +}; + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispSurface::SetHandle( int handle ) +{ + m_Index = handle; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline int CCoreDispSurface::GetHandle( void ) +{ + return m_Index; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispSurface::SetPointCount( int count ) +{ + // quad only -- currently! + if( count != 4 ) + return; + m_PointCount = count; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline int CCoreDispSurface::GetPointCount( void ) const +{ + return m_PointCount; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispSurface::SetPoint( int index, Vector const &pt ) +{ + Assert( index >= 0 ); + Assert( index < QUAD_POINT_COUNT ); + VectorCopy( pt, m_Points[index] ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispSurface::GetPoint( int index, Vector &pt ) const +{ + Assert( index >= 0 ); + Assert( index < QUAD_POINT_COUNT ); + VectorCopy( m_Points[index], pt ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline Vector const& CCoreDispSurface::GetPoint( int index ) const +{ + Assert( index >= 0 ); + Assert( index < QUAD_POINT_COUNT ); + return m_Points[index]; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispSurface::SetPointNormal( int index, Vector const &normal ) +{ + Assert( index >= 0 ); + Assert( index < QUAD_POINT_COUNT ); + VectorCopy( normal, m_Normals[index] ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispSurface::GetPointNormal( int index, Vector& normal ) +{ + Assert( index >= 0 ); + Assert( index < QUAD_POINT_COUNT ); + VectorCopy( m_Normals[index], normal ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispSurface::SetTexCoord( int index, Vector2D const& texCoord ) +{ + Assert( index >= 0 ); + Assert( index < QUAD_POINT_COUNT ); + Vector2DCopy( texCoord, m_TexCoords[index] ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispSurface::GetTexCoord( int index, Vector2D& texCoord ) const +{ + Assert( index >= 0 ); + Assert( index < QUAD_POINT_COUNT ); + Vector2DCopy( m_TexCoords[index], texCoord ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispSurface::SetLuxelCoord( int bumpIndex, int index, Vector2D const& luxelCoord ) +{ + Assert( index >= 0 ); + Assert( index < QUAD_POINT_COUNT ); + Assert( bumpIndex >= 0 ); + Assert( bumpIndex < NUM_BUMP_VECTS + 1 ); + Vector2DCopy( luxelCoord, m_LuxelCoords[bumpIndex][index] ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispSurface::GetLuxelCoord( int bumpIndex, int index, Vector2D& luxelCoord ) const +{ + Assert( index >= 0 ); + Assert( index < QUAD_POINT_COUNT ); + Assert( bumpIndex >= 0 ); + Assert( bumpIndex < NUM_BUMP_VECTS + 1 ); + Vector2DCopy( m_LuxelCoords[bumpIndex][index], luxelCoord ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispSurface::SetLuxelCoords( int bumpIndex, Vector2D const luxelCoords[4] ) +{ + Assert( bumpIndex >= 0 ); + Assert( bumpIndex < NUM_BUMP_VECTS + 1 ); + for( int i=0; i < 4; i++ ) + Vector2DCopy( luxelCoords[i], m_LuxelCoords[bumpIndex][i] ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispSurface::GetLuxelCoords( int bumpIndex, Vector2D luxelCoords[4] ) const +{ + Assert( bumpIndex >= 0 ); + Assert( bumpIndex < NUM_BUMP_VECTS + 1 ); + for( int i=0; i < 4; i++ ) + Vector2DCopy( m_LuxelCoords[bumpIndex][i], luxelCoords[i] ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispSurface::SetAlpha( int index, float alpha ) +{ + Assert( index >= 0 ); + Assert( index < QUAD_POINT_COUNT ); + m_Alphas[index] = alpha; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline float CCoreDispSurface::GetAlpha( int const index ) const +{ + Assert( index >= 0 ); + Assert( index < QUAD_POINT_COUNT ); + return m_Alphas[index]; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispSurface::SetFlags( int flag ) +{ + m_Flags = flag; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline int CCoreDispSurface::GetFlags( void ) +{ + return m_Flags; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispSurface::SetContents( int contents ) +{ + m_Contents = contents; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline int CCoreDispSurface::GetContents( void ) +{ + return m_Contents; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispSurface::SetSAxis( Vector const &axis ) +{ + VectorCopy( axis, sAxis ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispSurface::GetSAxis( Vector& axis ) +{ + VectorCopy( sAxis, axis ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispSurface::SetTAxis( Vector const &axis ) +{ + VectorCopy( axis, tAxis ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispSurface::GetTAxis( Vector& axis ) +{ + VectorCopy( tAxis, axis ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispSurface::SetPointStartIndex( int index ) +{ + Assert( index >= 0 ); + Assert( index < QUAD_POINT_COUNT ); + m_PointStartIndex = index; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline int CCoreDispSurface::GetPointStartIndex( void ) +{ + return m_PointStartIndex; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispSurface::SetPointStart( Vector const& pt ) +{ + VectorCopy( pt, m_PointStart ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispSurface::GetPointStart( Vector& pt ) +{ + VectorCopy( m_PointStart, pt ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispSurface::GetNormal( Vector& normal ) +{ + // + // calculate the displacement surface normal + // + Vector tmp[2]; + VectorSubtract( m_Points[1], m_Points[0], tmp[0] ); + VectorSubtract( m_Points[3], m_Points[0], tmp[1] ); + CrossProduct( tmp[1], tmp[0], normal ); + VectorNormalize( normal ); +} + + +//========================================================================= +// +// Node Class (for displacement quad-tree) +// +class CCoreDispNode +{ +public: + + enum { MAX_NEIGHBOR_NODE_COUNT = 4 }; + enum { MAX_NEIGHBOR_VERT_COUNT = 8 }; + enum { MAX_SURF_AT_NODE_COUNT = 8 }; + + //========================================================================= + // + // Initialization + // + void Init( void ); + + //========================================================================= + // + // + // + inline void SetBoundingBox( Vector const& bMin, Vector const& bMax ); + inline void GetBoundingBox( Vector& bMin, Vector& bMax ); + + inline void SetErrorTerm( float errorTerm ); + inline float GetErrorTerm( void ); + + inline void SetNeighborNodeIndex( int dir, int index ); + inline int GetNeighborNodeIndex( int dir ); + + inline void SetCenterVertIndex( int index ); + inline int GetCenterVertIndex( void ); + inline void SetNeighborVertIndex( int dir, int index ); + inline int GetNeighborVertIndex( int dir ); + + inline void SetTriBoundingBox( int index, Vector const& bMin, Vector const& bMax ); + inline void GetTriBoundingBox( int index, Vector& bMin, Vector& bMax ); + inline void SetTriPlane( int index, Vector const& normal, float dist ); + inline void GetTriPlane( int index, cplane_t *plane ); + + inline void SetRayBoundingBox( int index, Vector const& bMin, Vector const& bMax ); + inline void GetRayBoundingBox( int index, Vector& bMin, Vector& bMax ); + + //========================================================================= + // + // Node Functions (friend functions) + // + friend int GetNodeLevel( int index ); + friend int GetNodeCount( int power ); + friend int GetNodeParent( int index ); + friend int GetNodeChild( int power, int index, int direction ); + friend int GetNodeNeighborNode( int power, int index, int direction, int level ); + friend int GetNodeNeighborNodeFromNeighborSurf( int power, int index, int direction, int level, int neighborOrient ); + friend int GetNodeMinNodeAtLevel( int level ); + + friend void GetDispNodeTriVerts( CCoreDispInfo *pDisp, int nodeIndex, int triIndex, float *v1, float *v2, float *v3 ); + + friend void GetComponentsFromNodeIndex( int index, int *x, int *y ); + friend int GetNodeIndexFromComponents( int x, int y ); + +protected: + + Vector m_BBox[2]; // displacement node bounding box (take into account size of children) + float m_ErrorTerm; // LOD error term (the "precision" of the representation of the surface at this node's level) + int m_VertIndex; // the node's vertex index (center vertex of node) + int m_NeighborVertIndices[MAX_NEIGHBOR_VERT_COUNT]; // all other vertex indices in node (maximally creates 8 trianglar surfaces) + Vector m_SurfBBoxes[MAX_SURF_AT_NODE_COUNT][2]; // surface bounding boxes - old method + cplane_t m_SurfPlanes[MAX_SURF_AT_NODE_COUNT]; // surface plane info - old method + + Vector m_RayBBoxes[4][2]; // bounding boxes for ray traces +}; + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispNode::SetBoundingBox( Vector const& bMin, Vector const& bMax ) +{ + VectorCopy( bMin, m_BBox[0] ); + VectorCopy( bMax, m_BBox[1] ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispNode::GetBoundingBox( Vector& bMin, Vector& bMax ) +{ + VectorCopy( m_BBox[0], bMin ); + VectorCopy( m_BBox[1], bMax ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispNode::SetErrorTerm( float errorTerm ) +{ + m_ErrorTerm = errorTerm; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline float CCoreDispNode::GetErrorTerm( void ) +{ + return m_ErrorTerm; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispNode::SetCenterVertIndex( int index ) +{ + m_VertIndex = index; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline int CCoreDispNode::GetCenterVertIndex( void ) +{ + return m_VertIndex; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispNode::SetNeighborVertIndex( int dir, int index ) +{ + Assert( dir >= 0 ); + Assert( dir < MAX_NEIGHBOR_VERT_COUNT ); + m_NeighborVertIndices[dir] = index; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline int CCoreDispNode::GetNeighborVertIndex( int dir ) +{ + Assert( dir >= 0 ); + Assert( dir < MAX_NEIGHBOR_VERT_COUNT ); + return m_NeighborVertIndices[dir]; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispNode::SetTriBoundingBox( int index, Vector const& bMin, Vector const& bMax ) +{ + Assert( index >= 0 ); + Assert( index < MAX_SURF_AT_NODE_COUNT ); + VectorCopy( bMin, m_SurfBBoxes[index][0] ); + VectorCopy( bMax, m_SurfBBoxes[index][1] ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispNode::GetTriBoundingBox( int index, Vector& bMin, Vector& bMax ) +{ + Assert( index >= 0 ); + Assert( index < MAX_SURF_AT_NODE_COUNT ); + VectorCopy( m_SurfBBoxes[index][0], bMin ); + VectorCopy( m_SurfBBoxes[index][1], bMax ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispNode::SetTriPlane( int index, Vector const &normal, float dist ) +{ + Assert( index >= 0 ); + Assert( index < MAX_SURF_AT_NODE_COUNT ); + VectorCopy( normal, m_SurfPlanes[index].normal ); + m_SurfPlanes[index].dist = dist; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispNode::GetTriPlane( int index, cplane_t *plane ) +{ + Assert( index >= 0 ); + Assert( index < MAX_SURF_AT_NODE_COUNT ); + VectorCopy( m_SurfPlanes[index].normal, plane->normal ); + plane->dist = m_SurfPlanes[index].dist; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispNode::SetRayBoundingBox( int index, Vector const &bMin, Vector const &bMax ) +{ + Assert( index >= 0 ); + Assert( index < 4 ); + VectorCopy( bMin, m_RayBBoxes[index][0] ); + VectorCopy( bMax, m_RayBBoxes[index][1] ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispNode::GetRayBoundingBox( int index, Vector& bMin, Vector& bMax ) +{ + Assert( index >= 0 ); + Assert( index < 4 ); + VectorCopy( m_RayBBoxes[index][0], bMin ); + VectorCopy( m_RayBBoxes[index][1], bMax ); +} + + +//============================================================================= +// +// CCoreInfoBuilder - the primary data necessay to derive a displacement surface +// used by WorldCraft (CMapFace, CMapDisp), VRAD (dface_t, ddispinto_t), +// and the engine (msurface_t, CDispInfo) +// + +struct CoreDispVert_t +{ + Vector m_FieldVector; // displacement vector field + float m_FieldDistance; // the distances along the displacement vector normal + + Vector m_SubdivNormal; + Vector m_SubdivPos; // used the create curvature of displacements + + // generated displacement surface data + Vector m_Vert; // displacement surface vertices + Vector m_FlatVert; + Vector m_Normal; // displacement surface normals + Vector m_TangentS; // use in calculating the tangent space axes + Vector m_TangentT; // use in calculating the tangent space axes + Vector2D m_TexCoord; // displacement surface texture coordinates + Vector2D m_LuxelCoords[NUM_BUMP_VECTS+1]; // displacement surface lightmap coordinates + + // additional per-vertex data + float m_Alpha; // displacement alpha values (per displacement vertex) +}; + +// New, need to use this at the node level +#define COREDISPTRI_TAG_WALKABLE (1<<0) +#define COREDISPTRI_TAG_FORCE_WALKABLE_BIT (1<<1) +#define COREDISPTRI_TAG_FORCE_WALKABLE_VAL (1<<2) +#define COREDISPTRI_TAG_BUILDABLE (1<<3) +#define COREDISPTRI_TAG_FORCE_BUILDABLE_BIT (1<<4) +#define COREDISPTRI_TAG_FORCE_BUILDABLE_VAL (1<<5) + +struct CoreDispTri_t +{ + unsigned short m_iIndex[3]; // the three indices that make up a triangle + unsigned short m_uiTags; // walkable, buildable, etc. +}; + +class CCoreDispInfo : public CDispUtilsHelper +{ +public: + + // + // tree and displacement surface directions + // + enum { WEST = 0, + NORTH = 1, + EAST = 2, + SOUTH = 3, + SOUTHWEST = 4, + SOUTHEAST = 5, + NORTHWEST = 6, + NORTHEAST = 7 }; + +#if 0 + // + // building parameters + // + enum { BUILD_NORMALS = 0x1, + BUILD_TEXCOORDS = 0x2, + BUILD_LIGHTCOORDS = 0x4, + BUILD_LODTREE = 0x8, + BUILD_COLLISION = 0x10, + BUILD_TANGENTSPACE = 0x20 }; +#endif + + // + // surface info flags + // + enum { SURF_BUMPED = 0x1, + SURF_NOPHYSICS_COLL = 0x2, + SURF_NOHULL_COLL = 0x4, + SURF_NORAY_COLL = 0x8 }; + + enum { MAX_DISP_POWER = MAX_MAP_DISP_POWER }; + enum { MAX_VERT_COUNT = MAX_DISPVERTS }; + enum { MAX_NODE_COUNT = 85 }; + + +// Convert from a CDispUtilsHelper. +public: + + static CCoreDispInfo* FromDispUtils( CDispUtilsHelper *p ) { return (CCoreDispInfo*)p; } + + +// CDispUtilsHelper implementation. +public: + + virtual CDispNeighbor* GetEdgeNeighbor( int index ); + virtual CDispCornerNeighbors* GetCornerNeighbors( int index ); + virtual const CPowerInfo* GetPowerInfo() const; + virtual CDispUtilsHelper* GetDispUtilsByIndex( int index ); + + +public: + + //========================================================================= + // + // Creation/Destruction + // + CCoreDispInfo(); + ~CCoreDispInfo(); + + void InitSurf( int parentIndex, Vector points[4], Vector normals[4], + Vector2D texCoords[4], Vector2D lightCoords[4][4], int contents, int flags, + bool bGenerateSurfPointStart, Vector& startPoint, + bool bHasMappingAxes, Vector& uAxis, Vector& vAxis ); + + void InitDispInfo( int power, int minTess, float smoothingAngle, + float *alphas, Vector *dispVectorField, float *dispDistances ); + + // This just unpacks the contents of the verts into arrays and calls InitDispInfo. + void InitDispInfo( int power, int minTess, float smoothingAngle, const CDispVert *pVerts, const CDispTri *pTris ); + +// bool Create( int creationFlags ); + bool Create( void ); + bool CreateWithoutLOD( void ); + + //========================================================================= + // + // Parameter "Wrappers" + // + CCoreDispSurface* GetSurface() { return &m_Surf; } + const CCoreDispSurface* GetSurface() const { return &m_Surf; } + + inline CCoreDispNode *GetNode( int index ); + + inline void SetPower( int power ); + inline int GetPower( void ) const; + inline int GetPostSpacing( void ); + inline int GetWidth( void ); + inline int GetHeight( void ); + inline int GetSize( void ) const; + + // Use this disp as a CDispUtils. + void SetDispUtilsHelperInfo( CCoreDispInfo **ppListBase, int listSize ); + + void SetNeighborData( const CDispNeighbor edgeNeighbors[4], const CDispCornerNeighbors cornerNeighbors[4] ) { GetSurface()->SetNeighborData( edgeNeighbors, cornerNeighbors ); } + + // Get a corner point. Indexed by the CORNER_ defines. + const CVertIndex& GetCornerPointIndex( int index ) const { return GetPowerInfo()->GetCornerPointIndex( index ); } + const Vector& GetCornerPoint( int index ) const { return GetVert( VertIndexToInt( GetCornerPointIndex( index ) ) ); } + + inline void SetVert( int index, Vector const& vert ); + inline void GetVert( int index, Vector& vert ) const; + + inline const Vector& GetVert( int index ) const; + inline const Vector& GetVert( const CVertIndex &index ) const; + + inline void GetFlatVert( int index, Vector& vert ) const; + inline void SetFlatVert( int index, const Vector &vert ); + + inline void GetNormal( int index, Vector& normal ) const; + inline const Vector& GetNormal( int index ) const; + inline const Vector& GetNormal( const CVertIndex &index ) const; + inline void SetNormal( int index, Vector const& normal ); + inline void SetNormal( const CVertIndex &index, Vector const& normal ); + + inline void GetTangentS( int index, Vector& tangentS ) const; + inline void GetTangentT( int index, Vector& tangentT ) const; + inline void SetTexCoord( int index, Vector2D const& texCoord ); + inline void GetTexCoord( int index, Vector2D& texCoord ) const; + + inline void SetLuxelCoord( int bumpIndex, int index, Vector2D const& luxelCoord ); + inline void GetLuxelCoord( int bumpIndex, int index, Vector2D& luxelCoord ) const; + + inline void SetAlpha( int index, float alpha ); + inline float GetAlpha( int index ); + + int GetTriCount( void ); + void GetTriIndices( int iTri, unsigned short &v1, unsigned short &v2, unsigned short &v3 ); + void SetTriIndices( int iTri, unsigned short v1, unsigned short v2, unsigned short v3 ); + void GetTriPos( int iTri, Vector &v1, Vector &v2, Vector &v3 ); + inline void SetTriTag( int iTri, unsigned short nTag ) { m_pTris[iTri].m_uiTags |= nTag; } + inline void ResetTriTag( int iTri, unsigned short nTag ) { m_pTris[iTri].m_uiTags &= ~nTag; } + inline void ToggleTriTag( int iTri, unsigned short nTag ) { m_pTris[iTri].m_uiTags ^= nTag; } + inline bool IsTriTag( int iTri, unsigned short nTag ) { return ( ( m_pTris[iTri].m_uiTags & nTag ) != 0 ); } + inline unsigned short GetTriTagValue( int iTri ) { return m_pTris[iTri].m_uiTags; } + inline void SetTriTagValue( int iTri, unsigned short nVal ) { m_pTris[iTri].m_uiTags = nVal; } + + bool IsTriWalkable( int iTri ); + bool IsTriBuildable( int iTri ); + + inline void SetElevation( float elevation ); + inline float GetElevation( void ); + + inline void ResetFieldVectors( void ); + inline void SetFieldVector( int index, Vector const &v ); + inline void GetFieldVector( int index, Vector& v ); + inline void ResetFieldDistances( void ); + inline void SetFieldDistance( int index, float dist ); + inline float GetFieldDistance( int index ); + + inline void ResetSubdivPositions( void ); + inline void SetSubdivPosition( int ndx, Vector const &v ); + inline void GetSubdivPosition( int ndx, Vector& v ); + + inline void ResetSubdivNormals( void ); + inline void SetSubdivNormal( int ndx, Vector const &v ); + inline void GetSubdivNormal( int ndx, Vector &v ); + + inline void SetRenderIndexCount( int count ); + inline int GetRenderIndexCount( void ); + inline void SetRenderIndex( int index, int triIndex ); + inline int GetRenderIndex( int index ); + + inline CoreDispVert_t *GetDispVert( int iVert ) { return &m_pVerts[iVert]; } + inline CoreDispVert_t *GetDispVertList(); + inline unsigned short *GetRenderIndexList( void ); + + inline void SetTouched( bool touched ); + inline bool IsTouched( void ); + + void CalcDispSurfCoords( bool bLightMap, int lightmapID ); + void GetPositionOnSurface( float u, float v, Vector &vPos, Vector *pNormal, float *pAlpha ); + + void DispUVToSurf( Vector2D const &dispUV, Vector &vecPoint, Vector *pNormal, float *pAlpha ); + void BaseFacePlaneToDispUV( Vector const &planePt, Vector2D &dispUV ); + bool SurfToBaseFacePlane( Vector const &surfPt, Vector &planePt ); + + const CDispCornerNeighbors* GetCornerNeighbors( int iCorner ) const { return GetSurface()->GetCornerNeighbors( iCorner ); } + const CDispNeighbor* GetEdgeNeighbor( int iEdge ) const { return GetSurface()->GetEdgeNeighbor( iEdge ); } + + void SetListIndex( int nIndex ) { m_nListIndex = nIndex; } + int GetListIndex( void ) { return m_nListIndex; } + + CBitVec& GetAllowedVerts() { return m_AllowedVerts; } + const CBitVec& GetAllowedVerts() const { return m_AllowedVerts; } + void AllowedVerts_Clear( void ) { m_AllowedVerts.SetAll(); } + int AllowedVerts_GetNumDWords() const { return m_AllowedVerts.GetNumDWords(); } + unsigned long AllowedVerts_GetDWord(int i) const { return m_AllowedVerts.GetDWord( i ); } + void AllowedVerts_SetDWord(int i, unsigned long val) { m_AllowedVerts.SetDWord( i, val ); } + + + void Position_Update( int iVert, Vector vecPos ); + + //========================================================================= + // + // friend functions + // + friend void SmoothNeighboringDispSurfNormals( CCoreDispInfo **ppCoreDispInfoList, int listSize ); + +private: + // be changed to match the paint normal next pass) + // LOD/collision node data + CCoreDispNode *m_Nodes; // LOD quad-tree nodes + + float m_Elevation; // distance along the subdivision normal (should + + // defines the size of the displacement surface + int m_Power; // "size" of the displacement map + + // base surface data + CCoreDispSurface m_Surf; // surface containing displacement data + // be changed to match the paint normal next pass) + // Vertex data.. + CoreDispVert_t *m_pVerts; + + // Triangle data.. + CoreDispTri_t *m_pTris; + + // render specific data + int m_RenderIndexCount; // number of indices used in rendering + unsigned short *m_RenderIndices; // rendering index list (list of triangles) + int m_RenderCounter; // counter to verify surfaces are renderered/collided with only once per frame + + // utility data + bool m_bTouched; // touched flag + CCoreDispInfo *m_pNext; // used for chaining + + // The list that this disp is in (used for CDispUtils::IHelper implementation). + CCoreDispInfo **m_ppListBase; + int m_ListSize; + + CBitVec m_AllowedVerts; // Built in VBSP. Defines which verts are allowed to exist based on what the neighbors are. + + int m_nListIndex; + + //========================================================================= + // + // Creation Functions + // + + void GenerateDispSurf( void ); + void GenerateDispSurfNormals( void ); + void GenerateDispSurfTangentSpaces( void ); + bool DoesEdgeExist( int indexRow, int indexCol, int direction, int postSpacing ); + void CalcNormalFromEdges( int indexRow, int indexCol, bool bIsEdge[4], Vector& normal ); + void CalcDispSurfAlphas( void ); + void GenerateLODTree( void ); + void CalcVertIndicesAtNodes( int nodeIndex ); + int GetNodeVertIndexFromParentIndex( int level, int parentVertIndex, int direction ); + void CalcNodeInfo( int nodeIndex, int terminationLevel ); + void CalcNeighborVertIndicesAtNode( int nodeIndex, int level ); + void CalcNeighborNodeIndicesAtNode( int nodeIndex, int level ); + void CalcErrorTermAtNode( int nodeIndex, int level ); + float GetMaxErrorFromChildren( int nodeIndex, int level ); + void CalcBoundingBoxAtNode( int nodeIndex ); + void CalcMinMaxBoundingBoxAtNode( int nodeIndex, Vector& bMin, Vector& bMax ); + void CalcTriSurfInfoAtNode( int nodeIndex ); + void CalcTriSurfIndices( int nodeIndex, int indices[8][3] ); + void CalcTriSurfBoundingBoxes( int nodeIndex, int indices[8][3] ); + void CalcRayBoundingBoxes( int nodeIndex, int indices[8][3] ); + void CalcTriSurfPlanes( int nodeIndex, int indices[8][3] ); + void GenerateCollisionData( void ); + void GenerateCollisionSurface( void ); + + void CreateBoundingBoxes( CoreDispBBox_t *pBBox, int count ); + + void DispUVToSurf_TriTLToBR( Vector &vecPoint, Vector *pNormal, float *pAlpha, float flU, float flV, const Vector &vecIntersectPoint ); + void DispUVToSurf_TriBLToTR( Vector &vecPoint, Vector *pNormal, float *pAlpha, float flU, float flV, const Vector &vecIntersectPoint ); + void DispUVToSurf_TriTLToBR_1( const Vector &vecIntersectPoint, int nSnapU, int nNextU, int nSnapV, int nNextV, Vector &vecPoint, Vector *pNormal, float *pAlpha, bool bBackup ); + void DispUVToSurf_TriTLToBR_2( const Vector &vecIntersectPoint, int nSnapU, int nNextU, int nSnapV, int nNextV, Vector &vecPoint, Vector *pNormal, float *pAlpha, bool bBackup ); + void DispUVToSurf_TriBLToTR_1( const Vector &vecIntersectPoint, int nSnapU, int nNextU, int nSnapV, int nNextV, Vector &vecPoint, Vector *pNormal, float *pAlpha, bool bBackup ); + void DispUVToSurf_TriBLToTR_2( const Vector &vecIntersectPoint, int nSnapU, int nNextU, int nSnapV, int nNextV, Vector &vecPoint, Vector *pNormal, float *pAlpha, bool bBackup ); + + void GetTriangleIndicesForDispBBox( int nIndex, int nTris[2][3] ); + + void BuildTriTLtoBR( int ndx ); + void BuildTriBLtoTR( int ndx ); + + void InitTris( void ); + void CreateTris( void ); +}; + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispInfo::SetPower( int power ) +{ + m_Power = power; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline int CCoreDispInfo::GetPower( void ) const +{ + return m_Power; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline int CCoreDispInfo::GetPostSpacing( void ) +{ + return ( ( 1 << m_Power ) + 1 ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline int CCoreDispInfo::GetWidth( void ) +{ + return ( ( 1 << m_Power ) + 1 ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline int CCoreDispInfo::GetHeight( void ) +{ + return ( ( 1 << m_Power ) + 1 ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline int CCoreDispInfo::GetSize( void ) const +{ + return ( ( ( 1 << m_Power ) + 1 ) * ( ( 1 << m_Power ) + 1 ) ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispInfo::SetVert( int index, Vector const &vert ) +{ + Assert( index >= 0 ); + Assert( index < MAX_VERT_COUNT ); + VectorCopy( vert, m_pVerts[index].m_Vert ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispInfo::GetVert( int index, Vector& vert ) const +{ + Assert( index >= 0 ); + Assert( index < MAX_VERT_COUNT ); + VectorCopy( m_pVerts[index].m_Vert, vert ); +} + + +inline const Vector& CCoreDispInfo::GetVert( int index ) const +{ + Assert( index >= 0 ); + Assert( index < MAX_VERT_COUNT ); + return m_pVerts[index].m_Vert; +} + +inline const Vector& CCoreDispInfo::GetVert( const CVertIndex &index ) const +{ + return GetVert( VertIndexToInt( index ) ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispInfo::GetFlatVert( int index, Vector& vert ) const +{ + Assert( index >= 0 ); + Assert( index < MAX_VERT_COUNT ); + VectorCopy( m_pVerts[index].m_FlatVert, vert ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispInfo::SetFlatVert( int index, const Vector &vert ) +{ + Assert( index >= 0 ); + Assert( index < MAX_VERT_COUNT ); + VectorCopy( vert, m_pVerts[index].m_FlatVert ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispInfo::SetNormal( int index, Vector const &normal ) +{ + Assert( index >= 0 ); + Assert( index < MAX_VERT_COUNT ); + VectorCopy( normal, m_pVerts[index].m_Normal ); +} + + +inline void CCoreDispInfo::SetNormal( const CVertIndex &index, Vector const &normal ) +{ + SetNormal( VertIndexToInt( index ), normal ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispInfo::GetNormal( int index, Vector& normal ) const +{ + Assert( index >= 0 ); + Assert( index < MAX_VERT_COUNT ); + VectorCopy( m_pVerts[index].m_Normal, normal ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline const Vector& CCoreDispInfo::GetNormal( int index ) const +{ + Assert( index >= 0 ); + Assert( index < MAX_VERT_COUNT ); + return m_pVerts[index].m_Normal; +} + + +inline const Vector& CCoreDispInfo::GetNormal( const CVertIndex &index ) const +{ + return GetNormal( VertIndexToInt( index ) ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispInfo::GetTangentS( int index, Vector& tangentS ) const +{ + Assert( index >= 0 ); + Assert( index < GetSize() ); + VectorCopy( m_pVerts[index].m_TangentS, tangentS ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispInfo::GetTangentT( int index, Vector& tangentT ) const +{ + Assert( index >= 0 ); + Assert( index < GetSize() ); + VectorCopy( m_pVerts[index].m_TangentT, tangentT ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispInfo::SetTexCoord( int index, Vector2D const& texCoord ) +{ + Assert( index >= 0 ); + Assert( index < GetSize() ); + Vector2DCopy( texCoord, m_pVerts[index].m_TexCoord ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispInfo::GetTexCoord( int index, Vector2D& texCoord ) const +{ + Assert( index >= 0 ); + Assert( index < GetSize() ); + Vector2DCopy( m_pVerts[index].m_TexCoord, texCoord ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispInfo::SetLuxelCoord( int bumpIndex, int index, Vector2D const& luxelCoord ) +{ + Assert( index >= 0 ); + Assert( index < GetSize() ); + Assert( bumpIndex >= 0 ); + Assert( bumpIndex < NUM_BUMP_VECTS + 1 ); + Vector2DCopy( luxelCoord, m_pVerts[index].m_LuxelCoords[bumpIndex] ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispInfo::GetLuxelCoord( int bumpIndex, int index, Vector2D& luxelCoord ) const +{ + Assert( index >= 0 ); + Assert( index < MAX_VERT_COUNT ); + Assert( bumpIndex >= 0 ); + Assert( bumpIndex < NUM_BUMP_VECTS + 1 ); + Vector2DCopy( m_pVerts[index].m_LuxelCoords[bumpIndex], luxelCoord ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispInfo::SetAlpha( int index, float alpha ) +{ + Assert( index >= 0 ); + Assert( index < MAX_VERT_COUNT ); + m_pVerts[index].m_Alpha = alpha; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline float CCoreDispInfo::GetAlpha( int index ) +{ + Assert( index >= 0 ); + Assert( index < MAX_VERT_COUNT ); + return m_pVerts[index].m_Alpha; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispInfo::SetElevation( float elevation ) +{ + m_Elevation = elevation; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline float CCoreDispInfo::GetElevation( void ) +{ + return m_Elevation; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispInfo::ResetFieldVectors( void ) +{ +// Vector normal; +// m_Surf.GetNormal( normal ); + + int size = GetSize(); + for( int i = 0; i < size; i++ ) + { + m_pVerts[i].m_FieldVector.Init(); +// m_FieldVectors[i] = normal; + } +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispInfo::SetFieldVector( int index, Vector const &v ) +{ + Assert( index >= 0 ); + Assert( index < MAX_VERT_COUNT ); + VectorCopy( v, m_pVerts[index].m_FieldVector ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispInfo::GetFieldVector( int index, Vector& v ) +{ + Assert( index >= 0 ); + Assert( index < MAX_VERT_COUNT ); + VectorCopy( m_pVerts[index].m_FieldVector, v ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispInfo::ResetSubdivPositions( void ) +{ + int size = GetSize(); + for( int i = 0; i < size; i++ ) + { + m_pVerts[i].m_SubdivPos.Init(); + } +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispInfo::SetSubdivPosition( int ndx, Vector const &v ) +{ + Assert( ndx >= 0 ); + Assert( ndx < MAX_VERT_COUNT ); + m_pVerts[ndx].m_SubdivPos = v; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispInfo::GetSubdivPosition( int ndx, Vector& v ) +{ + Assert( ndx >= 0 ); + Assert( ndx < MAX_VERT_COUNT ); + v = m_pVerts[ndx].m_SubdivPos; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispInfo::ResetSubdivNormals( void ) +{ + Vector normal; + m_Surf.GetNormal( normal ); + + int size = GetSize(); + for( int i = 0; i < size; i++ ) + { + m_pVerts[i].m_SubdivNormal = normal; + } +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispInfo::SetSubdivNormal( int ndx, Vector const &v ) +{ + Assert( ndx >= 0 ); + Assert( ndx < MAX_VERT_COUNT ); + m_pVerts[ndx].m_SubdivNormal = v; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispInfo::GetSubdivNormal( int ndx, Vector &v ) +{ + Assert( ndx >= 0 ); + Assert( ndx < MAX_VERT_COUNT ); + v = m_pVerts[ndx].m_SubdivNormal; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispInfo::ResetFieldDistances( void ) +{ + int size = GetSize(); + for( int i = 0; i < size; i++ ) + { + m_pVerts[i].m_FieldDistance = 0.0f; + } +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispInfo::SetFieldDistance( int index, float dist ) +{ + Assert( index >= 0 ); + Assert( index < GetSize() ); + m_pVerts[index].m_FieldDistance = dist; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline float CCoreDispInfo::GetFieldDistance( int index ) +{ + Assert( index >= 0 ); + Assert( index < GetSize() ); + return m_pVerts[index].m_FieldDistance; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispInfo::SetRenderIndexCount( int count ) +{ + m_RenderIndexCount = count; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline int CCoreDispInfo::GetRenderIndexCount( void ) +{ + return m_RenderIndexCount; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispInfo::SetRenderIndex( int index, int triIndex ) +{ + Assert( index >= 0 ); + Assert( index < ( MAX_VERT_COUNT*2*3) ); + m_RenderIndices[index] = triIndex; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline int CCoreDispInfo::GetRenderIndex( int index ) +{ + Assert( index >= 0 ); + Assert( index < ( MAX_VERT_COUNT*2*3) ); + return m_RenderIndices[index]; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline CoreDispVert_t *CCoreDispInfo::GetDispVertList() +{ + return m_pVerts; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline unsigned short *CCoreDispInfo::GetRenderIndexList( void ) +{ + return &m_RenderIndices[0]; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispInfo::SetTouched( bool touched ) +{ + m_bTouched = touched; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline bool CCoreDispInfo::IsTouched( void ) +{ + return m_bTouched; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline CCoreDispNode *CCoreDispInfo::GetNode( int index ) +{ + Assert( index >= 0 ); + Assert( index < MAX_NODE_COUNT ); + return &m_Nodes[index]; +} + +bool CalcBarycentricCooefs( Vector const &v0, Vector const &v1, Vector const &v2, + Vector const &pt, float &c0, float &c1, float &c2 ); + +#endif // BUILDDISP_H diff --git a/public/bumpvects.cpp b/public/bumpvects.cpp index d3ad4188..7a2b94a9 100644 --- a/public/bumpvects.cpp +++ b/public/bumpvects.cpp @@ -1,69 +1,69 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $Workfile: $ -// $Date: $ -// -//----------------------------------------------------------------------------- -// $Log: $ -// -// $NoKeywords: $ -//=============================================================================// - -#if !defined(_STATIC_LINKED) || defined(_SHARED_LIB) - - -#ifdef QUIVER -#include "r_local.h" -#endif -#include "bumpvects.h" -#include "vector.h" -#include - -// memdbgon must be the last include file in a .cpp file!!! -#include "tier0/memdbgon.h" - -// z is coming out of the face. - -void GetBumpNormals( const Vector& sVect, const Vector& tVect, const Vector& flatNormal, - const Vector& phongNormal, Vector bumpNormals[NUM_BUMP_VECTS] ) -{ - Vector tmpNormal; - bool leftHanded; - int i; - - assert( NUM_BUMP_VECTS == 3 ); - - // Are we left or right handed? - CrossProduct( sVect, tVect, tmpNormal ); - if( DotProduct( flatNormal, tmpNormal ) < 0.0f ) - { - leftHanded = true; - } - else - { - leftHanded = false; - } - - // Build a basis for the face around the phong normal - matrix3x4_t smoothBasis; - CrossProduct( phongNormal.Base(), sVect.Base(), smoothBasis[1] ); - VectorNormalize( smoothBasis[1] ); - CrossProduct( smoothBasis[1], phongNormal.Base(), smoothBasis[0] ); - VectorNormalize( smoothBasis[0] ); - VectorCopy( phongNormal.Base(), smoothBasis[2] ); - - if( leftHanded ) - { - VectorNegate( smoothBasis[1] ); - } - - // move the g_localBumpBasis into world space to create bumpNormals - for( i = 0; i < 3; i++ ) - { - VectorIRotate( g_localBumpBasis[i], smoothBasis, bumpNormals[i] ); - } -} - -#endif // !_STATIC_LINKED || _SHARED_LIB \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $Workfile: $ +// $Date: $ +// +//----------------------------------------------------------------------------- +// $Log: $ +// +// $NoKeywords: $ +//=============================================================================// + +#if !defined(_STATIC_LINKED) || defined(_SHARED_LIB) + + +#ifdef QUIVER +#include "r_local.h" +#endif +#include "bumpvects.h" +#include "vector.h" +#include + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +// z is coming out of the face. + +void GetBumpNormals( const Vector& sVect, const Vector& tVect, const Vector& flatNormal, + const Vector& phongNormal, Vector bumpNormals[NUM_BUMP_VECTS] ) +{ + Vector tmpNormal; + bool leftHanded; + int i; + + assert( NUM_BUMP_VECTS == 3 ); + + // Are we left or right handed? + CrossProduct( sVect, tVect, tmpNormal ); + if( DotProduct( flatNormal, tmpNormal ) < 0.0f ) + { + leftHanded = true; + } + else + { + leftHanded = false; + } + + // Build a basis for the face around the phong normal + matrix3x4_t smoothBasis; + CrossProduct( phongNormal.Base(), sVect.Base(), smoothBasis[1] ); + VectorNormalize( smoothBasis[1] ); + CrossProduct( smoothBasis[1], phongNormal.Base(), smoothBasis[0] ); + VectorNormalize( smoothBasis[0] ); + VectorCopy( phongNormal.Base(), smoothBasis[2] ); + + if( leftHanded ) + { + VectorNegate( smoothBasis[1] ); + } + + // move the g_localBumpBasis into world space to create bumpNormals + for( i = 0; i < 3; i++ ) + { + VectorIRotate( g_localBumpBasis[i], smoothBasis, bumpNormals[i] ); + } +} + +#endif // !_STATIC_LINKED || _SHARED_LIB diff --git a/public/chunkfile.cpp b/public/chunkfile.cpp index 95c7b657..2d70296a 100644 --- a/public/chunkfile.cpp +++ b/public/chunkfile.cpp @@ -1,989 +1,1001 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: Implements an interface for reading and writing heirarchical -// text files of key value pairs. The format of the file is as follows: -// -// chunkname0 -// { -// "key0" "value0" -// "key1" "value1" -// ... -// "keyN" "valueN" -// chunkname1 -// { -// "key0" "value0" -// "key1" "value1" -// ... -// "keyN" "valueN" -// } -// } -// ... -// chunknameN -// { -// "key0" "value0" -// "key1" "value1" -// ... -// "keyN" "valueN" -// } -// -// The chunk names are not necessarily unique, nor are the key names, unless the -// parsing application requires them to be. -// -// $NoKeywords: $ -//=============================================================================// - -#include -#include -#include -#include -#include -#include -#include -#include "ChunkFile.h" -#include "Vector.h" -#include "Vector4D.h" -#include "vstdlib/strtools.h" - -// memdbgon must be the last include file in a .cpp file!!! -#include "tier0/memdbgon.h" - -// -// Fixes an infinite loop that occurs when loading certain VMFs. The bug -// occurs with Worldcraft built with DevStudio SP4. -// -#pragma optimize("g", off) - - -//----------------------------------------------------------------------------- -// Purpose: Constructor. -//----------------------------------------------------------------------------- -CChunkHandlerMap::CChunkHandlerMap(void) -{ - m_pHandlers = NULL; -} - - -//----------------------------------------------------------------------------- -// Purpose: Destructor. Frees handler list. -//----------------------------------------------------------------------------- -CChunkHandlerMap::~CChunkHandlerMap(void) -{ - ChunkHandlerInfoNode_t *pNode = m_pHandlers; - while (pNode != NULL) - { - ChunkHandlerInfoNode_t *pPrev = pNode; - pNode = pNode->pNext; - - delete pPrev; - } -} - - -//----------------------------------------------------------------------------- -// Purpose: Adds a chunk handler to the handler list. -// Input : pszChunkName - Name of chunk to be handled. -// pfnHandler - Address of handler callback function. -// pData - Data to pass to the handler callback. -//----------------------------------------------------------------------------- -void CChunkHandlerMap::AddHandler(const char *pszChunkName, ChunkHandler_t pfnHandler, void *pData) -{ - ChunkHandlerInfoNode_t *pNew = new ChunkHandlerInfoNode_t; - - Q_strncpy(pNew->Handler.szChunkName, pszChunkName, sizeof( pNew->Handler.szChunkName )); - pNew->Handler.pfnHandler = pfnHandler; - pNew->Handler.pData = pData; - pNew->pNext = NULL; - - if (m_pHandlers == NULL) - { - m_pHandlers = pNew; - } - else - { - ChunkHandlerInfoNode_t *pNode = m_pHandlers; - while (pNode->pNext != NULL) - { - pNode = pNode->pNext; - } - pNode->pNext = pNew; - } -} - - -//----------------------------------------------------------------------------- -// Purpose: Sets the callback for error handling within this chunk's scope. -// Input : pfnHandler - -// pData - -//----------------------------------------------------------------------------- -void CChunkHandlerMap::SetErrorHandler(ChunkErrorHandler_t pfnHandler, void *pData) -{ - m_pfnErrorHandler = pfnHandler; - m_pErrorData = pData; -} - - -//----------------------------------------------------------------------------- -// Purpose: -// Input : ppData - -// Output : ChunkErrorHandler_t -//----------------------------------------------------------------------------- -ChunkErrorHandler_t CChunkHandlerMap::GetErrorHandler(void **ppData) -{ - *ppData = m_pErrorData; - return(m_pfnErrorHandler); -} - - -//----------------------------------------------------------------------------- -// Purpose: Gets the handler for a given chunk name, if one has been set. -// Input : pszChunkName - Name of chunk. -// ppfnHandler - Receives the address of the callback function. -// ppData - Receives the context data for the given chunk. -// Output : Returns true if a handler was found, false if not. -//----------------------------------------------------------------------------- -ChunkHandler_t CChunkHandlerMap::GetHandler(const char *pszChunkName, void **ppData) -{ - ChunkHandlerInfoNode_t *pNode = m_pHandlers; - while (pNode != NULL) - { - if (!stricmp(pNode->Handler.szChunkName, pszChunkName)) - { - *ppData = pNode->Handler.pData; - return(pNode->Handler.pfnHandler); - } - - pNode = pNode->pNext; - } - - return(false); -} - - -//----------------------------------------------------------------------------- -// Purpose: Constructor. Initializes data members. -//----------------------------------------------------------------------------- -CChunkFile::CChunkFile(void) -{ - m_hFile = NULL; - m_nCurrentDepth = 0; - m_szIndent[0] = '\0'; - m_nHandlerStackDepth = 0; - m_DefaultChunkHandler = 0; -} - - -//----------------------------------------------------------------------------- -// Purpose: Destructor. Closes the file if it is currently open. -//----------------------------------------------------------------------------- -CChunkFile::~CChunkFile(void) -{ - if (m_hFile != NULL) - { - fclose(m_hFile); - } -} - - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *pszChunkName - -// Output : ChunkFileResult_t -//----------------------------------------------------------------------------- -ChunkFileResult_t CChunkFile::BeginChunk(const char *pszChunkName) -{ - // - // Write the chunk name and open curly. - // - char szBuf[MAX_KEYVALUE_LEN]; - Q_snprintf(szBuf, sizeof( szBuf ), "%s\r\n%s{", pszChunkName, m_szIndent); - ChunkFileResult_t eResult = WriteLine(szBuf); - - // - // Update the indentation depth. - // - if (eResult == ChunkFile_Ok) - { - m_nCurrentDepth++; - BuildIndentString(m_szIndent, m_nCurrentDepth); - } - - return(eResult); -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CChunkFile::BuildIndentString(char *pszDest, int nDepth) -{ - if (nDepth >= 0) - { - for (int i = 0; i < nDepth; i++) - { - pszDest[i] = '\t'; - } - - pszDest[nDepth] = '\0'; - } -} - - -//----------------------------------------------------------------------------- -// Purpose: -// Output : ChunkFileResult_t -//----------------------------------------------------------------------------- -ChunkFileResult_t CChunkFile::Close(void) -{ - if (m_hFile != NULL) - { - fclose(m_hFile); - m_hFile = NULL; - } - - return(ChunkFile_Ok); -} - - -//----------------------------------------------------------------------------- -// Purpose: -// Output : ChunkFileResult_t -//----------------------------------------------------------------------------- -ChunkFileResult_t CChunkFile::EndChunk(void) -{ - if (m_nCurrentDepth > 0) - { - m_nCurrentDepth--; - BuildIndentString(m_szIndent, m_nCurrentDepth); - } - - WriteLine("}"); - - return(ChunkFile_Ok); -} - - -//----------------------------------------------------------------------------- -// Purpose: Returns a string explaining the last error that occurred. -//----------------------------------------------------------------------------- -const char *CChunkFile::GetErrorText(ChunkFileResult_t eResult) -{ - static char szError[MAX_KEYVALUE_LEN]; - - switch (eResult) - { - case ChunkFile_UnexpectedEOF: - { - Q_strncpy(szError, "unexpected end of file", sizeof( szError ) ); - break; - } - - case ChunkFile_UnexpectedSymbol: - { - Q_snprintf(szError, sizeof( szError ), "unexpected symbol '%s'", m_szErrorToken); - break; - } - - case ChunkFile_OpenFail: - { - Q_snprintf(szError, sizeof( szError ), "%s", strerror(errno)) ; - break; - } - - case ChunkFile_StringTooLong: - { - Q_strncpy(szError, "unterminated string or string too long", sizeof( szError ) ); - break; - } - - default: - { - Q_snprintf(szError, sizeof( szError ), "error %d", eResult); - } - } - - return(m_TokenReader.Error(szError)); -} - - -//----------------------------------------------------------------------------- -// Purpose: -// Input : eError - -//----------------------------------------------------------------------------- -void CChunkFile::HandleError(const char *szChunkName, ChunkFileResult_t eError) -{ - // UNDONE: dispatch errors to the error handler. - // - keep track of current chunkname for reporting errors - // - use the last non-NULL handler that was pushed onto the stack? - // - need a return code to determine whether to abort parsing? -} - - -//----------------------------------------------------------------------------- -// Purpose: -// Output : ChunkFileResult_t -//----------------------------------------------------------------------------- -ChunkFileResult_t CChunkFile::HandleChunk(const char *szChunkName) -{ - // See if the default handler wants it? - if( m_DefaultChunkHandler ) - { - ChunkFileResult_t testResult = m_DefaultChunkHandler( this, m_pDefaultChunkHandlerData, szChunkName ); - if( testResult == ChunkFile_Ok ) - { - return ChunkFile_Ok; - } - } - - // - // If there is an active handler map... - // - if (m_nHandlerStackDepth > 0) - { - CChunkHandlerMap *pHandler = m_HandlerStack[m_nHandlerStackDepth - 1]; - - // - // If a chunk handler was found in the handler map... - // - void *pData; - ChunkHandler_t pfnHandler = pHandler->GetHandler(szChunkName, &pData); - if (pfnHandler != NULL) - { - // Dispatch this chunk to the handler. - ChunkFileResult_t eResult = pfnHandler(this, pData); - if (eResult != ChunkFile_Ok) - { - return(eResult); - } - } - else - { - // - // No handler for this chunk. Skip to the matching close curly brace. - // - int nDepth = 1; - ChunkFileResult_t eResult; - - do - { - ChunkType_t eChunkType; - char szKey[MAX_KEYVALUE_LEN]; - char szValue[MAX_KEYVALUE_LEN]; - - while ((eResult = ReadNext(szKey, szValue, sizeof(szValue), eChunkType)) == ChunkFile_Ok) - { - if (eChunkType == ChunkType_Chunk) - { - nDepth++; - } - } - - if (eResult == ChunkFile_EndOfChunk) - { - eResult = ChunkFile_Ok; - nDepth--; - } - else if (eResult == ChunkFile_EOF) - { - return(ChunkFile_UnexpectedEOF); - } - - } while ((nDepth) && (eResult == ChunkFile_Ok)); - } - } - - return(ChunkFile_Ok); -} - - -//----------------------------------------------------------------------------- -// Purpose: Opens the chunk file for reading or writing. -// Input : pszFileName - Path of file to open. -// eMode - ChunkFile_Read or ChunkFile_Write. -// Output : Returns ChunkFile_Ok on success, ChunkFile_Fail on failure. -// UNDONE: boolean return value? -//----------------------------------------------------------------------------- -ChunkFileResult_t CChunkFile::Open(const char *pszFileName, ChunkFileOpenMode_t eMode) -{ - if (eMode == ChunkFile_Read) - { - // UNDONE: TokenReader encapsulates file - unify reading and writing to use the same file I/O. - // UNDONE: Support in-memory parsing. - if (m_TokenReader.Open(pszFileName)) - { - m_nCurrentDepth = 0; - } - else - { - return(ChunkFile_OpenFail); - } - } - else if (eMode == ChunkFile_Write) - { - m_hFile = fopen(pszFileName, "wb"); - - if (m_hFile == NULL) - { - return(ChunkFile_OpenFail); - } - - m_nCurrentDepth = 0; - } - - return(ChunkFile_Ok); -} - - -//----------------------------------------------------------------------------- -// Purpose: Removes the topmost set of chunk handlers. -//----------------------------------------------------------------------------- -void CChunkFile::PopHandlers(void) -{ - if (m_nHandlerStackDepth > 0) - { - m_nHandlerStackDepth--; - } -} - - -void CChunkFile::SetDefaultChunkHandler( DefaultChunkHandler_t pHandler, void *pData ) -{ - m_DefaultChunkHandler = pHandler; - m_pDefaultChunkHandlerData = pData;} - - -//----------------------------------------------------------------------------- -// Purpose: Adds a set of chunk handlers to the top of the handler stack. -// Input : pHandlerMap - Object containing the list of chunk handlers. -//----------------------------------------------------------------------------- -void CChunkFile::PushHandlers(CChunkHandlerMap *pHandlerMap) -{ - if (m_nHandlerStackDepth < MAX_INDENT_DEPTH) - { - m_HandlerStack[m_nHandlerStackDepth] = pHandlerMap; - m_nHandlerStackDepth++; - } -} - - -//----------------------------------------------------------------------------- -// Purpose: Reads the next term from the chunk file. The type of term read is -// returned in the eChunkType parameter. -// Input : szName - Name of key or chunk. -// szValue - If eChunkType is ChunkType_Key, contains the value of the key. -// nValueSize - Size of the buffer pointed to by szValue. -// eChunkType - ChunkType_Key or ChunkType_Chunk. -// Output : Returns ChunkFile_Ok on success, an error code if a parsing error occurs. -//----------------------------------------------------------------------------- -ChunkFileResult_t CChunkFile::ReadNext(char *szName, char *szValue, int nValueSize, ChunkType_t &eChunkType) -{ - // HACK: pass in buffer sizes? - trtoken_t eTokenType = m_TokenReader.NextToken(szName, MAX_KEYVALUE_LEN); - - if (eTokenType != TOKENEOF) - { - switch (eTokenType) - { - case IDENT: - case STRING: - { - char szNext[MAX_KEYVALUE_LEN]; - trtoken_t eNextTokenType; - - // - // Read the next token to determine what we have. - // - eNextTokenType = m_TokenReader.NextToken(szNext, sizeof(szNext)); - - switch (eNextTokenType) - { - case OPERATOR: - { - if (!stricmp(szNext, "{")) - { - // Beginning of new chunk. - m_nCurrentDepth++; - eChunkType = ChunkType_Chunk; - szValue[0] = '\0'; - return(ChunkFile_Ok); - } - else - { - // Unexpected symbol. - Q_strncpy(m_szErrorToken, szNext, sizeof( m_szErrorToken ) ); - return(ChunkFile_UnexpectedSymbol); - } - } - - case STRING: - case IDENT: - { - // Key value pair. - Q_strncpy(szValue, szNext, nValueSize ); - eChunkType = ChunkType_Key; - return(ChunkFile_Ok); - } - - case TOKENEOF: - { - // Unexpected end of file. - return(ChunkFile_UnexpectedEOF); - } - - case TOKENSTRINGTOOLONG: - { - // String too long or unterminated string. - return ChunkFile_StringTooLong; - } - } - } - - case OPERATOR: - { - if (!stricmp(szName, "}")) - { - // End of current chunk. - m_nCurrentDepth--; - return(ChunkFile_EndOfChunk); - } - else - { - // Unexpected symbol. - Q_strncpy(m_szErrorToken, szName, sizeof( m_szErrorToken ) ); - return(ChunkFile_UnexpectedSymbol); - } - } - - case TOKENSTRINGTOOLONG: - { - return ChunkFile_StringTooLong; - } - } - } - - if (m_nCurrentDepth != 0) - { - // End of file while within the scope of a chunk. - return(ChunkFile_UnexpectedEOF); - } - - return(ChunkFile_EOF); -} - - -//----------------------------------------------------------------------------- -// Purpose: Reads the current chunk and dispatches keys and sub-chunks to the -// appropriate handler callbacks. -// Input : pfnKeyHandler - Callback for any key values in this chunk. -// pData - Data to pass to the key value callback function. -// Output : Normally returns ChunkFile_Ok or ChunkFile_EOF. Otherwise, returns -// a ChunkFile_xxx error code. -//----------------------------------------------------------------------------- -ChunkFileResult_t CChunkFile::ReadChunk(KeyHandler_t pfnKeyHandler, void *pData) -{ - // - // Read the keys and sub-chunks. - // - ChunkFileResult_t eResult; - do - { - char szName[MAX_KEYVALUE_LEN]; - char szValue[MAX_KEYVALUE_LEN]; - ChunkType_t eChunkType; - - eResult = ReadNext(szName, szValue, sizeof(szValue), eChunkType); - - if (eResult == ChunkFile_Ok) - { - if (eChunkType == ChunkType_Chunk) - { - // - // Dispatch sub-chunks to the appropriate handler. - // - eResult = HandleChunk(szName); - } - else if ((eChunkType == ChunkType_Key) && (pfnKeyHandler != NULL)) - { - // - // Dispatch keys to the key value handler. - // - eResult = pfnKeyHandler(szName, szValue, pData); - } - } - } while (eResult == ChunkFile_Ok); - - // - // Cover up ChunkFile_EndOfChunk results because the caller doesn't want to see them. - // - if (eResult == ChunkFile_EndOfChunk) - { - eResult = ChunkFile_Ok; - } - - // - // Dispatch errors to the handler. - // - if ((eResult != ChunkFile_Ok) && (eResult != ChunkFile_EOF)) - { - //HandleError("chunkname", eResult); - } - - return(eResult); -} - - -//----------------------------------------------------------------------------- -// Purpose: -// Input : pszValue - -// pbBool - -// Output : Returns true on success, false on failure. -//----------------------------------------------------------------------------- -bool CChunkFile::ReadKeyValueBool(const char *pszValue, bool &bBool) -{ - int nValue = atoi(pszValue); - - if (nValue > 0) - { - bBool = true; - } - else - { - bBool = false; - } - - return(true); -} - - -//----------------------------------------------------------------------------- -// Purpose: -// Input : pszValue - -// pfFloat - -// Output : Returns true on success, false on failure. -//----------------------------------------------------------------------------- -bool CChunkFile::ReadKeyValueFloat(const char *pszValue, float &flFloat) -{ - flFloat = (float)atof(pszValue); - return(true); -} - - -//----------------------------------------------------------------------------- -// Purpose: -// Input : pszValue - -// pnInt - -// Output : Returns true on success, false on failure. -//----------------------------------------------------------------------------- -bool CChunkFile::ReadKeyValueInt(const char *pszValue, int &nInt) -{ - nInt = atoi(pszValue); - return(true); -} - - -//----------------------------------------------------------------------------- -// Purpose: -// Input : pszKey - -// r - -// g - -// b - -// Output : -//----------------------------------------------------------------------------- -bool CChunkFile::ReadKeyValueColor(const char *pszValue, unsigned char &chRed, unsigned char &chGreen, unsigned char &chBlue) -{ - if (pszValue != NULL) - { - int r; - int g; - int b; - - if (sscanf(pszValue, "%d %d %d", &r, &g, &b) == 3) - { - chRed = r; - chGreen = g; - chBlue = b; - - return(true); - } - } - - return(false); -} - - -//----------------------------------------------------------------------------- -// Purpose: -// Input : pszValue - -// pfPoint - -// Output : Returns true on success, false on failure. -//----------------------------------------------------------------------------- -bool CChunkFile::ReadKeyValuePoint(const char *pszValue, Vector &Point) -{ - if (pszValue != NULL) - { - return(sscanf(pszValue, "(%f %f %f)", &Point.x, &Point.y, &Point.z) == 3); - } - - return(false); -} - - -//----------------------------------------------------------------------------- -// Purpose: -// Input : pszValue - -// pfVector - -// Output : Returns true on success, false on failure. -//----------------------------------------------------------------------------- -bool CChunkFile::ReadKeyValueVector2(const char *pszValue, Vector2D &vec) -{ - if (pszValue != NULL) - { - return ( sscanf( pszValue, "[%f %f]", &vec.x, &vec.y) == 2 ); - } - - return(false); -} - - -//----------------------------------------------------------------------------- -// Purpose: -// Input : pszValue - -// pfVector - -// Output : Returns true on success, false on failure. -//----------------------------------------------------------------------------- -bool CChunkFile::ReadKeyValueVector3(const char *pszValue, Vector &vec) -{ - if (pszValue != NULL) - { - return(sscanf(pszValue, "[%f %f %f]", &vec.x, &vec.y, &vec.z) == 3); - } - - return(false); -} - - -//----------------------------------------------------------------------------- -// Purpose: -// Input : pszValue - -// pfVector - -// Output : Returns true on success, false on failure. -//----------------------------------------------------------------------------- -bool CChunkFile::ReadKeyValueVector4(const char *pszValue, Vector4D &vec) -{ - if( pszValue != NULL ) - { - return(sscanf(pszValue, "[%f %f %f %f]", &vec[0], &vec[1], &vec[2], &vec[3]) == 4); - } - - return false; -} - - -//----------------------------------------------------------------------------- -// Purpose: -// Input : pszLine - -// Output : -//----------------------------------------------------------------------------- -ChunkFileResult_t CChunkFile::WriteKeyValue(const char *pszKey, const char *pszValue) -{ - if ((pszKey != NULL) && (pszValue != NULL)) - { - char szTemp[MAX_KEYVALUE_LEN]; - Q_snprintf(szTemp, sizeof( szTemp ), "\"%s\" \"%s\"", pszKey, pszValue); - return(WriteLine(szTemp)); - } - - return(ChunkFile_Ok); -} - - -//----------------------------------------------------------------------------- -// Purpose: -// Input : pszKey - -// bValue - -// Output : -//----------------------------------------------------------------------------- -ChunkFileResult_t CChunkFile::WriteKeyValueBool(const char *pszKey, bool bValue) -{ - if (pszKey != NULL) - { - char szBuf[MAX_KEYVALUE_LEN]; - Q_snprintf(szBuf, sizeof( szBuf ), "\"%s\" \"%d\"", pszKey, (int)bValue); - return(WriteLine(szBuf)); - } - - return(ChunkFile_Ok); -} - - -//----------------------------------------------------------------------------- -// Purpose: -// Input : pszKey - -// nValue - -// Output : -//----------------------------------------------------------------------------- -ChunkFileResult_t CChunkFile::WriteKeyValueInt(const char *pszKey, int nValue) -{ - if (pszKey != NULL) - { - char szBuf[MAX_KEYVALUE_LEN]; - Q_snprintf(szBuf, sizeof( szBuf ), "\"%s\" \"%d\"", pszKey, nValue); - return(WriteLine(szBuf)); - } - - return(ChunkFile_Ok); -} - - -//----------------------------------------------------------------------------- -// Purpose: -// Input : pszKey - -// fValue - -// Output : -//----------------------------------------------------------------------------- -ChunkFileResult_t CChunkFile::WriteKeyValueFloat(const char *pszKey, float fValue) -{ - if (pszKey != NULL) - { - char szBuf[MAX_KEYVALUE_LEN]; - Q_snprintf(szBuf, sizeof( szBuf ), "\"%s\" \"%g\"", pszKey, (double)fValue); - return(WriteLine(szBuf)); - } - - return(ChunkFile_Ok); -} - - -//----------------------------------------------------------------------------- -// Purpose: -// Input : pszKey - -// r - -// g - -// b - -// Output : -//----------------------------------------------------------------------------- -ChunkFileResult_t CChunkFile::WriteKeyValueColor(const char *pszKey, unsigned char r, unsigned char g, unsigned char b) -{ - if (pszKey != NULL) - { - char szBuf[MAX_KEYVALUE_LEN]; - Q_snprintf(szBuf, sizeof( szBuf ), "\"%s\" \"%d %d %d\"", pszKey, (int)r, (int)g, (int)b); - return(WriteLine(szBuf)); - } - - return(ChunkFile_Ok); -} - - -//----------------------------------------------------------------------------- -// Purpose: -// Input : pszKey - -// fVector - -// Output : -//----------------------------------------------------------------------------- -ChunkFileResult_t CChunkFile::WriteKeyValuePoint(const char *pszKey, const Vector &Point) -{ - if (pszKey != NULL) - { - char szBuf[MAX_KEYVALUE_LEN]; - Q_snprintf(szBuf, sizeof( szBuf ), "\"%s\" \"(%g %g %g)\"", pszKey, (double)Point[0], (double)Point[1], (double)Point[2]); - return(WriteLine(szBuf)); - } - - return(ChunkFile_Ok); -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -ChunkFileResult_t CChunkFile::WriteKeyValueVector2(const char *pszKey, const Vector2D &vec) -{ - if (pszKey != NULL) - { - char szBuf[MAX_KEYVALUE_LEN]; - Q_snprintf( szBuf, sizeof( szBuf ), "\"%s\" \"[%g %g]\"", pszKey, (double)vec.x, (double)vec.y ); - return(WriteLine(szBuf)); - } - - return(ChunkFile_Ok); -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -ChunkFileResult_t CChunkFile::WriteKeyValueVector3(const char *pszKey, const Vector &vec) -{ - if (pszKey != NULL) - { - char szBuf[MAX_KEYVALUE_LEN]; - Q_snprintf(szBuf, sizeof( szBuf ), "\"%s\" \"[%g %g %g]\"", pszKey, (double)vec.x, (double)vec.y, (double)vec.z); - return(WriteLine(szBuf)); - } - - return(ChunkFile_Ok); -} - - -//----------------------------------------------------------------------------- -// Purpose: -// Input : pszKey - -// fVector - -// Output : -//----------------------------------------------------------------------------- -ChunkFileResult_t CChunkFile::WriteKeyValueVector4(const char *pszKey, const Vector4D &vec) -{ - if (pszKey != NULL) - { - char szBuf[MAX_KEYVALUE_LEN]; - Q_snprintf(szBuf, sizeof( szBuf ), "\"%s\" \"[%g %g %g %g]\"", pszKey, (double)vec.x, (double)vec.y, (double)vec.z, (double)vec.w); - return( WriteLine( szBuf ) ); - } - - return( ChunkFile_Ok ); -} - - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *pszLine - -// Output : -//----------------------------------------------------------------------------- -ChunkFileResult_t CChunkFile::WriteLine(const char *pszLine) -{ - if (pszLine != NULL) - { - // - // Write the indentation string. - // - if (m_nCurrentDepth > 0) - { - int nWritten = fwrite(m_szIndent, 1, m_nCurrentDepth, m_hFile); - if (nWritten != m_nCurrentDepth) - { - return(ChunkFile_Fail); - } - } - - // - // Write the string. - // - int nLen = strlen(pszLine); - int nWritten = fwrite(pszLine, 1, nLen, m_hFile); - if (nWritten != nLen) - { - return(ChunkFile_Fail); - } - - // - // Write the linefeed. - // - if (fwrite("\r\n", 1, 2, m_hFile) != 2) - { - return(ChunkFile_Fail); - } - } - - return(ChunkFile_Ok); -} - +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: Implements an interface for reading and writing heirarchical +// text files of key value pairs. The format of the file is as follows: +// +// chunkname0 +// { +// "key0" "value0" +// "key1" "value1" +// ... +// "keyN" "valueN" +// chunkname1 +// { +// "key0" "value0" +// "key1" "value1" +// ... +// "keyN" "valueN" +// } +// } +// ... +// chunknameN +// { +// "key0" "value0" +// "key1" "value1" +// ... +// "keyN" "valueN" +// } +// +// The chunk names are not necessarily unique, nor are the key names, unless the +// parsing application requires them to be. +// +// $NoKeywords: $ +//=============================================================================// + +#include +#include +#ifdef _LINUX +#include +#else +#include +#endif +#include +#include +#include +#include +#include +#include "chunkfile.h" +#include "vector.h" +#include "vector4d.h" +#include "vstdlib/strtools.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +// +// Fixes an infinite loop that occurs when loading certain VMFs. The bug +// occurs with Worldcraft built with DevStudio SP4. +// +#ifdef _MSC_VER +#pragma optimize("g", off) +#endif + +//----------------------------------------------------------------------------- +// Purpose: Constructor. +//----------------------------------------------------------------------------- +CChunkHandlerMap::CChunkHandlerMap(void) +{ + m_pHandlers = NULL; +} + + +//----------------------------------------------------------------------------- +// Purpose: Destructor. Frees handler list. +//----------------------------------------------------------------------------- +CChunkHandlerMap::~CChunkHandlerMap(void) +{ + ChunkHandlerInfoNode_t *pNode = m_pHandlers; + while (pNode != NULL) + { + ChunkHandlerInfoNode_t *pPrev = pNode; + pNode = pNode->pNext; + + delete pPrev; + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Adds a chunk handler to the handler list. +// Input : pszChunkName - Name of chunk to be handled. +// pfnHandler - Address of handler callback function. +// pData - Data to pass to the handler callback. +//----------------------------------------------------------------------------- +void CChunkHandlerMap::AddHandler(const char *pszChunkName, ChunkHandler_t pfnHandler, void *pData) +{ + ChunkHandlerInfoNode_t *pNew = new ChunkHandlerInfoNode_t; + + Q_strncpy(pNew->Handler.szChunkName, pszChunkName, sizeof( pNew->Handler.szChunkName )); + pNew->Handler.pfnHandler = pfnHandler; + pNew->Handler.pData = pData; + pNew->pNext = NULL; + + if (m_pHandlers == NULL) + { + m_pHandlers = pNew; + } + else + { + ChunkHandlerInfoNode_t *pNode = m_pHandlers; + while (pNode->pNext != NULL) + { + pNode = pNode->pNext; + } + pNode->pNext = pNew; + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Sets the callback for error handling within this chunk's scope. +// Input : pfnHandler - +// pData - +//----------------------------------------------------------------------------- +void CChunkHandlerMap::SetErrorHandler(ChunkErrorHandler_t pfnHandler, void *pData) +{ + m_pfnErrorHandler = pfnHandler; + m_pErrorData = pData; +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : ppData - +// Output : ChunkErrorHandler_t +//----------------------------------------------------------------------------- +ChunkErrorHandler_t CChunkHandlerMap::GetErrorHandler(void **ppData) +{ + *ppData = m_pErrorData; + return(m_pfnErrorHandler); +} + + +//----------------------------------------------------------------------------- +// Purpose: Gets the handler for a given chunk name, if one has been set. +// Input : pszChunkName - Name of chunk. +// ppfnHandler - Receives the address of the callback function. +// ppData - Receives the context data for the given chunk. +// Output : Returns true if a handler was found, false if not. +//----------------------------------------------------------------------------- +ChunkHandler_t CChunkHandlerMap::GetHandler(const char *pszChunkName, void **ppData) +{ + ChunkHandlerInfoNode_t *pNode = m_pHandlers; + while (pNode != NULL) + { + if (!stricmp(pNode->Handler.szChunkName, pszChunkName)) + { + *ppData = pNode->Handler.pData; + return(pNode->Handler.pfnHandler); + } + + pNode = pNode->pNext; + } + + return(false); +} + + +//----------------------------------------------------------------------------- +// Purpose: Constructor. Initializes data members. +//----------------------------------------------------------------------------- +CChunkFile::CChunkFile(void) +{ + m_hFile = NULL; + m_nCurrentDepth = 0; + m_szIndent[0] = '\0'; + m_nHandlerStackDepth = 0; + m_DefaultChunkHandler = 0; +} + + +//----------------------------------------------------------------------------- +// Purpose: Destructor. Closes the file if it is currently open. +//----------------------------------------------------------------------------- +CChunkFile::~CChunkFile(void) +{ + if (m_hFile != NULL) + { + fclose(m_hFile); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *pszChunkName - +// Output : ChunkFileResult_t +//----------------------------------------------------------------------------- +ChunkFileResult_t CChunkFile::BeginChunk(const char *pszChunkName) +{ + // + // Write the chunk name and open curly. + // + char szBuf[MAX_KEYVALUE_LEN]; + Q_snprintf(szBuf, sizeof( szBuf ), "%s\r\n%s{", pszChunkName, m_szIndent); + ChunkFileResult_t eResult = WriteLine(szBuf); + + // + // Update the indentation depth. + // + if (eResult == ChunkFile_Ok) + { + m_nCurrentDepth++; + BuildIndentString(m_szIndent, m_nCurrentDepth); + } + + return(eResult); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CChunkFile::BuildIndentString(char *pszDest, int nDepth) +{ + if (nDepth >= 0) + { + for (int i = 0; i < nDepth; i++) + { + pszDest[i] = '\t'; + } + + pszDest[nDepth] = '\0'; + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Output : ChunkFileResult_t +//----------------------------------------------------------------------------- +ChunkFileResult_t CChunkFile::Close(void) +{ + if (m_hFile != NULL) + { + fclose(m_hFile); + m_hFile = NULL; + } + + return(ChunkFile_Ok); +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Output : ChunkFileResult_t +//----------------------------------------------------------------------------- +ChunkFileResult_t CChunkFile::EndChunk(void) +{ + if (m_nCurrentDepth > 0) + { + m_nCurrentDepth--; + BuildIndentString(m_szIndent, m_nCurrentDepth); + } + + WriteLine("}"); + + return(ChunkFile_Ok); +} + + +//----------------------------------------------------------------------------- +// Purpose: Returns a string explaining the last error that occurred. +//----------------------------------------------------------------------------- +const char *CChunkFile::GetErrorText(ChunkFileResult_t eResult) +{ + static char szError[MAX_KEYVALUE_LEN]; + + switch (eResult) + { + case ChunkFile_UnexpectedEOF: + { + Q_strncpy(szError, "unexpected end of file", sizeof( szError ) ); + break; + } + + case ChunkFile_UnexpectedSymbol: + { + Q_snprintf(szError, sizeof( szError ), "unexpected symbol '%s'", m_szErrorToken); + break; + } + + case ChunkFile_OpenFail: + { + Q_snprintf(szError, sizeof( szError ), "%s", strerror(errno)) ; + break; + } + + case ChunkFile_StringTooLong: + { + Q_strncpy(szError, "unterminated string or string too long", sizeof( szError ) ); + break; + } + + default: + { + Q_snprintf(szError, sizeof( szError ), "error %d", eResult); + } + } + + return(m_TokenReader.Error(szError)); +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : eError - +//----------------------------------------------------------------------------- +void CChunkFile::HandleError(const char *szChunkName, ChunkFileResult_t eError) +{ + // UNDONE: dispatch errors to the error handler. + // - keep track of current chunkname for reporting errors + // - use the last non-NULL handler that was pushed onto the stack? + // - need a return code to determine whether to abort parsing? +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Output : ChunkFileResult_t +//----------------------------------------------------------------------------- +ChunkFileResult_t CChunkFile::HandleChunk(const char *szChunkName) +{ + // See if the default handler wants it? + if( m_DefaultChunkHandler ) + { + ChunkFileResult_t testResult = m_DefaultChunkHandler( this, m_pDefaultChunkHandlerData, szChunkName ); + if( testResult == ChunkFile_Ok ) + { + return ChunkFile_Ok; + } + } + + // + // If there is an active handler map... + // + if (m_nHandlerStackDepth > 0) + { + CChunkHandlerMap *pHandler = m_HandlerStack[m_nHandlerStackDepth - 1]; + + // + // If a chunk handler was found in the handler map... + // + void *pData; + ChunkHandler_t pfnHandler = pHandler->GetHandler(szChunkName, &pData); + if (pfnHandler != NULL) + { + // Dispatch this chunk to the handler. + ChunkFileResult_t eResult = pfnHandler(this, pData); + if (eResult != ChunkFile_Ok) + { + return(eResult); + } + } + else + { + // + // No handler for this chunk. Skip to the matching close curly brace. + // + int nDepth = 1; + ChunkFileResult_t eResult; + + do + { + ChunkType_t eChunkType; + char szKey[MAX_KEYVALUE_LEN]; + char szValue[MAX_KEYVALUE_LEN]; + + while ((eResult = ReadNext(szKey, szValue, sizeof(szValue), eChunkType)) == ChunkFile_Ok) + { + if (eChunkType == ChunkType_Chunk) + { + nDepth++; + } + } + + if (eResult == ChunkFile_EndOfChunk) + { + eResult = ChunkFile_Ok; + nDepth--; + } + else if (eResult == ChunkFile_EOF) + { + return(ChunkFile_UnexpectedEOF); + } + + } while ((nDepth) && (eResult == ChunkFile_Ok)); + } + } + + return(ChunkFile_Ok); +} + + +//----------------------------------------------------------------------------- +// Purpose: Opens the chunk file for reading or writing. +// Input : pszFileName - Path of file to open. +// eMode - ChunkFile_Read or ChunkFile_Write. +// Output : Returns ChunkFile_Ok on success, ChunkFile_Fail on failure. +// UNDONE: boolean return value? +//----------------------------------------------------------------------------- +ChunkFileResult_t CChunkFile::Open(const char *pszFileName, ChunkFileOpenMode_t eMode) +{ + if (eMode == ChunkFile_Read) + { + // UNDONE: TokenReader encapsulates file - unify reading and writing to use the same file I/O. + // UNDONE: Support in-memory parsing. + if (m_TokenReader.Open(pszFileName)) + { + m_nCurrentDepth = 0; + } + else + { + return(ChunkFile_OpenFail); + } + } + else if (eMode == ChunkFile_Write) + { + m_hFile = fopen(pszFileName, "wb"); + + if (m_hFile == NULL) + { + return(ChunkFile_OpenFail); + } + + m_nCurrentDepth = 0; + } + + return(ChunkFile_Ok); +} + + +//----------------------------------------------------------------------------- +// Purpose: Removes the topmost set of chunk handlers. +//----------------------------------------------------------------------------- +void CChunkFile::PopHandlers(void) +{ + if (m_nHandlerStackDepth > 0) + { + m_nHandlerStackDepth--; + } +} + + +void CChunkFile::SetDefaultChunkHandler( DefaultChunkHandler_t pHandler, void *pData ) +{ + m_DefaultChunkHandler = pHandler; + m_pDefaultChunkHandlerData = pData;} + + +//----------------------------------------------------------------------------- +// Purpose: Adds a set of chunk handlers to the top of the handler stack. +// Input : pHandlerMap - Object containing the list of chunk handlers. +//----------------------------------------------------------------------------- +void CChunkFile::PushHandlers(CChunkHandlerMap *pHandlerMap) +{ + if (m_nHandlerStackDepth < MAX_INDENT_DEPTH) + { + m_HandlerStack[m_nHandlerStackDepth] = pHandlerMap; + m_nHandlerStackDepth++; + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Reads the next term from the chunk file. The type of term read is +// returned in the eChunkType parameter. +// Input : szName - Name of key or chunk. +// szValue - If eChunkType is ChunkType_Key, contains the value of the key. +// nValueSize - Size of the buffer pointed to by szValue. +// eChunkType - ChunkType_Key or ChunkType_Chunk. +// Output : Returns ChunkFile_Ok on success, an error code if a parsing error occurs. +//----------------------------------------------------------------------------- +ChunkFileResult_t CChunkFile::ReadNext(char *szName, char *szValue, int nValueSize, ChunkType_t &eChunkType) +{ + // HACK: pass in buffer sizes? + trtoken_t eTokenType = m_TokenReader.NextToken(szName, MAX_KEYVALUE_LEN); + + if (eTokenType != TOKENEOF) + { + switch (eTokenType) + { + case IDENT: + case STRING: + { + char szNext[MAX_KEYVALUE_LEN]; + trtoken_t eNextTokenType; + + // + // Read the next token to determine what we have. + // + eNextTokenType = m_TokenReader.NextToken(szNext, sizeof(szNext)); + + switch (eNextTokenType) + { + case OPERATOR: + { + if (!stricmp(szNext, "{")) + { + // Beginning of new chunk. + m_nCurrentDepth++; + eChunkType = ChunkType_Chunk; + szValue[0] = '\0'; + return(ChunkFile_Ok); + } + else + { + // Unexpected symbol. + Q_strncpy(m_szErrorToken, szNext, sizeof( m_szErrorToken ) ); + return(ChunkFile_UnexpectedSymbol); + } + } + + case STRING: + case IDENT: + { + // Key value pair. + Q_strncpy(szValue, szNext, nValueSize ); + eChunkType = ChunkType_Key; + return(ChunkFile_Ok); + } + + case TOKENEOF: + { + // Unexpected end of file. + return(ChunkFile_UnexpectedEOF); + } + + case TOKENSTRINGTOOLONG: + { + // String too long or unterminated string. + return ChunkFile_StringTooLong; + } + + default: + break; + } + } + + case OPERATOR: + { + if (!stricmp(szName, "}")) + { + // End of current chunk. + m_nCurrentDepth--; + return(ChunkFile_EndOfChunk); + } + else + { + // Unexpected symbol. + Q_strncpy(m_szErrorToken, szName, sizeof( m_szErrorToken ) ); + return(ChunkFile_UnexpectedSymbol); + } + } + + case TOKENSTRINGTOOLONG: + { + return ChunkFile_StringTooLong; + } + + default: + break; + } + } + + if (m_nCurrentDepth != 0) + { + // End of file while within the scope of a chunk. + return(ChunkFile_UnexpectedEOF); + } + + return(ChunkFile_EOF); +} + + +//----------------------------------------------------------------------------- +// Purpose: Reads the current chunk and dispatches keys and sub-chunks to the +// appropriate handler callbacks. +// Input : pfnKeyHandler - Callback for any key values in this chunk. +// pData - Data to pass to the key value callback function. +// Output : Normally returns ChunkFile_Ok or ChunkFile_EOF. Otherwise, returns +// a ChunkFile_xxx error code. +//----------------------------------------------------------------------------- +ChunkFileResult_t CChunkFile::ReadChunk(KeyHandler_t pfnKeyHandler, void *pData) +{ + // + // Read the keys and sub-chunks. + // + ChunkFileResult_t eResult; + do + { + char szName[MAX_KEYVALUE_LEN]; + char szValue[MAX_KEYVALUE_LEN]; + ChunkType_t eChunkType; + + eResult = ReadNext(szName, szValue, sizeof(szValue), eChunkType); + + if (eResult == ChunkFile_Ok) + { + if (eChunkType == ChunkType_Chunk) + { + // + // Dispatch sub-chunks to the appropriate handler. + // + eResult = HandleChunk(szName); + } + else if ((eChunkType == ChunkType_Key) && (pfnKeyHandler != NULL)) + { + // + // Dispatch keys to the key value handler. + // + eResult = pfnKeyHandler(szName, szValue, pData); + } + } + } while (eResult == ChunkFile_Ok); + + // + // Cover up ChunkFile_EndOfChunk results because the caller doesn't want to see them. + // + if (eResult == ChunkFile_EndOfChunk) + { + eResult = ChunkFile_Ok; + } + + // + // Dispatch errors to the handler. + // + if ((eResult != ChunkFile_Ok) && (eResult != ChunkFile_EOF)) + { + //HandleError("chunkname", eResult); + } + + return(eResult); +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : pszValue - +// pbBool - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CChunkFile::ReadKeyValueBool(const char *pszValue, bool &bBool) +{ + int nValue = atoi(pszValue); + + if (nValue > 0) + { + bBool = true; + } + else + { + bBool = false; + } + + return(true); +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : pszValue - +// pfFloat - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CChunkFile::ReadKeyValueFloat(const char *pszValue, float &flFloat) +{ + flFloat = (float)atof(pszValue); + return(true); +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : pszValue - +// pnInt - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CChunkFile::ReadKeyValueInt(const char *pszValue, int &nInt) +{ + nInt = atoi(pszValue); + return(true); +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : pszKey - +// r - +// g - +// b - +// Output : +//----------------------------------------------------------------------------- +bool CChunkFile::ReadKeyValueColor(const char *pszValue, unsigned char &chRed, unsigned char &chGreen, unsigned char &chBlue) +{ + if (pszValue != NULL) + { + int r; + int g; + int b; + + if (sscanf(pszValue, "%d %d %d", &r, &g, &b) == 3) + { + chRed = r; + chGreen = g; + chBlue = b; + + return(true); + } + } + + return(false); +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : pszValue - +// pfPoint - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CChunkFile::ReadKeyValuePoint(const char *pszValue, Vector &Point) +{ + if (pszValue != NULL) + { + return(sscanf(pszValue, "(%f %f %f)", &Point.x, &Point.y, &Point.z) == 3); + } + + return(false); +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : pszValue - +// pfVector - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CChunkFile::ReadKeyValueVector2(const char *pszValue, Vector2D &vec) +{ + if (pszValue != NULL) + { + return ( sscanf( pszValue, "[%f %f]", &vec.x, &vec.y) == 2 ); + } + + return(false); +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : pszValue - +// pfVector - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CChunkFile::ReadKeyValueVector3(const char *pszValue, Vector &vec) +{ + if (pszValue != NULL) + { + return(sscanf(pszValue, "[%f %f %f]", &vec.x, &vec.y, &vec.z) == 3); + } + + return(false); +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : pszValue - +// pfVector - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CChunkFile::ReadKeyValueVector4(const char *pszValue, Vector4D &vec) +{ + if( pszValue != NULL ) + { + return(sscanf(pszValue, "[%f %f %f %f]", &vec[0], &vec[1], &vec[2], &vec[3]) == 4); + } + + return false; +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : pszLine - +// Output : +//----------------------------------------------------------------------------- +ChunkFileResult_t CChunkFile::WriteKeyValue(const char *pszKey, const char *pszValue) +{ + if ((pszKey != NULL) && (pszValue != NULL)) + { + char szTemp[MAX_KEYVALUE_LEN]; + Q_snprintf(szTemp, sizeof( szTemp ), "\"%s\" \"%s\"", pszKey, pszValue); + return(WriteLine(szTemp)); + } + + return(ChunkFile_Ok); +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : pszKey - +// bValue - +// Output : +//----------------------------------------------------------------------------- +ChunkFileResult_t CChunkFile::WriteKeyValueBool(const char *pszKey, bool bValue) +{ + if (pszKey != NULL) + { + char szBuf[MAX_KEYVALUE_LEN]; + Q_snprintf(szBuf, sizeof( szBuf ), "\"%s\" \"%d\"", pszKey, (int)bValue); + return(WriteLine(szBuf)); + } + + return(ChunkFile_Ok); +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : pszKey - +// nValue - +// Output : +//----------------------------------------------------------------------------- +ChunkFileResult_t CChunkFile::WriteKeyValueInt(const char *pszKey, int nValue) +{ + if (pszKey != NULL) + { + char szBuf[MAX_KEYVALUE_LEN]; + Q_snprintf(szBuf, sizeof( szBuf ), "\"%s\" \"%d\"", pszKey, nValue); + return(WriteLine(szBuf)); + } + + return(ChunkFile_Ok); +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : pszKey - +// fValue - +// Output : +//----------------------------------------------------------------------------- +ChunkFileResult_t CChunkFile::WriteKeyValueFloat(const char *pszKey, float fValue) +{ + if (pszKey != NULL) + { + char szBuf[MAX_KEYVALUE_LEN]; + Q_snprintf(szBuf, sizeof( szBuf ), "\"%s\" \"%g\"", pszKey, (double)fValue); + return(WriteLine(szBuf)); + } + + return(ChunkFile_Ok); +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : pszKey - +// r - +// g - +// b - +// Output : +//----------------------------------------------------------------------------- +ChunkFileResult_t CChunkFile::WriteKeyValueColor(const char *pszKey, unsigned char r, unsigned char g, unsigned char b) +{ + if (pszKey != NULL) + { + char szBuf[MAX_KEYVALUE_LEN]; + Q_snprintf(szBuf, sizeof( szBuf ), "\"%s\" \"%d %d %d\"", pszKey, (int)r, (int)g, (int)b); + return(WriteLine(szBuf)); + } + + return(ChunkFile_Ok); +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : pszKey - +// fVector - +// Output : +//----------------------------------------------------------------------------- +ChunkFileResult_t CChunkFile::WriteKeyValuePoint(const char *pszKey, const Vector &Point) +{ + if (pszKey != NULL) + { + char szBuf[MAX_KEYVALUE_LEN]; + Q_snprintf(szBuf, sizeof( szBuf ), "\"%s\" \"(%g %g %g)\"", pszKey, (double)Point[0], (double)Point[1], (double)Point[2]); + return(WriteLine(szBuf)); + } + + return(ChunkFile_Ok); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +ChunkFileResult_t CChunkFile::WriteKeyValueVector2(const char *pszKey, const Vector2D &vec) +{ + if (pszKey != NULL) + { + char szBuf[MAX_KEYVALUE_LEN]; + Q_snprintf( szBuf, sizeof( szBuf ), "\"%s\" \"[%g %g]\"", pszKey, (double)vec.x, (double)vec.y ); + return(WriteLine(szBuf)); + } + + return(ChunkFile_Ok); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +ChunkFileResult_t CChunkFile::WriteKeyValueVector3(const char *pszKey, const Vector &vec) +{ + if (pszKey != NULL) + { + char szBuf[MAX_KEYVALUE_LEN]; + Q_snprintf(szBuf, sizeof( szBuf ), "\"%s\" \"[%g %g %g]\"", pszKey, (double)vec.x, (double)vec.y, (double)vec.z); + return(WriteLine(szBuf)); + } + + return(ChunkFile_Ok); +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : pszKey - +// fVector - +// Output : +//----------------------------------------------------------------------------- +ChunkFileResult_t CChunkFile::WriteKeyValueVector4(const char *pszKey, const Vector4D &vec) +{ + if (pszKey != NULL) + { + char szBuf[MAX_KEYVALUE_LEN]; + Q_snprintf(szBuf, sizeof( szBuf ), "\"%s\" \"[%g %g %g %g]\"", pszKey, (double)vec.x, (double)vec.y, (double)vec.z, (double)vec.w); + return( WriteLine( szBuf ) ); + } + + return( ChunkFile_Ok ); +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *pszLine - +// Output : +//----------------------------------------------------------------------------- +ChunkFileResult_t CChunkFile::WriteLine(const char *pszLine) +{ + if (pszLine != NULL) + { + // + // Write the indentation string. + // + if (m_nCurrentDepth > 0) + { + int nWritten = fwrite(m_szIndent, 1, m_nCurrentDepth, m_hFile); + if (nWritten != m_nCurrentDepth) + { + return(ChunkFile_Fail); + } + } + + // + // Write the string. + // + int nLen = strlen(pszLine); + int nWritten = fwrite(pszLine, 1, nLen, m_hFile); + if (nWritten != nLen) + { + return(ChunkFile_Fail); + } + + // + // Write the linefeed. + // + if (fwrite("\r\n", 1, 2, m_hFile) != 2) + { + return(ChunkFile_Fail); + } + } + + return(ChunkFile_Ok); +} + diff --git a/public/chunkfile.h b/public/chunkfile.h index e62491ea..f18c985f 100644 --- a/public/chunkfile.h +++ b/public/chunkfile.h @@ -1,197 +1,197 @@ -//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// -// -// Purpose: -// -// $NoKeywords: $ -//===========================================================================// - -#ifndef CHUNKFILE_H -#define CHUNKFILE_H - -#ifdef _WIN32 -#pragma once -#endif - -#include -#include "TokenReader.h" - - -#define MAX_INDENT_DEPTH 80 -#define MAX_KEYVALUE_LEN 1024 - - -class CChunkFile; -class Vector2D; -class Vector; -class Vector4D; - - -// -// Modes for Open. -// -enum ChunkFileOpenMode_t -{ - ChunkFile_Read = 0, - ChunkFile_Write, -}; - - -// -// Return codes. -// -enum ChunkFileResult_t -{ - ChunkFile_Ok = 0, - ChunkFile_Fail, - ChunkFile_OpenFail, - ChunkFile_EndOfChunk, - ChunkFile_EOF, - ChunkFile_UnexpectedEOF, - ChunkFile_UnexpectedSymbol, - ChunkFile_OutOfMemory, - ChunkFile_StringTooLong, - ChunkFile_NotHandled -}; - - -enum ChunkType_t -{ - ChunkType_Key = 0, - ChunkType_Chunk, -}; - - -typedef ChunkFileResult_t (*DefaultChunkHandler_t)(CChunkFile *pFile, void *pData, char const *pChunkName); - -typedef ChunkFileResult_t (*ChunkHandler_t)(CChunkFile *pFile, void *pData); -typedef ChunkFileResult_t (*KeyHandler_t)(const char *szKey, const char *szValue, void *pData); -typedef bool (*ChunkErrorHandler_t)(CChunkFile *pFile, const char *szChunkName, void *pData); - - -struct ChunkHandlerInfo_t -{ - char szChunkName[80]; - ChunkHandler_t pfnHandler; - void *pData; -}; - - -struct ChunkHandlerInfoNode_t -{ - ChunkHandlerInfo_t Handler; - struct ChunkHandlerInfoNode_t *pNext; -}; - - -// -// Consider handling chunks with handler objects instead of callbacks. -// -//class CChunkHandler -//{ -// virtual ChunkFileResult_t HandleChunk(const char *szName); -// virtual ChunkFileResult_t HandleKey(const char *szKey, const char *szValue); -// virtual bool HandleError(const char *szChunkName, ChunkFileResult_t eError); -//}; - - -class CChunkHandlerMap -{ - public: - - CChunkHandlerMap(void); - ~CChunkHandlerMap(void); - - void AddHandler(const char *pszChunkName, ChunkHandler_t pfnHandler, void *pData); - ChunkHandler_t GetHandler(const char *pszChunkName, void **pData); - - void SetErrorHandler(ChunkErrorHandler_t pfnHandler, void *pData); - ChunkErrorHandler_t GetErrorHandler(void **pData); - - protected: - - ChunkHandlerInfoNode_t *m_pHandlers; - ChunkErrorHandler_t m_pfnErrorHandler; - void *m_pErrorData; -}; - - -class CChunkFile -{ - public: - - CChunkFile(void); - ~CChunkFile(void); - - ChunkFileResult_t Open(const char *pszFileName, ChunkFileOpenMode_t eMode); - ChunkFileResult_t Close(void); - const char *GetErrorText(ChunkFileResult_t eResult); - - // - // Functions for writing chunk files. - // - ChunkFileResult_t BeginChunk(const char *pszChunkName); - ChunkFileResult_t EndChunk(void); - - ChunkFileResult_t WriteKeyValue(const char *pszKey, const char *pszValue); - ChunkFileResult_t WriteKeyValueBool(const char *pszKey, bool bValue); - ChunkFileResult_t WriteKeyValueColor(const char *pszKey, unsigned char r, unsigned char g, unsigned char b); - ChunkFileResult_t WriteKeyValueFloat(const char *pszKey, float fValue); - ChunkFileResult_t WriteKeyValueInt(const char *pszKey, int nValue); - ChunkFileResult_t WriteKeyValuePoint(const char *pszKey, const Vector &Point); - ChunkFileResult_t WriteKeyValueVector2(const char *pszKey, const Vector2D &vec); - ChunkFileResult_t WriteKeyValueVector3(const char *pszKey, const Vector &vec); - ChunkFileResult_t WriteKeyValueVector4( const char *pszKey, const Vector4D &vec); - - ChunkFileResult_t WriteLine(const char *pszLine); - - // - // Functions for reading chunk files. - // - ChunkFileResult_t ReadChunk(KeyHandler_t pfnKeyHandler = NULL, void *pData = NULL); - ChunkFileResult_t ReadNext(char *szKey, char *szValue, int nValueSize, ChunkType_t &eChunkType); - ChunkFileResult_t HandleChunk(const char *szChunkName); - void HandleError(const char *szChunkName, ChunkFileResult_t eError); - - // These functions should more really be named Parsexxx and possibly moved elsewhere. - static bool ReadKeyValueBool(const char *pszValue, bool &bBool); - static bool ReadKeyValueColor(const char *pszValue, unsigned char &chRed, unsigned char &chGreen, unsigned char &chBlue); - static bool ReadKeyValueInt(const char *pszValue, int &nInt); - static bool ReadKeyValueFloat(const char *pszValue, float &flFloat); - static bool ReadKeyValuePoint(const char *pszValue, Vector &Point); - static bool ReadKeyValueVector2(const char *pszValue, Vector2D &vec); - static bool ReadKeyValueVector3(const char *pszValue, Vector &vec); - static bool ReadKeyValueVector4( const char *pszValue, Vector4D &vec); - - // The default chunk handler gets called before any other chunk handlers. - // - // If the handler returns ChunkFile_Ok, then it goes into the chunk. - // If the handler returns ChunkFile_NotHandled, then the chunk is - // passed to the regular handlers. - // - // If you pass NULL in here, then it disables the default chunk handler. - void SetDefaultChunkHandler( DefaultChunkHandler_t pHandler, void *pData ); - - void PushHandlers(CChunkHandlerMap *pHandlerMap); - void PopHandlers(void); - - protected: - - void BuildIndentString(char *pszDest, int nDepth); - - TokenReader m_TokenReader; - - FILE *m_hFile; - char m_szErrorToken[80]; - char m_szIndent[MAX_INDENT_DEPTH]; - int m_nCurrentDepth; - - // See SetDefaultChunkHandler.. - DefaultChunkHandler_t m_DefaultChunkHandler; - void *m_pDefaultChunkHandlerData; - - CChunkHandlerMap *m_HandlerStack[MAX_INDENT_DEPTH]; - int m_nHandlerStackDepth; -}; - - -#endif // CHUNKFILE_H +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $NoKeywords: $ +//===========================================================================// + +#ifndef CHUNKFILE_H +#define CHUNKFILE_H + +#ifdef _WIN32 +#pragma once +#endif + +#include +#include "tokenreader.h" + + +#define MAX_INDENT_DEPTH 80 +#define MAX_KEYVALUE_LEN 1024 + + +class CChunkFile; +class Vector2D; +class Vector; +class Vector4D; + + +// +// Modes for Open. +// +enum ChunkFileOpenMode_t +{ + ChunkFile_Read = 0, + ChunkFile_Write, +}; + + +// +// Return codes. +// +enum ChunkFileResult_t +{ + ChunkFile_Ok = 0, + ChunkFile_Fail, + ChunkFile_OpenFail, + ChunkFile_EndOfChunk, + ChunkFile_EOF, + ChunkFile_UnexpectedEOF, + ChunkFile_UnexpectedSymbol, + ChunkFile_OutOfMemory, + ChunkFile_StringTooLong, + ChunkFile_NotHandled +}; + + +enum ChunkType_t +{ + ChunkType_Key = 0, + ChunkType_Chunk, +}; + + +typedef ChunkFileResult_t (*DefaultChunkHandler_t)(CChunkFile *pFile, void *pData, char const *pChunkName); + +typedef ChunkFileResult_t (*ChunkHandler_t)(CChunkFile *pFile, void *pData); +typedef ChunkFileResult_t (*KeyHandler_t)(const char *szKey, const char *szValue, void *pData); +typedef bool (*ChunkErrorHandler_t)(CChunkFile *pFile, const char *szChunkName, void *pData); + + +struct ChunkHandlerInfo_t +{ + char szChunkName[80]; + ChunkHandler_t pfnHandler; + void *pData; +}; + + +struct ChunkHandlerInfoNode_t +{ + ChunkHandlerInfo_t Handler; + struct ChunkHandlerInfoNode_t *pNext; +}; + + +// +// Consider handling chunks with handler objects instead of callbacks. +// +//class CChunkHandler +//{ +// virtual ChunkFileResult_t HandleChunk(const char *szName); +// virtual ChunkFileResult_t HandleKey(const char *szKey, const char *szValue); +// virtual bool HandleError(const char *szChunkName, ChunkFileResult_t eError); +//}; + + +class CChunkHandlerMap +{ + public: + + CChunkHandlerMap(void); + ~CChunkHandlerMap(void); + + void AddHandler(const char *pszChunkName, ChunkHandler_t pfnHandler, void *pData); + ChunkHandler_t GetHandler(const char *pszChunkName, void **pData); + + void SetErrorHandler(ChunkErrorHandler_t pfnHandler, void *pData); + ChunkErrorHandler_t GetErrorHandler(void **pData); + + protected: + + ChunkHandlerInfoNode_t *m_pHandlers; + ChunkErrorHandler_t m_pfnErrorHandler; + void *m_pErrorData; +}; + + +class CChunkFile +{ + public: + + CChunkFile(void); + ~CChunkFile(void); + + ChunkFileResult_t Open(const char *pszFileName, ChunkFileOpenMode_t eMode); + ChunkFileResult_t Close(void); + const char *GetErrorText(ChunkFileResult_t eResult); + + // + // Functions for writing chunk files. + // + ChunkFileResult_t BeginChunk(const char *pszChunkName); + ChunkFileResult_t EndChunk(void); + + ChunkFileResult_t WriteKeyValue(const char *pszKey, const char *pszValue); + ChunkFileResult_t WriteKeyValueBool(const char *pszKey, bool bValue); + ChunkFileResult_t WriteKeyValueColor(const char *pszKey, unsigned char r, unsigned char g, unsigned char b); + ChunkFileResult_t WriteKeyValueFloat(const char *pszKey, float fValue); + ChunkFileResult_t WriteKeyValueInt(const char *pszKey, int nValue); + ChunkFileResult_t WriteKeyValuePoint(const char *pszKey, const Vector &Point); + ChunkFileResult_t WriteKeyValueVector2(const char *pszKey, const Vector2D &vec); + ChunkFileResult_t WriteKeyValueVector3(const char *pszKey, const Vector &vec); + ChunkFileResult_t WriteKeyValueVector4( const char *pszKey, const Vector4D &vec); + + ChunkFileResult_t WriteLine(const char *pszLine); + + // + // Functions for reading chunk files. + // + ChunkFileResult_t ReadChunk(KeyHandler_t pfnKeyHandler = NULL, void *pData = NULL); + ChunkFileResult_t ReadNext(char *szKey, char *szValue, int nValueSize, ChunkType_t &eChunkType); + ChunkFileResult_t HandleChunk(const char *szChunkName); + void HandleError(const char *szChunkName, ChunkFileResult_t eError); + + // These functions should more really be named Parsexxx and possibly moved elsewhere. + static bool ReadKeyValueBool(const char *pszValue, bool &bBool); + static bool ReadKeyValueColor(const char *pszValue, unsigned char &chRed, unsigned char &chGreen, unsigned char &chBlue); + static bool ReadKeyValueInt(const char *pszValue, int &nInt); + static bool ReadKeyValueFloat(const char *pszValue, float &flFloat); + static bool ReadKeyValuePoint(const char *pszValue, Vector &Point); + static bool ReadKeyValueVector2(const char *pszValue, Vector2D &vec); + static bool ReadKeyValueVector3(const char *pszValue, Vector &vec); + static bool ReadKeyValueVector4( const char *pszValue, Vector4D &vec); + + // The default chunk handler gets called before any other chunk handlers. + // + // If the handler returns ChunkFile_Ok, then it goes into the chunk. + // If the handler returns ChunkFile_NotHandled, then the chunk is + // passed to the regular handlers. + // + // If you pass NULL in here, then it disables the default chunk handler. + void SetDefaultChunkHandler( DefaultChunkHandler_t pHandler, void *pData ); + + void PushHandlers(CChunkHandlerMap *pHandlerMap); + void PopHandlers(void); + + protected: + + void BuildIndentString(char *pszDest, int nDepth); + + TokenReader m_TokenReader; + + FILE *m_hFile; + char m_szErrorToken[80]; + char m_szIndent[MAX_INDENT_DEPTH]; + int m_nCurrentDepth; + + // See SetDefaultChunkHandler.. + DefaultChunkHandler_t m_DefaultChunkHandler; + void *m_pDefaultChunkHandlerData; + + CChunkHandlerMap *m_HandlerStack[MAX_INDENT_DEPTH]; + int m_nHandlerStackDepth; +}; + + +#endif // CHUNKFILE_H diff --git a/public/collisionutils.cpp b/public/collisionutils.cpp index 07097a15..af7590d2 100644 --- a/public/collisionutils.cpp +++ b/public/collisionutils.cpp @@ -1,2665 +1,2665 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: Common collision utility methods -// -// $Header: $ -// $NoKeywords: $ -//=============================================================================// - -#if !defined(_STATIC_LINKED) || defined(_SHARED_LIB) - -#include "collisionutils.h" -#include "cmodel.h" -#include "mathlib.h" -#include "vector.h" -#include "tier0/dbg.h" -#include -#include "vector4d.h" -#include "trace.h" - -// memdbgon must be the last include file in a .cpp file!!! -#include "tier0/memdbgon.h" - -#define UNINIT -99999.0 - -//----------------------------------------------------------------------------- -// Clears the trace -//----------------------------------------------------------------------------- -static void Collision_ClearTrace( const Vector &vecRayStart, const Vector &vecRayDelta, CBaseTrace *pTrace ) -{ - pTrace->startpos = vecRayStart; - pTrace->endpos = vecRayStart; - pTrace->endpos += vecRayDelta; - pTrace->startsolid = false; - pTrace->allsolid = false; - pTrace->fraction = 1.0f; - pTrace->contents = 0; -} - - -//----------------------------------------------------------------------------- -// Compute the offset in t along the ray that we'll use for the collision -//----------------------------------------------------------------------------- -static float ComputeBoxOffset( const Ray_t& ray ) -{ - if (ray.m_IsRay) - return 1e-3f; - - // Find the projection of the box diagonal along the ray... - float offset = FloatMakePositive(ray.m_Extents[0] * ray.m_Delta[0]) + - FloatMakePositive(ray.m_Extents[1] * ray.m_Delta[1]) + - FloatMakePositive(ray.m_Extents[2] * ray.m_Delta[2]); - - // We need to divide twice: Once to normalize the computation above - // so we get something in units of extents, and the second to normalize - // that with respect to the entire raycast. - offset *= InvRSquared( ray.m_Delta ); - - // 1e-3 is an epsilon - return offset + 1e-3; -} - - -//----------------------------------------------------------------------------- -// Intersects a swept box against a triangle -//----------------------------------------------------------------------------- -float IntersectRayWithTriangle( const Ray_t& ray, - const Vector& v1, const Vector& v2, const Vector& v3, bool oneSided ) -{ - // This is cute: Use barycentric coordinates to represent the triangle - // Vo(1-u-v) + V1u + V2v and intersect that with a line Po + Dt - // This gives us 3 equations + 3 unknowns, which we can solve with - // Cramer's rule... - // E1x u + E2x v - Dx t = Pox - Vox - // There's a couple of other optimizations, Cramer's rule involves - // computing the determinant of a matrix which has been constructed - // by three vectors. It turns out that - // det | A B C | = -( A x C ) dot B or -(C x B) dot A - // which we'll use below.. - - Vector edge1, edge2, org; - VectorSubtract( v2, v1, edge1 ); - VectorSubtract( v3, v1, edge2 ); - - // Cull out one-sided stuff - if (oneSided) - { - Vector normal; - CrossProduct( edge1, edge2, normal ); - if (DotProduct( normal, ray.m_Delta ) >= 0.0f) - return -1.0f; - } - - // FIXME: This is inaccurate, but fast for boxes - // We want to do a fast separating axis implementation here - // with a swept triangle along the reverse direction of the ray. - - // Compute some intermediary terms - Vector dirCrossEdge2, orgCrossEdge1; - CrossProduct( ray.m_Delta, edge2, dirCrossEdge2 ); - - // Compute the denominator of Cramer's rule: - // | -Dx E1x E2x | - // det | -Dy E1y E2y | = (D x E2) dot E1 - // | -Dz E1z E2z | - float denom = DotProduct( dirCrossEdge2, edge1 ); - if( FloatMakePositive( denom ) < 1e-6 ) - return -1.0f; - denom = 1.0f / denom; - - // Compute u. It's gotta lie in the range of 0 to 1. - // | -Dx orgx E2x | - // u = denom * det | -Dy orgy E2y | = (D x E2) dot org - // | -Dz orgz E2z | - VectorSubtract( ray.m_Start, v1, org ); - float u = DotProduct( dirCrossEdge2, org ) * denom; - if ((u < 0.0f) || (u > 1.0f)) - return -1.0f; - - // Compute t and v the same way... - // In barycentric coords, u + v < 1 - CrossProduct( org, edge1, orgCrossEdge1 ); - float v = DotProduct( orgCrossEdge1, ray.m_Delta ) * denom; - if ((v < 0.0f) || (v + u > 1.0f)) - return -1.0f; - - // Compute the distance along the ray direction that we need to fudge - // when using swept boxes - float boxt = ComputeBoxOffset( ray ); - float t = DotProduct( orgCrossEdge1, edge2 ) * denom; - if ((t < -boxt) || (t > 1.0f + boxt)) - return -1.0f; - - return clamp( t, 0, 1 ); -} - -//----------------------------------------------------------------------------- -// computes the barycentric coordinates of an intersection -//----------------------------------------------------------------------------- - -bool ComputeIntersectionBarycentricCoordinates( const Ray_t& ray, - const Vector& v1, const Vector& v2, const Vector& v3, float& u, float& v, - float *t ) -{ - Vector edge1, edge2, org; - VectorSubtract( v2, v1, edge1 ); - VectorSubtract( v3, v1, edge2 ); - - // Compute some intermediary terms - Vector dirCrossEdge2, orgCrossEdge1; - CrossProduct( ray.m_Delta, edge2, dirCrossEdge2 ); - - // Compute the denominator of Cramer's rule: - // | -Dx E1x E2x | - // det | -Dy E1y E2y | = (D x E2) dot E1 - // | -Dz E1z E2z | - float denom = DotProduct( dirCrossEdge2, edge1 ); - if( FloatMakePositive( denom ) < 1e-6 ) - return false; - denom = 1.0f / denom; - - // Compute u. It's gotta lie in the range of 0 to 1. - // | -Dx orgx E2x | - // u = denom * det | -Dy orgy E2y | = (D x E2) dot org - // | -Dz orgz E2z | - VectorSubtract( ray.m_Start, v1, org ); - u = DotProduct( dirCrossEdge2, org ) * denom; - - // Compute t and v the same way... - // In barycentric coords, u + v < 1 - CrossProduct( org, edge1, orgCrossEdge1 ); - v = DotProduct( orgCrossEdge1, ray.m_Delta ) * denom; - - // Compute the distance along the ray direction that we need to fudge - // when using swept boxes - if( t ) - { - float boxt = ComputeBoxOffset( ray ); - *t = DotProduct( orgCrossEdge1, edge2 ) * denom; - if( ( *t < -boxt ) || ( *t > 1.0f + boxt ) ) - return false; - } - - return true; -} - -//----------------------------------------------------------------------------- -// Intersects a plane with a triangle (requires barycentric definition) -//----------------------------------------------------------------------------- - -int IntersectTriangleWithPlaneBarycentric( const Vector& org, const Vector& edgeU, - const Vector& edgeV, const Vector4D& plane, Vector2D* pIntersection ) -{ - // This uses a barycentric method, since we need that to determine - // interpolated points, alphas, and normals - // Given the plane equation P dot N + d = 0 - // and the barycentric coodinate equation P = Org + EdgeU * u + EdgeV * v - // Plug em in. Intersection occurs at u = 0 or v = 0 or u + v = 1 - - float orgDotNormal = DotProduct( org, plane.AsVector3D() ); - float edgeUDotNormal = DotProduct( edgeU, plane.AsVector3D() ); - float edgeVDotNormal = DotProduct( edgeV, plane.AsVector3D() ); - - int ptIdx = 0; - - // u = 0 - if ( edgeVDotNormal != 0.0f ) - { - pIntersection[ptIdx].x = 0.0f; - pIntersection[ptIdx].y = - ( orgDotNormal - plane.w ) / edgeVDotNormal; - if ((pIntersection[ptIdx].y >= 0.0f) && (pIntersection[ptIdx].y <= 1.0f)) - ++ptIdx; - } - - // v = 0 - if ( edgeUDotNormal != 0.0f ) - { - pIntersection[ptIdx].x = - ( orgDotNormal - plane.w ) / edgeUDotNormal; - pIntersection[ptIdx].y = 0.0f; - if ((pIntersection[ptIdx].x >= 0.0f) && (pIntersection[ptIdx].x <= 1.0f)) - ++ptIdx; - } - - // u + v = 1 - if (ptIdx == 2) - return ptIdx; - - if ( edgeVDotNormal != edgeUDotNormal ) - { - pIntersection[ptIdx].x = - ( orgDotNormal - plane.w + edgeVDotNormal) / - ( edgeUDotNormal - edgeVDotNormal); - pIntersection[ptIdx].y = 1.0f - pIntersection[ptIdx].x;; - if ((pIntersection[ptIdx].x >= 0.0f) && (pIntersection[ptIdx].x <= 1.0f) && - (pIntersection[ptIdx].y >= 0.0f) && (pIntersection[ptIdx].y <= 1.0f)) - ++ptIdx; - } - - Assert( ptIdx < 3 ); - return ptIdx; -} - - -//----------------------------------------------------------------------------- -// Returns true if a box intersects with a sphere -//----------------------------------------------------------------------------- -bool IsSphereIntersectingSphere( const Vector& center1, float radius1, - const Vector& center2, float radius2 ) -{ - Vector delta; - VectorSubtract( center2, center1, delta ); - float distSq = delta.LengthSqr(); - float radiusSum = radius1 + radius2; - return (distSq <= (radiusSum * radiusSum)); -} - - -//----------------------------------------------------------------------------- -// Returns true if a box intersects with a sphere -//----------------------------------------------------------------------------- -bool IsBoxIntersectingSphere( const Vector& boxMin, const Vector& boxMax, - const Vector& center, float radius ) -{ - // See Graphics Gems, box-sphere intersection - float dmin = 0.0f; - float flDelta; - - // Unrolled the loop.. this is a big cycle stealer... - if (center[0] < boxMin[0]) - { - flDelta = center[0] - boxMin[0]; - dmin += flDelta * flDelta; - } - else if (center[0] > boxMax[0]) - { - flDelta = boxMax[0] - center[0]; - dmin += flDelta * flDelta; - } - - if (center[1] < boxMin[1]) - { - flDelta = center[1] - boxMin[1]; - dmin += flDelta * flDelta; - } - else if (center[1] > boxMax[1]) - { - flDelta = boxMax[1] - center[1]; - dmin += flDelta * flDelta; - } - - if (center[2] < boxMin[2]) - { - flDelta = center[2] - boxMin[2]; - dmin += flDelta * flDelta; - } - else if (center[2] > boxMax[2]) - { - flDelta = boxMax[2] - center[2]; - dmin += flDelta * flDelta; - } - - return dmin < radius * radius; -} - -bool IsBoxIntersectingSphereExtents( const Vector& boxCenter, const Vector& boxHalfDiag, - const Vector& center, float radius ) -{ - // See Graphics Gems, box-sphere intersection - float dmin = 0.0f; - float flDelta, flDiff; - - // Unrolled the loop.. this is a big cycle stealer... - flDiff = FloatMakePositive( center.x - boxCenter.x ); - if (flDiff > boxHalfDiag.x) - { - flDelta = flDiff - boxHalfDiag.x; - dmin += flDelta * flDelta; - } - - flDiff = FloatMakePositive( center.y - boxCenter.y ); - if (flDiff > boxHalfDiag.y) - { - flDelta = flDiff - boxHalfDiag.y; - dmin += flDelta * flDelta; - } - - flDiff = FloatMakePositive( center.z - boxCenter.z ); - if (flDiff > boxHalfDiag.z) - { - flDelta = flDiff - boxHalfDiag.z; - dmin += flDelta * flDelta; - } - - return dmin < radius * radius; -} - - -//----------------------------------------------------------------------------- -// Returns true if a rectangle intersects with a circle -//----------------------------------------------------------------------------- -bool IsCircleIntersectingRectangle( const Vector2D& boxMin, const Vector2D& boxMax, - const Vector2D& center, float radius ) -{ - // See Graphics Gems, box-sphere intersection - float dmin = 0.0f; - float flDelta; - - if (center[0] < boxMin[0]) - { - flDelta = center[0] - boxMin[0]; - dmin += flDelta * flDelta; - } - else if (center[0] > boxMax[0]) - { - flDelta = boxMax[0] - center[0]; - dmin += flDelta * flDelta; - } - - if (center[1] < boxMin[1]) - { - flDelta = center[1] - boxMin[1]; - dmin += flDelta * flDelta; - } - else if (center[1] > boxMax[1]) - { - flDelta = boxMax[1] - center[1]; - dmin += flDelta * flDelta; - } - - return dmin < radius * radius; -} - - -//----------------------------------------------------------------------------- -// returns true if there's an intersection between ray and sphere -//----------------------------------------------------------------------------- -bool IsRayIntersectingSphere( const Vector &vecRayOrigin, const Vector &vecRayDelta, - const Vector& vecCenter, float flRadius, float flTolerance ) -{ - // For this algorithm, find a point on the ray which is closest to the sphere origin - // Do this by making a plane passing through the sphere origin - // whose normal is parallel to the ray. Intersect that plane with the ray. - // Plane: N dot P = I, N = D (ray direction), I = C dot N = C dot D - // Ray: P = O + D * t - // D dot ( O + D * t ) = C dot D - // D dot O + D dot D * t = C dot D - // t = (C - O) dot D / D dot D - // Clamp t to (0,1) - // Find distance of the point on the ray to the sphere center. - Assert( flTolerance >= 0.0f ); - flRadius += flTolerance; - - Vector vecRayToSphere; - VectorSubtract( vecCenter, vecRayOrigin, vecRayToSphere ); - float flNumerator = DotProduct( vecRayToSphere, vecRayDelta ); - - float t; - if (flNumerator <= 0.0f) - { - t = 0.0f; - } - else - { - float flDenominator = DotProduct( vecRayDelta, vecRayDelta ); - if ( flNumerator > flDenominator ) - t = 1.0f; - else - t = flNumerator / flDenominator; - } - - Vector vecClosestPoint; - VectorMA( vecRayOrigin, t, vecRayDelta, vecClosestPoint ); - return ( vecClosestPoint.DistToSqr( vecCenter ) <= flRadius * flRadius ); - - // NOTE: This in an alternate algorithm which I didn't use because I'd have to use a sqrt - // So it's probably faster to do this other algorithm. I'll leave the comments here - // for how to go back if we want to - - // Solve using the ray equation + the sphere equation - // P = o + dt - // (x - xc)^2 + (y - yc)^2 + (z - zc)^2 = r^2 - // (ox + dx * t - xc)^2 + (oy + dy * t - yc)^2 + (oz + dz * t - zc)^2 = r^2 - // (ox - xc)^2 + 2 * (ox-xc) * dx * t + dx^2 * t^2 + - // (oy - yc)^2 + 2 * (oy-yc) * dy * t + dy^2 * t^2 + - // (oz - zc)^2 + 2 * (oz-zc) * dz * t + dz^2 * t^2 = r^2 - // (dx^2 + dy^2 + dz^2) * t^2 + 2 * ((ox-xc)dx + (oy-yc)dy + (oz-zc)dz) t + - // (ox-xc)^2 + (oy-yc)^2 + (oz-zc)^2 - r^2 = 0 - // or, t = (-b +/- sqrt( b^2 - 4ac)) / 2a - // a = DotProduct( vecRayDelta, vecRayDelta ); - // b = 2 * DotProduct( vecRayOrigin - vecCenter, vecRayDelta ) - // c = DotProduct(vecRayOrigin - vecCenter, vecRayOrigin - vecCenter) - flRadius * flRadius; - // Valid solutions are possible only if b^2 - 4ac >= 0 - // Therefore, compute that value + see if we got it -} - - -//----------------------------------------------------------------------------- -// -// IntersectInfiniteRayWithSphere -// -// Returns whether or not there was an intersection. -// Returns the two intersection points -// -//----------------------------------------------------------------------------- -bool IntersectInfiniteRayWithSphere( const Vector &vecRayOrigin, const Vector &vecRayDelta, - const Vector &vecSphereCenter, float flRadius, float *pT1, float *pT2 ) -{ - // Solve using the ray equation + the sphere equation - // P = o + dt - // (x - xc)^2 + (y - yc)^2 + (z - zc)^2 = r^2 - // (ox + dx * t - xc)^2 + (oy + dy * t - yc)^2 + (oz + dz * t - zc)^2 = r^2 - // (ox - xc)^2 + 2 * (ox-xc) * dx * t + dx^2 * t^2 + - // (oy - yc)^2 + 2 * (oy-yc) * dy * t + dy^2 * t^2 + - // (oz - zc)^2 + 2 * (oz-zc) * dz * t + dz^2 * t^2 = r^2 - // (dx^2 + dy^2 + dz^2) * t^2 + 2 * ((ox-xc)dx + (oy-yc)dy + (oz-zc)dz) t + - // (ox-xc)^2 + (oy-yc)^2 + (oz-zc)^2 - r^2 = 0 - // or, t = (-b +/- sqrt( b^2 - 4ac)) / 2a - // a = DotProduct( vecRayDelta, vecRayDelta ); - // b = 2 * DotProduct( vecRayOrigin - vecCenter, vecRayDelta ) - // c = DotProduct(vecRayOrigin - vecCenter, vecRayOrigin - vecCenter) - flRadius * flRadius; - - Vector vecSphereToRay; - VectorSubtract( vecRayOrigin, vecSphereCenter, vecSphereToRay ); - - float a = DotProduct( vecRayDelta, vecRayDelta ); - - // This would occur in the case of a zero-length ray - if ( a == 0.0f ) - { - *pT1 = *pT2 = 0.0f; - return vecSphereToRay.LengthSqr() <= flRadius * flRadius; - } - - float b = 2 * DotProduct( vecSphereToRay, vecRayDelta ); - float c = DotProduct( vecSphereToRay, vecSphereToRay ) - flRadius * flRadius; - float flDiscrim = b * b - 4 * a * c; - if ( flDiscrim < 0.0f ) - return false; - - flDiscrim = sqrt( flDiscrim ); - float oo2a = 0.5f / a; - *pT1 = ( - b - flDiscrim ) * oo2a; - *pT2 = ( - b + flDiscrim ) * oo2a; - return true; -} - - - -//----------------------------------------------------------------------------- -// -// IntersectRayWithSphere -// -// Returns whether or not there was an intersection. -// Returns the two intersection points, clamped to (0,1) -// -//----------------------------------------------------------------------------- -bool IntersectRayWithSphere( const Vector &vecRayOrigin, const Vector &vecRayDelta, - const Vector &vecSphereCenter, float flRadius, float *pT1, float *pT2 ) -{ - if ( !IntersectInfiniteRayWithSphere( vecRayOrigin, vecRayDelta, vecSphereCenter, flRadius, pT1, pT2 ) ) - return false; - - if (( *pT1 > 1.0f ) || ( *pT2 < 0.0f )) - return false; - - // Clamp it! - if ( *pT1 < 0.0f ) - *pT1 = 0.0f; - if ( *pT2 > 1.0f ) - *pT2 = 1.0f; - - return true; -} - - -//----------------------------------------------------------------------------- -// returns true if the point is in the box -//----------------------------------------------------------------------------- -bool IsPointInBox( const Vector& pt, const Vector& boxMin, const Vector& boxMax ) -{ - Assert( boxMin[0] <= boxMax[0] ); - Assert( boxMin[1] <= boxMax[1] ); - Assert( boxMin[2] <= boxMax[2] ); - - if ( (pt[0] > boxMax[0]) || (pt[0] < boxMin[0]) ) - return false; - if ( (pt[1] > boxMax[1]) || (pt[1] < boxMin[1]) ) - return false; - if ( (pt[2] > boxMax[2]) || (pt[2] < boxMin[2]) ) - return false; - return true; -} - - -bool IsPointInCone( const Vector &pt, const Vector &origin, const Vector &axis, float cosAngle, float length ) -{ - Vector delta = pt - origin; - float dist = VectorNormalize( delta ); - float dot = DotProduct( delta, axis ); - if ( dot < cosAngle ) - return false; - if ( dist * dot > length ) - return false; - - return true; -} - - -//----------------------------------------------------------------------------- -// returns true if there's an intersection between two boxes -//----------------------------------------------------------------------------- -bool IsBoxIntersectingBox( const Vector& boxMin1, const Vector& boxMax1, - const Vector& boxMin2, const Vector& boxMax2 ) -{ - Assert( boxMin1[0] <= boxMax1[0] ); - Assert( boxMin1[1] <= boxMax1[1] ); - Assert( boxMin1[2] <= boxMax1[2] ); - Assert( boxMin2[0] <= boxMax2[0] ); - Assert( boxMin2[1] <= boxMax2[1] ); - Assert( boxMin2[2] <= boxMax2[2] ); - - if ( (boxMin1[0] > boxMax2[0]) || (boxMax1[0] < boxMin2[0]) ) - return false; - if ( (boxMin1[1] > boxMax2[1]) || (boxMax1[1] < boxMin2[1]) ) - return false; - if ( (boxMin1[2] > boxMax2[2]) || (boxMax1[2] < boxMin2[2]) ) - return false; - return true; -} - -bool IsBoxIntersectingBoxExtents( const Vector& boxCenter1, const Vector& boxHalfDiagonal1, - const Vector& boxCenter2, const Vector& boxHalfDiagonal2 ) -{ - Vector vecDelta, vecSize; - VectorSubtract( boxCenter1, boxCenter2, vecDelta ); - VectorAdd( boxHalfDiagonal1, boxHalfDiagonal2, vecSize ); - return ( FloatMakePositive( vecDelta.x ) <= vecSize.x ) && - ( FloatMakePositive( vecDelta.y ) <= vecSize.y ) && - ( FloatMakePositive( vecDelta.z ) <= vecSize.z ); -} - - -//----------------------------------------------------------------------------- -// -// IsOBBIntersectingOBB -// -// returns true if there's an intersection between two OBBs -// -//----------------------------------------------------------------------------- -bool IsOBBIntersectingOBB( const Vector &vecOrigin1, const QAngle &vecAngles1, const Vector& boxMin1, const Vector& boxMax1, - const Vector &vecOrigin2, const QAngle &vecAngles2, const Vector& boxMin2, const Vector& boxMax2, float flTolerance ) -{ - // FIXME: Simple case AABB check doesn't work because the min and max extents are not oriented based on the angle - // this fast check would only be good for cubes. - /*if ( vecAngles1 == vecAngles2 ) - { - const Vector &vecDelta = vecOrigin2 - vecOrigin1; - Vector vecOtherMins, vecOtherMaxs; - VectorAdd( boxMin2, vecDelta, vecOtherMins ); - VectorAdd( boxMax2, vecDelta, vecOtherMaxs ); - return IsBoxIntersectingBox( boxMin1, boxMax1, vecOtherMins, vecOtherMaxs ); - }*/ - - // OBB test... - cplane_t plane; - bool bFoundPlane = ComputeSeparatingPlane( vecOrigin1, vecAngles1, boxMin1, boxMax1, - vecOrigin2, vecAngles2, boxMin2, boxMax2, flTolerance, &plane ); - return (bFoundPlane == false); -} - -//----------------------------------------------------------------------------- -// returns true if there's an intersection between box and ray -//----------------------------------------------------------------------------- -bool FASTCALL IsBoxIntersectingRay( const Vector& boxMin, const Vector& boxMax, - const Vector& origin, const Vector& delta, float flTolerance ) -{ - Assert( boxMin[0] <= boxMax[0] ); - Assert( boxMin[1] <= boxMax[1] ); - Assert( boxMin[2] <= boxMax[2] ); - - // FIXME: Surely there's a faster way - float tmin = -FLT_MAX; - float tmax = FLT_MAX; - - for (int i = 0; i < 3; ++i) - { - // Parallel case... - if (FloatMakePositive(delta[i]) < 1e-8) - { - // Check that origin is in the box - // if not, then it doesn't intersect.. - if ( (origin[i] < boxMin[i] - flTolerance) || (origin[i] > boxMax[i] + flTolerance) ) - return false; - - continue; - } - - // non-parallel case - // Find the t's corresponding to the entry and exit of - // the ray along x, y, and z. The find the furthest entry - // point, and the closest exit point. Once that is done, - // we know we don't collide if the closest exit point - // is behind the starting location. We also don't collide if - // the closest exit point is in front of the furthest entry point - - float invDelta = 1.0f / delta[i]; - float t1 = (boxMin[i] - flTolerance - origin[i]) * invDelta; - float t2 = (boxMax[i] + flTolerance - origin[i]) * invDelta; - if (t1 > t2) - { - float temp = t1; - t1 = t2; - t2 = temp; - } - if (t1 > tmin) - tmin = t1; - if (t2 < tmax) - tmax = t2; - if (tmin > tmax) - return false; - if (tmax < 0) - return false; - if (tmin > 1) - return false; - } - - return true; -} - -//----------------------------------------------------------------------------- -// returns true if there's an intersection between box and ray -//----------------------------------------------------------------------------- -bool FASTCALL IsBoxIntersectingRay( const Vector& boxMin, const Vector& boxMax, - const Vector& origin, const Vector& delta, - const Vector& invDelta, float flTolerance ) -{ - Assert( boxMin[0] <= boxMax[0] ); - Assert( boxMin[1] <= boxMax[1] ); - Assert( boxMin[2] <= boxMax[2] ); - - // FIXME: Surely there's a faster way - float tmin = -FLT_MAX; - float tmax = FLT_MAX; - - for ( int i = 0; i < 3; ++i ) - { - // Parallel case... - if ( FloatMakePositive( delta[i] ) < 1e-8 ) - { - // Check that origin is in the box, if not, then it doesn't intersect.. - if ( ( origin[i] < boxMin[i] - flTolerance ) || ( origin[i] > boxMax[i] + flTolerance ) ) - return false; - - continue; - } - - // Non-parallel case - // Find the t's corresponding to the entry and exit of - // the ray along x, y, and z. The find the furthest entry - // point, and the closest exit point. Once that is done, - // we know we don't collide if the closest exit point - // is behind the starting location. We also don't collide if - // the closest exit point is in front of the furthest entry point - float t1 = ( boxMin[i] - flTolerance - origin[i] ) * invDelta[i]; - float t2 = ( boxMax[i] + flTolerance - origin[i] ) * invDelta[i]; - if ( t1 > t2 ) - { - float temp = t1; - t1 = t2; - t2 = temp; - } - - if (t1 > tmin) - tmin = t1; - - if (t2 < tmax) - tmax = t2; - - if (tmin > tmax) - return false; - - if (tmax < 0) - return false; - - if (tmin > 1) - return false; - } - - return true; -} - -//----------------------------------------------------------------------------- -// Intersects a ray with a aabb, return true if they intersect -//----------------------------------------------------------------------------- -bool FASTCALL IsBoxIntersectingRay( const Vector& vecBoxMin, const Vector& vecBoxMax, const Ray_t& ray, float flTolerance ) -{ - if ( !ray.m_IsSwept ) - { - Vector rayMins, rayMaxs; - VectorSubtract( ray.m_Start, ray.m_Extents, rayMins ); - VectorAdd( ray.m_Start, ray.m_Extents, rayMaxs ); - if ( flTolerance != 0.0f ) - { - rayMins.x -= flTolerance; rayMins.y -= flTolerance; rayMins.z -= flTolerance; - rayMaxs.x += flTolerance; rayMaxs.y += flTolerance; rayMaxs.z += flTolerance; - } - return IsBoxIntersectingBox( vecBoxMin, vecBoxMax, rayMins, rayMaxs ); - } - - Vector vecExpandedBoxMin, vecExpandedBoxMax; - VectorSubtract( vecBoxMin, ray.m_Extents, vecExpandedBoxMin ); - VectorAdd( vecBoxMax, ray.m_Extents, vecExpandedBoxMax ); - return IsBoxIntersectingRay( vecExpandedBoxMin, vecExpandedBoxMax, ray.m_Start, ray.m_Delta, flTolerance ); -} - - -//----------------------------------------------------------------------------- -// Intersects a ray with a ray, return true if they intersect -// t, s = parameters of closest approach (if not intersecting!) -//----------------------------------------------------------------------------- -bool IntersectRayWithRay( const Ray_t &ray0, const Ray_t &ray1, float &t, float &s ) -{ - Assert( ray0.m_IsRay && ray1.m_IsRay ); - - // - // r0 = p0 + v0t - // r1 = p1 + v1s - // - // intersection : r0 = r1 :: p0 + v0t = p1 + v1s - // NOTE: v(0,1) are unit direction vectors - // - // subtract p0 from both sides and cross with v1 (NOTE: v1 x v1 = 0) - // (v0 x v1)t = ((p1 - p0 ) x v1) - // - // dotting with (v0 x v1) and dividing by |v0 x v1|^2 - // t = Det | (p1 - p0) , v1 , (v0 x v1) | / |v0 x v1|^2 - // s = Det | (p1 - p0) , v0 , (v0 x v1) | / |v0 x v1|^2 - // - // Det | A B C | = -( A x C ) dot B or -( C x B ) dot A - // - // NOTE: if |v0 x v1|^2 = 0, then the lines are parallel - // - Vector v0( ray0.m_Delta ); - Vector v1( ray1.m_Delta ); - VectorNormalize( v0 ); - VectorNormalize( v1 ); - - Vector v0xv1 = v0.Cross( v1 ); - float length = v0xv1.Length(); - if( length == 0.0f ) - { - t = 0; s = 0; - return false; // parallel - } - - Vector p1p0 = ray1.m_Start - ray0.m_Start; - - Vector AxC = p1p0.Cross( v0xv1 ); - AxC.Negate(); - float detT = AxC.Dot( v1 ); - - AxC = p1p0.Cross( v0xv1 ); - AxC.Negate(); - float detS = AxC.Dot( v0 ); - - t = detT / ( length * length ); - s = detS / ( length * length ); - - // intersection???? - Vector i0, i1; - i0 = v0 * t; - i1 = v1 * s; - i0 += ray0.m_Start; - i1 += ray1.m_Start; - if( i0.x == i1.x && i0.y == i1.y && i0.z == i1.z ) - return true; - - return false; -} - - -//----------------------------------------------------------------------------- -// Intersects a ray with a plane, returns distance t along ray. -//----------------------------------------------------------------------------- -float IntersectRayWithPlane( const Ray_t& ray, const cplane_t& plane ) -{ - float denom = DotProduct( ray.m_Delta, plane.normal ); - if (denom == 0.0f) - return 0.0f; - - denom = 1.0f / denom; - return (plane.dist - DotProduct( ray.m_Start, plane.normal )) * denom; -} - -float IntersectRayWithPlane( const Vector& org, const Vector& dir, const cplane_t& plane ) -{ - float denom = DotProduct( dir, plane.normal ); - if (denom == 0.0f) - return 0.0f; - - denom = 1.0f / denom; - return (plane.dist - DotProduct( org, plane.normal )) * denom; -} - -float IntersectRayWithPlane( const Vector& org, const Vector& dir, const Vector& normal, float dist ) -{ - float denom = DotProduct( dir, normal ); - if (denom == 0.0f) - return 0.0f; - - denom = 1.0f / denom; - return (dist - DotProduct( org, normal )) * denom; -} - -float IntersectRayWithAAPlane( const Vector& vecStart, const Vector& vecEnd, int nAxis, float flSign, float flDist ) -{ - float denom = flSign * (vecEnd[nAxis] - vecStart[nAxis]); - if (denom == 0.0f) - return 0.0f; - - denom = 1.0f / denom; - return (flDist - flSign * vecStart[nAxis]) * denom; -} - - -//----------------------------------------------------------------------------- -// Intersects a ray against a box -//----------------------------------------------------------------------------- -bool IntersectRayWithBox( const Vector &vecRayStart, const Vector &vecRayDelta, - const Vector &boxMins, const Vector &boxMaxs, float flTolerance, BoxTraceInfo_t *pTrace ) -{ - int i; - float d1, d2; - float f; - - pTrace->t1 = -1.0f; - pTrace->t2 = 1.0f; - pTrace->hitside = -1; - - // UNDONE: This makes this code a little messy - pTrace->startsolid = true; - - for ( i = 0; i < 6; ++i ) - { - if ( i >= 3 ) - { - d1 = vecRayStart[i-3] - boxMaxs[i-3]; - d2 = d1 + vecRayDelta[i-3]; - } - else - { - d1 = -vecRayStart[i] + boxMins[i]; - d2 = d1 - vecRayDelta[i]; - } - - // if completely in front of face, no intersection - if (d1 > 0 && d2 > 0) - { - // UNDONE: Have to revert this in case it's still set - // UNDONE: Refactor to have only 2 return points (true/false) from this function - pTrace->startsolid = false; - return false; - } - - // completely inside, check next face - if (d1 <= 0 && d2 <= 0) - continue; - - if (d1 > 0) - { - pTrace->startsolid = false; - } - - // crosses face - if (d1 > d2) - { - f = d1 - flTolerance; - if ( f < 0 ) - { - f = 0; - } - f = f / (d1-d2); - if (f > pTrace->t1) - { - pTrace->t1 = f; - pTrace->hitside = i; - } - } - else - { - // leave - f = (d1 + flTolerance) / (d1-d2); - if (f < pTrace->t2) - { - pTrace->t2 = f; - } - } - } - - return pTrace->startsolid || (pTrace->t1 < pTrace->t2 && pTrace->t1 >= 0.0f); -} - - -//----------------------------------------------------------------------------- -// Intersects a ray against a box -//----------------------------------------------------------------------------- -bool IntersectRayWithBox( const Vector &vecRayStart, const Vector &vecRayDelta, - const Vector &boxMins, const Vector &boxMaxs, float flTolerance, CBaseTrace *pTrace ) -{ - Collision_ClearTrace( vecRayStart, vecRayDelta, pTrace ); - - BoxTraceInfo_t trace; - - if ( IntersectRayWithBox( vecRayStart, vecRayDelta, boxMins, boxMaxs, flTolerance, &trace ) ) - { - pTrace->startsolid = trace.startsolid; - if (trace.t1 < trace.t2 && trace.t1 >= 0.0f) - { - pTrace->fraction = trace.t1; - VectorMA( pTrace->startpos, trace.t1, vecRayDelta, pTrace->endpos ); - pTrace->contents = CONTENTS_SOLID; - pTrace->plane.normal = vec3_origin; - if ( trace.hitside >= 3 ) - { - trace.hitside -= 3; - pTrace->plane.dist = boxMaxs[trace.hitside]; - pTrace->plane.normal[trace.hitside] = 1.0f; - pTrace->plane.type = trace.hitside; - } - else - { - pTrace->plane.dist = -boxMins[trace.hitside]; - pTrace->plane.normal[trace.hitside] = -1.0f; - pTrace->plane.type = trace.hitside; - } - return true; - } - - if ( pTrace->startsolid ) - { - pTrace->allsolid = (trace.t2 <= 0.0f) || (trace.t2 >= 1.0f); - pTrace->fraction = 0; - pTrace->endpos = pTrace->startpos; - pTrace->contents = CONTENTS_SOLID; - pTrace->plane.dist = pTrace->startpos[0]; - pTrace->plane.normal.Init( 1.0f, 0.0f, 0.0f ); - pTrace->plane.type = 0; - return true; - } - } - - return false; -} - - -//----------------------------------------------------------------------------- -// Intersects a ray against a box -//----------------------------------------------------------------------------- -bool IntersectRayWithBox( const Ray_t &ray, const Vector &boxMins, const Vector &boxMaxs, - float flTolerance, CBaseTrace *pTrace ) -{ - Vector vecExpandedMins = boxMins; - Vector vecExpandedMaxs = boxMaxs; - - if ( !ray.m_IsRay ) - { - vecExpandedMins -= ray.m_Extents; - vecExpandedMaxs += ray.m_Extents; - } - - bool bIntersects = IntersectRayWithBox( ray.m_Start, ray.m_Delta, vecExpandedMins, vecExpandedMaxs, flTolerance, pTrace ); - - if ( !ray.m_IsRay ) - { - pTrace->startpos += ray.m_StartOffset; - pTrace->endpos += ray.m_StartOffset; - } - - return bIntersects; -} - - -//----------------------------------------------------------------------------- -// Intersects a ray against an OBB, returns t1 and t2 -//----------------------------------------------------------------------------- -bool IntersectRayWithOBB( const Vector &vecRayStart, const Vector &vecRayDelta, - const matrix3x4_t &matOBBToWorld, const Vector &vecOBBMins, const Vector &vecOBBMaxs, - float flTolerance, BoxTraceInfo_t *pTrace ) -{ - // FIXME: Two transforms is pretty expensive. Should we optimize this? - Vector start, delta; - VectorITransform( vecRayStart, matOBBToWorld, start ); - VectorIRotate( vecRayDelta, matOBBToWorld, delta ); - - return IntersectRayWithBox( start, delta, vecOBBMins, vecOBBMaxs, flTolerance, pTrace ); -} - - - -//----------------------------------------------------------------------------- -// Intersects a ray against an OBB -//----------------------------------------------------------------------------- -bool IntersectRayWithOBB( const Vector &vecRayStart, const Vector &vecRayDelta, - const matrix3x4_t &matOBBToWorld, const Vector &vecOBBMins, const Vector &vecOBBMaxs, - float flTolerance, CBaseTrace *pTrace ) -{ - Collision_ClearTrace( vecRayStart, vecRayDelta, pTrace ); - - // FIXME: Make it work with tolerance - Assert( flTolerance == 0.0f ); - - // OPTIMIZE: Store this in the box instead of computing it here - // compute center in local space - Vector vecBoxExtents = (vecOBBMins + vecOBBMaxs) * 0.5; - Vector vecBoxCenter; - - // transform to world space - VectorTransform( vecBoxExtents, matOBBToWorld, vecBoxCenter ); - - // calc extents from local center - vecBoxExtents = vecOBBMaxs - vecBoxExtents; - - // OPTIMIZE: This is optimized for world space. If the transform is fast enough, it may make more - // sense to just xform and call UTIL_ClipToBox() instead. MEASURE THIS. - - // save the extents of the ray along - Vector extent, uextent; - Vector segmentCenter = vecRayStart + vecRayDelta - vecBoxCenter; - - extent.Init(); - - // check box axes for separation - for ( int j = 0; j < 3; j++ ) - { - extent[j] = vecRayDelta.x * matOBBToWorld[0][j] + vecRayDelta.y * matOBBToWorld[1][j] + vecRayDelta.z * matOBBToWorld[2][j]; - uextent[j] = fabsf(extent[j]); - float coord = segmentCenter.x * matOBBToWorld[0][j] + segmentCenter.y * matOBBToWorld[1][j] + segmentCenter.z * matOBBToWorld[2][j]; - coord = fabsf(coord); - - if ( coord > (vecBoxExtents[j] + uextent[j]) ) - return false; - } - - // now check cross axes for separation - float tmp, cextent; - Vector cross = vecRayDelta.Cross( segmentCenter ); - cextent = cross.x * matOBBToWorld[0][0] + cross.y * matOBBToWorld[1][0] + cross.z * matOBBToWorld[2][0]; - cextent = fabsf(cextent); - tmp = vecBoxExtents[1]*uextent[2] + vecBoxExtents[2]*uextent[1]; - if ( cextent > tmp ) - return false; - - cextent = cross.x * matOBBToWorld[0][1] + cross.y * matOBBToWorld[1][1] + cross.z * matOBBToWorld[2][1]; - cextent = fabsf(cextent); - tmp = vecBoxExtents[0]*uextent[2] + vecBoxExtents[2]*uextent[0]; - if ( cextent > tmp ) - return false; - - cextent = cross.x * matOBBToWorld[0][2] + cross.y * matOBBToWorld[1][2] + cross.z * matOBBToWorld[2][2]; - cextent = fabsf(cextent); - tmp = vecBoxExtents[0]*uextent[1] + vecBoxExtents[1]*uextent[0]; - if ( cextent > tmp ) - return false; - - // !!! We hit this box !!! compute intersection point and return - // Compute ray start in bone space - Vector start; - VectorITransform( vecRayStart, matOBBToWorld, start ); - - // extent is ray.m_Delta in bone space, recompute delta in bone space - extent *= 2.0f; - - // delta was prescaled by the current t, so no need to see if this intersection - // is closer - trace_t boxTrace; - if ( !IntersectRayWithBox( start, extent, vecOBBMins, vecOBBMaxs, flTolerance, pTrace ) ) - return false; - - // Fix up the plane information - float flSign = pTrace->plane.normal[ pTrace->plane.type ]; - pTrace->plane.normal[0] = flSign * matOBBToWorld[0][pTrace->plane.type]; - pTrace->plane.normal[1] = flSign * matOBBToWorld[1][pTrace->plane.type]; - pTrace->plane.normal[2] = flSign * matOBBToWorld[2][pTrace->plane.type]; - pTrace->plane.dist = DotProduct( pTrace->endpos, pTrace->plane.normal ); - pTrace->plane.type = 3; - return true; -} - - -//----------------------------------------------------------------------------- -// Intersects a ray against an OBB -//----------------------------------------------------------------------------- -bool IntersectRayWithOBB( const Vector &vecRayOrigin, const Vector &vecRayDelta, - const Vector &vecBoxOrigin, const QAngle &angBoxRotation, - const Vector &vecOBBMins, const Vector &vecOBBMaxs, float flTolerance, CBaseTrace *pTrace ) -{ - if (angBoxRotation == vec3_angle) - { - Vector vecAbsMins, vecAbsMaxs; - VectorAdd( vecBoxOrigin, vecOBBMins, vecAbsMins ); - VectorAdd( vecBoxOrigin, vecOBBMaxs, vecAbsMaxs ); - return IntersectRayWithBox( vecRayOrigin, vecRayDelta, vecAbsMins, vecAbsMaxs, flTolerance, pTrace ); - } - - matrix3x4_t obbToWorld; - AngleMatrix( angBoxRotation, vecBoxOrigin, obbToWorld ); - return IntersectRayWithOBB( vecRayOrigin, vecRayDelta, obbToWorld, vecOBBMins, vecOBBMaxs, flTolerance, pTrace ); -} - - -//----------------------------------------------------------------------------- -// Box support map -//----------------------------------------------------------------------------- -inline void ComputeSupportMap( const Vector &vecDirection, const Vector &vecBoxMins, - const Vector &vecBoxMaxs, float pDist[2] ) -{ - int nIndex = (vecDirection.x > 0.0f); - pDist[nIndex] = vecBoxMaxs.x * vecDirection.x; - pDist[1 - nIndex] = vecBoxMins.x * vecDirection.x; - - nIndex = (vecDirection.y > 0.0f); - pDist[nIndex] += vecBoxMaxs.y * vecDirection.y; - pDist[1 - nIndex] += vecBoxMins.y * vecDirection.y; - - nIndex = (vecDirection.z > 0.0f); - pDist[nIndex] += vecBoxMaxs.z * vecDirection.z; - pDist[1 - nIndex] += vecBoxMins.z * vecDirection.z; -} - -inline void ComputeSupportMap( const Vector &vecDirection, int i1, int i2, - const Vector &vecBoxMins, const Vector &vecBoxMaxs, float pDist[2] ) -{ - int nIndex = (vecDirection[i1] > 0.0f); - pDist[nIndex] = vecBoxMaxs[i1] * vecDirection[i1]; - pDist[1 - nIndex] = vecBoxMins[i1] * vecDirection[i1]; - - nIndex = (vecDirection[i2] > 0.0f); - pDist[nIndex] += vecBoxMaxs[i2] * vecDirection[i2]; - pDist[1 - nIndex] += vecBoxMins[i2] * vecDirection[i2]; -} - -//----------------------------------------------------------------------------- -// Intersects a ray against an OBB -//----------------------------------------------------------------------------- -static int s_ExtIndices[3][2] = -{ - { 2, 1 }, - { 0, 2 }, - { 0, 1 }, -}; - -static int s_MatIndices[3][2] = -{ - { 1, 2 }, - { 2, 0 }, - { 1, 0 }, -}; - -bool IntersectRayWithOBB( const Ray_t &ray, const matrix3x4_t &matOBBToWorld, - const Vector &vecOBBMins, const Vector &vecOBBMaxs, float flTolerance, CBaseTrace *pTrace ) -{ - if ( ray.m_IsRay ) - { - return IntersectRayWithOBB( ray.m_Start, ray.m_Delta, matOBBToWorld, - vecOBBMins, vecOBBMaxs, flTolerance, pTrace ); - } - - Collision_ClearTrace( ray.m_Start + ray.m_StartOffset, ray.m_Delta, pTrace ); - - // Compute a bounding sphere around the bloated OBB - Vector vecOBBCenter; - VectorAdd( vecOBBMins, vecOBBMaxs, vecOBBCenter ); - vecOBBCenter *= 0.5f; - vecOBBCenter.x += matOBBToWorld[0][3]; - vecOBBCenter.y += matOBBToWorld[1][3]; - vecOBBCenter.z += matOBBToWorld[2][3]; - - Vector vecOBBHalfDiagonal; - VectorSubtract( vecOBBMaxs, vecOBBMins, vecOBBHalfDiagonal ); - vecOBBHalfDiagonal *= 0.5f; - - float flRadius = vecOBBHalfDiagonal.Length() + ray.m_Extents.Length(); - if ( !IsRayIntersectingSphere( ray.m_Start, ray.m_Delta, vecOBBCenter, flRadius, flTolerance ) ) - return false; - - // Ok, we passed the trivial reject, so lets do the dirty deed. - // Basically we're going to do the GJK thing explicitly. We'll shrink the ray down - // to a point, and bloat the OBB by the ray's extents. This will generate facet - // planes which are perpendicular to all of the separating axes typically seen in - // a standard seperating axis implementation. - - // We're going to create a number of planes through various vertices in the OBB - // which represent all of the separating planes. Then we're going to bloat the planes - // by the ray extents. - - // We're going to do all work in OBB-space because it's easier to do the - // support-map in this case - - // First, transform the ray into the space of the OBB - Vector vecLocalRayOrigin, vecLocalRayDirection; - VectorITransform( ray.m_Start, matOBBToWorld, vecLocalRayOrigin ); - VectorIRotate( ray.m_Delta, matOBBToWorld, vecLocalRayDirection ); - - // Next compute all separating planes - Vector pPlaneNormal[15]; - float ppPlaneDist[15][2]; - - int i; - for ( i = 0; i < 3; ++i ) - { - // Each plane needs to be bloated an amount = to the abs dot product of - // the ray extents with the plane normal - // For the OBB planes, do it in world space; - // and use the direction of the OBB (the ith column of matOBBToWorld) in world space vs extents - pPlaneNormal[i].Init( ); - pPlaneNormal[i][i] = 1.0f; - - float flExtentDotNormal = - FloatMakePositive( matOBBToWorld[0][i] * ray.m_Extents.x ) + - FloatMakePositive( matOBBToWorld[1][i] * ray.m_Extents.y ) + - FloatMakePositive( matOBBToWorld[2][i] * ray.m_Extents.z ); - - ppPlaneDist[i][0] = vecOBBMins[i] - flExtentDotNormal; - ppPlaneDist[i][1] = vecOBBMaxs[i] + flExtentDotNormal; - - // For the ray-extents planes, they are bloated by the extents - // Use the support map to determine which - VectorCopy( matOBBToWorld[i], pPlaneNormal[i+3].Base() ); - ComputeSupportMap( pPlaneNormal[i+3], vecOBBMins, vecOBBMaxs, ppPlaneDist[i+3] ); - ppPlaneDist[i+3][0] -= ray.m_Extents[i]; - ppPlaneDist[i+3][1] += ray.m_Extents[i]; - - // Now the edge cases... (take the cross product of x,y,z axis w/ ray extent axes - // given by the rows of the obb to world matrix. - // Compute the ray extent bloat in world space because it's easier... - - // These are necessary to compute the world-space versions of - // the edges so we can compute the extent dot products - float flRayExtent0 = ray.m_Extents[s_ExtIndices[i][0]]; - float flRayExtent1 = ray.m_Extents[s_ExtIndices[i][1]]; - const float *pMatRow0 = matOBBToWorld[s_MatIndices[i][0]]; - const float *pMatRow1 = matOBBToWorld[s_MatIndices[i][1]]; - - // x axis of the OBB + world ith axis - pPlaneNormal[i+6].Init( 0.0f, -matOBBToWorld[i][2], matOBBToWorld[i][1] ); - ComputeSupportMap( pPlaneNormal[i+6], 1, 2, vecOBBMins, vecOBBMaxs, ppPlaneDist[i+6] ); - flExtentDotNormal = - FloatMakePositive( pMatRow0[0] ) * flRayExtent0 + - FloatMakePositive( pMatRow1[0] ) * flRayExtent1; - ppPlaneDist[i+6][0] -= flExtentDotNormal; - ppPlaneDist[i+6][1] += flExtentDotNormal; - - // y axis of the OBB + world ith axis - pPlaneNormal[i+9].Init( matOBBToWorld[i][2], 0.0f, -matOBBToWorld[i][0] ); - ComputeSupportMap( pPlaneNormal[i+9], 0, 2, vecOBBMins, vecOBBMaxs, ppPlaneDist[i+9] ); - flExtentDotNormal = - FloatMakePositive( pMatRow0[1] ) * flRayExtent0 + - FloatMakePositive( pMatRow1[1] ) * flRayExtent1; - ppPlaneDist[i+9][0] -= flExtentDotNormal; - ppPlaneDist[i+9][1] += flExtentDotNormal; - - // z axis of the OBB + world ith axis - pPlaneNormal[i+12].Init( -matOBBToWorld[i][1], matOBBToWorld[i][0], 0.0f ); - ComputeSupportMap( pPlaneNormal[i+12], 0, 1, vecOBBMins, vecOBBMaxs, ppPlaneDist[i+12] ); - flExtentDotNormal = - FloatMakePositive( pMatRow0[2] ) * flRayExtent0 + - FloatMakePositive( pMatRow1[2] ) * flRayExtent1; - ppPlaneDist[i+12][0] -= flExtentDotNormal; - ppPlaneDist[i+12][1] += flExtentDotNormal; - } - - float enterfrac, leavefrac; - float d1[2], d2[2]; - float f; - - int hitplane = -1; - int hitside = -1; - enterfrac = -1.0f; - leavefrac = 1.0f; - - pTrace->startsolid = true; - - Vector vecLocalRayEnd; - VectorAdd( vecLocalRayOrigin, vecLocalRayDirection, vecLocalRayEnd ); - - for ( i = 0; i < 15; ++i ) - { - // FIXME: Not particularly optimal since there's a lot of 0's in the plane normals - float flStartDot = DotProduct( pPlaneNormal[i], vecLocalRayOrigin ); - float flEndDot = DotProduct( pPlaneNormal[i], vecLocalRayEnd ); - - // NOTE: Negative here is because the plane normal + dist - // are defined in negative terms for the far plane (plane dist index 0) - d1[0] = -(flStartDot - ppPlaneDist[i][0]); - d2[0] = -(flEndDot - ppPlaneDist[i][0]); - - d1[1] = flStartDot - ppPlaneDist[i][1]; - d2[1] = flEndDot - ppPlaneDist[i][1]; - - int j; - for ( j = 0; j < 2; ++j ) - { - // if completely in front near plane or behind far plane no intersection - if (d1[j] > 0 && d2[j] > 0) - return false; - - // completely inside, check next plane set - if (d1[j] <= 0 && d2[j] <= 0) - continue; - - if (d1[j] > 0) - { - pTrace->startsolid = false; - } - - // crosses face - float flDenom = 1.0f / (d1[j] - d2[j]); - if (d1[j] > d2[j]) - { - f = d1[j] - flTolerance; - if ( f < 0 ) - { - f = 0; - } - f *= flDenom; - if (f > enterfrac) - { - enterfrac = f; - hitplane = i; - hitside = j; - } - } - else - { - // leave - f = (d1[j] + flTolerance) * flDenom; - if (f < leavefrac) - { - leavefrac = f; - } - } - } - } - - if (enterfrac < leavefrac && enterfrac >= 0.0f) - { - pTrace->fraction = enterfrac; - VectorMA( pTrace->startpos, enterfrac, ray.m_Delta, pTrace->endpos ); - pTrace->contents = CONTENTS_SOLID; - - // Need to transform the plane into world space... - cplane_t temp; - temp.normal = pPlaneNormal[hitplane]; - temp.dist = ppPlaneDist[hitplane][hitside]; - if (hitside == 0) - { - temp.normal *= -1.0f; - temp.dist *= -1.0f; - } - temp.type = 3; - - MatrixITransformPlane( matOBBToWorld, temp, pTrace->plane ); - return true; - } - - if ( pTrace->startsolid ) - { - pTrace->allsolid = (leavefrac <= 0.0f) || (leavefrac >= 1.0f); - pTrace->fraction = 0; - pTrace->endpos = pTrace->startpos; - pTrace->contents = CONTENTS_SOLID; - pTrace->plane.dist = pTrace->startpos[0]; - pTrace->plane.normal.Init( 1.0f, 0.0f, 0.0f ); - pTrace->plane.type = 0; - return true; - } - - return false; -} - - -//----------------------------------------------------------------------------- -// Intersects a ray against an OBB -//----------------------------------------------------------------------------- -bool IntersectRayWithOBB( const Ray_t &ray, const Vector &vecBoxOrigin, const QAngle &angBoxRotation, - const Vector &vecOBBMins, const Vector &vecOBBMaxs, float flTolerance, CBaseTrace *pTrace ) -{ - if ( angBoxRotation == vec3_angle ) - { - Vector vecWorldMins, vecWorldMaxs; - VectorAdd( vecBoxOrigin, vecOBBMins, vecWorldMins ); - VectorAdd( vecBoxOrigin, vecOBBMaxs, vecWorldMaxs ); - return IntersectRayWithBox( ray, vecWorldMins, vecWorldMaxs, flTolerance, pTrace ); - } - - if ( ray.m_IsRay ) - { - return IntersectRayWithOBB( ray.m_Start, ray.m_Delta, vecBoxOrigin, angBoxRotation, vecOBBMins, vecOBBMaxs, flTolerance, pTrace ); - } - - matrix3x4_t matOBBToWorld; - AngleMatrix( angBoxRotation, vecBoxOrigin, matOBBToWorld ); - return IntersectRayWithOBB( ray, matOBBToWorld, vecOBBMins, vecOBBMaxs, flTolerance, pTrace ); -} - - -//----------------------------------------------------------------------------- -// -//----------------------------------------------------------------------------- -void GetNonMajorAxes( const Vector &vNormal, Vector2D &axes ) -{ - axes[0] = 0; - axes[1] = 1; - - if( FloatMakePositive( vNormal.x ) > FloatMakePositive( vNormal.y ) ) - { - if( FloatMakePositive( vNormal.x ) > FloatMakePositive( vNormal.z ) ) - { - axes[0] = 1; - axes[1] = 2; - } - } - else - { - if( FloatMakePositive( vNormal.y ) > FloatMakePositive( vNormal.z ) ) - { - axes[0] = 0; - axes[1] = 2; - } - } -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -QuadBarycentricRetval_t QuadWithParallelEdges( const Vector &vecOrigin, - const Vector &vecU, float lengthU, const Vector &vecV, float lengthV, - const Vector &pt, Vector2D &vecUV ) -{ - Ray_t rayAxis; - Ray_t rayPt; - - // - // handle the u axis - // - rayAxis.m_Start = vecOrigin; - rayAxis.m_Delta = vecU; - rayAxis.m_IsRay = true; - - rayPt.m_Start = pt; - rayPt.m_Delta = vecV * -( lengthV * 10.0f ); - rayPt.m_IsRay = true; - - float s, t; - IntersectRayWithRay( rayAxis, rayPt, t, s ); - vecUV[0] = t / lengthU; - - // - // handle the v axis - // - rayAxis.m_Delta = vecV; - - rayPt.m_Delta = vecU * -( lengthU * 10.0f ); - - IntersectRayWithRay( rayAxis, rayPt, t, s ); - vecUV[1] = t / lengthV; - - // inside of the quad?? - if( ( vecUV[0] < 0.0f ) || ( vecUV[0] > 1.0f ) || - ( vecUV[1] < 0.0f ) || ( vecUV[1] > 1.0f ) ) - return BARY_QUADRATIC_FALSE; - - return BARY_QUADRATIC_TRUE; -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -void ResolveQuadratic( double tPlus, double tMinus, - const Vector axisU0, const Vector axisU1, - const Vector axisV0, const Vector axisV1, - const Vector axisOrigin, const Vector pt, - int projU, double &s, double &t ) -{ - // calculate the sPlus, sMinus pair(s) - double sDenomPlus = ( axisU0[projU] * ( 1 - tPlus ) ) + ( axisU1[projU] * tPlus ); - double sDenomMinus = ( axisU0[projU] * ( 1 - tMinus ) ) + ( axisU1[projU] * tMinus ); - - double sPlus = UNINIT, sMinus = UNINIT; - if( FloatMakePositive( sDenomPlus ) >= 1e-5 ) - { - sPlus = ( pt[projU] - axisOrigin[projU] - ( axisV0[projU] * tPlus ) ) / sDenomPlus; - } - - if( FloatMakePositive( sDenomMinus ) >= 1e-5 ) - { - sMinus = ( pt[projU] - axisOrigin[projU] - ( axisV0[projU] * tMinus ) ) / sDenomMinus; - } - - if( ( tPlus >= 0.0 ) && ( tPlus <= 1.0 ) && ( sPlus >= 0.0 ) && ( sPlus <= 1.0 ) ) - { - s = sPlus; - t = tPlus; - return; - } - - if( ( tMinus >= 0.0 ) && ( tMinus <= 1.0 ) && ( sMinus >= 0.0 ) && ( sMinus <= 1.0 ) ) - { - s = sMinus; - t = tMinus; - return; - } - - double s0, t0, s1, t1; - - s0 = sPlus; - t0 = tPlus; - if( s0 >= 1.0 ) { s0 -= 1.0; } - if( t0 >= 1.0 ) { t0 -= 1.0; } - - s1 = sMinus; - t1 = tMinus; - if( s1 >= 1.0 ) { s1 -= 1.0; } - if( t1 >= 1.0 ) { t1 -= 1.0; } - - s0 = FloatMakePositive( s0 ); - t0 = FloatMakePositive( t0 ); - s1 = FloatMakePositive( s1 ); - t1 = FloatMakePositive( t1 ); - - double max0, max1; - max0 = s0; - if( t0 > max0 ) { max0 = t0; } - max1 = s1; - if( t1 > max1 ) { max1 = t1; } - - if( max0 > max1 ) - { - s = sMinus; - t = tMinus; - } - else - { - s = sPlus; - t = tPlus; - } -} - - -//----------------------------------------------------------------------------- -// -//----------------------------------------------------------------------------- - -QuadBarycentricRetval_t PointInQuadToBarycentric( const Vector &v1, const Vector &v2, - const Vector &v3, const Vector &v4, const Vector &point, Vector2D &uv ) -{ -#define PIQ_TEXTURE_EPSILON 0.001 -#define PIQ_PLANE_EPSILON 0.1 -#define PIQ_DOT_EPSILON 0.99f - - // - // Think of a quad with points v1, v2, v3, v4 and u, v line segments - // u0 = v2 - v1 - // u1 = v3 - v4 - // v0 = v4 - v1 - // v1 = v3 - v2 - // - Vector axisU[2], axisV[2]; - Vector axisUNorm[2], axisVNorm[2]; - axisU[0] = axisUNorm[0] = v2 - v1; - axisU[1] = axisUNorm[1] = v3 - v4; - axisV[0] = axisVNorm[0] = v4 - v1; - axisV[1] = axisVNorm[1] = v3 - v2; - - float lengthU[2], lengthV[2]; - lengthU[0] = VectorNormalize( axisUNorm[0] ); - lengthU[1] = VectorNormalize( axisUNorm[1] ); - lengthV[0] = VectorNormalize( axisVNorm[0] ); - lengthV[1] = VectorNormalize( axisVNorm[1] ); - - // - // check for an early out - parallel opposite edges! - // NOTE: quad property if 1 set of opposite edges is parallel and equal - // in length, then the other set of edges is as well - // - if( axisUNorm[0].Dot( axisUNorm[1] ) > PIQ_DOT_EPSILON ) - { - if( FloatMakePositive( lengthU[0] - lengthU[1] ) < PIQ_PLANE_EPSILON ) - { - return QuadWithParallelEdges( v1, axisUNorm[0], lengthU[0], axisVNorm[0], lengthV[0], point, uv ); - } - } - - // - // since we are solving for s in our equations below we need to ensure that - // the v axes are non-parallel - // - bool bFlipped = false; - if( axisVNorm[0].Dot( axisVNorm[1] ) > PIQ_DOT_EPSILON ) - { - Vector tmp[2]; - tmp[0] = axisV[0]; - tmp[1] = axisV[1]; - axisV[0] = axisU[0]; - axisV[1] = axisU[1]; - axisU[0] = tmp[0]; - axisU[1] = tmp[1]; - bFlipped = true; - } - - // - // get the "projection" axes - // - Vector2D projAxes; - Vector vNormal = axisU[0].Cross( axisV[0] ); - GetNonMajorAxes( vNormal, projAxes ); - - // - // NOTE: axisU[0][projAxes[0]] < axisU[0][projAxes[1]], - // this is done to decrease error when dividing later - // - if( FloatMakePositive( axisU[0][projAxes[0]] ) < FloatMakePositive( axisU[0][projAxes[1]] ) ) - { - int tmp = projAxes[0]; - projAxes[0] = projAxes[1]; - projAxes[1] = tmp; - } - - // Here's how we got these equations: - // - // Given the points and u,v line segments above... - // - // Then: - // - // (1.0) PT = P0 + U0 * s + V * t - // - // where - // - // (1.1) V = V0 + s * (V1 - V0) - // (1.2) U = U0 + t * (U1 - U0) - // - // Therefore (from 1.1 + 1.0): - // PT - P0 = U0 * s + (V0 + s * (V1-V0)) * t - // Group s's: - // PT - P0 - t * V0 = s * (U0 + t * (V1-V0)) - // Two equations and two unknowns in x and y get you the following quadratic: - // - // solve the quadratic - // - double s = 0.0, t = 0.0; - double A, negB, C; - - A = ( axisU[0][projAxes[1]] * axisV[0][projAxes[0]] ) - - ( axisU[0][projAxes[0]] * axisV[0][projAxes[1]] ) - - ( axisU[1][projAxes[1]] * axisV[0][projAxes[0]] ) + - ( axisU[1][projAxes[0]] * axisV[0][projAxes[1]] ); - C = ( v1[projAxes[1]] * axisU[0][projAxes[0]] ) - - ( point[projAxes[1]] * axisU[0][projAxes[0]] ) - - ( v1[projAxes[0]] * axisU[0][projAxes[1]] ) + - ( point[projAxes[0]] * axisU[0][projAxes[1]] ); - negB = C - - ( v1[projAxes[1]] * axisU[1][projAxes[0]] ) + - ( point[projAxes[1]] * axisU[1][projAxes[0]] ) + - ( v1[projAxes[0]] * axisU[1][projAxes[1]] ) - - ( point[projAxes[0]] * axisU[1][projAxes[1]] ) + - ( axisU[0][projAxes[1]] * axisV[0][projAxes[0]] ) - - ( axisU[0][projAxes[0]] * axisV[0][projAxes[1]] ); - - if( ( A > -PIQ_PLANE_EPSILON ) && ( A < PIQ_PLANE_EPSILON ) ) - { - // shouldn't be here -- this should have been take care of in the "early out" -// Assert( 0 ); - - Vector vecUAvg, vecVAvg; - vecUAvg = ( axisUNorm[0] + axisUNorm[1] ) * 0.5f; - vecVAvg = ( axisVNorm[0] + axisVNorm[1] ) * 0.5f; - - float fLengthUAvg = ( lengthU[0] + lengthU[1] ) * 0.5f; - float fLengthVAvg = ( lengthV[0] + lengthV[1] ) * 0.5f; - - return QuadWithParallelEdges( v1, vecUAvg, fLengthUAvg, vecVAvg, fLengthVAvg, point, uv ); - -#if 0 - // legacy code -- kept here for completeness! - - // not a quadratic -- solve linearly - t = C / negB; - - // See (1.2) above - float ui = axisU[0][projAxes[0]] + t * ( axisU[1][projAxes[0]] - axisU[0][projAxes[0]] ); - if( FloatMakePositive( ui ) >= 1e-5 ) - { - // See (1.0) above - s = ( point[projAxes[0]] - v1[projAxes[0]] - axisV[0][projAxes[0]] * t ) / ui; - } -#endif - } - else - { - // (-b +/- sqrt( b^2 - 4ac )) / 2a - double discriminant = (negB*negB) - (4.0f * A * C); - if( discriminant < 0.0f ) - { - uv[0] = -99999.0f; - uv[1] = -99999.0f; - return BARY_QUADRATIC_NEGATIVE_DISCRIMINANT; - } - - double quad = sqrt( discriminant ); - double QPlus = ( negB + quad ) / ( 2.0f * A ); - double QMinus = ( negB - quad ) / ( 2.0f * A ); - - ResolveQuadratic( QPlus, QMinus, axisU[0], axisU[1], axisV[0], axisV[1], v1, point, projAxes[0], s, t ); - } - - if( !bFlipped ) - { - uv[0] = ( float )s; - uv[1] = ( float )t; - } - else - { - uv[0] = ( float )t; - uv[1] = ( float )s; - } - - // inside of the quad?? - if( ( uv[0] < 0.0f ) || ( uv[0] > 1.0f ) || ( uv[1] < 0.0f ) || ( uv[1] > 1.0f ) ) - return BARY_QUADRATIC_FALSE; - - return BARY_QUADRATIC_TRUE; - -#undef PIQ_TEXTURE_EPSILON -#undef PIQ_PLANE_EPSILON -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -void PointInQuadFromBarycentric( const Vector &v1, const Vector &v2, const Vector &v3, const Vector &v4, - const Vector2D &uv, Vector &point ) -{ - // - // Think of a quad with points v1, v2, v3, v4 and u, v line segments - // find the ray from v0 edge to v1 edge at v - // - Vector vPts[2]; - VectorLerp( v1, v4, uv[1], vPts[0] ); - VectorLerp( v2, v3, uv[1], vPts[1] ); - VectorLerp( vPts[0], vPts[1], uv[0], point ); -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -void TexCoordInQuadFromBarycentric( const Vector2D &v1, const Vector2D &v2, const Vector2D &v3, const Vector2D &v4, - const Vector2D &uv, Vector2D &texCoord ) -{ - // - // Think of a quad with points v1, v2, v3, v4 and u, v line segments - // find the ray from v0 edge to v1 edge at v - // - Vector2D vCoords[2]; - Vector2DLerp( v1, v4, uv[1], vCoords[0] ); - Vector2DLerp( v2, v3, uv[1], vCoords[1] ); - Vector2DLerp( vCoords[0], vCoords[1], uv[0], texCoord ); -} - - -//----------------------------------------------------------------------------- -// Compute point from barycentric specification -// Edge u goes from v0 to v1, edge v goes from v0 to v2 -//----------------------------------------------------------------------------- -void ComputePointFromBarycentric( const Vector& v0, const Vector& v1, const Vector& v2, - float u, float v, Vector& pt ) -{ - Vector edgeU, edgeV; - VectorSubtract( v1, v0, edgeU ); - VectorSubtract( v2, v0, edgeV ); - VectorMA( v0, u, edgeU, pt ); - VectorMA( pt, v, edgeV, pt ); -} - -void ComputePointFromBarycentric( const Vector2D& v0, const Vector2D& v1, const Vector2D& v2, - float u, float v, Vector2D& pt ) -{ - Vector2D edgeU, edgeV; - Vector2DSubtract( v1, v0, edgeU ); - Vector2DSubtract( v2, v0, edgeV ); - Vector2DMA( v0, u, edgeU, pt ); - Vector2DMA( pt, v, edgeV, pt ); -} - - -//----------------------------------------------------------------------------- -// Compute a matrix that has the correct orientation but which has an origin at -// the center of the bounds -//----------------------------------------------------------------------------- -static void ComputeCenterMatrix( const Vector& origin, const QAngle& angles, - const Vector& mins, const Vector& maxs, matrix3x4_t& matrix ) -{ - Vector centroid; - VectorAdd( mins, maxs, centroid ); - centroid *= 0.5f; - AngleMatrix( angles, matrix ); - - Vector worldCentroid; - VectorRotate( centroid, matrix, worldCentroid ); - worldCentroid += origin; - MatrixSetColumn( worldCentroid, 3, matrix ); -} - -static void ComputeCenterIMatrix( const Vector& origin, const QAngle& angles, - const Vector& mins, const Vector& maxs, matrix3x4_t& matrix ) -{ - Vector centroid; - VectorAdd( mins, maxs, centroid ); - centroid *= -0.5f; - AngleIMatrix( angles, matrix ); - - // For the translational component here, note that the origin in world space - // is T = R * C + O, (R = rotation matrix, C = centroid in local space, O = origin in world space) - // The IMatrix translation = - transpose(R) * T = -C - transpose(R) * 0 - Vector localOrigin; - VectorRotate( origin, matrix, localOrigin ); - centroid -= localOrigin; - MatrixSetColumn( centroid, 3, matrix ); -} - - -//----------------------------------------------------------------------------- -// Compute a matrix which is the absolute value of another -//----------------------------------------------------------------------------- -static inline void ComputeAbsMatrix( const matrix3x4_t& in, matrix3x4_t& out ) -{ - FloatBits(out[0][0]) = FloatAbsBits(in[0][0]); - FloatBits(out[0][1]) = FloatAbsBits(in[0][1]); - FloatBits(out[0][2]) = FloatAbsBits(in[0][2]); - FloatBits(out[1][0]) = FloatAbsBits(in[1][0]); - FloatBits(out[1][1]) = FloatAbsBits(in[1][1]); - FloatBits(out[1][2]) = FloatAbsBits(in[1][2]); - FloatBits(out[2][0]) = FloatAbsBits(in[2][0]); - FloatBits(out[2][1]) = FloatAbsBits(in[2][1]); - FloatBits(out[2][2]) = FloatAbsBits(in[2][2]); -} - - -//----------------------------------------------------------------------------- -// Compute a separating plane between two boxes (expensive!) -// Returns false if no separating plane exists -//----------------------------------------------------------------------------- -static bool ComputeSeparatingPlane( const matrix3x4_t &worldToBox1, const matrix3x4_t &box2ToWorld, - const Vector& box1Size, const Vector& box2Size, float tolerance, cplane_t* pPlane ) -{ - // The various separating planes can be either - // 1) A plane parallel to one of the box face planes - // 2) A plane parallel to the cross-product of an edge from each box - - // First, compute the basis of second box in the space of the first box - // NOTE: These basis place the origin at the centroid of each box! - matrix3x4_t box2ToBox1; - ConcatTransforms( worldToBox1, box2ToWorld, box2ToBox1 ); - - // We're going to be using the origin of box2 in the space of box1 alot, - // lets extract it from the matrix.... - Vector box2Origin; - MatrixGetColumn( box2ToBox1, 3, box2Origin ); - - // Next get the absolute values of these entries and store in absbox2ToBox1. - matrix3x4_t absBox2ToBox1; - ComputeAbsMatrix( box2ToBox1, absBox2ToBox1 ); - - // There are 15 tests to make. The first 3 involve trying planes parallel - // to the faces of the first box. - - // NOTE: The algorithm here involves finding the projections of the two boxes - // onto a particular line. If the projections on the line do not overlap, - // that means that there's a plane perpendicular to the line which separates - // the two boxes; and we've therefore found a separating plane. - - // The way we check for overlay is we find the projections of the two boxes - // onto the line, and add them up. We compare the sum with the projection - // of the relative center of box2 onto the same line. - - Vector tmp; - float boxProjectionSum; - float originProjection; - - // NOTE: For these guys, we're taking advantage of the fact that the ith - // row of the box2ToBox1 is the direction of the box1 (x,y,z)-axis - // transformed into the space of box2. - - // First side of box 1 - boxProjectionSum = box1Size.x + MatrixRowDotProduct( absBox2ToBox1, 0, box2Size ); - originProjection = FloatMakePositive( box2Origin.x ) + tolerance; - if ( FloatBits(originProjection) > FloatBits(boxProjectionSum) ) - { - VectorCopy( worldToBox1[0], pPlane->normal.Base() ); - return true; - } - - // Second side of box 1 - boxProjectionSum = box1Size.y + MatrixRowDotProduct( absBox2ToBox1, 1, box2Size ); - originProjection = FloatMakePositive( box2Origin.y ) + tolerance; - if ( FloatBits(originProjection) > FloatBits(boxProjectionSum) ) - { - VectorCopy( worldToBox1[1], pPlane->normal.Base() ); - return true; - } - - // Third side of box 1 - boxProjectionSum = box1Size.z + MatrixRowDotProduct( absBox2ToBox1, 2, box2Size ); - originProjection = FloatMakePositive( box2Origin.z ) + tolerance; - if ( FloatBits(originProjection) > FloatBits(boxProjectionSum) ) - { - VectorCopy( worldToBox1[2], pPlane->normal.Base() ); - return true; - } - - // The next three involve checking splitting planes parallel to the - // faces of the second box. - - // NOTE: For these guys, we're taking advantage of the fact that the 0th - // column of the box2ToBox1 is the direction of the box2 x-axis - // transformed into the space of box1. - // Here, we're determining the distance of box2's center from box1's center - // by projecting it onto a line parallel to box2's axis - - // First side of box 2 - boxProjectionSum = box2Size.x + MatrixColumnDotProduct( absBox2ToBox1, 0, box1Size ); - originProjection = FloatMakePositive( MatrixColumnDotProduct( box2ToBox1, 0, box2Origin ) ) + tolerance; - if ( FloatBits(originProjection) > FloatBits(boxProjectionSum) ) - { - MatrixGetColumn( box2ToWorld, 0, pPlane->normal ); - return true; - } - - // Second side of box 2 - boxProjectionSum = box2Size.y + MatrixColumnDotProduct( absBox2ToBox1, 1, box1Size ); - originProjection = FloatMakePositive( MatrixColumnDotProduct( box2ToBox1, 1, box2Origin ) ) + tolerance; - if ( FloatBits(originProjection) > FloatBits(boxProjectionSum) ) - { - MatrixGetColumn( box2ToWorld, 1, pPlane->normal ); - return true; - } - - // Third side of box 2 - boxProjectionSum = box2Size.z + MatrixColumnDotProduct( absBox2ToBox1, 2, box1Size ); - originProjection = FloatMakePositive( MatrixColumnDotProduct( box2ToBox1, 2, box2Origin ) ) + tolerance; - if ( FloatBits(originProjection) > FloatBits(boxProjectionSum) ) - { - MatrixGetColumn( box2ToWorld, 2, pPlane->normal ); - return true; - } - - // Next check the splitting planes which are orthogonal to the pairs - // of edges, one from box1 and one from box2. As only direction matters, - // there are 9 pairs since each box has 3 distinct edge directions. - - // Here, we take advantage of the fact that the edges from box 1 are all - // axis aligned; therefore the crossproducts are simplified. Let's walk through - // the example of b1e1 x b2e1: - - // In this example, the line to check is perpendicular to b1e1 + b2e2 - // we can compute this line by taking the cross-product: - // - // [ i j k ] - // [ 1 0 0 ] = - ez j + ey k = l1 - // [ ex ey ez ] - - // Where ex, ey, ez is the components of box2's x axis in the space of box 1, - // which is == to the 0th column of of box2toBox1 - - // The projection of box1 onto this line = the absolute dot product of the box size - // against the line, which = - // AbsDot( box1Size, l1 ) = abs( -ez * box1.y ) + abs( ey * box1.z ) - - // To compute the projection of box2 onto this line, we'll do it in the space of box 2 - // - // [ i j k ] - // [ fx fy fz ] = fz j - fy k = l2 - // [ 1 0 0 ] - - // Where fx, fy, fz is the components of box1's x axis in the space of box 2, - // which is == to the 0th row of of box2toBox1 - - // The projection of box2 onto this line = the absolute dot product of the box size - // against the line, which = - // AbsDot( box2Size, l2 ) = abs( fz * box2.y ) + abs ( fy * box2.z ) - - // The projection of the relative origin position on this line is done in the - // space of box 1: - // - // originProjection = DotProduct( <-ez j + ey k>, box2Origin ) = - // -ez * box2Origin.y + ey * box2Origin.z - - // b1e1 x b2e1 - boxProjectionSum = - box1Size.y * absBox2ToBox1[2][0] + box1Size.z * absBox2ToBox1[1][0] + - box2Size.y * absBox2ToBox1[0][2] + box2Size.z * absBox2ToBox1[0][1]; - originProjection = FloatMakePositive( -box2Origin.y * box2ToBox1[2][0] + box2Origin.z * box2ToBox1[1][0] ) + tolerance; - if ( FloatBits(originProjection) > FloatBits(boxProjectionSum) ) - { - MatrixGetColumn( box2ToWorld, 0, tmp ); - CrossProduct( worldToBox1[0], tmp.Base(), pPlane->normal.Base() ); - return true; - } - - // b1e1 x b2e2 - boxProjectionSum = - box1Size.y * absBox2ToBox1[2][1] + box1Size.z * absBox2ToBox1[1][1] + - box2Size.x * absBox2ToBox1[0][2] + box2Size.z * absBox2ToBox1[0][0]; - originProjection = FloatMakePositive( -box2Origin.y * box2ToBox1[2][1] + box2Origin.z * box2ToBox1[1][1] ) + tolerance; - if ( FloatBits(originProjection) > FloatBits(boxProjectionSum) ) - { - MatrixGetColumn( box2ToWorld, 1, tmp ); - CrossProduct( worldToBox1[0], tmp.Base(), pPlane->normal.Base() ); - return true; - } - - // b1e1 x b2e3 - boxProjectionSum = - box1Size.y * absBox2ToBox1[2][2] + box1Size.z * absBox2ToBox1[1][2] + - box2Size.x * absBox2ToBox1[0][1] + box2Size.y * absBox2ToBox1[0][0]; - originProjection = FloatMakePositive( -box2Origin.y * box2ToBox1[2][2] + box2Origin.z * box2ToBox1[1][2] ) + tolerance; - if ( FloatBits(originProjection) > FloatBits(boxProjectionSum) ) - { - MatrixGetColumn( box2ToWorld, 2, tmp ); - CrossProduct( worldToBox1[0], tmp.Base(), pPlane->normal.Base() ); - return true; - } - - // b1e2 x b2e1 - boxProjectionSum = - box1Size.x * absBox2ToBox1[2][0] + box1Size.z * absBox2ToBox1[0][0] + - box2Size.y * absBox2ToBox1[1][2] + box2Size.z * absBox2ToBox1[1][1]; - originProjection = FloatMakePositive( box2Origin.x * box2ToBox1[2][0] - box2Origin.z * box2ToBox1[0][0] ) + tolerance; - if ( FloatBits(originProjection) > FloatBits(boxProjectionSum) ) - { - MatrixGetColumn( box2ToWorld, 0, tmp ); - CrossProduct( worldToBox1[1], tmp.Base(), pPlane->normal.Base() ); - return true; - } - - // b1e2 x b2e2 - boxProjectionSum = - box1Size.x * absBox2ToBox1[2][1] + box1Size.z * absBox2ToBox1[0][1] + - box2Size.x * absBox2ToBox1[1][2] + box2Size.z * absBox2ToBox1[1][0]; - originProjection = FloatMakePositive( box2Origin.x * box2ToBox1[2][1] - box2Origin.z * box2ToBox1[0][1] ) + tolerance; - if ( FloatBits(originProjection) > FloatBits(boxProjectionSum) ) - { - MatrixGetColumn( box2ToWorld, 1, tmp ); - CrossProduct( worldToBox1[1], tmp.Base(), pPlane->normal.Base() ); - return true; - } - - // b1e2 x b2e3 - boxProjectionSum = - box1Size.x * absBox2ToBox1[2][2] + box1Size.z * absBox2ToBox1[0][2] + - box2Size.x * absBox2ToBox1[1][1] + box2Size.y * absBox2ToBox1[1][0]; - originProjection = FloatMakePositive( box2Origin.x * box2ToBox1[2][2] - box2Origin.z * box2ToBox1[0][2] ) + tolerance; - if ( FloatBits(originProjection) > FloatBits(boxProjectionSum) ) - { - MatrixGetColumn( box2ToWorld, 2, tmp ); - CrossProduct( worldToBox1[1], tmp.Base(), pPlane->normal.Base() ); - return true; - } - - // b1e3 x b2e1 - boxProjectionSum = - box1Size.x * absBox2ToBox1[1][0] + box1Size.y * absBox2ToBox1[0][0] + - box2Size.y * absBox2ToBox1[2][2] + box2Size.z * absBox2ToBox1[2][1]; - originProjection = FloatMakePositive( -box2Origin.x * box2ToBox1[1][0] + box2Origin.y * box2ToBox1[0][0] ) + tolerance; - if ( FloatBits(originProjection) > FloatBits(boxProjectionSum) ) - { - MatrixGetColumn( box2ToWorld, 0, tmp ); - CrossProduct( worldToBox1[2], tmp.Base(), pPlane->normal.Base() ); - return true; - } - - // b1e3 x b2e2 - boxProjectionSum = - box1Size.x * absBox2ToBox1[1][1] + box1Size.y * absBox2ToBox1[0][1] + - box2Size.x * absBox2ToBox1[2][2] + box2Size.z * absBox2ToBox1[2][0]; - originProjection = FloatMakePositive( -box2Origin.x * box2ToBox1[1][1] + box2Origin.y * box2ToBox1[0][1] ) + tolerance; - if ( FloatBits(originProjection) > FloatBits(boxProjectionSum) ) - { - MatrixGetColumn( box2ToWorld, 1, tmp ); - CrossProduct( worldToBox1[2], tmp.Base(), pPlane->normal.Base() ); - return true; - } - - // b1e3 x b2e3 - boxProjectionSum = - box1Size.x * absBox2ToBox1[1][2] + box1Size.y * absBox2ToBox1[0][2] + - box2Size.x * absBox2ToBox1[2][1] + box2Size.y * absBox2ToBox1[2][0]; - originProjection = FloatMakePositive( -box2Origin.x * box2ToBox1[1][2] + box2Origin.y * box2ToBox1[0][2] ) + tolerance; - if ( FloatBits(originProjection) > FloatBits(boxProjectionSum) ) - { - MatrixGetColumn( box2ToWorld, 2, tmp ); - CrossProduct( worldToBox1[2], tmp.Base(), pPlane->normal.Base() ); - return true; - } - - return false; -} - - -//----------------------------------------------------------------------------- -// Compute a separating plane between two boxes (expensive!) -// Returns false if no separating plane exists -//----------------------------------------------------------------------------- -bool ComputeSeparatingPlane( const Vector& org1, const QAngle& angles1, const Vector& min1, const Vector& max1, - const Vector& org2, const QAngle& angles2, const Vector& min2, const Vector& max2, - float tolerance, cplane_t* pPlane ) -{ - matrix3x4_t worldToBox1, box2ToWorld; - ComputeCenterIMatrix( org1, angles1, min1, max1, worldToBox1 ); - ComputeCenterMatrix( org2, angles2, min2, max2, box2ToWorld ); - - // Then compute the size of the two boxes - Vector box1Size, box2Size; - VectorSubtract( max1, min1, box1Size ); - VectorSubtract( max2, min2, box2Size ); - box1Size *= 0.5f; - box2Size *= 0.5f; - - return ComputeSeparatingPlane( worldToBox1, box2ToWorld, box1Size, box2Size, tolerance, pPlane ); -} - - -//----------------------------------------------------------------------------- -// Swept OBB test -//----------------------------------------------------------------------------- -bool IsRayIntersectingOBB( const Ray_t &ray, const Vector& org, const QAngle& angles, - const Vector& mins, const Vector& maxs ) -{ - if ( angles == vec3_angle ) - { - Vector vecWorldMins, vecWorldMaxs; - VectorAdd( org, mins, vecWorldMins ); - VectorAdd( org, maxs, vecWorldMaxs ); - return IsBoxIntersectingRay( vecWorldMins, vecWorldMaxs, ray ); - } - - if ( ray.m_IsRay ) - { - matrix3x4_t worldToBox; - AngleIMatrix( angles, org, worldToBox ); - - Ray_t rotatedRay; - VectorTransform( ray.m_Start, worldToBox, rotatedRay.m_Start ); - VectorRotate( ray.m_Delta, worldToBox, rotatedRay.m_Delta ); - rotatedRay.m_StartOffset = vec3_origin; - rotatedRay.m_Extents = vec3_origin; - rotatedRay.m_IsRay = ray.m_IsRay; - rotatedRay.m_IsSwept = ray.m_IsSwept; - - return IsBoxIntersectingRay( mins, maxs, rotatedRay ); - } - - if ( !ray.m_IsSwept ) - { - cplane_t plane; - return ComputeSeparatingPlane( ray.m_Start, vec3_angle, -ray.m_Extents, ray.m_Extents, - org, angles, mins, maxs, 0.0f, &plane ) == false; - } - - // NOTE: See the comments in ComputeSeparatingPlane to understand this math - - // First, compute the basis of box in the space of the ray - // NOTE: These basis place the origin at the centroid of each box! - matrix3x4_t worldToBox1, box2ToWorld; - ComputeCenterMatrix( org, angles, mins, maxs, box2ToWorld ); - - // Find the center + extents of an AABB surrounding the ray - Vector vecRayCenter; - VectorMA( ray.m_Start, 0.5, ray.m_Delta, vecRayCenter ); - vecRayCenter *= -1.0f; - SetIdentityMatrix( worldToBox1 ); - MatrixSetColumn( vecRayCenter, 3, worldToBox1 ); - - Vector box1Size; - box1Size.x = ray.m_Extents.x + FloatMakePositive( ray.m_Delta.x ) * 0.5f; - box1Size.y = ray.m_Extents.y + FloatMakePositive( ray.m_Delta.y ) * 0.5f; - box1Size.z = ray.m_Extents.z + FloatMakePositive( ray.m_Delta.z ) * 0.5f; - - // Then compute the size of the box - Vector box2Size; - VectorSubtract( maxs, mins, box2Size ); - box2Size *= 0.5f; - - // Do an OBB test of the box with the AABB surrounding the ray - cplane_t plane; - if ( ComputeSeparatingPlane( worldToBox1, box2ToWorld, box1Size, box2Size, 0.0f, &plane ) ) - return false; - - // Now deal with the planes which are the cross products of the ray sweep direction vs box edges - Vector vecRayDirection = ray.m_Delta; - VectorNormalize( vecRayDirection ); - - // Need a vector between ray center vs box center measured in the space of the ray (world) - Vector vecCenterDelta; - vecCenterDelta.x = box2ToWorld[0][3] - ray.m_Start.x; - vecCenterDelta.y = box2ToWorld[1][3] - ray.m_Start.y; - vecCenterDelta.z = box2ToWorld[2][3] - ray.m_Start.z; - - // Rotate the ray direction into the space of the OBB - Vector vecAbsRayDirBox2; - VectorIRotate( vecRayDirection, box2ToWorld, vecAbsRayDirBox2 ); - - // Make abs versions of the ray in world space + ray in box2 space - VectorAbs( vecAbsRayDirBox2, vecAbsRayDirBox2 ); - - // Now do the work for the planes which are perpendicular to the edges of the AABB - // and the sweep direction edges... - - // In this example, the line to check is perpendicular to box edge x + ray delta - // we can compute this line by taking the cross-product: - // - // [ i j k ] - // [ 1 0 0 ] = - dz j + dy k = l1 - // [ dx dy dz ] - - // Where dx, dy, dz is the ray delta (normalized) - - // The projection of the box onto this line = the absolute dot product of the box size - // against the line, which = - // AbsDot( vecBoxHalfDiagonal, l1 ) = abs( -dz * vecBoxHalfDiagonal.y ) + abs( dy * vecBoxHalfDiagonal.z ) - - // Because the plane contains the sweep direction, the sweep will produce - // no extra projection onto the line normal to the plane. - // Therefore all we need to do is project the ray extents onto this line also: - // AbsDot( ray.m_Extents, l1 ) = abs( -dz * ray.m_Extents.y ) + abs( dy * ray.m_Extents.z ) - - Vector vecPlaneNormal; - - // box x x ray delta - CrossProduct( vecRayDirection, Vector( box2ToWorld[0][0], box2ToWorld[1][0], box2ToWorld[2][0] ), vecPlaneNormal ); - float flCenterDeltaProjection = FloatMakePositive( DotProduct( vecPlaneNormal, vecCenterDelta ) ); - float flBoxProjectionSum = - vecAbsRayDirBox2.z * box2Size.y + vecAbsRayDirBox2.y * box2Size.z + - DotProductAbs( vecPlaneNormal, ray.m_Extents ); - if ( FloatBits(flCenterDeltaProjection) > FloatBits(flBoxProjectionSum) ) - return false; - - // box y x ray delta - CrossProduct( vecRayDirection, Vector( box2ToWorld[0][1], box2ToWorld[1][1], box2ToWorld[2][1] ), vecPlaneNormal ); - flCenterDeltaProjection = FloatMakePositive( DotProduct( vecPlaneNormal, vecCenterDelta ) ); - flBoxProjectionSum = - vecAbsRayDirBox2.z * box2Size.x + vecAbsRayDirBox2.x * box2Size.z + - DotProductAbs( vecPlaneNormal, ray.m_Extents ); - if ( FloatBits(flCenterDeltaProjection) > FloatBits(flBoxProjectionSum) ) - return false; - - // box z x ray delta - CrossProduct( vecRayDirection, Vector( box2ToWorld[0][2], box2ToWorld[1][2], box2ToWorld[2][2] ), vecPlaneNormal ); - flCenterDeltaProjection = FloatMakePositive( DotProduct( vecPlaneNormal, vecCenterDelta ) ); - flBoxProjectionSum = - vecAbsRayDirBox2.y * box2Size.x + vecAbsRayDirBox2.x * box2Size.y + - DotProductAbs( vecPlaneNormal, ray.m_Extents ); - if ( FloatBits(flCenterDeltaProjection) > FloatBits(flBoxProjectionSum) ) - return false; - - return true; -} - -//-------------------------------------------------------------------------- -// Purpose: -// -// NOTE: -// triangle points are given in clockwise order (aabb-triangle test) -// -// 1 edge0 = 1 - 0 -// | \ edge1 = 2 - 1 -// | \ edge2 = 0 - 2 -// | \ -// | \ -// 0-----2 -// -//-------------------------------------------------------------------------- - -//----------------------------------------------------------------------------- -// Purpose: find the minima and maxima of the 3 given values -//----------------------------------------------------------------------------- -inline void FindMinMax( float v1, float v2, float v3, float &min, float &max ) -{ - min = max = v1; - if ( v2 < min ) { min = v2; } - if ( v2 > max ) { max = v2; } - if ( v3 < min ) { min = v3; } - if ( v3 > max ) { max = v3; } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -inline bool AxisTestEdgeCrossX2( float flEdgeZ, float flEdgeY, float flAbsEdgeZ, float flAbsEdgeY, - const Vector &p1, const Vector &p3, const Vector &vecExtents, - float flTolerance ) -{ - // Cross Product( axialX(1,0,0) x edge ): x = 0.0f, y = edge.z, z = -edge.y - // Triangle Point Distances: dist(x) = normal.y * pt(x).y + normal.z * pt(x).z - float flDist1 = flEdgeZ * p1.y - flEdgeY * p1.z; - float flDist3 = flEdgeZ * p3.y - flEdgeY * p3.z; - - // Extents are symmetric: dist = abs( normal.y ) * extents.y + abs( normal.z ) * extents.z - float flDistBox = flAbsEdgeZ * vecExtents.y + flAbsEdgeY * vecExtents.z; - - // Either dist1, dist3 is the closest point to the box, determine which and test of overlap with box(AABB). - if ( flDist1 < flDist3 ) - { - if ( ( flDist1 > ( flDistBox + flTolerance ) ) || ( flDist3 < -( flDistBox + flTolerance ) ) ) - return false; - } - else - { - if ( ( flDist3 > ( flDistBox + flTolerance ) ) || ( flDist1 < -( flDistBox + flTolerance ) ) ) - return false; - } - - return true; -} - -//-------------------------------------------------------------------------- -// Purpose: -//-------------------------------------------------------------------------- -inline bool AxisTestEdgeCrossX3( float flEdgeZ, float flEdgeY, float flAbsEdgeZ, float flAbsEdgeY, - const Vector &p1, const Vector &p2, const Vector &vecExtents, - float flTolerance ) -{ - // Cross Product( axialX(1,0,0) x edge ): x = 0.0f, y = edge.z, z = -edge.y - // Triangle Point Distances: dist(x) = normal.y * pt(x).y + normal.z * pt(x).z - float flDist1 = flEdgeZ * p1.y - flEdgeY * p1.z; - float flDist2 = flEdgeZ * p2.y - flEdgeY * p2.z; - - // Extents are symmetric: dist = abs( normal.y ) * extents.y + abs( normal.z ) * extents.z - float flDistBox = flAbsEdgeZ * vecExtents.y + flAbsEdgeY * vecExtents.z; - - // Either dist1, dist2 is the closest point to the box, determine which and test of overlap with box(AABB). - if ( flDist1 < flDist2 ) - { - if ( ( flDist1 > ( flDistBox + flTolerance ) ) || ( flDist2 < -( flDistBox + flTolerance ) ) ) - return false; - } - else - { - if ( ( flDist2 > ( flDistBox + flTolerance ) ) || ( flDist1 < -( flDistBox + flTolerance ) ) ) - return false; - } - - return true; -} - -//-------------------------------------------------------------------------- -//-------------------------------------------------------------------------- -inline bool AxisTestEdgeCrossY2( float flEdgeZ, float flEdgeX, float flAbsEdgeZ, float flAbsEdgeX, - const Vector &p1, const Vector &p3, const Vector &vecExtents, - float flTolerance ) -{ - // Cross Product( axialY(0,1,0) x edge ): x = -edge.z, y = 0.0f, z = edge.x - // Triangle Point Distances: dist(x) = normal.x * pt(x).x + normal.z * pt(x).z - float flDist1 = -flEdgeZ * p1.x + flEdgeX * p1.z; - float flDist3 = -flEdgeZ * p3.x + flEdgeX * p3.z; - - // Extents are symmetric: dist = abs( normal.x ) * extents.x + abs( normal.z ) * extents.z - float flDistBox = flAbsEdgeZ * vecExtents.x + flAbsEdgeX * vecExtents.z; - - // Either dist1, dist3 is the closest point to the box, determine which and test of overlap with box(AABB). - if ( flDist1 < flDist3 ) - { - if ( ( flDist1 > ( flDistBox + flTolerance ) ) || ( flDist3 < -( flDistBox + flTolerance ) ) ) - return false; - } - else - { - if ( ( flDist3 > ( flDistBox + flTolerance ) ) || ( flDist1 < -( flDistBox + flTolerance ) ) ) - return false; - } - - return true; -} - -//-------------------------------------------------------------------------- -//-------------------------------------------------------------------------- -inline bool AxisTestEdgeCrossY3( float flEdgeZ, float flEdgeX, float flAbsEdgeZ, float flAbsEdgeX, - const Vector &p1, const Vector &p2, const Vector &vecExtents, - float flTolerance ) -{ - // Cross Product( axialY(0,1,0) x edge ): x = -edge.z, y = 0.0f, z = edge.x - // Triangle Point Distances: dist(x) = normal.x * pt(x).x + normal.z * pt(x).z - float flDist1 = -flEdgeZ * p1.x + flEdgeX * p1.z; - float flDist2 = -flEdgeZ * p2.x + flEdgeX * p2.z; - - // Extents are symmetric: dist = abs( normal.x ) * extents.x + abs( normal.z ) * extents.z - float flDistBox = flAbsEdgeZ * vecExtents.x + flAbsEdgeX * vecExtents.z; - - // Either dist1, dist2 is the closest point to the box, determine which and test of overlap with box(AABB). - if ( flDist1 < flDist2 ) - { - if ( ( flDist1 > ( flDistBox + flTolerance ) ) || ( flDist2 < -( flDistBox + flTolerance ) ) ) - return false; - } - else - { - if ( ( flDist2 > ( flDistBox + flTolerance ) ) || ( flDist1 < -( flDistBox + flTolerance ) ) ) - return false; - } - - return true; -} - -//-------------------------------------------------------------------------- -//-------------------------------------------------------------------------- -inline bool AxisTestEdgeCrossZ1( float flEdgeY, float flEdgeX, float flAbsEdgeY, float flAbsEdgeX, - const Vector &p2, const Vector &p3, const Vector &vecExtents, - float flTolerance ) -{ - // Cross Product( axialZ(0,0,1) x edge ): x = edge.y, y = -edge.x, z = 0.0f - // Triangle Point Distances: dist(x) = normal.x * pt(x).x + normal.y * pt(x).y - float flDist2 = flEdgeY * p2.x - flEdgeX * p2.y; - float flDist3 = flEdgeY * p3.x - flEdgeX * p3.y; - - // Extents are symmetric: dist = abs( normal.x ) * extents.x + abs( normal.y ) * extents.y - float flDistBox = flAbsEdgeY * vecExtents.x + flAbsEdgeX * vecExtents.y; - - // Either dist2, dist3 is the closest point to the box, determine which and test of overlap with box(AABB). - if ( flDist3 < flDist2 ) - { - if ( ( flDist3 > ( flDistBox + flTolerance ) ) || ( flDist2 < -( flDistBox + flTolerance ) ) ) - return false; - } - else - { - if ( ( flDist2 > ( flDistBox + flTolerance ) ) || ( flDist3 < -( flDistBox + flTolerance ) ) ) - return false; - } - - return true; -} - -//-------------------------------------------------------------------------- -//-------------------------------------------------------------------------- -inline bool AxisTestEdgeCrossZ2( float flEdgeY, float flEdgeX, float flAbsEdgeY, float flAbsEdgeX, - const Vector &p1, const Vector &p3, const Vector &vecExtents, - float flTolerance ) -{ - // Cross Product( axialZ(0,0,1) x edge ): x = edge.y, y = -edge.x, z = 0.0f - // Triangle Point Distances: dist(x) = normal.x * pt(x).x + normal.y * pt(x).y - float flDist1 = flEdgeY * p1.x - flEdgeX * p1.y; - float flDist3 = flEdgeY * p3.x - flEdgeX * p3.y; - - // Extents are symmetric: dist = abs( normal.x ) * extents.x + abs( normal.y ) * extents.y - float flDistBox = flAbsEdgeY * vecExtents.x + flAbsEdgeX * vecExtents.y; - - // Either dist1, dist3 is the closest point to the box, determine which and test of overlap with box(AABB). - if ( flDist1 < flDist3 ) - { - if ( ( flDist1 > ( flDistBox + flTolerance ) ) || ( flDist3 < -( flDistBox + flTolerance ) ) ) - return false; - } - else - { - if ( ( flDist3 > ( flDistBox + flTolerance ) ) || ( flDist1 < -( flDistBox + flTolerance ) ) ) - return false; - } - - return true; -} - -//----------------------------------------------------------------------------- -// Purpose: Test for an intersection (overlap) between an axial-aligned bounding -// box (AABB) and a triangle. -// -// Using the "Separating-Axis Theorem" to test for intersections between -// a triangle and an axial-aligned bounding box (AABB). -// 1. 3 Axis Planes - x, y, z -// 2. 9 Edge Planes Tests - the 3 edges of the triangle crossed with all 3 axial -// planes (x, y, z) -// 3. 1 Face Plane - the triangle plane (cplane_t plane below) -// Output: false = separating axis (no intersection) -// true = intersection -//----------------------------------------------------------------------------- -bool IsBoxIntersectingTriangle( const Vector &vecBoxCenter, const Vector &vecBoxExtents, - const Vector &v1, const Vector &v2, const Vector &v3, - const cplane_t &plane, float flTolerance ) -{ - // Test the axial planes (x,y,z) against the min, max of the triangle. - float flMin, flMax; - Vector p1, p2, p3; - - // x plane - p1.x = v1.x - vecBoxCenter.x; - p2.x = v2.x - vecBoxCenter.x; - p3.x = v3.x - vecBoxCenter.x; - FindMinMax( p1.x, p2.x, p3.x, flMin, flMax ); - if ( ( flMin > ( vecBoxExtents.x + flTolerance ) ) || ( flMax < -( vecBoxExtents.x + flTolerance ) ) ) - return false; - - // y plane - p1.y = v1.y - vecBoxCenter.y; - p2.y = v2.y - vecBoxCenter.y; - p3.y = v3.y - vecBoxCenter.y; - FindMinMax( p1.y, p2.y, p3.y, flMin, flMax ); - if ( ( flMin > ( vecBoxExtents.y + flTolerance ) ) || ( flMax < -( vecBoxExtents.y + flTolerance ) ) ) - return false; - - // z plane - p1.z = v1.z - vecBoxCenter.z; - p2.z = v2.z - vecBoxCenter.z; - p3.z = v3.z - vecBoxCenter.z; - FindMinMax( p1.z, p2.z, p3.z, flMin, flMax ); - if ( ( flMin > ( vecBoxExtents.z + flTolerance ) ) || ( flMax < -( vecBoxExtents.z + flTolerance ) ) ) - return false; - - // Test the 9 edge cases. - Vector vecEdge, vecAbsEdge; - - // edge 0 (cross x,y,z) - vecEdge = p2 - p1; - vecAbsEdge.y = FloatMakePositive( vecEdge.y ); - vecAbsEdge.z = FloatMakePositive( vecEdge.z ); - if ( !AxisTestEdgeCrossX2( vecEdge.z, vecEdge.y, vecAbsEdge.z, vecAbsEdge.y, p1, p3, vecBoxExtents, flTolerance ) ) - return false; - - vecAbsEdge.x = FloatMakePositive( vecEdge.x ); - if ( !AxisTestEdgeCrossY2( vecEdge.z, vecEdge.x, vecAbsEdge.z, vecAbsEdge.x, p1, p3, vecBoxExtents, flTolerance ) ) - return false; - - if ( !AxisTestEdgeCrossZ1( vecEdge.y, vecEdge.x, vecAbsEdge.y, vecAbsEdge.x, p2, p3, vecBoxExtents, flTolerance ) ) - return false; - - // edge 1 (cross x,y,z) - vecEdge = p3 - p2; - vecAbsEdge.y = FloatMakePositive( vecEdge.y ); - vecAbsEdge.z = FloatMakePositive( vecEdge.z ); - if ( !AxisTestEdgeCrossX2( vecEdge.z, vecEdge.y, vecAbsEdge.z, vecAbsEdge.y, p1, p2, vecBoxExtents, flTolerance ) ) - return false; - - vecAbsEdge.x = FloatMakePositive( vecEdge.x ); - if ( !AxisTestEdgeCrossY2( vecEdge.z, vecEdge.x, vecAbsEdge.z, vecAbsEdge.x, p1, p2, vecBoxExtents, flTolerance ) ) - return false; - - if ( !AxisTestEdgeCrossZ2( vecEdge.y, vecEdge.x, vecAbsEdge.y, vecAbsEdge.x, p1, p3, vecBoxExtents, flTolerance ) ) - return false; - - // edge 2 (cross x,y,z) - vecEdge = p1 - p3; - vecAbsEdge.y = FloatMakePositive( vecEdge.y ); - vecAbsEdge.z = FloatMakePositive( vecEdge.z ); - if ( !AxisTestEdgeCrossX3( vecEdge.z, vecEdge.y, vecAbsEdge.z, vecAbsEdge.y, p1, p2, vecBoxExtents, flTolerance ) ) - return false; - - vecAbsEdge.x = FloatMakePositive( vecEdge.x ); - if ( !AxisTestEdgeCrossY3( vecEdge.z, vecEdge.x, vecAbsEdge.z, vecAbsEdge.x, p1, p2, vecBoxExtents, flTolerance ) ) - return false; - - if ( !AxisTestEdgeCrossZ1( vecEdge.y, vecEdge.x, vecAbsEdge.y, vecAbsEdge.x, p2, p3, vecBoxExtents, flTolerance ) ) - return false; - - // Test against the triangle face plane. - Vector vecMin, vecMax; - VectorSubtract( vecBoxCenter, vecBoxExtents, vecMin ); - VectorAdd( vecBoxCenter, vecBoxExtents, vecMax ); - if ( BoxOnPlaneSide( vecMin, vecMax, &plane ) != 3 ) - return false; - - return true; -} - -// NOTE: JAY: This is untested code based on Real-time Collision Detection by Ericson -#if 0 -Vector CalcClosestPointOnTriangle( const Vector &P, const Vector &v0, const Vector &v1, const Vector &v2 ) -{ - Vector e0 = v1 - v0; - Vector e1 = v2 - v0; - Vector p0 = P - v0; - - // voronoi region of v0 - float d1 = DotProduct( e0, p0 ); - float d2 = DotProduct( e1, p0 ); - if (d1 <= 0.0f && d2 <= 0.0f) - return v0; - - // voronoi region of v1 - Vector p1 = P - v1; - float d3 = DotProduct( e0, p1 ); - float d4 = DotProduct( e1, p1 ); - if (d3 >=0.0f && d4 <= d3) - return v1; - - // voronoi region of e0 (v0-v1) - float ve2 = d1*d4 - d3*d2; - if ( ve2 <= 0.0f && d1 >= 0.0f && d3 <= 0.0f ) - { - float v = d1 / (d1-d3); - return v0 + v * e0; - } - // voronoi region of v2 - Vector p2 = P - v2; - float d5 = DotProduct( e0, p2 ); - float d6 = DotProduct( e1, p2 ); - if (d6 >= 0.0f && d5 <= d6) - return v2; - // voronoi region of e1 - float ve1 = d5*d2 - d1*d6; - if (ve1 <= 0.0f && d2 >= 0.0f && d6 >= 0.0f) - { - float w = d2 / (d2-d6); - return v0 + w * e1; - } - // voronoi region on e2 - float ve0 = d3*d6 - d5*d4; - if ( ve0 <= 0.0f && (d4-d3) >= 0.0f && (d5-d6) >= 0.0f ) - { - float w = (d4-d3)/((d4-d3) + (d5-d6)); - return v1 + w * (v2-v1); - } - // voronoi region of v0v1v2 triangle - float denom = 1.0f / (ve0+ve1+ve2); - float v = ve1*denom; - float w = ve2 * denom; - return v0 + e0 * v + e1 * w; -} -#endif - -#endif // !_STATIC_LINKED || _SHARED_LIB \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: Common collision utility methods +// +// $Header: $ +// $NoKeywords: $ +//=============================================================================// + +#if !defined(_STATIC_LINKED) || defined(_SHARED_LIB) + +#include "collisionutils.h" +#include "cmodel.h" +#include "mathlib.h" +#include "vector.h" +#include "tier0/dbg.h" +#include +#include "vector4d.h" +#include "trace.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +#define UNINIT -99999.0 + +//----------------------------------------------------------------------------- +// Clears the trace +//----------------------------------------------------------------------------- +static void Collision_ClearTrace( const Vector &vecRayStart, const Vector &vecRayDelta, CBaseTrace *pTrace ) +{ + pTrace->startpos = vecRayStart; + pTrace->endpos = vecRayStart; + pTrace->endpos += vecRayDelta; + pTrace->startsolid = false; + pTrace->allsolid = false; + pTrace->fraction = 1.0f; + pTrace->contents = 0; +} + + +//----------------------------------------------------------------------------- +// Compute the offset in t along the ray that we'll use for the collision +//----------------------------------------------------------------------------- +static float ComputeBoxOffset( const Ray_t& ray ) +{ + if (ray.m_IsRay) + return 1e-3f; + + // Find the projection of the box diagonal along the ray... + float offset = FloatMakePositive(ray.m_Extents[0] * ray.m_Delta[0]) + + FloatMakePositive(ray.m_Extents[1] * ray.m_Delta[1]) + + FloatMakePositive(ray.m_Extents[2] * ray.m_Delta[2]); + + // We need to divide twice: Once to normalize the computation above + // so we get something in units of extents, and the second to normalize + // that with respect to the entire raycast. + offset *= InvRSquared( ray.m_Delta ); + + // 1e-3 is an epsilon + return offset + 1e-3; +} + + +//----------------------------------------------------------------------------- +// Intersects a swept box against a triangle +//----------------------------------------------------------------------------- +float IntersectRayWithTriangle( const Ray_t& ray, + const Vector& v1, const Vector& v2, const Vector& v3, bool oneSided ) +{ + // This is cute: Use barycentric coordinates to represent the triangle + // Vo(1-u-v) + V1u + V2v and intersect that with a line Po + Dt + // This gives us 3 equations + 3 unknowns, which we can solve with + // Cramer's rule... + // E1x u + E2x v - Dx t = Pox - Vox + // There's a couple of other optimizations, Cramer's rule involves + // computing the determinant of a matrix which has been constructed + // by three vectors. It turns out that + // det | A B C | = -( A x C ) dot B or -(C x B) dot A + // which we'll use below.. + + Vector edge1, edge2, org; + VectorSubtract( v2, v1, edge1 ); + VectorSubtract( v3, v1, edge2 ); + + // Cull out one-sided stuff + if (oneSided) + { + Vector normal; + CrossProduct( edge1, edge2, normal ); + if (DotProduct( normal, ray.m_Delta ) >= 0.0f) + return -1.0f; + } + + // FIXME: This is inaccurate, but fast for boxes + // We want to do a fast separating axis implementation here + // with a swept triangle along the reverse direction of the ray. + + // Compute some intermediary terms + Vector dirCrossEdge2, orgCrossEdge1; + CrossProduct( ray.m_Delta, edge2, dirCrossEdge2 ); + + // Compute the denominator of Cramer's rule: + // | -Dx E1x E2x | + // det | -Dy E1y E2y | = (D x E2) dot E1 + // | -Dz E1z E2z | + float denom = DotProduct( dirCrossEdge2, edge1 ); + if( FloatMakePositive( denom ) < 1e-6 ) + return -1.0f; + denom = 1.0f / denom; + + // Compute u. It's gotta lie in the range of 0 to 1. + // | -Dx orgx E2x | + // u = denom * det | -Dy orgy E2y | = (D x E2) dot org + // | -Dz orgz E2z | + VectorSubtract( ray.m_Start, v1, org ); + float u = DotProduct( dirCrossEdge2, org ) * denom; + if ((u < 0.0f) || (u > 1.0f)) + return -1.0f; + + // Compute t and v the same way... + // In barycentric coords, u + v < 1 + CrossProduct( org, edge1, orgCrossEdge1 ); + float v = DotProduct( orgCrossEdge1, ray.m_Delta ) * denom; + if ((v < 0.0f) || (v + u > 1.0f)) + return -1.0f; + + // Compute the distance along the ray direction that we need to fudge + // when using swept boxes + float boxt = ComputeBoxOffset( ray ); + float t = DotProduct( orgCrossEdge1, edge2 ) * denom; + if ((t < -boxt) || (t > 1.0f + boxt)) + return -1.0f; + + return clamp( t, 0, 1 ); +} + +//----------------------------------------------------------------------------- +// computes the barycentric coordinates of an intersection +//----------------------------------------------------------------------------- + +bool ComputeIntersectionBarycentricCoordinates( const Ray_t& ray, + const Vector& v1, const Vector& v2, const Vector& v3, float& u, float& v, + float *t ) +{ + Vector edge1, edge2, org; + VectorSubtract( v2, v1, edge1 ); + VectorSubtract( v3, v1, edge2 ); + + // Compute some intermediary terms + Vector dirCrossEdge2, orgCrossEdge1; + CrossProduct( ray.m_Delta, edge2, dirCrossEdge2 ); + + // Compute the denominator of Cramer's rule: + // | -Dx E1x E2x | + // det | -Dy E1y E2y | = (D x E2) dot E1 + // | -Dz E1z E2z | + float denom = DotProduct( dirCrossEdge2, edge1 ); + if( FloatMakePositive( denom ) < 1e-6 ) + return false; + denom = 1.0f / denom; + + // Compute u. It's gotta lie in the range of 0 to 1. + // | -Dx orgx E2x | + // u = denom * det | -Dy orgy E2y | = (D x E2) dot org + // | -Dz orgz E2z | + VectorSubtract( ray.m_Start, v1, org ); + u = DotProduct( dirCrossEdge2, org ) * denom; + + // Compute t and v the same way... + // In barycentric coords, u + v < 1 + CrossProduct( org, edge1, orgCrossEdge1 ); + v = DotProduct( orgCrossEdge1, ray.m_Delta ) * denom; + + // Compute the distance along the ray direction that we need to fudge + // when using swept boxes + if( t ) + { + float boxt = ComputeBoxOffset( ray ); + *t = DotProduct( orgCrossEdge1, edge2 ) * denom; + if( ( *t < -boxt ) || ( *t > 1.0f + boxt ) ) + return false; + } + + return true; +} + +//----------------------------------------------------------------------------- +// Intersects a plane with a triangle (requires barycentric definition) +//----------------------------------------------------------------------------- + +int IntersectTriangleWithPlaneBarycentric( const Vector& org, const Vector& edgeU, + const Vector& edgeV, const Vector4D& plane, Vector2D* pIntersection ) +{ + // This uses a barycentric method, since we need that to determine + // interpolated points, alphas, and normals + // Given the plane equation P dot N + d = 0 + // and the barycentric coodinate equation P = Org + EdgeU * u + EdgeV * v + // Plug em in. Intersection occurs at u = 0 or v = 0 or u + v = 1 + + float orgDotNormal = DotProduct( org, plane.AsVector3D() ); + float edgeUDotNormal = DotProduct( edgeU, plane.AsVector3D() ); + float edgeVDotNormal = DotProduct( edgeV, plane.AsVector3D() ); + + int ptIdx = 0; + + // u = 0 + if ( edgeVDotNormal != 0.0f ) + { + pIntersection[ptIdx].x = 0.0f; + pIntersection[ptIdx].y = - ( orgDotNormal - plane.w ) / edgeVDotNormal; + if ((pIntersection[ptIdx].y >= 0.0f) && (pIntersection[ptIdx].y <= 1.0f)) + ++ptIdx; + } + + // v = 0 + if ( edgeUDotNormal != 0.0f ) + { + pIntersection[ptIdx].x = - ( orgDotNormal - plane.w ) / edgeUDotNormal; + pIntersection[ptIdx].y = 0.0f; + if ((pIntersection[ptIdx].x >= 0.0f) && (pIntersection[ptIdx].x <= 1.0f)) + ++ptIdx; + } + + // u + v = 1 + if (ptIdx == 2) + return ptIdx; + + if ( edgeVDotNormal != edgeUDotNormal ) + { + pIntersection[ptIdx].x = - ( orgDotNormal - plane.w + edgeVDotNormal) / + ( edgeUDotNormal - edgeVDotNormal); + pIntersection[ptIdx].y = 1.0f - pIntersection[ptIdx].x;; + if ((pIntersection[ptIdx].x >= 0.0f) && (pIntersection[ptIdx].x <= 1.0f) && + (pIntersection[ptIdx].y >= 0.0f) && (pIntersection[ptIdx].y <= 1.0f)) + ++ptIdx; + } + + Assert( ptIdx < 3 ); + return ptIdx; +} + + +//----------------------------------------------------------------------------- +// Returns true if a box intersects with a sphere +//----------------------------------------------------------------------------- +bool IsSphereIntersectingSphere( const Vector& center1, float radius1, + const Vector& center2, float radius2 ) +{ + Vector delta; + VectorSubtract( center2, center1, delta ); + float distSq = delta.LengthSqr(); + float radiusSum = radius1 + radius2; + return (distSq <= (radiusSum * radiusSum)); +} + + +//----------------------------------------------------------------------------- +// Returns true if a box intersects with a sphere +//----------------------------------------------------------------------------- +bool IsBoxIntersectingSphere( const Vector& boxMin, const Vector& boxMax, + const Vector& center, float radius ) +{ + // See Graphics Gems, box-sphere intersection + float dmin = 0.0f; + float flDelta; + + // Unrolled the loop.. this is a big cycle stealer... + if (center[0] < boxMin[0]) + { + flDelta = center[0] - boxMin[0]; + dmin += flDelta * flDelta; + } + else if (center[0] > boxMax[0]) + { + flDelta = boxMax[0] - center[0]; + dmin += flDelta * flDelta; + } + + if (center[1] < boxMin[1]) + { + flDelta = center[1] - boxMin[1]; + dmin += flDelta * flDelta; + } + else if (center[1] > boxMax[1]) + { + flDelta = boxMax[1] - center[1]; + dmin += flDelta * flDelta; + } + + if (center[2] < boxMin[2]) + { + flDelta = center[2] - boxMin[2]; + dmin += flDelta * flDelta; + } + else if (center[2] > boxMax[2]) + { + flDelta = boxMax[2] - center[2]; + dmin += flDelta * flDelta; + } + + return dmin < radius * radius; +} + +bool IsBoxIntersectingSphereExtents( const Vector& boxCenter, const Vector& boxHalfDiag, + const Vector& center, float radius ) +{ + // See Graphics Gems, box-sphere intersection + float dmin = 0.0f; + float flDelta, flDiff; + + // Unrolled the loop.. this is a big cycle stealer... + flDiff = FloatMakePositive( center.x - boxCenter.x ); + if (flDiff > boxHalfDiag.x) + { + flDelta = flDiff - boxHalfDiag.x; + dmin += flDelta * flDelta; + } + + flDiff = FloatMakePositive( center.y - boxCenter.y ); + if (flDiff > boxHalfDiag.y) + { + flDelta = flDiff - boxHalfDiag.y; + dmin += flDelta * flDelta; + } + + flDiff = FloatMakePositive( center.z - boxCenter.z ); + if (flDiff > boxHalfDiag.z) + { + flDelta = flDiff - boxHalfDiag.z; + dmin += flDelta * flDelta; + } + + return dmin < radius * radius; +} + + +//----------------------------------------------------------------------------- +// Returns true if a rectangle intersects with a circle +//----------------------------------------------------------------------------- +bool IsCircleIntersectingRectangle( const Vector2D& boxMin, const Vector2D& boxMax, + const Vector2D& center, float radius ) +{ + // See Graphics Gems, box-sphere intersection + float dmin = 0.0f; + float flDelta; + + if (center[0] < boxMin[0]) + { + flDelta = center[0] - boxMin[0]; + dmin += flDelta * flDelta; + } + else if (center[0] > boxMax[0]) + { + flDelta = boxMax[0] - center[0]; + dmin += flDelta * flDelta; + } + + if (center[1] < boxMin[1]) + { + flDelta = center[1] - boxMin[1]; + dmin += flDelta * flDelta; + } + else if (center[1] > boxMax[1]) + { + flDelta = boxMax[1] - center[1]; + dmin += flDelta * flDelta; + } + + return dmin < radius * radius; +} + + +//----------------------------------------------------------------------------- +// returns true if there's an intersection between ray and sphere +//----------------------------------------------------------------------------- +bool IsRayIntersectingSphere( const Vector &vecRayOrigin, const Vector &vecRayDelta, + const Vector& vecCenter, float flRadius, float flTolerance ) +{ + // For this algorithm, find a point on the ray which is closest to the sphere origin + // Do this by making a plane passing through the sphere origin + // whose normal is parallel to the ray. Intersect that plane with the ray. + // Plane: N dot P = I, N = D (ray direction), I = C dot N = C dot D + // Ray: P = O + D * t + // D dot ( O + D * t ) = C dot D + // D dot O + D dot D * t = C dot D + // t = (C - O) dot D / D dot D + // Clamp t to (0,1) + // Find distance of the point on the ray to the sphere center. + Assert( flTolerance >= 0.0f ); + flRadius += flTolerance; + + Vector vecRayToSphere; + VectorSubtract( vecCenter, vecRayOrigin, vecRayToSphere ); + float flNumerator = DotProduct( vecRayToSphere, vecRayDelta ); + + float t; + if (flNumerator <= 0.0f) + { + t = 0.0f; + } + else + { + float flDenominator = DotProduct( vecRayDelta, vecRayDelta ); + if ( flNumerator > flDenominator ) + t = 1.0f; + else + t = flNumerator / flDenominator; + } + + Vector vecClosestPoint; + VectorMA( vecRayOrigin, t, vecRayDelta, vecClosestPoint ); + return ( vecClosestPoint.DistToSqr( vecCenter ) <= flRadius * flRadius ); + + // NOTE: This in an alternate algorithm which I didn't use because I'd have to use a sqrt + // So it's probably faster to do this other algorithm. I'll leave the comments here + // for how to go back if we want to + + // Solve using the ray equation + the sphere equation + // P = o + dt + // (x - xc)^2 + (y - yc)^2 + (z - zc)^2 = r^2 + // (ox + dx * t - xc)^2 + (oy + dy * t - yc)^2 + (oz + dz * t - zc)^2 = r^2 + // (ox - xc)^2 + 2 * (ox-xc) * dx * t + dx^2 * t^2 + + // (oy - yc)^2 + 2 * (oy-yc) * dy * t + dy^2 * t^2 + + // (oz - zc)^2 + 2 * (oz-zc) * dz * t + dz^2 * t^2 = r^2 + // (dx^2 + dy^2 + dz^2) * t^2 + 2 * ((ox-xc)dx + (oy-yc)dy + (oz-zc)dz) t + + // (ox-xc)^2 + (oy-yc)^2 + (oz-zc)^2 - r^2 = 0 + // or, t = (-b +/- sqrt( b^2 - 4ac)) / 2a + // a = DotProduct( vecRayDelta, vecRayDelta ); + // b = 2 * DotProduct( vecRayOrigin - vecCenter, vecRayDelta ) + // c = DotProduct(vecRayOrigin - vecCenter, vecRayOrigin - vecCenter) - flRadius * flRadius; + // Valid solutions are possible only if b^2 - 4ac >= 0 + // Therefore, compute that value + see if we got it +} + + +//----------------------------------------------------------------------------- +// +// IntersectInfiniteRayWithSphere +// +// Returns whether or not there was an intersection. +// Returns the two intersection points +// +//----------------------------------------------------------------------------- +bool IntersectInfiniteRayWithSphere( const Vector &vecRayOrigin, const Vector &vecRayDelta, + const Vector &vecSphereCenter, float flRadius, float *pT1, float *pT2 ) +{ + // Solve using the ray equation + the sphere equation + // P = o + dt + // (x - xc)^2 + (y - yc)^2 + (z - zc)^2 = r^2 + // (ox + dx * t - xc)^2 + (oy + dy * t - yc)^2 + (oz + dz * t - zc)^2 = r^2 + // (ox - xc)^2 + 2 * (ox-xc) * dx * t + dx^2 * t^2 + + // (oy - yc)^2 + 2 * (oy-yc) * dy * t + dy^2 * t^2 + + // (oz - zc)^2 + 2 * (oz-zc) * dz * t + dz^2 * t^2 = r^2 + // (dx^2 + dy^2 + dz^2) * t^2 + 2 * ((ox-xc)dx + (oy-yc)dy + (oz-zc)dz) t + + // (ox-xc)^2 + (oy-yc)^2 + (oz-zc)^2 - r^2 = 0 + // or, t = (-b +/- sqrt( b^2 - 4ac)) / 2a + // a = DotProduct( vecRayDelta, vecRayDelta ); + // b = 2 * DotProduct( vecRayOrigin - vecCenter, vecRayDelta ) + // c = DotProduct(vecRayOrigin - vecCenter, vecRayOrigin - vecCenter) - flRadius * flRadius; + + Vector vecSphereToRay; + VectorSubtract( vecRayOrigin, vecSphereCenter, vecSphereToRay ); + + float a = DotProduct( vecRayDelta, vecRayDelta ); + + // This would occur in the case of a zero-length ray + if ( a == 0.0f ) + { + *pT1 = *pT2 = 0.0f; + return vecSphereToRay.LengthSqr() <= flRadius * flRadius; + } + + float b = 2 * DotProduct( vecSphereToRay, vecRayDelta ); + float c = DotProduct( vecSphereToRay, vecSphereToRay ) - flRadius * flRadius; + float flDiscrim = b * b - 4 * a * c; + if ( flDiscrim < 0.0f ) + return false; + + flDiscrim = sqrt( flDiscrim ); + float oo2a = 0.5f / a; + *pT1 = ( - b - flDiscrim ) * oo2a; + *pT2 = ( - b + flDiscrim ) * oo2a; + return true; +} + + + +//----------------------------------------------------------------------------- +// +// IntersectRayWithSphere +// +// Returns whether or not there was an intersection. +// Returns the two intersection points, clamped to (0,1) +// +//----------------------------------------------------------------------------- +bool IntersectRayWithSphere( const Vector &vecRayOrigin, const Vector &vecRayDelta, + const Vector &vecSphereCenter, float flRadius, float *pT1, float *pT2 ) +{ + if ( !IntersectInfiniteRayWithSphere( vecRayOrigin, vecRayDelta, vecSphereCenter, flRadius, pT1, pT2 ) ) + return false; + + if (( *pT1 > 1.0f ) || ( *pT2 < 0.0f )) + return false; + + // Clamp it! + if ( *pT1 < 0.0f ) + *pT1 = 0.0f; + if ( *pT2 > 1.0f ) + *pT2 = 1.0f; + + return true; +} + + +//----------------------------------------------------------------------------- +// returns true if the point is in the box +//----------------------------------------------------------------------------- +bool IsPointInBox( const Vector& pt, const Vector& boxMin, const Vector& boxMax ) +{ + Assert( boxMin[0] <= boxMax[0] ); + Assert( boxMin[1] <= boxMax[1] ); + Assert( boxMin[2] <= boxMax[2] ); + + if ( (pt[0] > boxMax[0]) || (pt[0] < boxMin[0]) ) + return false; + if ( (pt[1] > boxMax[1]) || (pt[1] < boxMin[1]) ) + return false; + if ( (pt[2] > boxMax[2]) || (pt[2] < boxMin[2]) ) + return false; + return true; +} + + +bool IsPointInCone( const Vector &pt, const Vector &origin, const Vector &axis, float cosAngle, float length ) +{ + Vector delta = pt - origin; + float dist = VectorNormalize( delta ); + float dot = DotProduct( delta, axis ); + if ( dot < cosAngle ) + return false; + if ( dist * dot > length ) + return false; + + return true; +} + + +//----------------------------------------------------------------------------- +// returns true if there's an intersection between two boxes +//----------------------------------------------------------------------------- +bool IsBoxIntersectingBox( const Vector& boxMin1, const Vector& boxMax1, + const Vector& boxMin2, const Vector& boxMax2 ) +{ + Assert( boxMin1[0] <= boxMax1[0] ); + Assert( boxMin1[1] <= boxMax1[1] ); + Assert( boxMin1[2] <= boxMax1[2] ); + Assert( boxMin2[0] <= boxMax2[0] ); + Assert( boxMin2[1] <= boxMax2[1] ); + Assert( boxMin2[2] <= boxMax2[2] ); + + if ( (boxMin1[0] > boxMax2[0]) || (boxMax1[0] < boxMin2[0]) ) + return false; + if ( (boxMin1[1] > boxMax2[1]) || (boxMax1[1] < boxMin2[1]) ) + return false; + if ( (boxMin1[2] > boxMax2[2]) || (boxMax1[2] < boxMin2[2]) ) + return false; + return true; +} + +bool IsBoxIntersectingBoxExtents( const Vector& boxCenter1, const Vector& boxHalfDiagonal1, + const Vector& boxCenter2, const Vector& boxHalfDiagonal2 ) +{ + Vector vecDelta, vecSize; + VectorSubtract( boxCenter1, boxCenter2, vecDelta ); + VectorAdd( boxHalfDiagonal1, boxHalfDiagonal2, vecSize ); + return ( FloatMakePositive( vecDelta.x ) <= vecSize.x ) && + ( FloatMakePositive( vecDelta.y ) <= vecSize.y ) && + ( FloatMakePositive( vecDelta.z ) <= vecSize.z ); +} + + +//----------------------------------------------------------------------------- +// +// IsOBBIntersectingOBB +// +// returns true if there's an intersection between two OBBs +// +//----------------------------------------------------------------------------- +bool IsOBBIntersectingOBB( const Vector &vecOrigin1, const QAngle &vecAngles1, const Vector& boxMin1, const Vector& boxMax1, + const Vector &vecOrigin2, const QAngle &vecAngles2, const Vector& boxMin2, const Vector& boxMax2, float flTolerance ) +{ + // FIXME: Simple case AABB check doesn't work because the min and max extents are not oriented based on the angle + // this fast check would only be good for cubes. + /*if ( vecAngles1 == vecAngles2 ) + { + const Vector &vecDelta = vecOrigin2 - vecOrigin1; + Vector vecOtherMins, vecOtherMaxs; + VectorAdd( boxMin2, vecDelta, vecOtherMins ); + VectorAdd( boxMax2, vecDelta, vecOtherMaxs ); + return IsBoxIntersectingBox( boxMin1, boxMax1, vecOtherMins, vecOtherMaxs ); + }*/ + + // OBB test... + cplane_t plane; + bool bFoundPlane = ComputeSeparatingPlane( vecOrigin1, vecAngles1, boxMin1, boxMax1, + vecOrigin2, vecAngles2, boxMin2, boxMax2, flTolerance, &plane ); + return (bFoundPlane == false); +} + +//----------------------------------------------------------------------------- +// returns true if there's an intersection between box and ray +//----------------------------------------------------------------------------- +bool FASTCALL IsBoxIntersectingRay( const Vector& boxMin, const Vector& boxMax, + const Vector& origin, const Vector& delta, float flTolerance ) +{ + Assert( boxMin[0] <= boxMax[0] ); + Assert( boxMin[1] <= boxMax[1] ); + Assert( boxMin[2] <= boxMax[2] ); + + // FIXME: Surely there's a faster way + float tmin = -FLT_MAX; + float tmax = FLT_MAX; + + for (int i = 0; i < 3; ++i) + { + // Parallel case... + if (FloatMakePositive(delta[i]) < 1e-8) + { + // Check that origin is in the box + // if not, then it doesn't intersect.. + if ( (origin[i] < boxMin[i] - flTolerance) || (origin[i] > boxMax[i] + flTolerance) ) + return false; + + continue; + } + + // non-parallel case + // Find the t's corresponding to the entry and exit of + // the ray along x, y, and z. The find the furthest entry + // point, and the closest exit point. Once that is done, + // we know we don't collide if the closest exit point + // is behind the starting location. We also don't collide if + // the closest exit point is in front of the furthest entry point + + float invDelta = 1.0f / delta[i]; + float t1 = (boxMin[i] - flTolerance - origin[i]) * invDelta; + float t2 = (boxMax[i] + flTolerance - origin[i]) * invDelta; + if (t1 > t2) + { + float temp = t1; + t1 = t2; + t2 = temp; + } + if (t1 > tmin) + tmin = t1; + if (t2 < tmax) + tmax = t2; + if (tmin > tmax) + return false; + if (tmax < 0) + return false; + if (tmin > 1) + return false; + } + + return true; +} + +//----------------------------------------------------------------------------- +// returns true if there's an intersection between box and ray +//----------------------------------------------------------------------------- +bool FASTCALL IsBoxIntersectingRay( const Vector& boxMin, const Vector& boxMax, + const Vector& origin, const Vector& delta, + const Vector& invDelta, float flTolerance ) +{ + Assert( boxMin[0] <= boxMax[0] ); + Assert( boxMin[1] <= boxMax[1] ); + Assert( boxMin[2] <= boxMax[2] ); + + // FIXME: Surely there's a faster way + float tmin = -FLT_MAX; + float tmax = FLT_MAX; + + for ( int i = 0; i < 3; ++i ) + { + // Parallel case... + if ( FloatMakePositive( delta[i] ) < 1e-8 ) + { + // Check that origin is in the box, if not, then it doesn't intersect.. + if ( ( origin[i] < boxMin[i] - flTolerance ) || ( origin[i] > boxMax[i] + flTolerance ) ) + return false; + + continue; + } + + // Non-parallel case + // Find the t's corresponding to the entry and exit of + // the ray along x, y, and z. The find the furthest entry + // point, and the closest exit point. Once that is done, + // we know we don't collide if the closest exit point + // is behind the starting location. We also don't collide if + // the closest exit point is in front of the furthest entry point + float t1 = ( boxMin[i] - flTolerance - origin[i] ) * invDelta[i]; + float t2 = ( boxMax[i] + flTolerance - origin[i] ) * invDelta[i]; + if ( t1 > t2 ) + { + float temp = t1; + t1 = t2; + t2 = temp; + } + + if (t1 > tmin) + tmin = t1; + + if (t2 < tmax) + tmax = t2; + + if (tmin > tmax) + return false; + + if (tmax < 0) + return false; + + if (tmin > 1) + return false; + } + + return true; +} + +//----------------------------------------------------------------------------- +// Intersects a ray with a aabb, return true if they intersect +//----------------------------------------------------------------------------- +bool FASTCALL IsBoxIntersectingRay( const Vector& vecBoxMin, const Vector& vecBoxMax, const Ray_t& ray, float flTolerance ) +{ + if ( !ray.m_IsSwept ) + { + Vector rayMins, rayMaxs; + VectorSubtract( ray.m_Start, ray.m_Extents, rayMins ); + VectorAdd( ray.m_Start, ray.m_Extents, rayMaxs ); + if ( flTolerance != 0.0f ) + { + rayMins.x -= flTolerance; rayMins.y -= flTolerance; rayMins.z -= flTolerance; + rayMaxs.x += flTolerance; rayMaxs.y += flTolerance; rayMaxs.z += flTolerance; + } + return IsBoxIntersectingBox( vecBoxMin, vecBoxMax, rayMins, rayMaxs ); + } + + Vector vecExpandedBoxMin, vecExpandedBoxMax; + VectorSubtract( vecBoxMin, ray.m_Extents, vecExpandedBoxMin ); + VectorAdd( vecBoxMax, ray.m_Extents, vecExpandedBoxMax ); + return IsBoxIntersectingRay( vecExpandedBoxMin, vecExpandedBoxMax, ray.m_Start, ray.m_Delta, flTolerance ); +} + + +//----------------------------------------------------------------------------- +// Intersects a ray with a ray, return true if they intersect +// t, s = parameters of closest approach (if not intersecting!) +//----------------------------------------------------------------------------- +bool IntersectRayWithRay( const Ray_t &ray0, const Ray_t &ray1, float &t, float &s ) +{ + Assert( ray0.m_IsRay && ray1.m_IsRay ); + + // + // r0 = p0 + v0t + // r1 = p1 + v1s + // + // intersection : r0 = r1 :: p0 + v0t = p1 + v1s + // NOTE: v(0,1) are unit direction vectors + // + // subtract p0 from both sides and cross with v1 (NOTE: v1 x v1 = 0) + // (v0 x v1)t = ((p1 - p0 ) x v1) + // + // dotting with (v0 x v1) and dividing by |v0 x v1|^2 + // t = Det | (p1 - p0) , v1 , (v0 x v1) | / |v0 x v1|^2 + // s = Det | (p1 - p0) , v0 , (v0 x v1) | / |v0 x v1|^2 + // + // Det | A B C | = -( A x C ) dot B or -( C x B ) dot A + // + // NOTE: if |v0 x v1|^2 = 0, then the lines are parallel + // + Vector v0( ray0.m_Delta ); + Vector v1( ray1.m_Delta ); + VectorNormalize( v0 ); + VectorNormalize( v1 ); + + Vector v0xv1 = v0.Cross( v1 ); + float length = v0xv1.Length(); + if( length == 0.0f ) + { + t = 0; s = 0; + return false; // parallel + } + + Vector p1p0 = ray1.m_Start - ray0.m_Start; + + Vector AxC = p1p0.Cross( v0xv1 ); + AxC.Negate(); + float detT = AxC.Dot( v1 ); + + AxC = p1p0.Cross( v0xv1 ); + AxC.Negate(); + float detS = AxC.Dot( v0 ); + + t = detT / ( length * length ); + s = detS / ( length * length ); + + // intersection???? + Vector i0, i1; + i0 = v0 * t; + i1 = v1 * s; + i0 += ray0.m_Start; + i1 += ray1.m_Start; + if( i0.x == i1.x && i0.y == i1.y && i0.z == i1.z ) + return true; + + return false; +} + + +//----------------------------------------------------------------------------- +// Intersects a ray with a plane, returns distance t along ray. +//----------------------------------------------------------------------------- +float IntersectRayWithPlane( const Ray_t& ray, const cplane_t& plane ) +{ + float denom = DotProduct( ray.m_Delta, plane.normal ); + if (denom == 0.0f) + return 0.0f; + + denom = 1.0f / denom; + return (plane.dist - DotProduct( ray.m_Start, plane.normal )) * denom; +} + +float IntersectRayWithPlane( const Vector& org, const Vector& dir, const cplane_t& plane ) +{ + float denom = DotProduct( dir, plane.normal ); + if (denom == 0.0f) + return 0.0f; + + denom = 1.0f / denom; + return (plane.dist - DotProduct( org, plane.normal )) * denom; +} + +float IntersectRayWithPlane( const Vector& org, const Vector& dir, const Vector& normal, float dist ) +{ + float denom = DotProduct( dir, normal ); + if (denom == 0.0f) + return 0.0f; + + denom = 1.0f / denom; + return (dist - DotProduct( org, normal )) * denom; +} + +float IntersectRayWithAAPlane( const Vector& vecStart, const Vector& vecEnd, int nAxis, float flSign, float flDist ) +{ + float denom = flSign * (vecEnd[nAxis] - vecStart[nAxis]); + if (denom == 0.0f) + return 0.0f; + + denom = 1.0f / denom; + return (flDist - flSign * vecStart[nAxis]) * denom; +} + + +//----------------------------------------------------------------------------- +// Intersects a ray against a box +//----------------------------------------------------------------------------- +bool IntersectRayWithBox( const Vector &vecRayStart, const Vector &vecRayDelta, + const Vector &boxMins, const Vector &boxMaxs, float flTolerance, BoxTraceInfo_t *pTrace ) +{ + int i; + float d1, d2; + float f; + + pTrace->t1 = -1.0f; + pTrace->t2 = 1.0f; + pTrace->hitside = -1; + + // UNDONE: This makes this code a little messy + pTrace->startsolid = true; + + for ( i = 0; i < 6; ++i ) + { + if ( i >= 3 ) + { + d1 = vecRayStart[i-3] - boxMaxs[i-3]; + d2 = d1 + vecRayDelta[i-3]; + } + else + { + d1 = -vecRayStart[i] + boxMins[i]; + d2 = d1 - vecRayDelta[i]; + } + + // if completely in front of face, no intersection + if (d1 > 0 && d2 > 0) + { + // UNDONE: Have to revert this in case it's still set + // UNDONE: Refactor to have only 2 return points (true/false) from this function + pTrace->startsolid = false; + return false; + } + + // completely inside, check next face + if (d1 <= 0 && d2 <= 0) + continue; + + if (d1 > 0) + { + pTrace->startsolid = false; + } + + // crosses face + if (d1 > d2) + { + f = d1 - flTolerance; + if ( f < 0 ) + { + f = 0; + } + f = f / (d1-d2); + if (f > pTrace->t1) + { + pTrace->t1 = f; + pTrace->hitside = i; + } + } + else + { + // leave + f = (d1 + flTolerance) / (d1-d2); + if (f < pTrace->t2) + { + pTrace->t2 = f; + } + } + } + + return pTrace->startsolid || (pTrace->t1 < pTrace->t2 && pTrace->t1 >= 0.0f); +} + + +//----------------------------------------------------------------------------- +// Intersects a ray against a box +//----------------------------------------------------------------------------- +bool IntersectRayWithBox( const Vector &vecRayStart, const Vector &vecRayDelta, + const Vector &boxMins, const Vector &boxMaxs, float flTolerance, CBaseTrace *pTrace ) +{ + Collision_ClearTrace( vecRayStart, vecRayDelta, pTrace ); + + BoxTraceInfo_t trace; + + if ( IntersectRayWithBox( vecRayStart, vecRayDelta, boxMins, boxMaxs, flTolerance, &trace ) ) + { + pTrace->startsolid = trace.startsolid; + if (trace.t1 < trace.t2 && trace.t1 >= 0.0f) + { + pTrace->fraction = trace.t1; + VectorMA( pTrace->startpos, trace.t1, vecRayDelta, pTrace->endpos ); + pTrace->contents = CONTENTS_SOLID; + pTrace->plane.normal = vec3_origin; + if ( trace.hitside >= 3 ) + { + trace.hitside -= 3; + pTrace->plane.dist = boxMaxs[trace.hitside]; + pTrace->plane.normal[trace.hitside] = 1.0f; + pTrace->plane.type = trace.hitside; + } + else + { + pTrace->plane.dist = -boxMins[trace.hitside]; + pTrace->plane.normal[trace.hitside] = -1.0f; + pTrace->plane.type = trace.hitside; + } + return true; + } + + if ( pTrace->startsolid ) + { + pTrace->allsolid = (trace.t2 <= 0.0f) || (trace.t2 >= 1.0f); + pTrace->fraction = 0; + pTrace->endpos = pTrace->startpos; + pTrace->contents = CONTENTS_SOLID; + pTrace->plane.dist = pTrace->startpos[0]; + pTrace->plane.normal.Init( 1.0f, 0.0f, 0.0f ); + pTrace->plane.type = 0; + return true; + } + } + + return false; +} + + +//----------------------------------------------------------------------------- +// Intersects a ray against a box +//----------------------------------------------------------------------------- +bool IntersectRayWithBox( const Ray_t &ray, const Vector &boxMins, const Vector &boxMaxs, + float flTolerance, CBaseTrace *pTrace ) +{ + Vector vecExpandedMins = boxMins; + Vector vecExpandedMaxs = boxMaxs; + + if ( !ray.m_IsRay ) + { + vecExpandedMins -= ray.m_Extents; + vecExpandedMaxs += ray.m_Extents; + } + + bool bIntersects = IntersectRayWithBox( ray.m_Start, ray.m_Delta, vecExpandedMins, vecExpandedMaxs, flTolerance, pTrace ); + + if ( !ray.m_IsRay ) + { + pTrace->startpos += ray.m_StartOffset; + pTrace->endpos += ray.m_StartOffset; + } + + return bIntersects; +} + + +//----------------------------------------------------------------------------- +// Intersects a ray against an OBB, returns t1 and t2 +//----------------------------------------------------------------------------- +bool IntersectRayWithOBB( const Vector &vecRayStart, const Vector &vecRayDelta, + const matrix3x4_t &matOBBToWorld, const Vector &vecOBBMins, const Vector &vecOBBMaxs, + float flTolerance, BoxTraceInfo_t *pTrace ) +{ + // FIXME: Two transforms is pretty expensive. Should we optimize this? + Vector start, delta; + VectorITransform( vecRayStart, matOBBToWorld, start ); + VectorIRotate( vecRayDelta, matOBBToWorld, delta ); + + return IntersectRayWithBox( start, delta, vecOBBMins, vecOBBMaxs, flTolerance, pTrace ); +} + + + +//----------------------------------------------------------------------------- +// Intersects a ray against an OBB +//----------------------------------------------------------------------------- +bool IntersectRayWithOBB( const Vector &vecRayStart, const Vector &vecRayDelta, + const matrix3x4_t &matOBBToWorld, const Vector &vecOBBMins, const Vector &vecOBBMaxs, + float flTolerance, CBaseTrace *pTrace ) +{ + Collision_ClearTrace( vecRayStart, vecRayDelta, pTrace ); + + // FIXME: Make it work with tolerance + Assert( flTolerance == 0.0f ); + + // OPTIMIZE: Store this in the box instead of computing it here + // compute center in local space + Vector vecBoxExtents = (vecOBBMins + vecOBBMaxs) * 0.5; + Vector vecBoxCenter; + + // transform to world space + VectorTransform( vecBoxExtents, matOBBToWorld, vecBoxCenter ); + + // calc extents from local center + vecBoxExtents = vecOBBMaxs - vecBoxExtents; + + // OPTIMIZE: This is optimized for world space. If the transform is fast enough, it may make more + // sense to just xform and call UTIL_ClipToBox() instead. MEASURE THIS. + + // save the extents of the ray along + Vector extent, uextent; + Vector segmentCenter = vecRayStart + vecRayDelta - vecBoxCenter; + + extent.Init(); + + // check box axes for separation + for ( int j = 0; j < 3; j++ ) + { + extent[j] = vecRayDelta.x * matOBBToWorld[0][j] + vecRayDelta.y * matOBBToWorld[1][j] + vecRayDelta.z * matOBBToWorld[2][j]; + uextent[j] = fabsf(extent[j]); + float coord = segmentCenter.x * matOBBToWorld[0][j] + segmentCenter.y * matOBBToWorld[1][j] + segmentCenter.z * matOBBToWorld[2][j]; + coord = fabsf(coord); + + if ( coord > (vecBoxExtents[j] + uextent[j]) ) + return false; + } + + // now check cross axes for separation + float tmp, cextent; + Vector cross = vecRayDelta.Cross( segmentCenter ); + cextent = cross.x * matOBBToWorld[0][0] + cross.y * matOBBToWorld[1][0] + cross.z * matOBBToWorld[2][0]; + cextent = fabsf(cextent); + tmp = vecBoxExtents[1]*uextent[2] + vecBoxExtents[2]*uextent[1]; + if ( cextent > tmp ) + return false; + + cextent = cross.x * matOBBToWorld[0][1] + cross.y * matOBBToWorld[1][1] + cross.z * matOBBToWorld[2][1]; + cextent = fabsf(cextent); + tmp = vecBoxExtents[0]*uextent[2] + vecBoxExtents[2]*uextent[0]; + if ( cextent > tmp ) + return false; + + cextent = cross.x * matOBBToWorld[0][2] + cross.y * matOBBToWorld[1][2] + cross.z * matOBBToWorld[2][2]; + cextent = fabsf(cextent); + tmp = vecBoxExtents[0]*uextent[1] + vecBoxExtents[1]*uextent[0]; + if ( cextent > tmp ) + return false; + + // !!! We hit this box !!! compute intersection point and return + // Compute ray start in bone space + Vector start; + VectorITransform( vecRayStart, matOBBToWorld, start ); + + // extent is ray.m_Delta in bone space, recompute delta in bone space + extent *= 2.0f; + + // delta was prescaled by the current t, so no need to see if this intersection + // is closer + trace_t boxTrace; + if ( !IntersectRayWithBox( start, extent, vecOBBMins, vecOBBMaxs, flTolerance, pTrace ) ) + return false; + + // Fix up the plane information + float flSign = pTrace->plane.normal[ pTrace->plane.type ]; + pTrace->plane.normal[0] = flSign * matOBBToWorld[0][pTrace->plane.type]; + pTrace->plane.normal[1] = flSign * matOBBToWorld[1][pTrace->plane.type]; + pTrace->plane.normal[2] = flSign * matOBBToWorld[2][pTrace->plane.type]; + pTrace->plane.dist = DotProduct( pTrace->endpos, pTrace->plane.normal ); + pTrace->plane.type = 3; + return true; +} + + +//----------------------------------------------------------------------------- +// Intersects a ray against an OBB +//----------------------------------------------------------------------------- +bool IntersectRayWithOBB( const Vector &vecRayOrigin, const Vector &vecRayDelta, + const Vector &vecBoxOrigin, const QAngle &angBoxRotation, + const Vector &vecOBBMins, const Vector &vecOBBMaxs, float flTolerance, CBaseTrace *pTrace ) +{ + if (angBoxRotation == vec3_angle) + { + Vector vecAbsMins, vecAbsMaxs; + VectorAdd( vecBoxOrigin, vecOBBMins, vecAbsMins ); + VectorAdd( vecBoxOrigin, vecOBBMaxs, vecAbsMaxs ); + return IntersectRayWithBox( vecRayOrigin, vecRayDelta, vecAbsMins, vecAbsMaxs, flTolerance, pTrace ); + } + + matrix3x4_t obbToWorld; + AngleMatrix( angBoxRotation, vecBoxOrigin, obbToWorld ); + return IntersectRayWithOBB( vecRayOrigin, vecRayDelta, obbToWorld, vecOBBMins, vecOBBMaxs, flTolerance, pTrace ); +} + + +//----------------------------------------------------------------------------- +// Box support map +//----------------------------------------------------------------------------- +inline void ComputeSupportMap( const Vector &vecDirection, const Vector &vecBoxMins, + const Vector &vecBoxMaxs, float pDist[2] ) +{ + int nIndex = (vecDirection.x > 0.0f); + pDist[nIndex] = vecBoxMaxs.x * vecDirection.x; + pDist[1 - nIndex] = vecBoxMins.x * vecDirection.x; + + nIndex = (vecDirection.y > 0.0f); + pDist[nIndex] += vecBoxMaxs.y * vecDirection.y; + pDist[1 - nIndex] += vecBoxMins.y * vecDirection.y; + + nIndex = (vecDirection.z > 0.0f); + pDist[nIndex] += vecBoxMaxs.z * vecDirection.z; + pDist[1 - nIndex] += vecBoxMins.z * vecDirection.z; +} + +inline void ComputeSupportMap( const Vector &vecDirection, int i1, int i2, + const Vector &vecBoxMins, const Vector &vecBoxMaxs, float pDist[2] ) +{ + int nIndex = (vecDirection[i1] > 0.0f); + pDist[nIndex] = vecBoxMaxs[i1] * vecDirection[i1]; + pDist[1 - nIndex] = vecBoxMins[i1] * vecDirection[i1]; + + nIndex = (vecDirection[i2] > 0.0f); + pDist[nIndex] += vecBoxMaxs[i2] * vecDirection[i2]; + pDist[1 - nIndex] += vecBoxMins[i2] * vecDirection[i2]; +} + +//----------------------------------------------------------------------------- +// Intersects a ray against an OBB +//----------------------------------------------------------------------------- +static int s_ExtIndices[3][2] = +{ + { 2, 1 }, + { 0, 2 }, + { 0, 1 }, +}; + +static int s_MatIndices[3][2] = +{ + { 1, 2 }, + { 2, 0 }, + { 1, 0 }, +}; + +bool IntersectRayWithOBB( const Ray_t &ray, const matrix3x4_t &matOBBToWorld, + const Vector &vecOBBMins, const Vector &vecOBBMaxs, float flTolerance, CBaseTrace *pTrace ) +{ + if ( ray.m_IsRay ) + { + return IntersectRayWithOBB( ray.m_Start, ray.m_Delta, matOBBToWorld, + vecOBBMins, vecOBBMaxs, flTolerance, pTrace ); + } + + Collision_ClearTrace( ray.m_Start + ray.m_StartOffset, ray.m_Delta, pTrace ); + + // Compute a bounding sphere around the bloated OBB + Vector vecOBBCenter; + VectorAdd( vecOBBMins, vecOBBMaxs, vecOBBCenter ); + vecOBBCenter *= 0.5f; + vecOBBCenter.x += matOBBToWorld[0][3]; + vecOBBCenter.y += matOBBToWorld[1][3]; + vecOBBCenter.z += matOBBToWorld[2][3]; + + Vector vecOBBHalfDiagonal; + VectorSubtract( vecOBBMaxs, vecOBBMins, vecOBBHalfDiagonal ); + vecOBBHalfDiagonal *= 0.5f; + + float flRadius = vecOBBHalfDiagonal.Length() + ray.m_Extents.Length(); + if ( !IsRayIntersectingSphere( ray.m_Start, ray.m_Delta, vecOBBCenter, flRadius, flTolerance ) ) + return false; + + // Ok, we passed the trivial reject, so lets do the dirty deed. + // Basically we're going to do the GJK thing explicitly. We'll shrink the ray down + // to a point, and bloat the OBB by the ray's extents. This will generate facet + // planes which are perpendicular to all of the separating axes typically seen in + // a standard seperating axis implementation. + + // We're going to create a number of planes through various vertices in the OBB + // which represent all of the separating planes. Then we're going to bloat the planes + // by the ray extents. + + // We're going to do all work in OBB-space because it's easier to do the + // support-map in this case + + // First, transform the ray into the space of the OBB + Vector vecLocalRayOrigin, vecLocalRayDirection; + VectorITransform( ray.m_Start, matOBBToWorld, vecLocalRayOrigin ); + VectorIRotate( ray.m_Delta, matOBBToWorld, vecLocalRayDirection ); + + // Next compute all separating planes + Vector pPlaneNormal[15]; + float ppPlaneDist[15][2]; + + int i; + for ( i = 0; i < 3; ++i ) + { + // Each plane needs to be bloated an amount = to the abs dot product of + // the ray extents with the plane normal + // For the OBB planes, do it in world space; + // and use the direction of the OBB (the ith column of matOBBToWorld) in world space vs extents + pPlaneNormal[i].Init( ); + pPlaneNormal[i][i] = 1.0f; + + float flExtentDotNormal = + FloatMakePositive( matOBBToWorld[0][i] * ray.m_Extents.x ) + + FloatMakePositive( matOBBToWorld[1][i] * ray.m_Extents.y ) + + FloatMakePositive( matOBBToWorld[2][i] * ray.m_Extents.z ); + + ppPlaneDist[i][0] = vecOBBMins[i] - flExtentDotNormal; + ppPlaneDist[i][1] = vecOBBMaxs[i] + flExtentDotNormal; + + // For the ray-extents planes, they are bloated by the extents + // Use the support map to determine which + VectorCopy( matOBBToWorld[i], pPlaneNormal[i+3].Base() ); + ComputeSupportMap( pPlaneNormal[i+3], vecOBBMins, vecOBBMaxs, ppPlaneDist[i+3] ); + ppPlaneDist[i+3][0] -= ray.m_Extents[i]; + ppPlaneDist[i+3][1] += ray.m_Extents[i]; + + // Now the edge cases... (take the cross product of x,y,z axis w/ ray extent axes + // given by the rows of the obb to world matrix. + // Compute the ray extent bloat in world space because it's easier... + + // These are necessary to compute the world-space versions of + // the edges so we can compute the extent dot products + float flRayExtent0 = ray.m_Extents[s_ExtIndices[i][0]]; + float flRayExtent1 = ray.m_Extents[s_ExtIndices[i][1]]; + const float *pMatRow0 = matOBBToWorld[s_MatIndices[i][0]]; + const float *pMatRow1 = matOBBToWorld[s_MatIndices[i][1]]; + + // x axis of the OBB + world ith axis + pPlaneNormal[i+6].Init( 0.0f, -matOBBToWorld[i][2], matOBBToWorld[i][1] ); + ComputeSupportMap( pPlaneNormal[i+6], 1, 2, vecOBBMins, vecOBBMaxs, ppPlaneDist[i+6] ); + flExtentDotNormal = + FloatMakePositive( pMatRow0[0] ) * flRayExtent0 + + FloatMakePositive( pMatRow1[0] ) * flRayExtent1; + ppPlaneDist[i+6][0] -= flExtentDotNormal; + ppPlaneDist[i+6][1] += flExtentDotNormal; + + // y axis of the OBB + world ith axis + pPlaneNormal[i+9].Init( matOBBToWorld[i][2], 0.0f, -matOBBToWorld[i][0] ); + ComputeSupportMap( pPlaneNormal[i+9], 0, 2, vecOBBMins, vecOBBMaxs, ppPlaneDist[i+9] ); + flExtentDotNormal = + FloatMakePositive( pMatRow0[1] ) * flRayExtent0 + + FloatMakePositive( pMatRow1[1] ) * flRayExtent1; + ppPlaneDist[i+9][0] -= flExtentDotNormal; + ppPlaneDist[i+9][1] += flExtentDotNormal; + + // z axis of the OBB + world ith axis + pPlaneNormal[i+12].Init( -matOBBToWorld[i][1], matOBBToWorld[i][0], 0.0f ); + ComputeSupportMap( pPlaneNormal[i+12], 0, 1, vecOBBMins, vecOBBMaxs, ppPlaneDist[i+12] ); + flExtentDotNormal = + FloatMakePositive( pMatRow0[2] ) * flRayExtent0 + + FloatMakePositive( pMatRow1[2] ) * flRayExtent1; + ppPlaneDist[i+12][0] -= flExtentDotNormal; + ppPlaneDist[i+12][1] += flExtentDotNormal; + } + + float enterfrac, leavefrac; + float d1[2], d2[2]; + float f; + + int hitplane = -1; + int hitside = -1; + enterfrac = -1.0f; + leavefrac = 1.0f; + + pTrace->startsolid = true; + + Vector vecLocalRayEnd; + VectorAdd( vecLocalRayOrigin, vecLocalRayDirection, vecLocalRayEnd ); + + for ( i = 0; i < 15; ++i ) + { + // FIXME: Not particularly optimal since there's a lot of 0's in the plane normals + float flStartDot = DotProduct( pPlaneNormal[i], vecLocalRayOrigin ); + float flEndDot = DotProduct( pPlaneNormal[i], vecLocalRayEnd ); + + // NOTE: Negative here is because the plane normal + dist + // are defined in negative terms for the far plane (plane dist index 0) + d1[0] = -(flStartDot - ppPlaneDist[i][0]); + d2[0] = -(flEndDot - ppPlaneDist[i][0]); + + d1[1] = flStartDot - ppPlaneDist[i][1]; + d2[1] = flEndDot - ppPlaneDist[i][1]; + + int j; + for ( j = 0; j < 2; ++j ) + { + // if completely in front near plane or behind far plane no intersection + if (d1[j] > 0 && d2[j] > 0) + return false; + + // completely inside, check next plane set + if (d1[j] <= 0 && d2[j] <= 0) + continue; + + if (d1[j] > 0) + { + pTrace->startsolid = false; + } + + // crosses face + float flDenom = 1.0f / (d1[j] - d2[j]); + if (d1[j] > d2[j]) + { + f = d1[j] - flTolerance; + if ( f < 0 ) + { + f = 0; + } + f *= flDenom; + if (f > enterfrac) + { + enterfrac = f; + hitplane = i; + hitside = j; + } + } + else + { + // leave + f = (d1[j] + flTolerance) * flDenom; + if (f < leavefrac) + { + leavefrac = f; + } + } + } + } + + if (enterfrac < leavefrac && enterfrac >= 0.0f) + { + pTrace->fraction = enterfrac; + VectorMA( pTrace->startpos, enterfrac, ray.m_Delta, pTrace->endpos ); + pTrace->contents = CONTENTS_SOLID; + + // Need to transform the plane into world space... + cplane_t temp; + temp.normal = pPlaneNormal[hitplane]; + temp.dist = ppPlaneDist[hitplane][hitside]; + if (hitside == 0) + { + temp.normal *= -1.0f; + temp.dist *= -1.0f; + } + temp.type = 3; + + MatrixITransformPlane( matOBBToWorld, temp, pTrace->plane ); + return true; + } + + if ( pTrace->startsolid ) + { + pTrace->allsolid = (leavefrac <= 0.0f) || (leavefrac >= 1.0f); + pTrace->fraction = 0; + pTrace->endpos = pTrace->startpos; + pTrace->contents = CONTENTS_SOLID; + pTrace->plane.dist = pTrace->startpos[0]; + pTrace->plane.normal.Init( 1.0f, 0.0f, 0.0f ); + pTrace->plane.type = 0; + return true; + } + + return false; +} + + +//----------------------------------------------------------------------------- +// Intersects a ray against an OBB +//----------------------------------------------------------------------------- +bool IntersectRayWithOBB( const Ray_t &ray, const Vector &vecBoxOrigin, const QAngle &angBoxRotation, + const Vector &vecOBBMins, const Vector &vecOBBMaxs, float flTolerance, CBaseTrace *pTrace ) +{ + if ( angBoxRotation == vec3_angle ) + { + Vector vecWorldMins, vecWorldMaxs; + VectorAdd( vecBoxOrigin, vecOBBMins, vecWorldMins ); + VectorAdd( vecBoxOrigin, vecOBBMaxs, vecWorldMaxs ); + return IntersectRayWithBox( ray, vecWorldMins, vecWorldMaxs, flTolerance, pTrace ); + } + + if ( ray.m_IsRay ) + { + return IntersectRayWithOBB( ray.m_Start, ray.m_Delta, vecBoxOrigin, angBoxRotation, vecOBBMins, vecOBBMaxs, flTolerance, pTrace ); + } + + matrix3x4_t matOBBToWorld; + AngleMatrix( angBoxRotation, vecBoxOrigin, matOBBToWorld ); + return IntersectRayWithOBB( ray, matOBBToWorld, vecOBBMins, vecOBBMaxs, flTolerance, pTrace ); +} + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void GetNonMajorAxes( const Vector &vNormal, Vector2D &axes ) +{ + axes[0] = 0; + axes[1] = 1; + + if( FloatMakePositive( vNormal.x ) > FloatMakePositive( vNormal.y ) ) + { + if( FloatMakePositive( vNormal.x ) > FloatMakePositive( vNormal.z ) ) + { + axes[0] = 1; + axes[1] = 2; + } + } + else + { + if( FloatMakePositive( vNormal.y ) > FloatMakePositive( vNormal.z ) ) + { + axes[0] = 0; + axes[1] = 2; + } + } +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +QuadBarycentricRetval_t QuadWithParallelEdges( const Vector &vecOrigin, + const Vector &vecU, float lengthU, const Vector &vecV, float lengthV, + const Vector &pt, Vector2D &vecUV ) +{ + Ray_t rayAxis; + Ray_t rayPt; + + // + // handle the u axis + // + rayAxis.m_Start = vecOrigin; + rayAxis.m_Delta = vecU; + rayAxis.m_IsRay = true; + + rayPt.m_Start = pt; + rayPt.m_Delta = vecV * -( lengthV * 10.0f ); + rayPt.m_IsRay = true; + + float s, t; + IntersectRayWithRay( rayAxis, rayPt, t, s ); + vecUV[0] = t / lengthU; + + // + // handle the v axis + // + rayAxis.m_Delta = vecV; + + rayPt.m_Delta = vecU * -( lengthU * 10.0f ); + + IntersectRayWithRay( rayAxis, rayPt, t, s ); + vecUV[1] = t / lengthV; + + // inside of the quad?? + if( ( vecUV[0] < 0.0f ) || ( vecUV[0] > 1.0f ) || + ( vecUV[1] < 0.0f ) || ( vecUV[1] > 1.0f ) ) + return BARY_QUADRATIC_FALSE; + + return BARY_QUADRATIC_TRUE; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void ResolveQuadratic( double tPlus, double tMinus, + const Vector axisU0, const Vector axisU1, + const Vector axisV0, const Vector axisV1, + const Vector axisOrigin, const Vector pt, + int projU, double &s, double &t ) +{ + // calculate the sPlus, sMinus pair(s) + double sDenomPlus = ( axisU0[projU] * ( 1 - tPlus ) ) + ( axisU1[projU] * tPlus ); + double sDenomMinus = ( axisU0[projU] * ( 1 - tMinus ) ) + ( axisU1[projU] * tMinus ); + + double sPlus = UNINIT, sMinus = UNINIT; + if( FloatMakePositive( sDenomPlus ) >= 1e-5 ) + { + sPlus = ( pt[projU] - axisOrigin[projU] - ( axisV0[projU] * tPlus ) ) / sDenomPlus; + } + + if( FloatMakePositive( sDenomMinus ) >= 1e-5 ) + { + sMinus = ( pt[projU] - axisOrigin[projU] - ( axisV0[projU] * tMinus ) ) / sDenomMinus; + } + + if( ( tPlus >= 0.0 ) && ( tPlus <= 1.0 ) && ( sPlus >= 0.0 ) && ( sPlus <= 1.0 ) ) + { + s = sPlus; + t = tPlus; + return; + } + + if( ( tMinus >= 0.0 ) && ( tMinus <= 1.0 ) && ( sMinus >= 0.0 ) && ( sMinus <= 1.0 ) ) + { + s = sMinus; + t = tMinus; + return; + } + + double s0, t0, s1, t1; + + s0 = sPlus; + t0 = tPlus; + if( s0 >= 1.0 ) { s0 -= 1.0; } + if( t0 >= 1.0 ) { t0 -= 1.0; } + + s1 = sMinus; + t1 = tMinus; + if( s1 >= 1.0 ) { s1 -= 1.0; } + if( t1 >= 1.0 ) { t1 -= 1.0; } + + s0 = FloatMakePositive( s0 ); + t0 = FloatMakePositive( t0 ); + s1 = FloatMakePositive( s1 ); + t1 = FloatMakePositive( t1 ); + + double max0, max1; + max0 = s0; + if( t0 > max0 ) { max0 = t0; } + max1 = s1; + if( t1 > max1 ) { max1 = t1; } + + if( max0 > max1 ) + { + s = sMinus; + t = tMinus; + } + else + { + s = sPlus; + t = tPlus; + } +} + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- + +QuadBarycentricRetval_t PointInQuadToBarycentric( const Vector &v1, const Vector &v2, + const Vector &v3, const Vector &v4, const Vector &point, Vector2D &uv ) +{ +#define PIQ_TEXTURE_EPSILON 0.001 +#define PIQ_PLANE_EPSILON 0.1 +#define PIQ_DOT_EPSILON 0.99f + + // + // Think of a quad with points v1, v2, v3, v4 and u, v line segments + // u0 = v2 - v1 + // u1 = v3 - v4 + // v0 = v4 - v1 + // v1 = v3 - v2 + // + Vector axisU[2], axisV[2]; + Vector axisUNorm[2], axisVNorm[2]; + axisU[0] = axisUNorm[0] = v2 - v1; + axisU[1] = axisUNorm[1] = v3 - v4; + axisV[0] = axisVNorm[0] = v4 - v1; + axisV[1] = axisVNorm[1] = v3 - v2; + + float lengthU[2], lengthV[2]; + lengthU[0] = VectorNormalize( axisUNorm[0] ); + lengthU[1] = VectorNormalize( axisUNorm[1] ); + lengthV[0] = VectorNormalize( axisVNorm[0] ); + lengthV[1] = VectorNormalize( axisVNorm[1] ); + + // + // check for an early out - parallel opposite edges! + // NOTE: quad property if 1 set of opposite edges is parallel and equal + // in length, then the other set of edges is as well + // + if( axisUNorm[0].Dot( axisUNorm[1] ) > PIQ_DOT_EPSILON ) + { + if( FloatMakePositive( lengthU[0] - lengthU[1] ) < PIQ_PLANE_EPSILON ) + { + return QuadWithParallelEdges( v1, axisUNorm[0], lengthU[0], axisVNorm[0], lengthV[0], point, uv ); + } + } + + // + // since we are solving for s in our equations below we need to ensure that + // the v axes are non-parallel + // + bool bFlipped = false; + if( axisVNorm[0].Dot( axisVNorm[1] ) > PIQ_DOT_EPSILON ) + { + Vector tmp[2]; + tmp[0] = axisV[0]; + tmp[1] = axisV[1]; + axisV[0] = axisU[0]; + axisV[1] = axisU[1]; + axisU[0] = tmp[0]; + axisU[1] = tmp[1]; + bFlipped = true; + } + + // + // get the "projection" axes + // + Vector2D projAxes; + Vector vNormal = axisU[0].Cross( axisV[0] ); + GetNonMajorAxes( vNormal, projAxes ); + + // + // NOTE: axisU[0][projAxes[0]] < axisU[0][projAxes[1]], + // this is done to decrease error when dividing later + // + if( FloatMakePositive( axisU[0][static_cast(projAxes[0])] ) < FloatMakePositive( axisU[0][static_cast(projAxes[1])] ) ) + { + int tmp = static_cast(projAxes[0]); + projAxes[0] = projAxes[1]; + projAxes[1] = tmp; + } + + // Here's how we got these equations: + // + // Given the points and u,v line segments above... + // + // Then: + // + // (1.0) PT = P0 + U0 * s + V * t + // + // where + // + // (1.1) V = V0 + s * (V1 - V0) + // (1.2) U = U0 + t * (U1 - U0) + // + // Therefore (from 1.1 + 1.0): + // PT - P0 = U0 * s + (V0 + s * (V1-V0)) * t + // Group s's: + // PT - P0 - t * V0 = s * (U0 + t * (V1-V0)) + // Two equations and two unknowns in x and y get you the following quadratic: + // + // solve the quadratic + // + double s = 0.0, t = 0.0; + double A, negB, C; + + A = ( axisU[0][static_cast(projAxes[1])] * axisV[0][static_cast(projAxes[0])] ) - + ( axisU[0][static_cast(projAxes[0])] * axisV[0][static_cast(projAxes[1])] ) - + ( axisU[1][static_cast(projAxes[1])] * axisV[0][static_cast(projAxes[0])] ) + + ( axisU[1][static_cast(projAxes[0])] * axisV[0][static_cast(projAxes[1])] ); + C = ( v1[static_cast(projAxes[1])] * axisU[0][static_cast(projAxes[0])] ) - + ( point[static_cast(projAxes[1])] * axisU[0][static_cast(projAxes[0])] ) - + ( v1[static_cast(projAxes[0])] * axisU[0][static_cast(projAxes[1])] ) + + ( point[static_cast(projAxes[0])] * axisU[0][static_cast(projAxes[1])] ); + negB = C - + ( v1[static_cast(projAxes[1])] * axisU[1][static_cast(projAxes[0])] ) + + ( point[static_cast(projAxes[1])] * axisU[1][static_cast(projAxes[0])] ) + + ( v1[static_cast(projAxes[0])] * axisU[1][static_cast(projAxes[1])] ) - + ( point[static_cast(projAxes[0])] * axisU[1][static_cast(projAxes[1])] ) + + ( axisU[0][static_cast(projAxes[1])] * axisV[0][static_cast(projAxes[0])] ) - + ( axisU[0][static_cast(projAxes[0])] * axisV[0][static_cast(projAxes[1])] ); + + if( ( A > -PIQ_PLANE_EPSILON ) && ( A < PIQ_PLANE_EPSILON ) ) + { + // shouldn't be here -- this should have been take care of in the "early out" +// Assert( 0 ); + + Vector vecUAvg, vecVAvg; + vecUAvg = ( axisUNorm[0] + axisUNorm[1] ) * 0.5f; + vecVAvg = ( axisVNorm[0] + axisVNorm[1] ) * 0.5f; + + float fLengthUAvg = ( lengthU[0] + lengthU[1] ) * 0.5f; + float fLengthVAvg = ( lengthV[0] + lengthV[1] ) * 0.5f; + + return QuadWithParallelEdges( v1, vecUAvg, fLengthUAvg, vecVAvg, fLengthVAvg, point, uv ); + +#if 0 + // legacy code -- kept here for completeness! + + // not a quadratic -- solve linearly + t = C / negB; + + // See (1.2) above + float ui = axisU[0][projAxes[0]] + t * ( axisU[1][projAxes[0]] - axisU[0][projAxes[0]] ); + if( FloatMakePositive( ui ) >= 1e-5 ) + { + // See (1.0) above + s = ( point[projAxes[0]] - v1[projAxes[0]] - axisV[0][projAxes[0]] * t ) / ui; + } +#endif + } + else + { + // (-b +/- sqrt( b^2 - 4ac )) / 2a + double discriminant = (negB*negB) - (4.0f * A * C); + if( discriminant < 0.0f ) + { + uv[0] = -99999.0f; + uv[1] = -99999.0f; + return BARY_QUADRATIC_NEGATIVE_DISCRIMINANT; + } + + double quad = sqrt( discriminant ); + double QPlus = ( negB + quad ) / ( 2.0f * A ); + double QMinus = ( negB - quad ) / ( 2.0f * A ); + + ResolveQuadratic( QPlus, QMinus, axisU[0], axisU[1], axisV[0], axisV[1], v1, point, static_cast(projAxes[0]), s, t ); + } + + if( !bFlipped ) + { + uv[0] = ( float )s; + uv[1] = ( float )t; + } + else + { + uv[0] = ( float )t; + uv[1] = ( float )s; + } + + // inside of the quad?? + if( ( uv[0] < 0.0f ) || ( uv[0] > 1.0f ) || ( uv[1] < 0.0f ) || ( uv[1] > 1.0f ) ) + return BARY_QUADRATIC_FALSE; + + return BARY_QUADRATIC_TRUE; + +#undef PIQ_TEXTURE_EPSILON +#undef PIQ_PLANE_EPSILON +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void PointInQuadFromBarycentric( const Vector &v1, const Vector &v2, const Vector &v3, const Vector &v4, + const Vector2D &uv, Vector &point ) +{ + // + // Think of a quad with points v1, v2, v3, v4 and u, v line segments + // find the ray from v0 edge to v1 edge at v + // + Vector vPts[2]; + VectorLerp( v1, v4, uv[1], vPts[0] ); + VectorLerp( v2, v3, uv[1], vPts[1] ); + VectorLerp( vPts[0], vPts[1], uv[0], point ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void TexCoordInQuadFromBarycentric( const Vector2D &v1, const Vector2D &v2, const Vector2D &v3, const Vector2D &v4, + const Vector2D &uv, Vector2D &texCoord ) +{ + // + // Think of a quad with points v1, v2, v3, v4 and u, v line segments + // find the ray from v0 edge to v1 edge at v + // + Vector2D vCoords[2]; + Vector2DLerp( v1, v4, uv[1], vCoords[0] ); + Vector2DLerp( v2, v3, uv[1], vCoords[1] ); + Vector2DLerp( vCoords[0], vCoords[1], uv[0], texCoord ); +} + + +//----------------------------------------------------------------------------- +// Compute point from barycentric specification +// Edge u goes from v0 to v1, edge v goes from v0 to v2 +//----------------------------------------------------------------------------- +void ComputePointFromBarycentric( const Vector& v0, const Vector& v1, const Vector& v2, + float u, float v, Vector& pt ) +{ + Vector edgeU, edgeV; + VectorSubtract( v1, v0, edgeU ); + VectorSubtract( v2, v0, edgeV ); + VectorMA( v0, u, edgeU, pt ); + VectorMA( pt, v, edgeV, pt ); +} + +void ComputePointFromBarycentric( const Vector2D& v0, const Vector2D& v1, const Vector2D& v2, + float u, float v, Vector2D& pt ) +{ + Vector2D edgeU, edgeV; + Vector2DSubtract( v1, v0, edgeU ); + Vector2DSubtract( v2, v0, edgeV ); + Vector2DMA( v0, u, edgeU, pt ); + Vector2DMA( pt, v, edgeV, pt ); +} + + +//----------------------------------------------------------------------------- +// Compute a matrix that has the correct orientation but which has an origin at +// the center of the bounds +//----------------------------------------------------------------------------- +static void ComputeCenterMatrix( const Vector& origin, const QAngle& angles, + const Vector& mins, const Vector& maxs, matrix3x4_t& matrix ) +{ + Vector centroid; + VectorAdd( mins, maxs, centroid ); + centroid *= 0.5f; + AngleMatrix( angles, matrix ); + + Vector worldCentroid; + VectorRotate( centroid, matrix, worldCentroid ); + worldCentroid += origin; + MatrixSetColumn( worldCentroid, 3, matrix ); +} + +static void ComputeCenterIMatrix( const Vector& origin, const QAngle& angles, + const Vector& mins, const Vector& maxs, matrix3x4_t& matrix ) +{ + Vector centroid; + VectorAdd( mins, maxs, centroid ); + centroid *= -0.5f; + AngleIMatrix( angles, matrix ); + + // For the translational component here, note that the origin in world space + // is T = R * C + O, (R = rotation matrix, C = centroid in local space, O = origin in world space) + // The IMatrix translation = - transpose(R) * T = -C - transpose(R) * 0 + Vector localOrigin; + VectorRotate( origin, matrix, localOrigin ); + centroid -= localOrigin; + MatrixSetColumn( centroid, 3, matrix ); +} + + +//----------------------------------------------------------------------------- +// Compute a matrix which is the absolute value of another +//----------------------------------------------------------------------------- +static inline void ComputeAbsMatrix( const matrix3x4_t& in, matrix3x4_t& out ) +{ + FloatBits(out[0][0]) = FloatAbsBits(in[0][0]); + FloatBits(out[0][1]) = FloatAbsBits(in[0][1]); + FloatBits(out[0][2]) = FloatAbsBits(in[0][2]); + FloatBits(out[1][0]) = FloatAbsBits(in[1][0]); + FloatBits(out[1][1]) = FloatAbsBits(in[1][1]); + FloatBits(out[1][2]) = FloatAbsBits(in[1][2]); + FloatBits(out[2][0]) = FloatAbsBits(in[2][0]); + FloatBits(out[2][1]) = FloatAbsBits(in[2][1]); + FloatBits(out[2][2]) = FloatAbsBits(in[2][2]); +} + + +//----------------------------------------------------------------------------- +// Compute a separating plane between two boxes (expensive!) +// Returns false if no separating plane exists +//----------------------------------------------------------------------------- +static bool ComputeSeparatingPlane( const matrix3x4_t &worldToBox1, const matrix3x4_t &box2ToWorld, + const Vector& box1Size, const Vector& box2Size, float tolerance, cplane_t* pPlane ) +{ + // The various separating planes can be either + // 1) A plane parallel to one of the box face planes + // 2) A plane parallel to the cross-product of an edge from each box + + // First, compute the basis of second box in the space of the first box + // NOTE: These basis place the origin at the centroid of each box! + matrix3x4_t box2ToBox1; + ConcatTransforms( worldToBox1, box2ToWorld, box2ToBox1 ); + + // We're going to be using the origin of box2 in the space of box1 alot, + // lets extract it from the matrix.... + Vector box2Origin; + MatrixGetColumn( box2ToBox1, 3, box2Origin ); + + // Next get the absolute values of these entries and store in absbox2ToBox1. + matrix3x4_t absBox2ToBox1; + ComputeAbsMatrix( box2ToBox1, absBox2ToBox1 ); + + // There are 15 tests to make. The first 3 involve trying planes parallel + // to the faces of the first box. + + // NOTE: The algorithm here involves finding the projections of the two boxes + // onto a particular line. If the projections on the line do not overlap, + // that means that there's a plane perpendicular to the line which separates + // the two boxes; and we've therefore found a separating plane. + + // The way we check for overlay is we find the projections of the two boxes + // onto the line, and add them up. We compare the sum with the projection + // of the relative center of box2 onto the same line. + + Vector tmp; + float boxProjectionSum; + float originProjection; + + // NOTE: For these guys, we're taking advantage of the fact that the ith + // row of the box2ToBox1 is the direction of the box1 (x,y,z)-axis + // transformed into the space of box2. + + // First side of box 1 + boxProjectionSum = box1Size.x + MatrixRowDotProduct( absBox2ToBox1, 0, box2Size ); + originProjection = FloatMakePositive( box2Origin.x ) + tolerance; + if ( FloatBits(originProjection) > FloatBits(boxProjectionSum) ) + { + VectorCopy( worldToBox1[0], pPlane->normal.Base() ); + return true; + } + + // Second side of box 1 + boxProjectionSum = box1Size.y + MatrixRowDotProduct( absBox2ToBox1, 1, box2Size ); + originProjection = FloatMakePositive( box2Origin.y ) + tolerance; + if ( FloatBits(originProjection) > FloatBits(boxProjectionSum) ) + { + VectorCopy( worldToBox1[1], pPlane->normal.Base() ); + return true; + } + + // Third side of box 1 + boxProjectionSum = box1Size.z + MatrixRowDotProduct( absBox2ToBox1, 2, box2Size ); + originProjection = FloatMakePositive( box2Origin.z ) + tolerance; + if ( FloatBits(originProjection) > FloatBits(boxProjectionSum) ) + { + VectorCopy( worldToBox1[2], pPlane->normal.Base() ); + return true; + } + + // The next three involve checking splitting planes parallel to the + // faces of the second box. + + // NOTE: For these guys, we're taking advantage of the fact that the 0th + // column of the box2ToBox1 is the direction of the box2 x-axis + // transformed into the space of box1. + // Here, we're determining the distance of box2's center from box1's center + // by projecting it onto a line parallel to box2's axis + + // First side of box 2 + boxProjectionSum = box2Size.x + MatrixColumnDotProduct( absBox2ToBox1, 0, box1Size ); + originProjection = FloatMakePositive( MatrixColumnDotProduct( box2ToBox1, 0, box2Origin ) ) + tolerance; + if ( FloatBits(originProjection) > FloatBits(boxProjectionSum) ) + { + MatrixGetColumn( box2ToWorld, 0, pPlane->normal ); + return true; + } + + // Second side of box 2 + boxProjectionSum = box2Size.y + MatrixColumnDotProduct( absBox2ToBox1, 1, box1Size ); + originProjection = FloatMakePositive( MatrixColumnDotProduct( box2ToBox1, 1, box2Origin ) ) + tolerance; + if ( FloatBits(originProjection) > FloatBits(boxProjectionSum) ) + { + MatrixGetColumn( box2ToWorld, 1, pPlane->normal ); + return true; + } + + // Third side of box 2 + boxProjectionSum = box2Size.z + MatrixColumnDotProduct( absBox2ToBox1, 2, box1Size ); + originProjection = FloatMakePositive( MatrixColumnDotProduct( box2ToBox1, 2, box2Origin ) ) + tolerance; + if ( FloatBits(originProjection) > FloatBits(boxProjectionSum) ) + { + MatrixGetColumn( box2ToWorld, 2, pPlane->normal ); + return true; + } + + // Next check the splitting planes which are orthogonal to the pairs + // of edges, one from box1 and one from box2. As only direction matters, + // there are 9 pairs since each box has 3 distinct edge directions. + + // Here, we take advantage of the fact that the edges from box 1 are all + // axis aligned; therefore the crossproducts are simplified. Let's walk through + // the example of b1e1 x b2e1: + + // In this example, the line to check is perpendicular to b1e1 + b2e2 + // we can compute this line by taking the cross-product: + // + // [ i j k ] + // [ 1 0 0 ] = - ez j + ey k = l1 + // [ ex ey ez ] + + // Where ex, ey, ez is the components of box2's x axis in the space of box 1, + // which is == to the 0th column of of box2toBox1 + + // The projection of box1 onto this line = the absolute dot product of the box size + // against the line, which = + // AbsDot( box1Size, l1 ) = abs( -ez * box1.y ) + abs( ey * box1.z ) + + // To compute the projection of box2 onto this line, we'll do it in the space of box 2 + // + // [ i j k ] + // [ fx fy fz ] = fz j - fy k = l2 + // [ 1 0 0 ] + + // Where fx, fy, fz is the components of box1's x axis in the space of box 2, + // which is == to the 0th row of of box2toBox1 + + // The projection of box2 onto this line = the absolute dot product of the box size + // against the line, which = + // AbsDot( box2Size, l2 ) = abs( fz * box2.y ) + abs ( fy * box2.z ) + + // The projection of the relative origin position on this line is done in the + // space of box 1: + // + // originProjection = DotProduct( <-ez j + ey k>, box2Origin ) = + // -ez * box2Origin.y + ey * box2Origin.z + + // b1e1 x b2e1 + boxProjectionSum = + box1Size.y * absBox2ToBox1[2][0] + box1Size.z * absBox2ToBox1[1][0] + + box2Size.y * absBox2ToBox1[0][2] + box2Size.z * absBox2ToBox1[0][1]; + originProjection = FloatMakePositive( -box2Origin.y * box2ToBox1[2][0] + box2Origin.z * box2ToBox1[1][0] ) + tolerance; + if ( FloatBits(originProjection) > FloatBits(boxProjectionSum) ) + { + MatrixGetColumn( box2ToWorld, 0, tmp ); + CrossProduct( worldToBox1[0], tmp.Base(), pPlane->normal.Base() ); + return true; + } + + // b1e1 x b2e2 + boxProjectionSum = + box1Size.y * absBox2ToBox1[2][1] + box1Size.z * absBox2ToBox1[1][1] + + box2Size.x * absBox2ToBox1[0][2] + box2Size.z * absBox2ToBox1[0][0]; + originProjection = FloatMakePositive( -box2Origin.y * box2ToBox1[2][1] + box2Origin.z * box2ToBox1[1][1] ) + tolerance; + if ( FloatBits(originProjection) > FloatBits(boxProjectionSum) ) + { + MatrixGetColumn( box2ToWorld, 1, tmp ); + CrossProduct( worldToBox1[0], tmp.Base(), pPlane->normal.Base() ); + return true; + } + + // b1e1 x b2e3 + boxProjectionSum = + box1Size.y * absBox2ToBox1[2][2] + box1Size.z * absBox2ToBox1[1][2] + + box2Size.x * absBox2ToBox1[0][1] + box2Size.y * absBox2ToBox1[0][0]; + originProjection = FloatMakePositive( -box2Origin.y * box2ToBox1[2][2] + box2Origin.z * box2ToBox1[1][2] ) + tolerance; + if ( FloatBits(originProjection) > FloatBits(boxProjectionSum) ) + { + MatrixGetColumn( box2ToWorld, 2, tmp ); + CrossProduct( worldToBox1[0], tmp.Base(), pPlane->normal.Base() ); + return true; + } + + // b1e2 x b2e1 + boxProjectionSum = + box1Size.x * absBox2ToBox1[2][0] + box1Size.z * absBox2ToBox1[0][0] + + box2Size.y * absBox2ToBox1[1][2] + box2Size.z * absBox2ToBox1[1][1]; + originProjection = FloatMakePositive( box2Origin.x * box2ToBox1[2][0] - box2Origin.z * box2ToBox1[0][0] ) + tolerance; + if ( FloatBits(originProjection) > FloatBits(boxProjectionSum) ) + { + MatrixGetColumn( box2ToWorld, 0, tmp ); + CrossProduct( worldToBox1[1], tmp.Base(), pPlane->normal.Base() ); + return true; + } + + // b1e2 x b2e2 + boxProjectionSum = + box1Size.x * absBox2ToBox1[2][1] + box1Size.z * absBox2ToBox1[0][1] + + box2Size.x * absBox2ToBox1[1][2] + box2Size.z * absBox2ToBox1[1][0]; + originProjection = FloatMakePositive( box2Origin.x * box2ToBox1[2][1] - box2Origin.z * box2ToBox1[0][1] ) + tolerance; + if ( FloatBits(originProjection) > FloatBits(boxProjectionSum) ) + { + MatrixGetColumn( box2ToWorld, 1, tmp ); + CrossProduct( worldToBox1[1], tmp.Base(), pPlane->normal.Base() ); + return true; + } + + // b1e2 x b2e3 + boxProjectionSum = + box1Size.x * absBox2ToBox1[2][2] + box1Size.z * absBox2ToBox1[0][2] + + box2Size.x * absBox2ToBox1[1][1] + box2Size.y * absBox2ToBox1[1][0]; + originProjection = FloatMakePositive( box2Origin.x * box2ToBox1[2][2] - box2Origin.z * box2ToBox1[0][2] ) + tolerance; + if ( FloatBits(originProjection) > FloatBits(boxProjectionSum) ) + { + MatrixGetColumn( box2ToWorld, 2, tmp ); + CrossProduct( worldToBox1[1], tmp.Base(), pPlane->normal.Base() ); + return true; + } + + // b1e3 x b2e1 + boxProjectionSum = + box1Size.x * absBox2ToBox1[1][0] + box1Size.y * absBox2ToBox1[0][0] + + box2Size.y * absBox2ToBox1[2][2] + box2Size.z * absBox2ToBox1[2][1]; + originProjection = FloatMakePositive( -box2Origin.x * box2ToBox1[1][0] + box2Origin.y * box2ToBox1[0][0] ) + tolerance; + if ( FloatBits(originProjection) > FloatBits(boxProjectionSum) ) + { + MatrixGetColumn( box2ToWorld, 0, tmp ); + CrossProduct( worldToBox1[2], tmp.Base(), pPlane->normal.Base() ); + return true; + } + + // b1e3 x b2e2 + boxProjectionSum = + box1Size.x * absBox2ToBox1[1][1] + box1Size.y * absBox2ToBox1[0][1] + + box2Size.x * absBox2ToBox1[2][2] + box2Size.z * absBox2ToBox1[2][0]; + originProjection = FloatMakePositive( -box2Origin.x * box2ToBox1[1][1] + box2Origin.y * box2ToBox1[0][1] ) + tolerance; + if ( FloatBits(originProjection) > FloatBits(boxProjectionSum) ) + { + MatrixGetColumn( box2ToWorld, 1, tmp ); + CrossProduct( worldToBox1[2], tmp.Base(), pPlane->normal.Base() ); + return true; + } + + // b1e3 x b2e3 + boxProjectionSum = + box1Size.x * absBox2ToBox1[1][2] + box1Size.y * absBox2ToBox1[0][2] + + box2Size.x * absBox2ToBox1[2][1] + box2Size.y * absBox2ToBox1[2][0]; + originProjection = FloatMakePositive( -box2Origin.x * box2ToBox1[1][2] + box2Origin.y * box2ToBox1[0][2] ) + tolerance; + if ( FloatBits(originProjection) > FloatBits(boxProjectionSum) ) + { + MatrixGetColumn( box2ToWorld, 2, tmp ); + CrossProduct( worldToBox1[2], tmp.Base(), pPlane->normal.Base() ); + return true; + } + + return false; +} + + +//----------------------------------------------------------------------------- +// Compute a separating plane between two boxes (expensive!) +// Returns false if no separating plane exists +//----------------------------------------------------------------------------- +bool ComputeSeparatingPlane( const Vector& org1, const QAngle& angles1, const Vector& min1, const Vector& max1, + const Vector& org2, const QAngle& angles2, const Vector& min2, const Vector& max2, + float tolerance, cplane_t* pPlane ) +{ + matrix3x4_t worldToBox1, box2ToWorld; + ComputeCenterIMatrix( org1, angles1, min1, max1, worldToBox1 ); + ComputeCenterMatrix( org2, angles2, min2, max2, box2ToWorld ); + + // Then compute the size of the two boxes + Vector box1Size, box2Size; + VectorSubtract( max1, min1, box1Size ); + VectorSubtract( max2, min2, box2Size ); + box1Size *= 0.5f; + box2Size *= 0.5f; + + return ComputeSeparatingPlane( worldToBox1, box2ToWorld, box1Size, box2Size, tolerance, pPlane ); +} + + +//----------------------------------------------------------------------------- +// Swept OBB test +//----------------------------------------------------------------------------- +bool IsRayIntersectingOBB( const Ray_t &ray, const Vector& org, const QAngle& angles, + const Vector& mins, const Vector& maxs ) +{ + if ( angles == vec3_angle ) + { + Vector vecWorldMins, vecWorldMaxs; + VectorAdd( org, mins, vecWorldMins ); + VectorAdd( org, maxs, vecWorldMaxs ); + return IsBoxIntersectingRay( vecWorldMins, vecWorldMaxs, ray ); + } + + if ( ray.m_IsRay ) + { + matrix3x4_t worldToBox; + AngleIMatrix( angles, org, worldToBox ); + + Ray_t rotatedRay; + VectorTransform( ray.m_Start, worldToBox, rotatedRay.m_Start ); + VectorRotate( ray.m_Delta, worldToBox, rotatedRay.m_Delta ); + rotatedRay.m_StartOffset = vec3_origin; + rotatedRay.m_Extents = vec3_origin; + rotatedRay.m_IsRay = ray.m_IsRay; + rotatedRay.m_IsSwept = ray.m_IsSwept; + + return IsBoxIntersectingRay( mins, maxs, rotatedRay ); + } + + if ( !ray.m_IsSwept ) + { + cplane_t plane; + return ComputeSeparatingPlane( ray.m_Start, vec3_angle, -ray.m_Extents, ray.m_Extents, + org, angles, mins, maxs, 0.0f, &plane ) == false; + } + + // NOTE: See the comments in ComputeSeparatingPlane to understand this math + + // First, compute the basis of box in the space of the ray + // NOTE: These basis place the origin at the centroid of each box! + matrix3x4_t worldToBox1, box2ToWorld; + ComputeCenterMatrix( org, angles, mins, maxs, box2ToWorld ); + + // Find the center + extents of an AABB surrounding the ray + Vector vecRayCenter; + VectorMA( ray.m_Start, 0.5, ray.m_Delta, vecRayCenter ); + vecRayCenter *= -1.0f; + SetIdentityMatrix( worldToBox1 ); + MatrixSetColumn( vecRayCenter, 3, worldToBox1 ); + + Vector box1Size; + box1Size.x = ray.m_Extents.x + FloatMakePositive( ray.m_Delta.x ) * 0.5f; + box1Size.y = ray.m_Extents.y + FloatMakePositive( ray.m_Delta.y ) * 0.5f; + box1Size.z = ray.m_Extents.z + FloatMakePositive( ray.m_Delta.z ) * 0.5f; + + // Then compute the size of the box + Vector box2Size; + VectorSubtract( maxs, mins, box2Size ); + box2Size *= 0.5f; + + // Do an OBB test of the box with the AABB surrounding the ray + cplane_t plane; + if ( ComputeSeparatingPlane( worldToBox1, box2ToWorld, box1Size, box2Size, 0.0f, &plane ) ) + return false; + + // Now deal with the planes which are the cross products of the ray sweep direction vs box edges + Vector vecRayDirection = ray.m_Delta; + VectorNormalize( vecRayDirection ); + + // Need a vector between ray center vs box center measured in the space of the ray (world) + Vector vecCenterDelta; + vecCenterDelta.x = box2ToWorld[0][3] - ray.m_Start.x; + vecCenterDelta.y = box2ToWorld[1][3] - ray.m_Start.y; + vecCenterDelta.z = box2ToWorld[2][3] - ray.m_Start.z; + + // Rotate the ray direction into the space of the OBB + Vector vecAbsRayDirBox2; + VectorIRotate( vecRayDirection, box2ToWorld, vecAbsRayDirBox2 ); + + // Make abs versions of the ray in world space + ray in box2 space + VectorAbs( vecAbsRayDirBox2, vecAbsRayDirBox2 ); + + // Now do the work for the planes which are perpendicular to the edges of the AABB + // and the sweep direction edges... + + // In this example, the line to check is perpendicular to box edge x + ray delta + // we can compute this line by taking the cross-product: + // + // [ i j k ] + // [ 1 0 0 ] = - dz j + dy k = l1 + // [ dx dy dz ] + + // Where dx, dy, dz is the ray delta (normalized) + + // The projection of the box onto this line = the absolute dot product of the box size + // against the line, which = + // AbsDot( vecBoxHalfDiagonal, l1 ) = abs( -dz * vecBoxHalfDiagonal.y ) + abs( dy * vecBoxHalfDiagonal.z ) + + // Because the plane contains the sweep direction, the sweep will produce + // no extra projection onto the line normal to the plane. + // Therefore all we need to do is project the ray extents onto this line also: + // AbsDot( ray.m_Extents, l1 ) = abs( -dz * ray.m_Extents.y ) + abs( dy * ray.m_Extents.z ) + + Vector vecPlaneNormal; + + // box x x ray delta + CrossProduct( vecRayDirection, Vector( box2ToWorld[0][0], box2ToWorld[1][0], box2ToWorld[2][0] ), vecPlaneNormal ); + float flCenterDeltaProjection = FloatMakePositive( DotProduct( vecPlaneNormal, vecCenterDelta ) ); + float flBoxProjectionSum = + vecAbsRayDirBox2.z * box2Size.y + vecAbsRayDirBox2.y * box2Size.z + + DotProductAbs( vecPlaneNormal, ray.m_Extents ); + if ( FloatBits(flCenterDeltaProjection) > FloatBits(flBoxProjectionSum) ) + return false; + + // box y x ray delta + CrossProduct( vecRayDirection, Vector( box2ToWorld[0][1], box2ToWorld[1][1], box2ToWorld[2][1] ), vecPlaneNormal ); + flCenterDeltaProjection = FloatMakePositive( DotProduct( vecPlaneNormal, vecCenterDelta ) ); + flBoxProjectionSum = + vecAbsRayDirBox2.z * box2Size.x + vecAbsRayDirBox2.x * box2Size.z + + DotProductAbs( vecPlaneNormal, ray.m_Extents ); + if ( FloatBits(flCenterDeltaProjection) > FloatBits(flBoxProjectionSum) ) + return false; + + // box z x ray delta + CrossProduct( vecRayDirection, Vector( box2ToWorld[0][2], box2ToWorld[1][2], box2ToWorld[2][2] ), vecPlaneNormal ); + flCenterDeltaProjection = FloatMakePositive( DotProduct( vecPlaneNormal, vecCenterDelta ) ); + flBoxProjectionSum = + vecAbsRayDirBox2.y * box2Size.x + vecAbsRayDirBox2.x * box2Size.y + + DotProductAbs( vecPlaneNormal, ray.m_Extents ); + if ( FloatBits(flCenterDeltaProjection) > FloatBits(flBoxProjectionSum) ) + return false; + + return true; +} + +/*-------------------------------------------------------------------------- +Purpose: + +NOTE: +triangle points are given in clockwise order (aabb-triangle test) + + 1 edge0 = 1 - 0 + | \ edge1 = 2 - 1 + | \ edge2 = 0 - 2 + | \ + | \ + 0-----2 + +--------------------------------------------------------------------------*/ + +//----------------------------------------------------------------------------- +// Purpose: find the minima and maxima of the 3 given values +//----------------------------------------------------------------------------- +inline void FindMinMax( float v1, float v2, float v3, float &min, float &max ) +{ + min = max = v1; + if ( v2 < min ) { min = v2; } + if ( v2 > max ) { max = v2; } + if ( v3 < min ) { min = v3; } + if ( v3 > max ) { max = v3; } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +inline bool AxisTestEdgeCrossX2( float flEdgeZ, float flEdgeY, float flAbsEdgeZ, float flAbsEdgeY, + const Vector &p1, const Vector &p3, const Vector &vecExtents, + float flTolerance ) +{ + // Cross Product( axialX(1,0,0) x edge ): x = 0.0f, y = edge.z, z = -edge.y + // Triangle Point Distances: dist(x) = normal.y * pt(x).y + normal.z * pt(x).z + float flDist1 = flEdgeZ * p1.y - flEdgeY * p1.z; + float flDist3 = flEdgeZ * p3.y - flEdgeY * p3.z; + + // Extents are symmetric: dist = abs( normal.y ) * extents.y + abs( normal.z ) * extents.z + float flDistBox = flAbsEdgeZ * vecExtents.y + flAbsEdgeY * vecExtents.z; + + // Either dist1, dist3 is the closest point to the box, determine which and test of overlap with box(AABB). + if ( flDist1 < flDist3 ) + { + if ( ( flDist1 > ( flDistBox + flTolerance ) ) || ( flDist3 < -( flDistBox + flTolerance ) ) ) + return false; + } + else + { + if ( ( flDist3 > ( flDistBox + flTolerance ) ) || ( flDist1 < -( flDistBox + flTolerance ) ) ) + return false; + } + + return true; +} + +//-------------------------------------------------------------------------- +// Purpose: +//-------------------------------------------------------------------------- +inline bool AxisTestEdgeCrossX3( float flEdgeZ, float flEdgeY, float flAbsEdgeZ, float flAbsEdgeY, + const Vector &p1, const Vector &p2, const Vector &vecExtents, + float flTolerance ) +{ + // Cross Product( axialX(1,0,0) x edge ): x = 0.0f, y = edge.z, z = -edge.y + // Triangle Point Distances: dist(x) = normal.y * pt(x).y + normal.z * pt(x).z + float flDist1 = flEdgeZ * p1.y - flEdgeY * p1.z; + float flDist2 = flEdgeZ * p2.y - flEdgeY * p2.z; + + // Extents are symmetric: dist = abs( normal.y ) * extents.y + abs( normal.z ) * extents.z + float flDistBox = flAbsEdgeZ * vecExtents.y + flAbsEdgeY * vecExtents.z; + + // Either dist1, dist2 is the closest point to the box, determine which and test of overlap with box(AABB). + if ( flDist1 < flDist2 ) + { + if ( ( flDist1 > ( flDistBox + flTolerance ) ) || ( flDist2 < -( flDistBox + flTolerance ) ) ) + return false; + } + else + { + if ( ( flDist2 > ( flDistBox + flTolerance ) ) || ( flDist1 < -( flDistBox + flTolerance ) ) ) + return false; + } + + return true; +} + +//-------------------------------------------------------------------------- +//-------------------------------------------------------------------------- +inline bool AxisTestEdgeCrossY2( float flEdgeZ, float flEdgeX, float flAbsEdgeZ, float flAbsEdgeX, + const Vector &p1, const Vector &p3, const Vector &vecExtents, + float flTolerance ) +{ + // Cross Product( axialY(0,1,0) x edge ): x = -edge.z, y = 0.0f, z = edge.x + // Triangle Point Distances: dist(x) = normal.x * pt(x).x + normal.z * pt(x).z + float flDist1 = -flEdgeZ * p1.x + flEdgeX * p1.z; + float flDist3 = -flEdgeZ * p3.x + flEdgeX * p3.z; + + // Extents are symmetric: dist = abs( normal.x ) * extents.x + abs( normal.z ) * extents.z + float flDistBox = flAbsEdgeZ * vecExtents.x + flAbsEdgeX * vecExtents.z; + + // Either dist1, dist3 is the closest point to the box, determine which and test of overlap with box(AABB). + if ( flDist1 < flDist3 ) + { + if ( ( flDist1 > ( flDistBox + flTolerance ) ) || ( flDist3 < -( flDistBox + flTolerance ) ) ) + return false; + } + else + { + if ( ( flDist3 > ( flDistBox + flTolerance ) ) || ( flDist1 < -( flDistBox + flTolerance ) ) ) + return false; + } + + return true; +} + +//-------------------------------------------------------------------------- +//-------------------------------------------------------------------------- +inline bool AxisTestEdgeCrossY3( float flEdgeZ, float flEdgeX, float flAbsEdgeZ, float flAbsEdgeX, + const Vector &p1, const Vector &p2, const Vector &vecExtents, + float flTolerance ) +{ + // Cross Product( axialY(0,1,0) x edge ): x = -edge.z, y = 0.0f, z = edge.x + // Triangle Point Distances: dist(x) = normal.x * pt(x).x + normal.z * pt(x).z + float flDist1 = -flEdgeZ * p1.x + flEdgeX * p1.z; + float flDist2 = -flEdgeZ * p2.x + flEdgeX * p2.z; + + // Extents are symmetric: dist = abs( normal.x ) * extents.x + abs( normal.z ) * extents.z + float flDistBox = flAbsEdgeZ * vecExtents.x + flAbsEdgeX * vecExtents.z; + + // Either dist1, dist2 is the closest point to the box, determine which and test of overlap with box(AABB). + if ( flDist1 < flDist2 ) + { + if ( ( flDist1 > ( flDistBox + flTolerance ) ) || ( flDist2 < -( flDistBox + flTolerance ) ) ) + return false; + } + else + { + if ( ( flDist2 > ( flDistBox + flTolerance ) ) || ( flDist1 < -( flDistBox + flTolerance ) ) ) + return false; + } + + return true; +} + +//-------------------------------------------------------------------------- +//-------------------------------------------------------------------------- +inline bool AxisTestEdgeCrossZ1( float flEdgeY, float flEdgeX, float flAbsEdgeY, float flAbsEdgeX, + const Vector &p2, const Vector &p3, const Vector &vecExtents, + float flTolerance ) +{ + // Cross Product( axialZ(0,0,1) x edge ): x = edge.y, y = -edge.x, z = 0.0f + // Triangle Point Distances: dist(x) = normal.x * pt(x).x + normal.y * pt(x).y + float flDist2 = flEdgeY * p2.x - flEdgeX * p2.y; + float flDist3 = flEdgeY * p3.x - flEdgeX * p3.y; + + // Extents are symmetric: dist = abs( normal.x ) * extents.x + abs( normal.y ) * extents.y + float flDistBox = flAbsEdgeY * vecExtents.x + flAbsEdgeX * vecExtents.y; + + // Either dist2, dist3 is the closest point to the box, determine which and test of overlap with box(AABB). + if ( flDist3 < flDist2 ) + { + if ( ( flDist3 > ( flDistBox + flTolerance ) ) || ( flDist2 < -( flDistBox + flTolerance ) ) ) + return false; + } + else + { + if ( ( flDist2 > ( flDistBox + flTolerance ) ) || ( flDist3 < -( flDistBox + flTolerance ) ) ) + return false; + } + + return true; +} + +//-------------------------------------------------------------------------- +//-------------------------------------------------------------------------- +inline bool AxisTestEdgeCrossZ2( float flEdgeY, float flEdgeX, float flAbsEdgeY, float flAbsEdgeX, + const Vector &p1, const Vector &p3, const Vector &vecExtents, + float flTolerance ) +{ + // Cross Product( axialZ(0,0,1) x edge ): x = edge.y, y = -edge.x, z = 0.0f + // Triangle Point Distances: dist(x) = normal.x * pt(x).x + normal.y * pt(x).y + float flDist1 = flEdgeY * p1.x - flEdgeX * p1.y; + float flDist3 = flEdgeY * p3.x - flEdgeX * p3.y; + + // Extents are symmetric: dist = abs( normal.x ) * extents.x + abs( normal.y ) * extents.y + float flDistBox = flAbsEdgeY * vecExtents.x + flAbsEdgeX * vecExtents.y; + + // Either dist1, dist3 is the closest point to the box, determine which and test of overlap with box(AABB). + if ( flDist1 < flDist3 ) + { + if ( ( flDist1 > ( flDistBox + flTolerance ) ) || ( flDist3 < -( flDistBox + flTolerance ) ) ) + return false; + } + else + { + if ( ( flDist3 > ( flDistBox + flTolerance ) ) || ( flDist1 < -( flDistBox + flTolerance ) ) ) + return false; + } + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: Test for an intersection (overlap) between an axial-aligned bounding +// box (AABB) and a triangle. +// +// Using the "Separating-Axis Theorem" to test for intersections between +// a triangle and an axial-aligned bounding box (AABB). +// 1. 3 Axis Planes - x, y, z +// 2. 9 Edge Planes Tests - the 3 edges of the triangle crossed with all 3 axial +// planes (x, y, z) +// 3. 1 Face Plane - the triangle plane (cplane_t plane below) +// Output: false = separating axis (no intersection) +// true = intersection +//----------------------------------------------------------------------------- +bool IsBoxIntersectingTriangle( const Vector &vecBoxCenter, const Vector &vecBoxExtents, + const Vector &v1, const Vector &v2, const Vector &v3, + const cplane_t &plane, float flTolerance ) +{ + // Test the axial planes (x,y,z) against the min, max of the triangle. + float flMin, flMax; + Vector p1, p2, p3; + + // x plane + p1.x = v1.x - vecBoxCenter.x; + p2.x = v2.x - vecBoxCenter.x; + p3.x = v3.x - vecBoxCenter.x; + FindMinMax( p1.x, p2.x, p3.x, flMin, flMax ); + if ( ( flMin > ( vecBoxExtents.x + flTolerance ) ) || ( flMax < -( vecBoxExtents.x + flTolerance ) ) ) + return false; + + // y plane + p1.y = v1.y - vecBoxCenter.y; + p2.y = v2.y - vecBoxCenter.y; + p3.y = v3.y - vecBoxCenter.y; + FindMinMax( p1.y, p2.y, p3.y, flMin, flMax ); + if ( ( flMin > ( vecBoxExtents.y + flTolerance ) ) || ( flMax < -( vecBoxExtents.y + flTolerance ) ) ) + return false; + + // z plane + p1.z = v1.z - vecBoxCenter.z; + p2.z = v2.z - vecBoxCenter.z; + p3.z = v3.z - vecBoxCenter.z; + FindMinMax( p1.z, p2.z, p3.z, flMin, flMax ); + if ( ( flMin > ( vecBoxExtents.z + flTolerance ) ) || ( flMax < -( vecBoxExtents.z + flTolerance ) ) ) + return false; + + // Test the 9 edge cases. + Vector vecEdge, vecAbsEdge; + + // edge 0 (cross x,y,z) + vecEdge = p2 - p1; + vecAbsEdge.y = FloatMakePositive( vecEdge.y ); + vecAbsEdge.z = FloatMakePositive( vecEdge.z ); + if ( !AxisTestEdgeCrossX2( vecEdge.z, vecEdge.y, vecAbsEdge.z, vecAbsEdge.y, p1, p3, vecBoxExtents, flTolerance ) ) + return false; + + vecAbsEdge.x = FloatMakePositive( vecEdge.x ); + if ( !AxisTestEdgeCrossY2( vecEdge.z, vecEdge.x, vecAbsEdge.z, vecAbsEdge.x, p1, p3, vecBoxExtents, flTolerance ) ) + return false; + + if ( !AxisTestEdgeCrossZ1( vecEdge.y, vecEdge.x, vecAbsEdge.y, vecAbsEdge.x, p2, p3, vecBoxExtents, flTolerance ) ) + return false; + + // edge 1 (cross x,y,z) + vecEdge = p3 - p2; + vecAbsEdge.y = FloatMakePositive( vecEdge.y ); + vecAbsEdge.z = FloatMakePositive( vecEdge.z ); + if ( !AxisTestEdgeCrossX2( vecEdge.z, vecEdge.y, vecAbsEdge.z, vecAbsEdge.y, p1, p2, vecBoxExtents, flTolerance ) ) + return false; + + vecAbsEdge.x = FloatMakePositive( vecEdge.x ); + if ( !AxisTestEdgeCrossY2( vecEdge.z, vecEdge.x, vecAbsEdge.z, vecAbsEdge.x, p1, p2, vecBoxExtents, flTolerance ) ) + return false; + + if ( !AxisTestEdgeCrossZ2( vecEdge.y, vecEdge.x, vecAbsEdge.y, vecAbsEdge.x, p1, p3, vecBoxExtents, flTolerance ) ) + return false; + + // edge 2 (cross x,y,z) + vecEdge = p1 - p3; + vecAbsEdge.y = FloatMakePositive( vecEdge.y ); + vecAbsEdge.z = FloatMakePositive( vecEdge.z ); + if ( !AxisTestEdgeCrossX3( vecEdge.z, vecEdge.y, vecAbsEdge.z, vecAbsEdge.y, p1, p2, vecBoxExtents, flTolerance ) ) + return false; + + vecAbsEdge.x = FloatMakePositive( vecEdge.x ); + if ( !AxisTestEdgeCrossY3( vecEdge.z, vecEdge.x, vecAbsEdge.z, vecAbsEdge.x, p1, p2, vecBoxExtents, flTolerance ) ) + return false; + + if ( !AxisTestEdgeCrossZ1( vecEdge.y, vecEdge.x, vecAbsEdge.y, vecAbsEdge.x, p2, p3, vecBoxExtents, flTolerance ) ) + return false; + + // Test against the triangle face plane. + Vector vecMin, vecMax; + VectorSubtract( vecBoxCenter, vecBoxExtents, vecMin ); + VectorAdd( vecBoxCenter, vecBoxExtents, vecMax ); + if ( BoxOnPlaneSide( vecMin, vecMax, &plane ) != 3 ) + return false; + + return true; +} + +// NOTE: JAY: This is untested code based on Real-time Collision Detection by Ericson +#if 0 +Vector CalcClosestPointOnTriangle( const Vector &P, const Vector &v0, const Vector &v1, const Vector &v2 ) +{ + Vector e0 = v1 - v0; + Vector e1 = v2 - v0; + Vector p0 = P - v0; + + // voronoi region of v0 + float d1 = DotProduct( e0, p0 ); + float d2 = DotProduct( e1, p0 ); + if (d1 <= 0.0f && d2 <= 0.0f) + return v0; + + // voronoi region of v1 + Vector p1 = P - v1; + float d3 = DotProduct( e0, p1 ); + float d4 = DotProduct( e1, p1 ); + if (d3 >=0.0f && d4 <= d3) + return v1; + + // voronoi region of e0 (v0-v1) + float ve2 = d1*d4 - d3*d2; + if ( ve2 <= 0.0f && d1 >= 0.0f && d3 <= 0.0f ) + { + float v = d1 / (d1-d3); + return v0 + v * e0; + } + // voronoi region of v2 + Vector p2 = P - v2; + float d5 = DotProduct( e0, p2 ); + float d6 = DotProduct( e1, p2 ); + if (d6 >= 0.0f && d5 <= d6) + return v2; + // voronoi region of e1 + float ve1 = d5*d2 - d1*d6; + if (ve1 <= 0.0f && d2 >= 0.0f && d6 >= 0.0f) + { + float w = d2 / (d2-d6); + return v0 + w * e1; + } + // voronoi region on e2 + float ve0 = d3*d6 - d5*d4; + if ( ve0 <= 0.0f && (d4-d3) >= 0.0f && (d5-d6) >= 0.0f ) + { + float w = (d4-d3)/((d4-d3) + (d5-d6)); + return v1 + w * (v2-v1); + } + // voronoi region of v0v1v2 triangle + float denom = 1.0f / (ve0+ve1+ve2); + float v = ve1*denom; + float w = ve2 * denom; + return v0 + e0 * v + e1 * w; +} +#endif + +#endif // !_STATIC_LINKED || _SHARED_LIB diff --git a/public/collisionutils.h b/public/collisionutils.h index 69006419..55437b87 100644 --- a/public/collisionutils.h +++ b/public/collisionutils.h @@ -1,348 +1,348 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: Common collision utility methods -// -// $Header: $ -// $NoKeywords: $ -//=============================================================================// - -#ifndef COLLISIONUTILS_H -#define COLLISIONUTILS_H - -#include "tier0/platform.h" - -#ifdef _WIN32 -#pragma once -#endif - - -//----------------------------------------------------------------------------- -// forward declarations -//----------------------------------------------------------------------------- -struct Ray_t; -class Vector; -class Vector2D; -class Vector4D; -struct cplane_t; -class QAngle; -class CBaseTrace; -struct matrix3x4_t; - - -//----------------------------------------------------------------------------- -// -// IntersectRayWithTriangle -// -// Intersects a ray with a triangle, returns distance t along ray. -// t will be less than zero if no intersection occurred -// oneSided will cull collisions which approach the triangle from the back -// side, assuming the vertices are specified in counter-clockwise order -// The vertices need not be specified in that order if oneSided is not used -// -//----------------------------------------------------------------------------- -float IntersectRayWithTriangle( const Ray_t& ray, - const Vector& v1, const Vector& v2, const Vector& v3, - bool oneSided ); - -//----------------------------------------------------------------------------- -// -// ComputeIntersectionBarycentricCoordinates -// -// Figures out the barycentric coordinates (u,v) where a ray hits a -// triangle. Note that this will ignore the ray extents, and it also ignores -// the ray length. Note that the edge from v1->v2 represents u (v2: u = 1), -// and the edge from v1->v3 represents v (v3: v = 1). It returns false -// if the ray is parallel to the triangle (or when t is specified if t is less -// than zero). -// -//----------------------------------------------------------------------------- -bool ComputeIntersectionBarycentricCoordinates( const Ray_t& ray, - const Vector& v1, const Vector& v2, const Vector& v3, float& u, float& v, - float *t = 0 ); - -//----------------------------------------------------------------------------- -// -// IntersectRayWithRay -// -// Returns whether or not there was an intersection. The "t" paramter is the -// distance along ray0 and the "s" parameter is the distance along ray1. If -// the two lines to not intersect the "t" and "s" represent the closest approach. -// "t" and "s" will not change if the rays are parallel. -// -//----------------------------------------------------------------------------- -bool IntersectRayWithRay( const Ray_t &ray0, const Ray_t &ray1, float &t, float &s ); - - -//----------------------------------------------------------------------------- -// -// IntersectRayWithSphere -// -// Returns whether or not there was an intersection. Returns the two intersection points. -// NOTE: The point of closest approach can be found at the average t value. -// -//----------------------------------------------------------------------------- -bool IntersectRayWithSphere( const Vector &vecRayOrigin, const Vector &vecRayDelta, const Vector &vecSphereCenter, float flRadius, float *pT1, float *pT2 ); - - -//----------------------------------------------------------------------------- -// -// IntersectInfiniteRayWithSphere -// -// Returns whether or not there was an intersection of a sphere against an infinitely -// extending ray. -// Returns the two intersection points -// -//----------------------------------------------------------------------------- -bool IntersectInfiniteRayWithSphere( const Vector &vecRayOrigin, const Vector &vecRayDelta, - const Vector &vecSphereCenter, float flRadius, float *pT1, float *pT2 ); - - -//----------------------------------------------------------------------------- -// -// IntersectRayWithPlane -// -// Intersects a ray with a plane, returns distance t along ray. -// t will be less than zero the intersection occurs in the opposite direction of the ray. -// -//----------------------------------------------------------------------------- -float IntersectRayWithPlane( const Ray_t& ray, const cplane_t& plane ); -float IntersectRayWithPlane( const Vector& org, const Vector& dir, const cplane_t& plane ); -float IntersectRayWithPlane( const Vector& org, const Vector& dir, const Vector& normal, float dist ); - -// This version intersects a ray with an axis-aligned plane -float IntersectRayWithAAPlane( const Vector& vecStart, const Vector& vecEnd, int nAxis, float flSign, float flDist ); - - -//----------------------------------------------------------------------------- -// IntersectRayWithBox -// -// Purpose: Computes the intersection of a ray with a box (AABB) -// Output : Returns true if there is an intersection + trace information -//----------------------------------------------------------------------------- -bool IntersectRayWithBox( const Vector &rayStart, const Vector &rayDelta, const Vector &boxMins, const Vector &boxMaxs, float epsilon, CBaseTrace *pTrace ); -bool IntersectRayWithBox( const Ray_t &ray, const Vector &boxMins, const Vector &boxMaxs, float epsilon, CBaseTrace *pTrace ); - -//----------------------------------------------------------------------------- -// Intersects a ray against a box -//----------------------------------------------------------------------------- -struct BoxTraceInfo_t -{ - float t1; - float t2; - int hitside; - bool startsolid; -}; - -bool IntersectRayWithBox( const Vector &vecRayStart, const Vector &vecRayDelta, - const Vector &boxMins, const Vector &boxMaxs, float flTolerance, BoxTraceInfo_t *pTrace ); - - -//----------------------------------------------------------------------------- -// IntersectRayWithOBB -// -// Purpose: Computes the intersection of a ray with a oriented box (OBB) -// Output : Returns true if there is an intersection + trace information -//----------------------------------------------------------------------------- -bool IntersectRayWithOBB( const Vector &vecRayStart, const Vector &vecRayDelta, - const matrix3x4_t &matOBBToWorld, const Vector &vecOBBMins, const Vector &vecOBBMaxs, - float flTolerance, CBaseTrace *pTrace ); - -bool IntersectRayWithOBB( const Vector &vecRayOrigin, const Vector &vecRayDelta, - const Vector &vecBoxOrigin, const QAngle &angBoxRotation, - const Vector &vecOBBMins, const Vector &vecOBBMaxs, float flTolerance, CBaseTrace *pTrace ); - -bool IntersectRayWithOBB( const Ray_t &ray, const Vector &vecBoxOrigin, const QAngle &angBoxRotation, - const Vector &vecOBBMins, const Vector &vecOBBMaxs, float flTolerance, CBaseTrace *pTrace ); - -bool IntersectRayWithOBB( const Ray_t &ray, const matrix3x4_t &matOBBToWorld, - const Vector &vecOBBMins, const Vector &vecOBBMaxs, float flTolerance, CBaseTrace *pTrace ); - -bool IntersectRayWithOBB( const Vector &vecRayStart, const Vector &vecRayDelta, - const matrix3x4_t &matOBBToWorld, const Vector &vecOBBMins, const Vector &vecOBBMaxs, - float flTolerance, BoxTraceInfo_t *pTrace ); - -//----------------------------------------------------------------------------- -// -// IsSphereIntersectingSphere -// -// returns true if there's an intersection between sphere and sphere -// -//----------------------------------------------------------------------------- -bool IsSphereIntersectingSphere( const Vector& center1, float radius1, - const Vector& center2, float radius2 ); - - -//----------------------------------------------------------------------------- -// -// IsBoxIntersectingSphere -// -// returns true if there's an intersection between box and sphere -// -//----------------------------------------------------------------------------- -bool IsBoxIntersectingSphere( const Vector& boxMin, const Vector& boxMax, - const Vector& center, float radius ); - -bool IsBoxIntersectingSphereExtents( const Vector& boxCenter, const Vector& boxHalfDiag, - const Vector& center, float radius ); - -//----------------------------------------------------------------------------- -// returns true if there's an intersection between ray and sphere -//----------------------------------------------------------------------------- -bool IsRayIntersectingSphere( const Vector &vecRayOrigin, const Vector &vecRayDelta, - const Vector &vecSphereCenter, float flRadius, float flTolerance = 0.0f ); - - -//----------------------------------------------------------------------------- -// -// IsCircleIntersectingRectangle -// -// returns true if there's an intersection between rectangle and circle -// -//----------------------------------------------------------------------------- -bool IsCircleIntersectingRectangle( const Vector2D& boxMin, const Vector2D& boxMax, - const Vector2D& center, float radius ); - - -//----------------------------------------------------------------------------- -// -// IsBoxIntersectingBox -// -// returns true if there's an intersection between two boxes -// -//----------------------------------------------------------------------------- -bool IsBoxIntersectingBox( const Vector& boxMin1, const Vector& boxMax1, - const Vector& boxMin2, const Vector& boxMax2 ); - -bool IsBoxIntersectingBoxExtents( const Vector& boxCenter1, const Vector& boxHalfDiagonal1, - const Vector& boxCenter2, const Vector& boxHalfDiagonal2 ); - - -//----------------------------------------------------------------------------- -// -// IsOBBIntersectingOBB -// -// returns true if there's an intersection between two OBBs -// -//----------------------------------------------------------------------------- -bool IsOBBIntersectingOBB( const Vector &vecOrigin1, const QAngle &vecAngles1, const Vector& boxMin1, const Vector& boxMax1, - const Vector &vecOrigin2, const QAngle &vecAngles2, const Vector& boxMin2, const Vector& boxMax2, float flTolerance = 0.0f ); - - -//----------------------------------------------------------------------------- -// -// IsBoxIntersectingRay -// -// returns true if there's an intersection between box and ray -// -//----------------------------------------------------------------------------- -bool FASTCALL IsBoxIntersectingRay( const Vector& boxMin, const Vector& boxMax, - const Vector& origin, const Vector& delta, float flTolerance = 0.0f ); - -bool FASTCALL IsBoxIntersectingRay( const Vector& boxMin, const Vector& boxMax, - const Ray_t& ray, float flTolerance = 0.0f ); - -bool FASTCALL IsBoxIntersectingRay( const Vector& boxMin, const Vector& boxMax, - const Vector& origin, const Vector& delta, - const Vector& invDelta, float flTolerance = 0.0f ); - -//----------------------------------------------------------------------------- -// -// IsPointInBox -// -// returns true if the point is in the box -// -//----------------------------------------------------------------------------- -bool IsPointInBox( const Vector& pt, const Vector& boxMin, const Vector& boxMax ); - - -//----------------------------------------------------------------------------- -// Purpose: returns true if pt intersects the truncated cone -// origin - cone tip, axis unit cone axis, cosAngle - cosine of cone axis to surface angle -//----------------------------------------------------------------------------- -bool IsPointInCone( const Vector &pt, const Vector &origin, const Vector &axis, float cosAngle, float length ); - -//----------------------------------------------------------------------------- -// Intersects a plane with a triangle (using barycentric definition) -// The return value, in pIntersection, is an array of barycentric coordinates -// describing at most 2 intersection points. -// The return value is the number of intersection points -//----------------------------------------------------------------------------- -int IntersectTriangleWithPlaneBarycentric( const Vector& org, const Vector& edgeU, const Vector& edgeV, - const Vector4D& plane, Vector2D* pIntersection ); - -//----------------------------------------------------------------------------- -// -// PointInQuadBarycentric -// -// Given a point and a quad in a plane return the u and v (barycentric) positions -// of the point relative to the quad. The points (v1,v2,v3,v4) should be given -// in a counter-clockwise order with v1 acting as the primary corner (u=0, v=0). -// Thus, u0 = v2 - v1, and v0 = v4 - v1. -// -//----------------------------------------------------------------------------- - -enum QuadBarycentricRetval_t -{ - BARY_QUADRATIC_FALSE = 0, - BARY_QUADRATIC_TRUE = 1, - BARY_QUADRATIC_NEGATIVE_DISCRIMINANT = 2 -}; - -QuadBarycentricRetval_t PointInQuadToBarycentric( const Vector &v1, const Vector &v2, - const Vector &v3, const Vector &v4, const Vector &point, Vector2D &uv ); - - -void PointInQuadFromBarycentric( const Vector &v1, const Vector &v2, const Vector &v3, const Vector &v4, - const Vector2D &uv, Vector &point ); -void TexCoordInQuadFromBarycentric( const Vector2D &v1, const Vector2D &v2, const Vector2D &v3, const Vector2D &v4, - const Vector2D &uv, Vector2D &texCoord ); - - -//----------------------------------------------------------------------------- -// Compute point from barycentric specification -// Edge u goes from v0 to v1, edge v goes from v0 to v2 -//----------------------------------------------------------------------------- -void ComputePointFromBarycentric( const Vector& v0, const Vector& v1, const Vector& v2, - float u, float v, Vector& pt ); -void ComputePointFromBarycentric( const Vector2D& v0, const Vector2D& v1, const Vector2D& v2, - float u, float v, Vector2D& pt ); - - -//----------------------------------------------------------------------------- -// Swept OBB test -//----------------------------------------------------------------------------- -bool IsRayIntersectingOBB( const Ray_t &ray, const Vector& org, const QAngle& angles, - const Vector& mins, const Vector& maxs ); - - -//----------------------------------------------------------------------------- -// Compute a separating plane between two boxes (expensive!) -// Returns false if no separating plane exists -//----------------------------------------------------------------------------- -bool ComputeSeparatingPlane( const Vector& org1, const QAngle& angles1, const Vector& min1, const Vector& max1, - const Vector& org2, const QAngle& angles2, const Vector& min2, const Vector& max2, - float tolerance, cplane_t* pPlane ); - -//----------------------------------------------------------------------------- -// IsBoxIntersectingTriangle -// -// Test for an intersection (overlap) between an axial-aligned bounding -// box (AABB) and a triangle. -// -// Triangle points are in counter-clockwise order with the normal facing "out." -// -// Using the "Separating-Axis Theorem" to test for intersections between -// a triangle and an axial-aligned bounding box (AABB). -// 1. 3 Axis Plane Tests - x, y, z -// 2. 9 Edge Planes Tests - the 3 edges of the triangle crossed with all 3 axial -// planes (x, y, z) -// 3. 1 Face Plane Test - the plane the triangle resides in (cplane_t plane) -//----------------------------------------------------------------------------- -bool IsBoxIntersectingTriangle( const Vector &vecBoxCenter, const Vector &vecBoxExtents, - const Vector &v1, const Vector &v2, const Vector &v3, - const cplane_t &plane, float flTolerance ); - - -Vector CalcClosestPointOnTriangle( const Vector &P, const Vector &v0, const Vector &v1, const Vector &v2 ); - -#endif // COLLISIONUTILS_H \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: Common collision utility methods +// +// $Header: $ +// $NoKeywords: $ +//=============================================================================// + +#ifndef COLLISIONUTILS_H +#define COLLISIONUTILS_H + +#include "tier0/platform.h" + +#ifdef _WIN32 +#pragma once +#endif + + +//----------------------------------------------------------------------------- +// forward declarations +//----------------------------------------------------------------------------- +struct Ray_t; +class Vector; +class Vector2D; +class Vector4D; +struct cplane_t; +class QAngle; +class CBaseTrace; +struct matrix3x4_t; + + +//----------------------------------------------------------------------------- +// +// IntersectRayWithTriangle +// +// Intersects a ray with a triangle, returns distance t along ray. +// t will be less than zero if no intersection occurred +// oneSided will cull collisions which approach the triangle from the back +// side, assuming the vertices are specified in counter-clockwise order +// The vertices need not be specified in that order if oneSided is not used +// +//----------------------------------------------------------------------------- +float IntersectRayWithTriangle( const Ray_t& ray, + const Vector& v1, const Vector& v2, const Vector& v3, + bool oneSided ); + +//----------------------------------------------------------------------------- +// +// ComputeIntersectionBarycentricCoordinates +// +// Figures out the barycentric coordinates (u,v) where a ray hits a +// triangle. Note that this will ignore the ray extents, and it also ignores +// the ray length. Note that the edge from v1->v2 represents u (v2: u = 1), +// and the edge from v1->v3 represents v (v3: v = 1). It returns false +// if the ray is parallel to the triangle (or when t is specified if t is less +// than zero). +// +//----------------------------------------------------------------------------- +bool ComputeIntersectionBarycentricCoordinates( const Ray_t& ray, + const Vector& v1, const Vector& v2, const Vector& v3, float& u, float& v, + float *t = 0 ); + +//----------------------------------------------------------------------------- +// +// IntersectRayWithRay +// +// Returns whether or not there was an intersection. The "t" paramter is the +// distance along ray0 and the "s" parameter is the distance along ray1. If +// the two lines to not intersect the "t" and "s" represent the closest approach. +// "t" and "s" will not change if the rays are parallel. +// +//----------------------------------------------------------------------------- +bool IntersectRayWithRay( const Ray_t &ray0, const Ray_t &ray1, float &t, float &s ); + + +//----------------------------------------------------------------------------- +// +// IntersectRayWithSphere +// +// Returns whether or not there was an intersection. Returns the two intersection points. +// NOTE: The point of closest approach can be found at the average t value. +// +//----------------------------------------------------------------------------- +bool IntersectRayWithSphere( const Vector &vecRayOrigin, const Vector &vecRayDelta, const Vector &vecSphereCenter, float flRadius, float *pT1, float *pT2 ); + + +//----------------------------------------------------------------------------- +// +// IntersectInfiniteRayWithSphere +// +// Returns whether or not there was an intersection of a sphere against an infinitely +// extending ray. +// Returns the two intersection points +// +//----------------------------------------------------------------------------- +bool IntersectInfiniteRayWithSphere( const Vector &vecRayOrigin, const Vector &vecRayDelta, + const Vector &vecSphereCenter, float flRadius, float *pT1, float *pT2 ); + + +//----------------------------------------------------------------------------- +// +// IntersectRayWithPlane +// +// Intersects a ray with a plane, returns distance t along ray. +// t will be less than zero the intersection occurs in the opposite direction of the ray. +// +//----------------------------------------------------------------------------- +float IntersectRayWithPlane( const Ray_t& ray, const cplane_t& plane ); +float IntersectRayWithPlane( const Vector& org, const Vector& dir, const cplane_t& plane ); +float IntersectRayWithPlane( const Vector& org, const Vector& dir, const Vector& normal, float dist ); + +// This version intersects a ray with an axis-aligned plane +float IntersectRayWithAAPlane( const Vector& vecStart, const Vector& vecEnd, int nAxis, float flSign, float flDist ); + + +//----------------------------------------------------------------------------- +// IntersectRayWithBox +// +// Purpose: Computes the intersection of a ray with a box (AABB) +// Output : Returns true if there is an intersection + trace information +//----------------------------------------------------------------------------- +bool IntersectRayWithBox( const Vector &rayStart, const Vector &rayDelta, const Vector &boxMins, const Vector &boxMaxs, float epsilon, CBaseTrace *pTrace ); +bool IntersectRayWithBox( const Ray_t &ray, const Vector &boxMins, const Vector &boxMaxs, float epsilon, CBaseTrace *pTrace ); + +//----------------------------------------------------------------------------- +// Intersects a ray against a box +//----------------------------------------------------------------------------- +struct BoxTraceInfo_t +{ + float t1; + float t2; + int hitside; + bool startsolid; +}; + +bool IntersectRayWithBox( const Vector &vecRayStart, const Vector &vecRayDelta, + const Vector &boxMins, const Vector &boxMaxs, float flTolerance, BoxTraceInfo_t *pTrace ); + + +//----------------------------------------------------------------------------- +// IntersectRayWithOBB +// +// Purpose: Computes the intersection of a ray with a oriented box (OBB) +// Output : Returns true if there is an intersection + trace information +//----------------------------------------------------------------------------- +bool IntersectRayWithOBB( const Vector &vecRayStart, const Vector &vecRayDelta, + const matrix3x4_t &matOBBToWorld, const Vector &vecOBBMins, const Vector &vecOBBMaxs, + float flTolerance, CBaseTrace *pTrace ); + +bool IntersectRayWithOBB( const Vector &vecRayOrigin, const Vector &vecRayDelta, + const Vector &vecBoxOrigin, const QAngle &angBoxRotation, + const Vector &vecOBBMins, const Vector &vecOBBMaxs, float flTolerance, CBaseTrace *pTrace ); + +bool IntersectRayWithOBB( const Ray_t &ray, const Vector &vecBoxOrigin, const QAngle &angBoxRotation, + const Vector &vecOBBMins, const Vector &vecOBBMaxs, float flTolerance, CBaseTrace *pTrace ); + +bool IntersectRayWithOBB( const Ray_t &ray, const matrix3x4_t &matOBBToWorld, + const Vector &vecOBBMins, const Vector &vecOBBMaxs, float flTolerance, CBaseTrace *pTrace ); + +bool IntersectRayWithOBB( const Vector &vecRayStart, const Vector &vecRayDelta, + const matrix3x4_t &matOBBToWorld, const Vector &vecOBBMins, const Vector &vecOBBMaxs, + float flTolerance, BoxTraceInfo_t *pTrace ); + +//----------------------------------------------------------------------------- +// +// IsSphereIntersectingSphere +// +// returns true if there's an intersection between sphere and sphere +// +//----------------------------------------------------------------------------- +bool IsSphereIntersectingSphere( const Vector& center1, float radius1, + const Vector& center2, float radius2 ); + + +//----------------------------------------------------------------------------- +// +// IsBoxIntersectingSphere +// +// returns true if there's an intersection between box and sphere +// +//----------------------------------------------------------------------------- +bool IsBoxIntersectingSphere( const Vector& boxMin, const Vector& boxMax, + const Vector& center, float radius ); + +bool IsBoxIntersectingSphereExtents( const Vector& boxCenter, const Vector& boxHalfDiag, + const Vector& center, float radius ); + +//----------------------------------------------------------------------------- +// returns true if there's an intersection between ray and sphere +//----------------------------------------------------------------------------- +bool IsRayIntersectingSphere( const Vector &vecRayOrigin, const Vector &vecRayDelta, + const Vector &vecSphereCenter, float flRadius, float flTolerance = 0.0f ); + + +//----------------------------------------------------------------------------- +// +// IsCircleIntersectingRectangle +// +// returns true if there's an intersection between rectangle and circle +// +//----------------------------------------------------------------------------- +bool IsCircleIntersectingRectangle( const Vector2D& boxMin, const Vector2D& boxMax, + const Vector2D& center, float radius ); + + +//----------------------------------------------------------------------------- +// +// IsBoxIntersectingBox +// +// returns true if there's an intersection between two boxes +// +//----------------------------------------------------------------------------- +bool IsBoxIntersectingBox( const Vector& boxMin1, const Vector& boxMax1, + const Vector& boxMin2, const Vector& boxMax2 ); + +bool IsBoxIntersectingBoxExtents( const Vector& boxCenter1, const Vector& boxHalfDiagonal1, + const Vector& boxCenter2, const Vector& boxHalfDiagonal2 ); + + +//----------------------------------------------------------------------------- +// +// IsOBBIntersectingOBB +// +// returns true if there's an intersection between two OBBs +// +//----------------------------------------------------------------------------- +bool IsOBBIntersectingOBB( const Vector &vecOrigin1, const QAngle &vecAngles1, const Vector& boxMin1, const Vector& boxMax1, + const Vector &vecOrigin2, const QAngle &vecAngles2, const Vector& boxMin2, const Vector& boxMax2, float flTolerance = 0.0f ); + + +//----------------------------------------------------------------------------- +// +// IsBoxIntersectingRay +// +// returns true if there's an intersection between box and ray +// +//----------------------------------------------------------------------------- +bool FASTCALL IsBoxIntersectingRay( const Vector& boxMin, const Vector& boxMax, + const Vector& origin, const Vector& delta, float flTolerance = 0.0f ); + +bool FASTCALL IsBoxIntersectingRay( const Vector& boxMin, const Vector& boxMax, + const Ray_t& ray, float flTolerance = 0.0f ); + +bool FASTCALL IsBoxIntersectingRay( const Vector& boxMin, const Vector& boxMax, + const Vector& origin, const Vector& delta, + const Vector& invDelta, float flTolerance = 0.0f ); + +//----------------------------------------------------------------------------- +// +// IsPointInBox +// +// returns true if the point is in the box +// +//----------------------------------------------------------------------------- +bool IsPointInBox( const Vector& pt, const Vector& boxMin, const Vector& boxMax ); + + +//----------------------------------------------------------------------------- +// Purpose: returns true if pt intersects the truncated cone +// origin - cone tip, axis unit cone axis, cosAngle - cosine of cone axis to surface angle +//----------------------------------------------------------------------------- +bool IsPointInCone( const Vector &pt, const Vector &origin, const Vector &axis, float cosAngle, float length ); + +//----------------------------------------------------------------------------- +// Intersects a plane with a triangle (using barycentric definition) +// The return value, in pIntersection, is an array of barycentric coordinates +// describing at most 2 intersection points. +// The return value is the number of intersection points +//----------------------------------------------------------------------------- +int IntersectTriangleWithPlaneBarycentric( const Vector& org, const Vector& edgeU, const Vector& edgeV, + const Vector4D& plane, Vector2D* pIntersection ); + +//----------------------------------------------------------------------------- +// +// PointInQuadBarycentric +// +// Given a point and a quad in a plane return the u and v (barycentric) positions +// of the point relative to the quad. The points (v1,v2,v3,v4) should be given +// in a counter-clockwise order with v1 acting as the primary corner (u=0, v=0). +// Thus, u0 = v2 - v1, and v0 = v4 - v1. +// +//----------------------------------------------------------------------------- + +enum QuadBarycentricRetval_t +{ + BARY_QUADRATIC_FALSE = 0, + BARY_QUADRATIC_TRUE = 1, + BARY_QUADRATIC_NEGATIVE_DISCRIMINANT = 2 +}; + +QuadBarycentricRetval_t PointInQuadToBarycentric( const Vector &v1, const Vector &v2, + const Vector &v3, const Vector &v4, const Vector &point, Vector2D &uv ); + + +void PointInQuadFromBarycentric( const Vector &v1, const Vector &v2, const Vector &v3, const Vector &v4, + const Vector2D &uv, Vector &point ); +void TexCoordInQuadFromBarycentric( const Vector2D &v1, const Vector2D &v2, const Vector2D &v3, const Vector2D &v4, + const Vector2D &uv, Vector2D &texCoord ); + + +//----------------------------------------------------------------------------- +// Compute point from barycentric specification +// Edge u goes from v0 to v1, edge v goes from v0 to v2 +//----------------------------------------------------------------------------- +void ComputePointFromBarycentric( const Vector& v0, const Vector& v1, const Vector& v2, + float u, float v, Vector& pt ); +void ComputePointFromBarycentric( const Vector2D& v0, const Vector2D& v1, const Vector2D& v2, + float u, float v, Vector2D& pt ); + + +//----------------------------------------------------------------------------- +// Swept OBB test +//----------------------------------------------------------------------------- +bool IsRayIntersectingOBB( const Ray_t &ray, const Vector& org, const QAngle& angles, + const Vector& mins, const Vector& maxs ); + + +//----------------------------------------------------------------------------- +// Compute a separating plane between two boxes (expensive!) +// Returns false if no separating plane exists +//----------------------------------------------------------------------------- +bool ComputeSeparatingPlane( const Vector& org1, const QAngle& angles1, const Vector& min1, const Vector& max1, + const Vector& org2, const QAngle& angles2, const Vector& min2, const Vector& max2, + float tolerance, cplane_t* pPlane ); + +//----------------------------------------------------------------------------- +// IsBoxIntersectingTriangle +// +// Test for an intersection (overlap) between an axial-aligned bounding +// box (AABB) and a triangle. +// +// Triangle points are in counter-clockwise order with the normal facing "out." +// +// Using the "Separating-Axis Theorem" to test for intersections between +// a triangle and an axial-aligned bounding box (AABB). +// 1. 3 Axis Plane Tests - x, y, z +// 2. 9 Edge Planes Tests - the 3 edges of the triangle crossed with all 3 axial +// planes (x, y, z) +// 3. 1 Face Plane Test - the plane the triangle resides in (cplane_t plane) +//----------------------------------------------------------------------------- +bool IsBoxIntersectingTriangle( const Vector &vecBoxCenter, const Vector &vecBoxExtents, + const Vector &v1, const Vector &v2, const Vector &v3, + const cplane_t &plane, float flTolerance ); + + +Vector CalcClosestPointOnTriangle( const Vector &P, const Vector &v0, const Vector &v1, const Vector &v2 ); + +#endif // COLLISIONUTILS_H diff --git a/public/coordsize.h b/public/coordsize.h index 1c0addb6..2aebeab8 100644 --- a/public/coordsize.h +++ b/public/coordsize.h @@ -1,92 +1,92 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -// -//=============================================================================// - -#ifndef COORDSIZE_H -#define COORDSIZE_H -#pragma once - -#include "worldsize.h" - -// OVERALL Coordinate Size Limits used in COMMON.C MSG_*BitCoord() Routines (and someday the HUD) -#define COORD_INTEGER_BITS 14 -#define COORD_FRACTIONAL_BITS 5 -#define COORD_DENOMINATOR (1<<(COORD_FRACTIONAL_BITS)) -#define COORD_RESOLUTION (1.0/(COORD_DENOMINATOR)) - -#define NORMAL_FRACTIONAL_BITS 11 -#define NORMAL_DENOMINATOR ( (1<<(NORMAL_FRACTIONAL_BITS)) - 1 ) -#define NORMAL_RESOLUTION (1.0/(NORMAL_DENOMINATOR)) - -// this is limited by the network fractional bits used for coords -// because net coords will be only be accurate to 5 bits fractional -// Standard collision test epsilon -// 1/32nd inch collision epsilon -#define DIST_EPSILON (0.03125) - -// Verify that coordsize.h and worldsize.h are consistently defined -#if (MAX_COORD_INTEGER != (1<m) #endif @@ -175,7 +175,7 @@ DECLARE_FIELD_SIZE( FIELD_MATERIALINDEX, sizeof(int) ) // INPUTS #define DEFINE_INPUT( name, fieldtype, inputname ) { fieldtype, #name, { offsetof(classNameTypedef, name), 0 }, 1, FTYPEDESC_INPUT | FTYPEDESC_SAVE | FTYPEDESC_KEY, inputname, NULL, NULL, NULL, sizeof( ((classNameTypedef *)0)->name ) } -#define DEFINE_INPUTFUNC( fieldtype, inputname, inputfunc ) { fieldtype, #inputfunc, { NULL, NULL }, 1, FTYPEDESC_INPUT, inputname, NULL, static_cast (&classNameTypedef::inputfunc) } +#define DEFINE_INPUTFUNC( fieldtype, inputname, inputfunc ) { fieldtype, #inputfunc, { 0, 0 }, 1, FTYPEDESC_INPUT, inputname, NULL, static_cast (&classNameTypedef::inputfunc) } // OUTPUTS // the variable 'name' MUST BE derived from CBaseOutput @@ -185,7 +185,7 @@ extern ISaveRestoreOps *eventFuncs; #define DEFINE_OUTPUT( name, outputname ) { FIELD_CUSTOM, #name, { offsetof(classNameTypedef, name), 0 }, 1, FTYPEDESC_OUTPUT | FTYPEDESC_SAVE | FTYPEDESC_KEY, outputname, eventFuncs } // replaces EXPORT table for portability and non-DLL based systems (xbox) -#define DEFINE_FUNCTION_RAW( function, func_type ) { FIELD_VOID, nameHolder.GenerateName(#function), { NULL, NULL }, 1, FTYPEDESC_FUNCTIONTABLE, NULL, NULL, (inputfunc_t)((func_type)(&classNameTypedef::function)) } +#define DEFINE_FUNCTION_RAW( function, func_type ) { FIELD_VOID, nameHolder.GenerateName(#function), { 0, 0 }, 1, FTYPEDESC_FUNCTIONTABLE, NULL, NULL, (inputfunc_t)((func_type)(&classNameTypedef::function)) } #define DEFINE_FUNCTION( function ) DEFINE_FUNCTION_RAW( function, inputfunc_t ) diff --git a/public/datamodel/idatamodel.h b/public/datamodel/idatamodel.h index 89e3b1fa..c57aa707 100644 --- a/public/datamodel/idatamodel.h +++ b/public/datamodel/idatamodel.h @@ -237,8 +237,8 @@ abstract_class IUndoElement { protected: explicit IUndoElement( char const *desc ) : - m_pDesc( desc ), - m_bEndOfStream( false ) + m_bEndOfStream( false ), + m_pDesc( desc ) { } public: diff --git a/public/disp_powerinfo.cpp b/public/disp_powerinfo.cpp index 2f43d4db..ed3aca78 100644 --- a/public/disp_powerinfo.cpp +++ b/public/disp_powerinfo.cpp @@ -1,580 +1,580 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -//=============================================================================// - -#include "disp_powerinfo.h" -#include "disp_common.h" -#include "commonmacros.h" - -// memdbgon must be the last include file in a .cpp file!!! -#include "tier0/memdbgon.h" - -// ------------------------------------------------------------------------ // -// Internal classes. -// ------------------------------------------------------------------------ // - -// These point at the vertices connecting to each of the [north,south,east,west] vertices. -class CVertCorners -{ -public: - short m_Corner1[2]; - short m_Corner2[2]; -}; - - -// ------------------------------------------------------------------------ // -// Globals. -// ------------------------------------------------------------------------ // - -// This points at vertices to the side of a node (north, south, east, west). -static short g_SideVertMul[4][2] = { {1,0}, {0,1}, {-1,0}, {0,-1} }; - -static CVertCorners g_SideVertCorners[4] = -{ - { {1,-1}, {1,1} }, - { {1,1}, {-1,1} }, - { {-1,1}, {-1,-1} }, - { {-1,-1}, {1,-1} } -}; - -// This is used in loops on child nodes. The indices point at the nodes: -// 0 = upper-right -// 1 = upper-left -// 2 = lower-left -// 3 = lower-right -static CVertIndex g_ChildNodeIndexMul[4] = -{ - CVertIndex(1,1), - CVertIndex(-1,1), - CVertIndex(-1,-1), - CVertIndex(1,-1) -}; - -// These are multipliers on vertMul (not nodeMul). -static CVertIndex g_ChildNodeDependencies[4][2] = -{ - { CVertIndex(1,0), CVertIndex(0,1) }, - { CVertIndex(0,1), CVertIndex(-1,0) }, - { CVertIndex(-1,0), CVertIndex(0,-1) }, - { CVertIndex(0,-1), CVertIndex(1,0) } -}; - -// 2x2 rotation matrices for each orientation. -static int g_OrientationRotations[4][2][2] = -{ - {{1, 0}, // CCW_0 - {0, 1}}, - - {{0, 1}, // CCW_90 - {-1,0}}, - - {{-1,0}, // CCW_180 - {0,-1}}, - - {{0, -1}, // CCW_270 - {1, 0}} -}; - - -// ------------------------------------------------------------------------ // -// Helper functions. -// ------------------------------------------------------------------------ // - -// Apply a 2D rotation to the specified CVertIndex around the specified centerpoint. -static CVertIndex Transform2D( - int const mat[2][2], - CVertIndex const &vert, - CVertIndex const ¢erPoint ) -{ - CVertIndex translated = vert - centerPoint; - - CVertIndex transformed( - translated.x*mat[0][0] + translated.y*mat[0][1], - translated.x*mat[1][0] + translated.y*mat[1][1] ); - - return transformed + centerPoint; -} - - -// Rotate a given CVertIndex with a specified orientation. -// Do this with a lookup table eventually! -static void GetEdgeVertIndex( int sideLength, int iEdge, int iVert, CVertIndex &out ) -{ - if( iEdge == NEIGHBOREDGE_RIGHT ) - { - out.x = sideLength - 1; - out.y = iVert; - } - else if( iEdge == NEIGHBOREDGE_TOP ) - { - out.x = iVert; - out.y = sideLength - 1; - } - else if( iEdge == NEIGHBOREDGE_LEFT ) - { - out.x = 0; - out.y = iVert; - } - else - { - out.x = iVert; - out.y = 0; - } -} - - -// Generate an index given a CVertIndex and the size of the displacement it resides in. -static int VertIndex( CVertIndex const &vert, int iMaxPower ) -{ - return vert.y * ((1 << iMaxPower) + 1) + vert.x; -} - - -static CVertIndex WrapVertIndex( CVertIndex const &in, int sideLength ) -{ - int out[2]; - - for( int i=0; i < 2; i++ ) - { - if( in[i] < 0 ) - out[i] = sideLength - 1 - (-in[i] % sideLength); - else if( in[i] >= sideLength ) - out[i] = in[i] % sideLength; - else - out[i] = in[i]; - } - - return CVertIndex( out[0], out[1] ); -} - - -static int GetFreeDependency( CVertDependency *pDep, int nElements ) -{ - for( int i=0; i < nElements; i++ ) - { - if( !pDep[i].IsValid() ) - return i; - } - - Assert( false ); - return 0; -} - - -static void AddDependency( - CVertInfo *dependencies, - int sideLength, - CVertIndex const &nodeIndex, - CVertIndex const &dependency, - int iMaxPower, - bool bCheckNeighborDependency, - bool bAddReverseDependency ) -{ - int iNodeIndex = VertIndex( nodeIndex, iMaxPower ); - CVertInfo *pNode = &dependencies[iNodeIndex]; - - int iDep = GetFreeDependency( pNode->m_Dependencies, sizeof(pNode->m_Dependencies)/sizeof(pNode->m_Dependencies[0]) ); - pNode->m_Dependencies[iDep].m_iVert = dependency; - pNode->m_Dependencies[iDep].m_iNeighbor = -1; - - if( bAddReverseDependency ) - { - CVertInfo *pDep = &dependencies[VertIndex( dependency, iMaxPower )]; - iDep = GetFreeDependency( pDep->m_ReverseDependencies, CVertInfo::NUM_REVERSE_DEPENDENCIES ); - pDep->m_ReverseDependencies[iDep].m_iVert = nodeIndex; - pDep->m_ReverseDependencies[iDep].m_iNeighbor = -1; - } - - // Edge verts automatically add a dependency for the neighbor. - // Internal verts wind up in here twice anyway so it doesn't need to - if( bCheckNeighborDependency ) - { - int iConnection = GetEdgeIndexFromPoint( nodeIndex, iMaxPower ); - if( iConnection != -1 ) - { - Assert( !pNode->m_Dependencies[1].IsValid() ); - - CVertIndex delta( nodeIndex.x - dependency.x, nodeIndex.y - dependency.y ); - CVertIndex newIndex( nodeIndex.x + delta.x, nodeIndex.y + delta.y ); - - int fullSideLength = (1 << iMaxPower) + 1; - pNode->m_Dependencies[1].m_iVert = WrapVertIndex( CVertIndex( newIndex.x, newIndex.y ), fullSideLength ); - pNode->m_Dependencies[1].m_iNeighbor = iConnection; - } - } -} - - -// --------------------------------------------------------------------------------- // -// CTesselateWinding stuff. -// --------------------------------------------------------------------------------- // - -CTesselateVert::CTesselateVert( CVertIndex const &index, int iNode ) - : m_Index( index ) -{ - m_iNode = iNode; -} - - -CVertInfo::CVertInfo() -{ - int i; - for( i=0; i < sizeof(m_Dependencies)/sizeof(m_Dependencies[0]); i++ ) - { - m_Dependencies[i].m_iVert = CVertIndex( -1, -1 ); - m_Dependencies[i].m_iNeighbor = -1; - } - - for( i=0; i < sizeof(m_ReverseDependencies)/sizeof(m_ReverseDependencies[0]); i++ ) - { - m_ReverseDependencies[i].m_iVert = CVertIndex( -1, -1 ); - m_ReverseDependencies[i].m_iNeighbor = -1; - } - - m_iParent.x = m_iParent.y = -1; - m_iNodeLevel = -1; -} - - -CTesselateVert g_TesselateVerts[] = -{ - CTesselateVert( CVertIndex(1,-1), CHILDNODE_LOWER_RIGHT), - CTesselateVert( CVertIndex(0,-1), -1), - CTesselateVert( CVertIndex(-1,-1), CHILDNODE_LOWER_LEFT), - CTesselateVert( CVertIndex(-1, 0), -1), - CTesselateVert( CVertIndex(-1, 1), CHILDNODE_UPPER_LEFT), - CTesselateVert( CVertIndex(0, 1), -1), - CTesselateVert( CVertIndex(1, 1), CHILDNODE_UPPER_RIGHT), - CTesselateVert( CVertIndex(1, 0), -1), - CTesselateVert( CVertIndex(1,-1), CHILDNODE_LOWER_RIGHT) -}; - -CTesselateWinding g_TWinding = -{ - g_TesselateVerts, - sizeof( g_TesselateVerts ) / sizeof( g_TesselateVerts[0] ) -}; - - - -// --------------------------------------------------------------------------------- // -// CPowerInfo stuff. -// --------------------------------------------------------------------------------- // - -// Precalculated info about each particular displacement size. -#define DECLARE_TABLES( size ) \ - static CVertInfo g_VertInfo_##size##x##size[ size*size ]; \ - static CFourVerts g_SideVerts_##size##x##size[ size*size ]; \ - static CFourVerts g_ChildVerts_##size##x##size[ size*size ]; \ - static CFourVerts g_SideVertCorners_##size##x##size[ size*size ]; \ - static CTwoUShorts g_ErrorEdges_##size##x##size[ size*size ]; \ - static CTriInfo g_TriInfos_##size##x##size[ (size-1)*(size-1)*2 ]; \ - static CPowerInfo g_PowerInfo_##size##x##size( \ - g_VertInfo_##size##x##size, \ - g_SideVerts_##size##x##size, \ - g_ChildVerts_##size##x##size, \ - g_SideVertCorners_##size##x##size,\ - g_ErrorEdges_##size##x##size, \ - g_TriInfos_##size##x##size \ - ) - -#define POWERINFO_ENTRY( size ) \ - (&g_PowerInfo_##size##x##size) - -DECLARE_TABLES( 5 ); -DECLARE_TABLES( 9 ); -DECLARE_TABLES( 17 ); - - -// Index by m_Power. -CPowerInfo *g_PowerInfos[NUM_POWERINFOS] = -{ - NULL, - NULL, - POWERINFO_ENTRY(5), - POWERINFO_ENTRY(9), - POWERINFO_ENTRY(17) -}; - - -CPowerInfo::CPowerInfo( - CVertInfo *pVertInfo, - CFourVerts *pSideVerts, - CFourVerts *pChildVerts, - CFourVerts *pSideVertCorners, - CTwoUShorts *pErrorEdges, - CTriInfo *pTriInfos ) -{ - m_pVertInfo = pVertInfo; - m_pSideVerts = pSideVerts; - m_pChildVerts = pChildVerts; - m_pSideVertCorners = pSideVertCorners; - m_pErrorEdges = pErrorEdges; - m_pTriInfos = pTriInfos; -} - -static void InitPowerInfoTriInfos_R( - CPowerInfo *pInfo, - CVertIndex const &nodeIndex, - CTriInfo* &pTriInfo, - int iMaxPower, - int iLevel ) -{ - int iNodeIndex = VertIndex( nodeIndex, iMaxPower ); - - if( iLevel+1 < iMaxPower ) - { - // Recurse into children. - for( int iChild=0; iChild < 4; iChild++ ) - { - InitPowerInfoTriInfos_R( - pInfo, - pInfo->m_pChildVerts[iNodeIndex].m_Verts[iChild], - pTriInfo, - iMaxPower, - iLevel+1 ); - } - } - else - { - unsigned short indices[3]; - - int vertInc = 1 << ((iMaxPower - iLevel) - 1); - - // We're at a leaf, generate the tris. - CTesselateWinding *pWinding = &g_TWinding; - - // Starting at the bottom-left, wind clockwise picking up vertices and - // generating triangles. - int iCurTriVert = 0; - for( int iVert=0; iVert < pWinding->m_nVerts; iVert++ ) - { - CVertIndex sideVert = BuildOffsetVertIndex( nodeIndex, pWinding->m_Verts[iVert].m_Index, vertInc ); - - if( iCurTriVert == 1 ) - { - // Add this vert and finish the tri. - pTriInfo->m_Indices[0] = indices[0]; - pTriInfo->m_Indices[1] = VertIndex( sideVert, iMaxPower ); - pTriInfo->m_Indices[2] = iNodeIndex; - ++pTriInfo; - } - - indices[0] = VertIndex( sideVert, iMaxPower ); - iCurTriVert = 1; - } - } -} - - -static void InitPowerInfo_R( - CPowerInfo *pPowerInfo, - int iMaxPower, - CVertIndex const &nodeIndex, - CVertIndex const &dependency1, - CVertIndex const &dependency2, - CVertIndex const &nodeEdge1, - CVertIndex const &nodeEdge2, - CVertIndex const &iParent, - int iLevel ) -{ - int sideLength = ((1 << iMaxPower) + 1); - int iNodeIndex = VertIndex( nodeIndex, iMaxPower ); - - pPowerInfo->m_pVertInfo[iNodeIndex].m_iParent = iParent; - pPowerInfo->m_pVertInfo[iNodeIndex].m_iNodeLevel = iLevel + 1; - - pPowerInfo->m_pErrorEdges[iNodeIndex].m_Values[0] = (unsigned short)(VertIndex( nodeEdge1, iMaxPower )); - pPowerInfo->m_pErrorEdges[iNodeIndex].m_Values[1] = (unsigned short)(VertIndex( nodeEdge2, iMaxPower )); - - // Add this node's dependencies. - AddDependency( pPowerInfo->m_pVertInfo, sideLength, nodeIndex, dependency1, iMaxPower, false, true ); - AddDependency( pPowerInfo->m_pVertInfo, sideLength, nodeIndex, dependency2, iMaxPower, false, true ); - - // The 4 side vertices depend on this node. - int iPower = iMaxPower - iLevel; - int vertInc = 1 << (iPower - 1); - - for( int iSide=0; iSide < 4; iSide++ ) - { - // Store the side vert index. - CVertIndex sideVert( nodeIndex.x + g_SideVertMul[iSide][0]*vertInc, nodeIndex.y + g_SideVertMul[iSide][1]*vertInc ); - int iSideVert = VertIndex( sideVert, iMaxPower ); - - pPowerInfo->m_pSideVerts[iNodeIndex].m_Verts[iSide] = sideVert; - - // Store the side vert corners. - CVertIndex sideVertCorner0 = CVertIndex( nodeIndex.x + g_SideVertCorners[iSide].m_Corner1[0]*vertInc, nodeIndex.y + g_SideVertCorners[iSide].m_Corner1[1]*vertInc ); - CVertIndex sideVertCorner1 = CVertIndex( nodeIndex.x + g_SideVertCorners[iSide].m_Corner2[0]*vertInc, nodeIndex.y + g_SideVertCorners[iSide].m_Corner2[1]*vertInc ); - - pPowerInfo->m_pSideVertCorners[iNodeIndex].m_Verts[iSide] = sideVertCorner0; - - // Write the side vert corners into the error-edges list. - pPowerInfo->m_pErrorEdges[iSideVert].m_Values[0] = (unsigned short)VertIndex( sideVertCorner0, iMaxPower ); - pPowerInfo->m_pErrorEdges[iSideVert].m_Values[1] = (unsigned short)VertIndex( sideVertCorner1, iMaxPower ); - - AddDependency( - pPowerInfo->m_pVertInfo, - sideLength, - sideVert, - nodeIndex, - iMaxPower, - true, - true ); - } - - // Recurse into the children. - int nodeInc = vertInc >> 1; - if( nodeInc ) - { - for( int iChild=0; iChild < 4; iChild++ ) - { - CVertIndex childVert( nodeIndex.x + g_ChildNodeIndexMul[iChild].x * nodeInc, nodeIndex.y + g_ChildNodeIndexMul[iChild].y * nodeInc ); - - pPowerInfo->m_pChildVerts[iNodeIndex].m_Verts[iChild] = childVert; - - InitPowerInfo_R( pPowerInfo, - iMaxPower, - childVert, - - CVertIndex(nodeIndex.x + g_ChildNodeDependencies[iChild][0].x*vertInc, nodeIndex.y + g_ChildNodeDependencies[iChild][0].y*vertInc), - CVertIndex(nodeIndex.x + g_ChildNodeDependencies[iChild][1].x*vertInc, nodeIndex.y + g_ChildNodeDependencies[iChild][1].y*vertInc), - - nodeIndex, - CVertIndex( nodeIndex.x + g_ChildNodeIndexMul[iChild].x * vertInc, nodeIndex.y + g_ChildNodeIndexMul[iChild].y * vertInc ), - - nodeIndex, - iLevel + 1 ); - } - } -} - - -void InitPowerInfo( CPowerInfo *pInfo, int iMaxPower ) -{ - int sideLength = (1 << iMaxPower) + 1; - - // Precalculate the dependency graph. - CVertIndex nodeDependency1( sideLength-1, sideLength-1 ); - CVertIndex nodeDependency2( 0, 0 ); - - pInfo->m_RootNode = CVertIndex( sideLength/2, sideLength/2 ); - pInfo->m_SideLength = sideLength; - pInfo->m_SideLengthM1 = sideLength - 1; - pInfo->m_MidPoint = sideLength / 2; - pInfo->m_MaxVerts = sideLength * sideLength; - - // Setup the corner indices. - pInfo->m_CornerPointIndices[CORNER_LOWER_LEFT].Init( 0, 0 ); - pInfo->m_CornerPointIndices[CORNER_UPPER_LEFT].Init( 0, sideLength-1 ); - pInfo->m_CornerPointIndices[CORNER_UPPER_RIGHT].Init( sideLength-1, sideLength-1 ); - pInfo->m_CornerPointIndices[CORNER_LOWER_RIGHT].Init( sideLength-1, 0 ); - - InitPowerInfo_R( - pInfo, - iMaxPower, - pInfo->m_RootNode, - - nodeDependency1, // dependencies - nodeDependency2, - - CVertIndex(0,0), // error edge - CVertIndex(sideLength-1, sideLength-1), - - CVertIndex(-1,-1), // parent - 0 ); - - pInfo->m_Power = iMaxPower; - - CTriInfo *pTriInfo = pInfo->m_pTriInfos; - InitPowerInfoTriInfos_R( pInfo, pInfo->m_RootNode, pTriInfo, iMaxPower, 0 ); - - for( int iEdge=0; iEdge < 4; iEdge++ ) - { - // Figure out the start vert and increment. - CVertIndex nextVert; - GetEdgeVertIndex( sideLength, iEdge, 0, pInfo->m_EdgeStartVerts[iEdge] ); - GetEdgeVertIndex( sideLength, iEdge, 1, nextVert ); - pInfo->m_EdgeIncrements[iEdge] = nextVert - pInfo->m_EdgeStartVerts[iEdge]; - - // Now get the neighbor's start vert and increment. - CVertIndex nbStartVert, nbNextVert, nbDelta; - GetEdgeVertIndex( sideLength, (iEdge+2)&3, 0, nbStartVert ); - GetEdgeVertIndex( sideLength, (iEdge+2)&3, 1, nbNextVert ); - nbDelta = nbNextVert - nbStartVert; - - // Rotate it for each orientation. - for( int orient=0; orient < 4; orient++ ) - { - pInfo->m_NeighborStartVerts[iEdge][orient] = Transform2D( - g_OrientationRotations[orient], - nbStartVert, - CVertIndex( sideLength/2, sideLength/2 ) ); - - pInfo->m_NeighborIncrements[iEdge][orient] = Transform2D( - g_OrientationRotations[orient], - nbDelta, - CVertIndex(0,0) ); - } - } - - - // Init the node index increments. - int curPowerOf4 = 1; - int curTotal = 0; - for( int i=0; i < iMaxPower-1; i++ ) - { - curTotal += curPowerOf4; - - pInfo->m_NodeIndexIncrements[iMaxPower-i-2] = curTotal; - - curPowerOf4 *= 4; - } - - // Store off the total node count - pInfo->m_NodeCount = curTotal + curPowerOf4; - - pInfo->m_nTriInfos = Square( 1 << iMaxPower ) * 2; -} - -class CPowerInfoInitializer -{ -public: - CPowerInfoInitializer() - { - Assert( MAX_MAP_DISP_POWER+1 == NUM_POWERINFOS ); - - for( int i=0; i <= MAX_MAP_DISP_POWER; i++ ) - { - if( g_PowerInfos[i] ) - { - InitPowerInfo( g_PowerInfos[i], i ); - } - } - } -}; - -static CPowerInfoInitializer g_PowerInfoInitializer; - - -const CPowerInfo* GetPowerInfo( int iPower ) -{ - Assert( iPower >= 0 && iPower < ARRAYSIZE( g_PowerInfos ) ); - Assert( g_PowerInfos[iPower] ); - return g_PowerInfos[iPower]; -} - - -// ------------------------------------------------------------------------------------------------ // -// CPowerInfo member function initialization. -// ------------------------------------------------------------------------------------------------ // - -const CVertIndex& CPowerInfo::GetCornerPointIndex( int iCorner ) const -{ - Assert( iCorner >= 0 && iCorner < 4 ); - return m_CornerPointIndices[iCorner]; -} - +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include "disp_powerinfo.h" +#include "disp_common.h" +#include "commonmacros.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +// ------------------------------------------------------------------------ // +// Internal classes. +// ------------------------------------------------------------------------ // + +// These point at the vertices connecting to each of the [north,south,east,west] vertices. +class CVertCorners +{ +public: + short m_Corner1[2]; + short m_Corner2[2]; +}; + + +// ------------------------------------------------------------------------ // +// Globals. +// ------------------------------------------------------------------------ // + +// This points at vertices to the side of a node (north, south, east, west). +static short g_SideVertMul[4][2] = { {1,0}, {0,1}, {-1,0}, {0,-1} }; + +static CVertCorners g_SideVertCorners[4] = +{ + { {1,-1}, {1,1} }, + { {1,1}, {-1,1} }, + { {-1,1}, {-1,-1} }, + { {-1,-1}, {1,-1} } +}; + +// This is used in loops on child nodes. The indices point at the nodes: +// 0 = upper-right +// 1 = upper-left +// 2 = lower-left +// 3 = lower-right +static CVertIndex g_ChildNodeIndexMul[4] = +{ + CVertIndex(1,1), + CVertIndex(-1,1), + CVertIndex(-1,-1), + CVertIndex(1,-1) +}; + +// These are multipliers on vertMul (not nodeMul). +static CVertIndex g_ChildNodeDependencies[4][2] = +{ + { CVertIndex(1,0), CVertIndex(0,1) }, + { CVertIndex(0,1), CVertIndex(-1,0) }, + { CVertIndex(-1,0), CVertIndex(0,-1) }, + { CVertIndex(0,-1), CVertIndex(1,0) } +}; + +// 2x2 rotation matrices for each orientation. +static int g_OrientationRotations[4][2][2] = +{ + {{1, 0}, // CCW_0 + {0, 1}}, + + {{0, 1}, // CCW_90 + {-1,0}}, + + {{-1,0}, // CCW_180 + {0,-1}}, + + {{0, -1}, // CCW_270 + {1, 0}} +}; + + +// ------------------------------------------------------------------------ // +// Helper functions. +// ------------------------------------------------------------------------ // + +// Apply a 2D rotation to the specified CVertIndex around the specified centerpoint. +static CVertIndex Transform2D( + int const mat[2][2], + CVertIndex const &vert, + CVertIndex const ¢erPoint ) +{ + CVertIndex translated = vert - centerPoint; + + CVertIndex transformed( + translated.x*mat[0][0] + translated.y*mat[0][1], + translated.x*mat[1][0] + translated.y*mat[1][1] ); + + return transformed + centerPoint; +} + + +// Rotate a given CVertIndex with a specified orientation. +// Do this with a lookup table eventually! +static void GetEdgeVertIndex( int sideLength, int iEdge, int iVert, CVertIndex &out ) +{ + if( iEdge == NEIGHBOREDGE_RIGHT ) + { + out.x = sideLength - 1; + out.y = iVert; + } + else if( iEdge == NEIGHBOREDGE_TOP ) + { + out.x = iVert; + out.y = sideLength - 1; + } + else if( iEdge == NEIGHBOREDGE_LEFT ) + { + out.x = 0; + out.y = iVert; + } + else + { + out.x = iVert; + out.y = 0; + } +} + + +// Generate an index given a CVertIndex and the size of the displacement it resides in. +static int VertIndex( CVertIndex const &vert, int iMaxPower ) +{ + return vert.y * ((1 << iMaxPower) + 1) + vert.x; +} + + +static CVertIndex WrapVertIndex( CVertIndex const &in, int sideLength ) +{ + int out[2]; + + for( int i=0; i < 2; i++ ) + { + if( in[i] < 0 ) + out[i] = sideLength - 1 - (-in[i] % sideLength); + else if( in[i] >= sideLength ) + out[i] = in[i] % sideLength; + else + out[i] = in[i]; + } + + return CVertIndex( out[0], out[1] ); +} + + +static int GetFreeDependency( CVertDependency *pDep, int nElements ) +{ + for( int i=0; i < nElements; i++ ) + { + if( !pDep[i].IsValid() ) + return i; + } + + Assert( false ); + return 0; +} + + +static void AddDependency( + CVertInfo *dependencies, + int sideLength, + CVertIndex const &nodeIndex, + CVertIndex const &dependency, + int iMaxPower, + bool bCheckNeighborDependency, + bool bAddReverseDependency ) +{ + int iNodeIndex = VertIndex( nodeIndex, iMaxPower ); + CVertInfo *pNode = &dependencies[iNodeIndex]; + + int iDep = GetFreeDependency( pNode->m_Dependencies, sizeof(pNode->m_Dependencies)/sizeof(pNode->m_Dependencies[0]) ); + pNode->m_Dependencies[iDep].m_iVert = dependency; + pNode->m_Dependencies[iDep].m_iNeighbor = -1; + + if( bAddReverseDependency ) + { + CVertInfo *pDep = &dependencies[VertIndex( dependency, iMaxPower )]; + iDep = GetFreeDependency( pDep->m_ReverseDependencies, CVertInfo::NUM_REVERSE_DEPENDENCIES ); + pDep->m_ReverseDependencies[iDep].m_iVert = nodeIndex; + pDep->m_ReverseDependencies[iDep].m_iNeighbor = -1; + } + + // Edge verts automatically add a dependency for the neighbor. + // Internal verts wind up in here twice anyway so it doesn't need to + if( bCheckNeighborDependency ) + { + int iConnection = GetEdgeIndexFromPoint( nodeIndex, iMaxPower ); + if( iConnection != -1 ) + { + Assert( !pNode->m_Dependencies[1].IsValid() ); + + CVertIndex delta( nodeIndex.x - dependency.x, nodeIndex.y - dependency.y ); + CVertIndex newIndex( nodeIndex.x + delta.x, nodeIndex.y + delta.y ); + + int fullSideLength = (1 << iMaxPower) + 1; + pNode->m_Dependencies[1].m_iVert = WrapVertIndex( CVertIndex( newIndex.x, newIndex.y ), fullSideLength ); + pNode->m_Dependencies[1].m_iNeighbor = iConnection; + } + } +} + + +// --------------------------------------------------------------------------------- // +// CTesselateWinding stuff. +// --------------------------------------------------------------------------------- // + +CTesselateVert::CTesselateVert( CVertIndex const &index, int iNode ) + : m_Index( index ) +{ + m_iNode = iNode; +} + + +CVertInfo::CVertInfo() +{ + size_t i; + for( i=0; i < sizeof(m_Dependencies)/sizeof(m_Dependencies[0]); i++ ) + { + m_Dependencies[i].m_iVert = CVertIndex( -1, -1 ); + m_Dependencies[i].m_iNeighbor = -1; + } + + for( i=0; i < sizeof(m_ReverseDependencies)/sizeof(m_ReverseDependencies[0]); i++ ) + { + m_ReverseDependencies[i].m_iVert = CVertIndex( -1, -1 ); + m_ReverseDependencies[i].m_iNeighbor = -1; + } + + m_iParent.x = m_iParent.y = -1; + m_iNodeLevel = -1; +} + + +CTesselateVert g_TesselateVerts[] = +{ + CTesselateVert( CVertIndex(1,-1), CHILDNODE_LOWER_RIGHT), + CTesselateVert( CVertIndex(0,-1), -1), + CTesselateVert( CVertIndex(-1,-1), CHILDNODE_LOWER_LEFT), + CTesselateVert( CVertIndex(-1, 0), -1), + CTesselateVert( CVertIndex(-1, 1), CHILDNODE_UPPER_LEFT), + CTesselateVert( CVertIndex(0, 1), -1), + CTesselateVert( CVertIndex(1, 1), CHILDNODE_UPPER_RIGHT), + CTesselateVert( CVertIndex(1, 0), -1), + CTesselateVert( CVertIndex(1,-1), CHILDNODE_LOWER_RIGHT) +}; + +CTesselateWinding g_TWinding = +{ + g_TesselateVerts, + sizeof( g_TesselateVerts ) / sizeof( g_TesselateVerts[0] ) +}; + + + +// --------------------------------------------------------------------------------- // +// CPowerInfo stuff. +// --------------------------------------------------------------------------------- // + +// Precalculated info about each particular displacement size. +#define DECLARE_TABLES( size ) \ + static CVertInfo g_VertInfo_##size##x##size[ size*size ]; \ + static CFourVerts g_SideVerts_##size##x##size[ size*size ]; \ + static CFourVerts g_ChildVerts_##size##x##size[ size*size ]; \ + static CFourVerts g_SideVertCorners_##size##x##size[ size*size ]; \ + static CTwoUShorts g_ErrorEdges_##size##x##size[ size*size ]; \ + static CTriInfo g_TriInfos_##size##x##size[ (size-1)*(size-1)*2 ]; \ + static CPowerInfo g_PowerInfo_##size##x##size( \ + g_VertInfo_##size##x##size, \ + g_SideVerts_##size##x##size, \ + g_ChildVerts_##size##x##size, \ + g_SideVertCorners_##size##x##size,\ + g_ErrorEdges_##size##x##size, \ + g_TriInfos_##size##x##size \ + ) + +#define POWERINFO_ENTRY( size ) \ + (&g_PowerInfo_##size##x##size) + +DECLARE_TABLES( 5 ); +DECLARE_TABLES( 9 ); +DECLARE_TABLES( 17 ); + + +// Index by m_Power. +CPowerInfo *g_PowerInfos[NUM_POWERINFOS] = +{ + NULL, + NULL, + POWERINFO_ENTRY(5), + POWERINFO_ENTRY(9), + POWERINFO_ENTRY(17) +}; + + +CPowerInfo::CPowerInfo( + CVertInfo *pVertInfo, + CFourVerts *pSideVerts, + CFourVerts *pChildVerts, + CFourVerts *pSideVertCorners, + CTwoUShorts *pErrorEdges, + CTriInfo *pTriInfos ) +{ + m_pVertInfo = pVertInfo; + m_pSideVerts = pSideVerts; + m_pChildVerts = pChildVerts; + m_pSideVertCorners = pSideVertCorners; + m_pErrorEdges = pErrorEdges; + m_pTriInfos = pTriInfos; +} + +static void InitPowerInfoTriInfos_R( + CPowerInfo *pInfo, + CVertIndex const &nodeIndex, + CTriInfo* &pTriInfo, + int iMaxPower, + int iLevel ) +{ + int iNodeIndex = VertIndex( nodeIndex, iMaxPower ); + + if( iLevel+1 < iMaxPower ) + { + // Recurse into children. + for( int iChild=0; iChild < 4; iChild++ ) + { + InitPowerInfoTriInfos_R( + pInfo, + pInfo->m_pChildVerts[iNodeIndex].m_Verts[iChild], + pTriInfo, + iMaxPower, + iLevel+1 ); + } + } + else + { + unsigned short indices[3] = {0, 0, 0}; + + int vertInc = 1 << ((iMaxPower - iLevel) - 1); + + // We're at a leaf, generate the tris. + CTesselateWinding *pWinding = &g_TWinding; + + // Starting at the bottom-left, wind clockwise picking up vertices and + // generating triangles. + int iCurTriVert = 0; + for( int iVert=0; iVert < pWinding->m_nVerts; iVert++ ) + { + CVertIndex sideVert = BuildOffsetVertIndex( nodeIndex, pWinding->m_Verts[iVert].m_Index, vertInc ); + + if( iCurTriVert == 1 ) + { + // Add this vert and finish the tri. + pTriInfo->m_Indices[0] = indices[0]; + pTriInfo->m_Indices[1] = VertIndex( sideVert, iMaxPower ); + pTriInfo->m_Indices[2] = iNodeIndex; + ++pTriInfo; + } + + indices[0] = VertIndex( sideVert, iMaxPower ); + iCurTriVert = 1; + } + } +} + + +static void InitPowerInfo_R( + CPowerInfo *pPowerInfo, + int iMaxPower, + CVertIndex const &nodeIndex, + CVertIndex const &dependency1, + CVertIndex const &dependency2, + CVertIndex const &nodeEdge1, + CVertIndex const &nodeEdge2, + CVertIndex const &iParent, + int iLevel ) +{ + int sideLength = ((1 << iMaxPower) + 1); + int iNodeIndex = VertIndex( nodeIndex, iMaxPower ); + + pPowerInfo->m_pVertInfo[iNodeIndex].m_iParent = iParent; + pPowerInfo->m_pVertInfo[iNodeIndex].m_iNodeLevel = iLevel + 1; + + pPowerInfo->m_pErrorEdges[iNodeIndex].m_Values[0] = (unsigned short)(VertIndex( nodeEdge1, iMaxPower )); + pPowerInfo->m_pErrorEdges[iNodeIndex].m_Values[1] = (unsigned short)(VertIndex( nodeEdge2, iMaxPower )); + + // Add this node's dependencies. + AddDependency( pPowerInfo->m_pVertInfo, sideLength, nodeIndex, dependency1, iMaxPower, false, true ); + AddDependency( pPowerInfo->m_pVertInfo, sideLength, nodeIndex, dependency2, iMaxPower, false, true ); + + // The 4 side vertices depend on this node. + int iPower = iMaxPower - iLevel; + int vertInc = 1 << (iPower - 1); + + for( int iSide=0; iSide < 4; iSide++ ) + { + // Store the side vert index. + CVertIndex sideVert( nodeIndex.x + g_SideVertMul[iSide][0]*vertInc, nodeIndex.y + g_SideVertMul[iSide][1]*vertInc ); + int iSideVert = VertIndex( sideVert, iMaxPower ); + + pPowerInfo->m_pSideVerts[iNodeIndex].m_Verts[iSide] = sideVert; + + // Store the side vert corners. + CVertIndex sideVertCorner0 = CVertIndex( nodeIndex.x + g_SideVertCorners[iSide].m_Corner1[0]*vertInc, nodeIndex.y + g_SideVertCorners[iSide].m_Corner1[1]*vertInc ); + CVertIndex sideVertCorner1 = CVertIndex( nodeIndex.x + g_SideVertCorners[iSide].m_Corner2[0]*vertInc, nodeIndex.y + g_SideVertCorners[iSide].m_Corner2[1]*vertInc ); + + pPowerInfo->m_pSideVertCorners[iNodeIndex].m_Verts[iSide] = sideVertCorner0; + + // Write the side vert corners into the error-edges list. + pPowerInfo->m_pErrorEdges[iSideVert].m_Values[0] = (unsigned short)VertIndex( sideVertCorner0, iMaxPower ); + pPowerInfo->m_pErrorEdges[iSideVert].m_Values[1] = (unsigned short)VertIndex( sideVertCorner1, iMaxPower ); + + AddDependency( + pPowerInfo->m_pVertInfo, + sideLength, + sideVert, + nodeIndex, + iMaxPower, + true, + true ); + } + + // Recurse into the children. + int nodeInc = vertInc >> 1; + if( nodeInc ) + { + for( int iChild=0; iChild < 4; iChild++ ) + { + CVertIndex childVert( nodeIndex.x + g_ChildNodeIndexMul[iChild].x * nodeInc, nodeIndex.y + g_ChildNodeIndexMul[iChild].y * nodeInc ); + + pPowerInfo->m_pChildVerts[iNodeIndex].m_Verts[iChild] = childVert; + + InitPowerInfo_R( pPowerInfo, + iMaxPower, + childVert, + + CVertIndex(nodeIndex.x + g_ChildNodeDependencies[iChild][0].x*vertInc, nodeIndex.y + g_ChildNodeDependencies[iChild][0].y*vertInc), + CVertIndex(nodeIndex.x + g_ChildNodeDependencies[iChild][1].x*vertInc, nodeIndex.y + g_ChildNodeDependencies[iChild][1].y*vertInc), + + nodeIndex, + CVertIndex( nodeIndex.x + g_ChildNodeIndexMul[iChild].x * vertInc, nodeIndex.y + g_ChildNodeIndexMul[iChild].y * vertInc ), + + nodeIndex, + iLevel + 1 ); + } + } +} + + +void InitPowerInfo( CPowerInfo *pInfo, int iMaxPower ) +{ + int sideLength = (1 << iMaxPower) + 1; + + // Precalculate the dependency graph. + CVertIndex nodeDependency1( sideLength-1, sideLength-1 ); + CVertIndex nodeDependency2( 0, 0 ); + + pInfo->m_RootNode = CVertIndex( sideLength/2, sideLength/2 ); + pInfo->m_SideLength = sideLength; + pInfo->m_SideLengthM1 = sideLength - 1; + pInfo->m_MidPoint = sideLength / 2; + pInfo->m_MaxVerts = sideLength * sideLength; + + // Setup the corner indices. + pInfo->m_CornerPointIndices[CORNER_LOWER_LEFT].Init( 0, 0 ); + pInfo->m_CornerPointIndices[CORNER_UPPER_LEFT].Init( 0, sideLength-1 ); + pInfo->m_CornerPointIndices[CORNER_UPPER_RIGHT].Init( sideLength-1, sideLength-1 ); + pInfo->m_CornerPointIndices[CORNER_LOWER_RIGHT].Init( sideLength-1, 0 ); + + InitPowerInfo_R( + pInfo, + iMaxPower, + pInfo->m_RootNode, + + nodeDependency1, // dependencies + nodeDependency2, + + CVertIndex(0,0), // error edge + CVertIndex(sideLength-1, sideLength-1), + + CVertIndex(-1,-1), // parent + 0 ); + + pInfo->m_Power = iMaxPower; + + CTriInfo *pTriInfo = pInfo->m_pTriInfos; + InitPowerInfoTriInfos_R( pInfo, pInfo->m_RootNode, pTriInfo, iMaxPower, 0 ); + + for( int iEdge=0; iEdge < 4; iEdge++ ) + { + // Figure out the start vert and increment. + CVertIndex nextVert; + GetEdgeVertIndex( sideLength, iEdge, 0, pInfo->m_EdgeStartVerts[iEdge] ); + GetEdgeVertIndex( sideLength, iEdge, 1, nextVert ); + pInfo->m_EdgeIncrements[iEdge] = nextVert - pInfo->m_EdgeStartVerts[iEdge]; + + // Now get the neighbor's start vert and increment. + CVertIndex nbStartVert, nbNextVert, nbDelta; + GetEdgeVertIndex( sideLength, (iEdge+2)&3, 0, nbStartVert ); + GetEdgeVertIndex( sideLength, (iEdge+2)&3, 1, nbNextVert ); + nbDelta = nbNextVert - nbStartVert; + + // Rotate it for each orientation. + for( int orient=0; orient < 4; orient++ ) + { + pInfo->m_NeighborStartVerts[iEdge][orient] = Transform2D( + g_OrientationRotations[orient], + nbStartVert, + CVertIndex( sideLength/2, sideLength/2 ) ); + + pInfo->m_NeighborIncrements[iEdge][orient] = Transform2D( + g_OrientationRotations[orient], + nbDelta, + CVertIndex(0,0) ); + } + } + + + // Init the node index increments. + int curPowerOf4 = 1; + int curTotal = 0; + for( int i=0; i < iMaxPower-1; i++ ) + { + curTotal += curPowerOf4; + + pInfo->m_NodeIndexIncrements[iMaxPower-i-2] = curTotal; + + curPowerOf4 *= 4; + } + + // Store off the total node count + pInfo->m_NodeCount = curTotal + curPowerOf4; + + pInfo->m_nTriInfos = Square( 1 << iMaxPower ) * 2; +} + +class CPowerInfoInitializer +{ +public: + CPowerInfoInitializer() + { + Assert( MAX_MAP_DISP_POWER+1 == NUM_POWERINFOS ); + + for( int i=0; i <= MAX_MAP_DISP_POWER; i++ ) + { + if( g_PowerInfos[i] ) + { + InitPowerInfo( g_PowerInfos[i], i ); + } + } + } +}; + +static CPowerInfoInitializer g_PowerInfoInitializer; + + +const CPowerInfo* GetPowerInfo( int iPower ) +{ + Assert( iPower >= 0 && iPower < ARRAYSIZE( g_PowerInfos ) ); + Assert( g_PowerInfos[iPower] ); + return g_PowerInfos[iPower]; +} + + +// ------------------------------------------------------------------------------------------------ // +// CPowerInfo member function initialization. +// ------------------------------------------------------------------------------------------------ // + +const CVertIndex& CPowerInfo::GetCornerPointIndex( int iCorner ) const +{ + Assert( iCorner >= 0 && iCorner < 4 ); + return m_CornerPointIndices[iCorner]; +} + diff --git a/public/dispcoll.cpp b/public/dispcoll.cpp index b80cc63c..38e8263c 100644 --- a/public/dispcoll.cpp +++ b/public/dispcoll.cpp @@ -1,1994 +1,1994 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -//=============================================================================// - -#include "BuildDisp.h" -#include "DispColl.h" -#include "tier0/dbg.h" - -//============================================================================= - -const float CDispCollTree::COLLISION_EPSILON = 0.01f; -const float CDispCollTree::ONE_MINUS_COLLISION_EPSILON = 1.0f - COLLISION_EPSILON; - -//============================================================================= -// -// Displacement Collision Triangle Functions -// - - -//----------------------------------------------------------------------------- -// Purpose: initialize the displacement triangles -//----------------------------------------------------------------------------- -void CDispCollTri::Init( void ) -{ - for( int i = 0; i < 3; i++ ) - { - m_Points[i].x = 0.0f; m_Points[i].y = 0.0f; m_Points[i].z = 0.0f; - m_PointNormals[i].x = 0.0f; m_PointNormals[i].y = 0.0f; m_PointNormals[i].z = 0.0f; - } - - m_Normal.x = 0.0f; m_Normal.y = 0.0f; m_Normal.z = 0.0f; - m_Distance = 0.0f; - - m_ProjAxes[0] = -1; - m_ProjAxes[1] = -1; - - m_bIntersect = false; -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -inline void CDispCollTri::SetPoint( int index, Vector const &vert ) -{ - Assert( index >= 0 ); - Assert( index < 3 ); - - m_Points[index].x = vert[0]; - m_Points[index].y = vert[1]; - m_Points[index].z = vert[2]; -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -inline void CDispCollTri::SetPointNormal( int index, Vector const &normal ) -{ - Assert( index >= 0 ); - Assert( index < 3 ); - - m_PointNormals[index].x = normal[0]; - m_PointNormals[index].y = normal[1]; - m_PointNormals[index].z = normal[2]; -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CDispCollTri::CalcPlane( void ) -{ - // - // calculate the plane normal and distance - // - Vector segment1, segment2, cross; - - segment1 = m_Points[1] - m_Points[0]; - segment2 = m_Points[2] - m_Points[0]; - cross = segment1.Cross( segment2 ); - m_Normal = cross; - VectorNormalize(m_Normal); - - m_Distance = m_Normal.Dot( m_Points[0] ); - - // - // calculate the projection axes - // - if( FloatMakePositive( m_Normal[0] ) > FloatMakePositive( m_Normal[1] ) ) - { - if( FloatMakePositive( m_Normal[0] ) > FloatMakePositive( m_Normal[2] ) ) - { - m_ProjAxes[0] = 1; - m_ProjAxes[1] = 2; - } - else - { - m_ProjAxes[0] = 0; - m_ProjAxes[1] = 1; - } - } - else - { - if( FloatMakePositive( m_Normal[1] ) > FloatMakePositive( m_Normal[2] ) ) - { - m_ProjAxes[0] = 0; - m_ProjAxes[1] = 2; - } - else - { - m_ProjAxes[0] = 0; - m_ProjAxes[1] = 1; - } - } -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -inline void CDispCollTri::SetIntersect( bool bIntersect ) -{ - m_bIntersect = bIntersect; -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -inline bool CDispCollTri::IsIntersect( void ) -{ - return m_bIntersect; -} - - -//============================================================================= -// -// Displacement Collision Node Functions -// - - -//----------------------------------------------------------------------------- -// Purpose: constructor -//----------------------------------------------------------------------------- -CDispCollNode::CDispCollNode() -{ - m_Bounds[0].x = m_Bounds[0].y = m_Bounds[0].z = 99999.9f; - m_Bounds[1].x = m_Bounds[1].y = m_Bounds[1].z = -99999.9f; - - m_Tris[0].Init(); - m_Tris[1].Init(); - - m_bIsLeaf = false; -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -inline bool CDispCollNode::IsLeaf( void ) -{ - return m_bIsLeaf; -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -inline void CDispCollNode::SetBounds( Vector const &bMin, Vector const &bMax ) -{ - m_Bounds[0] = bMin; - m_Bounds[1] = bMax; -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -inline void CDispCollNode::GetBounds( Vector &bMin, Vector &bMax ) -{ - bMin = m_Bounds[0]; - bMax = m_Bounds[1]; -} - - -//============================================================================= -// -// Displacement Collision Tree Functions -// - -//----------------------------------------------------------------------------- -// Purpose: constructor -//----------------------------------------------------------------------------- -CDispCollTree::CDispCollTree() -{ - m_Power = 0; - - m_NodeCount = 0; - m_pNodes = NULL; - - InitAABBData(); -} - - -//----------------------------------------------------------------------------- -// Purpose: deconstructor -//----------------------------------------------------------------------------- -CDispCollTree::~CDispCollTree() -{ - FreeNodes(); -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CDispCollTree::InitAABBData( void ) -{ - m_AABBNormals[0].x = -1.0f; m_AABBNormals[0].y = 0.0f; m_AABBNormals[0].z = 0.0f; - m_AABBNormals[1].x = 1.0f; m_AABBNormals[1].y = 0.0f; m_AABBNormals[1].z = 0.0f; - - m_AABBNormals[2].x = 0.0f; m_AABBNormals[2].y = -1.0f; m_AABBNormals[2].z = 0.0f; - m_AABBNormals[3].x = 0.0f; m_AABBNormals[3].y = 1.0f; m_AABBNormals[3].z = 0.0f; - - m_AABBNormals[4].x = 0.0f; m_AABBNormals[4].y = 0.0f; m_AABBNormals[4].z = -1.0f; - m_AABBNormals[5].x = 0.0f; m_AABBNormals[5].y = 0.0f; m_AABBNormals[5].z = 1.0f; -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CDispCollTree::CalcBounds( CDispCollNode *pNode, int nodeIndex ) -{ - Vector bounds[2]; - bounds[0].Init( 99999.9f, 99999.9f, 99999.9f ); - bounds[1].Init( -99999.9f, -99999.9f, -99999.9f ); - - // - // handle leaves differently -- bounding volume defined by triangles - // - if( pNode->IsLeaf() ) - { - for( int i = 0; i < 2; i++ ) - { - for( int j = 0; j < 3; j++ ) - { - // - // minimum - // - if( bounds[0].x > pNode->m_Tris[i].m_Points[j].x ) { bounds[0].x = pNode->m_Tris[i].m_Points[j].x; } - if( bounds[0].y > pNode->m_Tris[i].m_Points[j].y ) { bounds[0].y = pNode->m_Tris[i].m_Points[j].y; } - if( bounds[0].z > pNode->m_Tris[i].m_Points[j].z ) { bounds[0].z = pNode->m_Tris[i].m_Points[j].z; } - - // - // maximum - // - if( bounds[1].x < pNode->m_Tris[i].m_Points[j].x ) { bounds[1].x = pNode->m_Tris[i].m_Points[j].x; } - if( bounds[1].y < pNode->m_Tris[i].m_Points[j].y ) { bounds[1].y = pNode->m_Tris[i].m_Points[j].y; } - if( bounds[1].z < pNode->m_Tris[i].m_Points[j].z ) { bounds[1].z = pNode->m_Tris[i].m_Points[j].z; } - } - } - } - // - // bounding volume defined by maxima and minima of children volumes - // - else - { - for( int i = 0; i < 4; i++ ) - { - int childIndex = GetChildNode( nodeIndex, i ); - CDispCollNode *pChildNode = &m_pNodes[childIndex]; - - Vector childBounds[2]; - pChildNode->GetBounds( childBounds[0], childBounds[1] ); - - // - // minimum - // - if( bounds[0].x > childBounds[0].x ) { bounds[0].x = childBounds[0].x; } - if( bounds[0].y > childBounds[0].y ) { bounds[0].y = childBounds[0].y; } - if( bounds[0].z > childBounds[0].z ) { bounds[0].z = childBounds[0].z; } - - // - // maximum - // - if( bounds[1].x < childBounds[1].x ) { bounds[1].x = childBounds[1].x; } - if( bounds[1].y < childBounds[1].y ) { bounds[1].y = childBounds[1].y; } - if( bounds[1].z < childBounds[1].z ) { bounds[1].z = childBounds[1].z; } - } - } - - pNode->SetBounds( bounds[0], bounds[1] ); -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CDispCollTree::CreateNodes_r( CCoreDispInfo *pDisp, int nodeIndex, int termLevel ) -{ - int nodeLevel = GetNodeLevel( nodeIndex ); - - // - // terminating condition -- set node info (leaf or otherwise) - // - if( nodeLevel == termLevel ) - { - CDispCollNode *pNode = &m_pNodes[nodeIndex]; - CalcBounds( pNode, nodeIndex ); - - return; - } - - // - // recurse into children - // - for( int i = 0; i < 4; i++ ) - { - CreateNodes_r( pDisp, GetChildNode( nodeIndex, i ), termLevel ); - } -} - - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CDispCollTree::CreateNodes( CCoreDispInfo *pDisp ) -{ - // - // create all nodes in tree - // - int power = pDisp->GetPower() + 1; - for( int level = power; level > 0; level-- ) - { - CreateNodes_r( pDisp, 0 /* rootIndex */, level ); - } -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -int CDispCollTree::GetNodeIndexFromComponents( int x, int y ) -{ - int index = 0; - - // Interleave bits from the x and y values to create the index: - - for( int shift = 0; x != 0; shift += 2, x >>= 1 ) - { - index |= ( x & 1 ) << shift; - } - - for( shift = 1; y != 0; shift += 2, y >>= 1 ) - { - index |= ( y & 1 ) << shift; - } - - return index; -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CDispCollTree::InitLeaves( CCoreDispInfo *pDisp ) -{ - // - // get power and width and displacement surface - // - int power = pDisp->GetPower(); - int width = pDisp->GetWidth(); - - // - // get leaf indices - // - int startIndex = CalcNodeCount( power - 1 ); - int endIndex = CalcNodeCount( power ); - - for( int index = startIndex; index < endIndex; index++ ) - { - // - // create triangles at leaves - // - int x = ( index - startIndex ) % ( width - 1 ); - int y = ( index - startIndex ) / ( width - 1 ); - - int nodeIndex = GetNodeIndexFromComponents( x, y ); - nodeIndex += startIndex; - - Vector vert; - Vector normal; - - // - // tri 1 - // - pDisp->GetVert( x + ( y * width ), vert ); - pDisp->GetNormal( x + ( y * width ), normal ); - m_pNodes[nodeIndex].m_Tris[0].SetPoint( 0, vert ); - m_pNodes[nodeIndex].m_Tris[0].SetPointNormal( 0, normal ); - - pDisp->GetVert( x + ( ( y + 1 ) * width ), vert ); - pDisp->GetNormal( x + ( ( y + 1 ) * width ), normal ); - m_pNodes[nodeIndex].m_Tris[0].SetPoint( 1, vert ); - m_pNodes[nodeIndex].m_Tris[0].SetPointNormal( 1, normal ); - - pDisp->GetVert( ( x + 1 ) + ( y * width ), vert ); - pDisp->GetNormal( ( x + 1 ) + ( y * width ), normal ); - m_pNodes[nodeIndex].m_Tris[0].SetPoint( 2, vert ); - m_pNodes[nodeIndex].m_Tris[0].SetPointNormal( 2, normal ); - - m_pNodes[nodeIndex].m_Tris[0].CalcPlane(); - - // - // tri 2 - // - pDisp->GetVert( ( x + 1 ) + ( y * width ), vert ); - pDisp->GetNormal( ( x + 1 ) + ( y * width ), normal ); - m_pNodes[nodeIndex].m_Tris[1].SetPoint( 0, vert ); - m_pNodes[nodeIndex].m_Tris[1].SetPointNormal( 0, normal ); - - pDisp->GetVert( x + ( ( y + 1 ) * width ), vert ); - pDisp->GetNormal( x + ( ( y + 1 ) * width ), normal ); - m_pNodes[nodeIndex].m_Tris[1].SetPoint( 1, vert ); - m_pNodes[nodeIndex].m_Tris[1].SetPointNormal( 1, normal ); - - pDisp->GetVert( ( x + 1 ) + ( ( y + 1 ) * width ), vert ); - pDisp->GetNormal( ( x + 1 ) + ( ( y + 1 ) * width ), normal ); - m_pNodes[nodeIndex].m_Tris[1].SetPoint( 2, vert ); - m_pNodes[nodeIndex].m_Tris[1].SetPointNormal( 2, normal ); - - m_pNodes[nodeIndex].m_Tris[1].CalcPlane(); - - // set node as leaf - m_pNodes[nodeIndex].m_bIsLeaf = true; - } -} - - -//----------------------------------------------------------------------------- -// Purpose: allocate and initialize the displacement collision tree -// Input: power - size of the displacement surface -// Output: bool - success? (true/false) -//----------------------------------------------------------------------------- -bool CDispCollTree::Create( CCoreDispInfo *pDisp ) -{ - // - // calculate the number of nodes needed given the size of the displacement - // - m_Power = pDisp->GetPower(); - m_NodeCount = CalcNodeCount( m_Power ); - - // - // allocate tree space - // - if( !AllocNodes( m_NodeCount ) ) - return false; - - // initialize leaves - InitLeaves( pDisp ); - - // create tree nodes - CreateNodes( pDisp ); - - // tree successfully created! - return true; -} - - -//----------------------------------------------------------------------------- -// Purpose: allocate memory for the displacement collision tree -// Input: nodeCount - number of nodes to allocate -// Output: bool - success? (true/false) -//----------------------------------------------------------------------------- -bool CDispCollTree::AllocNodes( int nodeCount ) -{ - // sanity check - Assert( nodeCount != 0 ); - - m_pNodes = new CDispCollNode[nodeCount]; - if( !m_pNodes ) - return false; - - // tree successfully allocated! - return true; -} - - -//----------------------------------------------------------------------------- -// Purpose: release allocated memory for displacement collision tree -//----------------------------------------------------------------------------- -void CDispCollTree::FreeNodes( void ) -{ - if( m_pNodes ) - { - delete [] m_pNodes; - m_pNodes = NULL; - } -} - - -//----------------------------------------------------------------------------- -// Purpose: calculate the number of tree nodes given the size of the -// displacement surface -// Input: power - size of the displacement surface -// Output: int - the number of tree nodes -//----------------------------------------------------------------------------- -inline int CDispCollTree::CalcNodeCount( int power ) -{ - // power range [2...4] - Assert( power > 0 ); - Assert( power < 5 ); - - return ( ( 1 << ( ( power + 1 ) << 1 ) ) / 3 ); -} - - -//----------------------------------------------------------------------------- -// Purpose: get the parent node index given the current node -// Input: nodeIndex - current node index -// Output: int - the index of the parent node -//----------------------------------------------------------------------------- -inline int CDispCollTree::GetParentNode( int nodeIndex ) -{ - // node range [0...m_NodeCount) - Assert( nodeIndex >= 0 ); - Assert( nodeIndex < m_NodeCount ); - - // ( nodeIndex - 1 ) / 4 - return ( ( nodeIndex - 1 ) >> 2 ); -} - - -//----------------------------------------------------------------------------- -// Purpose: get the child node index given the current node index and direction -// of the child (1 of 4) -// Input: nodeIndex - current node index -// direction - direction of the child ( [0...3] - SW, SE, NW, NE ) -// Output: int - the index of the child node -//----------------------------------------------------------------------------- -inline int CDispCollTree::GetChildNode( int nodeIndex, int direction ) -{ - // node range [0...m_NodeCount) - Assert( nodeIndex >= 0 ); - Assert( nodeIndex < m_NodeCount ); - - // ( nodeIndex * 4 ) + ( direction + 1 ) - return ( ( nodeIndex << 2 ) + ( direction + 1 ) ); -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -inline int CDispCollTree::GetNodeLevel( int nodeIndex ) -{ - // node range [0...m_NodeCount) - Assert( nodeIndex >= 0 ); - Assert( nodeIndex < m_NodeCount ); - - // level = 2^n + 1 - if( nodeIndex == 0 ) { return 1; } - if( nodeIndex < 5 ) { return 2; } - if( nodeIndex < 21 ) { return 3; } - if( nodeIndex < 85 ) { return 4; } - if( nodeIndex < 341 ) { return 5; } - - return -1; -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -bool CDispCollTree::RayTriTest( Vector const &rayStart, Vector const &rayDir, float const rayLength, - CDispCollTri const *pTri, float *fraction ) -{ - const float DET_EPSILON = 0.001f; - const float DIST_EPSILON = 0.001f; - - // - // calculate the edges - // - Vector edge1 = pTri->m_Points[1] - pTri->m_Points[0]; - Vector edge2 = pTri->m_Points[2] - pTri->m_Points[0]; - -// Vector faceNormal = edge1.Cross( edge2 ); -// Vector normNormal = faceNormal.Normalize(); - - // - // calculate the triangle's determinant - // - Vector pVec = rayDir.Cross( edge2 ); - float det = pVec.Dot( edge1 ); - - // if determinant is zero -- ray lies in plane - if( ( det > -DET_EPSILON ) && ( det < DET_EPSILON ) ) - return false; - - // - // utility calculations - inverse determinant and distance from v0 to ray start - // - double invDet = 1.0f / det; - Vector tVec = rayStart - pTri->m_Points[0]; - - // - // calculate the U parameter and test bounds - // - double u = pVec.Dot( tVec ) * invDet; - if( ( u < 0.0f ) || ( u > 1.0f ) ) - return false; - - Vector qVec = tVec.Cross( edge1 ); - - // - // calculate the V parameter and test bounds - // - double v = qVec.Dot( rayDir ) * invDet; - if( ( v < 0.0f ) || ( ( u + v ) > 1.0f ) ) - return false; - - // calculate where ray intersects triangle - *fraction = qVec.Dot( edge2 ) * invDet; - *fraction /= rayLength; - - if( ( *fraction < DIST_EPSILON ) || ( *fraction > ( 1.0f - DIST_EPSILON ) ) ) - return false; - - return true; -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -bool CDispCollTree::RayTriListTest( CDispCollTreeTempData *pTemp, CDispCollData *pData ) -{ - // save starting fraction -- to test for collision - float startFraction = pData->m_Fraction; - - // - // calculate the ray - // - Vector seg = pData->m_EndPos - pData->m_StartPos; - Vector rayDir = seg; - float rayLength = VectorNormalize( rayDir ); - - // - // test ray against all triangles in list - // - for( int i = 0; i < pTemp->m_TriListCount; i++ ) - { - float fraction = 1.0f; - bool bResult = RayTriTest( pData->m_StartPos, rayDir, rayLength, pTemp->m_ppTriList[i], &fraction ); - if( !bResult ) - continue; - - if( pData->m_bOcclude ) - { - return true; - } - - if( fraction < pData->m_Fraction ) - { - pData->m_Fraction = fraction; - pData->m_Normal = pTemp->m_ppTriList[i]->m_Normal; - pData->m_Distance = pTemp->m_ppTriList[i]->m_Distance; - } - } - - // collision! - if( pData->m_Fraction < startFraction ) - return true; - - // no collision! - return false; -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -bool CDispCollTree::RayAABBTest( CDispCollTreeTempData *pTemp, Vector &rayStart, Vector &rayEnd ) -{ - const float MY_DIST_EPSILON = 0.01f; - - for( int i = 0; i < 6; i++ ) - { - float dist1 = m_AABBNormals[i].Dot( rayStart ) - pTemp->m_AABBDistances[i]; - float dist2 = m_AABBNormals[i].Dot( rayEnd ) - pTemp->m_AABBDistances[i]; - - // - // entry intersection point - move ray start up to intersection - // - if( ( dist1 > MY_DIST_EPSILON ) && ( dist2 < -MY_DIST_EPSILON ) ) - { - float fraction = ( dist1 / ( dist1 - dist2 ) ); - - Vector segment, increment; - segment = ( rayEnd - rayStart ) * fraction; - increment = segment; - VectorNormalize(increment); - segment += increment; - rayStart += segment; - } - else if( ( dist1 > MY_DIST_EPSILON ) && ( dist2 > MY_DIST_EPSILON ) ) - { - return false; - } - } - - return true; -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CDispCollTree::CreatePlanesFromBounds( CDispCollTreeTempData *pTemp, Vector const &bbMin, Vector const &bbMax ) -{ - // - // note -- these never change! - // -// m_AABBNormals[0].x = -1; -// m_AABBNormals[1].x = 1; - -// m_AABBNormals[2].y = -1; -// m_AABBNormals[3].y = 1; - -// m_AABBNormals[4].z = -1; -// m_AABBNormals[5].z = 1; - - pTemp->m_AABBDistances[0] = -bbMin.x; - pTemp->m_AABBDistances[1] = bbMax.x; - - pTemp->m_AABBDistances[2] = -bbMin.y; - pTemp->m_AABBDistances[3] = bbMax.y; - - pTemp->m_AABBDistances[4] = -bbMin.z; - pTemp->m_AABBDistances[5] = bbMax.z; -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CDispCollTree::RayNodeTest_r( CDispCollTreeTempData *pTemp, int nodeIndex, Vector rayStart, Vector rayEnd ) -{ - // get the current node - CDispCollNode *pNode = &m_pNodes[nodeIndex]; - - // - // get node bounding box and create collision planes - // - Vector bounds[2]; - pNode->GetBounds( bounds[0], bounds[1] ); - CreatePlanesFromBounds( pTemp, bounds[0], bounds[1] ); - - bool bIntersect = RayAABBTest( pTemp, rayStart, rayEnd ); - if( bIntersect ) - { - // done -- add triangles to triangle list - if( pNode->IsLeaf() ) - { - // Assert for now -- flush cache later!!!!! - Assert( pTemp->m_TriListCount >= 0 ); - Assert( pTemp->m_TriListCount < TRILIST_CACHE_SIZE ); - - pTemp->m_ppTriList[pTemp->m_TriListCount] = &pNode->m_Tris[0]; - pTemp->m_ppTriList[pTemp->m_TriListCount+1] = &pNode->m_Tris[1]; - pTemp->m_TriListCount += 2; - } - // continue recursion - else - { - for( int i = 0; i < 4; i++ ) - { - RayNodeTest_r( pTemp, GetChildNode( nodeIndex, i ), rayStart, rayEnd ); - } - } - } -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -bool CDispCollTree::RayTestAllTris( CDispCollData *pData, int power ) -{ - // - // get leaf indices - // - int startIndex = CalcNodeCount( power - 1 ); - int endIndex = CalcNodeCount( power ); - - // save incoming fraction - float startFraction = pData->m_Fraction; - float fraction = pData->m_Fraction; - - Vector ray = pData->m_EndPos - pData->m_StartPos; - Vector rayDir = ray; - float rayLength = VectorNormalize(rayDir); - - // - // test ray against all triangles in list - // - for( int index = startIndex; index < endIndex; index++ ) - { - for( int j = 0; j < 2; j++ ) - { - bool bResult = RayTriTest( pData->m_StartPos, rayDir, rayLength, &m_pNodes[index].m_Tris[j], &fraction ); - if( !bResult ) - continue; - - if( pData->m_bOcclude ) - { - return true; - } - - if( fraction < pData->m_Fraction ) - { - pData->m_Fraction = fraction; - pData->m_Normal = m_pNodes[index].m_Tris[j].m_Normal; - pData->m_Distance = m_pNodes[index].m_Tris[j].m_Distance; - } - } - } - - // collision! - if( pData->m_Fraction < startFraction ) - return true; - - // no collision! - return false; -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -bool CDispCollTree::RayTest( CDispCollData *pData ) -{ - // reset the triangle list count - CDispCollTreeTempData tmp; - tmp.m_TriListCount = 0; - - // trace against nodes (copy start, end because they change) - RayNodeTest_r( &tmp, 0, pData->m_StartPos, pData->m_EndPos ); - - // - // trace against tris (if need be) - // - if( tmp.m_TriListCount != 0 ) - { - bool result = RayTriListTest( &tmp, pData ); - return result; - } - - return false; -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -bool CDispCollTree::SweptAABBTriIntersect( Vector &rayStart, Vector &rayEnd, Vector &extents, - CDispCollTri const *pTri, Vector &plNormal, float *plDist, - float *fraction ) -{ - - // - // PUT A COPY HERE OF START AND END -- SINCE I CHANGE THEM!!!!!! - // - - - - - - int dir, ptIndex; - float closeValue; - float distStart, distEnd; - float t; - Vector rayPt; - - // get ray direction - Vector rayDir = rayEnd - rayStart; - - // initialize fraction - *fraction = 1.0f; - - // - // test for collision with axial planes (x, y, z) - // - for( dir = 0; dir < 3; dir++ ) - { - if( rayDir[dir] < 0.0f ) - { - closeValue = -99999.9f; - for( ptIndex = 0; ptIndex < 3; ptIndex++ ) - { - if( pTri->m_Points[ptIndex][dir] > closeValue ) - { - closeValue = pTri->m_Points[ptIndex][dir]; - } - } - - closeValue += extents[dir]; - - distStart = rayStart[dir] - closeValue; - distEnd = rayEnd[dir] - closeValue; - } - else - { - closeValue = 99999.9f; - for( ptIndex = 0; ptIndex < 3; ptIndex++ ) - { - if( pTri->m_Points[ptIndex][dir] < closeValue ) - { - closeValue = pTri->m_Points[ptIndex][dir]; - } - } - - closeValue -= extents[dir]; - - distStart = -( rayStart[dir] - closeValue ); - distEnd = -( rayEnd[dir] - closeValue ); - } - - if( ( distStart > COLLISION_EPSILON ) && ( distEnd < -COLLISION_EPSILON ) ) - { - t = ( distStart - COLLISION_EPSILON ) / ( distStart - distEnd ); - if( t > *fraction ) - { - VectorScale( rayDir, t, rayPt ); - VectorAdd( rayStart, rayPt, rayStart ); - *fraction = t; - plNormal.Init(); - plNormal[dir] = 1.0f; - *plDist = closeValue; - } - } - else if( ( distStart < -COLLISION_EPSILON ) && ( distEnd > COLLISION_EPSILON ) ) - { - t = ( distStart + COLLISION_EPSILON ) / ( distStart - distEnd ); - VectorScale( rayDir, t, rayPt ); - VectorAdd( rayStart, rayPt, rayEnd ); - } - else if( ( distStart > COLLISION_EPSILON ) && ( distEnd > COLLISION_EPSILON ) ) - { - return false; - } - } - - // - // check for an early out - // - if( ( pTri->m_Normal[0] > ONE_MINUS_COLLISION_EPSILON ) || - ( pTri->m_Normal[1] > ONE_MINUS_COLLISION_EPSILON ) || - ( pTri->m_Normal[2] > ONE_MINUS_COLLISION_EPSILON ) ) - { - if( *fraction == 1.0f ) - return false; - - return true; - } - - // - // handle 9 edge tests - // - Vector normal; - Vector edge; - float dist; - - // find the closest box point - Vector boxPt( 0.0f, 0.0f, 0.0f ); - for( dir = 0; dir < 3; dir++ ) - { - if( rayDir[dir] < 0.0f ) - { - boxPt[dir] = extents[dir]; - } - else - { - boxPt[dir] = -extents[dir]; - } - } - - // - // edge 0 - // - edge = pTri->m_Points[1] - pTri->m_Points[0]; - - // cross x-edge - normal.x = 0.0f; - normal.y = -edge.z; - normal.z = edge.y; - - // extents adjusted dist - dist = ( normal.y * ( boxPt.y - pTri->m_Points[0].y ) ) + ( normal.z * ( boxPt.z - pTri->m_Points[0].z ) ); - - // find distances from plane (start, end) - distStart = ( normal.y * rayStart.y ) + ( normal.z * rayStart.z ) - dist; - distEnd = ( normal.y * rayEnd.y ) + ( normal.z * rayEnd.z ) - dist; - - if( ( distStart > COLLISION_EPSILON ) && ( distEnd < -COLLISION_EPSILON ) ) - { - t = ( distStart - COLLISION_EPSILON ) / ( distStart - distEnd ); - if( t > *fraction ) - { - VectorScale( rayDir, t, rayPt ); - VectorAdd( rayStart, rayPt, rayStart ); - *fraction = t; - plNormal = normal; - *plDist = dist; - } - } - else if( ( distStart < -COLLISION_EPSILON ) && ( distEnd > COLLISION_EPSILON ) ) - { - t = ( distStart + COLLISION_EPSILON ) / ( distStart - distEnd ); - VectorScale( rayDir, t, rayPt ); - VectorAdd( rayStart, rayPt, rayEnd ); - } - else if( ( distStart > COLLISION_EPSILON ) && ( distEnd > COLLISION_EPSILON ) ) - { - return false; - } - - // cross y-edge - normal.x = edge.z; - normal.y = 0.0f; - normal.z = edge.y; - - // extents adjusted dist - dist = ( normal.x * ( boxPt.x - pTri->m_Points[0].x ) ) + ( normal.z * ( boxPt.z - pTri->m_Points[0].z ) ); - - // find distances from plane (start, end) - distStart = ( normal.x * rayStart.x ) + ( normal.z * rayStart.z ) - dist; - distEnd = ( normal.x * rayEnd.x ) + ( normal.z * rayEnd.z ) - dist; - - if( ( distStart > COLLISION_EPSILON ) && ( distEnd < -COLLISION_EPSILON ) ) - { - t = ( distStart - COLLISION_EPSILON ) / ( distStart - distEnd ); - if( t > *fraction ) - { - VectorScale( rayDir, t, rayPt ); - VectorAdd( rayStart, rayPt, rayStart ); - *fraction = t; - plNormal = normal; - *plDist = dist; - } - } - else if( ( distStart < -COLLISION_EPSILON ) && ( distEnd > COLLISION_EPSILON ) ) - { - t = ( distStart + COLLISION_EPSILON ) / ( distStart - distEnd ); - VectorScale( rayDir, t, rayPt ); - VectorAdd( rayStart, rayPt, rayEnd ); - } - else if( ( distStart > COLLISION_EPSILON ) && ( distEnd > COLLISION_EPSILON ) ) - { - return false; - } - - // cross z-edge - normal.x = -edge.y; - normal.y = edge.x; - normal.z = 0.0f; - - // extents adjusted dist - dist = ( normal.x * ( boxPt.x - pTri->m_Points[0].x ) ) + ( normal.y * ( boxPt.y - pTri->m_Points[0].y ) ); - - // find distances from plane (start, end) - distStart = ( normal.x * rayStart.x ) + ( normal.y * rayStart.y ) - dist; - distEnd = ( normal.x * rayEnd.x ) + ( normal.y * rayEnd.y ) - dist; - - if( ( distStart > COLLISION_EPSILON ) && ( distEnd < -COLLISION_EPSILON ) ) - { - t = ( distStart - COLLISION_EPSILON ) / ( distStart - distEnd ); - if( t > *fraction ) - { - VectorScale( rayDir, t, rayPt ); - VectorAdd( rayStart, rayPt, rayStart ); - *fraction = t; - plNormal = normal; - *plDist = dist; - } - } - else if( ( distStart < -COLLISION_EPSILON ) && ( distEnd > COLLISION_EPSILON ) ) - { - t = ( distStart + COLLISION_EPSILON ) / ( distStart - distEnd ); - VectorScale( rayDir, t, rayPt ); - VectorAdd( rayStart, rayPt, rayEnd ); - } - else if( ( distStart > COLLISION_EPSILON ) && ( distEnd > COLLISION_EPSILON ) ) - { - return false; - } - - // - // edge 1 - // - edge = pTri->m_Points[2] - pTri->m_Points[1]; - - // cross x-edge - normal.x = 0.0f; - normal.y = -edge.z; - normal.z = edge.y; - - // extents adjusted dist - dist = ( normal.y * ( boxPt.y - pTri->m_Points[0].y ) ) + ( normal.z * ( boxPt.z - pTri->m_Points[0].z ) ); - - // find distances from plane (start, end) - distStart = ( normal.y * rayStart.y ) + ( normal.z * rayStart.z ) - dist; - distEnd = ( normal.y * rayEnd.y ) + ( normal.z * rayEnd.z ) - dist; - - if( ( distStart > COLLISION_EPSILON ) && ( distEnd < -COLLISION_EPSILON ) ) - { - t = ( distStart - COLLISION_EPSILON ) / ( distStart - distEnd ); - if( t > *fraction ) - { - VectorScale( rayDir, t, rayPt ); - VectorAdd( rayStart, rayPt, rayStart ); - *fraction = t; - plNormal = normal; - *plDist = dist; - } - } - else if( ( distStart < -COLLISION_EPSILON ) && ( distEnd > COLLISION_EPSILON ) ) - { - t = ( distStart + COLLISION_EPSILON ) / ( distStart - distEnd ); - VectorScale( rayDir, t, rayPt ); - VectorAdd( rayStart, rayPt, rayEnd ); - } - else if( ( distStart > COLLISION_EPSILON ) && ( distEnd > COLLISION_EPSILON ) ) - { - return false; - } - - // cross y-edge - normal.x = edge.z; - normal.y = 0.0f; - normal.z = edge.y; - - // extents adjusted dist - dist = ( normal.x * ( boxPt.x - pTri->m_Points[0].x ) ) + ( normal.z * ( boxPt.z - pTri->m_Points[0].z ) ); - - // find distances from plane (start, end) - distStart = ( normal.x * rayStart.x ) + ( normal.z * rayStart.z ) - dist; - distEnd = ( normal.x * rayEnd.x ) + ( normal.z * rayEnd.z ) - dist; - - if( ( distStart > COLLISION_EPSILON ) && ( distEnd < -COLLISION_EPSILON ) ) - { - t = ( distStart - COLLISION_EPSILON ) / ( distStart - distEnd ); - if( t > *fraction ) - { - VectorScale( rayDir, t, rayPt ); - VectorAdd( rayStart, rayPt, rayStart ); - *fraction = t; - plNormal = normal; - *plDist = dist; - } - } - else if( ( distStart < -COLLISION_EPSILON ) && ( distEnd > COLLISION_EPSILON ) ) - { - t = ( distStart + COLLISION_EPSILON ) / ( distStart - distEnd ); - VectorScale( rayDir, t, rayPt ); - VectorAdd( rayStart, rayPt, rayEnd ); - } - else if( ( distStart > COLLISION_EPSILON ) && ( distEnd > COLLISION_EPSILON ) ) - { - return false; - } - - // cross z-edge - normal.x = -edge.y; - normal.y = edge.x; - normal.z = 0.0f; - - // extents adjusted dist - dist = ( normal.x * ( boxPt.x - pTri->m_Points[0].x ) ) + ( normal.y * ( boxPt.y - pTri->m_Points[0].y ) ); - - // find distances from plane (start, end) - distStart = ( normal.x * rayStart.x ) + ( normal.y * rayStart.y ) - dist; - distEnd = ( normal.x * rayEnd.x ) + ( normal.y * rayEnd.y ) - dist; - - if( ( distStart > COLLISION_EPSILON ) && ( distEnd < -COLLISION_EPSILON ) ) - { - t = ( distStart - COLLISION_EPSILON ) / ( distStart - distEnd ); - if( t > *fraction ) - { - VectorScale( rayDir, t, rayPt ); - VectorAdd( rayStart, rayPt, rayStart ); - *fraction = t; - plNormal = normal; - *plDist = dist; - } - } - else if( ( distStart < -COLLISION_EPSILON ) && ( distEnd > COLLISION_EPSILON ) ) - { - t = ( distStart + COLLISION_EPSILON ) / ( distStart - distEnd ); - VectorScale( rayDir, t, rayPt ); - VectorAdd( rayStart, rayPt, rayEnd ); - } - else if( ( distStart > COLLISION_EPSILON ) && ( distEnd > COLLISION_EPSILON ) ) - { - return false; - } - - // - // edge 2 - // - edge = pTri->m_Points[0] - pTri->m_Points[2]; - - // cross x-edge - normal.x = 0.0f; - normal.y = -edge.z; - normal.z = edge.y; - - // extents adjusted dist - dist = ( normal.y * ( boxPt.y - pTri->m_Points[0].y ) ) + ( normal.z * ( boxPt.z - pTri->m_Points[0].z ) ); - - // find distances from plane (start, end) - distStart = ( normal.y * rayStart.y ) + ( normal.z * rayStart.z ) - dist; - distEnd = ( normal.y * rayEnd.y ) + ( normal.z * rayEnd.z ) - dist; - - if( ( distStart > COLLISION_EPSILON ) && ( distEnd < -COLLISION_EPSILON ) ) - { - t = ( distStart - COLLISION_EPSILON ) / ( distStart - distEnd ); - if( t > *fraction ) - { - VectorScale( rayDir, t, rayPt ); - VectorAdd( rayStart, rayPt, rayStart ); - *fraction = t; - plNormal = normal; - *plDist = dist; - } - } - else if( ( distStart < -COLLISION_EPSILON ) && ( distEnd > COLLISION_EPSILON ) ) - { - t = ( distStart + COLLISION_EPSILON ) / ( distStart - distEnd ); - VectorScale( rayDir, t, rayPt ); - VectorAdd( rayStart, rayPt, rayEnd ); - } - else if( ( distStart > COLLISION_EPSILON ) && ( distEnd > COLLISION_EPSILON ) ) - { - return false; - } - - // cross y-edge - normal.x = edge.z; - normal.y = 0.0f; - normal.z = edge.y; - - // extents adjusted dist - dist = ( normal.x * ( boxPt.x - pTri->m_Points[0].x ) ) + ( normal.z * ( boxPt.z - pTri->m_Points[0].z ) ); - - // find distances from plane (start, end) - distStart = ( normal.x * rayStart.x ) + ( normal.z * rayStart.z ) - dist; - distEnd = ( normal.x * rayEnd.x ) + ( normal.z * rayEnd.z ) - dist; - - if( ( distStart > COLLISION_EPSILON ) && ( distEnd < -COLLISION_EPSILON ) ) - { - t = ( distStart - COLLISION_EPSILON ) / ( distStart - distEnd ); - if( t > *fraction ) - { - VectorScale( rayDir, t, rayPt ); - VectorAdd( rayStart, rayPt, rayStart ); - *fraction = t; - plNormal = normal; - *plDist = dist; - } - } - else if( ( distStart < -COLLISION_EPSILON ) && ( distEnd > COLLISION_EPSILON ) ) - { - t = ( distStart + COLLISION_EPSILON ) / ( distStart - distEnd ); - VectorScale( rayDir, t, rayPt ); - VectorAdd( rayStart, rayPt, rayEnd ); - } - else if( ( distStart > COLLISION_EPSILON ) && ( distEnd > COLLISION_EPSILON ) ) - { - return false; - } - - // cross z-edge - normal.x = -edge.y; - normal.y = edge.x; - normal.z = 0.0f; - - // extents adjusted dist - dist = ( normal.x * ( boxPt.x - pTri->m_Points[0].x ) ) + ( normal.y * ( boxPt.y - pTri->m_Points[0].y ) ); - - // find distances from plane (start, end) - distStart = ( normal.x * rayStart.x ) + ( normal.y * rayStart.y ) - dist; - distEnd = ( normal.x * rayEnd.x ) + ( normal.y * rayEnd.y ) - dist; - - if( ( distStart > COLLISION_EPSILON ) && ( distEnd < -COLLISION_EPSILON ) ) - { - t = ( distStart - COLLISION_EPSILON ) / ( distStart - distEnd ); - if( t > *fraction ) - { - VectorScale( rayDir, t, rayPt ); - VectorAdd( rayStart, rayPt, rayStart ); - *fraction = t; - plNormal = normal; - *plDist = dist; - } - } - else if( ( distStart < -COLLISION_EPSILON ) && ( distEnd > COLLISION_EPSILON ) ) - { - t = ( distStart + COLLISION_EPSILON ) / ( distStart - distEnd ); - VectorScale( rayDir, t, rayPt ); - VectorAdd( rayStart, rayPt, rayEnd ); - } - else if( ( distStart > COLLISION_EPSILON ) && ( distEnd > COLLISION_EPSILON ) ) - { - return false; - } - - // - // test face plane - // - dist = ( pTri->m_Normal.x * ( boxPt.x - pTri->m_Points[0].x ) ) + - ( pTri->m_Normal.y * ( boxPt.y - pTri->m_Points[0].y ) ) + - ( pTri->m_Normal.z * ( boxPt.z - pTri->m_Points[0].z ) ); - - distStart = pTri->m_Normal.Dot( rayStart ) - dist; - distEnd = pTri->m_Normal.Dot( rayEnd ) - dist; - - if( ( distStart > COLLISION_EPSILON ) && ( distEnd < -COLLISION_EPSILON ) ) - { - t = ( distStart - COLLISION_EPSILON ) / ( distStart - distEnd ); - if( t > *fraction ) - { - VectorScale( rayDir, t, rayPt ); - VectorAdd( rayStart, rayPt, rayStart ); - *fraction = t; - plNormal = normal; - *plDist = dist; - } - } - else if( ( distStart < -COLLISION_EPSILON ) && ( distEnd > COLLISION_EPSILON ) ) - { - t = ( distStart + COLLISION_EPSILON ) / ( distStart - distEnd ); - VectorScale( rayDir, t, rayPt ); - VectorAdd( rayStart, rayPt, rayEnd ); - } - else if( ( distStart > COLLISION_EPSILON ) && ( distEnd > COLLISION_EPSILON ) ) - { - return false; - } - - if( *fraction == 1.0f ) - return false; - - return true; -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -bool CDispCollTree::AABBTriIntersect( CDispCollTreeTempData *pTemp, CDispCollData *pData ) -{ - bool bResult = false; - - Vector normal; - float fraction, dist; - - // - // sweep ABB against all triangles in list - // - for( int i = 0; i < pTemp->m_TriListCount; i++ ) - { - if( pTemp->m_ppTriList[i]->IsIntersect() ) - { - bResult = SweptAABBTriIntersect( pData->m_StartPos, pData->m_EndPos, pData->m_Extents, - pTemp->m_ppTriList[i], normal, &dist, &fraction ); - if( bResult ) - { - if( fraction < pData->m_Fraction ) - { - pData->m_Fraction = fraction; - pData->m_Normal = normal; - pData->m_Distance = dist; - } - } - } - } - - return bResult; -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -bool CDispCollTree::IntersectAABBTriTest( Vector &rayStart, Vector &extents, - CDispCollTri const *pTri ) -{ - int dir, ptIndex; - float dist; - - // - // test axail planes (x, y, z) - // - - for( dir = 0; dir < 3; dir++ ) - { - // - // negative axial plane, component = dir - // - dist = rayStart[dir] - extents[dir]; - for( ptIndex = 0; ptIndex < 3; ptIndex++ ) - { - if( pTri->m_Points[ptIndex][dir] > dist ) - break; - } - - if( ptIndex == 3 ) - return false; - - // - // positive axial plane, component = dir - // - dist = rayStart[dir] + extents[dir]; - for( ptIndex = 0; ptIndex < 3; ptIndex++ ) - { - if( pTri->m_Points[ptIndex][dir] < dist ) - break; - } - - if( ptIndex == 3 ) - return false; - } - - // - // add a test here to see if triangle face normal is close to axial -- done if so!!! - // - if( ( pTri->m_Normal[0] > ONE_MINUS_COLLISION_EPSILON ) || - ( pTri->m_Normal[1] > ONE_MINUS_COLLISION_EPSILON ) || - ( pTri->m_Normal[2] > ONE_MINUS_COLLISION_EPSILON ) ) - return true; - - // find the closest point on the box (use negated tri face noraml) - Vector boxPt( 0.0f, 0.0f, 0.0f ); - for( dir = 0; dir < 3; dir++ ) - { - if( pTri->m_Normal[dir] < 0.0f ) - { - boxPt[dir] = extents[dir]; - } - else - { - boxPt[dir] = -extents[dir]; - } - } - - // - // triangle plane test - // - // do the opposite because the ray has been negated - if( ( ( pTri->m_Normal.x * ( boxPt.x - pTri->m_Points[0].x ) ) + - ( pTri->m_Normal.y * ( boxPt.y - pTri->m_Points[0].y ) ) + - ( pTri->m_Normal.z * ( boxPt.z - pTri->m_Points[0].z ) ) ) > 0.0f ) - return false; - - // - // test edge planes - 9 of them - // - Vector normal; - Vector edge; - - // - // edge 0 - // - edge = pTri->m_Points[1] - pTri->m_Points[0]; - - // cross x - normal.x = 0.0f; - normal.y = -edge.z; - normal.z = edge.y; - if( ( ( normal.y * ( boxPt.y - pTri->m_Points[0].y ) ) + ( normal.z * ( boxPt.z - pTri->m_Points[0].z ) ) ) > 0.0f ) - return false; - - // cross y - normal.x = edge.z; - normal.y = 0.0f; - normal.z = edge.y; - if( ( ( normal.x * ( boxPt.x - pTri->m_Points[0].x ) ) + ( normal.z * ( boxPt.z - pTri->m_Points[0].z ) ) ) > 0.0f ) - return false; - - // cross z - normal.x = -edge.y; - normal.y = edge.x; - normal.z = 0.0f; - if( ( ( normal.x * ( boxPt.x - pTri->m_Points[0].x ) ) + ( normal.y * ( boxPt.y - pTri->m_Points[0].y ) ) ) > 0.0f ) - return false; - - // - // edge 1 - // - edge = pTri->m_Points[2] - pTri->m_Points[1]; - - // cross x - normal.x = 0.0f; - normal.y = -edge.z; - normal.z = edge.y; - if( ( ( normal.y * ( boxPt.y - pTri->m_Points[0].y ) ) + ( normal.z * ( boxPt.z - pTri->m_Points[0].z ) ) ) > 0.0f ) - return false; - - // cross y - normal.x = edge.z; - normal.y = 0.0f; - normal.z = edge.y; - if( ( ( normal.x * ( boxPt.x - pTri->m_Points[0].x ) ) + ( normal.z * ( boxPt.z - pTri->m_Points[0].z ) ) ) > 0.0f ) - return false; - - // cross z - normal.x = -edge.y; - normal.y = edge.x; - normal.z = 0.0f; - if( ( ( normal.x * ( boxPt.x - pTri->m_Points[0].x ) ) + ( normal.y * ( boxPt.y - pTri->m_Points[0].y ) ) ) > 0.0f ) - return false; - - // - // edge 2 - // - edge = pTri->m_Points[0] - pTri->m_Points[2]; - - // cross x - normal.x = 0.0f; - normal.y = -edge.z; - normal.z = edge.y; - if( ( ( normal.y * ( boxPt.y - pTri->m_Points[0].y ) ) + ( normal.z * ( boxPt.z - pTri->m_Points[0].z ) ) ) > 0.0f ) - return false; - - // cross y - normal.x = edge.z; - normal.y = 0.0f; - normal.z = edge.y; - if( ( ( normal.x * ( boxPt.x - pTri->m_Points[0].x ) ) + ( normal.z * ( boxPt.z - pTri->m_Points[0].z ) ) ) > 0.0f ) - return false; - - // cross z - normal.x = -edge.y; - normal.y = edge.x; - normal.z = 0.0f; - if( ( ( normal.x * ( boxPt.x - pTri->m_Points[0].x ) ) + ( normal.y * ( boxPt.y - pTri->m_Points[0].y ) ) ) > 0.0f ) - return false; - - return true; -} - - - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -bool CDispCollTree::SweptAABBTriTest( Vector &rayStart, Vector &rayEnd, Vector &extents, - CDispCollTri const *pTri ) -{ - // get ray direction - Vector rayDir = rayEnd - rayStart; - - // - // quick and dirty test -- test to see if the object is traveling away from triangle surface??? - // - if( pTri->m_Normal.Dot( rayDir ) > 0.0f ) - return false; - - // - // calc the swept triangle face (negate the ray -- opposite direction of box travel) - // - rayDir.Negate(); - - Vector points[3]; - points[0] = pTri->m_Points[0] + rayDir; - points[1] = pTri->m_Points[1] + rayDir; - points[2] = pTri->m_Points[2] + rayDir; - - // - // handle 4 faces tests (3 axial planes and triangle face) - // - int dir; - float dist; - - // - // axial planes tests (x, y, z) - // - for( dir = 0; dir < 3; dir++ ) - { - bool bOutside = true; - - if( rayDir[dir] < 0.0f ) - { - dist = rayStart[dir] - extents[dir]; - for( int ptIndex = 0; ptIndex < 3; ptIndex ) - { - if( points[ptIndex][dir] > dist ) - { - bOutside = false; - break; - } - } - } - else - { - dist = rayStart[dir] + extents[dir]; - for( int ptIndex = 0; ptIndex < 3; ptIndex ) - { - if( pTri->m_Points[ptIndex][dir] < dist ) - { - bOutside = false; - break; - } - } - } - - if( bOutside ) - return false; - } - - // - // add a test here to see if triangle face normal is close to axial -- done if so!!! - // - if( ( pTri->m_Normal[0] > ONE_MINUS_COLLISION_EPSILON ) || - ( pTri->m_Normal[1] > ONE_MINUS_COLLISION_EPSILON ) || - ( pTri->m_Normal[2] > ONE_MINUS_COLLISION_EPSILON ) ) - return true; - - // - // handle 9 edge tests - always use the newly swept face for this - // - Vector normal; - Vector edge; - - // find the closest box point - (is written opposite to normal due to negating ray) - Vector boxPt( 0.0f, 0.0f, 0.0f ); - for( dir = 0; dir < 3; dir++ ) - { - if( rayDir[dir] < 0.0f ) - { - boxPt[dir] = rayStart[dir] - extents[dir]; - } - else - { - boxPt[dir] = rayStart[dir] + extents[dir]; - } - } - - // - // edge 0 - // - edge = points[1] - points[0]; - - // cross x-edge - normal.x = 0.0f; - normal.y = -edge.z; - normal.z = edge.y; - if( ( ( normal.y * ( boxPt.y - points[0].y ) ) + ( normal.z * ( boxPt.z - points[0].z ) ) ) > 0.0f ) - return false; - - // cross, y-edge - normal.x = edge.z; - normal.y = 0.0f; - normal.z = edge.y; - if( ( ( normal.x * ( boxPt.x - points[0].x ) ) + ( normal.z * ( boxPt.z - points[0].z ) ) ) > 0.0f ) - return false; - - // cross z-edge - normal.x = -edge.y; - normal.y = edge.x; - normal.z = 0.0f; - if( ( ( normal.x * ( boxPt.x - points[0].x ) ) + ( normal.y * ( boxPt.y - points[0].y ) ) ) > 0.0f ) - return false; - - // - // edge 1 - // - edge = points[2] - points[1]; - - // cross x-edge - normal.x = 0.0f; - normal.y = -edge.z; - normal.z = edge.y; - if( ( ( normal.y * ( boxPt.y - points[0].y ) ) + ( normal.z * ( boxPt.z - points[0].z ) ) ) > 0.0f ) - return false; - - // cross, y-edge - normal.x = edge.z; - normal.y = 0.0f; - normal.z = edge.y; - if( ( ( normal.x * ( boxPt.x - points[0].x ) ) + ( normal.z * ( boxPt.z - points[0].z ) ) ) > 0.0f ) - return false; - - // cross z-edge - normal.x = -edge.y; - normal.y = edge.x; - normal.z = 0.0f; - if( ( ( normal.x * ( boxPt.x - points[0].x ) ) + ( normal.y * ( boxPt.y - points[0].y ) ) ) > 0.0f ) - return false; - - // - // edge 2 - // - edge = points[0] - points[2]; - - // cross x-edge - normal.x = 0.0f; - normal.y = -edge.z; - normal.z = edge.y; - if( ( ( normal.y * ( boxPt.y - points[0].y ) ) + ( normal.z * ( boxPt.z - points[0].z ) ) ) > 0.0f ) - return false; - - // cross, y-edge - normal.x = edge.z; - normal.y = 0.0f; - normal.z = edge.y; - if( ( ( normal.x * ( boxPt.x - points[0].x ) ) + ( normal.z * ( boxPt.z - points[0].z ) ) ) > 0.0f ) - return false; - - // cross z-edge - normal.x = -edge.y; - normal.y = edge.x; - normal.z = 0.0f; - if( ( ( normal.x * ( boxPt.x - points[0].x ) ) + ( normal.y * ( boxPt.y - points[0].y ) ) ) > 0.0f ) - return false; - - // - // triangle plane test - // - // do the opposite because the ray has been negated - if( ( ( pTri->m_Normal.x * ( boxPt.x - points[0].x ) ) + - ( pTri->m_Normal.y * ( boxPt.y - points[0].y ) ) + - ( pTri->m_Normal.z * ( boxPt.z - points[0].z ) ) ) > 0.0f ) - return false; - - return true; -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -bool CDispCollTree::CullTriList( CDispCollTreeTempData *pTemp, Vector &rayStart, Vector &rayEnd, Vector &extents, bool bIntersect ) -{ - // - // intersect AABB with all triangles in list - // - if( bIntersect ) - { - for( int i = 0; i < pTemp->m_TriListCount; i++ ) - { - if( IntersectAABBTriTest( rayStart, extents, pTemp->m_ppTriList[i] ) ) - return true; - } - - return false; - } - // - // sweep AABB against all triangles in list - // - else - { - bool bResult = false; - - for( int i = 0; i < pTemp->m_TriListCount; i++ ) - { - if( SweptAABBTriTest( rayStart, rayEnd, extents, pTemp->m_ppTriList[i] ) ) - { - pTemp->m_ppTriList[i]->SetIntersect( true ); - bResult = true; - } - } - - return bResult; - } -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -bool CDispCollTree::IntersectAABBAABBTest( CDispCollTreeTempData *pTemp, const Vector &pos, const Vector &extents ) -{ - float dist; - - for( int dir = 0; dir < 3; dir++ ) - { - // negative direction - dist = -( pos[dir] - ( pTemp->m_AABBDistances[(dir>>1)] - extents[dir] ) ); - if( dist > COLLISION_EPSILON ) - return false; - - // positive direction - dist = pos[dir] - ( pTemp->m_AABBDistances[(dir>>1)+1] + extents[dir] ); - if( dist > COLLISION_EPSILON ) - return false; - } - - return true; -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -bool CDispCollTree::SweptAABBAABBTest( CDispCollTreeTempData *pTemp, const Vector &rayStart, const Vector &rayEnd, const Vector &extents ) -{ - int dir; - float distStart, distEnd; - float fraction; - float deltas[3]; - float scalers[3]; - - // - // enter and exit fractions - // - float enterFraction = 0.0f; - float exitFraction = 0.0f; - - // - // de-normalize the paramter space so that we don't have to divide - // to find the fractional amount later (clamped for precision) - // - deltas[0] = rayEnd.x - rayStart.x; - deltas[1] = rayEnd.y - rayStart.y; - deltas[2] = rayEnd.z - rayStart.z; - if( ( deltas[0] < COLLISION_EPSILON ) && ( deltas[0] > -COLLISION_EPSILON ) ) { deltas[0] = 1.0f; } - if( ( deltas[1] < COLLISION_EPSILON ) && ( deltas[1] > -COLLISION_EPSILON ) ) { deltas[0] = 1.0f; } - if( ( deltas[2] < COLLISION_EPSILON ) && ( deltas[2] > -COLLISION_EPSILON ) ) { deltas[0] = 1.0f; } - scalers[0] = deltas[1] * deltas[2]; - scalers[1] = deltas[0] * deltas[2]; - scalers[2] = deltas[0] * deltas[1]; - - for( dir = 0; dir < 3; dir++ ) - { - // - // negative direction - // - distStart = -( rayStart[dir] - ( pTemp->m_AABBDistances[(dir>>1)] - extents[dir] ) ); - distEnd = -( rayEnd[dir] - ( pTemp->m_AABBDistances[(dir>>1)] - extents[dir] ) ); - - if( ( distStart > COLLISION_EPSILON ) && ( distEnd < -COLLISION_EPSILON ) ) - { - fraction = distStart * scalers[dir]; - if( fraction > enterFraction ) - { - enterFraction = fraction; - } - } - else if( ( distStart < -COLLISION_EPSILON ) && ( distEnd > COLLISION_EPSILON ) ) - { - fraction = distStart * scalers[dir]; - if( fraction < exitFraction ) - { - exitFraction = fraction; - } - } - else if( ( distStart > COLLISION_EPSILON ) && ( distEnd > COLLISION_EPSILON ) ) - { - return false; - } - - // - // positive direction - // - distStart = rayStart[dir] - ( pTemp->m_AABBDistances[(dir>>1)+1] + extents[dir] ); - distEnd = rayEnd[dir] - ( pTemp->m_AABBDistances[(dir>>1)+1] + extents[dir] ); - - if( ( distStart > COLLISION_EPSILON ) && ( distEnd < -COLLISION_EPSILON ) ) - { - fraction = distStart * scalers[dir]; - if( fraction > enterFraction ) - { - enterFraction = fraction; - } - } - else if( ( distStart < -COLLISION_EPSILON ) && ( distEnd > COLLISION_EPSILON ) ) - { - fraction = distStart * scalers[dir]; - if( fraction < exitFraction ) - { - exitFraction = fraction; - } - } - else if( ( distStart > COLLISION_EPSILON ) && ( distEnd > COLLISION_EPSILON ) ) - { - return false; - } - } - - if( exitFraction < enterFraction ) - return false; - - return true; -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CDispCollTree::BuildTriList_r( CDispCollTreeTempData *pTemp, int nodeIndex, Vector &rayStart, Vector &rayEnd, Vector &extents, - bool bIntersect ) -{ - // - // get the current nodes bounds and create collision test planes - // (saved in the in class cache m_AABBNormals, m_AABBDistances) - // - Vector bounds[2]; - CDispCollNode *pNode = &m_pNodes[nodeIndex]; - pNode->GetBounds( bounds[0], bounds[1] ); - CreatePlanesFromBounds( pTemp, bounds[0], bounds[1] ); - - // - // interesect/sweep test - // - bool bResult; - if( bIntersect ) - { - bResult = IntersectAABBAABBTest( pTemp, rayStart, extents ); - } - else - { - bResult = SweptAABBAABBTest( pTemp, rayStart, rayEnd, extents ); - } - - if( bResult ) - { - // if leaf node -- add triangles to interstection test list - if( pNode->IsLeaf() ) - { - // Assert for now -- flush cache later!!!!! - Assert( pTemp->m_TriListCount >= 0 ); - Assert( pTemp->m_TriListCount < TRILIST_CACHE_SIZE ); - - pTemp->m_ppTriList[pTemp->m_TriListCount] = &pNode->m_Tris[0]; - pTemp->m_ppTriList[pTemp->m_TriListCount+1] = &pNode->m_Tris[1]; - pTemp->m_TriListCount += 2; - } - // continue recursion - else - { - BuildTriList_r( pTemp, GetChildNode( nodeIndex, 0 ), rayStart, rayEnd, extents, bIntersect ); - BuildTriList_r( pTemp, GetChildNode( nodeIndex, 1 ), rayStart, rayEnd, extents, bIntersect ); - BuildTriList_r( pTemp, GetChildNode( nodeIndex, 2 ), rayStart, rayEnd, extents, bIntersect ); - BuildTriList_r( pTemp, GetChildNode( nodeIndex, 3 ), rayStart, rayEnd, extents, bIntersect ); - } - } -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -bool CDispCollTree::AABBSweep( CDispCollData *pData ) -{ - // reset the triangle lists counts - CDispCollTreeTempData tmp; - tmp.m_TriListCount = 0; - - // sweep the AABB against the tree - BuildTriList_r( &tmp, 0, pData->m_StartPos, pData->m_EndPos, pData->m_Extents, false ); - - // find collision triangles - if( CullTriList( &tmp, pData->m_StartPos, pData->m_EndPos, pData->m_Extents, false ) ) - { - // find closest intersection - return AABBTriIntersect( &tmp, pData ); - } - - return false; -} - - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -bool CDispCollTree::AABBIntersect( CDispCollData *pData ) -{ - // reset the triangle lists counts - CDispCollTreeTempData tmp; - tmp.m_TriListCount = 0; - - // sweep the AABB against the tree - BuildTriList_r( &tmp, 0, pData->m_StartPos, pData->m_StartPos, pData->m_Extents, true ); - - // find collision triangles - return CullTriList( &tmp, pData->m_StartPos, pData->m_StartPos, pData->m_Extents, true ); -} +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include "builddisp.h" +#include "dispcoll.h" +#include "tier0/dbg.h" + +//============================================================================= + +const float CDispCollTree::COLLISION_EPSILON = 0.01f; +const float CDispCollTree::ONE_MINUS_COLLISION_EPSILON = 1.0f - COLLISION_EPSILON; + +//============================================================================= +// +// Displacement Collision Triangle Functions +// + + +//----------------------------------------------------------------------------- +// Purpose: initialize the displacement triangles +//----------------------------------------------------------------------------- +void CDispCollTri::Init( void ) +{ + for( int i = 0; i < 3; i++ ) + { + m_Points[i].x = 0.0f; m_Points[i].y = 0.0f; m_Points[i].z = 0.0f; + m_PointNormals[i].x = 0.0f; m_PointNormals[i].y = 0.0f; m_PointNormals[i].z = 0.0f; + } + + m_Normal.x = 0.0f; m_Normal.y = 0.0f; m_Normal.z = 0.0f; + m_Distance = 0.0f; + + m_ProjAxes[0] = -1; + m_ProjAxes[1] = -1; + + m_bIntersect = false; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +inline void CDispCollTri::SetPoint( int index, Vector const &vert ) +{ + Assert( index >= 0 ); + Assert( index < 3 ); + + m_Points[index].x = vert[0]; + m_Points[index].y = vert[1]; + m_Points[index].z = vert[2]; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +inline void CDispCollTri::SetPointNormal( int index, Vector const &normal ) +{ + Assert( index >= 0 ); + Assert( index < 3 ); + + m_PointNormals[index].x = normal[0]; + m_PointNormals[index].y = normal[1]; + m_PointNormals[index].z = normal[2]; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CDispCollTri::CalcPlane( void ) +{ + // + // calculate the plane normal and distance + // + Vector segment1, segment2, cross; + + segment1 = m_Points[1] - m_Points[0]; + segment2 = m_Points[2] - m_Points[0]; + cross = segment1.Cross( segment2 ); + m_Normal = cross; + VectorNormalize(m_Normal); + + m_Distance = m_Normal.Dot( m_Points[0] ); + + // + // calculate the projection axes + // + if( FloatMakePositive( m_Normal[0] ) > FloatMakePositive( m_Normal[1] ) ) + { + if( FloatMakePositive( m_Normal[0] ) > FloatMakePositive( m_Normal[2] ) ) + { + m_ProjAxes[0] = 1; + m_ProjAxes[1] = 2; + } + else + { + m_ProjAxes[0] = 0; + m_ProjAxes[1] = 1; + } + } + else + { + if( FloatMakePositive( m_Normal[1] ) > FloatMakePositive( m_Normal[2] ) ) + { + m_ProjAxes[0] = 0; + m_ProjAxes[1] = 2; + } + else + { + m_ProjAxes[0] = 0; + m_ProjAxes[1] = 1; + } + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +inline void CDispCollTri::SetIntersect( bool bIntersect ) +{ + m_bIntersect = bIntersect; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +inline bool CDispCollTri::IsIntersect( void ) +{ + return m_bIntersect; +} + + +//============================================================================= +// +// Displacement Collision Node Functions +// + + +//----------------------------------------------------------------------------- +// Purpose: constructor +//----------------------------------------------------------------------------- +CDispCollNode::CDispCollNode() +{ + m_Bounds[0].x = m_Bounds[0].y = m_Bounds[0].z = 99999.9f; + m_Bounds[1].x = m_Bounds[1].y = m_Bounds[1].z = -99999.9f; + + m_Tris[0].Init(); + m_Tris[1].Init(); + + m_bIsLeaf = false; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +inline bool CDispCollNode::IsLeaf( void ) +{ + return m_bIsLeaf; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +inline void CDispCollNode::SetBounds( Vector const &bMin, Vector const &bMax ) +{ + m_Bounds[0] = bMin; + m_Bounds[1] = bMax; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +inline void CDispCollNode::GetBounds( Vector &bMin, Vector &bMax ) +{ + bMin = m_Bounds[0]; + bMax = m_Bounds[1]; +} + + +//============================================================================= +// +// Displacement Collision Tree Functions +// + +//----------------------------------------------------------------------------- +// Purpose: constructor +//----------------------------------------------------------------------------- +CDispCollTree::CDispCollTree() +{ + m_Power = 0; + + m_NodeCount = 0; + m_pNodes = NULL; + + InitAABBData(); +} + + +//----------------------------------------------------------------------------- +// Purpose: deconstructor +//----------------------------------------------------------------------------- +CDispCollTree::~CDispCollTree() +{ + FreeNodes(); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CDispCollTree::InitAABBData( void ) +{ + m_AABBNormals[0].x = -1.0f; m_AABBNormals[0].y = 0.0f; m_AABBNormals[0].z = 0.0f; + m_AABBNormals[1].x = 1.0f; m_AABBNormals[1].y = 0.0f; m_AABBNormals[1].z = 0.0f; + + m_AABBNormals[2].x = 0.0f; m_AABBNormals[2].y = -1.0f; m_AABBNormals[2].z = 0.0f; + m_AABBNormals[3].x = 0.0f; m_AABBNormals[3].y = 1.0f; m_AABBNormals[3].z = 0.0f; + + m_AABBNormals[4].x = 0.0f; m_AABBNormals[4].y = 0.0f; m_AABBNormals[4].z = -1.0f; + m_AABBNormals[5].x = 0.0f; m_AABBNormals[5].y = 0.0f; m_AABBNormals[5].z = 1.0f; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CDispCollTree::CalcBounds( CDispCollNode *pNode, int nodeIndex ) +{ + Vector bounds[2]; + bounds[0].Init( 99999.9f, 99999.9f, 99999.9f ); + bounds[1].Init( -99999.9f, -99999.9f, -99999.9f ); + + // + // handle leaves differently -- bounding volume defined by triangles + // + if( pNode->IsLeaf() ) + { + for( int i = 0; i < 2; i++ ) + { + for( int j = 0; j < 3; j++ ) + { + // + // minimum + // + if( bounds[0].x > pNode->m_Tris[i].m_Points[j].x ) { bounds[0].x = pNode->m_Tris[i].m_Points[j].x; } + if( bounds[0].y > pNode->m_Tris[i].m_Points[j].y ) { bounds[0].y = pNode->m_Tris[i].m_Points[j].y; } + if( bounds[0].z > pNode->m_Tris[i].m_Points[j].z ) { bounds[0].z = pNode->m_Tris[i].m_Points[j].z; } + + // + // maximum + // + if( bounds[1].x < pNode->m_Tris[i].m_Points[j].x ) { bounds[1].x = pNode->m_Tris[i].m_Points[j].x; } + if( bounds[1].y < pNode->m_Tris[i].m_Points[j].y ) { bounds[1].y = pNode->m_Tris[i].m_Points[j].y; } + if( bounds[1].z < pNode->m_Tris[i].m_Points[j].z ) { bounds[1].z = pNode->m_Tris[i].m_Points[j].z; } + } + } + } + // + // bounding volume defined by maxima and minima of children volumes + // + else + { + for( int i = 0; i < 4; i++ ) + { + int childIndex = GetChildNode( nodeIndex, i ); + CDispCollNode *pChildNode = &m_pNodes[childIndex]; + + Vector childBounds[2]; + pChildNode->GetBounds( childBounds[0], childBounds[1] ); + + // + // minimum + // + if( bounds[0].x > childBounds[0].x ) { bounds[0].x = childBounds[0].x; } + if( bounds[0].y > childBounds[0].y ) { bounds[0].y = childBounds[0].y; } + if( bounds[0].z > childBounds[0].z ) { bounds[0].z = childBounds[0].z; } + + // + // maximum + // + if( bounds[1].x < childBounds[1].x ) { bounds[1].x = childBounds[1].x; } + if( bounds[1].y < childBounds[1].y ) { bounds[1].y = childBounds[1].y; } + if( bounds[1].z < childBounds[1].z ) { bounds[1].z = childBounds[1].z; } + } + } + + pNode->SetBounds( bounds[0], bounds[1] ); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CDispCollTree::CreateNodes_r( CCoreDispInfo *pDisp, int nodeIndex, int termLevel ) +{ + int nodeLevel = GetNodeLevel( nodeIndex ); + + // + // terminating condition -- set node info (leaf or otherwise) + // + if( nodeLevel == termLevel ) + { + CDispCollNode *pNode = &m_pNodes[nodeIndex]; + CalcBounds( pNode, nodeIndex ); + + return; + } + + // + // recurse into children + // + for( int i = 0; i < 4; i++ ) + { + CreateNodes_r( pDisp, GetChildNode( nodeIndex, i ), termLevel ); + } +} + + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CDispCollTree::CreateNodes( CCoreDispInfo *pDisp ) +{ + // + // create all nodes in tree + // + int power = pDisp->GetPower() + 1; + for( int level = power; level > 0; level-- ) + { + CreateNodes_r( pDisp, 0 /* rootIndex */, level ); + } +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +int CDispCollTree::GetNodeIndexFromComponents( int x, int y ) +{ + int index = 0; + + // Interleave bits from the x and y values to create the index: + + for( int shift = 0; x != 0; shift += 2, x >>= 1 ) + { + index |= ( x & 1 ) << shift; + } + + for( int shift = 1; y != 0; shift += 2, y >>= 1 ) + { + index |= ( y & 1 ) << shift; + } + + return index; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CDispCollTree::InitLeaves( CCoreDispInfo *pDisp ) +{ + // + // get power and width and displacement surface + // + int power = pDisp->GetPower(); + int width = pDisp->GetWidth(); + + // + // get leaf indices + // + int startIndex = CalcNodeCount( power - 1 ); + int endIndex = CalcNodeCount( power ); + + for( int index = startIndex; index < endIndex; index++ ) + { + // + // create triangles at leaves + // + int x = ( index - startIndex ) % ( width - 1 ); + int y = ( index - startIndex ) / ( width - 1 ); + + int nodeIndex = GetNodeIndexFromComponents( x, y ); + nodeIndex += startIndex; + + Vector vert; + Vector normal; + + // + // tri 1 + // + pDisp->GetVert( x + ( y * width ), vert ); + pDisp->GetNormal( x + ( y * width ), normal ); + m_pNodes[nodeIndex].m_Tris[0].SetPoint( 0, vert ); + m_pNodes[nodeIndex].m_Tris[0].SetPointNormal( 0, normal ); + + pDisp->GetVert( x + ( ( y + 1 ) * width ), vert ); + pDisp->GetNormal( x + ( ( y + 1 ) * width ), normal ); + m_pNodes[nodeIndex].m_Tris[0].SetPoint( 1, vert ); + m_pNodes[nodeIndex].m_Tris[0].SetPointNormal( 1, normal ); + + pDisp->GetVert( ( x + 1 ) + ( y * width ), vert ); + pDisp->GetNormal( ( x + 1 ) + ( y * width ), normal ); + m_pNodes[nodeIndex].m_Tris[0].SetPoint( 2, vert ); + m_pNodes[nodeIndex].m_Tris[0].SetPointNormal( 2, normal ); + + m_pNodes[nodeIndex].m_Tris[0].CalcPlane(); + + // + // tri 2 + // + pDisp->GetVert( ( x + 1 ) + ( y * width ), vert ); + pDisp->GetNormal( ( x + 1 ) + ( y * width ), normal ); + m_pNodes[nodeIndex].m_Tris[1].SetPoint( 0, vert ); + m_pNodes[nodeIndex].m_Tris[1].SetPointNormal( 0, normal ); + + pDisp->GetVert( x + ( ( y + 1 ) * width ), vert ); + pDisp->GetNormal( x + ( ( y + 1 ) * width ), normal ); + m_pNodes[nodeIndex].m_Tris[1].SetPoint( 1, vert ); + m_pNodes[nodeIndex].m_Tris[1].SetPointNormal( 1, normal ); + + pDisp->GetVert( ( x + 1 ) + ( ( y + 1 ) * width ), vert ); + pDisp->GetNormal( ( x + 1 ) + ( ( y + 1 ) * width ), normal ); + m_pNodes[nodeIndex].m_Tris[1].SetPoint( 2, vert ); + m_pNodes[nodeIndex].m_Tris[1].SetPointNormal( 2, normal ); + + m_pNodes[nodeIndex].m_Tris[1].CalcPlane(); + + // set node as leaf + m_pNodes[nodeIndex].m_bIsLeaf = true; + } +} + + +//----------------------------------------------------------------------------- +// Purpose: allocate and initialize the displacement collision tree +// Input: power - size of the displacement surface +// Output: bool - success? (true/false) +//----------------------------------------------------------------------------- +bool CDispCollTree::Create( CCoreDispInfo *pDisp ) +{ + // + // calculate the number of nodes needed given the size of the displacement + // + m_Power = pDisp->GetPower(); + m_NodeCount = CalcNodeCount( m_Power ); + + // + // allocate tree space + // + if( !AllocNodes( m_NodeCount ) ) + return false; + + // initialize leaves + InitLeaves( pDisp ); + + // create tree nodes + CreateNodes( pDisp ); + + // tree successfully created! + return true; +} + + +//----------------------------------------------------------------------------- +// Purpose: allocate memory for the displacement collision tree +// Input: nodeCount - number of nodes to allocate +// Output: bool - success? (true/false) +//----------------------------------------------------------------------------- +bool CDispCollTree::AllocNodes( int nodeCount ) +{ + // sanity check + Assert( nodeCount != 0 ); + + m_pNodes = new CDispCollNode[nodeCount]; + if( !m_pNodes ) + return false; + + // tree successfully allocated! + return true; +} + + +//----------------------------------------------------------------------------- +// Purpose: release allocated memory for displacement collision tree +//----------------------------------------------------------------------------- +void CDispCollTree::FreeNodes( void ) +{ + if( m_pNodes ) + { + delete [] m_pNodes; + m_pNodes = NULL; + } +} + + +//----------------------------------------------------------------------------- +// Purpose: calculate the number of tree nodes given the size of the +// displacement surface +// Input: power - size of the displacement surface +// Output: int - the number of tree nodes +//----------------------------------------------------------------------------- +inline int CDispCollTree::CalcNodeCount( int power ) +{ + // power range [2...4] + Assert( power > 0 ); + Assert( power < 5 ); + + return ( ( 1 << ( ( power + 1 ) << 1 ) ) / 3 ); +} + + +//----------------------------------------------------------------------------- +// Purpose: get the parent node index given the current node +// Input: nodeIndex - current node index +// Output: int - the index of the parent node +//----------------------------------------------------------------------------- +inline int CDispCollTree::GetParentNode( int nodeIndex ) +{ + // node range [0...m_NodeCount) + Assert( nodeIndex >= 0 ); + Assert( nodeIndex < m_NodeCount ); + + // ( nodeIndex - 1 ) / 4 + return ( ( nodeIndex - 1 ) >> 2 ); +} + + +//----------------------------------------------------------------------------- +// Purpose: get the child node index given the current node index and direction +// of the child (1 of 4) +// Input: nodeIndex - current node index +// direction - direction of the child ( [0...3] - SW, SE, NW, NE ) +// Output: int - the index of the child node +//----------------------------------------------------------------------------- +inline int CDispCollTree::GetChildNode( int nodeIndex, int direction ) +{ + // node range [0...m_NodeCount) + Assert( nodeIndex >= 0 ); + Assert( nodeIndex < m_NodeCount ); + + // ( nodeIndex * 4 ) + ( direction + 1 ) + return ( ( nodeIndex << 2 ) + ( direction + 1 ) ); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +inline int CDispCollTree::GetNodeLevel( int nodeIndex ) +{ + // node range [0...m_NodeCount) + Assert( nodeIndex >= 0 ); + Assert( nodeIndex < m_NodeCount ); + + // level = 2^n + 1 + if( nodeIndex == 0 ) { return 1; } + if( nodeIndex < 5 ) { return 2; } + if( nodeIndex < 21 ) { return 3; } + if( nodeIndex < 85 ) { return 4; } + if( nodeIndex < 341 ) { return 5; } + + return -1; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +bool CDispCollTree::RayTriTest( Vector const &rayStart, Vector const &rayDir, float const rayLength, + CDispCollTri const *pTri, float *fraction ) +{ + const float DET_EPSILON = 0.001f; + const float DIST_EPSILON = 0.001f; + + // + // calculate the edges + // + Vector edge1 = pTri->m_Points[1] - pTri->m_Points[0]; + Vector edge2 = pTri->m_Points[2] - pTri->m_Points[0]; + +// Vector faceNormal = edge1.Cross( edge2 ); +// Vector normNormal = faceNormal.Normalize(); + + // + // calculate the triangle's determinant + // + Vector pVec = rayDir.Cross( edge2 ); + float det = pVec.Dot( edge1 ); + + // if determinant is zero -- ray lies in plane + if( ( det > -DET_EPSILON ) && ( det < DET_EPSILON ) ) + return false; + + // + // utility calculations - inverse determinant and distance from v0 to ray start + // + double invDet = 1.0f / det; + Vector tVec = rayStart - pTri->m_Points[0]; + + // + // calculate the U parameter and test bounds + // + double u = pVec.Dot( tVec ) * invDet; + if( ( u < 0.0f ) || ( u > 1.0f ) ) + return false; + + Vector qVec = tVec.Cross( edge1 ); + + // + // calculate the V parameter and test bounds + // + double v = qVec.Dot( rayDir ) * invDet; + if( ( v < 0.0f ) || ( ( u + v ) > 1.0f ) ) + return false; + + // calculate where ray intersects triangle + *fraction = qVec.Dot( edge2 ) * invDet; + *fraction /= rayLength; + + if( ( *fraction < DIST_EPSILON ) || ( *fraction > ( 1.0f - DIST_EPSILON ) ) ) + return false; + + return true; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +bool CDispCollTree::RayTriListTest( CDispCollTreeTempData *pTemp, CDispCollData *pData ) +{ + // save starting fraction -- to test for collision + float startFraction = pData->m_Fraction; + + // + // calculate the ray + // + Vector seg = pData->m_EndPos - pData->m_StartPos; + Vector rayDir = seg; + float rayLength = VectorNormalize( rayDir ); + + // + // test ray against all triangles in list + // + for( int i = 0; i < pTemp->m_TriListCount; i++ ) + { + float fraction = 1.0f; + bool bResult = RayTriTest( pData->m_StartPos, rayDir, rayLength, pTemp->m_ppTriList[i], &fraction ); + if( !bResult ) + continue; + + if( pData->m_bOcclude ) + { + return true; + } + + if( fraction < pData->m_Fraction ) + { + pData->m_Fraction = fraction; + pData->m_Normal = pTemp->m_ppTriList[i]->m_Normal; + pData->m_Distance = pTemp->m_ppTriList[i]->m_Distance; + } + } + + // collision! + if( pData->m_Fraction < startFraction ) + return true; + + // no collision! + return false; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CDispCollTree::RayAABBTest( CDispCollTreeTempData *pTemp, Vector &rayStart, Vector &rayEnd ) +{ + const float MY_DIST_EPSILON = 0.01f; + + for( int i = 0; i < 6; i++ ) + { + float dist1 = m_AABBNormals[i].Dot( rayStart ) - pTemp->m_AABBDistances[i]; + float dist2 = m_AABBNormals[i].Dot( rayEnd ) - pTemp->m_AABBDistances[i]; + + // + // entry intersection point - move ray start up to intersection + // + if( ( dist1 > MY_DIST_EPSILON ) && ( dist2 < -MY_DIST_EPSILON ) ) + { + float fraction = ( dist1 / ( dist1 - dist2 ) ); + + Vector segment, increment; + segment = ( rayEnd - rayStart ) * fraction; + increment = segment; + VectorNormalize(increment); + segment += increment; + rayStart += segment; + } + else if( ( dist1 > MY_DIST_EPSILON ) && ( dist2 > MY_DIST_EPSILON ) ) + { + return false; + } + } + + return true; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CDispCollTree::CreatePlanesFromBounds( CDispCollTreeTempData *pTemp, Vector const &bbMin, Vector const &bbMax ) +{ + // + // note -- these never change! + // +// m_AABBNormals[0].x = -1; +// m_AABBNormals[1].x = 1; + +// m_AABBNormals[2].y = -1; +// m_AABBNormals[3].y = 1; + +// m_AABBNormals[4].z = -1; +// m_AABBNormals[5].z = 1; + + pTemp->m_AABBDistances[0] = -bbMin.x; + pTemp->m_AABBDistances[1] = bbMax.x; + + pTemp->m_AABBDistances[2] = -bbMin.y; + pTemp->m_AABBDistances[3] = bbMax.y; + + pTemp->m_AABBDistances[4] = -bbMin.z; + pTemp->m_AABBDistances[5] = bbMax.z; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CDispCollTree::RayNodeTest_r( CDispCollTreeTempData *pTemp, int nodeIndex, Vector rayStart, Vector rayEnd ) +{ + // get the current node + CDispCollNode *pNode = &m_pNodes[nodeIndex]; + + // + // get node bounding box and create collision planes + // + Vector bounds[2]; + pNode->GetBounds( bounds[0], bounds[1] ); + CreatePlanesFromBounds( pTemp, bounds[0], bounds[1] ); + + bool bIntersect = RayAABBTest( pTemp, rayStart, rayEnd ); + if( bIntersect ) + { + // done -- add triangles to triangle list + if( pNode->IsLeaf() ) + { + // Assert for now -- flush cache later!!!!! + Assert( pTemp->m_TriListCount >= 0 ); + Assert( pTemp->m_TriListCount < TRILIST_CACHE_SIZE ); + + pTemp->m_ppTriList[pTemp->m_TriListCount] = &pNode->m_Tris[0]; + pTemp->m_ppTriList[pTemp->m_TriListCount+1] = &pNode->m_Tris[1]; + pTemp->m_TriListCount += 2; + } + // continue recursion + else + { + for( int i = 0; i < 4; i++ ) + { + RayNodeTest_r( pTemp, GetChildNode( nodeIndex, i ), rayStart, rayEnd ); + } + } + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CDispCollTree::RayTestAllTris( CDispCollData *pData, int power ) +{ + // + // get leaf indices + // + int startIndex = CalcNodeCount( power - 1 ); + int endIndex = CalcNodeCount( power ); + + // save incoming fraction + float startFraction = pData->m_Fraction; + float fraction = pData->m_Fraction; + + Vector ray = pData->m_EndPos - pData->m_StartPos; + Vector rayDir = ray; + float rayLength = VectorNormalize(rayDir); + + // + // test ray against all triangles in list + // + for( int index = startIndex; index < endIndex; index++ ) + { + for( int j = 0; j < 2; j++ ) + { + bool bResult = RayTriTest( pData->m_StartPos, rayDir, rayLength, &m_pNodes[index].m_Tris[j], &fraction ); + if( !bResult ) + continue; + + if( pData->m_bOcclude ) + { + return true; + } + + if( fraction < pData->m_Fraction ) + { + pData->m_Fraction = fraction; + pData->m_Normal = m_pNodes[index].m_Tris[j].m_Normal; + pData->m_Distance = m_pNodes[index].m_Tris[j].m_Distance; + } + } + } + + // collision! + if( pData->m_Fraction < startFraction ) + return true; + + // no collision! + return false; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CDispCollTree::RayTest( CDispCollData *pData ) +{ + // reset the triangle list count + CDispCollTreeTempData tmp; + tmp.m_TriListCount = 0; + + // trace against nodes (copy start, end because they change) + RayNodeTest_r( &tmp, 0, pData->m_StartPos, pData->m_EndPos ); + + // + // trace against tris (if need be) + // + if( tmp.m_TriListCount != 0 ) + { + bool result = RayTriListTest( &tmp, pData ); + return result; + } + + return false; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CDispCollTree::SweptAABBTriIntersect( Vector &rayStart, Vector &rayEnd, Vector &extents, + CDispCollTri const *pTri, Vector &plNormal, float *plDist, + float *fraction ) +{ + + // + // PUT A COPY HERE OF START AND END -- SINCE I CHANGE THEM!!!!!! + // + + + + + + int dir, ptIndex; + float closeValue; + float distStart, distEnd; + float t; + Vector rayPt; + + // get ray direction + Vector rayDir = rayEnd - rayStart; + + // initialize fraction + *fraction = 1.0f; + + // + // test for collision with axial planes (x, y, z) + // + for( dir = 0; dir < 3; dir++ ) + { + if( rayDir[dir] < 0.0f ) + { + closeValue = -99999.9f; + for( ptIndex = 0; ptIndex < 3; ptIndex++ ) + { + if( pTri->m_Points[ptIndex][dir] > closeValue ) + { + closeValue = pTri->m_Points[ptIndex][dir]; + } + } + + closeValue += extents[dir]; + + distStart = rayStart[dir] - closeValue; + distEnd = rayEnd[dir] - closeValue; + } + else + { + closeValue = 99999.9f; + for( ptIndex = 0; ptIndex < 3; ptIndex++ ) + { + if( pTri->m_Points[ptIndex][dir] < closeValue ) + { + closeValue = pTri->m_Points[ptIndex][dir]; + } + } + + closeValue -= extents[dir]; + + distStart = -( rayStart[dir] - closeValue ); + distEnd = -( rayEnd[dir] - closeValue ); + } + + if( ( distStart > COLLISION_EPSILON ) && ( distEnd < -COLLISION_EPSILON ) ) + { + t = ( distStart - COLLISION_EPSILON ) / ( distStart - distEnd ); + if( t > *fraction ) + { + VectorScale( rayDir, t, rayPt ); + VectorAdd( rayStart, rayPt, rayStart ); + *fraction = t; + plNormal.Init(); + plNormal[dir] = 1.0f; + *plDist = closeValue; + } + } + else if( ( distStart < -COLLISION_EPSILON ) && ( distEnd > COLLISION_EPSILON ) ) + { + t = ( distStart + COLLISION_EPSILON ) / ( distStart - distEnd ); + VectorScale( rayDir, t, rayPt ); + VectorAdd( rayStart, rayPt, rayEnd ); + } + else if( ( distStart > COLLISION_EPSILON ) && ( distEnd > COLLISION_EPSILON ) ) + { + return false; + } + } + + // + // check for an early out + // + if( ( pTri->m_Normal[0] > ONE_MINUS_COLLISION_EPSILON ) || + ( pTri->m_Normal[1] > ONE_MINUS_COLLISION_EPSILON ) || + ( pTri->m_Normal[2] > ONE_MINUS_COLLISION_EPSILON ) ) + { + if( *fraction == 1.0f ) + return false; + + return true; + } + + // + // handle 9 edge tests + // + Vector normal; + Vector edge; + float dist; + + // find the closest box point + Vector boxPt( 0.0f, 0.0f, 0.0f ); + for( dir = 0; dir < 3; dir++ ) + { + if( rayDir[dir] < 0.0f ) + { + boxPt[dir] = extents[dir]; + } + else + { + boxPt[dir] = -extents[dir]; + } + } + + // + // edge 0 + // + edge = pTri->m_Points[1] - pTri->m_Points[0]; + + // cross x-edge + normal.x = 0.0f; + normal.y = -edge.z; + normal.z = edge.y; + + // extents adjusted dist + dist = ( normal.y * ( boxPt.y - pTri->m_Points[0].y ) ) + ( normal.z * ( boxPt.z - pTri->m_Points[0].z ) ); + + // find distances from plane (start, end) + distStart = ( normal.y * rayStart.y ) + ( normal.z * rayStart.z ) - dist; + distEnd = ( normal.y * rayEnd.y ) + ( normal.z * rayEnd.z ) - dist; + + if( ( distStart > COLLISION_EPSILON ) && ( distEnd < -COLLISION_EPSILON ) ) + { + t = ( distStart - COLLISION_EPSILON ) / ( distStart - distEnd ); + if( t > *fraction ) + { + VectorScale( rayDir, t, rayPt ); + VectorAdd( rayStart, rayPt, rayStart ); + *fraction = t; + plNormal = normal; + *plDist = dist; + } + } + else if( ( distStart < -COLLISION_EPSILON ) && ( distEnd > COLLISION_EPSILON ) ) + { + t = ( distStart + COLLISION_EPSILON ) / ( distStart - distEnd ); + VectorScale( rayDir, t, rayPt ); + VectorAdd( rayStart, rayPt, rayEnd ); + } + else if( ( distStart > COLLISION_EPSILON ) && ( distEnd > COLLISION_EPSILON ) ) + { + return false; + } + + // cross y-edge + normal.x = edge.z; + normal.y = 0.0f; + normal.z = edge.y; + + // extents adjusted dist + dist = ( normal.x * ( boxPt.x - pTri->m_Points[0].x ) ) + ( normal.z * ( boxPt.z - pTri->m_Points[0].z ) ); + + // find distances from plane (start, end) + distStart = ( normal.x * rayStart.x ) + ( normal.z * rayStart.z ) - dist; + distEnd = ( normal.x * rayEnd.x ) + ( normal.z * rayEnd.z ) - dist; + + if( ( distStart > COLLISION_EPSILON ) && ( distEnd < -COLLISION_EPSILON ) ) + { + t = ( distStart - COLLISION_EPSILON ) / ( distStart - distEnd ); + if( t > *fraction ) + { + VectorScale( rayDir, t, rayPt ); + VectorAdd( rayStart, rayPt, rayStart ); + *fraction = t; + plNormal = normal; + *plDist = dist; + } + } + else if( ( distStart < -COLLISION_EPSILON ) && ( distEnd > COLLISION_EPSILON ) ) + { + t = ( distStart + COLLISION_EPSILON ) / ( distStart - distEnd ); + VectorScale( rayDir, t, rayPt ); + VectorAdd( rayStart, rayPt, rayEnd ); + } + else if( ( distStart > COLLISION_EPSILON ) && ( distEnd > COLLISION_EPSILON ) ) + { + return false; + } + + // cross z-edge + normal.x = -edge.y; + normal.y = edge.x; + normal.z = 0.0f; + + // extents adjusted dist + dist = ( normal.x * ( boxPt.x - pTri->m_Points[0].x ) ) + ( normal.y * ( boxPt.y - pTri->m_Points[0].y ) ); + + // find distances from plane (start, end) + distStart = ( normal.x * rayStart.x ) + ( normal.y * rayStart.y ) - dist; + distEnd = ( normal.x * rayEnd.x ) + ( normal.y * rayEnd.y ) - dist; + + if( ( distStart > COLLISION_EPSILON ) && ( distEnd < -COLLISION_EPSILON ) ) + { + t = ( distStart - COLLISION_EPSILON ) / ( distStart - distEnd ); + if( t > *fraction ) + { + VectorScale( rayDir, t, rayPt ); + VectorAdd( rayStart, rayPt, rayStart ); + *fraction = t; + plNormal = normal; + *plDist = dist; + } + } + else if( ( distStart < -COLLISION_EPSILON ) && ( distEnd > COLLISION_EPSILON ) ) + { + t = ( distStart + COLLISION_EPSILON ) / ( distStart - distEnd ); + VectorScale( rayDir, t, rayPt ); + VectorAdd( rayStart, rayPt, rayEnd ); + } + else if( ( distStart > COLLISION_EPSILON ) && ( distEnd > COLLISION_EPSILON ) ) + { + return false; + } + + // + // edge 1 + // + edge = pTri->m_Points[2] - pTri->m_Points[1]; + + // cross x-edge + normal.x = 0.0f; + normal.y = -edge.z; + normal.z = edge.y; + + // extents adjusted dist + dist = ( normal.y * ( boxPt.y - pTri->m_Points[0].y ) ) + ( normal.z * ( boxPt.z - pTri->m_Points[0].z ) ); + + // find distances from plane (start, end) + distStart = ( normal.y * rayStart.y ) + ( normal.z * rayStart.z ) - dist; + distEnd = ( normal.y * rayEnd.y ) + ( normal.z * rayEnd.z ) - dist; + + if( ( distStart > COLLISION_EPSILON ) && ( distEnd < -COLLISION_EPSILON ) ) + { + t = ( distStart - COLLISION_EPSILON ) / ( distStart - distEnd ); + if( t > *fraction ) + { + VectorScale( rayDir, t, rayPt ); + VectorAdd( rayStart, rayPt, rayStart ); + *fraction = t; + plNormal = normal; + *plDist = dist; + } + } + else if( ( distStart < -COLLISION_EPSILON ) && ( distEnd > COLLISION_EPSILON ) ) + { + t = ( distStart + COLLISION_EPSILON ) / ( distStart - distEnd ); + VectorScale( rayDir, t, rayPt ); + VectorAdd( rayStart, rayPt, rayEnd ); + } + else if( ( distStart > COLLISION_EPSILON ) && ( distEnd > COLLISION_EPSILON ) ) + { + return false; + } + + // cross y-edge + normal.x = edge.z; + normal.y = 0.0f; + normal.z = edge.y; + + // extents adjusted dist + dist = ( normal.x * ( boxPt.x - pTri->m_Points[0].x ) ) + ( normal.z * ( boxPt.z - pTri->m_Points[0].z ) ); + + // find distances from plane (start, end) + distStart = ( normal.x * rayStart.x ) + ( normal.z * rayStart.z ) - dist; + distEnd = ( normal.x * rayEnd.x ) + ( normal.z * rayEnd.z ) - dist; + + if( ( distStart > COLLISION_EPSILON ) && ( distEnd < -COLLISION_EPSILON ) ) + { + t = ( distStart - COLLISION_EPSILON ) / ( distStart - distEnd ); + if( t > *fraction ) + { + VectorScale( rayDir, t, rayPt ); + VectorAdd( rayStart, rayPt, rayStart ); + *fraction = t; + plNormal = normal; + *plDist = dist; + } + } + else if( ( distStart < -COLLISION_EPSILON ) && ( distEnd > COLLISION_EPSILON ) ) + { + t = ( distStart + COLLISION_EPSILON ) / ( distStart - distEnd ); + VectorScale( rayDir, t, rayPt ); + VectorAdd( rayStart, rayPt, rayEnd ); + } + else if( ( distStart > COLLISION_EPSILON ) && ( distEnd > COLLISION_EPSILON ) ) + { + return false; + } + + // cross z-edge + normal.x = -edge.y; + normal.y = edge.x; + normal.z = 0.0f; + + // extents adjusted dist + dist = ( normal.x * ( boxPt.x - pTri->m_Points[0].x ) ) + ( normal.y * ( boxPt.y - pTri->m_Points[0].y ) ); + + // find distances from plane (start, end) + distStart = ( normal.x * rayStart.x ) + ( normal.y * rayStart.y ) - dist; + distEnd = ( normal.x * rayEnd.x ) + ( normal.y * rayEnd.y ) - dist; + + if( ( distStart > COLLISION_EPSILON ) && ( distEnd < -COLLISION_EPSILON ) ) + { + t = ( distStart - COLLISION_EPSILON ) / ( distStart - distEnd ); + if( t > *fraction ) + { + VectorScale( rayDir, t, rayPt ); + VectorAdd( rayStart, rayPt, rayStart ); + *fraction = t; + plNormal = normal; + *plDist = dist; + } + } + else if( ( distStart < -COLLISION_EPSILON ) && ( distEnd > COLLISION_EPSILON ) ) + { + t = ( distStart + COLLISION_EPSILON ) / ( distStart - distEnd ); + VectorScale( rayDir, t, rayPt ); + VectorAdd( rayStart, rayPt, rayEnd ); + } + else if( ( distStart > COLLISION_EPSILON ) && ( distEnd > COLLISION_EPSILON ) ) + { + return false; + } + + // + // edge 2 + // + edge = pTri->m_Points[0] - pTri->m_Points[2]; + + // cross x-edge + normal.x = 0.0f; + normal.y = -edge.z; + normal.z = edge.y; + + // extents adjusted dist + dist = ( normal.y * ( boxPt.y - pTri->m_Points[0].y ) ) + ( normal.z * ( boxPt.z - pTri->m_Points[0].z ) ); + + // find distances from plane (start, end) + distStart = ( normal.y * rayStart.y ) + ( normal.z * rayStart.z ) - dist; + distEnd = ( normal.y * rayEnd.y ) + ( normal.z * rayEnd.z ) - dist; + + if( ( distStart > COLLISION_EPSILON ) && ( distEnd < -COLLISION_EPSILON ) ) + { + t = ( distStart - COLLISION_EPSILON ) / ( distStart - distEnd ); + if( t > *fraction ) + { + VectorScale( rayDir, t, rayPt ); + VectorAdd( rayStart, rayPt, rayStart ); + *fraction = t; + plNormal = normal; + *plDist = dist; + } + } + else if( ( distStart < -COLLISION_EPSILON ) && ( distEnd > COLLISION_EPSILON ) ) + { + t = ( distStart + COLLISION_EPSILON ) / ( distStart - distEnd ); + VectorScale( rayDir, t, rayPt ); + VectorAdd( rayStart, rayPt, rayEnd ); + } + else if( ( distStart > COLLISION_EPSILON ) && ( distEnd > COLLISION_EPSILON ) ) + { + return false; + } + + // cross y-edge + normal.x = edge.z; + normal.y = 0.0f; + normal.z = edge.y; + + // extents adjusted dist + dist = ( normal.x * ( boxPt.x - pTri->m_Points[0].x ) ) + ( normal.z * ( boxPt.z - pTri->m_Points[0].z ) ); + + // find distances from plane (start, end) + distStart = ( normal.x * rayStart.x ) + ( normal.z * rayStart.z ) - dist; + distEnd = ( normal.x * rayEnd.x ) + ( normal.z * rayEnd.z ) - dist; + + if( ( distStart > COLLISION_EPSILON ) && ( distEnd < -COLLISION_EPSILON ) ) + { + t = ( distStart - COLLISION_EPSILON ) / ( distStart - distEnd ); + if( t > *fraction ) + { + VectorScale( rayDir, t, rayPt ); + VectorAdd( rayStart, rayPt, rayStart ); + *fraction = t; + plNormal = normal; + *plDist = dist; + } + } + else if( ( distStart < -COLLISION_EPSILON ) && ( distEnd > COLLISION_EPSILON ) ) + { + t = ( distStart + COLLISION_EPSILON ) / ( distStart - distEnd ); + VectorScale( rayDir, t, rayPt ); + VectorAdd( rayStart, rayPt, rayEnd ); + } + else if( ( distStart > COLLISION_EPSILON ) && ( distEnd > COLLISION_EPSILON ) ) + { + return false; + } + + // cross z-edge + normal.x = -edge.y; + normal.y = edge.x; + normal.z = 0.0f; + + // extents adjusted dist + dist = ( normal.x * ( boxPt.x - pTri->m_Points[0].x ) ) + ( normal.y * ( boxPt.y - pTri->m_Points[0].y ) ); + + // find distances from plane (start, end) + distStart = ( normal.x * rayStart.x ) + ( normal.y * rayStart.y ) - dist; + distEnd = ( normal.x * rayEnd.x ) + ( normal.y * rayEnd.y ) - dist; + + if( ( distStart > COLLISION_EPSILON ) && ( distEnd < -COLLISION_EPSILON ) ) + { + t = ( distStart - COLLISION_EPSILON ) / ( distStart - distEnd ); + if( t > *fraction ) + { + VectorScale( rayDir, t, rayPt ); + VectorAdd( rayStart, rayPt, rayStart ); + *fraction = t; + plNormal = normal; + *plDist = dist; + } + } + else if( ( distStart < -COLLISION_EPSILON ) && ( distEnd > COLLISION_EPSILON ) ) + { + t = ( distStart + COLLISION_EPSILON ) / ( distStart - distEnd ); + VectorScale( rayDir, t, rayPt ); + VectorAdd( rayStart, rayPt, rayEnd ); + } + else if( ( distStart > COLLISION_EPSILON ) && ( distEnd > COLLISION_EPSILON ) ) + { + return false; + } + + // + // test face plane + // + dist = ( pTri->m_Normal.x * ( boxPt.x - pTri->m_Points[0].x ) ) + + ( pTri->m_Normal.y * ( boxPt.y - pTri->m_Points[0].y ) ) + + ( pTri->m_Normal.z * ( boxPt.z - pTri->m_Points[0].z ) ); + + distStart = pTri->m_Normal.Dot( rayStart ) - dist; + distEnd = pTri->m_Normal.Dot( rayEnd ) - dist; + + if( ( distStart > COLLISION_EPSILON ) && ( distEnd < -COLLISION_EPSILON ) ) + { + t = ( distStart - COLLISION_EPSILON ) / ( distStart - distEnd ); + if( t > *fraction ) + { + VectorScale( rayDir, t, rayPt ); + VectorAdd( rayStart, rayPt, rayStart ); + *fraction = t; + plNormal = normal; + *plDist = dist; + } + } + else if( ( distStart < -COLLISION_EPSILON ) && ( distEnd > COLLISION_EPSILON ) ) + { + t = ( distStart + COLLISION_EPSILON ) / ( distStart - distEnd ); + VectorScale( rayDir, t, rayPt ); + VectorAdd( rayStart, rayPt, rayEnd ); + } + else if( ( distStart > COLLISION_EPSILON ) && ( distEnd > COLLISION_EPSILON ) ) + { + return false; + } + + if( *fraction == 1.0f ) + return false; + + return true; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CDispCollTree::AABBTriIntersect( CDispCollTreeTempData *pTemp, CDispCollData *pData ) +{ + bool bResult = false; + + Vector normal; + float fraction, dist; + + // + // sweep ABB against all triangles in list + // + for( int i = 0; i < pTemp->m_TriListCount; i++ ) + { + if( pTemp->m_ppTriList[i]->IsIntersect() ) + { + bResult = SweptAABBTriIntersect( pData->m_StartPos, pData->m_EndPos, pData->m_Extents, + pTemp->m_ppTriList[i], normal, &dist, &fraction ); + if( bResult ) + { + if( fraction < pData->m_Fraction ) + { + pData->m_Fraction = fraction; + pData->m_Normal = normal; + pData->m_Distance = dist; + } + } + } + } + + return bResult; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CDispCollTree::IntersectAABBTriTest( Vector &rayStart, Vector &extents, + CDispCollTri const *pTri ) +{ + int dir, ptIndex; + float dist; + + // + // test axail planes (x, y, z) + // + + for( dir = 0; dir < 3; dir++ ) + { + // + // negative axial plane, component = dir + // + dist = rayStart[dir] - extents[dir]; + for( ptIndex = 0; ptIndex < 3; ptIndex++ ) + { + if( pTri->m_Points[ptIndex][dir] > dist ) + break; + } + + if( ptIndex == 3 ) + return false; + + // + // positive axial plane, component = dir + // + dist = rayStart[dir] + extents[dir]; + for( ptIndex = 0; ptIndex < 3; ptIndex++ ) + { + if( pTri->m_Points[ptIndex][dir] < dist ) + break; + } + + if( ptIndex == 3 ) + return false; + } + + // + // add a test here to see if triangle face normal is close to axial -- done if so!!! + // + if( ( pTri->m_Normal[0] > ONE_MINUS_COLLISION_EPSILON ) || + ( pTri->m_Normal[1] > ONE_MINUS_COLLISION_EPSILON ) || + ( pTri->m_Normal[2] > ONE_MINUS_COLLISION_EPSILON ) ) + return true; + + // find the closest point on the box (use negated tri face noraml) + Vector boxPt( 0.0f, 0.0f, 0.0f ); + for( dir = 0; dir < 3; dir++ ) + { + if( pTri->m_Normal[dir] < 0.0f ) + { + boxPt[dir] = extents[dir]; + } + else + { + boxPt[dir] = -extents[dir]; + } + } + + // + // triangle plane test + // + // do the opposite because the ray has been negated + if( ( ( pTri->m_Normal.x * ( boxPt.x - pTri->m_Points[0].x ) ) + + ( pTri->m_Normal.y * ( boxPt.y - pTri->m_Points[0].y ) ) + + ( pTri->m_Normal.z * ( boxPt.z - pTri->m_Points[0].z ) ) ) > 0.0f ) + return false; + + // + // test edge planes - 9 of them + // + Vector normal; + Vector edge; + + // + // edge 0 + // + edge = pTri->m_Points[1] - pTri->m_Points[0]; + + // cross x + normal.x = 0.0f; + normal.y = -edge.z; + normal.z = edge.y; + if( ( ( normal.y * ( boxPt.y - pTri->m_Points[0].y ) ) + ( normal.z * ( boxPt.z - pTri->m_Points[0].z ) ) ) > 0.0f ) + return false; + + // cross y + normal.x = edge.z; + normal.y = 0.0f; + normal.z = edge.y; + if( ( ( normal.x * ( boxPt.x - pTri->m_Points[0].x ) ) + ( normal.z * ( boxPt.z - pTri->m_Points[0].z ) ) ) > 0.0f ) + return false; + + // cross z + normal.x = -edge.y; + normal.y = edge.x; + normal.z = 0.0f; + if( ( ( normal.x * ( boxPt.x - pTri->m_Points[0].x ) ) + ( normal.y * ( boxPt.y - pTri->m_Points[0].y ) ) ) > 0.0f ) + return false; + + // + // edge 1 + // + edge = pTri->m_Points[2] - pTri->m_Points[1]; + + // cross x + normal.x = 0.0f; + normal.y = -edge.z; + normal.z = edge.y; + if( ( ( normal.y * ( boxPt.y - pTri->m_Points[0].y ) ) + ( normal.z * ( boxPt.z - pTri->m_Points[0].z ) ) ) > 0.0f ) + return false; + + // cross y + normal.x = edge.z; + normal.y = 0.0f; + normal.z = edge.y; + if( ( ( normal.x * ( boxPt.x - pTri->m_Points[0].x ) ) + ( normal.z * ( boxPt.z - pTri->m_Points[0].z ) ) ) > 0.0f ) + return false; + + // cross z + normal.x = -edge.y; + normal.y = edge.x; + normal.z = 0.0f; + if( ( ( normal.x * ( boxPt.x - pTri->m_Points[0].x ) ) + ( normal.y * ( boxPt.y - pTri->m_Points[0].y ) ) ) > 0.0f ) + return false; + + // + // edge 2 + // + edge = pTri->m_Points[0] - pTri->m_Points[2]; + + // cross x + normal.x = 0.0f; + normal.y = -edge.z; + normal.z = edge.y; + if( ( ( normal.y * ( boxPt.y - pTri->m_Points[0].y ) ) + ( normal.z * ( boxPt.z - pTri->m_Points[0].z ) ) ) > 0.0f ) + return false; + + // cross y + normal.x = edge.z; + normal.y = 0.0f; + normal.z = edge.y; + if( ( ( normal.x * ( boxPt.x - pTri->m_Points[0].x ) ) + ( normal.z * ( boxPt.z - pTri->m_Points[0].z ) ) ) > 0.0f ) + return false; + + // cross z + normal.x = -edge.y; + normal.y = edge.x; + normal.z = 0.0f; + if( ( ( normal.x * ( boxPt.x - pTri->m_Points[0].x ) ) + ( normal.y * ( boxPt.y - pTri->m_Points[0].y ) ) ) > 0.0f ) + return false; + + return true; +} + + + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CDispCollTree::SweptAABBTriTest( Vector &rayStart, Vector &rayEnd, Vector &extents, + CDispCollTri const *pTri ) +{ + // get ray direction + Vector rayDir = rayEnd - rayStart; + + // + // quick and dirty test -- test to see if the object is traveling away from triangle surface??? + // + if( pTri->m_Normal.Dot( rayDir ) > 0.0f ) + return false; + + // + // calc the swept triangle face (negate the ray -- opposite direction of box travel) + // + rayDir.Negate(); + + Vector points[3]; + points[0] = pTri->m_Points[0] + rayDir; + points[1] = pTri->m_Points[1] + rayDir; + points[2] = pTri->m_Points[2] + rayDir; + + // + // handle 4 faces tests (3 axial planes and triangle face) + // + int dir; + float dist; + + // + // axial planes tests (x, y, z) + // + for( dir = 0; dir < 3; dir++ ) + { + bool bOutside = true; + + if( rayDir[dir] < 0.0f ) + { + dist = rayStart[dir] - extents[dir]; + for( int ptIndex = 0; ptIndex < 3; ) + { + if( points[ptIndex][dir] > dist ) + { + bOutside = false; + break; + } + } + } + else + { + dist = rayStart[dir] + extents[dir]; + for( int ptIndex = 0; ptIndex < 3; ) + { + if( pTri->m_Points[ptIndex][dir] < dist ) + { + bOutside = false; + break; + } + } + } + + if( bOutside ) + return false; + } + + // + // add a test here to see if triangle face normal is close to axial -- done if so!!! + // + if( ( pTri->m_Normal[0] > ONE_MINUS_COLLISION_EPSILON ) || + ( pTri->m_Normal[1] > ONE_MINUS_COLLISION_EPSILON ) || + ( pTri->m_Normal[2] > ONE_MINUS_COLLISION_EPSILON ) ) + return true; + + // + // handle 9 edge tests - always use the newly swept face for this + // + Vector normal; + Vector edge; + + // find the closest box point - (is written opposite to normal due to negating ray) + Vector boxPt( 0.0f, 0.0f, 0.0f ); + for( dir = 0; dir < 3; dir++ ) + { + if( rayDir[dir] < 0.0f ) + { + boxPt[dir] = rayStart[dir] - extents[dir]; + } + else + { + boxPt[dir] = rayStart[dir] + extents[dir]; + } + } + + // + // edge 0 + // + edge = points[1] - points[0]; + + // cross x-edge + normal.x = 0.0f; + normal.y = -edge.z; + normal.z = edge.y; + if( ( ( normal.y * ( boxPt.y - points[0].y ) ) + ( normal.z * ( boxPt.z - points[0].z ) ) ) > 0.0f ) + return false; + + // cross, y-edge + normal.x = edge.z; + normal.y = 0.0f; + normal.z = edge.y; + if( ( ( normal.x * ( boxPt.x - points[0].x ) ) + ( normal.z * ( boxPt.z - points[0].z ) ) ) > 0.0f ) + return false; + + // cross z-edge + normal.x = -edge.y; + normal.y = edge.x; + normal.z = 0.0f; + if( ( ( normal.x * ( boxPt.x - points[0].x ) ) + ( normal.y * ( boxPt.y - points[0].y ) ) ) > 0.0f ) + return false; + + // + // edge 1 + // + edge = points[2] - points[1]; + + // cross x-edge + normal.x = 0.0f; + normal.y = -edge.z; + normal.z = edge.y; + if( ( ( normal.y * ( boxPt.y - points[0].y ) ) + ( normal.z * ( boxPt.z - points[0].z ) ) ) > 0.0f ) + return false; + + // cross, y-edge + normal.x = edge.z; + normal.y = 0.0f; + normal.z = edge.y; + if( ( ( normal.x * ( boxPt.x - points[0].x ) ) + ( normal.z * ( boxPt.z - points[0].z ) ) ) > 0.0f ) + return false; + + // cross z-edge + normal.x = -edge.y; + normal.y = edge.x; + normal.z = 0.0f; + if( ( ( normal.x * ( boxPt.x - points[0].x ) ) + ( normal.y * ( boxPt.y - points[0].y ) ) ) > 0.0f ) + return false; + + // + // edge 2 + // + edge = points[0] - points[2]; + + // cross x-edge + normal.x = 0.0f; + normal.y = -edge.z; + normal.z = edge.y; + if( ( ( normal.y * ( boxPt.y - points[0].y ) ) + ( normal.z * ( boxPt.z - points[0].z ) ) ) > 0.0f ) + return false; + + // cross, y-edge + normal.x = edge.z; + normal.y = 0.0f; + normal.z = edge.y; + if( ( ( normal.x * ( boxPt.x - points[0].x ) ) + ( normal.z * ( boxPt.z - points[0].z ) ) ) > 0.0f ) + return false; + + // cross z-edge + normal.x = -edge.y; + normal.y = edge.x; + normal.z = 0.0f; + if( ( ( normal.x * ( boxPt.x - points[0].x ) ) + ( normal.y * ( boxPt.y - points[0].y ) ) ) > 0.0f ) + return false; + + // + // triangle plane test + // + // do the opposite because the ray has been negated + if( ( ( pTri->m_Normal.x * ( boxPt.x - points[0].x ) ) + + ( pTri->m_Normal.y * ( boxPt.y - points[0].y ) ) + + ( pTri->m_Normal.z * ( boxPt.z - points[0].z ) ) ) > 0.0f ) + return false; + + return true; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CDispCollTree::CullTriList( CDispCollTreeTempData *pTemp, Vector &rayStart, Vector &rayEnd, Vector &extents, bool bIntersect ) +{ + // + // intersect AABB with all triangles in list + // + if( bIntersect ) + { + for( int i = 0; i < pTemp->m_TriListCount; i++ ) + { + if( IntersectAABBTriTest( rayStart, extents, pTemp->m_ppTriList[i] ) ) + return true; + } + + return false; + } + // + // sweep AABB against all triangles in list + // + else + { + bool bResult = false; + + for( int i = 0; i < pTemp->m_TriListCount; i++ ) + { + if( SweptAABBTriTest( rayStart, rayEnd, extents, pTemp->m_ppTriList[i] ) ) + { + pTemp->m_ppTriList[i]->SetIntersect( true ); + bResult = true; + } + } + + return bResult; + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CDispCollTree::IntersectAABBAABBTest( CDispCollTreeTempData *pTemp, const Vector &pos, const Vector &extents ) +{ + float dist; + + for( int dir = 0; dir < 3; dir++ ) + { + // negative direction + dist = -( pos[dir] - ( pTemp->m_AABBDistances[(dir>>1)] - extents[dir] ) ); + if( dist > COLLISION_EPSILON ) + return false; + + // positive direction + dist = pos[dir] - ( pTemp->m_AABBDistances[(dir>>1)+1] + extents[dir] ); + if( dist > COLLISION_EPSILON ) + return false; + } + + return true; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CDispCollTree::SweptAABBAABBTest( CDispCollTreeTempData *pTemp, const Vector &rayStart, const Vector &rayEnd, const Vector &extents ) +{ + int dir; + float distStart, distEnd; + float fraction; + float deltas[3]; + float scalers[3]; + + // + // enter and exit fractions + // + float enterFraction = 0.0f; + float exitFraction = 0.0f; + + // + // de-normalize the paramter space so that we don't have to divide + // to find the fractional amount later (clamped for precision) + // + deltas[0] = rayEnd.x - rayStart.x; + deltas[1] = rayEnd.y - rayStart.y; + deltas[2] = rayEnd.z - rayStart.z; + if( ( deltas[0] < COLLISION_EPSILON ) && ( deltas[0] > -COLLISION_EPSILON ) ) { deltas[0] = 1.0f; } + if( ( deltas[1] < COLLISION_EPSILON ) && ( deltas[1] > -COLLISION_EPSILON ) ) { deltas[0] = 1.0f; } + if( ( deltas[2] < COLLISION_EPSILON ) && ( deltas[2] > -COLLISION_EPSILON ) ) { deltas[0] = 1.0f; } + scalers[0] = deltas[1] * deltas[2]; + scalers[1] = deltas[0] * deltas[2]; + scalers[2] = deltas[0] * deltas[1]; + + for( dir = 0; dir < 3; dir++ ) + { + // + // negative direction + // + distStart = -( rayStart[dir] - ( pTemp->m_AABBDistances[(dir>>1)] - extents[dir] ) ); + distEnd = -( rayEnd[dir] - ( pTemp->m_AABBDistances[(dir>>1)] - extents[dir] ) ); + + if( ( distStart > COLLISION_EPSILON ) && ( distEnd < -COLLISION_EPSILON ) ) + { + fraction = distStart * scalers[dir]; + if( fraction > enterFraction ) + { + enterFraction = fraction; + } + } + else if( ( distStart < -COLLISION_EPSILON ) && ( distEnd > COLLISION_EPSILON ) ) + { + fraction = distStart * scalers[dir]; + if( fraction < exitFraction ) + { + exitFraction = fraction; + } + } + else if( ( distStart > COLLISION_EPSILON ) && ( distEnd > COLLISION_EPSILON ) ) + { + return false; + } + + // + // positive direction + // + distStart = rayStart[dir] - ( pTemp->m_AABBDistances[(dir>>1)+1] + extents[dir] ); + distEnd = rayEnd[dir] - ( pTemp->m_AABBDistances[(dir>>1)+1] + extents[dir] ); + + if( ( distStart > COLLISION_EPSILON ) && ( distEnd < -COLLISION_EPSILON ) ) + { + fraction = distStart * scalers[dir]; + if( fraction > enterFraction ) + { + enterFraction = fraction; + } + } + else if( ( distStart < -COLLISION_EPSILON ) && ( distEnd > COLLISION_EPSILON ) ) + { + fraction = distStart * scalers[dir]; + if( fraction < exitFraction ) + { + exitFraction = fraction; + } + } + else if( ( distStart > COLLISION_EPSILON ) && ( distEnd > COLLISION_EPSILON ) ) + { + return false; + } + } + + if( exitFraction < enterFraction ) + return false; + + return true; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CDispCollTree::BuildTriList_r( CDispCollTreeTempData *pTemp, int nodeIndex, Vector &rayStart, Vector &rayEnd, Vector &extents, + bool bIntersect ) +{ + // + // get the current nodes bounds and create collision test planes + // (saved in the in class cache m_AABBNormals, m_AABBDistances) + // + Vector bounds[2]; + CDispCollNode *pNode = &m_pNodes[nodeIndex]; + pNode->GetBounds( bounds[0], bounds[1] ); + CreatePlanesFromBounds( pTemp, bounds[0], bounds[1] ); + + // + // interesect/sweep test + // + bool bResult; + if( bIntersect ) + { + bResult = IntersectAABBAABBTest( pTemp, rayStart, extents ); + } + else + { + bResult = SweptAABBAABBTest( pTemp, rayStart, rayEnd, extents ); + } + + if( bResult ) + { + // if leaf node -- add triangles to interstection test list + if( pNode->IsLeaf() ) + { + // Assert for now -- flush cache later!!!!! + Assert( pTemp->m_TriListCount >= 0 ); + Assert( pTemp->m_TriListCount < TRILIST_CACHE_SIZE ); + + pTemp->m_ppTriList[pTemp->m_TriListCount] = &pNode->m_Tris[0]; + pTemp->m_ppTriList[pTemp->m_TriListCount+1] = &pNode->m_Tris[1]; + pTemp->m_TriListCount += 2; + } + // continue recursion + else + { + BuildTriList_r( pTemp, GetChildNode( nodeIndex, 0 ), rayStart, rayEnd, extents, bIntersect ); + BuildTriList_r( pTemp, GetChildNode( nodeIndex, 1 ), rayStart, rayEnd, extents, bIntersect ); + BuildTriList_r( pTemp, GetChildNode( nodeIndex, 2 ), rayStart, rayEnd, extents, bIntersect ); + BuildTriList_r( pTemp, GetChildNode( nodeIndex, 3 ), rayStart, rayEnd, extents, bIntersect ); + } + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CDispCollTree::AABBSweep( CDispCollData *pData ) +{ + // reset the triangle lists counts + CDispCollTreeTempData tmp; + tmp.m_TriListCount = 0; + + // sweep the AABB against the tree + BuildTriList_r( &tmp, 0, pData->m_StartPos, pData->m_EndPos, pData->m_Extents, false ); + + // find collision triangles + if( CullTriList( &tmp, pData->m_StartPos, pData->m_EndPos, pData->m_Extents, false ) ) + { + // find closest intersection + return AABBTriIntersect( &tmp, pData ); + } + + return false; +} + + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CDispCollTree::AABBIntersect( CDispCollData *pData ) +{ + // reset the triangle lists counts + CDispCollTreeTempData tmp; + tmp.m_TriListCount = 0; + + // sweep the AABB against the tree + BuildTriList_r( &tmp, 0, pData->m_StartPos, pData->m_StartPos, pData->m_Extents, true ); + + // find collision triangles + return CullTriList( &tmp, pData->m_StartPos, pData->m_StartPos, pData->m_Extents, true ); +} diff --git a/public/dispcoll.h b/public/dispcoll.h index 645a60c7..95ca4887 100644 --- a/public/dispcoll.h +++ b/public/dispcoll.h @@ -1,249 +1,249 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -//=============================================================================// - -#ifndef DISPCOLL_H -#define DISPCOLL_H -#pragma once - -#include "Vector.h" - -class CCoreDispInfo; - -//============================================================================= -// -// Displacement Collision Triangle Data -// -class CDispCollTri -{ -public: - - void Init( void ); - inline void SetPoint( int index, Vector const& vert ); - inline void SetPointNormal( int index, Vector const& normal ); - void CalcPlane( void ); - - inline void SetIntersect( bool bIntersect ); - inline bool IsIntersect( void ); - - Vector m_Points[3]; // polygon points - Vector m_PointNormals[3]; // polygon point normals - Vector m_Normal; // plane normal - float m_Distance; // plane distance - short m_ProjAxes[2]; // projection axes (2 minor axes) - bool m_bIntersect; // intersected triangle??? -}; - -//============================================================================= -// -// Displacement Collision Node Data -// -class CDispCollNode -{ -public: - - CDispCollNode(); - inline bool IsLeaf( void ); - inline void SetBounds( Vector const &bMin, Vector const &bMax ); - inline void GetBounds( Vector &bMin, Vector &bMax ); - - Vector m_Bounds[2]; // node minimum and maximum - - bool m_bIsLeaf; // is the node a leaf? ( may have to make this an int for alignment!) - CDispCollTri m_Tris[2]; // two triangles contained in leaf node -}; - - -//============================================================================= -// -// Displacement Collision Data -// -class CDispCollData -{ -public: - - Vector m_StartPos; - Vector m_EndPos; - Vector m_Extents; - float m_Fraction; - int m_Contents; - Vector m_Normal; - float m_Distance; - bool m_bOcclude; -}; - - - -// HACKHACK: JAY: Moved this out of CDispCollTree to be thread safe in vrad -enum { TRILIST_CACHE_SIZE = 128 }; - -class CDispCollTreeTempData -{ -public: - // - // temps - // - int m_TriListCount; - CDispCollTri *m_ppTriList[TRILIST_CACHE_SIZE]; - - // collision tree node cache - float m_AABBDistances[6]; -}; - - -//============================================================================= -// -// Displacement Collision Tree -// -class CDispCollTree -{ -public: - - static const float COLLISION_EPSILON; - static const float ONE_MINUS_COLLISION_EPSILON; - - //========================================================================= - // - // Creation/Destruction - // - CDispCollTree(); - ~CDispCollTree(); - - virtual bool Create( CCoreDispInfo *pDisp ); - - //========================================================================= - // - // Collision Functions - // - bool RayTest( CDispCollData *pData ); - bool RayTestAllTris( CDispCollData *pData, int power ); - - bool AABBIntersect( CDispCollData *pData ); - bool AABBSweep( CDispCollData *pData ); - - //========================================================================= - // - // Attrib Functions - // - inline void SetPower( int power ); - inline int GetPower( void ); - - inline void SetCheckCount( int count ); - inline int GetCheckCount( void ); - - inline void GetBounds( Vector& boundMin, Vector& boundMax ); - -protected: - - int m_Power; - - int m_NodeCount; - CDispCollNode *m_pNodes; - - int m_CheckCount; - - // collision tree node cache - Vector m_AABBNormals[6]; - //========================================================================= - // - // Creation/Destruction - // - void InitAABBData( void ); - void InitLeaves( CCoreDispInfo *pDisp ); - void CreateNodes( CCoreDispInfo *pDisp ); - void CreateNodes_r( CCoreDispInfo *pDisp, int nodeIndex, int termLevel ); - void CalcBounds( CDispCollNode *pNode, int nodeIndex ); - - //========================================================================= - // - // Collision Functions - // - void CreatePlanesFromBounds( CDispCollTreeTempData *pTemp, Vector const &bbMin, Vector const &bbMax ); - -// void RayNodeTest_r( int nodeIndex, Vector &rayStart, Vector &rayEnd ); - void RayNodeTest_r( CDispCollTreeTempData *pTemp, int nodeIndex, Vector rayStart, Vector rayEnd ); - bool RayAABBTest( CDispCollTreeTempData *pTemp, Vector &rayStart, Vector &rayEnd ); - bool RayTriListTest( CDispCollTreeTempData *pTemp, CDispCollData *pData ); - bool RayTriTest( Vector const &rayStart, Vector const &rayDir, float const rayLength, CDispCollTri const *pTri, float *fraction ); - - void BuildTriList_r( CDispCollTreeTempData *pTemp, int nodeIndex, Vector &rayStart, Vector &rayEnd, Vector &extents, bool bIntersect ); - bool IntersectAABBAABBTest( CDispCollTreeTempData *pTemp, const Vector &pos, const Vector &extents ); - bool SweptAABBAABBTest( CDispCollTreeTempData *pTemp, const Vector &rayStart, const Vector &rayEnd, const Vector &extents ); - - bool CullTriList( CDispCollTreeTempData *pTemp, Vector &rayStart, Vector &rayEnd, Vector &extents, bool bIntersect ); - bool SweptAABBTriTest( Vector &rayStart, Vector &rayEnd, Vector &extents, CDispCollTri const *pTri ); - bool AABBTriIntersect( CDispCollTreeTempData *pTemp, CDispCollData *pData ); - bool IntersectAABBTriTest( Vector &rayStart, Vector &extents, CDispCollTri const *pTri ); - bool SweptAABBTriIntersect( Vector &rayStart, Vector &rayEnd, Vector &extents, - CDispCollTri const *pTri, Vector &plNormal, float *plDist, - float *fraction ); - - //========================================================================= - // - // Memory Functions - // - bool AllocNodes( int nodeCount ); - void FreeNodes( void ); - - //========================================================================= - // - // Utility Functions - // - inline int CalcNodeCount( int power ); - inline int GetParentNode( int nodeIndex ); - inline int GetChildNode( int nodeIndex, int direction ); - inline int GetNodeLevel( int nodeIndex ); - int GetNodeIndexFromComponents( int x, int y ); -}; - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -inline void CDispCollTree::SetPower( int power ) -{ - m_Power = power; -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -inline int CDispCollTree::GetPower( void ) -{ - return m_Power; -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -inline void CDispCollTree::SetCheckCount( int count ) -{ - m_CheckCount = count; -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -inline int CDispCollTree::GetCheckCount( void ) -{ - return m_CheckCount; -} - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -inline void CDispCollTree::GetBounds( Vector& boundMin, Vector& boundMax ) -{ - boundMin[0] = m_pNodes[0].m_Bounds[0].x; - boundMin[1] = m_pNodes[0].m_Bounds[0].y; - boundMin[2] = m_pNodes[0].m_Bounds[0].z; - - boundMax[0] = m_pNodes[0].m_Bounds[1].x; - boundMax[1] = m_pNodes[0].m_Bounds[1].y; - boundMax[2] = m_pNodes[0].m_Bounds[1].z; -} - - -#endif // DISPCOLL_H \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef DISPCOLL_H +#define DISPCOLL_H +#pragma once + +#include "vector.h" + +class CCoreDispInfo; + +//============================================================================= +// +// Displacement Collision Triangle Data +// +class CDispCollTri +{ +public: + + void Init( void ); + inline void SetPoint( int index, Vector const& vert ); + inline void SetPointNormal( int index, Vector const& normal ); + void CalcPlane( void ); + + inline void SetIntersect( bool bIntersect ); + inline bool IsIntersect( void ); + + Vector m_Points[3]; // polygon points + Vector m_PointNormals[3]; // polygon point normals + Vector m_Normal; // plane normal + float m_Distance; // plane distance + short m_ProjAxes[2]; // projection axes (2 minor axes) + bool m_bIntersect; // intersected triangle??? +}; + +//============================================================================= +// +// Displacement Collision Node Data +// +class CDispCollNode +{ +public: + + CDispCollNode(); + inline bool IsLeaf( void ); + inline void SetBounds( Vector const &bMin, Vector const &bMax ); + inline void GetBounds( Vector &bMin, Vector &bMax ); + + Vector m_Bounds[2]; // node minimum and maximum + + bool m_bIsLeaf; // is the node a leaf? ( may have to make this an int for alignment!) + CDispCollTri m_Tris[2]; // two triangles contained in leaf node +}; + + +//============================================================================= +// +// Displacement Collision Data +// +class CDispCollData +{ +public: + + Vector m_StartPos; + Vector m_EndPos; + Vector m_Extents; + float m_Fraction; + int m_Contents; + Vector m_Normal; + float m_Distance; + bool m_bOcclude; +}; + + + +// HACKHACK: JAY: Moved this out of CDispCollTree to be thread safe in vrad +enum { TRILIST_CACHE_SIZE = 128 }; + +class CDispCollTreeTempData +{ +public: + // + // temps + // + int m_TriListCount; + CDispCollTri *m_ppTriList[TRILIST_CACHE_SIZE]; + + // collision tree node cache + float m_AABBDistances[6]; +}; + + +//============================================================================= +// +// Displacement Collision Tree +// +class CDispCollTree +{ +public: + + static const float COLLISION_EPSILON; + static const float ONE_MINUS_COLLISION_EPSILON; + + //========================================================================= + // + // Creation/Destruction + // + CDispCollTree(); + ~CDispCollTree(); + + virtual bool Create( CCoreDispInfo *pDisp ); + + //========================================================================= + // + // Collision Functions + // + bool RayTest( CDispCollData *pData ); + bool RayTestAllTris( CDispCollData *pData, int power ); + + bool AABBIntersect( CDispCollData *pData ); + bool AABBSweep( CDispCollData *pData ); + + //========================================================================= + // + // Attrib Functions + // + inline void SetPower( int power ); + inline int GetPower( void ); + + inline void SetCheckCount( int count ); + inline int GetCheckCount( void ); + + inline void GetBounds( Vector& boundMin, Vector& boundMax ); + +protected: + + int m_Power; + + int m_NodeCount; + CDispCollNode *m_pNodes; + + int m_CheckCount; + + // collision tree node cache + Vector m_AABBNormals[6]; + //========================================================================= + // + // Creation/Destruction + // + void InitAABBData( void ); + void InitLeaves( CCoreDispInfo *pDisp ); + void CreateNodes( CCoreDispInfo *pDisp ); + void CreateNodes_r( CCoreDispInfo *pDisp, int nodeIndex, int termLevel ); + void CalcBounds( CDispCollNode *pNode, int nodeIndex ); + + //========================================================================= + // + // Collision Functions + // + void CreatePlanesFromBounds( CDispCollTreeTempData *pTemp, Vector const &bbMin, Vector const &bbMax ); + +// void RayNodeTest_r( int nodeIndex, Vector &rayStart, Vector &rayEnd ); + void RayNodeTest_r( CDispCollTreeTempData *pTemp, int nodeIndex, Vector rayStart, Vector rayEnd ); + bool RayAABBTest( CDispCollTreeTempData *pTemp, Vector &rayStart, Vector &rayEnd ); + bool RayTriListTest( CDispCollTreeTempData *pTemp, CDispCollData *pData ); + bool RayTriTest( Vector const &rayStart, Vector const &rayDir, float const rayLength, CDispCollTri const *pTri, float *fraction ); + + void BuildTriList_r( CDispCollTreeTempData *pTemp, int nodeIndex, Vector &rayStart, Vector &rayEnd, Vector &extents, bool bIntersect ); + bool IntersectAABBAABBTest( CDispCollTreeTempData *pTemp, const Vector &pos, const Vector &extents ); + bool SweptAABBAABBTest( CDispCollTreeTempData *pTemp, const Vector &rayStart, const Vector &rayEnd, const Vector &extents ); + + bool CullTriList( CDispCollTreeTempData *pTemp, Vector &rayStart, Vector &rayEnd, Vector &extents, bool bIntersect ); + bool SweptAABBTriTest( Vector &rayStart, Vector &rayEnd, Vector &extents, CDispCollTri const *pTri ); + bool AABBTriIntersect( CDispCollTreeTempData *pTemp, CDispCollData *pData ); + bool IntersectAABBTriTest( Vector &rayStart, Vector &extents, CDispCollTri const *pTri ); + bool SweptAABBTriIntersect( Vector &rayStart, Vector &rayEnd, Vector &extents, + CDispCollTri const *pTri, Vector &plNormal, float *plDist, + float *fraction ); + + //========================================================================= + // + // Memory Functions + // + bool AllocNodes( int nodeCount ); + void FreeNodes( void ); + + //========================================================================= + // + // Utility Functions + // + inline int CalcNodeCount( int power ); + inline int GetParentNode( int nodeIndex ); + inline int GetChildNode( int nodeIndex, int direction ); + inline int GetNodeLevel( int nodeIndex ); + int GetNodeIndexFromComponents( int x, int y ); +}; + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CDispCollTree::SetPower( int power ) +{ + m_Power = power; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline int CDispCollTree::GetPower( void ) +{ + return m_Power; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CDispCollTree::SetCheckCount( int count ) +{ + m_CheckCount = count; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline int CDispCollTree::GetCheckCount( void ) +{ + return m_CheckCount; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CDispCollTree::GetBounds( Vector& boundMin, Vector& boundMax ) +{ + boundMin[0] = m_pNodes[0].m_Bounds[0].x; + boundMin[1] = m_pNodes[0].m_Bounds[0].y; + boundMin[2] = m_pNodes[0].m_Bounds[0].z; + + boundMax[0] = m_pNodes[0].m_Bounds[1].x; + boundMax[1] = m_pNodes[0].m_Bounds[1].y; + boundMax[2] = m_pNodes[0].m_Bounds[1].z; +} + + +#endif // DISPCOLL_H diff --git a/public/dlight.h b/public/dlight.h index 912442ea..4c1dae48 100644 --- a/public/dlight.h +++ b/public/dlight.h @@ -1,84 +1,84 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -// -//=============================================================================// -#if !defined ( DLIGHTH ) -#define DLIGHTH -#ifdef _WIN32 -#pragma once -#endif - -#include "vector.h" - -//----------------------------------------------------------------------------- -// Dynamic light structure -//----------------------------------------------------------------------------- - -enum -{ - DLIGHT_NO_WORLD_ILLUMINATION = 0x1, - DLIGHT_NO_MODEL_ILLUMINATION = 0x2, - - // NOTE: These two features are used to dynamically tweak the alpha on displacements - // which is a special effect for selecting which texture to use. If - // we ever change how alpha is stored for displacements, we'll have to kill this feature - DLIGHT_ADD_DISPLACEMENT_ALPHA = 0x4, - DLIGHT_SUBTRACT_DISPLACEMENT_ALPHA = 0x8, - DLIGHT_DISPLACEMENT_MASK = (DLIGHT_ADD_DISPLACEMENT_ALPHA | DLIGHT_SUBTRACT_DISPLACEMENT_ALPHA), -}; - -// This is the lighting value that is used to determine when something can be -// culle from lighting because it is close enough to black to be virtually black. -//#define MIN_LIGHTING_VALUE (1.0f/256.0f) - -// This is the broken value of MIN_LIGHTING_VALUE that we have to take into consideration -// to make sure that the lighting for dlights look the same as they did in HL2. -// We'll use the real MIN_LIGHTING_VALUE above to calculate larger radii for dynamic -// light sources. -//#define HL2_BROKEN_MIN_LIGHTING_VALUE (20.0f/256.0f) - -struct dlight_t -{ - int flags; - Vector origin; - float radius; - ColorRGBExp32 color; // Light color with exponent - float die; // stop lighting after this time - float decay; // drop this each second - float minlight; // don't add when contributing less - int key; - int style; // lightstyle - - // For spotlights. Use m_OuterAngle == 0 for point lights - Vector m_Direction; // center of the light cone - float m_InnerAngle; - float m_OuterAngle; - - // see comments above about HL2_BROKEN_MIN_LIGHTING_VALUE and MIN_LIGHTING_VALUE - // THIS SHOULD ONLY GET CALLED FROM THE ENGINE - float GetRadius() const - { -// return FastSqrt( radius * radius * ( HL2_BROKEN_MIN_LIGHTING_VALUE / MIN_LIGHTING_VALUE ) ); - return radius; - } - - // see comments above about HL2_BROKEN_MIN_LIGHTING_VALUE and MIN_LIGHTING_VALUE - // THIS SHOULD ONLY GET CALLED FROM THE ENGINE - float GetRadiusSquared() const - { -// return radius * radius * ( HL2_BROKEN_MIN_LIGHTING_VALUE / MIN_LIGHTING_VALUE ); - return radius * radius; - } - - // THIS SHOULD ONLY GET CALLED FROM THE ENGINE - float IsRadiusGreaterThanZero() const - { - // don't bother calculating the new radius if you just want to know if it is greater than zero. - return radius > 0.0f; - } -}; - -#endif \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// +#if !defined ( DLIGHTH ) +#define DLIGHTH +#ifdef _WIN32 +#pragma once +#endif + +#include "vector.h" + +//----------------------------------------------------------------------------- +// Dynamic light structure +//----------------------------------------------------------------------------- + +enum +{ + DLIGHT_NO_WORLD_ILLUMINATION = 0x1, + DLIGHT_NO_MODEL_ILLUMINATION = 0x2, + + // NOTE: These two features are used to dynamically tweak the alpha on displacements + // which is a special effect for selecting which texture to use. If + // we ever change how alpha is stored for displacements, we'll have to kill this feature + DLIGHT_ADD_DISPLACEMENT_ALPHA = 0x4, + DLIGHT_SUBTRACT_DISPLACEMENT_ALPHA = 0x8, + DLIGHT_DISPLACEMENT_MASK = (DLIGHT_ADD_DISPLACEMENT_ALPHA | DLIGHT_SUBTRACT_DISPLACEMENT_ALPHA), +}; + +// This is the lighting value that is used to determine when something can be +// culle from lighting because it is close enough to black to be virtually black. +//#define MIN_LIGHTING_VALUE (1.0f/256.0f) + +// This is the broken value of MIN_LIGHTING_VALUE that we have to take into consideration +// to make sure that the lighting for dlights look the same as they did in HL2. +// We'll use the real MIN_LIGHTING_VALUE above to calculate larger radii for dynamic +// light sources. +//#define HL2_BROKEN_MIN_LIGHTING_VALUE (20.0f/256.0f) + +struct dlight_t +{ + int flags; + Vector origin; + float radius; + ColorRGBExp32 color; // Light color with exponent + float die; // stop lighting after this time + float decay; // drop this each second + float minlight; // don't add when contributing less + int key; + int style; // lightstyle + + // For spotlights. Use m_OuterAngle == 0 for point lights + Vector m_Direction; // center of the light cone + float m_InnerAngle; + float m_OuterAngle; + + // see comments above about HL2_BROKEN_MIN_LIGHTING_VALUE and MIN_LIGHTING_VALUE + // THIS SHOULD ONLY GET CALLED FROM THE ENGINE + float GetRadius() const + { +// return FastSqrt( radius * radius * ( HL2_BROKEN_MIN_LIGHTING_VALUE / MIN_LIGHTING_VALUE ) ); + return radius; + } + + // see comments above about HL2_BROKEN_MIN_LIGHTING_VALUE and MIN_LIGHTING_VALUE + // THIS SHOULD ONLY GET CALLED FROM THE ENGINE + float GetRadiusSquared() const + { +// return radius * radius * ( HL2_BROKEN_MIN_LIGHTING_VALUE / MIN_LIGHTING_VALUE ); + return radius * radius; + } + + // THIS SHOULD ONLY GET CALLED FROM THE ENGINE + float IsRadiusGreaterThanZero() const + { + // don't bother calculating the new radius if you just want to know if it is greater than zero. + return radius > 0.0f; + } +}; + +#endif diff --git a/public/dt_recv.cpp b/public/dt_recv.cpp index 61147148..247670c5 100644 --- a/public/dt_recv.cpp +++ b/public/dt_recv.cpp @@ -1,405 +1,406 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -// -//=============================================================================// - - -#include "dt_recv.h" -#include "vector.h" -#include "vstdlib/strtools.h" -#include "dt_utlvector_common.h" - -// memdbgon must be the last include file in a .cpp file!!! -#include "tier0/memdbgon.h" - -#if !defined(_STATIC_LINKED) || defined(CLIENT_DLL) - -char *s_ClientElementNames[MAX_ARRAY_ELEMENTS] = -{ - "000", "001", "002", "003", "004", "005", "006", "007", "008", "009", - "010", "011", "012", "013", "014", "015", "016", "017", "018", "019", - "020", "021", "022", "023", "024", "025", "026", "027", "028", "029", - "030", "031", "032", "033", "034", "035", "036", "037", "038", "039", - "040", "041", "042", "043", "044", "045", "046", "047", "048", "049", - "050", "051", "052", "053", "054", "055", "056", "057", "058", "059", - "060", "061", "062", "063", "064", "065", "066", "067", "068", "069", - "070", "071", "072", "073", "074", "075", "076", "077", "078", "079", - "080", "081", "082", "083", "084", "085", "086", "087", "088", "089", - "090", "091", "092", "093", "094", "095", "096", "097", "098", "099", - "100", "101", "102", "103", "104", "105", "106", "107", "108", "109", - "110", "111", "112", "113", "114", "115", "116", "117", "118", "119", - "120", "121", "122", "123", "124", "125", "126", "127", "128", "129", - "130", "131", "132", "133", "134", "135", "136", "137", "138", "139", - "140", "141", "142", "143", "144", "145", "146", "147", "148", "149", - "150", "151", "152", "153", "154", "155", "156", "157", "158", "159", - "160", "161", "162", "163", "164", "165", "166", "167", "168", "169", - "170", "171", "172", "173", "174", "175", "176", "177", "178", "179", - "180", "181", "182", "183", "184", "185", "186", "187", "188", "189", - "190", "191", "192", "193", "194", "195", "196", "197", "198", "199", - "200", "201", "202", "203", "204", "205", "206", "207", "208", "209", - "210", "211", "212", "213", "214", "215", "216", "217", "218", "219", - "220", "221", "222", "223", "224", "225", "226", "227", "228", "229", - "230", "231", "232", "233", "234", "235", "236", "237", "238", "239", - "240", "241", "242", "243", "244", "245", "246", "247", "248", "249", - "250", "251", "252", "253", "254", "255" -}; - -CStandardRecvProxies::CStandardRecvProxies() -{ - m_Int32ToInt8 = RecvProxy_Int32ToInt8; - m_Int32ToInt16 = RecvProxy_Int32ToInt16; - m_Int32ToInt32 = RecvProxy_Int32ToInt32; - m_FloatToFloat = RecvProxy_FloatToFloat; - m_VectorToVector = RecvProxy_VectorToVector; -} - -CStandardRecvProxies g_StandardRecvProxies; - - -// ---------------------------------------------------------------------- // -// RecvProp. -// ---------------------------------------------------------------------- // -RecvProp::RecvProp() -{ - m_pExtraData = NULL; - m_pVarName = NULL; - m_Offset = 0; - m_RecvType = DPT_Int; - m_Flags = 0; - m_ProxyFn = NULL; - m_DataTableProxyFn = NULL; - m_pDataTable = NULL; - m_nElements = 1; - m_ElementStride = -1; - m_pArrayProp = NULL; - m_ArrayLengthProxy = NULL; - m_bInsideArray = false; -} - -// ---------------------------------------------------------------------- // -// RecvTable. -// ---------------------------------------------------------------------- // -RecvTable::RecvTable() -{ - Construct( NULL, 0, NULL ); -} - -RecvTable::RecvTable(RecvProp *pProps, int nProps, char *pNetTableName) -{ - Construct( pProps, nProps, pNetTableName ); -} - -RecvTable::~RecvTable() -{ -} - -void RecvTable::Construct( RecvProp *pProps, int nProps, char *pNetTableName ) -{ - m_pProps = pProps; - m_nProps = nProps; - m_pDecoder = NULL; - m_pNetTableName = pNetTableName; - m_bInitialized = false; - m_bInMainList = false; -} - - -// ---------------------------------------------------------------------- // -// Prop setup functions (for building tables). -// ---------------------------------------------------------------------- // - -RecvProp RecvPropFloat( - char *pVarName, - int offset, - int sizeofVar, - int flags, - RecvVarProxyFn varProxy - ) -{ - RecvProp ret; - - // Debug type checks. - if ( varProxy == RecvProxy_FloatToFloat ) - { - Assert( sizeofVar == 0 || sizeofVar == 4 ); - } - - ret.m_pVarName = pVarName; - ret.SetOffset( offset ); - ret.m_RecvType = DPT_Float; - ret.m_Flags = flags; - ret.SetProxyFn( varProxy ); - - return ret; -} - -RecvProp RecvPropVector( - char *pVarName, - int offset, - int sizeofVar, - int flags, - RecvVarProxyFn varProxy - ) -{ - RecvProp ret; - - // Debug type checks. - #ifdef _DEBUG - if(varProxy == RecvProxy_VectorToVector) - { - Assert(sizeofVar == sizeof(Vector)); - } - #endif - - ret.m_pVarName = pVarName; - ret.SetOffset( offset ); - ret.m_RecvType = DPT_Vector; - ret.m_Flags = flags; - ret.SetProxyFn( varProxy ); - - return ret; -} - -#if 0 // We can't ship this since it changes the size of DTVariant to be 20 bytes instead of 16 and that breaks MODs!!! - -RecvProp RecvPropQuaternion( - char *pVarName, - int offset, - int sizeofVar, // Handled by RECVINFO macro, but set to SIZEOF_IGNORE if you don't want to bother. - int flags, - RecvVarProxyFn varProxy - ) -{ - RecvProp ret; - - // Debug type checks. - #ifdef _DEBUG - if(varProxy == RecvProxy_QuaternionToQuaternion) - { - Assert(sizeofVar == sizeof(Quaternion)); - } - #endif - - ret.m_pVarName = pVarName; - ret.SetOffset( offset ); - ret.m_RecvType = DPT_Quaternion; - ret.m_Flags = flags; - ret.SetProxyFn( varProxy ); - - return ret; -} -#endif - -RecvProp RecvPropInt( - char *pVarName, - int offset, - int sizeofVar, - int flags, - RecvVarProxyFn varProxy - ) -{ - RecvProp ret; - - // If they didn't specify a proxy, then figure out what type we're writing to. - if(varProxy == NULL) - { - if(sizeofVar == 1) - { - varProxy = RecvProxy_Int32ToInt8; - } - else if(sizeofVar == 2) - { - varProxy = RecvProxy_Int32ToInt16; - } - else if(sizeofVar == 4) - { - varProxy = RecvProxy_Int32ToInt32; - } - else - { - Assert(!"RecvPropInt var has invalid size"); - varProxy = RecvProxy_Int32ToInt8; // safest one... - } - } - - ret.m_pVarName = pVarName; - ret.SetOffset( offset ); - ret.m_RecvType = DPT_Int; - ret.m_Flags = flags; - ret.SetProxyFn( varProxy ); - - return ret; -} - -RecvProp RecvPropString( - char *pVarName, - int offset, - int bufferSize, - int flags, - RecvVarProxyFn varProxy - ) -{ - RecvProp ret; - - ret.m_pVarName = pVarName; - ret.SetOffset( offset ); - ret.m_RecvType = DPT_String; - ret.m_Flags = flags; - ret.m_StringBufferSize = bufferSize; - ret.SetProxyFn( varProxy ); - - return ret; -} - -RecvProp RecvPropDataTable( - char *pVarName, - int offset, - int flags, - RecvTable *pTable, - DataTableRecvVarProxyFn varProxy - ) -{ - RecvProp ret; - - ret.m_pVarName = pVarName; - ret.SetOffset( offset ); - ret.m_RecvType = DPT_DataTable; - ret.m_Flags = flags; - ret.SetDataTableProxyFn( varProxy ); - ret.SetDataTable( pTable ); - - return ret; -} - -RecvProp RecvPropArray3( - char *pVarName, - int offset, - int sizeofVar, - int elements, - RecvProp pArrayProp, - DataTableRecvVarProxyFn varProxy - ) -{ - RecvProp ret; - - Assert( elements <= MAX_ARRAY_ELEMENTS ); - - ret.m_pVarName = pVarName; - ret.SetOffset( offset ); - ret.m_RecvType = DPT_DataTable; - ret.SetDataTableProxyFn( varProxy ); - - RecvProp *pProps = new RecvProp[elements]; // TODO free that again - - const char *pParentArrayPropName = AllocateStringHelper( "%s", pVarName ); - - for ( int i=0; i < elements; i++ ) - { - pProps[i] = pArrayProp; // copy basic property settings - pProps[i].SetOffset( i * sizeofVar ); // adjust offset - pProps[i].m_pVarName = s_ClientElementNames[i]; // give unique name - pProps[i].SetParentArrayPropName( pParentArrayPropName ); // For debugging... - } - - RecvTable *pTable = new RecvTable( pProps, elements, pVarName ); // TODO free that again - - ret.SetDataTable( pTable ); - - return ret; -} - -RecvProp InternalRecvPropArray( - const int elementCount, - const int elementStride, - char *pName, - ArrayLengthRecvProxyFn proxy - ) -{ - RecvProp ret; - - ret.InitArray( elementCount, elementStride ); - ret.m_pVarName = pName; - ret.SetArrayLengthProxy( proxy ); - - return ret; -} - - -// ---------------------------------------------------------------------- // -// Proxies. -// ---------------------------------------------------------------------- // - -void RecvProxy_FloatToFloat( const CRecvProxyData *pData, void *pStruct, void *pOut ) -{ - Assert( IsFinite( pData->m_Value.m_Float ) ); - *((float*)pOut) = pData->m_Value.m_Float; -} - -void RecvProxy_VectorToVector( const CRecvProxyData *pData, void *pStruct, void *pOut ) -{ - const float *v = pData->m_Value.m_Vector; - - Assert( IsFinite( v[0] ) && IsFinite( v[1] ) && IsFinite( v[2] ) ); - ((float*)pOut)[0] = v[0]; - ((float*)pOut)[1] = v[1]; - ((float*)pOut)[2] = v[2]; -} - -void RecvProxy_QuaternionToQuaternion( const CRecvProxyData *pData, void *pStruct, void *pOut ) -{ - const float *v = pData->m_Value.m_Vector; - - Assert( IsFinite( v[0] ) && IsFinite( v[1] ) && IsFinite( v[2] ) && IsFinite( v[3] ) ); - ((float*)pOut)[0] = v[0]; - ((float*)pOut)[1] = v[1]; - ((float*)pOut)[2] = v[2]; - ((float*)pOut)[3] = v[3]; -} - -void RecvProxy_Int32ToInt8( const CRecvProxyData *pData, void *pStruct, void *pOut ) -{ - *((unsigned char*)pOut) = *((unsigned char*)&pData->m_Value.m_Int); -} - -void RecvProxy_Int32ToInt16( const CRecvProxyData *pData, void *pStruct, void *pOut ) -{ - *((unsigned short*)pOut) = *((unsigned short*)&pData->m_Value.m_Int); -} - -void RecvProxy_Int32ToInt32( const CRecvProxyData *pData, void *pStruct, void *pOut ) -{ - *((unsigned long*)pOut) = *((unsigned long*)&pData->m_Value.m_Int); -} - -void RecvProxy_StringToString( const CRecvProxyData *pData, void *pStruct, void *pOut ) -{ - char *pStrOut = (char*)pOut; - if ( pData->m_pRecvProp->m_StringBufferSize <= 0 ) - { - return; - } - - for ( int i=0; i < pData->m_pRecvProp->m_StringBufferSize; i++ ) - { - pStrOut[i] = pData->m_Value.m_pString[i]; - if(pStrOut[i] == 0) - break; - } - - pStrOut[pData->m_pRecvProp->m_StringBufferSize-1] = 0; -} - -void DataTableRecvProxy_StaticDataTable( const RecvProp *pProp, void **pOut, void *pData, int objectID ) -{ - *pOut = pData; -} - -void DataTableRecvProxy_PointerDataTable( const RecvProp *pProp, void **pOut, void *pData, int objectID ) -{ - *pOut = *((void**)pData); -} - -#endif +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + + +#include "dt_recv.h" +#include "vector.h" +#include "vstdlib/strtools.h" +#include "dt_utlvector_common.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +#if !defined(_STATIC_LINKED) || defined(CLIENT_DLL) + +char *s_ClientElementNames[MAX_ARRAY_ELEMENTS] = +{ + "000", "001", "002", "003", "004", "005", "006", "007", "008", "009", + "010", "011", "012", "013", "014", "015", "016", "017", "018", "019", + "020", "021", "022", "023", "024", "025", "026", "027", "028", "029", + "030", "031", "032", "033", "034", "035", "036", "037", "038", "039", + "040", "041", "042", "043", "044", "045", "046", "047", "048", "049", + "050", "051", "052", "053", "054", "055", "056", "057", "058", "059", + "060", "061", "062", "063", "064", "065", "066", "067", "068", "069", + "070", "071", "072", "073", "074", "075", "076", "077", "078", "079", + "080", "081", "082", "083", "084", "085", "086", "087", "088", "089", + "090", "091", "092", "093", "094", "095", "096", "097", "098", "099", + "100", "101", "102", "103", "104", "105", "106", "107", "108", "109", + "110", "111", "112", "113", "114", "115", "116", "117", "118", "119", + "120", "121", "122", "123", "124", "125", "126", "127", "128", "129", + "130", "131", "132", "133", "134", "135", "136", "137", "138", "139", + "140", "141", "142", "143", "144", "145", "146", "147", "148", "149", + "150", "151", "152", "153", "154", "155", "156", "157", "158", "159", + "160", "161", "162", "163", "164", "165", "166", "167", "168", "169", + "170", "171", "172", "173", "174", "175", "176", "177", "178", "179", + "180", "181", "182", "183", "184", "185", "186", "187", "188", "189", + "190", "191", "192", "193", "194", "195", "196", "197", "198", "199", + "200", "201", "202", "203", "204", "205", "206", "207", "208", "209", + "210", "211", "212", "213", "214", "215", "216", "217", "218", "219", + "220", "221", "222", "223", "224", "225", "226", "227", "228", "229", + "230", "231", "232", "233", "234", "235", "236", "237", "238", "239", + "240", "241", "242", "243", "244", "245", "246", "247", "248", "249", + "250", "251", "252", "253", "254", "255" +}; + +CStandardRecvProxies::CStandardRecvProxies() +{ + m_Int32ToInt8 = RecvProxy_Int32ToInt8; + m_Int32ToInt16 = RecvProxy_Int32ToInt16; + m_Int32ToInt32 = RecvProxy_Int32ToInt32; + m_FloatToFloat = RecvProxy_FloatToFloat; + m_VectorToVector = RecvProxy_VectorToVector; +} + +CStandardRecvProxies g_StandardRecvProxies; + + +// ---------------------------------------------------------------------- // +// RecvProp. +// ---------------------------------------------------------------------- // +RecvProp::RecvProp() +{ + m_pExtraData = NULL; + m_pVarName = NULL; + m_Offset = 0; + m_RecvType = DPT_Int; + m_Flags = 0; + m_ProxyFn = NULL; + m_DataTableProxyFn = NULL; + m_pDataTable = NULL; + m_nElements = 1; + m_ElementStride = -1; + m_pArrayProp = NULL; + m_ArrayLengthProxy = NULL; + m_bInsideArray = false; +} + +// ---------------------------------------------------------------------- // +// RecvTable. +// ---------------------------------------------------------------------- // +RecvTable::RecvTable() +{ + Construct( NULL, 0, NULL ); +} + +RecvTable::RecvTable(RecvProp *pProps, int nProps, char *pNetTableName) +{ + Construct( pProps, nProps, pNetTableName ); +} + +RecvTable::~RecvTable() +{ +} + +void RecvTable::Construct( RecvProp *pProps, int nProps, char *pNetTableName ) +{ + m_pProps = pProps; + m_nProps = nProps; + m_pDecoder = NULL; + m_pNetTableName = pNetTableName; + m_bInitialized = false; + m_bInMainList = false; +} + + +// ---------------------------------------------------------------------- // +// Prop setup functions (for building tables). +// ---------------------------------------------------------------------- // + +RecvProp RecvPropFloat( + char *pVarName, + int offset, + int sizeofVar, + int flags, + RecvVarProxyFn varProxy + ) +{ + RecvProp ret; + + // Debug type checks. + if ( varProxy == RecvProxy_FloatToFloat ) + { + Assert( sizeofVar == 0 || sizeofVar == 4 ); + } + + ret.m_pVarName = pVarName; + ret.SetOffset( offset ); + ret.m_RecvType = DPT_Float; + ret.m_Flags = flags; + ret.SetProxyFn( varProxy ); + + return ret; +} + +RecvProp RecvPropVector( + char *pVarName, + int offset, + int sizeofVar, + int flags, + RecvVarProxyFn varProxy + ) +{ + RecvProp ret; + + // Debug type checks. + #ifdef _DEBUG + if(varProxy == RecvProxy_VectorToVector) + { + Assert(sizeofVar == sizeof(Vector)); + } + #endif + + ret.m_pVarName = pVarName; + ret.SetOffset( offset ); + ret.m_RecvType = DPT_Vector; + ret.m_Flags = flags; + ret.SetProxyFn( varProxy ); + + return ret; +} + +#if 0 // We can't ship this since it changes the size of DTVariant to be 20 bytes instead of 16 and that breaks MODs!!! + +RecvProp RecvPropQuaternion( + char *pVarName, + int offset, + int sizeofVar, // Handled by RECVINFO macro, but set to SIZEOF_IGNORE if you don't want to bother. + int flags, + RecvVarProxyFn varProxy + ) +{ + RecvProp ret; + + // Debug type checks. + #ifdef _DEBUG + if(varProxy == RecvProxy_QuaternionToQuaternion) + { + Assert(sizeofVar == sizeof(Quaternion)); + } + #endif + + ret.m_pVarName = pVarName; + ret.SetOffset( offset ); + ret.m_RecvType = DPT_Quaternion; + ret.m_Flags = flags; + ret.SetProxyFn( varProxy ); + + return ret; +} +#endif + +RecvProp RecvPropInt( + char *pVarName, + int offset, + int sizeofVar, + int flags, + RecvVarProxyFn varProxy + ) +{ + RecvProp ret; + + // If they didn't specify a proxy, then figure out what type we're writing to. + if(varProxy == NULL) + { + if(sizeofVar == 1) + { + varProxy = RecvProxy_Int32ToInt8; + } + else if(sizeofVar == 2) + { + varProxy = RecvProxy_Int32ToInt16; + } + else if(sizeofVar == 4) + { + varProxy = RecvProxy_Int32ToInt32; + } + else + { + Assert(!"RecvPropInt var has invalid size"); + varProxy = RecvProxy_Int32ToInt8; // safest one... + } + } + + ret.m_pVarName = pVarName; + ret.SetOffset( offset ); + ret.m_RecvType = DPT_Int; + ret.m_Flags = flags; + ret.SetProxyFn( varProxy ); + + return ret; +} + +RecvProp RecvPropString( + char *pVarName, + int offset, + int bufferSize, + int flags, + RecvVarProxyFn varProxy + ) +{ + RecvProp ret; + + ret.m_pVarName = pVarName; + ret.SetOffset( offset ); + ret.m_RecvType = DPT_String; + ret.m_Flags = flags; + ret.m_StringBufferSize = bufferSize; + ret.SetProxyFn( varProxy ); + + return ret; +} + +RecvProp RecvPropDataTable( + char *pVarName, + int offset, + int flags, + RecvTable *pTable, + DataTableRecvVarProxyFn varProxy + ) +{ + RecvProp ret; + + ret.m_pVarName = pVarName; + ret.SetOffset( offset ); + ret.m_RecvType = DPT_DataTable; + ret.m_Flags = flags; + ret.SetDataTableProxyFn( varProxy ); + ret.SetDataTable( pTable ); + + return ret; +} + +RecvProp RecvPropArray3( + char *pVarName, + int offset, + int sizeofVar, + int elements, + RecvProp pArrayProp, + DataTableRecvVarProxyFn varProxy + ) +{ + RecvProp ret; + + Assert( elements <= MAX_ARRAY_ELEMENTS ); + + ret.m_pVarName = pVarName; + ret.SetOffset( offset ); + ret.m_RecvType = DPT_DataTable; + ret.SetDataTableProxyFn( varProxy ); + + RecvProp *pProps = new RecvProp[elements]; // TODO free that again + + const char *pParentArrayPropName = AllocateStringHelper( "%s", pVarName ); + + for ( int i=0; i < elements; i++ ) + { + pProps[i] = pArrayProp; // copy basic property settings + pProps[i].SetOffset( i * sizeofVar ); // adjust offset + pProps[i].m_pVarName = s_ClientElementNames[i]; // give unique name + pProps[i].SetParentArrayPropName( pParentArrayPropName ); // For debugging... + } + + RecvTable *pTable = new RecvTable( pProps, elements, pVarName ); // TODO free that again + + ret.SetDataTable( pTable ); + + return ret; +} + +RecvProp InternalRecvPropArray( + const int elementCount, + const int elementStride, + char *pName, + ArrayLengthRecvProxyFn proxy + ) +{ + RecvProp ret; + + ret.InitArray( elementCount, elementStride ); + ret.m_pVarName = pName; + ret.SetArrayLengthProxy( proxy ); + + return ret; +} + + +// ---------------------------------------------------------------------- // +// Proxies. +// ---------------------------------------------------------------------- // + +void RecvProxy_FloatToFloat( const CRecvProxyData *pData, void *pStruct, void *pOut ) +{ + Assert( IsFinite( pData->m_Value.m_Float ) ); + *((float*)pOut) = pData->m_Value.m_Float; +} + +void RecvProxy_VectorToVector( const CRecvProxyData *pData, void *pStruct, void *pOut ) +{ + const float *v = pData->m_Value.m_Vector; + + Assert( IsFinite( v[0] ) && IsFinite( v[1] ) && IsFinite( v[2] ) ); + ((float*)pOut)[0] = v[0]; + ((float*)pOut)[1] = v[1]; + ((float*)pOut)[2] = v[2]; +} + +void RecvProxy_QuaternionToQuaternion( const CRecvProxyData *pData, void *pStruct, void *pOut ) +{ + const float *v = pData->m_Value.m_Vector; + + Assert( IsFinite( v[0] ) && IsFinite( v[1] ) && IsFinite( v[2] ) && IsFinite( v[3] ) ); + ((float*)pOut)[0] = v[0]; + ((float*)pOut)[1] = v[1]; + ((float*)pOut)[2] = v[2]; + ((float*)pOut)[3] = v[3]; +} + +void RecvProxy_Int32ToInt8( const CRecvProxyData *pData, void *pStruct, void *pOut ) +{ + *((unsigned char*)pOut) = *((unsigned char*)&pData->m_Value.m_Int); +} + +void RecvProxy_Int32ToInt16( const CRecvProxyData *pData, void *pStruct, void *pOut ) +{ + const void *addr = &pData->m_Value.m_Int; + *((unsigned short*)pOut) = *((unsigned short*)addr); +} + +void RecvProxy_Int32ToInt32( const CRecvProxyData *pData, void *pStruct, void *pOut ) +{ + *((unsigned long*)pOut) = *((unsigned long*)&pData->m_Value.m_Int); +} + +void RecvProxy_StringToString( const CRecvProxyData *pData, void *pStruct, void *pOut ) +{ + char *pStrOut = (char*)pOut; + if ( pData->m_pRecvProp->m_StringBufferSize <= 0 ) + { + return; + } + + for ( int i=0; i < pData->m_pRecvProp->m_StringBufferSize; i++ ) + { + pStrOut[i] = pData->m_Value.m_pString[i]; + if(pStrOut[i] == 0) + break; + } + + pStrOut[pData->m_pRecvProp->m_StringBufferSize-1] = 0; +} + +void DataTableRecvProxy_StaticDataTable( const RecvProp *pProp, void **pOut, void *pData, int objectID ) +{ + *pOut = pData; +} + +void DataTableRecvProxy_PointerDataTable( const RecvProp *pProp, void **pOut, void *pData, int objectID ) +{ + *pOut = *((void**)pData); +} + +#endif diff --git a/public/dt_send.cpp b/public/dt_send.cpp index c2ff4fed..2c0ea383 100644 --- a/public/dt_send.cpp +++ b/public/dt_send.cpp @@ -225,7 +225,7 @@ float AssignRangeMultiplier( int nBits, double range ) // Squeeze it down smaller and smaller until it's going to produce an integer // in the valid range when given the highest value. float multipliers[] = { 0.9999, 0.99, 0.9, 0.8, 0.7 }; - int i; + size_t i; for ( i=0; i < ARRAYSIZE( multipliers ); i++ ) { float fHighLowMul = (float)( iHighValue / range ) * multipliers[i]; diff --git a/public/dt_shared.cpp b/public/dt_shared.cpp index 488a38a2..df39326f 100644 --- a/public/dt_shared.cpp +++ b/public/dt_shared.cpp @@ -1,113 +1,113 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -// -//=============================================================================// - -#include "dt_shared.h" - -#if !defined (CLIENT_DLL) -#include "sendproxy.h" -#else -#include "recvproxy.h" -#endif - - -// ------------------------------------------------------------------------ // -// Just wrappers to make shared code look easier... -// ------------------------------------------------------------------------ // - -// Use these functions to setup your data tables. -DataTableProp PropFloat( - char *pVarName, // Variable name. - int offset, // Offset into container structure. - int sizeofVar, - int nBits, // Number of bits to use when encoding. - int flags, - float fLowValue, // For floating point, low and high values. - float fHighValue // High value. If HIGH_DEFAULT, it's (1< *g_STDict = 0; static CUtlDict *g_RTDict = 0; - +#endif char* AllocateStringHelper2( const char *pFormat, va_list marker ) { diff --git a/public/dt_utlvector_recv.h b/public/dt_utlvector_recv.h index 82fe93af..5252466b 100644 --- a/public/dt_utlvector_recv.h +++ b/public/dt_utlvector_recv.h @@ -1,60 +1,60 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: Player for HL1. -// -// $NoKeywords: $ -//=============================================================================// - -#ifndef DT_UTLVECTOR_RECV_H -#define DT_UTLVECTOR_RECV_H -#pragma once - - -#include "dt_recv.h" -#include "dt_utlvector_common.h" - - - -#define RECVINFO_UTLVECTOR( varName ) #varName, \ - offsetof(currentRecvDTClass, varName), \ - sizeof(((currentRecvDTClass*)0)->varName[0]), \ - GetResizeUtlVectorTemplate( ((currentRecvDTClass*)0)->varName ), \ - GetEnsureCapacityTemplate( ((currentRecvDTClass*)0)->varName ) - -// Use this macro to specify a utlvector where you specify the function -// that gets called to make sure the size of the utlvector is correct. -// The size function looks like this: void ResizeUtlVector( void *pVoid, int len ) -#define RECVINFO_UTLVECTOR_SIZEFN( varName, resizeFn ) #varName, \ - offsetof(currentRecvDTClass, varName), \ - sizeof(((currentRecvDTClass*)0)->varName[0]), \ - resizeFn, \ - GetEnsureCapacityTemplate( ((currentRecvDTClass*)0)->varName ) - - -#define RecvPropUtlVectorDataTable( varName, nMaxElements, dataTableName ) \ - RecvPropUtlVector( RECVINFO_UTLVECTOR( varName ), nMaxElements, RecvPropDataTable(NULL,0,0, &REFERENCE_RECV_TABLE( dataTableName ) ) ) - - -// -// Receive a property sent with SendPropUtlVector. -// -// Example usage: -// -// RecvPropUtlVectorDataTable( m_StructArray, 11, DT_StructArray ) -// -// RecvPropUtlVector( RECVINFO_UTLVECTOR( m_FloatArray ), 16, RecvPropFloat(NULL,0,0) ) -// -RecvProp RecvPropUtlVector( - char *pVarName, // Use RECVINFO_UTLVECTOR to generate these first 5 parameters. - int offset, - int sizeofVar, - ResizeUtlVectorFn fn, - EnsureCapacityFn ensureFn, - - int nMaxElements, // Max # of elements in the array. Keep this as low as possible. - RecvProp pArrayProp // The definition of the property you're receiving into. - // You can leave all of its parameters at 0 (name, offset, size, etc). - ); - - -#endif // DT_UTLVECTOR_RECV_H \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: Player for HL1. +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef DT_UTLVECTOR_RECV_H +#define DT_UTLVECTOR_RECV_H +#pragma once + + +#include "dt_recv.h" +#include "dt_utlvector_common.h" + + + +#define RECVINFO_UTLVECTOR( varName ) #varName, \ + offsetof(currentRecvDTClass, varName), \ + sizeof(((currentRecvDTClass*)0)->varName[0]), \ + GetResizeUtlVectorTemplate( ((currentRecvDTClass*)0)->varName ), \ + GetEnsureCapacityTemplate( ((currentRecvDTClass*)0)->varName ) + +// Use this macro to specify a utlvector where you specify the function +// that gets called to make sure the size of the utlvector is correct. +// The size function looks like this: void ResizeUtlVector( void *pVoid, int len ) +#define RECVINFO_UTLVECTOR_SIZEFN( varName, resizeFn ) #varName, \ + offsetof(currentRecvDTClass, varName), \ + sizeof(((currentRecvDTClass*)0)->varName[0]), \ + resizeFn, \ + GetEnsureCapacityTemplate( ((currentRecvDTClass*)0)->varName ) + + +#define RecvPropUtlVectorDataTable( varName, nMaxElements, dataTableName ) \ + RecvPropUtlVector( RECVINFO_UTLVECTOR( varName ), nMaxElements, RecvPropDataTable(NULL,0,0, &REFERENCE_RECV_TABLE( dataTableName ) ) ) + + +// +// Receive a property sent with SendPropUtlVector. +// +// Example usage: +// +// RecvPropUtlVectorDataTable( m_StructArray, 11, DT_StructArray ) +// +// RecvPropUtlVector( RECVINFO_UTLVECTOR( m_FloatArray ), 16, RecvPropFloat(NULL,0,0) ) +// +RecvProp RecvPropUtlVector( + char *pVarName, // Use RECVINFO_UTLVECTOR to generate these first 5 parameters. + int offset, + int sizeofVar, + ResizeUtlVectorFn fn, + EnsureCapacityFn ensureFn, + + int nMaxElements, // Max # of elements in the array. Keep this as low as possible. + RecvProp pArrayProp // The definition of the property you're receiving into. + // You can leave all of its parameters at 0 (name, offset, size, etc). + ); + + +#endif // DT_UTLVECTOR_RECV_H diff --git a/public/eiface.h b/public/eiface.h index 3e3036d1..d9d7c679 100644 --- a/public/eiface.h +++ b/public/eiface.h @@ -343,11 +343,21 @@ public: // Returns true if the engine is an internal build. i.e. is using the internal bugreporter. virtual bool IsInternalBuild( void ) = 0; - virtual IChangeInfoAccessor *GetChangeAccessor( const edict_t *pEdict ) = 0; + virtual IChangeInfoAccessor *GetChangeAccessor( const edict_t *pEdict ) = 0; + + // Call this to find out the value of a cvar on the client. + // + // It is an asynchronous query, and it will call IServerGameDLL::OnQueryCvarValueFinished when + // the value comes in from the client. + // + // Store the return value if you want to match this specific query to the OnQueryCvarValueFinished call. + // Returns InvalidQueryCvarCookie if the entity is invalid. + virtual QueryCvarCookie_t StartQueryCvarValue( edict_t *pPlayerEntity, const char *pName ) = 0; }; #define INTERFACEVERSION_SERVERGAMEDLL_VERSION_4 "ServerGameDLL004" -#define INTERFACEVERSION_SERVERGAMEDLL "ServerGameDLL005" +#define INTERFACEVERSION_SERVERGAMEDLL_VERSION_5 "ServerGameDLL005" +#define INTERFACEVERSION_SERVERGAMEDLL "ServerGameDLL006" //----------------------------------------------------------------------------- // Purpose: These are the interfaces that the game .dll exposes to the engine @@ -437,6 +447,15 @@ public: #ifdef _XBOX virtual void GetTitleName( const char *pMapName, char* pTitleBuff, int titleBuffSize ) = 0; #endif + + // * This function is new with version 6 of the interface. + // + // This is called when a query from IVEngineServer::StartQueryCvarValue is finished. + // iCookie is the value returned by IVEngineServer::StartQueryCvarValue. + // Added with version 2 of the interface. + virtual void OnQueryCvarValueFinished( QueryCvarCookie_t iCookie, edict_t *pPlayerEntity, EQueryCvarValueStatus eStatus, const char *pCvarName, const char *pCvarValue ) + { + } }; //----------------------------------------------------------------------------- diff --git a/public/engine/iserverplugin.h b/public/engine/iserverplugin.h index 7f658c66..699b1dcb 100644 --- a/public/engine/iserverplugin.h +++ b/public/engine/iserverplugin.h @@ -28,8 +28,19 @@ typedef enum PLUGIN_STOP, // don't run the game dll function at all } PLUGIN_RESULT; +typedef enum +{ + eQueryCvarValueStatus_ValueIntact=0, // It got the value fine. + eQueryCvarValueStatus_CvarNotFound=1, + eQueryCvarValueStatus_NotACvar=2, // There's a ConCommand, but it's not a ConVar. + eQueryCvarValueStatus_CvarProtected=3 // The cvar was marked with FCVAR_SERVER_CAN_NOT_QUERY, so the server is not allowed to have its value. +} EQueryCvarValueStatus; -#define INTERFACEVERSION_ISERVERPLUGINCALLBACKS "ISERVERPLUGINCALLBACKS001" +typedef int QueryCvarCookie_t; + +#define InvalidQueryCvarCookie -1 +#define INTERFACEVERSION_ISERVERPLUGINCALLBACKS_VERSION_1 "ISERVERPLUGINCALLBACKS001" +#define INTERFACEVERSION_ISERVERPLUGINCALLBACKS "ISERVERPLUGINCALLBACKS002" //----------------------------------------------------------------------------- // Purpose: callbacks the engine exposes to the 3rd party plugins (ala MetaMod) //----------------------------------------------------------------------------- @@ -88,6 +99,13 @@ public: // A user has had their network id setup and validated virtual PLUGIN_RESULT NetworkIDValidated( const char *pszUserName, const char *pszNetworkID ) = 0; + + // This is called when a query from IServerPluginHelpers::StartQueryCvarValue is finished. + // iCookie is the value returned by IServerPluginHelpers::StartQueryCvarValue. + // Added with version 2 of the interface. + virtual void OnQueryCvarValueFinished( QueryCvarCookie_t iCookie, edict_t *pPlayerEntity, EQueryCvarValueStatus eStatus, const char *pCvarName, const char *pCvarValue ) + { + } }; #define INTERFACEVERSION_ISERVERPLUGINHELPERS "ISERVERPLUGINHELPERS001" @@ -121,6 +139,15 @@ public: // virtual void CreateMessage( edict_t *pEntity, DIALOG_TYPE type, KeyValues *data, IServerPluginCallbacks *plugin ) = 0; virtual void ClientCommand( edict_t *pEntity, const char *cmd ) = 0; + + // Call this to find out the value of a cvar on the client. + // + // It is an asynchronous query, and it will call IServerPluginCallbacks::OnQueryCvarValueFinished when + // the value comes in from the client. + // + // Store the return value if you want to match this specific query to the OnQueryCvarValueFinished call. + // Returns InvalidQueryCvarCookie if the entity is invalid. + virtual QueryCvarCookie_t StartQueryCvarValue( edict_t *pEntity, const char *pName ) = 0; }; #endif //ISERVERPLUGIN_H diff --git a/public/engine/ivmodelinfo.h b/public/engine/ivmodelinfo.h index aefa2e6f..b55403be 100644 --- a/public/engine/ivmodelinfo.h +++ b/public/engine/ivmodelinfo.h @@ -1,121 +1,121 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -//=============================================================================// - -#ifndef IVMODELINFO_H -#define IVMODELINFO_H - -#ifdef _WIN32 -#pragma once -#endif - - -//----------------------------------------------------------------------------- -// Forward declarations -//----------------------------------------------------------------------------- -class IMaterial; -class KeyValues; -struct vcollide_t; -struct model_t; -class Vector; -class QAngle; -class CGameTrace; -typedef CGameTrace trace_t; -struct studiohdr_t; -struct virtualmodel_t; -typedef unsigned char byte; -struct virtualterrainparams_t; -class CPhysCollide; -//----------------------------------------------------------------------------- -// Model info interface -//----------------------------------------------------------------------------- - -// change this when the new version is incompatable with the old -#define VMODELINFO_CLIENT_INTERFACE_VERSION "VModelInfoClient003" -#define VMODELINFO_SERVER_INTERFACE_VERSION "VModelInfoServer001" - -class IVModelInfo -{ -public: - virtual ~IVModelInfo( void ) { } - - virtual const model_t *GetModel( int modelindex ) const = 0; - // Returns index of model by name - virtual int GetModelIndex( const char *name ) const = 0; - - // Returns name of model - virtual const char *GetModelName( const model_t *model ) const = 0; - virtual vcollide_t *GetVCollide( const model_t *model ) const = 0; - virtual vcollide_t *GetVCollide( int modelindex ) const = 0; - virtual void GetModelBounds( const model_t *model, Vector& mins, Vector& maxs ) const = 0; - virtual void GetModelRenderBounds( const model_t *model, Vector& mins, Vector& maxs ) const = 0; - virtual int GetModelFrameCount( const model_t *model ) const = 0; - virtual int GetModelType( const model_t *model ) const = 0; - virtual void *GetModelExtraData( const model_t *model ) = 0; - virtual bool ModelHasMaterialProxy( const model_t *model ) const = 0; - virtual bool IsTranslucent( model_t const* model ) const = 0; - virtual bool IsTranslucentTwoPass( const model_t *model ) const = 0; - virtual void RecomputeTranslucency( const model_t *model ) = 0; - virtual int GetModelMaterialCount( const model_t* model ) const = 0; - virtual void GetModelMaterials( const model_t *model, int count, IMaterial** ppMaterial ) = 0; - virtual bool IsModelVertexLit( const model_t *model ) const = 0; - virtual const char *GetModelKeyValueText( const model_t *model ) = 0; - virtual float GetModelRadius( const model_t *model ) = 0; - - virtual const studiohdr_t *FindModel( const studiohdr_t *pStudioHdr, void **cache, const char *modelname ) const = 0; - virtual const studiohdr_t *FindModel( void *cache ) const = 0; - virtual virtualmodel_t *GetVirtualModel( const studiohdr_t *pStudioHdr ) const = 0; - virtual byte *GetAnimBlock( const studiohdr_t *pStudioHdr, int iBlock ) const = 0; - - // Available on client only!!! - virtual void GetModelMaterialColorAndLighting( const model_t *model, Vector const& origin, - QAngle const& angles, trace_t* pTrace, - Vector& lighting, Vector& matColor ) = 0; - virtual void GetIlluminationPoint( const model_t *model, Vector const& origin, - QAngle const& angles, Vector* pLightingCenter ) = 0; - - virtual int GetModelContents( int modelIndex ) const = 0; - virtual studiohdr_t *GetStudiomodel( const model_t *mod ) = 0; - virtual int GetModelSpriteWidth( const model_t *model ) const = 0; - virtual int GetModelSpriteHeight( const model_t *model ) const = 0; - - // Sets/gets a map-specified fade range (client only) - virtual void SetLevelScreenFadeRange( float flMinSize, float flMaxSize ) = 0; - virtual void GetLevelScreenFadeRange( float *pMinArea, float *pMaxArea ) const = 0; - - // Sets/gets a map-specified per-view fade range (client only) - virtual void SetViewScreenFadeRange( float flMinSize, float flMaxSize ) = 0; - - // Computes fade alpha based on distance fade + screen fade (client only) - virtual unsigned char ComputeLevelScreenFade( const Vector &vecAbsOrigin, float flRadius, float flFadeScale ) const = 0; - virtual unsigned char ComputeViewScreenFade( const Vector &vecAbsOrigin, float flRadius, float flFadeScale ) const = 0; - - // both client and server - virtual int GetAutoplayList( const studiohdr_t *pStudioHdr, unsigned short **pAutoplayList ) const = 0; - - // Gets a virtual terrain collision model (creates if necessary) - // NOTE: This may return NULL if the terrain model cannot be virtualized - virtual CPhysCollide *GetCollideForVirtualTerrain( const virtualterrainparams_t ¶ms ) = 0; - - virtual bool IsUsingFBTexture( const model_t *model ) const = 0; - - virtual const model_t *FindOrLoadModel( const char *name ) const = 0; -}; - - -class IVModelInfoClient : public IVModelInfo -{ -public: -}; - - -struct virtualterrainparams_t -{ - // UNDONE: Add grouping here, specified in BSP file? (test grouping to see if this is necessary) - int index; -}; - -#endif // IVMODELINFO_H \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef IVMODELINFO_H +#define IVMODELINFO_H + +#ifdef _WIN32 +#pragma once +#endif + + +//----------------------------------------------------------------------------- +// Forward declarations +//----------------------------------------------------------------------------- +class IMaterial; +class KeyValues; +struct vcollide_t; +struct model_t; +class Vector; +class QAngle; +class CGameTrace; +typedef CGameTrace trace_t; +struct studiohdr_t; +struct virtualmodel_t; +typedef unsigned char byte; +struct virtualterrainparams_t; +class CPhysCollide; +//----------------------------------------------------------------------------- +// Model info interface +//----------------------------------------------------------------------------- + +// change this when the new version is incompatable with the old +#define VMODELINFO_CLIENT_INTERFACE_VERSION "VModelInfoClient003" +#define VMODELINFO_SERVER_INTERFACE_VERSION "VModelInfoServer001" + +class IVModelInfo +{ +public: + virtual ~IVModelInfo( void ) { } + + virtual const model_t *GetModel( int modelindex ) const = 0; + // Returns index of model by name + virtual int GetModelIndex( const char *name ) const = 0; + + // Returns name of model + virtual const char *GetModelName( const model_t *model ) const = 0; + virtual vcollide_t *GetVCollide( const model_t *model ) const = 0; + virtual vcollide_t *GetVCollide( int modelindex ) const = 0; + virtual void GetModelBounds( const model_t *model, Vector& mins, Vector& maxs ) const = 0; + virtual void GetModelRenderBounds( const model_t *model, Vector& mins, Vector& maxs ) const = 0; + virtual int GetModelFrameCount( const model_t *model ) const = 0; + virtual int GetModelType( const model_t *model ) const = 0; + virtual void *GetModelExtraData( const model_t *model ) = 0; + virtual bool ModelHasMaterialProxy( const model_t *model ) const = 0; + virtual bool IsTranslucent( model_t const* model ) const = 0; + virtual bool IsTranslucentTwoPass( const model_t *model ) const = 0; + virtual void RecomputeTranslucency( const model_t *model ) = 0; + virtual int GetModelMaterialCount( const model_t* model ) const = 0; + virtual void GetModelMaterials( const model_t *model, int count, IMaterial** ppMaterial ) = 0; + virtual bool IsModelVertexLit( const model_t *model ) const = 0; + virtual const char *GetModelKeyValueText( const model_t *model ) = 0; + virtual float GetModelRadius( const model_t *model ) = 0; + + virtual const studiohdr_t *FindModel( const studiohdr_t *pStudioHdr, void **cache, const char *modelname ) const = 0; + virtual const studiohdr_t *FindModel( void *cache ) const = 0; + virtual virtualmodel_t *GetVirtualModel( const studiohdr_t *pStudioHdr ) const = 0; + virtual byte *GetAnimBlock( const studiohdr_t *pStudioHdr, int iBlock ) const = 0; + + // Available on client only!!! + virtual void GetModelMaterialColorAndLighting( const model_t *model, Vector const& origin, + QAngle const& angles, trace_t* pTrace, + Vector& lighting, Vector& matColor ) = 0; + virtual void GetIlluminationPoint( const model_t *model, Vector const& origin, + QAngle const& angles, Vector* pLightingCenter ) = 0; + + virtual int GetModelContents( int modelIndex ) const = 0; + virtual studiohdr_t *GetStudiomodel( const model_t *mod ) = 0; + virtual int GetModelSpriteWidth( const model_t *model ) const = 0; + virtual int GetModelSpriteHeight( const model_t *model ) const = 0; + + // Sets/gets a map-specified fade range (client only) + virtual void SetLevelScreenFadeRange( float flMinSize, float flMaxSize ) = 0; + virtual void GetLevelScreenFadeRange( float *pMinArea, float *pMaxArea ) const = 0; + + // Sets/gets a map-specified per-view fade range (client only) + virtual void SetViewScreenFadeRange( float flMinSize, float flMaxSize ) = 0; + + // Computes fade alpha based on distance fade + screen fade (client only) + virtual unsigned char ComputeLevelScreenFade( const Vector &vecAbsOrigin, float flRadius, float flFadeScale ) const = 0; + virtual unsigned char ComputeViewScreenFade( const Vector &vecAbsOrigin, float flRadius, float flFadeScale ) const = 0; + + // both client and server + virtual int GetAutoplayList( const studiohdr_t *pStudioHdr, unsigned short **pAutoplayList ) const = 0; + + // Gets a virtual terrain collision model (creates if necessary) + // NOTE: This may return NULL if the terrain model cannot be virtualized + virtual CPhysCollide *GetCollideForVirtualTerrain( const virtualterrainparams_t ¶ms ) = 0; + + virtual bool IsUsingFBTexture( const model_t *model ) const = 0; + + virtual const model_t *FindOrLoadModel( const char *name ) const = 0; +}; + + +class IVModelInfoClient : public IVModelInfo +{ +public: +}; + + +struct virtualterrainparams_t +{ + // UNDONE: Add grouping here, specified in BSP file? (test grouping to see if this is necessary) + int index; +}; + +#endif // IVMODELINFO_H diff --git a/public/filesystem_init.cpp b/public/filesystem_init.cpp index 8ea24e50..b12a62d4 100644 --- a/public/filesystem_init.cpp +++ b/public/filesystem_init.cpp @@ -1,1143 +1,1145 @@ -//====== Copyright © 1996-2004, Valve Corporation, All rights reserved. ======= -// -// Purpose: -// -//============================================================================= - -#if !defined(_STATIC_LINKED) || defined(_SHARED_LIB) - -#undef PROTECTED_THINGS_ENABLE -#undef PROTECT_FILEIO_FUNCTIONS -#undef fopen - -#ifdef _XBOX -#include "xbox/xbox_platform.h" -#include "xbox/xbox_win32stubs.h" -#include "xbox/xbox_core.h" -#endif -#if defined(_WIN32) && !defined(_XBOX) -#include -#include -#include // _chmod -#include -#elif _LINUX -#include -#define _putenv putenv -#define _chdir chdir -#define _access access -#endif -#include -#include -#include "vstdlib/strtools.h" -#include "filesystem_init.h" -#include "vstdlib/ICommandLine.h" -#include "KeyValues.h" - -// memdbgon must be the last include file in a .cpp file!!! -#include - -#define GAMEINFO_FILENAME "gameinfo.txt" - -static char g_FileSystemError[256]; -static FSErrorMode_t g_FileSystemErrorMode = FS_ERRORMODE_VCONFIG; - -// This class lets you modify environment variables, and it restores the original value -// when it goes out of scope. -#ifndef _XBOX -class CTempEnvVar -{ -public: - CTempEnvVar( const char *pVarName ) - { - m_bRestoreOriginalValue = true; - m_pVarName = pVarName; - - const char *pValue = NULL; - -#ifdef _WIN32 - // Use GetEnvironmentVariable instead of getenv because getenv doesn't pick up changes - // to the process environment after the DLL was loaded. - char szBuf[ 4096 ]; - if ( GetEnvironmentVariable( m_pVarName, szBuf, sizeof( szBuf ) ) != 0) - { - pValue = szBuf; - } -#else - // LINUX BUG: see above - pValue = getenv( pVarName ); -#endif // _WIN32 - - if ( pValue ) - { - m_bExisted = true; - m_OriginalValue.SetSize( strlen( pValue ) + 1 ); - memcpy( m_OriginalValue.Base(), pValue, m_OriginalValue.Count() ); - } - else - { - m_bExisted = false; - } - } - - ~CTempEnvVar() - { - if ( m_bRestoreOriginalValue ) - { - // Restore the original value. - if ( m_bExisted ) - { - SetValue( "%s", m_OriginalValue.Base() ); - } - else - { - ClearValue(); - } - } - } - - void SetRestoreOriginalValue( bool bRestore ) - { - m_bRestoreOriginalValue = bRestore; - } - - int GetValue(char *pszBuf, int nBufSize ) - { - if ( !pszBuf || ( nBufSize <= 0 ) ) - return 0; - -#ifdef _WIN32 - // Use GetEnvironmentVariable instead of getenv because getenv doesn't pick up changes - // to the process environment after the DLL was loaded. - return GetEnvironmentVariable( m_pVarName, pszBuf, nBufSize ); -#else - // LINUX BUG: see above - const char *pszOut = getenv( m_pVarName ); - if ( !pszOut ) - { - *pszBuf = '\0'; - return 0; - } - - Q_strncpy( pszBuf, pszOut, nBufSize ); - return Q_strlen( pszBuf ); -#endif // _WIN32 - } - - void SetValue( const char *pValue, ... ) - { - char valueString[4096]; - va_list marker; - va_start( marker, pValue ); - Q_vsnprintf( valueString, sizeof( valueString ), pValue, marker ); - va_end( marker ); - - char str[4096]; - Q_snprintf( str, sizeof( str ), "%s=%s", m_pVarName, valueString ); - _putenv( str ); - } - - void ClearValue() - { - char str[512]; - Q_snprintf( str, sizeof( str ), "%s=", m_pVarName ); - _putenv( str ); - } - -private: - bool m_bRestoreOriginalValue; - const char *m_pVarName; - bool m_bExisted; - CUtlVector m_OriginalValue; -}; - - -class CSteamEnvVars -{ -public: - CSteamEnvVars() : - m_SteamAppId( "SteamAppId" ), - m_SteamUserPassphrase( "SteamUserPassphrase" ), - m_SteamAppUser( "SteamAppUser" ), - m_Path( "path" ) - { - } - - void SetRestoreOriginalValue_ALL( bool bRestore ) - { - m_SteamAppId.SetRestoreOriginalValue( bRestore ); - m_SteamUserPassphrase.SetRestoreOriginalValue( bRestore ); - m_SteamAppUser.SetRestoreOriginalValue( bRestore ); - m_Path.SetRestoreOriginalValue( bRestore ); - } - - CTempEnvVar m_SteamAppId; - CTempEnvVar m_SteamUserPassphrase; - CTempEnvVar m_SteamAppUser; - CTempEnvVar m_Path; -}; -#endif - - - -// ---------------------------------------------------------------------------------------------------- // -// Helpers. -// ---------------------------------------------------------------------------------------------------- // -#ifndef _XBOX -void Q_getwd( char *out, int outSize ) -{ -#if defined( _WIN32 ) || defined( WIN32 ) - _getcwd( out, outSize ); - Q_strncat( out, "\\", outSize, COPY_ALL_CHARACTERS ); -#else - getcwd(out, outSize); - strcat(out, "/"); -#endif - Q_FixSlashes( out ); -} -#endif - -// ---------------------------------------------------------------------------------------------------- // -// Module interface. -// ---------------------------------------------------------------------------------------------------- // - -CFSSearchPathsInit::CFSSearchPathsInit() -{ - m_pDirectoryName = NULL; - m_pLanguage = NULL; - m_ModPath[0] = 0; -} - - -CFSSteamSetupInfo::CFSSteamSetupInfo() -{ - m_pDirectoryName = NULL; - m_bOnlyUseDirectoryName = false; - m_bSteam = false; - m_bToolsMode = true; - m_bNoGameInfo = false; -} - - -CFSLoadModuleInfo::CFSLoadModuleInfo() -{ - m_pFileSystemDLLName = NULL; - m_pFileSystem = NULL; - m_pModule = NULL; -} - - -CFSMountContentInfo::CFSMountContentInfo() -{ - m_bToolsMode = true; - m_pDirectoryName = NULL; - m_pFileSystem = NULL; -} - - -const char *FileSystem_GetLastErrorString() -{ - return g_FileSystemError; -} - - -void AddLanguageGameDir( IFileSystem *pFileSystem, const char *pLocation, const char *pLanguage ) -{ -#if !defined(SWDS) && !defined(_XBOX) - char temp[MAX_PATH]; - Q_snprintf( temp, sizeof(temp), "%s_%s", pLocation, pLanguage ); - pFileSystem->AddSearchPath( temp, "GAME", PATH_ADD_TO_TAIL ); - - if ( !pFileSystem->IsSteam() ) - { - // also look in "..\localization\" if not running Steam - char baseDir[MAX_PATH]; - char *tempPtr = NULL, *gameDir = NULL; - - Q_strncpy( baseDir, pLocation, sizeof(baseDir) ); - tempPtr = Q_strstr( baseDir, "\\game\\" ); - - if ( tempPtr ) - { - gameDir = tempPtr + Q_strlen( "\\game\\" ); - *tempPtr = 0; - Q_snprintf( temp, sizeof(temp), "%s%clocalization%c%s_%s", baseDir, CORRECT_PATH_SEPARATOR, CORRECT_PATH_SEPARATOR, gameDir, pLanguage ); - pFileSystem->AddSearchPath( temp, "GAME", PATH_ADD_TO_TAIL ); - } - } -#endif -} - - -void AddGameBinDir( IFileSystem *pFileSystem, const char *pLocation ) -{ -#ifndef _XBOX - char temp[MAX_PATH]; - Q_snprintf( temp, sizeof(temp), "%s%cbin", pLocation, CORRECT_PATH_SEPARATOR ); - pFileSystem->AddSearchPath( temp, "GAMEBIN", PATH_ADD_TO_TAIL ); -#endif -} - -#ifndef _XBOX -KeyValues* ReadKeyValuesFile( const char *pFilename ) -{ - // Read in the gameinfo.txt file and null-terminate it. - FILE *fp = fopen( pFilename, "rb" ); - if ( !fp ) - return NULL; - CUtlVector buf; - fseek( fp, 0, SEEK_END ); - buf.SetSize( ftell( fp ) + 1 ); - fseek( fp, 0, SEEK_SET ); - fread( buf.Base(), 1, buf.Count()-1, fp ); - fclose( fp ); - buf[buf.Count()-1] = 0; - - KeyValues *kv = new KeyValues( "" ); - if ( !kv->LoadFromBuffer( pFilename, buf.Base() ) ) - { - kv->deleteThis(); - return NULL; - } - - return kv; -} -#endif - -static int Sys_GetExecutableName( char *out, int len ) -{ -#ifdef _WIN32 - if ( !::GetModuleFileName( ( HINSTANCE )GetModuleHandle( NULL ), out, len ) ) - { - return 0; - } -#else - if ( CommandLine()->GetParm(0) ) - { - Q_MakeAbsolutePath( out, len, CommandLine()->GetParm(0) ); - } - else - { - return 0; - } -#endif - return 1; -} - -bool FileSystem_GetExecutableDir( char *exedir, int exeDirLen ) -{ - exedir[ 0 ] = 0; - if ( !Sys_GetExecutableName( exedir, exeDirLen ) ) - return false; - - Q_StripFilename( exedir ); - Q_FixSlashes( exedir ); - - // Return the bin directory as the executable dir if it's not in there - // because that's really where we're running from... - char ext[MAX_PATH]; - Q_StrRight( exedir, 4, ext, sizeof( ext ) ); - if ( ext[0] != CORRECT_PATH_SEPARATOR || Q_stricmp( ext+1, "bin" ) != 0 ) - { - Q_strncat( exedir, "\\bin", exeDirLen, COPY_ALL_CHARACTERS ); - Q_FixSlashes( exedir ); - } - return true; -} - -#ifndef _XBOX -static bool FileSystem_GetBaseDir( char *baseDir, int baseDirLen ) -{ - if ( FileSystem_GetExecutableDir( baseDir, baseDirLen ) ) - { - Q_StripFilename( baseDir ); - return true; - } - else - { - return false; - } -} -#endif - -#ifndef _XBOX -void LaunchVConfig() -{ -#ifdef _WIN32 - char vconfigExe[MAX_PATH]; - FileSystem_GetExecutableDir( vconfigExe, sizeof( vconfigExe ) ); - Q_AppendSlash( vconfigExe, sizeof( vconfigExe ) ); - Q_strncat( vconfigExe, "vconfig.exe", sizeof( vconfigExe ), COPY_ALL_CHARACTERS ); - - char *argv[] = - { - vconfigExe, - "-allowdebug", - NULL - }; - - _spawnv( _P_NOWAIT, vconfigExe, argv ); -#endif -} -#endif - -#ifndef _XBOX -const char* GetVProjectCmdLineValue() -{ - return CommandLine()->ParmValue( "-vproject", CommandLine()->ParmValue( "-game" ) ); -} -#endif - -FSReturnCode_t SetupFileSystemError( bool bRunVConfig, FSReturnCode_t retVal, const char *pMsg, ... ) -{ - va_list marker; - va_start( marker, pMsg ); - Q_vsnprintf( g_FileSystemError, sizeof( g_FileSystemError ), pMsg, marker ); - va_end( marker ); - - Warning( "%s", g_FileSystemError ); - -#ifndef _XBOX - // Run vconfig? - // Don't do it if they specifically asked for it not to, or if they manually specified a vconfig with -game or -vproject. - if ( bRunVConfig && g_FileSystemErrorMode == FS_ERRORMODE_VCONFIG && !CommandLine()->FindParm( CMDLINEOPTION_NOVCONFIG ) && !GetVProjectCmdLineValue() ) - LaunchVConfig(); -#endif - - if ( g_FileSystemErrorMode == FS_ERRORMODE_AUTO || g_FileSystemErrorMode == FS_ERRORMODE_VCONFIG ) - Error( "%s\n", g_FileSystemError ); - - return retVal; -} - - -#ifndef _XBOX -FSReturnCode_t LoadGameInfoFile( - const char *pDirectoryName, - KeyValues *&pMainFile, - KeyValues *&pFileSystemInfo, - KeyValues *&pSearchPaths ) -{ - // If GameInfo.txt exists under pBaseDir, then this is their game directory. - // All the filesystem mappings will be in this file. - char gameinfoFilename[MAX_PATH]; - Q_strncpy( gameinfoFilename, pDirectoryName, sizeof( gameinfoFilename ) ); - Q_AppendSlash( gameinfoFilename, sizeof( gameinfoFilename ) ); - Q_strncat( gameinfoFilename, GAMEINFO_FILENAME, sizeof( gameinfoFilename ), COPY_ALL_CHARACTERS ); - Q_FixSlashes( gameinfoFilename ); - pMainFile = ReadKeyValuesFile( gameinfoFilename ); - if ( !pMainFile ) - return SetupFileSystemError( true, FS_MISSING_GAMEINFO_FILE, "%s is missing.", gameinfoFilename ); - - pFileSystemInfo = pMainFile->FindKey( "FileSystem" ); - if ( !pFileSystemInfo ) - { - pMainFile->deleteThis(); - return SetupFileSystemError( true, FS_INVALID_GAMEINFO_FILE, "%s is not a valid format.", gameinfoFilename ); - } - - // Now read in all the search paths. - pSearchPaths = pFileSystemInfo->FindKey( "SearchPaths" ); - if ( !pSearchPaths ) - { - pMainFile->deleteThis(); - return SetupFileSystemError( true, FS_INVALID_GAMEINFO_FILE, "%s is not a valid format.", gameinfoFilename ); - } - return FS_OK; -} -#endif - -// checks the registry for the low violence setting -// Check "HKEY_CURRENT_USER\Software\Valve\Source\Settings" and "User Token 2" or "User Token 3" -#ifndef _XBOX -bool IsLowViolenceBuild( void ) -{ -#if defined(_WIN32) - HKEY hKey; - char szValue[64]; - unsigned long len = sizeof(szValue) - 1; - bool retVal = false; - - if ( RegOpenKeyEx( HKEY_CURRENT_USER, "Software\\Valve\\Source\\Settings", NULL, KEY_READ, &hKey) == ERROR_SUCCESS ) - { - // User Token 2 - if ( RegQueryValueEx( hKey, "User Token 2", NULL, NULL, (unsigned char*)szValue, &len ) == ERROR_SUCCESS ) - { - if ( Q_strlen( szValue ) > 0 ) - { - retVal = true; - } - } - - if ( !retVal ) - { - // reset "len" for the next check - len = sizeof(szValue) - 1; - - // User Token 3 - if ( RegQueryValueEx( hKey, "User Token 3", NULL, NULL, (unsigned char*)szValue, &len ) == ERROR_SUCCESS ) - { - if ( Q_strlen( szValue ) > 0 ) - { - retVal = true; - } - } - } - - RegCloseKey(hKey); - } - - return retVal; -#elif _LINUX - return false; -#elif -#error "Fix me" -#endif -} -#endif - -FSReturnCode_t FileSystem_LoadSearchPaths( CFSSearchPathsInit &initInfo ) -{ -#ifndef _XBOX - bool bLowViolence = IsLowViolenceBuild(); - - if ( !initInfo.m_pFileSystem || !initInfo.m_pDirectoryName ) - return SetupFileSystemError( false, FS_INVALID_PARAMETERS, "FileSystem_LoadSearchPaths: Invalid parameters specified." ); - - KeyValues *pMainFile, *pFileSystemInfo, *pSearchPaths; - FSReturnCode_t retVal = LoadGameInfoFile( initInfo.m_pDirectoryName, pMainFile, pFileSystemInfo, pSearchPaths ); - if ( retVal != FS_OK ) - return retVal; - - // All paths except those marked with |gameinfo_path| are relative to the base dir. - char baseDir[MAX_PATH]; - if ( !FileSystem_GetBaseDir( baseDir, sizeof( baseDir ) ) ) - return SetupFileSystemError( false, FS_INVALID_PARAMETERS, "FileSystem_GetBaseDir failed." ); - - initInfo.m_ModPath[0] = 0; - - bool bFirstGamePath = true; - for ( KeyValues *pCur=pSearchPaths->GetFirstValue(); pCur; pCur=pCur->GetNextValue() ) - { - const char *pPathID = pCur->GetName(); - - char fullLocationPath[MAX_PATH]; - const char *pLocation = pCur->GetString(); - if ( Q_stristr( pLocation, "|gameinfo_path|" ) == pLocation ) - Q_MakeAbsolutePath( fullLocationPath, sizeof( fullLocationPath ), pLocation + strlen( "|gameinfo_path|" ), initInfo.m_pDirectoryName ); - else - Q_MakeAbsolutePath( fullLocationPath, sizeof( fullLocationPath ), pLocation, baseDir ); - - // Add language, mod, and gamebin search paths automatically. - if ( Q_stricmp( pPathID, "game" ) == 0 ) - { - // add the low violence path - if ( bLowViolence ) - { - char szPath[MAX_PATH]; - Q_snprintf( szPath, sizeof(szPath), "%s_lv", fullLocationPath ); - initInfo.m_pFileSystem->AddSearchPath( szPath, pPathID, PATH_ADD_TO_TAIL ); - } - - // add the language path - if ( initInfo.m_pLanguage ) - { - AddLanguageGameDir( initInfo.m_pFileSystem, fullLocationPath, initInfo.m_pLanguage ); - } - -#ifndef _XBOX - if ( CommandLine()->FindParm( "-tempcontent" ) != 0 ) - { - char szPath[MAX_PATH]; - Q_snprintf( szPath, sizeof(szPath), "%s_tempcontent", fullLocationPath ); - initInfo.m_pFileSystem->AddSearchPath( szPath, pPathID, PATH_ADD_TO_TAIL ); - } -#endif - - // mark the first "game" dir as the "MOD" dir - if ( bFirstGamePath ) - { - bFirstGamePath = false; - initInfo.m_pFileSystem->AddSearchPath( fullLocationPath, "MOD", PATH_ADD_TO_TAIL ); - Q_strncpy( initInfo.m_ModPath, fullLocationPath, sizeof( initInfo.m_ModPath ) ); - } - - // add the game bin - AddGameBinDir( initInfo.m_pFileSystem, fullLocationPath ); - } - - initInfo.m_pFileSystem->AddSearchPath( fullLocationPath, pPathID, PATH_ADD_TO_TAIL ); - } - - pMainFile->deleteThis(); - - // Also, mark specific path IDs as "by request only". That way, we won't waste time searching in them - // when people forget to specify a search path. - initInfo.m_pFileSystem->MarkPathIDByRequestOnly( "executable_path", true ); - initInfo.m_pFileSystem->MarkPathIDByRequestOnly( "gamebin", true ); - initInfo.m_pFileSystem->MarkPathIDByRequestOnly( "mod", true ); - if ( initInfo.m_ModPath[0] != 0 ) - { - // Add the write path last. - initInfo.m_pFileSystem->AddSearchPath( initInfo.m_ModPath, "DEFAULT_WRITE_PATH", PATH_ADD_TO_TAIL ); - } - -#ifdef _DEBUG - initInfo.m_pFileSystem->PrintSearchPaths(); -#endif - -#endif // _XBOX - return FS_OK; -} - -#ifndef _XBOX -bool DoesFileExistIn( const char *pDirectoryName, const char *pFilename ) -{ - char filename[MAX_PATH]; - Q_strncpy( filename, pDirectoryName, sizeof( filename ) ); - Q_AppendSlash( filename, sizeof( filename ) ); - Q_strncat( filename, pFilename, sizeof( filename ), COPY_ALL_CHARACTERS ); - Q_FixSlashes( filename ); - return ( _access( filename, 0 ) == 0 ); -} -#endif - -#ifndef _XBOX -FSReturnCode_t LocateGameInfoFile( const CFSSteamSetupInfo &fsInfo, char *pOutDir, int outDirLen ) -{ - // Engine and Hammer don't want to search around for it. - if ( fsInfo.m_bOnlyUseDirectoryName ) - { - if ( !fsInfo.m_pDirectoryName ) - return SetupFileSystemError( false, FS_MISSING_GAMEINFO_FILE, "bOnlyUseDirectoryName=1 and pDirectoryName=NULL." ); - - if ( !DoesFileExistIn( fsInfo.m_pDirectoryName, GAMEINFO_FILENAME ) ) - return SetupFileSystemError( true, FS_MISSING_GAMEINFO_FILE, "%s doesn't exist in %s.", GAMEINFO_FILENAME, fsInfo.m_pDirectoryName ); - - Q_strncpy( pOutDir, fsInfo.m_pDirectoryName, outDirLen ); - return FS_OK; - } - - // First, check for overrides on the command line or environment variables. - const char *pProject = GetVProjectCmdLineValue(); - if ( !pProject ) - { - // Check their registry. - pProject = getenv( GAMEDIR_TOKEN ); - } - - if ( pProject ) - { - if ( DoesFileExistIn( pProject, GAMEINFO_FILENAME ) ) - { - Q_MakeAbsolutePath( pOutDir, outDirLen, pProject ); - return FS_OK; - } - else if ( fsInfo.m_bNoGameInfo ) - { - // fsInfo.m_bNoGameInfo is set by the Steam dedicated server, before it knows which mod to use. - // Steam dedicated server doesn't need a gameinfo.txt, because we'll ask which mod to use, even if - // -game is supplied on the command line. - Q_strncpy( pOutDir, "", outDirLen ); - return FS_OK; - } - else - { - // They either specified vproject on the command line or it's in their registry. Either way, - // we don't want to continue if they've specified it but it's not valid. - goto ShowError; - } - } - - if ( fsInfo.m_bNoGameInfo ) - { - Q_strncpy( pOutDir, "", outDirLen ); - return FS_OK; - } - - Warning( "Warning: falling back to auto detection of vproject directory.\n" ); - - // Now look for it in the directory they passed in. - if ( fsInfo.m_pDirectoryName ) - Q_MakeAbsolutePath( pOutDir, outDirLen, fsInfo.m_pDirectoryName ); - else - Q_MakeAbsolutePath( pOutDir, outDirLen, "." ); - - do - { - if ( DoesFileExistIn( pOutDir, GAMEINFO_FILENAME ) ) - return FS_OK; - } while ( Q_StripLastDir( pOutDir, outDirLen ) ); - - // use the cwd and hunt down the tree until we find something - Q_getwd( pOutDir, outDirLen ); - do - { - if ( DoesFileExistIn( pOutDir, GAMEINFO_FILENAME ) ) - return FS_OK; - } while ( Q_StripLastDir( pOutDir, outDirLen ) ); - -ShowError: - return SetupFileSystemError( true, FS_MISSING_GAMEINFO_FILE, - "Unable to find %s. Solutions:\n\n" - "1. Read http://www.valve-erc.com/srcsdk/faq.html#NoGameDir\n" - "2. Run vconfig to specify which game you're working on.\n" - "3. Add -game on the command line where is the directory that %s is in.\n", - GAMEINFO_FILENAME, GAMEINFO_FILENAME ); -} -#endif - -#ifndef _XBOX -bool DoesPathExistAlready( const char *pPathEnvVar, const char *pTestPath ) -{ - // Fix the slashes in the input arguments. - char correctedPathEnvVar[8192], correctedTestPath[MAX_PATH]; - Q_strncpy( correctedPathEnvVar, pPathEnvVar, sizeof( correctedPathEnvVar ) ); - Q_FixSlashes( correctedPathEnvVar ); - pPathEnvVar = correctedPathEnvVar; - - Q_strncpy( correctedTestPath, pTestPath, sizeof( correctedTestPath ) ); - Q_FixSlashes( correctedTestPath ); - if ( strlen( correctedTestPath ) > 0 && PATHSEPARATOR( correctedTestPath[strlen(correctedTestPath)-1] ) ) - correctedTestPath[ strlen(correctedTestPath) - 1 ] = 0; - - pTestPath = correctedTestPath; - - const char *pCurPos = pPathEnvVar; - while ( 1 ) - { - const char *pTestPos = Q_stristr( pCurPos, pTestPath ); - if ( !pTestPos ) - return false; - - // Ok, we found pTestPath in the path, but it's only valid if it's followed by an optional slash and a semicolon. - pTestPos += strlen( pTestPath ); - if ( pTestPos[0] == 0 || pTestPos[0] == ';' || (PATHSEPARATOR( pTestPos[0] ) && pTestPos[1] == ';') ) - return true; - - // Advance our marker.. - pCurPos = pTestPos; - } -} -#endif - -#ifndef _XBOX -FSReturnCode_t SetSteamInstallPath( char *steamInstallPath, int steamInstallPathLen, CSteamEnvVars &steamEnvVars, bool bErrorsAsWarnings ) -{ - // Start at our bin directory and move up until we find a directory with steam.dll in it. - char executablePath[MAX_PATH]; - if ( !FileSystem_GetExecutableDir( executablePath, sizeof( executablePath ) ) ) - { - if ( bErrorsAsWarnings ) - { - Warning( "SetSteamInstallPath: FileSystem_GetExecutableDir failed." ); - return FS_INVALID_PARAMETERS; - } - else - { - return SetupFileSystemError( false, FS_INVALID_PARAMETERS, "FileSystem_GetExecutableDir failed." ); - } - } - - Q_strncpy( steamInstallPath, executablePath, steamInstallPathLen ); - while ( 1 ) - { - // Ignore steamapp.cfg here in case they're debugging. We still need to know the real steam path so we can find their username. - // find - if ( DoesFileExistIn( steamInstallPath, "steam.dll" ) && !DoesFileExistIn( steamInstallPath, "steamapp.cfg" ) ) - break; - - if ( !Q_StripLastDir( steamInstallPath, steamInstallPathLen ) ) - { - if ( bErrorsAsWarnings ) - { - Warning( "Can't find steam.dll relative to executable path: %s.", executablePath ); - return FS_MISSING_STEAM_DLL; - } - else - { - return SetupFileSystemError( false, FS_MISSING_STEAM_DLL, "Can't find steam.dll relative to executable path: %s.", executablePath ); - } - } - } - - // Also, add the install path to their PATH environment variable, so filesystem_steam.dll can get to steam.dll. - char szPath[ 8192 ]; - steamEnvVars.m_Path.GetValue( szPath, sizeof( szPath ) ); - if ( !DoesPathExistAlready( szPath, steamInstallPath ) ) - { - steamEnvVars.m_Path.SetValue( "%s;%s", szPath, steamInstallPath ); - } - return FS_OK; -} -#endif - -#ifndef _XBOX -FSReturnCode_t GetSteamCfgPath( char *steamCfgPath, int steamCfgPathLen ) -{ - steamCfgPath[0] = 0; - char executablePath[MAX_PATH]; - if ( !FileSystem_GetExecutableDir( executablePath, sizeof( executablePath ) ) ) - { - return SetupFileSystemError( false, FS_INVALID_PARAMETERS, "FileSystem_GetExecutableDir failed." ); - } - Q_strncpy( steamCfgPath, executablePath, steamCfgPathLen ); - while ( 1 ) - { - if ( DoesFileExistIn( steamCfgPath, "steam.cfg" ) ) - break; - - if ( !Q_StripLastDir( steamCfgPath, steamCfgPathLen) ) - { - // the file isnt found, thats ok, its not mandatory - return FS_OK; - } - } - Q_AppendSlash( steamCfgPath, steamCfgPathLen ); - Q_strncat( steamCfgPath, "steam.cfg", steamCfgPathLen, COPY_ALL_CHARACTERS ); - - return FS_OK; -} -#endif - -#ifndef _XBOX -void SetSteamAppUser( KeyValues *pSteamInfo, const char *steamInstallPath, CSteamEnvVars &steamEnvVars ) -{ - // Always inherit the Steam user if it's already set, since it probably means we (or the - // the app that launched us) were launched from Steam. - char appUser[MAX_PATH]; - if ( steamEnvVars.m_SteamAppUser.GetValue( appUser, sizeof( appUser ) ) ) - return; - - const char *pTempAppUser = NULL; - if ( pSteamInfo && (pTempAppUser = pSteamInfo->GetString( "SteamAppUser", NULL )) != NULL ) - { - Q_strncpy( appUser, pTempAppUser, sizeof( appUser ) ); - } - else - { - // They don't have SteamInfo.txt, or it's missing SteamAppUser. Try to figure out the user - // by looking in \config\SteamAppData.vdf. - char fullFilename[MAX_PATH]; - Q_strncpy( fullFilename, steamInstallPath, sizeof( fullFilename ) ); - Q_AppendSlash( fullFilename, sizeof( fullFilename ) ); - Q_strncat( fullFilename, "config\\SteamAppData.vdf", sizeof( fullFilename ), COPY_ALL_CHARACTERS ); - - KeyValues *pSteamAppData = ReadKeyValuesFile( fullFilename ); - if ( !pSteamAppData || (pTempAppUser = pSteamAppData->GetString( "AutoLoginUser", NULL )) == NULL ) - { - Error( "Can't find steam app user info." ); - } - Q_strncpy( appUser, pTempAppUser, sizeof( appUser ) ); - - pSteamAppData->deleteThis(); - } - - Q_strlower( appUser ); - steamEnvVars.m_SteamAppUser.SetValue( "%s", appUser ); -} -#endif - -#ifndef _XBOX -void SetSteamUserPassphrase( KeyValues *pSteamInfo, CSteamEnvVars &steamEnvVars ) -{ - // Always inherit the passphrase if it's already set, since it probably means we (or the - // the app that launched us) were launched from Steam. - char szPassPhrase[ MAX_PATH ]; - if ( steamEnvVars.m_SteamUserPassphrase.GetValue( szPassPhrase, sizeof( szPassPhrase ) ) ) - return; - - // SteamUserPassphrase. - const char *pStr; - if ( pSteamInfo && (pStr = pSteamInfo->GetString( "SteamUserPassphrase", NULL )) != NULL ) - { - steamEnvVars.m_SteamUserPassphrase.SetValue( "%s", pStr ); - } -} -#endif - -#ifndef _XBOX -void SetSteamAppId( KeyValues *pFileSystemInfo, const char *pGameInfoDirectory, CSteamEnvVars &steamEnvVars ) -{ - // SteamAppId is in gameinfo.txt->FileSystem->FileSystemInfo_Steam->SteamAppId. - int iAppId = pFileSystemInfo->GetInt( "SteamAppId", -1 ); - if ( iAppId == -1 ) - Error( "Missing SteamAppId in %s\\%s.", pGameInfoDirectory, GAMEINFO_FILENAME ); - - steamEnvVars.m_SteamAppId.SetValue( "%d", iAppId ); -} -#endif - -#ifndef _XBOX -FSReturnCode_t SetupSteamStartupEnvironment( KeyValues *pFileSystemInfo, const char *pGameInfoDirectory, CSteamEnvVars &steamEnvVars ) -{ - // Ok, we're going to run Steam. See if they have SteamInfo.txt. If not, we'll try to deduce what we can. - char steamInfoFile[MAX_PATH]; - Q_strncpy( steamInfoFile, pGameInfoDirectory, sizeof( steamInfoFile ) ); - Q_AppendSlash( steamInfoFile, sizeof( steamInfoFile ) ); - Q_strncat( steamInfoFile, "steaminfo.txt", sizeof( steamInfoFile ), COPY_ALL_CHARACTERS ); - KeyValues *pSteamInfo = ReadKeyValuesFile( steamInfoFile ); - - char steamInstallPath[MAX_PATH]; - FSReturnCode_t ret = SetSteamInstallPath( steamInstallPath, sizeof( steamInstallPath ), steamEnvVars, false ); - if ( ret != FS_OK ) - return ret; - - SetSteamAppUser( pSteamInfo, steamInstallPath, steamEnvVars ); - SetSteamUserPassphrase( pSteamInfo, steamEnvVars ); - SetSteamAppId( pFileSystemInfo, pGameInfoDirectory, steamEnvVars ); - - if ( pSteamInfo ) - pSteamInfo->deleteThis(); - - return FS_OK; -} -#endif - -#ifndef _XBOX -FSReturnCode_t GetSteamExtraAppId( const char *pDirectoryName, int *nExtraAppId ) -{ - // Now, load gameinfo.txt (to make sure it's there) - KeyValues *pMainFile, *pFileSystemInfo, *pSearchPaths; - FSReturnCode_t ret = LoadGameInfoFile( pDirectoryName, pMainFile, pFileSystemInfo, pSearchPaths ); - if ( ret != FS_OK ) - return ret; - - *nExtraAppId = pFileSystemInfo->GetInt( "ToolsAppId", -1 ); - pMainFile->deleteThis(); - return FS_OK; -} -#endif - -#ifndef _XBOX -FSReturnCode_t FileSystem_SetBasePaths( IFileSystem *pFileSystem ) -{ - pFileSystem->RemoveSearchPaths( "EXECUTABLE_PATH" ); - - char executablePath[MAX_PATH]; - if ( !FileSystem_GetExecutableDir( executablePath, sizeof( executablePath ) ) ) - return SetupFileSystemError( false, FS_INVALID_PARAMETERS, "FileSystem_GetExecutableDir failed." ); - - pFileSystem->AddSearchPath( executablePath, "EXECUTABLE_PATH" ); - return FS_OK; -} -#endif - -//----------------------------------------------------------------------------- -// Returns the name of the file system DLL to use -//----------------------------------------------------------------------------- -FSReturnCode_t FileSystem_GetFileSystemDLLName( char *pFileSystemDLL, int nMaxLen, bool &bSteam ) -{ - bSteam = false; - - // Inside of here, we don't have a filesystem yet, so we have to assume that the filesystem_stdio or filesystem_steam - // is in this same directory with us. - char executablePath[MAX_PATH]; - if ( !FileSystem_GetExecutableDir( executablePath, sizeof( executablePath ) ) ) - return SetupFileSystemError( false, FS_INVALID_PARAMETERS, "FileSystem_GetExecutableDir failed." ); - -#ifdef _WIN32 - Q_snprintf( pFileSystemDLL, nMaxLen, "%s%cfilesystem_stdio.dll", executablePath, CORRECT_PATH_SEPARATOR ); - - // If filesystem_stdio.dll is missing or -steam is specified, then load filesystem_steam.dll. - // There are two command line parameters for Steam: - // 1) -steam (runs Steam in remote filesystem mode; requires Steam backend) - // 2) -steamlocal (runs Steam in local filesystem mode (all content off HDD) -#ifndef _XBOX - if ( CommandLine()->FindParm( "-steam" ) || CommandLine()->FindParm( "-steamlocal" ) || _access( pFileSystemDLL, 0 ) != 0 ) - { - Q_snprintf( pFileSystemDLL, nMaxLen, "%s%cfilesystem_steam.dll", executablePath, CORRECT_PATH_SEPARATOR ); - bSteam = true; - } -#endif -#elif _LINUX - Q_snprintf( pFileSystemDLL, nMaxLen, "%s%cfilesystem_i486.so", executablePath, CORRECT_PATH_SEPARATOR ); -#else - #error "define a filesystem dll name" -#endif - - return FS_OK; -} - -//----------------------------------------------------------------------------- -// Sets up the steam.dll install path in our PATH env var (so you can then just -// LoadLibrary() on filesystem_steam.dll without having to copy steam.dll anywhere special ) -//----------------------------------------------------------------------------- -FSReturnCode_t FileSystem_SetupSteamInstallPath() -{ -#ifndef _XBOX - CSteamEnvVars steamEnvVars; - char steamInstallPath[MAX_PATH]; - FSReturnCode_t ret = SetSteamInstallPath( steamInstallPath, sizeof( steamInstallPath ), steamEnvVars, true ); - steamEnvVars.m_Path.SetRestoreOriginalValue( false ); // We want to keep the change to the path going forward. - return ret; -#else - return FS_OK; -#endif -} - -//----------------------------------------------------------------------------- -// Sets up the steam environment + gets back the gameinfo.txt path -//----------------------------------------------------------------------------- -FSReturnCode_t FileSystem_SetupSteamEnvironment( CFSSteamSetupInfo &fsInfo ) -{ -#ifndef _XBOX - // First, locate the directory with gameinfo.txt. - FSReturnCode_t ret = LocateGameInfoFile( fsInfo, fsInfo.m_GameInfoPath, sizeof( fsInfo.m_GameInfoPath ) ); - if ( ret != FS_OK ) - return ret; - - CSteamEnvVars steamEnvVars; - if ( fsInfo.m_bSteam ) - { - if ( fsInfo.m_bToolsMode ) - { - // Now, load gameinfo.txt (to make sure it's there) - KeyValues *pMainFile, *pFileSystemInfo, *pSearchPaths; - ret = LoadGameInfoFile( fsInfo.m_GameInfoPath, pMainFile, pFileSystemInfo, pSearchPaths ); - if ( ret != FS_OK ) - return ret; - - // If filesystem_stdio.dll is missing or -steam is specified, then load filesystem_steam.dll. - // There are two command line parameters for Steam: - // 1) -steam (runs Steam in remote filesystem mode; requires Steam backend) - // 2) -steamlocal (runs Steam in local filesystem mode (all content off HDD) - - // Setup all the environment variables related to Steam so filesystem_steam.dll knows how to initialize Steam. - ret = SetupSteamStartupEnvironment( pFileSystemInfo, fsInfo.m_GameInfoPath, steamEnvVars ); - if ( ret != FS_OK ) - return ret; - - steamEnvVars.m_SteamAppId.SetRestoreOriginalValue( false ); // We want to keep the change to the path going forward. - - // We're done with main file - pMainFile->deleteThis(); - } - else if ( fsInfo.m_bSetSteamDLLPath ) - { - // This is used by the engine to automatically set the path to their steam.dll when running the engine, - // so they can debug it without having to copy steam.dll up into their hl2.exe folder. - char steamInstallPath[MAX_PATH]; - ret = SetSteamInstallPath( steamInstallPath, sizeof( steamInstallPath ), steamEnvVars, true ); - steamEnvVars.m_Path.SetRestoreOriginalValue( false ); // We want to keep the change to the path going forward. - } - } -#endif - - return FS_OK; -} - - -//----------------------------------------------------------------------------- -// Loads the file system module -//----------------------------------------------------------------------------- -FSReturnCode_t FileSystem_LoadFileSystemModule( CFSLoadModuleInfo &fsInfo ) -{ - // First, locate the directory with gameinfo.txt. - FSReturnCode_t ret = FileSystem_SetupSteamEnvironment( fsInfo ); - if ( ret != FS_OK ) - return ret; - - // Now that the environment is setup, load the filesystem module. - if ( !Sys_LoadInterface( - fsInfo.m_pFileSystemDLLName, - FILESYSTEM_INTERFACE_VERSION, - &fsInfo.m_pModule, - (void**)&fsInfo.m_pFileSystem ) ) - { - return SetupFileSystemError( false, FS_UNABLE_TO_INIT, "Can't load %s.", fsInfo.m_pFileSystemDLLName ); - } - - if ( !fsInfo.m_pFileSystem->Connect( fsInfo.m_ConnectFactory ) ) - return SetupFileSystemError( false, FS_UNABLE_TO_INIT, "%s IFileSystem::Connect failed.", fsInfo.m_pFileSystemDLLName ); - - if ( fsInfo.m_pFileSystem->Init() != INIT_OK ) - return SetupFileSystemError( false, FS_UNABLE_TO_INIT, "%s IFileSystem::Init failed.", fsInfo.m_pFileSystemDLLName ); - - return FS_OK; -} - - -//----------------------------------------------------------------------------- -// Mounds a particular steam cache -//----------------------------------------------------------------------------- -#ifndef _XBOX -FSReturnCode_t FileSystem_MountContent( CFSMountContentInfo &mountContentInfo ) -{ - // This part is Steam-only. - if ( mountContentInfo.m_pFileSystem->IsSteam() ) - { - // Find out the "extra app id". This is for tools, which want to mount a base app's filesystem - // like HL2, then mount the SDK content (tools materials and models, etc) in addition. - int nExtraAppId = -1; - if ( mountContentInfo.m_bToolsMode ) - { - FSReturnCode_t ret = GetSteamExtraAppId( mountContentInfo.m_pDirectoryName, &nExtraAppId ); - if ( ret != FS_OK ) - return ret; - } - - // Set our working directory temporarily so Steam can remember it. - // This is what Steam strips off absolute filenames like c:\program files\valve\steam\steamapps\username\sourcesdk - // to get to the relative part of the path. - char baseDir[MAX_PATH], oldWorkingDir[MAX_PATH]; - if ( !FileSystem_GetBaseDir( baseDir, sizeof( baseDir ) ) ) - return SetupFileSystemError( false, FS_INVALID_PARAMETERS, "FileSystem_GetBaseDir failed." ); - - Q_getwd( oldWorkingDir, sizeof( oldWorkingDir ) ); - _chdir( baseDir ); - - // Filesystem_tools needs to add dependencies in here beforehand. - FilesystemMountRetval_t retVal = mountContentInfo.m_pFileSystem->MountSteamContent( nExtraAppId ); - - _chdir( oldWorkingDir ); - - if ( retVal != FILESYSTEM_MOUNT_OK ) - return SetupFileSystemError( true, FS_UNABLE_TO_INIT, "Unable to mount Steam content in the file system" ); - } - - return FileSystem_SetBasePaths( mountContentInfo.m_pFileSystem ); -} -#endif - -void FileSystem_SetErrorMode( FSErrorMode_t errorMode ) -{ - g_FileSystemErrorMode = errorMode; -} - -#ifndef _XBOX -void FileSystem_ClearSteamEnvVars() -{ - CSteamEnvVars envVars; - - // Change the values and don't restore the originals. - envVars.m_SteamAppId.SetValue( "" ); - envVars.m_SteamUserPassphrase.SetValue( "" ); - envVars.m_SteamAppUser.SetValue( "" ); - - envVars.SetRestoreOriginalValue_ALL( false ); -} -#endif - -#endif // !_STATIC_LINKED || _SHARED_LIB - - -//----------------------------------------------------------------------------- -// Adds the platform folder to the search path. -//----------------------------------------------------------------------------- -void FileSystem_AddSearchPath_Platform( IFileSystem *pFileSystem, const char *szGameInfoPath ) -{ - char platform[MAX_PATH]; - if ( pFileSystem->IsSteam() ) - { - // Steam doesn't support relative paths - Q_strncpy( platform, "platform", MAX_PATH ); - } - else - { - Q_strncpy( platform, szGameInfoPath, MAX_PATH ); - Q_StripTrailingSlash( platform ); - Q_strncat( platform, "/../platform", MAX_PATH, MAX_PATH ); - } - - pFileSystem->AddSearchPath( platform, "PLATFORM" ); -} +//====== Copyright © 1996-2004, Valve Corporation, All rights reserved. ======= +// +// Purpose: +// +//============================================================================= + +#if !defined(_STATIC_LINKED) || defined(_SHARED_LIB) + +#undef PROTECTED_THINGS_ENABLE +#undef PROTECT_FILEIO_FUNCTIONS +#undef fopen + +#ifdef _XBOX +#include "xbox/xbox_platform.h" +#include "xbox/xbox_win32stubs.h" +#include "xbox/xbox_core.h" +#endif +#if defined(_WIN32) && !defined(_XBOX) +#include +#include +#include // _chmod +#include +#elif _LINUX +#include +#define _putenv putenv +#define _chdir chdir +#define _access access +#endif +#include +#include +#include "vstdlib/strtools.h" +#include "filesystem_init.h" +#include "vstdlib/ICommandLine.h" +#include "KeyValues.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include + +#define GAMEINFO_FILENAME "gameinfo.txt" + +static char g_FileSystemError[256]; +static FSErrorMode_t g_FileSystemErrorMode = FS_ERRORMODE_VCONFIG; + +// This class lets you modify environment variables, and it restores the original value +// when it goes out of scope. +#ifndef _XBOX +class CTempEnvVar +{ +public: + CTempEnvVar( const char *pVarName ) + { + m_bRestoreOriginalValue = true; + m_pVarName = pVarName; + + const char *pValue = NULL; + +#ifdef _WIN32 + // Use GetEnvironmentVariable instead of getenv because getenv doesn't pick up changes + // to the process environment after the DLL was loaded. + char szBuf[ 4096 ]; + if ( GetEnvironmentVariable( m_pVarName, szBuf, sizeof( szBuf ) ) != 0) + { + pValue = szBuf; + } +#else + // LINUX BUG: see above + pValue = getenv( pVarName ); +#endif // _WIN32 + + if ( pValue ) + { + m_bExisted = true; + m_OriginalValue.SetSize( strlen( pValue ) + 1 ); + memcpy( m_OriginalValue.Base(), pValue, m_OriginalValue.Count() ); + } + else + { + m_bExisted = false; + } + } + + ~CTempEnvVar() + { + if ( m_bRestoreOriginalValue ) + { + // Restore the original value. + if ( m_bExisted ) + { + SetValue( "%s", m_OriginalValue.Base() ); + } + else + { + ClearValue(); + } + } + } + + void SetRestoreOriginalValue( bool bRestore ) + { + m_bRestoreOriginalValue = bRestore; + } + + int GetValue(char *pszBuf, int nBufSize ) + { + if ( !pszBuf || ( nBufSize <= 0 ) ) + return 0; + +#ifdef _WIN32 + // Use GetEnvironmentVariable instead of getenv because getenv doesn't pick up changes + // to the process environment after the DLL was loaded. + return GetEnvironmentVariable( m_pVarName, pszBuf, nBufSize ); +#else + // LINUX BUG: see above + const char *pszOut = getenv( m_pVarName ); + if ( !pszOut ) + { + *pszBuf = '\0'; + return 0; + } + + Q_strncpy( pszBuf, pszOut, nBufSize ); + return Q_strlen( pszBuf ); +#endif // _WIN32 + } + + void SetValue( const char *pValue, ... ) + { + char valueString[4096]; + va_list marker; + va_start( marker, pValue ); + Q_vsnprintf( valueString, sizeof( valueString ), pValue, marker ); + va_end( marker ); + + char str[4096]; + Q_snprintf( str, sizeof( str ), "%s=%s", m_pVarName, valueString ); + _putenv( str ); + } + + void ClearValue() + { + char str[512]; + Q_snprintf( str, sizeof( str ), "%s=", m_pVarName ); + _putenv( str ); + } + +private: + bool m_bRestoreOriginalValue; + const char *m_pVarName; + bool m_bExisted; + CUtlVector m_OriginalValue; +}; + + +class CSteamEnvVars +{ +public: + CSteamEnvVars() : + m_SteamAppId( "SteamAppId" ), + m_SteamUserPassphrase( "SteamUserPassphrase" ), + m_SteamAppUser( "SteamAppUser" ), + m_Path( "path" ) + { + } + + void SetRestoreOriginalValue_ALL( bool bRestore ) + { + m_SteamAppId.SetRestoreOriginalValue( bRestore ); + m_SteamUserPassphrase.SetRestoreOriginalValue( bRestore ); + m_SteamAppUser.SetRestoreOriginalValue( bRestore ); + m_Path.SetRestoreOriginalValue( bRestore ); + } + + CTempEnvVar m_SteamAppId; + CTempEnvVar m_SteamUserPassphrase; + CTempEnvVar m_SteamAppUser; + CTempEnvVar m_Path; +}; +#endif + + + +// ---------------------------------------------------------------------------------------------------- // +// Helpers. +// ---------------------------------------------------------------------------------------------------- // +#ifndef _XBOX +void Q_getwd( char *out, int outSize ) +{ +#if defined( _WIN32 ) || defined( WIN32 ) + _getcwd( out, outSize ); + Q_strncat( out, "\\", outSize, COPY_ALL_CHARACTERS ); +#else + getcwd(out, outSize); + strcat(out, "/"); +#endif + Q_FixSlashes( out ); +} +#endif + +// ---------------------------------------------------------------------------------------------------- // +// Module interface. +// ---------------------------------------------------------------------------------------------------- // + +CFSSearchPathsInit::CFSSearchPathsInit() +{ + m_pDirectoryName = NULL; + m_pLanguage = NULL; + m_ModPath[0] = 0; +} + + +CFSSteamSetupInfo::CFSSteamSetupInfo() +{ + m_pDirectoryName = NULL; + m_bOnlyUseDirectoryName = false; + m_bSteam = false; + m_bToolsMode = true; + m_bNoGameInfo = false; +} + + +CFSLoadModuleInfo::CFSLoadModuleInfo() +{ + m_pFileSystemDLLName = NULL; + m_pFileSystem = NULL; + m_pModule = NULL; +} + + +CFSMountContentInfo::CFSMountContentInfo() +{ + m_bToolsMode = true; + m_pDirectoryName = NULL; + m_pFileSystem = NULL; +} + + +const char *FileSystem_GetLastErrorString() +{ + return g_FileSystemError; +} + + +void AddLanguageGameDir( IFileSystem *pFileSystem, const char *pLocation, const char *pLanguage ) +{ +#if !defined(SWDS) && !defined(_XBOX) + char temp[MAX_PATH]; + Q_snprintf( temp, sizeof(temp), "%s_%s", pLocation, pLanguage ); + pFileSystem->AddSearchPath( temp, "GAME", PATH_ADD_TO_TAIL ); + + if ( !pFileSystem->IsSteam() ) + { + // also look in "..\localization\" if not running Steam + char baseDir[MAX_PATH]; + char *tempPtr = NULL, *gameDir = NULL; + + Q_strncpy( baseDir, pLocation, sizeof(baseDir) ); + tempPtr = Q_strstr( baseDir, "\\game\\" ); + + if ( tempPtr ) + { + gameDir = tempPtr + Q_strlen( "\\game\\" ); + *tempPtr = 0; + Q_snprintf( temp, sizeof(temp), "%s%clocalization%c%s_%s", baseDir, CORRECT_PATH_SEPARATOR, CORRECT_PATH_SEPARATOR, gameDir, pLanguage ); + pFileSystem->AddSearchPath( temp, "GAME", PATH_ADD_TO_TAIL ); + } + } +#endif +} + + +void AddGameBinDir( IFileSystem *pFileSystem, const char *pLocation ) +{ +#ifndef _XBOX + char temp[MAX_PATH]; + Q_snprintf( temp, sizeof(temp), "%s%cbin", pLocation, CORRECT_PATH_SEPARATOR ); + pFileSystem->AddSearchPath( temp, "GAMEBIN", PATH_ADD_TO_TAIL ); +#endif +} + +#ifndef _XBOX +KeyValues* ReadKeyValuesFile( const char *pFilename ) +{ + // Read in the gameinfo.txt file and null-terminate it. + FILE *fp = fopen( pFilename, "rb" ); + if ( !fp ) + return NULL; + CUtlVector buf; + fseek( fp, 0, SEEK_END ); + buf.SetSize( ftell( fp ) + 1 ); + fseek( fp, 0, SEEK_SET ); + fread( buf.Base(), 1, buf.Count()-1, fp ); + fclose( fp ); + buf[buf.Count()-1] = 0; + + KeyValues *kv = new KeyValues( "" ); + if ( !kv->LoadFromBuffer( pFilename, buf.Base() ) ) + { + kv->deleteThis(); + return NULL; + } + + return kv; +} +#endif + +static int Sys_GetExecutableName( char *out, int len ) +{ +#ifdef _WIN32 + if ( !::GetModuleFileName( ( HINSTANCE )GetModuleHandle( NULL ), out, len ) ) + { + return 0; + } +#else + if ( CommandLine()->GetParm(0) ) + { + Q_MakeAbsolutePath( out, len, CommandLine()->GetParm(0) ); + } + else + { + return 0; + } +#endif + return 1; +} + +bool FileSystem_GetExecutableDir( char *exedir, int exeDirLen ) +{ + exedir[ 0 ] = 0; + if ( !Sys_GetExecutableName( exedir, exeDirLen ) ) + return false; + + Q_StripFilename( exedir ); + Q_FixSlashes( exedir ); + + // Return the bin directory as the executable dir if it's not in there + // because that's really where we're running from... + char ext[MAX_PATH]; + Q_StrRight( exedir, 4, ext, sizeof( ext ) ); + if ( ext[0] != CORRECT_PATH_SEPARATOR || Q_stricmp( ext+1, "bin" ) != 0 ) + { + Q_strncat( exedir, "\\bin", exeDirLen, COPY_ALL_CHARACTERS ); + Q_FixSlashes( exedir ); + } + return true; +} + +#ifndef _XBOX +static bool FileSystem_GetBaseDir( char *baseDir, int baseDirLen ) +{ + if ( FileSystem_GetExecutableDir( baseDir, baseDirLen ) ) + { + Q_StripFilename( baseDir ); + return true; + } + else + { + return false; + } +} +#endif + +#ifndef _XBOX +void LaunchVConfig() +{ +#ifdef _WIN32 + char vconfigExe[MAX_PATH]; + FileSystem_GetExecutableDir( vconfigExe, sizeof( vconfigExe ) ); + Q_AppendSlash( vconfigExe, sizeof( vconfigExe ) ); + Q_strncat( vconfigExe, "vconfig.exe", sizeof( vconfigExe ), COPY_ALL_CHARACTERS ); + + char *argv[] = + { + vconfigExe, + "-allowdebug", + NULL + }; + + _spawnv( _P_NOWAIT, vconfigExe, argv ); +#endif +} +#endif + +#ifndef _XBOX +const char* GetVProjectCmdLineValue() +{ + return CommandLine()->ParmValue( "-vproject", CommandLine()->ParmValue( "-game" ) ); +} +#endif + +FSReturnCode_t SetupFileSystemError( bool bRunVConfig, FSReturnCode_t retVal, const char *pMsg, ... ) +{ + va_list marker; + va_start( marker, pMsg ); + Q_vsnprintf( g_FileSystemError, sizeof( g_FileSystemError ), pMsg, marker ); + va_end( marker ); + + Warning( "%s", g_FileSystemError ); + +#ifndef _XBOX + // Run vconfig? + // Don't do it if they specifically asked for it not to, or if they manually specified a vconfig with -game or -vproject. + if ( bRunVConfig && g_FileSystemErrorMode == FS_ERRORMODE_VCONFIG && !CommandLine()->FindParm( CMDLINEOPTION_NOVCONFIG ) && !GetVProjectCmdLineValue() ) + LaunchVConfig(); +#endif + + if ( g_FileSystemErrorMode == FS_ERRORMODE_AUTO || g_FileSystemErrorMode == FS_ERRORMODE_VCONFIG ) + Error( "%s\n", g_FileSystemError ); + + return retVal; +} + + +#ifndef _XBOX +FSReturnCode_t LoadGameInfoFile( + const char *pDirectoryName, + KeyValues *&pMainFile, + KeyValues *&pFileSystemInfo, + KeyValues *&pSearchPaths ) +{ + // If GameInfo.txt exists under pBaseDir, then this is their game directory. + // All the filesystem mappings will be in this file. + char gameinfoFilename[MAX_PATH]; + Q_strncpy( gameinfoFilename, pDirectoryName, sizeof( gameinfoFilename ) ); + Q_AppendSlash( gameinfoFilename, sizeof( gameinfoFilename ) ); + Q_strncat( gameinfoFilename, GAMEINFO_FILENAME, sizeof( gameinfoFilename ), COPY_ALL_CHARACTERS ); + Q_FixSlashes( gameinfoFilename ); + pMainFile = ReadKeyValuesFile( gameinfoFilename ); + if ( !pMainFile ) + return SetupFileSystemError( true, FS_MISSING_GAMEINFO_FILE, "%s is missing.", gameinfoFilename ); + + pFileSystemInfo = pMainFile->FindKey( "FileSystem" ); + if ( !pFileSystemInfo ) + { + pMainFile->deleteThis(); + return SetupFileSystemError( true, FS_INVALID_GAMEINFO_FILE, "%s is not a valid format.", gameinfoFilename ); + } + + // Now read in all the search paths. + pSearchPaths = pFileSystemInfo->FindKey( "SearchPaths" ); + if ( !pSearchPaths ) + { + pMainFile->deleteThis(); + return SetupFileSystemError( true, FS_INVALID_GAMEINFO_FILE, "%s is not a valid format.", gameinfoFilename ); + } + return FS_OK; +} +#endif + +// checks the registry for the low violence setting +// Check "HKEY_CURRENT_USER\Software\Valve\Source\Settings" and "User Token 2" or "User Token 3" +#ifndef _XBOX +bool IsLowViolenceBuild( void ) +{ +#if defined(_WIN32) + HKEY hKey; + char szValue[64]; + unsigned long len = sizeof(szValue) - 1; + bool retVal = false; + + if ( RegOpenKeyEx( HKEY_CURRENT_USER, "Software\\Valve\\Source\\Settings", NULL, KEY_READ, &hKey) == ERROR_SUCCESS ) + { + // User Token 2 + if ( RegQueryValueEx( hKey, "User Token 2", NULL, NULL, (unsigned char*)szValue, &len ) == ERROR_SUCCESS ) + { + if ( Q_strlen( szValue ) > 0 ) + { + retVal = true; + } + } + + if ( !retVal ) + { + // reset "len" for the next check + len = sizeof(szValue) - 1; + + // User Token 3 + if ( RegQueryValueEx( hKey, "User Token 3", NULL, NULL, (unsigned char*)szValue, &len ) == ERROR_SUCCESS ) + { + if ( Q_strlen( szValue ) > 0 ) + { + retVal = true; + } + } + } + + RegCloseKey(hKey); + } + + return retVal; +#elif _LINUX + return false; +#elif +#error "Fix me" +#endif +} +#endif + +FSReturnCode_t FileSystem_LoadSearchPaths( CFSSearchPathsInit &initInfo ) +{ +#ifndef _XBOX + bool bLowViolence = IsLowViolenceBuild(); + + if ( !initInfo.m_pFileSystem || !initInfo.m_pDirectoryName ) + return SetupFileSystemError( false, FS_INVALID_PARAMETERS, "FileSystem_LoadSearchPaths: Invalid parameters specified." ); + + KeyValues *pMainFile, *pFileSystemInfo, *pSearchPaths; + FSReturnCode_t retVal = LoadGameInfoFile( initInfo.m_pDirectoryName, pMainFile, pFileSystemInfo, pSearchPaths ); + if ( retVal != FS_OK ) + return retVal; + + // All paths except those marked with |gameinfo_path| are relative to the base dir. + char baseDir[MAX_PATH]; + if ( !FileSystem_GetBaseDir( baseDir, sizeof( baseDir ) ) ) + return SetupFileSystemError( false, FS_INVALID_PARAMETERS, "FileSystem_GetBaseDir failed." ); + + initInfo.m_ModPath[0] = 0; + + bool bFirstGamePath = true; + for ( KeyValues *pCur=pSearchPaths->GetFirstValue(); pCur; pCur=pCur->GetNextValue() ) + { + const char *pPathID = pCur->GetName(); + + char fullLocationPath[MAX_PATH]; + const char *pLocation = pCur->GetString(); + if ( Q_stristr( pLocation, "|gameinfo_path|" ) == pLocation ) + Q_MakeAbsolutePath( fullLocationPath, sizeof( fullLocationPath ), pLocation + strlen( "|gameinfo_path|" ), initInfo.m_pDirectoryName ); + else + Q_MakeAbsolutePath( fullLocationPath, sizeof( fullLocationPath ), pLocation, baseDir ); + + // Add language, mod, and gamebin search paths automatically. + if ( Q_stricmp( pPathID, "game" ) == 0 ) + { + // add the low violence path + if ( bLowViolence ) + { + char szPath[MAX_PATH]; + Q_snprintf( szPath, sizeof(szPath), "%s_lv", fullLocationPath ); + initInfo.m_pFileSystem->AddSearchPath( szPath, pPathID, PATH_ADD_TO_TAIL ); + } + + // add the language path + if ( initInfo.m_pLanguage ) + { + AddLanguageGameDir( initInfo.m_pFileSystem, fullLocationPath, initInfo.m_pLanguage ); + } + +#ifndef _XBOX + if ( CommandLine()->FindParm( "-tempcontent" ) != 0 ) + { + char szPath[MAX_PATH]; + Q_snprintf( szPath, sizeof(szPath), "%s_tempcontent", fullLocationPath ); + initInfo.m_pFileSystem->AddSearchPath( szPath, pPathID, PATH_ADD_TO_TAIL ); + } +#endif + + // mark the first "game" dir as the "MOD" dir + if ( bFirstGamePath ) + { + bFirstGamePath = false; + initInfo.m_pFileSystem->AddSearchPath( fullLocationPath, "MOD", PATH_ADD_TO_TAIL ); + Q_strncpy( initInfo.m_ModPath, fullLocationPath, sizeof( initInfo.m_ModPath ) ); + } + + // add the game bin + AddGameBinDir( initInfo.m_pFileSystem, fullLocationPath ); + } + + initInfo.m_pFileSystem->AddSearchPath( fullLocationPath, pPathID, PATH_ADD_TO_TAIL ); + } + + pMainFile->deleteThis(); + + // Also, mark specific path IDs as "by request only". That way, we won't waste time searching in them + // when people forget to specify a search path. + initInfo.m_pFileSystem->MarkPathIDByRequestOnly( "executable_path", true ); + initInfo.m_pFileSystem->MarkPathIDByRequestOnly( "gamebin", true ); + initInfo.m_pFileSystem->MarkPathIDByRequestOnly( "mod", true ); + if ( initInfo.m_ModPath[0] != 0 ) + { + // Add the write path last. + initInfo.m_pFileSystem->AddSearchPath( initInfo.m_ModPath, "DEFAULT_WRITE_PATH", PATH_ADD_TO_TAIL ); + } + +#ifdef _DEBUG + initInfo.m_pFileSystem->PrintSearchPaths(); +#endif + +#endif // _XBOX + return FS_OK; +} + +#ifndef _XBOX +bool DoesFileExistIn( const char *pDirectoryName, const char *pFilename ) +{ + char filename[MAX_PATH]; + Q_strncpy( filename, pDirectoryName, sizeof( filename ) ); + Q_AppendSlash( filename, sizeof( filename ) ); + Q_strncat( filename, pFilename, sizeof( filename ), COPY_ALL_CHARACTERS ); + Q_FixSlashes( filename ); + return ( _access( filename, 0 ) == 0 ); +} +#endif + +#ifndef _XBOX +FSReturnCode_t LocateGameInfoFile( const CFSSteamSetupInfo &fsInfo, char *pOutDir, int outDirLen ) +{ + // Engine and Hammer don't want to search around for it. + if ( fsInfo.m_bOnlyUseDirectoryName ) + { + if ( !fsInfo.m_pDirectoryName ) + return SetupFileSystemError( false, FS_MISSING_GAMEINFO_FILE, "bOnlyUseDirectoryName=1 and pDirectoryName=NULL." ); + + if ( !DoesFileExistIn( fsInfo.m_pDirectoryName, GAMEINFO_FILENAME ) ) + return SetupFileSystemError( true, FS_MISSING_GAMEINFO_FILE, "%s doesn't exist in %s.", GAMEINFO_FILENAME, fsInfo.m_pDirectoryName ); + + Q_strncpy( pOutDir, fsInfo.m_pDirectoryName, outDirLen ); + return FS_OK; + } + + // First, check for overrides on the command line or environment variables. + const char *pProject = GetVProjectCmdLineValue(); + if ( !pProject ) + { + // Check their registry. + pProject = getenv( GAMEDIR_TOKEN ); + } + + if ( pProject ) + { + if ( DoesFileExistIn( pProject, GAMEINFO_FILENAME ) ) + { + Q_MakeAbsolutePath( pOutDir, outDirLen, pProject ); + return FS_OK; + } + else if ( fsInfo.m_bNoGameInfo ) + { + // fsInfo.m_bNoGameInfo is set by the Steam dedicated server, before it knows which mod to use. + // Steam dedicated server doesn't need a gameinfo.txt, because we'll ask which mod to use, even if + // -game is supplied on the command line. + Q_strncpy( pOutDir, "", outDirLen ); + return FS_OK; + } + else + { + // They either specified vproject on the command line or it's in their registry. Either way, + // we don't want to continue if they've specified it but it's not valid. + goto ShowError; + } + } + + if ( fsInfo.m_bNoGameInfo ) + { + Q_strncpy( pOutDir, "", outDirLen ); + return FS_OK; + } + + Warning( "Warning: falling back to auto detection of vproject directory.\n" ); + + // Now look for it in the directory they passed in. + if ( fsInfo.m_pDirectoryName ) + Q_MakeAbsolutePath( pOutDir, outDirLen, fsInfo.m_pDirectoryName ); + else + Q_MakeAbsolutePath( pOutDir, outDirLen, "." ); + + do + { + if ( DoesFileExistIn( pOutDir, GAMEINFO_FILENAME ) ) + return FS_OK; + } while ( Q_StripLastDir( pOutDir, outDirLen ) ); + + // use the cwd and hunt down the tree until we find something + Q_getwd( pOutDir, outDirLen ); + do + { + if ( DoesFileExistIn( pOutDir, GAMEINFO_FILENAME ) ) + return FS_OK; + } while ( Q_StripLastDir( pOutDir, outDirLen ) ); + +ShowError: + return SetupFileSystemError( true, FS_MISSING_GAMEINFO_FILE, + "Unable to find %s. Solutions:\n\n" + "1. Read http://www.valve-erc.com/srcsdk/faq.html#NoGameDir\n" + "2. Run vconfig to specify which game you're working on.\n" + "3. Add -game on the command line where is the directory that %s is in.\n", + GAMEINFO_FILENAME, GAMEINFO_FILENAME ); +} +#endif + +#ifndef _XBOX +bool DoesPathExistAlready( const char *pPathEnvVar, const char *pTestPath ) +{ + // Fix the slashes in the input arguments. + char correctedPathEnvVar[8192], correctedTestPath[MAX_PATH]; + Q_strncpy( correctedPathEnvVar, pPathEnvVar, sizeof( correctedPathEnvVar ) ); + Q_FixSlashes( correctedPathEnvVar ); + pPathEnvVar = correctedPathEnvVar; + + Q_strncpy( correctedTestPath, pTestPath, sizeof( correctedTestPath ) ); + Q_FixSlashes( correctedTestPath ); + if ( strlen( correctedTestPath ) > 0 && PATHSEPARATOR( correctedTestPath[strlen(correctedTestPath)-1] ) ) + correctedTestPath[ strlen(correctedTestPath) - 1 ] = 0; + + pTestPath = correctedTestPath; + + const char *pCurPos = pPathEnvVar; + while ( 1 ) + { + const char *pTestPos = Q_stristr( pCurPos, pTestPath ); + if ( !pTestPos ) + return false; + + // Ok, we found pTestPath in the path, but it's only valid if it's followed by an optional slash and a semicolon. + pTestPos += strlen( pTestPath ); + if ( pTestPos[0] == 0 || pTestPos[0] == ';' || (PATHSEPARATOR( pTestPos[0] ) && pTestPos[1] == ';') ) + return true; + + // Advance our marker.. + pCurPos = pTestPos; + } +} +#endif + +#ifndef _XBOX +FSReturnCode_t SetSteamInstallPath( char *steamInstallPath, int steamInstallPathLen, CSteamEnvVars &steamEnvVars, bool bErrorsAsWarnings ) +{ + // Start at our bin directory and move up until we find a directory with steam.dll in it. + char executablePath[MAX_PATH]; + if ( !FileSystem_GetExecutableDir( executablePath, sizeof( executablePath ) ) ) + { + if ( bErrorsAsWarnings ) + { + Warning( "SetSteamInstallPath: FileSystem_GetExecutableDir failed." ); + return FS_INVALID_PARAMETERS; + } + else + { + return SetupFileSystemError( false, FS_INVALID_PARAMETERS, "FileSystem_GetExecutableDir failed." ); + } + } + + Q_strncpy( steamInstallPath, executablePath, steamInstallPathLen ); + while ( 1 ) + { + // Ignore steamapp.cfg here in case they're debugging. We still need to know the real steam path so we can find their username. + // find + if ( DoesFileExistIn( steamInstallPath, "steam.dll" ) && !DoesFileExistIn( steamInstallPath, "steamapp.cfg" ) ) + break; + + if ( !Q_StripLastDir( steamInstallPath, steamInstallPathLen ) ) + { + if ( bErrorsAsWarnings ) + { + Warning( "Can't find steam.dll relative to executable path: %s.", executablePath ); + return FS_MISSING_STEAM_DLL; + } + else + { + return SetupFileSystemError( false, FS_MISSING_STEAM_DLL, "Can't find steam.dll relative to executable path: %s.", executablePath ); + } + } + } + + // Also, add the install path to their PATH environment variable, so filesystem_steam.dll can get to steam.dll. + char szPath[ 8192 ]; + steamEnvVars.m_Path.GetValue( szPath, sizeof( szPath ) ); + if ( !DoesPathExistAlready( szPath, steamInstallPath ) ) + { + steamEnvVars.m_Path.SetValue( "%s;%s", szPath, steamInstallPath ); + } + return FS_OK; +} +#endif + +#ifndef _XBOX +FSReturnCode_t GetSteamCfgPath( char *steamCfgPath, int steamCfgPathLen ) +{ + steamCfgPath[0] = 0; + char executablePath[MAX_PATH]; + if ( !FileSystem_GetExecutableDir( executablePath, sizeof( executablePath ) ) ) + { + return SetupFileSystemError( false, FS_INVALID_PARAMETERS, "FileSystem_GetExecutableDir failed." ); + } + Q_strncpy( steamCfgPath, executablePath, steamCfgPathLen ); + while ( 1 ) + { + if ( DoesFileExistIn( steamCfgPath, "steam.cfg" ) ) + break; + + if ( !Q_StripLastDir( steamCfgPath, steamCfgPathLen) ) + { + // the file isnt found, thats ok, its not mandatory + return FS_OK; + } + } + Q_AppendSlash( steamCfgPath, steamCfgPathLen ); + Q_strncat( steamCfgPath, "steam.cfg", steamCfgPathLen, COPY_ALL_CHARACTERS ); + + return FS_OK; +} +#endif + +#ifndef _XBOX +void SetSteamAppUser( KeyValues *pSteamInfo, const char *steamInstallPath, CSteamEnvVars &steamEnvVars ) +{ + // Always inherit the Steam user if it's already set, since it probably means we (or the + // the app that launched us) were launched from Steam. + char appUser[MAX_PATH]; + if ( steamEnvVars.m_SteamAppUser.GetValue( appUser, sizeof( appUser ) ) ) + return; + + const char *pTempAppUser = NULL; + if ( pSteamInfo && (pTempAppUser = pSteamInfo->GetString( "SteamAppUser", NULL )) != NULL ) + { + Q_strncpy( appUser, pTempAppUser, sizeof( appUser ) ); + } + else + { + // They don't have SteamInfo.txt, or it's missing SteamAppUser. Try to figure out the user + // by looking in \config\SteamAppData.vdf. + char fullFilename[MAX_PATH]; + Q_strncpy( fullFilename, steamInstallPath, sizeof( fullFilename ) ); + Q_AppendSlash( fullFilename, sizeof( fullFilename ) ); + Q_strncat( fullFilename, "config\\SteamAppData.vdf", sizeof( fullFilename ), COPY_ALL_CHARACTERS ); + + KeyValues *pSteamAppData = ReadKeyValuesFile( fullFilename ); + if ( !pSteamAppData || (pTempAppUser = pSteamAppData->GetString( "AutoLoginUser", NULL )) == NULL ) + { + Error( "Can't find steam app user info." ); + } + Q_strncpy( appUser, pTempAppUser, sizeof( appUser ) ); + + pSteamAppData->deleteThis(); + } + + Q_strlower( appUser ); + steamEnvVars.m_SteamAppUser.SetValue( "%s", appUser ); +} +#endif + +#ifndef _XBOX +void SetSteamUserPassphrase( KeyValues *pSteamInfo, CSteamEnvVars &steamEnvVars ) +{ + // Always inherit the passphrase if it's already set, since it probably means we (or the + // the app that launched us) were launched from Steam. + char szPassPhrase[ MAX_PATH ]; + if ( steamEnvVars.m_SteamUserPassphrase.GetValue( szPassPhrase, sizeof( szPassPhrase ) ) ) + return; + + // SteamUserPassphrase. + const char *pStr; + if ( pSteamInfo && (pStr = pSteamInfo->GetString( "SteamUserPassphrase", NULL )) != NULL ) + { + steamEnvVars.m_SteamUserPassphrase.SetValue( "%s", pStr ); + } +} +#endif + +#ifndef _XBOX +void SetSteamAppId( KeyValues *pFileSystemInfo, const char *pGameInfoDirectory, CSteamEnvVars &steamEnvVars ) +{ + // SteamAppId is in gameinfo.txt->FileSystem->FileSystemInfo_Steam->SteamAppId. + int iAppId = pFileSystemInfo->GetInt( "SteamAppId", -1 ); + if ( iAppId == -1 ) + Error( "Missing SteamAppId in %s\\%s.", pGameInfoDirectory, GAMEINFO_FILENAME ); + + steamEnvVars.m_SteamAppId.SetValue( "%d", iAppId ); +} +#endif + +#ifndef _XBOX +FSReturnCode_t SetupSteamStartupEnvironment( KeyValues *pFileSystemInfo, const char *pGameInfoDirectory, CSteamEnvVars &steamEnvVars ) +{ + // Ok, we're going to run Steam. See if they have SteamInfo.txt. If not, we'll try to deduce what we can. + char steamInfoFile[MAX_PATH]; + Q_strncpy( steamInfoFile, pGameInfoDirectory, sizeof( steamInfoFile ) ); + Q_AppendSlash( steamInfoFile, sizeof( steamInfoFile ) ); + Q_strncat( steamInfoFile, "steaminfo.txt", sizeof( steamInfoFile ), COPY_ALL_CHARACTERS ); + KeyValues *pSteamInfo = ReadKeyValuesFile( steamInfoFile ); + + char steamInstallPath[MAX_PATH]; + FSReturnCode_t ret = SetSteamInstallPath( steamInstallPath, sizeof( steamInstallPath ), steamEnvVars, false ); + if ( ret != FS_OK ) + return ret; + + SetSteamAppUser( pSteamInfo, steamInstallPath, steamEnvVars ); + SetSteamUserPassphrase( pSteamInfo, steamEnvVars ); + SetSteamAppId( pFileSystemInfo, pGameInfoDirectory, steamEnvVars ); + + if ( pSteamInfo ) + pSteamInfo->deleteThis(); + + return FS_OK; +} +#endif + +#ifndef _XBOX +FSReturnCode_t GetSteamExtraAppId( const char *pDirectoryName, int *nExtraAppId ) +{ + // Now, load gameinfo.txt (to make sure it's there) + KeyValues *pMainFile, *pFileSystemInfo, *pSearchPaths; + FSReturnCode_t ret = LoadGameInfoFile( pDirectoryName, pMainFile, pFileSystemInfo, pSearchPaths ); + if ( ret != FS_OK ) + return ret; + + *nExtraAppId = pFileSystemInfo->GetInt( "ToolsAppId", -1 ); + pMainFile->deleteThis(); + return FS_OK; +} +#endif + +#ifndef _XBOX +FSReturnCode_t FileSystem_SetBasePaths( IFileSystem *pFileSystem ) +{ + pFileSystem->RemoveSearchPaths( "EXECUTABLE_PATH" ); + + char executablePath[MAX_PATH]; + if ( !FileSystem_GetExecutableDir( executablePath, sizeof( executablePath ) ) ) + return SetupFileSystemError( false, FS_INVALID_PARAMETERS, "FileSystem_GetExecutableDir failed." ); + + pFileSystem->AddSearchPath( executablePath, "EXECUTABLE_PATH" ); + return FS_OK; +} +#endif + +//----------------------------------------------------------------------------- +// Returns the name of the file system DLL to use +//----------------------------------------------------------------------------- +FSReturnCode_t FileSystem_GetFileSystemDLLName( char *pFileSystemDLL, int nMaxLen, bool &bSteam ) +{ + bSteam = false; + + // Inside of here, we don't have a filesystem yet, so we have to assume that the filesystem_stdio or filesystem_steam + // is in this same directory with us. + char executablePath[MAX_PATH]; + if ( !FileSystem_GetExecutableDir( executablePath, sizeof( executablePath ) ) ) + return SetupFileSystemError( false, FS_INVALID_PARAMETERS, "FileSystem_GetExecutableDir failed." ); + +#ifdef _WIN32 + Q_snprintf( pFileSystemDLL, nMaxLen, "%s%cfilesystem_stdio.dll", executablePath, CORRECT_PATH_SEPARATOR ); + + // If filesystem_stdio.dll is missing or -steam is specified, then load filesystem_steam.dll. + // There are two command line parameters for Steam: + // 1) -steam (runs Steam in remote filesystem mode; requires Steam backend) + // 2) -steamlocal (runs Steam in local filesystem mode (all content off HDD) +#ifndef _XBOX + if ( CommandLine()->FindParm( "-steam" ) || CommandLine()->FindParm( "-steamlocal" ) || _access( pFileSystemDLL, 0 ) != 0 ) + { + Q_snprintf( pFileSystemDLL, nMaxLen, "%s%cfilesystem_steam.dll", executablePath, CORRECT_PATH_SEPARATOR ); + bSteam = true; + } +#endif +#elif _LINUX + Q_snprintf( pFileSystemDLL, nMaxLen, "%s%cfilesystem_i486.so", executablePath, CORRECT_PATH_SEPARATOR ); +#else + #error "define a filesystem dll name" +#endif + + return FS_OK; +} + +//----------------------------------------------------------------------------- +// Sets up the steam.dll install path in our PATH env var (so you can then just +// LoadLibrary() on filesystem_steam.dll without having to copy steam.dll anywhere special ) +//----------------------------------------------------------------------------- +FSReturnCode_t FileSystem_SetupSteamInstallPath() +{ +#ifndef _XBOX + CSteamEnvVars steamEnvVars; + char steamInstallPath[MAX_PATH]; + FSReturnCode_t ret = SetSteamInstallPath( steamInstallPath, sizeof( steamInstallPath ), steamEnvVars, true ); + steamEnvVars.m_Path.SetRestoreOriginalValue( false ); // We want to keep the change to the path going forward. + return ret; +#else + return FS_OK; +#endif +} + +//----------------------------------------------------------------------------- +// Sets up the steam environment + gets back the gameinfo.txt path +//----------------------------------------------------------------------------- +FSReturnCode_t FileSystem_SetupSteamEnvironment( CFSSteamSetupInfo &fsInfo ) +{ +#ifndef _XBOX + // First, locate the directory with gameinfo.txt. + FSReturnCode_t ret = LocateGameInfoFile( fsInfo, fsInfo.m_GameInfoPath, sizeof( fsInfo.m_GameInfoPath ) ); + if ( ret != FS_OK ) + return ret; + + CSteamEnvVars steamEnvVars; + if ( fsInfo.m_bSteam ) + { + if ( fsInfo.m_bToolsMode ) + { + // Now, load gameinfo.txt (to make sure it's there) + KeyValues *pMainFile, *pFileSystemInfo, *pSearchPaths; + ret = LoadGameInfoFile( fsInfo.m_GameInfoPath, pMainFile, pFileSystemInfo, pSearchPaths ); + if ( ret != FS_OK ) + return ret; + + // If filesystem_stdio.dll is missing or -steam is specified, then load filesystem_steam.dll. + // There are two command line parameters for Steam: + // 1) -steam (runs Steam in remote filesystem mode; requires Steam backend) + // 2) -steamlocal (runs Steam in local filesystem mode (all content off HDD) + + // Setup all the environment variables related to Steam so filesystem_steam.dll knows how to initialize Steam. + ret = SetupSteamStartupEnvironment( pFileSystemInfo, fsInfo.m_GameInfoPath, steamEnvVars ); + if ( ret != FS_OK ) + return ret; + + steamEnvVars.m_SteamAppId.SetRestoreOriginalValue( false ); // We want to keep the change to the path going forward. + + // We're done with main file + pMainFile->deleteThis(); + } + else if ( fsInfo.m_bSetSteamDLLPath ) + { + // This is used by the engine to automatically set the path to their steam.dll when running the engine, + // so they can debug it without having to copy steam.dll up into their hl2.exe folder. + char steamInstallPath[MAX_PATH]; + ret = SetSteamInstallPath( steamInstallPath, sizeof( steamInstallPath ), steamEnvVars, true ); + steamEnvVars.m_Path.SetRestoreOriginalValue( false ); // We want to keep the change to the path going forward. + } + } +#endif + + return FS_OK; +} + + +//----------------------------------------------------------------------------- +// Loads the file system module +//----------------------------------------------------------------------------- +FSReturnCode_t FileSystem_LoadFileSystemModule( CFSLoadModuleInfo &fsInfo ) +{ + // First, locate the directory with gameinfo.txt. + FSReturnCode_t ret = FileSystem_SetupSteamEnvironment( fsInfo ); + if ( ret != FS_OK ) + return ret; + + void *fs = &fsInfo.m_pFileSystem; + + // Now that the environment is setup, load the filesystem module. + if ( !Sys_LoadInterface( + fsInfo.m_pFileSystemDLLName, + FILESYSTEM_INTERFACE_VERSION, + &fsInfo.m_pModule, + (void**)fs ) ) + { + return SetupFileSystemError( false, FS_UNABLE_TO_INIT, "Can't load %s.", fsInfo.m_pFileSystemDLLName ); + } + + if ( !fsInfo.m_pFileSystem->Connect( fsInfo.m_ConnectFactory ) ) + return SetupFileSystemError( false, FS_UNABLE_TO_INIT, "%s IFileSystem::Connect failed.", fsInfo.m_pFileSystemDLLName ); + + if ( fsInfo.m_pFileSystem->Init() != INIT_OK ) + return SetupFileSystemError( false, FS_UNABLE_TO_INIT, "%s IFileSystem::Init failed.", fsInfo.m_pFileSystemDLLName ); + + return FS_OK; +} + + +//----------------------------------------------------------------------------- +// Mounds a particular steam cache +//----------------------------------------------------------------------------- +#ifndef _XBOX +FSReturnCode_t FileSystem_MountContent( CFSMountContentInfo &mountContentInfo ) +{ + // This part is Steam-only. + if ( mountContentInfo.m_pFileSystem->IsSteam() ) + { + // Find out the "extra app id". This is for tools, which want to mount a base app's filesystem + // like HL2, then mount the SDK content (tools materials and models, etc) in addition. + int nExtraAppId = -1; + if ( mountContentInfo.m_bToolsMode ) + { + FSReturnCode_t ret = GetSteamExtraAppId( mountContentInfo.m_pDirectoryName, &nExtraAppId ); + if ( ret != FS_OK ) + return ret; + } + + // Set our working directory temporarily so Steam can remember it. + // This is what Steam strips off absolute filenames like c:\program files\valve\steam\steamapps\username\sourcesdk + // to get to the relative part of the path. + char baseDir[MAX_PATH], oldWorkingDir[MAX_PATH]; + if ( !FileSystem_GetBaseDir( baseDir, sizeof( baseDir ) ) ) + return SetupFileSystemError( false, FS_INVALID_PARAMETERS, "FileSystem_GetBaseDir failed." ); + + Q_getwd( oldWorkingDir, sizeof( oldWorkingDir ) ); + _chdir( baseDir ); + + // Filesystem_tools needs to add dependencies in here beforehand. + FilesystemMountRetval_t retVal = mountContentInfo.m_pFileSystem->MountSteamContent( nExtraAppId ); + + _chdir( oldWorkingDir ); + + if ( retVal != FILESYSTEM_MOUNT_OK ) + return SetupFileSystemError( true, FS_UNABLE_TO_INIT, "Unable to mount Steam content in the file system" ); + } + + return FileSystem_SetBasePaths( mountContentInfo.m_pFileSystem ); +} +#endif + +void FileSystem_SetErrorMode( FSErrorMode_t errorMode ) +{ + g_FileSystemErrorMode = errorMode; +} + +#ifndef _XBOX +void FileSystem_ClearSteamEnvVars() +{ + CSteamEnvVars envVars; + + // Change the values and don't restore the originals. + envVars.m_SteamAppId.SetValue( "" ); + envVars.m_SteamUserPassphrase.SetValue( "" ); + envVars.m_SteamAppUser.SetValue( "" ); + + envVars.SetRestoreOriginalValue_ALL( false ); +} +#endif + +#endif // !_STATIC_LINKED || _SHARED_LIB + + +//----------------------------------------------------------------------------- +// Adds the platform folder to the search path. +//----------------------------------------------------------------------------- +void FileSystem_AddSearchPath_Platform( IFileSystem *pFileSystem, const char *szGameInfoPath ) +{ + char platform[MAX_PATH]; + if ( pFileSystem->IsSteam() ) + { + // Steam doesn't support relative paths + Q_strncpy( platform, "platform", MAX_PATH ); + } + else + { + Q_strncpy( platform, szGameInfoPath, MAX_PATH ); + Q_StripTrailingSlash( platform ); + Q_strncat( platform, "/../platform", MAX_PATH, MAX_PATH ); + } + + pFileSystem->AddSearchPath( platform, "PLATFORM" ); +} diff --git a/public/iclient.h b/public/iclient.h index 5b8b5677..5dfb0b1e 100644 --- a/public/iclient.h +++ b/public/iclient.h @@ -12,10 +12,10 @@ #include #include "tier0/platform.h" -#include "userid.h" class IServer; class INetMessage; +struct USERID_t; abstract_class IClient : public INetChannelHandler { diff --git a/public/igameevents.h b/public/igameevents.h index 5c72d078..50be8517 100644 --- a/public/igameevents.h +++ b/public/igameevents.h @@ -1,194 +1,191 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $Workfile: $ -// $Date: $ -// -//----------------------------------------------------------------------------- -// $Log: $ -// -// $NoKeywords: $ -//=============================================================================// -#if !defined( IGAMEEVENTS_H ) -#define IGAMEEVENTS_H -#ifdef _WIN32 -#pragma once -#endif - -#include "tier1/interface.h" - -#define INTERFACEVERSION_GAMEEVENTSMANAGER "GAMEEVENTSMANAGER001" // old game event manager, don't use it! -#define INTERFACEVERSION_GAMEEVENTSMANAGER2 "GAMEEVENTSMANAGER002" // new game event manager, - -//----------------------------------------------------------------------------- -// Purpose: Engine interface into global game event management -//----------------------------------------------------------------------------- - -/* - -The GameEventManager keeps track and fires of all global game events. Game events -are fired by game.dll for events like player death or team wins. Each event has a -unique name and comes with a KeyValue structure providing informations about this -event. Some events are generated also by the engine. - -Events are networked to connected clients and invoked there to. Therefore you -have to specify all data fields and there data types in an public resource -file which is parsed by server and broadcasted to it's clients. A typical game -event is defined like this: - - "game_start" // a new game starts - { - "roundslimit" "long" // max round - "timelimit" "long" // time limit - "fraglimit" "long" // frag limit - "objective" "string" // round objective - } - -All events must have unique names (case sensitive) and may have a list -of data fields. each data field must specify a data type, so the engine -knows how to serialize/unserialize that event for network transmission. -Valid data types are string, float, long, short, byte & bool. If a -data field should not be broadcasted to clients, use the type "local". -*/ - - -#define MAX_EVENT_NAME_LENGTH 32 // max game event name length -#define MAX_EVENT_BITS 9 // max bits needed for an event index -#define MAX_EVENT_NUMBER (1<name ) ) - return slot->type; - } - - Assert( !"Interpolator_InterpolatorForName failed!!!" ); - return INTERPOLATE_DEFAULT; -} - -char const *Interpolator_NameForInterpolator( int type, bool printname ) -{ - int i = (int)type; - int c = ARRAYSIZE( g_InterpolatorNameMap ); - if ( i < 0 || i >= c ) - { - Assert( "!Interpolator_NameForInterpolator: bogus type!" ); - // returns "unspecified!!!"; - return printname ? g_InterpolatorNameMap[ 0 ].printname : g_InterpolatorNameMap[ 0 ].name; - } - - return printname ? g_InterpolatorNameMap[ i ].printname : g_InterpolatorNameMap[ i ].name; -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -struct CurveNameMap_t -{ - int type; - int hotkey; -}; - -static CurveNameMap_t g_CurveNameMap[] = -{ - { CURVE_CATMULL_ROM_TO_CATMULL_ROM, '1' }, - { CURVE_EASE_IN_TO_EASE_OUT, '2' }, - { CURVE_EASE_IN_TO_EASE_IN, '3' }, - { CURVE_EASE_OUT_TO_EASE_OUT, '4' }, - { CURVE_BSPLINE_TO_BSPLINE, '5' }, - { CURVE_LINEAR_INTERP_TO_LINEAR_INTERP, '6' }, - { CURVE_KOCHANEK_BARTELS_TO_KOCHANEK_BARTELS, '7' }, - { CURVE_KOCHANEK_BARTELS_EARLY_TO_KOCHANEK_BARTELS_EARLY, '8' }, - { CURVE_KOCHANEK_BARTELS_LATE_TO_KOCHANEK_BARTELS_LATE, '9' }, - { CURVE_SIMPLE_CUBIC_TO_SIMPLE_CUBIC, '0' }, -}; - -// Turn enum into string and vice versa -int Interpolator_CurveTypeForName( const char *name ) -{ - char sz[ 128 ]; - Q_strncpy( sz, name, sizeof( sz ) ); - - int leftcurve = 0; - int rightcurve = 0; - - int skip = Q_strlen( "curve_" ); - - if ( !Q_strnicmp( sz, "curve_", skip ) ) - { - char *p = sz + skip; - char *second = Q_stristr( p, "_to_curve_" ); - - char save = *second; - *second = 0; - - leftcurve = Interpolator_InterpolatorForName( p ); - - *second = save; - - p = second + Q_strlen( "_to_curve_" ); - - rightcurve = Interpolator_InterpolatorForName( p ); - } - - return MAKE_CURVE_TYPE( leftcurve, rightcurve ); -} - -const char *Interpolator_NameForCurveType( int type, bool printname ) -{ - static char outname[ 256 ]; - - int leftside = GET_LEFT_CURVE( type ); - int rightside = GET_RIGHT_CURVE( type ); - - if ( !printname ) - { - Q_snprintf( outname, sizeof( outname ), "curve_%s_to_curve_%s", - Interpolator_NameForInterpolator( leftside, printname ), - Interpolator_NameForInterpolator( rightside, printname ) ); - } - else - { - Q_snprintf( outname, sizeof( outname ), "%s <-> %s", - Interpolator_NameForInterpolator( leftside, printname ), - Interpolator_NameForInterpolator( rightside, printname ) ); - } - - return outname; -} - -void Interpolator_CurveInterpolatorsForType( int type, int& inbound, int& outbound ) -{ - inbound = GET_LEFT_CURVE( type ); - outbound = GET_RIGHT_CURVE( type ); -} - -int Interpolator_CurveTypeForHotkey( int key ) -{ - int c = ARRAYSIZE( g_CurveNameMap ); - for ( int i = 0; i < c; ++i ) - { - CurveNameMap_t *slot = &g_CurveNameMap[ i ]; - if ( slot->hotkey == key ) - return slot->type; - } - - return -1; -} - -void Interpolator_GetKochanekBartelsParams( int interpolationType, float& tension, float& bias, float& continuity ) -{ - switch ( interpolationType ) - { - default: - tension = 0.0f; - bias = 0.0f; - continuity = 0.0f; - Assert( 0 ); - break; - case INTERPOLATE_KOCHANEK_BARTELS: - tension = 0.77f; - bias = 0.0f; - continuity = 0.77f; - break; - case INTERPOLATE_KOCHANEK_BARTELS_EARLY: - tension = 0.77f; - bias = -1.0f; - continuity = 0.77f; - break; - case INTERPOLATE_KOCHANEK_BARTELS_LATE: - tension = 0.77f; - bias = 1.0f; - continuity = 0.77f; - break; - } -} - -void Interpolator_CurveInterpolate( int interpolationType, - const Vector &vPre, - const Vector &vStart, - const Vector &vEnd, - const Vector &vNext, - float f, - Vector &vOut ) -{ - vOut.Init(); - - switch ( interpolationType ) - { - default: - Warning( "Unknown interpolation type %d\n", - (int)interpolationType ); - // break; // Fall through and use catmull_rom as default - case INTERPOLATE_DEFAULT: - case INTERPOLATE_CATMULL_ROM_NORMALIZEX: - Catmull_Rom_Spline_NormalizeX( - vPre, - vStart, - vEnd, - vNext, - f, - vOut ); - break; - case INTERPOLATE_CATMULL_ROM: - Catmull_Rom_Spline( - vPre, - vStart, - vEnd, - vNext, - f, - vOut ); - break; - case INTERPOLATE_CATMULL_ROM_NORMALIZE: - Catmull_Rom_Spline_Normalize( - vPre, - vStart, - vEnd, - vNext, - f, - vOut ); - break; - case INTERPOLATE_CATMULL_ROM_TANGENT: - Catmull_Rom_Spline_Tangent( - vPre, - vStart, - vEnd, - vNext, - f, - vOut ); - break; - case INTERPOLATE_EASE_IN: - { - f = sin( M_PI * f * 0.5f ); - // Fixme, since this ignores vPre and vNext we could omit computing them aove - VectorLerp( vStart, vEnd, f, vOut ); - } - break; - case INTERPOLATE_EASE_OUT: - { - f = 1.0f - sin( M_PI * f * 0.5f + 0.5f * M_PI ); - // Fixme, since this ignores vPre and vNext we could omit computing them aove - VectorLerp( vStart, vEnd, f, vOut ); - } - break; - case INTERPOLATE_EASE_INOUT: - { - f = SimpleSpline( f ); - // Fixme, since this ignores vPre and vNext we could omit computing them aove - VectorLerp( vStart, vEnd, f, vOut ); - } - break; - case INTERPOLATE_LINEAR_INTERP: - // Fixme, since this ignores vPre and vNext we could omit computing them aove - VectorLerp( vStart, vEnd, f, vOut ); - break; - case INTERPOLATE_KOCHANEK_BARTELS: - case INTERPOLATE_KOCHANEK_BARTELS_EARLY: - case INTERPOLATE_KOCHANEK_BARTELS_LATE: - { - float t, b, c; - Interpolator_GetKochanekBartelsParams( interpolationType, t, b, c ); - Kochanek_Bartels_Spline_NormalizeX - ( - t, b, c, - vPre, - vStart, - vEnd, - vNext, - f, - vOut - ); - } - break; - case INTERPOLATE_SIMPLE_CUBIC: - Cubic_Spline_NormalizeX( - vPre, - vStart, - vEnd, - vNext, - f, - vOut ); - break; - case INTERPOLATE_BSPLINE: - BSpline( - vPre, - vStart, - vEnd, - vNext, - f, - vOut ); - break; - case INTERPOLATE_EXPONENTIAL_DECAY: - { - float dt = vEnd.x - vStart.x; - if ( dt > 0.0f ) - { - float val = 1.0f - ExponentialDecay( 0.001, dt, f * dt ); - vOut.y = vStart.y + val * ( vEnd.y - vStart.y ); - } - else - { - vOut.y = vStart.y; - } - } - break; - case INTERPOLATE_HOLD: - { - vOut.y = vStart.y; - } - break; - } -} - -void Interpolator_CurveInterpolate_NonNormalized( int interpolationType, - const Vector &vPre, - const Vector &vStart, - const Vector &vEnd, - const Vector &vNext, - float f, - Vector &vOut ) -{ - vOut.Init(); - - switch ( interpolationType ) - { - default: - Warning( "Unknown interpolation type %d\n", - (int)interpolationType ); - // break; // Fall through and use catmull_rom as default - case INTERPOLATE_CATMULL_ROM_NORMALIZEX: - case INTERPOLATE_DEFAULT: - case INTERPOLATE_CATMULL_ROM: - case INTERPOLATE_CATMULL_ROM_NORMALIZE: - case INTERPOLATE_CATMULL_ROM_TANGENT: - Catmull_Rom_Spline( - vPre, - vStart, - vEnd, - vNext, - f, - vOut ); - break; - case INTERPOLATE_EASE_IN: - { - f = sin( M_PI * f * 0.5f ); - // Fixme, since this ignores vPre and vNext we could omit computing them aove - VectorLerp( vStart, vEnd, f, vOut ); - } - break; - case INTERPOLATE_EASE_OUT: - { - f = 1.0f - sin( M_PI * f * 0.5f + 0.5f * M_PI ); - // Fixme, since this ignores vPre and vNext we could omit computing them aove - VectorLerp( vStart, vEnd, f, vOut ); - } - break; - case INTERPOLATE_EASE_INOUT: - { - f = SimpleSpline( f ); - // Fixme, since this ignores vPre and vNext we could omit computing them aove - VectorLerp( vStart, vEnd, f, vOut ); - } - break; - case INTERPOLATE_LINEAR_INTERP: - // Fixme, since this ignores vPre and vNext we could omit computing them aove - VectorLerp( vStart, vEnd, f, vOut ); - break; - case INTERPOLATE_KOCHANEK_BARTELS: - case INTERPOLATE_KOCHANEK_BARTELS_EARLY: - case INTERPOLATE_KOCHANEK_BARTELS_LATE: - { - float t, b, c; - Interpolator_GetKochanekBartelsParams( interpolationType, t, b, c ); - Kochanek_Bartels_Spline - ( - t, b, c, - vPre, - vStart, - vEnd, - vNext, - f, - vOut - ); - } - break; - case INTERPOLATE_SIMPLE_CUBIC: - Cubic_Spline( - vPre, - vStart, - vEnd, - vNext, - f, - vOut ); - break; - case INTERPOLATE_BSPLINE: - BSpline( - vPre, - vStart, - vEnd, - vNext, - f, - vOut ); - break; - case INTERPOLATE_EXPONENTIAL_DECAY: - { - float dt = vEnd.x - vStart.x; - if ( dt > 0.0f ) - { - float val = 1.0f - ExponentialDecay( 0.001, dt, f * dt ); - vOut.y = vStart.y + val * ( vEnd.y - vStart.y ); - } - else - { - vOut.y = vStart.y; - } - } - break; - case INTERPOLATE_HOLD: - { - vOut.y = vStart.y; - } - break; - } -} \ No newline at end of file +//====== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======= +// +// Purpose: +// +//============================================================================= +#include "basetypes.h" +#include "vstdlib/strtools.h" +#include "interpolatortypes.h" +#include "tier0/dbg.h" +#include "mathlib.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +struct InterpolatorNameMap_t +{ + int type; + char const *name; + char const *printname; +}; + +static InterpolatorNameMap_t g_InterpolatorNameMap[] = +{ + { INTERPOLATE_DEFAULT, "default", "Default" }, + { INTERPOLATE_CATMULL_ROM_NORMALIZEX, "catmullrom_normalize_x", "Catmull-Rom (Norm X)" }, + { INTERPOLATE_EASE_IN, "easein", "Ease In" }, + { INTERPOLATE_EASE_OUT, "easeout", "Ease Out" }, + { INTERPOLATE_EASE_INOUT, "easeinout", "Ease In/Out" }, + { INTERPOLATE_BSPLINE, "bspline", "B-Spline" }, + { INTERPOLATE_LINEAR_INTERP, "linear_interp", "Linear Interp." }, + { INTERPOLATE_KOCHANEK_BARTELS, "kochanek", "Kochanek-Bartels" }, + { INTERPOLATE_KOCHANEK_BARTELS_EARLY, "kochanek_early", "Kochanek-Bartels Early" }, + { INTERPOLATE_KOCHANEK_BARTELS_LATE, "kochanek_late", "Kochanek-Bartels Late" }, + { INTERPOLATE_SIMPLE_CUBIC, "simple_cubic", "Simple Cubic" }, + { INTERPOLATE_CATMULL_ROM, "catmullrom", "Catmull-Rom" }, + { INTERPOLATE_CATMULL_ROM_NORMALIZE, "catmullrom_normalize", "Catmull-Rom (Norm)" }, + { INTERPOLATE_CATMULL_ROM_TANGENT, "catmullrom_tangent", "Catmull-Rom (Tangent)" }, + { INTERPOLATE_EXPONENTIAL_DECAY, "exponential_decay", "Exponential Decay" }, + { INTERPOLATE_HOLD, "hold", "Hold" }, +}; + +int Interpolator_InterpolatorForName( char const *name ) +{ + for ( int i = 0; i < NUM_INTERPOLATE_TYPES; ++i ) + { + InterpolatorNameMap_t *slot = &g_InterpolatorNameMap[ i ]; + if ( !Q_stricmp( name, slot->name ) ) + return slot->type; + } + + Assert( !"Interpolator_InterpolatorForName failed!!!" ); + return INTERPOLATE_DEFAULT; +} + +char const *Interpolator_NameForInterpolator( int type, bool printname ) +{ + int i = (int)type; + int c = ARRAYSIZE( g_InterpolatorNameMap ); + if ( i < 0 || i >= c ) + { + Assert( "!Interpolator_NameForInterpolator: bogus type!" ); + // returns "unspecified!!!"; + return printname ? g_InterpolatorNameMap[ 0 ].printname : g_InterpolatorNameMap[ 0 ].name; + } + + return printname ? g_InterpolatorNameMap[ i ].printname : g_InterpolatorNameMap[ i ].name; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +struct CurveNameMap_t +{ + int type; + int hotkey; +}; + +static CurveNameMap_t g_CurveNameMap[] = +{ + { CURVE_CATMULL_ROM_TO_CATMULL_ROM, '1' }, + { CURVE_EASE_IN_TO_EASE_OUT, '2' }, + { CURVE_EASE_IN_TO_EASE_IN, '3' }, + { CURVE_EASE_OUT_TO_EASE_OUT, '4' }, + { CURVE_BSPLINE_TO_BSPLINE, '5' }, + { CURVE_LINEAR_INTERP_TO_LINEAR_INTERP, '6' }, + { CURVE_KOCHANEK_BARTELS_TO_KOCHANEK_BARTELS, '7' }, + { CURVE_KOCHANEK_BARTELS_EARLY_TO_KOCHANEK_BARTELS_EARLY, '8' }, + { CURVE_KOCHANEK_BARTELS_LATE_TO_KOCHANEK_BARTELS_LATE, '9' }, + { CURVE_SIMPLE_CUBIC_TO_SIMPLE_CUBIC, '0' }, +}; + +// Turn enum into string and vice versa +int Interpolator_CurveTypeForName( const char *name ) +{ + char sz[ 128 ]; + Q_strncpy( sz, name, sizeof( sz ) ); + + int leftcurve = 0; + int rightcurve = 0; + + int skip = Q_strlen( "curve_" ); + + if ( !Q_strnicmp( sz, "curve_", skip ) ) + { + char *p = sz + skip; + char *second = Q_stristr( p, "_to_curve_" ); + + char save = *second; + *second = 0; + + leftcurve = Interpolator_InterpolatorForName( p ); + + *second = save; + + p = second + Q_strlen( "_to_curve_" ); + + rightcurve = Interpolator_InterpolatorForName( p ); + } + + return MAKE_CURVE_TYPE( leftcurve, rightcurve ); +} + +const char *Interpolator_NameForCurveType( int type, bool printname ) +{ + static char outname[ 256 ]; + + int leftside = GET_LEFT_CURVE( type ); + int rightside = GET_RIGHT_CURVE( type ); + + if ( !printname ) + { + Q_snprintf( outname, sizeof( outname ), "curve_%s_to_curve_%s", + Interpolator_NameForInterpolator( leftside, printname ), + Interpolator_NameForInterpolator( rightside, printname ) ); + } + else + { + Q_snprintf( outname, sizeof( outname ), "%s <-> %s", + Interpolator_NameForInterpolator( leftside, printname ), + Interpolator_NameForInterpolator( rightside, printname ) ); + } + + return outname; +} + +void Interpolator_CurveInterpolatorsForType( int type, int& inbound, int& outbound ) +{ + inbound = GET_LEFT_CURVE( type ); + outbound = GET_RIGHT_CURVE( type ); +} + +int Interpolator_CurveTypeForHotkey( int key ) +{ + int c = ARRAYSIZE( g_CurveNameMap ); + for ( int i = 0; i < c; ++i ) + { + CurveNameMap_t *slot = &g_CurveNameMap[ i ]; + if ( slot->hotkey == key ) + return slot->type; + } + + return -1; +} + +void Interpolator_GetKochanekBartelsParams( int interpolationType, float& tension, float& bias, float& continuity ) +{ + switch ( interpolationType ) + { + default: + tension = 0.0f; + bias = 0.0f; + continuity = 0.0f; + Assert( 0 ); + break; + case INTERPOLATE_KOCHANEK_BARTELS: + tension = 0.77f; + bias = 0.0f; + continuity = 0.77f; + break; + case INTERPOLATE_KOCHANEK_BARTELS_EARLY: + tension = 0.77f; + bias = -1.0f; + continuity = 0.77f; + break; + case INTERPOLATE_KOCHANEK_BARTELS_LATE: + tension = 0.77f; + bias = 1.0f; + continuity = 0.77f; + break; + } +} + +void Interpolator_CurveInterpolate( int interpolationType, + const Vector &vPre, + const Vector &vStart, + const Vector &vEnd, + const Vector &vNext, + float f, + Vector &vOut ) +{ + vOut.Init(); + + switch ( interpolationType ) + { + default: + Warning( "Unknown interpolation type %d\n", + (int)interpolationType ); + // break; // Fall through and use catmull_rom as default + case INTERPOLATE_DEFAULT: + case INTERPOLATE_CATMULL_ROM_NORMALIZEX: + Catmull_Rom_Spline_NormalizeX( + vPre, + vStart, + vEnd, + vNext, + f, + vOut ); + break; + case INTERPOLATE_CATMULL_ROM: + Catmull_Rom_Spline( + vPre, + vStart, + vEnd, + vNext, + f, + vOut ); + break; + case INTERPOLATE_CATMULL_ROM_NORMALIZE: + Catmull_Rom_Spline_Normalize( + vPre, + vStart, + vEnd, + vNext, + f, + vOut ); + break; + case INTERPOLATE_CATMULL_ROM_TANGENT: + Catmull_Rom_Spline_Tangent( + vPre, + vStart, + vEnd, + vNext, + f, + vOut ); + break; + case INTERPOLATE_EASE_IN: + { + f = sin( M_PI * f * 0.5f ); + // Fixme, since this ignores vPre and vNext we could omit computing them aove + VectorLerp( vStart, vEnd, f, vOut ); + } + break; + case INTERPOLATE_EASE_OUT: + { + f = 1.0f - sin( M_PI * f * 0.5f + 0.5f * M_PI ); + // Fixme, since this ignores vPre and vNext we could omit computing them aove + VectorLerp( vStart, vEnd, f, vOut ); + } + break; + case INTERPOLATE_EASE_INOUT: + { + f = SimpleSpline( f ); + // Fixme, since this ignores vPre and vNext we could omit computing them aove + VectorLerp( vStart, vEnd, f, vOut ); + } + break; + case INTERPOLATE_LINEAR_INTERP: + // Fixme, since this ignores vPre and vNext we could omit computing them aove + VectorLerp( vStart, vEnd, f, vOut ); + break; + case INTERPOLATE_KOCHANEK_BARTELS: + case INTERPOLATE_KOCHANEK_BARTELS_EARLY: + case INTERPOLATE_KOCHANEK_BARTELS_LATE: + { + float t, b, c; + Interpolator_GetKochanekBartelsParams( interpolationType, t, b, c ); + Kochanek_Bartels_Spline_NormalizeX + ( + t, b, c, + vPre, + vStart, + vEnd, + vNext, + f, + vOut + ); + } + break; + case INTERPOLATE_SIMPLE_CUBIC: + Cubic_Spline_NormalizeX( + vPre, + vStart, + vEnd, + vNext, + f, + vOut ); + break; + case INTERPOLATE_BSPLINE: + BSpline( + vPre, + vStart, + vEnd, + vNext, + f, + vOut ); + break; + case INTERPOLATE_EXPONENTIAL_DECAY: + { + float dt = vEnd.x - vStart.x; + if ( dt > 0.0f ) + { + float val = 1.0f - ExponentialDecay( 0.001, dt, f * dt ); + vOut.y = vStart.y + val * ( vEnd.y - vStart.y ); + } + else + { + vOut.y = vStart.y; + } + } + break; + case INTERPOLATE_HOLD: + { + vOut.y = vStart.y; + } + break; + } +} + +void Interpolator_CurveInterpolate_NonNormalized( int interpolationType, + const Vector &vPre, + const Vector &vStart, + const Vector &vEnd, + const Vector &vNext, + float f, + Vector &vOut ) +{ + vOut.Init(); + + switch ( interpolationType ) + { + default: + Warning( "Unknown interpolation type %d\n", + (int)interpolationType ); + // break; // Fall through and use catmull_rom as default + case INTERPOLATE_CATMULL_ROM_NORMALIZEX: + case INTERPOLATE_DEFAULT: + case INTERPOLATE_CATMULL_ROM: + case INTERPOLATE_CATMULL_ROM_NORMALIZE: + case INTERPOLATE_CATMULL_ROM_TANGENT: + Catmull_Rom_Spline( + vPre, + vStart, + vEnd, + vNext, + f, + vOut ); + break; + case INTERPOLATE_EASE_IN: + { + f = sin( M_PI * f * 0.5f ); + // Fixme, since this ignores vPre and vNext we could omit computing them aove + VectorLerp( vStart, vEnd, f, vOut ); + } + break; + case INTERPOLATE_EASE_OUT: + { + f = 1.0f - sin( M_PI * f * 0.5f + 0.5f * M_PI ); + // Fixme, since this ignores vPre and vNext we could omit computing them aove + VectorLerp( vStart, vEnd, f, vOut ); + } + break; + case INTERPOLATE_EASE_INOUT: + { + f = SimpleSpline( f ); + // Fixme, since this ignores vPre and vNext we could omit computing them aove + VectorLerp( vStart, vEnd, f, vOut ); + } + break; + case INTERPOLATE_LINEAR_INTERP: + // Fixme, since this ignores vPre and vNext we could omit computing them aove + VectorLerp( vStart, vEnd, f, vOut ); + break; + case INTERPOLATE_KOCHANEK_BARTELS: + case INTERPOLATE_KOCHANEK_BARTELS_EARLY: + case INTERPOLATE_KOCHANEK_BARTELS_LATE: + { + float t, b, c; + Interpolator_GetKochanekBartelsParams( interpolationType, t, b, c ); + Kochanek_Bartels_Spline + ( + t, b, c, + vPre, + vStart, + vEnd, + vNext, + f, + vOut + ); + } + break; + case INTERPOLATE_SIMPLE_CUBIC: + Cubic_Spline( + vPre, + vStart, + vEnd, + vNext, + f, + vOut ); + break; + case INTERPOLATE_BSPLINE: + BSpline( + vPre, + vStart, + vEnd, + vNext, + f, + vOut ); + break; + case INTERPOLATE_EXPONENTIAL_DECAY: + { + float dt = vEnd.x - vStart.x; + if ( dt > 0.0f ) + { + float val = 1.0f - ExponentialDecay( 0.001, dt, f * dt ); + vOut.y = vStart.y + val * ( vEnd.y - vStart.y ); + } + else + { + vOut.y = vStart.y; + } + } + break; + case INTERPOLATE_HOLD: + { + vOut.y = vStart.y; + } + break; + } +} diff --git a/public/kevvaluescompiler.cpp b/public/kevvaluescompiler.cpp index 432e1afb..08f7dab7 100644 --- a/public/kevvaluescompiler.cpp +++ b/public/kevvaluescompiler.cpp @@ -1,436 +1,436 @@ -//====== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======= -// -// Purpose: -// -//============================================================================= -#include "keyvaluescompiler.h" -#include "FileSystem.h" -#include "tier1/KeyValues.h" - -extern IFileSystem *g_pFullFileSystem; - -bool CRunTimeKeyValuesStringTable::ReadStringTable( int numStrings, CUtlBuffer& buf ) -{ - Assert( m_Strings.Count() == 0 ); - - CUtlVector< int > offsets; - offsets.EnsureCapacity( numStrings ); - - offsets.CopyArray( (int *)( buf.PeekGet() ), numStrings ); - - // Skip over data - buf.SeekGet( CUtlBuffer::SEEK_HEAD, buf.TellGet() + numStrings * sizeof( int ) ); - - int stringSize = buf.GetInt(); - - // Read in the string table - m_Strings.EnsureCapacity( numStrings ); - int i; - for ( i = 0 ; i < numStrings; ++i ) - { - m_Strings.AddToTail( (const char *)buf.PeekGet( offsets[ i ] ) ); - } - - buf.SeekGet( CUtlBuffer::SEEK_HEAD, buf.TellGet() + stringSize ); - - return true; -} - -void CCompiledKeyValuesWriter::BuildKVData_R( KeyValues *kv, int parent ) -{ - // Add self - KVInfo_t info; - info.key = m_StringTable.AddString( kv->GetName() ); - info.value = m_StringTable.AddString( kv->GetString() ); - - info.SetSubTree( kv->GetFirstSubKey() != NULL ? true : false ); - info.SetParent( parent ); - - int newParent = m_Data.AddToTail( info ); - - // Then add children - for ( KeyValues *sub = kv->GetFirstSubKey(); sub; sub = sub->GetNextKey() ) - { - BuildKVData_R( sub, newParent ); - } - - // Then add peers - if ( parent == -1 ) - { - if ( kv->GetNextKey() ) - { - BuildKVData_R( kv->GetNextKey(), parent ); - } - } -} - -void CCompiledKeyValuesWriter::Describe( const KVFile_t& file ) -{ - Msg( "file( %s )\n", m_StringTable.String( file.filename ) ); - - int c = file.numElements; - for ( int i = 0; i < c; ++i ) - { - KVInfo_t &info = m_Data[ file.firstElement + i ]; - if ( info.IsSubTree() ) - { - Msg( "%d: %s -> subtree at parent %i\n", - file.firstElement + i, - m_StringTable.String( info.key ), - info.GetParent() ); - } - else - { - Msg( "%d: %s -> %s at parent %i\n", - file.firstElement + i, - m_StringTable.String( info.key ), - m_StringTable.String( info.value ), - info.GetParent() ); - } - } -} - -void CCompiledKeyValuesWriter::AppendKeyValuesFile( char const *filename ) -{ - KVFile_t kvf; - kvf.filename = m_StringTable.AddString( filename ); - kvf.firstElement = m_Data.Count(); - - KeyValues *kv = new KeyValues( filename ); - if ( kv->LoadFromFile( g_pFullFileSystem, filename ) ) - { - // Add to dictionary - // do a depth first traversal of the keyvalues - BuildKVData_R( kv, -1 ); - } - kv->deleteThis(); - - kvf.numElements = m_Data.Count() - kvf.firstElement; - -// Describe( kvf ); - - m_Files.AddToTail( kvf ); -} - -void CCompiledKeyValuesWriter::WriteData( CUtlBuffer& buf ) -{ - int c = m_Data.Count(); - buf.PutInt( c ); - for ( int i = 0; i < c; ++i ) - { - KVInfo_t &info = m_Data[ i ]; - buf.PutShort( info.key ); - buf.PutShort( info.value ); - buf.PutShort( info.GetParent() ); - buf.PutChar( info.IsSubTree() ? 1 : 0 ); - } -} - -void CCompiledKeyValuesWriter::WriteFiles( CUtlBuffer &buf ) -{ - int c = m_Files.Count(); - buf.PutInt( c ); - for ( int i = 0; i < c; ++i ) - { - KVFile_t &file = m_Files[ i ]; - buf.PutShort( file.filename ); - buf.PutShort( file.firstElement ); - buf.PutShort( file.numElements ); - } -} - -void CCompiledKeyValuesWriter::WriteStringTable( CUtlBuffer& buf ) -{ - int i; - CUtlVector< int > offsets; - - CUtlBuffer stringBuffer; - - offsets.AddToTail( stringBuffer.TellPut() ); - - stringBuffer.PutString( "" ); - // save all the rest - int c = m_StringTable.GetNumStrings(); - for ( i = 1; i < c; i++) - { - offsets.AddToTail( stringBuffer.TellPut() ); - stringBuffer.PutString( m_StringTable.String( i ) ); - } - - buf.Put( offsets.Base(), offsets.Count() * sizeof( int ) ); - - buf.PutInt( stringBuffer.TellPut() ); - buf.Put( stringBuffer.Base(), stringBuffer.TellPut() ); -} - -void CCompiledKeyValuesWriter::WriteFile( char const *outfile ) -{ - CUtlBuffer buf; - - // Write the data file out - KVHeader_t header; - header.fileid = COMPILED_KEYVALUES_ID; - header.version = COMPILED_KEYVALUES_VERSION; - header.numStrings = m_StringTable.GetNumStrings(); - - buf.Put( &header, sizeof( header ) ); - - WriteStringTable( buf ); - WriteData( buf ); - WriteFiles( buf ); - - g_pFullFileSystem->WriteFile( outfile, NULL, buf ); -} - -CCompiledKeyValuesReader::CCompiledKeyValuesReader() - : m_Dict( 0, 0, FileInfo_t::Less ) -{ -} - -int CCompiledKeyValuesReader::First() const -{ - return m_Dict.FirstInorder(); -} - -int CCompiledKeyValuesReader::Next( int i ) const -{ - return m_Dict.NextInorder( i ); -} - -int CCompiledKeyValuesReader::InvalidIndex() const -{ - return m_Dict.InvalidIndex(); -} - -void CCompiledKeyValuesReader::GetFileName( int index, char *buf, size_t bufsize ) -{ - Assert( buf ); - buf[ 0 ] = 0; - FileNameHandle_t& handle = m_Dict[ index ].hFile; - g_pFullFileSystem->String( handle, buf, bufsize ); -} - -bool CCompiledKeyValuesReader::LoadFile( char const *filename ) -{ - int i; - m_LoadBuffer.Purge(); - - g_pFullFileSystem->ReadFile( filename, NULL, m_LoadBuffer ); - - KVHeader_t header; - m_LoadBuffer.Get( &header, sizeof( header ) ); - - if ( header.fileid != COMPILED_KEYVALUES_ID ) - { - return false; - } - - if ( header.version != COMPILED_KEYVALUES_VERSION ) - { - return false; - } - - if ( !m_StringTable.ReadStringTable( header.numStrings, m_LoadBuffer ) ) - { - return false; - } - - // Now parse the data - int dataCount = m_LoadBuffer.GetInt(); - m_Data.EnsureCapacity( dataCount ); - for ( i = 0; i < dataCount; ++i ) - { - KVInfo_t info; - info.key = m_LoadBuffer.GetShort(); - info.value = m_LoadBuffer.GetShort(); - info.SetParent( m_LoadBuffer.GetShort() ); - info.SetSubTree( m_LoadBuffer.GetChar() == 1 ? true : false ); - m_Data.AddToTail( info ); - } - - int fileCount = m_LoadBuffer.GetInt(); - for ( i = 0; i < fileCount; ++i ) - { - FileInfo_t kvf; - short fileNameString = m_LoadBuffer.GetShort(); - - kvf.hFile = g_pFullFileSystem->FindOrAddFileName( m_StringTable.Lookup( fileNameString ) ); - kvf.nFirstIndex = m_LoadBuffer.GetShort(); - kvf.nCount = m_LoadBuffer.GetShort(); - - m_Dict.Insert( kvf ); - } - - return true; -} - -struct CreateHelper_t -{ - int index; - KeyValues *kv; - KeyValues *tail; - - static bool Less( const CreateHelper_t& lhs, const CreateHelper_t& rhs ) - { - return lhs.index < rhs.index; - } -}; - -KeyValues *CCompiledKeyValuesReader::CreateFromData( const FileInfo_t& info ) -{ - KeyValues *head = new KeyValues( "" ); - if ( CreateInPlaceFromData( *head, info ) ) - { - return head; - } - else - { - head->deleteThis(); - return NULL; - } -} - -bool CCompiledKeyValuesReader::CreateInPlaceFromData( KeyValues& head, const FileInfo_t& info ) -{ - int first = info.nFirstIndex; - int num = info.nCount; - - KeyValues *root = NULL; - KeyValues *tail = NULL; - - CUtlRBTree< CreateHelper_t, int > helper( 0, 0, CreateHelper_t::Less ); - - for ( int i = 0; i < num; ++i ) - { - int offset = first + i; - KVInfo_t& info = m_Data[ offset ]; - - if ( info.GetParent() != -1 ) - { - CreateHelper_t search; - search.index = info.GetParent(); - int idx = helper.Find( search ); - if ( idx == helper.InvalidIndex() ) - { - return NULL; - } - - KeyValues *parent = helper[ idx ].kv; - Assert( parent ); - - KeyValues *sub = new KeyValues( m_StringTable.Lookup( info.key ) ); - - if ( !info.IsSubTree() ) - { - sub->SetStringValue(m_StringTable.Lookup( info.value ) ); - } - - if ( !parent->GetFirstSubKey() ) - { - parent->AddSubKey( sub ); - } - else - { - KeyValues *last = helper[ idx ].tail; - last->SetNextKey( sub ); - } - - helper[ idx ].tail = sub; - - CreateHelper_t insert; - insert.index = offset; - insert.kv = sub; - insert.tail = NULL; - helper.Insert( insert ); - } - else - { - if ( !root ) - { - root = &head; - root->SetName( m_StringTable.Lookup( info.key ) ); - tail = root; - - CreateHelper_t insert; - insert.index = offset; - insert.kv = root; - insert.tail = NULL; - helper.Insert( insert ); - } - else - { - CreateHelper_t insert; - insert.index = offset; - insert.kv = new KeyValues( m_StringTable.Lookup( info.key ) ); - insert.tail = NULL; - helper.Insert( insert ); - - tail->SetNextKey( insert.kv ); - tail = insert.kv; - } - } - } - return true; -} - - -bool CCompiledKeyValuesReader::InstanceInPlace( KeyValues& head, char const *kvfilename ) -{ - char sz[ 512 ]; - Q_strncpy( sz, kvfilename, sizeof( sz ) ); - Q_FixSlashes( sz ); - - FileInfo_t search; - search.hFile = g_pFullFileSystem->FindOrAddFileName( sz ); - - int idx = m_Dict.Find( search ); - if ( idx == m_Dict.InvalidIndex() ) - { - return false; - } - - const FileInfo_t& info = m_Dict[ idx ]; - - return CreateInPlaceFromData( head, info ); -} - -KeyValues *CCompiledKeyValuesReader::Instance( char const *kvfilename ) -{ - char sz[ 512 ]; - Q_strncpy( sz, kvfilename, sizeof( sz ) ); - Q_FixSlashes( sz ); - - FileInfo_t search; - search.hFile = g_pFullFileSystem->FindOrAddFileName( sz ); - - int idx = m_Dict.Find( search ); - if ( idx == m_Dict.InvalidIndex() ) - { - return NULL; - } - - const FileInfo_t& info = m_Dict[ idx ]; - - return CreateFromData( info ); -} - -bool CCompiledKeyValuesReader::LookupKeyValuesRootKeyName( char const *kvfilename, char *outbuf, size_t bufsize ) -{ - char sz[ 512 ]; - Q_strncpy( sz, kvfilename, sizeof( sz ) ); - Q_FixSlashes( sz ); - - FileInfo_t search; - search.hFile = g_pFullFileSystem->FindOrAddFileName( sz ); - - int idx = m_Dict.Find( search ); - if ( idx == m_Dict.InvalidIndex() ) - { - return false; - } - - const FileInfo_t& info = m_Dict[ idx ]; - - Q_strncpy( outbuf, m_StringTable.Lookup( m_Data[ info.nFirstIndex ].key ), bufsize ); - return true; -} +//====== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======= +// +// Purpose: +// +//============================================================================= +#include "keyvaluescompiler.h" +#include "filesystem.h" +#include "tier1/KeyValues.h" + +extern IFileSystem *g_pFullFileSystem; + +bool CRunTimeKeyValuesStringTable::ReadStringTable( int numStrings, CUtlBuffer& buf ) +{ + Assert( m_Strings.Count() == 0 ); + + CUtlVector< int > offsets; + offsets.EnsureCapacity( numStrings ); + + offsets.CopyArray( (int *)( buf.PeekGet() ), numStrings ); + + // Skip over data + buf.SeekGet( CUtlBuffer::SEEK_HEAD, buf.TellGet() + numStrings * sizeof( int ) ); + + int stringSize = buf.GetInt(); + + // Read in the string table + m_Strings.EnsureCapacity( numStrings ); + int i; + for ( i = 0 ; i < numStrings; ++i ) + { + m_Strings.AddToTail( (const char *)buf.PeekGet( offsets[ i ] ) ); + } + + buf.SeekGet( CUtlBuffer::SEEK_HEAD, buf.TellGet() + stringSize ); + + return true; +} + +void CCompiledKeyValuesWriter::BuildKVData_R( KeyValues *kv, int parent ) +{ + // Add self + KVInfo_t info; + info.key = m_StringTable.AddString( kv->GetName() ); + info.value = m_StringTable.AddString( kv->GetString() ); + + info.SetSubTree( kv->GetFirstSubKey() != NULL ? true : false ); + info.SetParent( parent ); + + int newParent = m_Data.AddToTail( info ); + + // Then add children + for ( KeyValues *sub = kv->GetFirstSubKey(); sub; sub = sub->GetNextKey() ) + { + BuildKVData_R( sub, newParent ); + } + + // Then add peers + if ( parent == -1 ) + { + if ( kv->GetNextKey() ) + { + BuildKVData_R( kv->GetNextKey(), parent ); + } + } +} + +void CCompiledKeyValuesWriter::Describe( const KVFile_t& file ) +{ + Msg( "file( %s )\n", m_StringTable.String( file.filename ) ); + + int c = file.numElements; + for ( int i = 0; i < c; ++i ) + { + KVInfo_t &info = m_Data[ file.firstElement + i ]; + if ( info.IsSubTree() ) + { + Msg( "%d: %s -> subtree at parent %i\n", + file.firstElement + i, + m_StringTable.String( info.key ), + info.GetParent() ); + } + else + { + Msg( "%d: %s -> %s at parent %i\n", + file.firstElement + i, + m_StringTable.String( info.key ), + m_StringTable.String( info.value ), + info.GetParent() ); + } + } +} + +void CCompiledKeyValuesWriter::AppendKeyValuesFile( char const *filename ) +{ + KVFile_t kvf; + kvf.filename = m_StringTable.AddString( filename ); + kvf.firstElement = m_Data.Count(); + + KeyValues *kv = new KeyValues( filename ); + if ( kv->LoadFromFile( g_pFullFileSystem, filename ) ) + { + // Add to dictionary + // do a depth first traversal of the keyvalues + BuildKVData_R( kv, -1 ); + } + kv->deleteThis(); + + kvf.numElements = m_Data.Count() - kvf.firstElement; + +// Describe( kvf ); + + m_Files.AddToTail( kvf ); +} + +void CCompiledKeyValuesWriter::WriteData( CUtlBuffer& buf ) +{ + int c = m_Data.Count(); + buf.PutInt( c ); + for ( int i = 0; i < c; ++i ) + { + KVInfo_t &info = m_Data[ i ]; + buf.PutShort( info.key ); + buf.PutShort( info.value ); + buf.PutShort( info.GetParent() ); + buf.PutChar( info.IsSubTree() ? 1 : 0 ); + } +} + +void CCompiledKeyValuesWriter::WriteFiles( CUtlBuffer &buf ) +{ + int c = m_Files.Count(); + buf.PutInt( c ); + for ( int i = 0; i < c; ++i ) + { + KVFile_t &file = m_Files[ i ]; + buf.PutShort( file.filename ); + buf.PutShort( file.firstElement ); + buf.PutShort( file.numElements ); + } +} + +void CCompiledKeyValuesWriter::WriteStringTable( CUtlBuffer& buf ) +{ + int i; + CUtlVector< int > offsets; + + CUtlBuffer stringBuffer; + + offsets.AddToTail( stringBuffer.TellPut() ); + + stringBuffer.PutString( "" ); + // save all the rest + int c = m_StringTable.GetNumStrings(); + for ( i = 1; i < c; i++) + { + offsets.AddToTail( stringBuffer.TellPut() ); + stringBuffer.PutString( m_StringTable.String( i ) ); + } + + buf.Put( offsets.Base(), offsets.Count() * sizeof( int ) ); + + buf.PutInt( stringBuffer.TellPut() ); + buf.Put( stringBuffer.Base(), stringBuffer.TellPut() ); +} + +void CCompiledKeyValuesWriter::WriteFile( char const *outfile ) +{ + CUtlBuffer buf; + + // Write the data file out + KVHeader_t header; + header.fileid = COMPILED_KEYVALUES_ID; + header.version = COMPILED_KEYVALUES_VERSION; + header.numStrings = m_StringTable.GetNumStrings(); + + buf.Put( &header, sizeof( header ) ); + + WriteStringTable( buf ); + WriteData( buf ); + WriteFiles( buf ); + + g_pFullFileSystem->WriteFile( outfile, NULL, buf ); +} + +CCompiledKeyValuesReader::CCompiledKeyValuesReader() + : m_Dict( 0, 0, FileInfo_t::Less ) +{ +} + +int CCompiledKeyValuesReader::First() const +{ + return m_Dict.FirstInorder(); +} + +int CCompiledKeyValuesReader::Next( int i ) const +{ + return m_Dict.NextInorder( i ); +} + +int CCompiledKeyValuesReader::InvalidIndex() const +{ + return m_Dict.InvalidIndex(); +} + +void CCompiledKeyValuesReader::GetFileName( int index, char *buf, size_t bufsize ) +{ + Assert( buf ); + buf[ 0 ] = 0; + FileNameHandle_t& handle = m_Dict[ index ].hFile; + g_pFullFileSystem->String( handle, buf, bufsize ); +} + +bool CCompiledKeyValuesReader::LoadFile( char const *filename ) +{ + int i; + m_LoadBuffer.Purge(); + + g_pFullFileSystem->ReadFile( filename, NULL, m_LoadBuffer ); + + KVHeader_t header; + m_LoadBuffer.Get( &header, sizeof( header ) ); + + if ( header.fileid != COMPILED_KEYVALUES_ID ) + { + return false; + } + + if ( header.version != COMPILED_KEYVALUES_VERSION ) + { + return false; + } + + if ( !m_StringTable.ReadStringTable( header.numStrings, m_LoadBuffer ) ) + { + return false; + } + + // Now parse the data + int dataCount = m_LoadBuffer.GetInt(); + m_Data.EnsureCapacity( dataCount ); + for ( i = 0; i < dataCount; ++i ) + { + KVInfo_t info; + info.key = m_LoadBuffer.GetShort(); + info.value = m_LoadBuffer.GetShort(); + info.SetParent( m_LoadBuffer.GetShort() ); + info.SetSubTree( m_LoadBuffer.GetChar() == 1 ? true : false ); + m_Data.AddToTail( info ); + } + + int fileCount = m_LoadBuffer.GetInt(); + for ( i = 0; i < fileCount; ++i ) + { + FileInfo_t kvf; + short fileNameString = m_LoadBuffer.GetShort(); + + kvf.hFile = g_pFullFileSystem->FindOrAddFileName( m_StringTable.Lookup( fileNameString ) ); + kvf.nFirstIndex = m_LoadBuffer.GetShort(); + kvf.nCount = m_LoadBuffer.GetShort(); + + m_Dict.Insert( kvf ); + } + + return true; +} + +struct CreateHelper_t +{ + int index; + KeyValues *kv; + KeyValues *tail; + + static bool Less( const CreateHelper_t& lhs, const CreateHelper_t& rhs ) + { + return lhs.index < rhs.index; + } +}; + +KeyValues *CCompiledKeyValuesReader::CreateFromData( const FileInfo_t& info ) +{ + KeyValues *head = new KeyValues( "" ); + if ( CreateInPlaceFromData( *head, info ) ) + { + return head; + } + else + { + head->deleteThis(); + return NULL; + } +} + +bool CCompiledKeyValuesReader::CreateInPlaceFromData( KeyValues& head, const FileInfo_t& info ) +{ + int first = info.nFirstIndex; + int num = info.nCount; + + KeyValues *root = NULL; + KeyValues *tail = NULL; + + CUtlRBTree< CreateHelper_t, int > helper( 0, 0, CreateHelper_t::Less ); + + for ( int i = 0; i < num; ++i ) + { + int offset = first + i; + KVInfo_t& info = m_Data[ offset ]; + + if ( info.GetParent() != -1 ) + { + CreateHelper_t search; + search.index = info.GetParent(); + int idx = helper.Find( search ); + if ( idx == helper.InvalidIndex() ) + { + return false; + } + + KeyValues *parent = helper[ idx ].kv; + Assert( parent ); + + KeyValues *sub = new KeyValues( m_StringTable.Lookup( info.key ) ); + + if ( !info.IsSubTree() ) + { + sub->SetStringValue(m_StringTable.Lookup( info.value ) ); + } + + if ( !parent->GetFirstSubKey() ) + { + parent->AddSubKey( sub ); + } + else + { + KeyValues *last = helper[ idx ].tail; + last->SetNextKey( sub ); + } + + helper[ idx ].tail = sub; + + CreateHelper_t insert; + insert.index = offset; + insert.kv = sub; + insert.tail = NULL; + helper.Insert( insert ); + } + else + { + if ( !root ) + { + root = &head; + root->SetName( m_StringTable.Lookup( info.key ) ); + tail = root; + + CreateHelper_t insert; + insert.index = offset; + insert.kv = root; + insert.tail = NULL; + helper.Insert( insert ); + } + else + { + CreateHelper_t insert; + insert.index = offset; + insert.kv = new KeyValues( m_StringTable.Lookup( info.key ) ); + insert.tail = NULL; + helper.Insert( insert ); + + tail->SetNextKey( insert.kv ); + tail = insert.kv; + } + } + } + return true; +} + + +bool CCompiledKeyValuesReader::InstanceInPlace( KeyValues& head, char const *kvfilename ) +{ + char sz[ 512 ]; + Q_strncpy( sz, kvfilename, sizeof( sz ) ); + Q_FixSlashes( sz ); + + FileInfo_t search; + search.hFile = g_pFullFileSystem->FindOrAddFileName( sz ); + + int idx = m_Dict.Find( search ); + if ( idx == m_Dict.InvalidIndex() ) + { + return false; + } + + const FileInfo_t& info = m_Dict[ idx ]; + + return CreateInPlaceFromData( head, info ); +} + +KeyValues *CCompiledKeyValuesReader::Instance( char const *kvfilename ) +{ + char sz[ 512 ]; + Q_strncpy( sz, kvfilename, sizeof( sz ) ); + Q_FixSlashes( sz ); + + FileInfo_t search; + search.hFile = g_pFullFileSystem->FindOrAddFileName( sz ); + + int idx = m_Dict.Find( search ); + if ( idx == m_Dict.InvalidIndex() ) + { + return NULL; + } + + const FileInfo_t& info = m_Dict[ idx ]; + + return CreateFromData( info ); +} + +bool CCompiledKeyValuesReader::LookupKeyValuesRootKeyName( char const *kvfilename, char *outbuf, size_t bufsize ) +{ + char sz[ 512 ]; + Q_strncpy( sz, kvfilename, sizeof( sz ) ); + Q_FixSlashes( sz ); + + FileInfo_t search; + search.hFile = g_pFullFileSystem->FindOrAddFileName( sz ); + + int idx = m_Dict.Find( search ); + if ( idx == m_Dict.InvalidIndex() ) + { + return false; + } + + const FileInfo_t& info = m_Dict[ idx ]; + + Q_strncpy( outbuf, m_StringTable.Lookup( m_Data[ info.nFirstIndex ].key ), bufsize ); + return true; +} diff --git a/public/keyframe/keyframe.cpp b/public/keyframe/keyframe.cpp index 2710c826..35e1381b 100644 --- a/public/keyframe/keyframe.cpp +++ b/public/keyframe/keyframe.cpp @@ -13,7 +13,10 @@ #include typedef unsigned char byte; + +#ifdef _MSC_VER #pragma warning(disable:4244) +#endif #include "tier0/dbg.h" #include "vector.h" diff --git a/public/keyvaluescompiler.h b/public/keyvaluescompiler.h index fef2c482..c98a366f 100644 --- a/public/keyvaluescompiler.h +++ b/public/keyvaluescompiler.h @@ -1,187 +1,187 @@ -//====== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======= -// -// Purpose: -// -//============================================================================= - -#ifndef KEYVALUESCOMPILER_H -#define KEYVALUESCOMPILER_H -#ifdef _WIN32 -#pragma once -#endif - -#include "tier1/UtlBuffer.h" -#include "tier1/UtlSymbol.h" -#include "tier1/UtlDict.h" - -class KeyValues; - -#define COMPILED_KEYVALUES_ID MAKEID( 'V', 'K', 'V', 'F' ) - -#define COMPILED_KEYVALUES_VERSION 1 - -struct KVHeader_t -{ - int fileid; - int version; - int numStrings; -}; - -#pragma pack(1) -struct KVFile_t -{ - KVFile_t() : - filename( 0 ), - firstElement( 0 ), - numElements( 0 ) - { - } - short filename; - short firstElement; - short numElements; -}; - -struct KVInfo_t -{ - KVInfo_t() : - key( 0 ), - value( 0 ), - parentIndex( -1 ), - issubtree( false ) - { - } - - inline void SetParent( int index ) - { - Assert( index <= 32768 ); - parentIndex = index; - } - - inline short GetParent() const - { - return parentIndex; - } - - inline void SetSubTree( bool state ) - { - issubtree = state; - } - - inline bool IsSubTree() const - { - return issubtree; - } - - short key; - short value; - -private: - - short parentIndex; - bool issubtree; -}; -#pragma pack() - -//----------------------------------------------------------------------------- -// Purpose: stringtable is a session global string table. -//----------------------------------------------------------------------------- -class CCompiledKeyValuesWriter -{ -public: - - CCompiledKeyValuesWriter() - { - m_StringTable.AddString( "" ); - } - - void AppendKeyValuesFile( char const *filename ); - void WriteFile( char const *outfile ); - -private: - - void Describe( const KVFile_t& file ); - - void BuildKVData_R( KeyValues *kv, int parent ); - - void WriteStringTable( CUtlBuffer& buf ); - void WriteData( CUtlBuffer& buf ); - void WriteFiles( CUtlBuffer &buf ); - - CUtlVector< KVFile_t > m_Files; - CUtlVector< KVInfo_t > m_Data; - - CUtlSymbolTable m_StringTable; -}; - - -class CRunTimeKeyValuesStringTable -{ -public: - - bool ReadStringTable( int numStrings, CUtlBuffer& buf ); - - inline int CRunTimeKeyValuesStringTable::Count() const - { - return m_Strings.Count(); - } - - inline char const *Lookup( short index ) - { - return m_Strings[ index ]; - } - -private: - CUtlVector< const char * > m_Strings; -}; - -class CCompiledKeyValuesReader -{ -public: - - CCompiledKeyValuesReader(); - - bool LoadFile( char const *filename ); - - KeyValues *Instance( char const *kvfilename ); - bool InstanceInPlace( KeyValues& head, char const *kvfilename ); - bool LookupKeyValuesRootKeyName( char const *kvfilename, char *outbuf, size_t bufsize ); - - int First() const; - int Next( int i ) const; - int InvalidIndex() const; - - void GetFileName( int index, char *buf, size_t bufsize ); - -private: - - struct FileInfo_t - { - FileInfo_t() : - hFile( 0 ), - nFirstIndex( 0 ), - nCount( 0 ) - { - } - FileNameHandle_t hFile; - short nFirstIndex; - short nCount; - - static bool Less( const FileInfo_t& lhs, const FileInfo_t& rhs ) - { - return lhs.hFile < rhs.hFile; - } - }; - - KeyValues *CreateFromData( const FileInfo_t& info ); - bool CreateInPlaceFromData( KeyValues& head, const FileInfo_t& info ); - - // Now get the actual files - CUtlRBTree< FileInfo_t, unsigned short > m_Dict; - CUtlVector< KVInfo_t > m_Data; - - CRunTimeKeyValuesStringTable m_StringTable; - - CUtlBuffer m_LoadBuffer; -}; - -#endif // KEYVALUESCOMPILER_H +//====== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======= +// +// Purpose: +// +//============================================================================= + +#ifndef KEYVALUESCOMPILER_H +#define KEYVALUESCOMPILER_H +#ifdef _WIN32 +#pragma once +#endif + +#include "tier1/utlbuffer.h" +#include "tier1/utlsymbol.h" +#include "tier1/utldict.h" + +class KeyValues; + +#define COMPILED_KEYVALUES_ID MAKEID( 'V', 'K', 'V', 'F' ) + +#define COMPILED_KEYVALUES_VERSION 1 + +struct KVHeader_t +{ + int fileid; + int version; + int numStrings; +}; + +#pragma pack(1) +struct KVFile_t +{ + KVFile_t() : + filename( 0 ), + firstElement( 0 ), + numElements( 0 ) + { + } + short filename; + short firstElement; + short numElements; +}; + +struct KVInfo_t +{ + KVInfo_t() : + key( 0 ), + value( 0 ), + parentIndex( -1 ), + issubtree( false ) + { + } + + inline void SetParent( int index ) + { + Assert( index <= 32768 ); + parentIndex = index; + } + + inline short GetParent() const + { + return parentIndex; + } + + inline void SetSubTree( bool state ) + { + issubtree = state; + } + + inline bool IsSubTree() const + { + return issubtree; + } + + short key; + short value; + +private: + + short parentIndex; + bool issubtree; +}; +#pragma pack() + +//----------------------------------------------------------------------------- +// Purpose: stringtable is a session global string table. +//----------------------------------------------------------------------------- +class CCompiledKeyValuesWriter +{ +public: + + CCompiledKeyValuesWriter() + { + m_StringTable.AddString( "" ); + } + + void AppendKeyValuesFile( char const *filename ); + void WriteFile( char const *outfile ); + +private: + + void Describe( const KVFile_t& file ); + + void BuildKVData_R( KeyValues *kv, int parent ); + + void WriteStringTable( CUtlBuffer& buf ); + void WriteData( CUtlBuffer& buf ); + void WriteFiles( CUtlBuffer &buf ); + + CUtlVector< KVFile_t > m_Files; + CUtlVector< KVInfo_t > m_Data; + + CUtlSymbolTable m_StringTable; +}; + + +class CRunTimeKeyValuesStringTable +{ +public: + + bool ReadStringTable( int numStrings, CUtlBuffer& buf ); + + inline int Count() const + { + return m_Strings.Count(); + } + + inline char const *Lookup( short index ) + { + return m_Strings[ index ]; + } + +private: + CUtlVector< const char * > m_Strings; +}; + +class CCompiledKeyValuesReader +{ +public: + + CCompiledKeyValuesReader(); + + bool LoadFile( char const *filename ); + + KeyValues *Instance( char const *kvfilename ); + bool InstanceInPlace( KeyValues& head, char const *kvfilename ); + bool LookupKeyValuesRootKeyName( char const *kvfilename, char *outbuf, size_t bufsize ); + + int First() const; + int Next( int i ) const; + int InvalidIndex() const; + + void GetFileName( int index, char *buf, size_t bufsize ); + +private: + + struct FileInfo_t + { + FileInfo_t() : + hFile( 0 ), + nFirstIndex( 0 ), + nCount( 0 ) + { + } + FileNameHandle_t hFile; + short nFirstIndex; + short nCount; + + static bool Less( const FileInfo_t& lhs, const FileInfo_t& rhs ) + { + return lhs.hFile < rhs.hFile; + } + }; + + KeyValues *CreateFromData( const FileInfo_t& info ); + bool CreateInPlaceFromData( KeyValues& head, const FileInfo_t& info ); + + // Now get the actual files + CUtlRBTree< FileInfo_t, unsigned short > m_Dict; + CUtlVector< KVInfo_t > m_Data; + + CRunTimeKeyValuesStringTable m_StringTable; + + CUtlBuffer m_LoadBuffer; +}; + +#endif // KEYVALUESCOMPILER_H diff --git a/public/loadcmdline.cpp b/public/loadcmdline.cpp index 8810787e..9a29443c 100644 --- a/public/loadcmdline.cpp +++ b/public/loadcmdline.cpp @@ -1,123 +1,123 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: loads additional command line options from a config file -// -// $NoKeywords: $ -//=============================================================================// - -#include "KeyValues.h" -#include "vstdlib/strtools.h" -#include "FileSystem_Tools.h" -#include "tier1/utlstring.h" - -// So we know whether or not we own argv's memory -static bool sFoundConfigArgs = false; - -//----------------------------------------------------------------------------- -// Purpose: Parses arguments and adds them to argv and argc -//----------------------------------------------------------------------------- -static void AddArguments( int &argc, char **&argv, const char *str ) -{ - char **args = 0; - char *argList = 0; - int argCt = argc; - - argList = new char[ Q_strlen( str ) + 1 ]; - Q_strcpy( argList, str ); - - // Parse the arguments out of the string - char *token = strtok( argList, " " ); - while( token ) - { - ++argCt; - token = strtok( NULL, " " ); - } - - // Make sure someting was actually found in the file - if( argCt > argc ) - { - sFoundConfigArgs = true; - - // Allocate a new array for argv - args = new char*[ argCt ]; - - // Copy original arguments, up to the last one - int i; - for( i = 0; i < argc - 1; ++i ) - { - args[ i ] = new char[ Q_strlen( argv[ i ] ) + 1 ]; - Q_strcpy( args[ i ], argv[ i ] ); - } - - // copy new arguments - Q_strcpy( argList, str ); - token = strtok( argList, " " ); - for( ; i < argCt - 1; ++i ) - { - args[ i ] = new char[ Q_strlen( token ) + 1 ]; - Q_strcpy( args[ i ], token ); - token = strtok( NULL, " " ); - } - - // Copy the last original argument - args[ i ] = new char[ Q_strlen( argv[ argc - 1 ] ) + 1 ]; - Q_strcpy( args[ i ], argv[ argc - 1 ] ); - - argc = argCt; - argv = args; - } - - delete [] argList; -} - -//----------------------------------------------------------------------------- -// Purpose: Loads additional commandline arguments from a config file for an app. -// Filesystem must be initialized before calling this function. -// keyname: Name of the block containing the key/args pairs (ie map or model name) -// appname: Keyname for the commandline arguments to be loaded - typically the exe name. -//----------------------------------------------------------------------------- -void LoadCmdLineFromFile( int &argc, char **&argv, const char *keyname, const char *appname ) -{ - sFoundConfigArgs = false; - - assert( g_pFileSystem ); - if( !g_pFileSystem ) - return; - - // Load the cfg file, and find the keyname - KeyValues *kv = new KeyValues( "CommandLine" ); - - char filename[512]; - Q_snprintf( filename, sizeof( filename ), "%s/cfg/commandline.cfg", gamedir ); - - if ( kv->LoadFromFile( g_pFileSystem, filename ) ) - { - // Load the commandline arguments for this app - KeyValues *appKey = kv->FindKey( keyname ); - if( appKey ) - { - const char *str = appKey->GetString( appname ); - Msg( "Command Line found: %s\n", str ); - - AddArguments( argc, argv, str ); - } - } - - kv->deleteThis(); -} - -//----------------------------------------------------------------------------- -// Purpose: Cleans up any memory allocated for the new argv. Pass in the app's -// argc and argv - this is safe even if no extra arguments were loaded. -//----------------------------------------------------------------------------- -void DeleteCmdLine( int argc, char **argv ) -{ - if( !sFoundConfigArgs ) - return; - - for( int i = 0; i < argc; ++i ) - { - delete [] argv[i]; - } - delete [] argv; -} \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: loads additional command line options from a config file +// +// $NoKeywords: $ +//=============================================================================// + +#include "KeyValues.h" +#include "vstdlib/strtools.h" +#include "filesystem_tools.h" +#include "tier1/utlstring.h" + +// So we know whether or not we own argv's memory +static bool sFoundConfigArgs = false; + +//----------------------------------------------------------------------------- +// Purpose: Parses arguments and adds them to argv and argc +//----------------------------------------------------------------------------- +static void AddArguments( int &argc, char **&argv, const char *str ) +{ + char **args = 0; + char *argList = 0; + int argCt = argc; + + argList = new char[ Q_strlen( str ) + 1 ]; + Q_strcpy( argList, str ); + + // Parse the arguments out of the string + char *token = strtok( argList, " " ); + while( token ) + { + ++argCt; + token = strtok( NULL, " " ); + } + + // Make sure someting was actually found in the file + if( argCt > argc ) + { + sFoundConfigArgs = true; + + // Allocate a new array for argv + args = new char*[ argCt ]; + + // Copy original arguments, up to the last one + int i; + for( i = 0; i < argc - 1; ++i ) + { + args[ i ] = new char[ Q_strlen( argv[ i ] ) + 1 ]; + Q_strcpy( args[ i ], argv[ i ] ); + } + + // copy new arguments + Q_strcpy( argList, str ); + token = strtok( argList, " " ); + for( ; i < argCt - 1; ++i ) + { + args[ i ] = new char[ Q_strlen( token ) + 1 ]; + Q_strcpy( args[ i ], token ); + token = strtok( NULL, " " ); + } + + // Copy the last original argument + args[ i ] = new char[ Q_strlen( argv[ argc - 1 ] ) + 1 ]; + Q_strcpy( args[ i ], argv[ argc - 1 ] ); + + argc = argCt; + argv = args; + } + + delete [] argList; +} + +//----------------------------------------------------------------------------- +// Purpose: Loads additional commandline arguments from a config file for an app. +// Filesystem must be initialized before calling this function. +// keyname: Name of the block containing the key/args pairs (ie map or model name) +// appname: Keyname for the commandline arguments to be loaded - typically the exe name. +//----------------------------------------------------------------------------- +void LoadCmdLineFromFile( int &argc, char **&argv, const char *keyname, const char *appname ) +{ + sFoundConfigArgs = false; + + assert( g_pFileSystem ); + if( !g_pFileSystem ) + return; + + // Load the cfg file, and find the keyname + KeyValues *kv = new KeyValues( "CommandLine" ); + + char filename[512]; + Q_snprintf( filename, sizeof( filename ), "%s/cfg/commandline.cfg", gamedir ); + + if ( kv->LoadFromFile( g_pFileSystem, filename ) ) + { + // Load the commandline arguments for this app + KeyValues *appKey = kv->FindKey( keyname ); + if( appKey ) + { + const char *str = appKey->GetString( appname ); + Msg( "Command Line found: %s\n", str ); + + AddArguments( argc, argv, str ); + } + } + + kv->deleteThis(); +} + +//----------------------------------------------------------------------------- +// Purpose: Cleans up any memory allocated for the new argv. Pass in the app's +// argc and argv - this is safe even if no extra arguments were loaded. +//----------------------------------------------------------------------------- +void DeleteCmdLine( int argc, char **argv ) +{ + if( !sFoundConfigArgs ) + return; + + for( int i = 0; i < argc; ++i ) + { + delete [] argv[i]; + } + delete [] argv; +} diff --git a/public/materialsystem/shader_vcs_version.h b/public/materialsystem/shader_vcs_version.h index 29eacb2a..d23a797e 100644 --- a/public/materialsystem/shader_vcs_version.h +++ b/public/materialsystem/shader_vcs_version.h @@ -1,64 +1,63 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -//=============================================================================// - -#ifndef SHADER_VCS_VERSION_H -#define SHADER_VCS_VERSION_H -#ifdef _WIN32 -#pragma once -#endif - -// 1=hl2 shipped. 2=compressed with diffs version (lostcoast) -#define SHADER_VCS_VERSION_NUMBER 2 - -#pragma pack(1) -struct ShaderHeader_t_V1 -{ - int m_nVersion; - int m_nTotalCombos; - int m_nDynamicCombos; - unsigned int m_nFlags; - unsigned int m_nCentroidMask; -}; - -struct ShaderHeader_t : ShaderHeader_t_V1 -{ - int m_ReferenceComboSizeForDiffs; -}; - -struct ShaderDictionaryEntry_t -{ - int m_Offset; - int m_Size; -}; -#pragma pack() - -// 2=xbox shipped. -#define SHADER_XCS_VERSION_NUMBER 2 - -// xcs file format -#pragma pack(1) -struct XShaderDictionaryEntry_t -{ - int m_Offset; - unsigned short m_PackedSize; - unsigned short m_Size; -}; -struct XShaderHeader_t -{ - int m_nVersion; - int m_nTotalCombos; - int m_nDynamicCombos; - unsigned int m_nFlags; - unsigned int m_nCentroidMask; - int m_nReferenceShader; - - bool IsValid() { return m_nVersion == SHADER_XCS_VERSION_NUMBER; } - unsigned int BytesToPreload() { return sizeof( XShaderHeader_t ) + sizeof( XShaderDictionaryEntry_t ) * m_nTotalCombos; } -}; -#pragma pack() - -#endif // SHADER_VCS_VERSION_H - \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#ifndef SHADER_VCS_VERSION_H +#define SHADER_VCS_VERSION_H +#ifdef _WIN32 +#pragma once +#endif + +// 1=hl2 shipped. 2=compressed with diffs version (lostcoast) +#define SHADER_VCS_VERSION_NUMBER 2 + +#pragma pack(1) +struct ShaderHeader_t_V1 +{ + int m_nVersion; + int m_nTotalCombos; + int m_nDynamicCombos; + unsigned int m_nFlags; + unsigned int m_nCentroidMask; +}; + +struct ShaderHeader_t : ShaderHeader_t_V1 +{ + int m_ReferenceComboSizeForDiffs; +}; + +struct ShaderDictionaryEntry_t +{ + int m_Offset; + int m_Size; +}; +#pragma pack() + +// 2=xbox shipped. +#define SHADER_XCS_VERSION_NUMBER 2 + +// xcs file format +#pragma pack(1) +struct XShaderDictionaryEntry_t +{ + int m_Offset; + unsigned short m_PackedSize; + unsigned short m_Size; +}; +struct XShaderHeader_t +{ + int m_nVersion; + int m_nTotalCombos; + int m_nDynamicCombos; + unsigned int m_nFlags; + unsigned int m_nCentroidMask; + int m_nReferenceShader; + + bool IsValid() { return m_nVersion == SHADER_XCS_VERSION_NUMBER; } + unsigned int BytesToPreload() { return sizeof( XShaderHeader_t ) + sizeof( XShaderDictionaryEntry_t ) * m_nTotalCombos; } +}; +#pragma pack() + +#endif // SHADER_VCS_VERSION_H diff --git a/public/mathlib/math_base.h b/public/mathlib/math_base.h index 6b41e6c1..0b394834 100644 --- a/public/mathlib/math_base.h +++ b/public/mathlib/math_base.h @@ -13,6 +13,8 @@ #include "vector.h" #include "vector2d.h" #include "tier0/dbg.h" + +#undef MINMAX_H #include "minmax.h" #ifdef _WIN32 @@ -504,7 +506,7 @@ inline float RemapValClamped( float val, float A, float B, float C, float D) template FORCEINLINE_MATH T Lerp( float flPercent, T const &A, T const &B ) { - return A + (B - A) * flPercent; + return static_cast(A + (B - A) * flPercent); } // 5-argument floating point linear interpolation. @@ -1071,11 +1073,12 @@ inline unsigned long RoundFloatToUnsignedLong(float f) // Fast, accurate ftol: inline int Float2Int( float a ) { - int CtrlwdHolder; - int CtrlwdSetter; int RetVal; #ifdef _WIN32 + int CtrlwdHolder; + int CtrlwdSetter; + __asm { fld a // push 'a' onto the FP stack @@ -1098,11 +1101,12 @@ inline int Float2Int( float a ) // Over 15x faster than: (int)floor(value) inline int Floor2Int( float a ) { - int CtrlwdHolder; - int CtrlwdSetter; int RetVal; #ifdef _WIN32 + int CtrlwdHolder; + int CtrlwdSetter; + __asm { fld a // push 'a' onto the FP stack @@ -1146,11 +1150,12 @@ inline float ClampToMsec( float in ) // Over 15x faster than: (int)ceil(value) inline int Ceil2Int( float a ) { - int CtrlwdHolder; - int CtrlwdSetter; int RetVal; #ifdef _WIN32 + int CtrlwdHolder; + int CtrlwdSetter; + __asm { fld a // push 'a' onto the FP stack diff --git a/public/mathlib/ssemath.h b/public/mathlib/ssemath.h index c7dc3c6d..a73d935f 100644 --- a/public/mathlib/ssemath.h +++ b/public/mathlib/ssemath.h @@ -1,400 +1,403 @@ -// $Id$ - - -// ssemath.h - defines sse-based "structure of arrays" classes and functions. - - -#ifndef SSEMATH_H -#define SSEMATH_H - -//#ifdef _WIN32 - -#include -#include - -#ifdef _LINUX -typedef int int32; -typedef union __attribute__ ((aligned (16))) - { - float m128_f32[4]; - } l_m128; - -#endif - -// I thought about defining a class/union for the sse packed floats instead of using __m128, -// but decided against it because (a) the nature of sse code which includes comparisons is to blur -// the relationship between packed floats and packed integer types and (b) not sure that the -// compiler would handle generating good code for the intrinsics. - - -// useful constants in SSE packed float format: -extern __m128 Four_Zeros; // 0 0 0 0 -extern __m128 Four_Ones; // 1 1 1 1 -extern __m128 Four_PointFives; // .5 .5 .5 .5 -extern __m128 Four_Epsilons; // FLT_EPSILON FLT_EPSILON FLT_EPSILON FLT_EPSILON - - -/// replicate a single floating point value to all 4 components of an sse packed float -static inline __m128 MMReplicate(float f) -{ - __m128 value=_mm_set_ss(f); - return _mm_shuffle_ps(value,value,0); -} - -/// replicate a single 32 bit integer value to all 4 components of an m128 -static inline __m128 MMReplicateI(int i) -{ - __m128 value=_mm_set_ss(*((float *)&i));; - return _mm_shuffle_ps(value,value,0); -} - -/// 1/x for all 4 values. uses reciprocal approximation instruction plus newton iteration. -/// No error checking! -static inline __m128 MMReciprocal(__m128 x) -{ - __m128 ret=_mm_rcp_ps(x); - // newton iteration is Y(n+1)=2*Y(N)-x*Y(n)^2 - ret=_mm_sub_ps(_mm_add_ps(ret,ret),_mm_mul_ps(x,_mm_mul_ps(ret,ret))); - return ret; -} -/// 1/x for all 4 values. uses reciprocal approximation instruction plus newton iteration. -/// 1/0 will result in a big but NOT infinite result -static inline __m128 MMReciprocalSaturate(__m128 x) -{ - __m128 zero_mask=_mm_cmpeq_ps(x,Four_Zeros); - x=_mm_or_ps(x,_mm_and_ps(Four_Epsilons,zero_mask)); - __m128 ret=_mm_rcp_ps(x); - // newton iteration is Y(n+1)=2*Y(N)-x*Y(n)^2 - ret=_mm_sub_ps(_mm_add_ps(ret,ret),_mm_mul_ps(x,_mm_mul_ps(ret,ret))); - return ret; -} - -/// class FourVectors stores 4 independent vectors for use by sse processing. These vectors are -/// stored in the format x x x x y y y y z z z z so that they can be efficiently accelerated by -/// SSE. -class FourVectors -{ -public: - __m128 x,y,z; // x x x x y y y y z z z z - - inline void DuplicateVector(Vector const &v) //< set all 4 vectors to the same vector value - { - x=MMReplicate(v.x); - y=MMReplicate(v.y); - z=MMReplicate(v.z); - } - - inline __m128 const & operator[](int idx) const - { - return *((&x)+idx); - } - - inline __m128 & operator[](int idx) - { - return *((&x)+idx); - } - - inline void operator+=(FourVectors const &b) //< add 4 vectors to another 4 vectors - { - x=_mm_add_ps(x,b.x); - y=_mm_add_ps(y,b.y); - z=_mm_add_ps(z,b.z); - } - inline void operator-=(FourVectors const &b) //< subtract 4 vectors from another 4 - { - x=_mm_sub_ps(x,b.x); - y=_mm_sub_ps(y,b.y); - z=_mm_sub_ps(z,b.z); - } - - inline void operator*=(FourVectors const &b) //< scale all four vectors per component scale - { - x=_mm_mul_ps(x,b.x); - y=_mm_mul_ps(y,b.y); - z=_mm_mul_ps(z,b.z); - } - - inline void operator*=(float scale) //< uniformly scale all 4 vectors - { - __m128 scalepacked=MMReplicate(scale); - x=_mm_mul_ps(x,scalepacked); - y=_mm_mul_ps(y,scalepacked); - z=_mm_mul_ps(z,scalepacked); - } - - inline void operator*=(__m128 scale) //< scale - { - x=_mm_mul_ps(x,scale); - y=_mm_mul_ps(y,scale); - z=_mm_mul_ps(z,scale); - } - - inline __m128 operator*(FourVectors const &b) const //< 4 dot products - { - __m128 dot=_mm_mul_ps(x,b.x); - dot=_mm_add_ps(dot,_mm_mul_ps(y,b.y)); - dot=_mm_add_ps(dot,_mm_mul_ps(z,b.z)); - return dot; - } - - inline __m128 operator*(Vector const &b) const //< dot product all 4 vectors with 1 vector - { - __m128 dot=_mm_mul_ps(x,MMReplicate(b.x)); - dot=_mm_add_ps(dot,_mm_mul_ps(y,MMReplicate(b.y))); - dot=_mm_add_ps(dot,_mm_mul_ps(z,MMReplicate(b.z))); - return dot; - } - - - inline void VProduct(FourVectors const &b) //< component by component mul - { - x=_mm_mul_ps(x,b.x); - y=_mm_mul_ps(y,b.y); - z=_mm_mul_ps(z,b.z); - } - inline void MakeReciprocal(void) //< (x,y,z)=(1/x,1/y,1/z) - { - x=MMReciprocal(x); - y=MMReciprocal(y); - z=MMReciprocal(z); - } - - inline void MakeReciprocalSaturate(void) //< (x,y,z)=(1/x,1/y,1/z), 1/0=1.0e23 - { - x=MMReciprocalSaturate(x); - y=MMReciprocalSaturate(y); - z=MMReciprocalSaturate(z); - } - - // X(),Y(),Z() - get at the desired component of the i'th (0..3) vector. - inline float const &X(int idx) const - { -#ifdef _LINUX - return ((l_m128*)&x)->m128_f32[idx]; -#else - return x.m128_f32[idx]; -#endif - } - - inline float const &Y(int idx) const - { -#ifdef _LINUX - return ((l_m128*)&y)->m128_f32[idx]; -#else - return y.m128_f32[idx]; -#endif - } - inline float const &Z(int idx) const - { -#ifdef _LINUX - return ((l_m128*)&z)->m128_f32[idx]; -#else - return z.m128_f32[idx]; -#endif - } - - // X(),Y(),Z() - get at the desired component of the i'th (0..3) vector. - inline float &X(int idx) - { -#ifdef _LINUX - return ((l_m128*)&x)->m128_f32[idx]; -#else - return x.m128_f32[idx]; -#endif - } - - inline float &Y(int idx) - { -#ifdef _LINUX - return ((l_m128*)&y)->m128_f32[idx]; -#else - return y.m128_f32[idx]; -#endif - } - inline float &Z(int idx) - { -#ifdef _LINUX - return ((l_m128*)&z)->m128_f32[idx]; -#else - return z.m128_f32[idx]; -#endif - } - - inline Vector Vec(int idx) const //< unpack one of the vectors - { - return Vector(X(idx),Y(idx),Z(idx)); - } - - FourVectors(void) - { - } - - /// LoadAndSwizzle - load 4 Vectors into a FourVectors, perofrming transpose op - inline void LoadAndSwizzle(Vector const &a, Vector const &b, Vector const &c, Vector const &d) - { - __m128 row0=_mm_loadu_ps(&(a.x)); - __m128 row1=_mm_loadu_ps(&(b.x)); - __m128 row2=_mm_loadu_ps(&(c.x)); - __m128 row3=_mm_loadu_ps(&(d.x)); - // now, matrix is: - // x y z ? - // x y z ? - // x y z ? - // x y z ? - _MM_TRANSPOSE4_PS(row0,row1,row2,row3); - x=row0; - y=row1; - z=row2; - } - - /// LoadAndSwizzleAligned - load 4 Vectors into a FourVectors, perofrming transpose op. - /// all 4 vectors muyst be 128 bit boundary - inline void LoadAndSwizzleAligned(Vector const &a, Vector const &b, Vector const &c, Vector const &d) - { - __m128 row0=_mm_load_ps(&(a.x)); - __m128 row1=_mm_load_ps(&(b.x)); - __m128 row2=_mm_load_ps(&(c.x)); - __m128 row3=_mm_load_ps(&(d.x)); - // now, matrix is: - // x y z ? - // x y z ? - // x y z ? - // x y z ? - _MM_TRANSPOSE4_PS(row0,row1,row2,row3); - x=row0; - y=row1; - z=row2; - } - - /// return the squared length of all 4 vectors - inline __m128 length2(void) const - { - return (*this)*(*this); - } - - /// return the approximate length of all 4 vectors. uses the sse sqrt approximation instr - inline __m128 length(void) const - { - return _mm_sqrt_ps(length2()); - } - - /// normalize all 4 vectors in place. not mega-accurate (uses sse reciprocal approximation instr) - inline void VectorNormalizeFast(void) - { - __m128 mag_sq=(*this)*(*this); // length^2 - (*this) *= _mm_rsqrt_ps(mag_sq); // *(1.0/sqrt(length^2)) - } - - /// normnalize all 4 vectors in place. uses newton iteration for higher precision results than - /// from VectorNormalizeFast. - inline void VectorNormalize(void) - { - static __m128 FourHalves={0.5,0.5,0.5,0.5}; - static __m128 FourThrees={3.,3.,3.,3.}; - __m128 mag_sq=(*this)*(*this); // length^2 - __m128 guess=_mm_rsqrt_ps(mag_sq); - // newton iteration for 1/sqrt(x) : y(n+1)=1/2 (y(n)*(3-x*y(n)^2)); - guess=_mm_mul_ps(guess,_mm_sub_ps(FourThrees,_mm_mul_ps(mag_sq,_mm_mul_ps(guess,guess)))); - guess=_mm_mul_ps(Four_PointFives,guess); - (*this) *= guess; - } - - /// construct a FourVectors from 4 separate Vectors - inline FourVectors(Vector const &a, Vector const &b, Vector const &c, Vector const &d) - { - LoadAndSwizzle(a,b,c,d); - } - -}; - -/// form 4 cross products -inline FourVectors operator ^(const FourVectors &a, const FourVectors &b) -{ - FourVectors ret; - ret.x=_mm_sub_ps(_mm_mul_ps(a.y,b.z),_mm_mul_ps(a.z,b.y)); - ret.y=_mm_sub_ps(_mm_mul_ps(a.z,b.x),_mm_mul_ps(a.x,b.z)); - ret.z=_mm_sub_ps(_mm_mul_ps(a.x,b.y),_mm_mul_ps(a.y,b.x)); - return ret; -} - -/// component-by-componentwise MAX operator -inline FourVectors maximum(const FourVectors &a, const FourVectors &b) -{ - FourVectors ret; - ret.x=_mm_max_ps(a.x,b.x); - ret.y=_mm_max_ps(a.y,b.y); - ret.z=_mm_max_ps(a.z,b.z); - return ret; -} - -/// component-by-componentwise MIN operator -inline FourVectors minimum(const FourVectors &a, const FourVectors &b) -{ - FourVectors ret; - ret.x=_mm_min_ps(a.x,b.x); - ret.y=_mm_min_ps(a.y,b.y); - ret.z=_mm_min_ps(a.z,b.z); - return ret; -} - - -/// check an m128 for all zeros. Not sure if this is the best way -inline bool IsAllZeros(__m128 var) -{ -#ifdef _WIN32 - __declspec(align(16)) int32 myints[4]; -#elif _LINUX - int32 myints[4] __attribute__ ((aligned (16))); -#endif - _mm_store_ps((float *) myints,var); - return (myints[0]|myints[1]|myints[2]|myints[3])==0; -} - -/// calculate the absolute value of a packed single -inline __m128 fabs(__m128 x) -{ -#ifdef _WIN32 - static __declspec(align(16)) int32 clear_signmask[4]= - {0x7fffffff,0x7fffffff,0x7fffffff,0x7fffffff}; -#elif _LINUX - static int32 clear_signmask[4] __attribute__ ((aligned (16)))= - {0x7fffffff,0x7fffffff,0x7fffffff,0x7fffffff}; -#endif - return _mm_and_ps(x,_mm_load_ps((float *) clear_signmask)); -} - -/// negate all four components of an sse packed single -inline __m128 fnegate(__m128 x) -{ -#ifdef _WIN32 - static __declspec(align(16)) int32 signmask[4]= - {0x80000000,0x80000000,0x80000000,0x80000000}; -#elif _LINUX - static int32 signmask[4] __attribute__ ((aligned (16)))= - {0x80000000,0x80000000,0x80000000,0x80000000}; -#endif - return _mm_xor_ps(x,_mm_load_ps((float *) signmask)); -} - -__m128 PowSSE_FixedPoint_Exponent(__m128 x, int exponent); - -// PowSSE - raise an sse register to a power. This is analogous to the C pow() function, with some -// restictions: fractional exponents are only handled with 2 bits of precision. Basically, -// fractions of 0,.25,.5, and .75 are handled. PowSSE(x,.30) will be the same as PowSSE(x,.25). -// negative and fractional powers are handled by the SSE reciprocal and square root approximation -// instructions and so are not especially accurate ----Note that this routine does not raise -// numeric exceptions because it uses SSE--- This routine is O(log2(exponent)). -inline __m128 PowSSE(__m128 x, float exponent) -{ - return PowSSE_FixedPoint_Exponent(x,(int) (4.0*exponent)); -} - -#define SSE1234(x) ((x).m128_f32[0]),((x).m128_f32[1]),((x).m128_f32[2]),((x).m128_f32[3]) - -#endif // WIN32 - - - - - -//#endif +// $Id$ + + +// ssemath.h - defines sse-based "structure of arrays" classes and functions. + + +#ifndef SSEMATH_H +#define SSEMATH_H + +//#ifdef _WIN32 + +#include +#include + +#ifdef _LINUX +typedef int int32; +typedef union __attribute__ ((aligned (16))) + { + float m128_f32[4]; + } l_m128; + +#endif + +// I thought about defining a class/union for the sse packed floats instead of using __m128, +// but decided against it because (a) the nature of sse code which includes comparisons is to blur +// the relationship between packed floats and packed integer types and (b) not sure that the +// compiler would handle generating good code for the intrinsics. + + +// useful constants in SSE packed float format: +extern __m128 Four_Zeros; // 0 0 0 0 +extern __m128 Four_Ones; // 1 1 1 1 +extern __m128 Four_PointFives; // .5 .5 .5 .5 +extern __m128 Four_Epsilons; // FLT_EPSILON FLT_EPSILON FLT_EPSILON FLT_EPSILON + + +/// replicate a single floating point value to all 4 components of an sse packed float +static inline __m128 MMReplicate(float f) +{ + __m128 value=_mm_set_ss(f); + return _mm_shuffle_ps(value,value,0); +} + +/// replicate a single 32 bit integer value to all 4 components of an m128 +static inline __m128 MMReplicateI(int i) +{ + void *t = &i; + __m128 value = _mm_set_ss(*reinterpret_cast(t)); + return _mm_shuffle_ps(value,value,0); +} + +/// 1/x for all 4 values. uses reciprocal approximation instruction plus newton iteration. +/// No error checking! +static inline __m128 MMReciprocal(__m128 x) +{ + __m128 ret=_mm_rcp_ps(x); + // newton iteration is Y(n+1)=2*Y(N)-x*Y(n)^2 + ret=_mm_sub_ps(_mm_add_ps(ret,ret),_mm_mul_ps(x,_mm_mul_ps(ret,ret))); + return ret; +} +/// 1/x for all 4 values. uses reciprocal approximation instruction plus newton iteration. +/// 1/0 will result in a big but NOT infinite result +static inline __m128 MMReciprocalSaturate(__m128 x) +{ + __m128 zero_mask=_mm_cmpeq_ps(x,Four_Zeros); + x=_mm_or_ps(x,_mm_and_ps(Four_Epsilons,zero_mask)); + __m128 ret=_mm_rcp_ps(x); + // newton iteration is Y(n+1)=2*Y(N)-x*Y(n)^2 + ret=_mm_sub_ps(_mm_add_ps(ret,ret),_mm_mul_ps(x,_mm_mul_ps(ret,ret))); + return ret; +} + +/// class FourVectors stores 4 independent vectors for use by sse processing. These vectors are +/// stored in the format x x x x y y y y z z z z so that they can be efficiently accelerated by +/// SSE. +class FourVectors +{ +public: + __m128 x,y,z; // x x x x y y y y z z z z + + inline void DuplicateVector(Vector const &v) //< set all 4 vectors to the same vector value + { + x=MMReplicate(v.x); + y=MMReplicate(v.y); + z=MMReplicate(v.z); + } + + inline __m128 const & operator[](int idx) const + { + return *((&x)+idx); + } + + inline __m128 & operator[](int idx) + { + return *((&x)+idx); + } + + inline void operator+=(FourVectors const &b) //< add 4 vectors to another 4 vectors + { + x=_mm_add_ps(x,b.x); + y=_mm_add_ps(y,b.y); + z=_mm_add_ps(z,b.z); + } + inline void operator-=(FourVectors const &b) //< subtract 4 vectors from another 4 + { + x=_mm_sub_ps(x,b.x); + y=_mm_sub_ps(y,b.y); + z=_mm_sub_ps(z,b.z); + } + + inline void operator*=(FourVectors const &b) //< scale all four vectors per component scale + { + x=_mm_mul_ps(x,b.x); + y=_mm_mul_ps(y,b.y); + z=_mm_mul_ps(z,b.z); + } + + inline void operator*=(float scale) //< uniformly scale all 4 vectors + { + __m128 scalepacked=MMReplicate(scale); + x=_mm_mul_ps(x,scalepacked); + y=_mm_mul_ps(y,scalepacked); + z=_mm_mul_ps(z,scalepacked); + } + + inline void operator*=(__m128 scale) //< scale + { + x=_mm_mul_ps(x,scale); + y=_mm_mul_ps(y,scale); + z=_mm_mul_ps(z,scale); + } + + inline __m128 operator*(FourVectors const &b) const //< 4 dot products + { + __m128 dot=_mm_mul_ps(x,b.x); + dot=_mm_add_ps(dot,_mm_mul_ps(y,b.y)); + dot=_mm_add_ps(dot,_mm_mul_ps(z,b.z)); + return dot; + } + + inline __m128 operator*(Vector const &b) const //< dot product all 4 vectors with 1 vector + { + __m128 dot=_mm_mul_ps(x,MMReplicate(b.x)); + dot=_mm_add_ps(dot,_mm_mul_ps(y,MMReplicate(b.y))); + dot=_mm_add_ps(dot,_mm_mul_ps(z,MMReplicate(b.z))); + return dot; + } + + + inline void VProduct(FourVectors const &b) //< component by component mul + { + x=_mm_mul_ps(x,b.x); + y=_mm_mul_ps(y,b.y); + z=_mm_mul_ps(z,b.z); + } + inline void MakeReciprocal(void) //< (x,y,z)=(1/x,1/y,1/z) + { + x=MMReciprocal(x); + y=MMReciprocal(y); + z=MMReciprocal(z); + } + + inline void MakeReciprocalSaturate(void) //< (x,y,z)=(1/x,1/y,1/z), 1/0=1.0e23 + { + x=MMReciprocalSaturate(x); + y=MMReciprocalSaturate(y); + z=MMReciprocalSaturate(z); + } + + // X(),Y(),Z() - get at the desired component of the i'th (0..3) vector. + inline float const &X(int idx) const + { +#ifdef _LINUX + return ((l_m128*)&x)->m128_f32[idx]; +#else + return x.m128_f32[idx]; +#endif + } + + inline float const &Y(int idx) const + { +#ifdef _LINUX + return ((l_m128*)&y)->m128_f32[idx]; +#else + return y.m128_f32[idx]; +#endif + } + inline float const &Z(int idx) const + { +#ifdef _LINUX + return ((l_m128*)&z)->m128_f32[idx]; +#else + return z.m128_f32[idx]; +#endif + } + + // X(),Y(),Z() - get at the desired component of the i'th (0..3) vector. + inline float &X(int idx) + { +#ifdef _LINUX + return ((l_m128*)&x)->m128_f32[idx]; +#else + return x.m128_f32[idx]; +#endif + } + + inline float &Y(int idx) + { +#ifdef _LINUX + return ((l_m128*)&y)->m128_f32[idx]; +#else + return y.m128_f32[idx]; +#endif + } + inline float &Z(int idx) + { +#ifdef _LINUX + return ((l_m128*)&z)->m128_f32[idx]; +#else + return z.m128_f32[idx]; +#endif + } + + inline Vector Vec(int idx) const //< unpack one of the vectors + { + return Vector(X(idx),Y(idx),Z(idx)); + } + + FourVectors(void) + { + } + + /// LoadAndSwizzle - load 4 Vectors into a FourVectors, perofrming transpose op + inline void LoadAndSwizzle(Vector const &a, Vector const &b, Vector const &c, Vector const &d) + { + __m128 row0=_mm_loadu_ps(&(a.x)); + __m128 row1=_mm_loadu_ps(&(b.x)); + __m128 row2=_mm_loadu_ps(&(c.x)); + __m128 row3=_mm_loadu_ps(&(d.x)); + // now, matrix is: + // x y z ? + // x y z ? + // x y z ? + // x y z ? + _MM_TRANSPOSE4_PS(row0,row1,row2,row3); + x=row0; + y=row1; + z=row2; + } + + /// LoadAndSwizzleAligned - load 4 Vectors into a FourVectors, perofrming transpose op. + /// all 4 vectors muyst be 128 bit boundary + inline void LoadAndSwizzleAligned(Vector const &a, Vector const &b, Vector const &c, Vector const &d) + { + __m128 row0=_mm_load_ps(&(a.x)); + __m128 row1=_mm_load_ps(&(b.x)); + __m128 row2=_mm_load_ps(&(c.x)); + __m128 row3=_mm_load_ps(&(d.x)); + // now, matrix is: + // x y z ? + // x y z ? + // x y z ? + // x y z ? + _MM_TRANSPOSE4_PS(row0,row1,row2,row3); + x=row0; + y=row1; + z=row2; + } + + /// return the squared length of all 4 vectors + inline __m128 length2(void) const + { + return (*this)*(*this); + } + + /// return the approximate length of all 4 vectors. uses the sse sqrt approximation instr + inline __m128 length(void) const + { + return _mm_sqrt_ps(length2()); + } + + /// normalize all 4 vectors in place. not mega-accurate (uses sse reciprocal approximation instr) + inline void VectorNormalizeFast(void) + { + __m128 mag_sq=(*this)*(*this); // length^2 + (*this) *= _mm_rsqrt_ps(mag_sq); // *(1.0/sqrt(length^2)) + } + + /// normnalize all 4 vectors in place. uses newton iteration for higher precision results than + /// from VectorNormalizeFast. + inline void VectorNormalize(void) + { + static __m128 FourThrees={3.,3.,3.,3.}; + __m128 mag_sq=(*this)*(*this); // length^2 + __m128 guess=_mm_rsqrt_ps(mag_sq); + // newton iteration for 1/sqrt(x) : y(n+1)=1/2 (y(n)*(3-x*y(n)^2)); + guess=_mm_mul_ps(guess,_mm_sub_ps(FourThrees,_mm_mul_ps(mag_sq,_mm_mul_ps(guess,guess)))); + guess=_mm_mul_ps(Four_PointFives,guess); + (*this) *= guess; + } + + /// construct a FourVectors from 4 separate Vectors + inline FourVectors(Vector const &a, Vector const &b, Vector const &c, Vector const &d) + { + LoadAndSwizzle(a,b,c,d); + } + +}; + +/// form 4 cross products +inline FourVectors operator ^(const FourVectors &a, const FourVectors &b) +{ + FourVectors ret; + ret.x=_mm_sub_ps(_mm_mul_ps(a.y,b.z),_mm_mul_ps(a.z,b.y)); + ret.y=_mm_sub_ps(_mm_mul_ps(a.z,b.x),_mm_mul_ps(a.x,b.z)); + ret.z=_mm_sub_ps(_mm_mul_ps(a.x,b.y),_mm_mul_ps(a.y,b.x)); + return ret; +} + +/// component-by-componentwise MAX operator +inline FourVectors maximum(const FourVectors &a, const FourVectors &b) +{ + FourVectors ret; + ret.x=_mm_max_ps(a.x,b.x); + ret.y=_mm_max_ps(a.y,b.y); + ret.z=_mm_max_ps(a.z,b.z); + return ret; +} + +/// component-by-componentwise MIN operator +inline FourVectors minimum(const FourVectors &a, const FourVectors &b) +{ + FourVectors ret; + ret.x=_mm_min_ps(a.x,b.x); + ret.y=_mm_min_ps(a.y,b.y); + ret.z=_mm_min_ps(a.z,b.z); + return ret; +} + + +/// check an m128 for all zeros. Not sure if this is the best way +inline bool IsAllZeros(__m128 var) +{ +#ifdef _WIN32 + __declspec(align(16)) int32 myints[4]; +#elif _LINUX + int32 myints[4] __attribute__ ((aligned (16))); +#endif + void *ints = &myints; + _mm_store_ps(static_cast(ints),var); + return (myints[0]|myints[1]|myints[2]|myints[3])==0; +} + +/// calculate the absolute value of a packed single +inline __m128 fabs(__m128 x) +{ +#ifdef _WIN32 + static __declspec(align(16)) int32 clear_signmask[4]= + {0x7fffffff,0x7fffffff,0x7fffffff,0x7fffffff}; +#elif _LINUX + static int32 clear_signmask[4] __attribute__ ((aligned (16)))= + {0x7fffffff,0x7fffffff,0x7fffffff,0x7fffffff}; +#endif + void *mask = &clear_signmask; + return _mm_and_ps(x,_mm_load_ps(static_cast(mask))); +} + +/// negate all four components of an sse packed single +inline __m128 fnegate(__m128 x) +{ +#ifdef _WIN32 + static __declspec(align(16)) int32 signmask[4]= + {0x80000000,0x80000000,0x80000000,0x80000000}; +#elif _LINUX + static int32 signmask[4] __attribute__ ((aligned (16)))= + {0x80000000,0x80000000,0x80000000,0x80000000}; +#endif + void *mask = &signmask; + return _mm_xor_ps(x,_mm_load_ps(static_cast(mask))); +} + +__m128 PowSSE_FixedPoint_Exponent(__m128 x, int exponent); + +// PowSSE - raise an sse register to a power. This is analogous to the C pow() function, with some +// restictions: fractional exponents are only handled with 2 bits of precision. Basically, +// fractions of 0,.25,.5, and .75 are handled. PowSSE(x,.30) will be the same as PowSSE(x,.25). +// negative and fractional powers are handled by the SSE reciprocal and square root approximation +// instructions and so are not especially accurate ----Note that this routine does not raise +// numeric exceptions because it uses SSE--- This routine is O(log2(exponent)). +inline __m128 PowSSE(__m128 x, float exponent) +{ + return PowSSE_FixedPoint_Exponent(x,(int) (4.0*exponent)); +} + +#define SSE1234(x) ((x).m128_f32[0]),((x).m128_f32[1]),((x).m128_f32[2]),((x).m128_f32[3]) + +#endif // WIN32 + + + + + +//#endif diff --git a/public/measure_section.cpp b/public/measure_section.cpp index a27ccf20..441192d6 100644 --- a/public/measure_section.cpp +++ b/public/measure_section.cpp @@ -1,271 +1,271 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $Workfile: $ -// $Date: $ -// -//----------------------------------------------------------------------------- -// $Log: $ -// -// $NoKeywords: $ -//=============================================================================// -#include -#include -#include "basetypes.h" -#include "measure_section.h" -#include "convar.h" - - -// Static members -CMeasureSection *CMeasureSection::s_pSections = 0; -int CMeasureSection::s_nCount = 0; -double CMeasureSection::m_dNextResort = 0.0; - -ConVar measure_resort( "measure_resort", "1.0", 0, "How often to re-sort profiling sections\n" ); -ConVar game_speeds( "game_speeds","0" ); -extern ConVar host_speeds; - -//----------------------------------------------------------------------------- -// Purpose: Creates a profiling section -// Input : *name - name of the section ( allocated on stack hopefully ) -//----------------------------------------------------------------------------- -CMeasureSection::CMeasureSection( const char *name ) -{ - // Just point at name since it's static - m_pszName = name; - - // Clear accumulators - Reset(); - SortReset(); - m_dMaxTime.Init(); - - // Link into master list - m_pNext = s_pSections; - s_pSections = this; - // Update count - s_nCount++; -} - -//----------------------------------------------------------------------------- -// Purpose: Destroys the object -//----------------------------------------------------------------------------- -CMeasureSection::~CMeasureSection( void ) -{ - m_pNext = NULL; - s_nCount--; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CMeasureSection::UpdateMax( void ) -{ - if (m_dMaxTime.IsLessThan(m_dAccumulatedTime)) - { - m_dMaxTime.Init(); - CCycleCount::Add(m_dMaxTime,m_dAccumulatedTime,m_dMaxTime); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CMeasureSection::Reset( void ) -{ - m_dAccumulatedTime.Init(); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CMeasureSection::SortReset( void ) -{ - m_dTotalTime.Init(); -} - -//----------------------------------------------------------------------------- -// Purpose: -// Output : const char -//----------------------------------------------------------------------------- -const char *CMeasureSection::GetName( void ) -{ - return m_pszName; -} - -//----------------------------------------------------------------------------- -// Purpose: -// Output : -//----------------------------------------------------------------------------- -CCycleCount const& CMeasureSection::GetTotalTime( void ) -{ - return m_dTotalTime; -} - -//----------------------------------------------------------------------------- -// Purpose: -// Output : -//----------------------------------------------------------------------------- -CCycleCount const& CMeasureSection::GetTime( void ) -{ - return m_dAccumulatedTime; -} - -//----------------------------------------------------------------------------- -// Purpose: -// Output : -//----------------------------------------------------------------------------- -CCycleCount const& CMeasureSection::GetMaxTime( void ) -{ - return m_dMaxTime; -} - -//----------------------------------------------------------------------------- -// Purpose: Accumulates a timeslice -// Input : time - -//----------------------------------------------------------------------------- -void CMeasureSection::AddTime( CCycleCount const &rCount ) -{ - CCycleCount::Add(m_dAccumulatedTime, rCount, m_dAccumulatedTime); - CCycleCount::Add(m_dTotalTime, rCount, m_dTotalTime); -} - -//----------------------------------------------------------------------------- -// Purpose: -// Output : CMeasureSection -//----------------------------------------------------------------------------- -CMeasureSection *CMeasureSection::GetNext( void ) -{ - return m_pNext; -} - -//----------------------------------------------------------------------------- -// Purpose: -// Output : CMeasureSection -//----------------------------------------------------------------------------- -CMeasureSection *CMeasureSection::GetList( void ) -{ - return s_pSections; -} - -//----------------------------------------------------------------------------- -// Purpose: Compares accumulated time for two sections -// Input : ppms1 - -// ppms2 - -// Output : static int -//----------------------------------------------------------------------------- -static int SectionCompare( const void* ppms1,const void* ppms2 ) -{ - CMeasureSection* pms1 = *(CMeasureSection**)ppms1; - CMeasureSection* pms2 = *(CMeasureSection**)ppms2; - - if ( pms1->GetTotalTime().IsLessThan(pms2->GetTotalTime()) ) - return 1; - else - return -1; -} - -//----------------------------------------------------------------------------- -// Purpose: Sorts sections by time usage -//----------------------------------------------------------------------------- -void CMeasureSection::SortSections( void ) -{ - // Not enough to be sortable - if ( s_nCount <= 1 ) - return; - - CMeasureSection *sortarray[ 128 ]; - CMeasureSection *ms; - - memset(sortarray,sizeof(CMeasureSection*)*128,0); - - ms = GetList(); - int i; - int c = 0; - while ( ms ) - { - sortarray[ c++ ] = ms; - ms = ms->GetNext(); - } - - // Sort the array alphabetically - qsort( sortarray, c , sizeof( CMeasureSection * ), SectionCompare ); - - // Fix next pointers - for ( i = 0; i < c-1; i++ ) - { - sortarray[ i ]->m_pNext = sortarray[ i + 1 ]; - } - sortarray[i]->m_pNext = NULL; - - // Point head of list at it - s_pSections = sortarray[ 0 ]; -} - -//----------------------------------------------------------------------------- -// Purpose: Creates an instance for timing a section -// Input : *ms - -//----------------------------------------------------------------------------- -CMeasureSectionInstance::CMeasureSectionInstance( CMeasureSection *ms ) -{ - // Remember where to put accumulated time - m_pMS = ms; - - if ( host_speeds.GetInt() < 3 && !game_speeds.GetInt()) - return; - - // Get initial timestamp - m_Timer.Start(); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -CMeasureSectionInstance::~CMeasureSectionInstance( void ) -{ - if ( host_speeds.GetInt() < 3 && !game_speeds.GetInt()) - return; - - // Get final timestamp - m_Timer.End(); - - // Add time to section - m_pMS->AddTime( m_Timer.GetDuration() ); -} - -//----------------------------------------------------------------------------- -// Purpose: Re-sort all data and determine whether sort keys should be reset too ( after -// re-doing sort if needed ). -//----------------------------------------------------------------------------- -void ResetTimeMeasurements( void ) -{ -#if defined( _DEBUG ) || defined( FORCE_MEASURE ) - bool sort_reset = false; - - // Time to redo sort? - if ( measure_resort.GetFloat() > 0.0 && - GetRealTime() >= CMeasureSection::m_dNextResort ) - { - // Redo it - CMeasureSection::SortSections(); - // Set next time - CMeasureSection::m_dNextResort = GetRealTime() + measure_resort.GetFloat(); - // Flag to reset sort accumulator, too - sort_reset = true; - } - - // Iterate through the sections now - CMeasureSection *p = CMeasureSection::GetList(); - while ( p ) - { - // Reset regular accum. - p->Reset(); - // Reset sort accum less often - if ( sort_reset ) - { - p->SortReset(); - } - p = p->GetNext(); - } -#endif -} \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $Workfile: $ +// $Date: $ +// +//----------------------------------------------------------------------------- +// $Log: $ +// +// $NoKeywords: $ +//=============================================================================// +#include +#include +#include "basetypes.h" +#include "measure_section.h" +#include "convar.h" + + +// Static members +CMeasureSection *CMeasureSection::s_pSections = 0; +int CMeasureSection::s_nCount = 0; +double CMeasureSection::m_dNextResort = 0.0; + +ConVar measure_resort( "measure_resort", "1.0", 0, "How often to re-sort profiling sections\n" ); +ConVar game_speeds( "game_speeds","0" ); +extern ConVar host_speeds; + +//----------------------------------------------------------------------------- +// Purpose: Creates a profiling section +// Input : *name - name of the section ( allocated on stack hopefully ) +//----------------------------------------------------------------------------- +CMeasureSection::CMeasureSection( const char *name ) +{ + // Just point at name since it's static + m_pszName = name; + + // Clear accumulators + Reset(); + SortReset(); + m_dMaxTime.Init(); + + // Link into master list + m_pNext = s_pSections; + s_pSections = this; + // Update count + s_nCount++; +} + +//----------------------------------------------------------------------------- +// Purpose: Destroys the object +//----------------------------------------------------------------------------- +CMeasureSection::~CMeasureSection( void ) +{ + m_pNext = NULL; + s_nCount--; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CMeasureSection::UpdateMax( void ) +{ + if (m_dMaxTime.IsLessThan(m_dAccumulatedTime)) + { + m_dMaxTime.Init(); + CCycleCount::Add(m_dMaxTime,m_dAccumulatedTime,m_dMaxTime); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CMeasureSection::Reset( void ) +{ + m_dAccumulatedTime.Init(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CMeasureSection::SortReset( void ) +{ + m_dTotalTime.Init(); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : const char +//----------------------------------------------------------------------------- +const char *CMeasureSection::GetName( void ) +{ + return m_pszName; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : +//----------------------------------------------------------------------------- +CCycleCount const& CMeasureSection::GetTotalTime( void ) +{ + return m_dTotalTime; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : +//----------------------------------------------------------------------------- +CCycleCount const& CMeasureSection::GetTime( void ) +{ + return m_dAccumulatedTime; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : +//----------------------------------------------------------------------------- +CCycleCount const& CMeasureSection::GetMaxTime( void ) +{ + return m_dMaxTime; +} + +//----------------------------------------------------------------------------- +// Purpose: Accumulates a timeslice +// Input : time - +//----------------------------------------------------------------------------- +void CMeasureSection::AddTime( CCycleCount const &rCount ) +{ + CCycleCount::Add(m_dAccumulatedTime, rCount, m_dAccumulatedTime); + CCycleCount::Add(m_dTotalTime, rCount, m_dTotalTime); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : CMeasureSection +//----------------------------------------------------------------------------- +CMeasureSection *CMeasureSection::GetNext( void ) +{ + return m_pNext; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : CMeasureSection +//----------------------------------------------------------------------------- +CMeasureSection *CMeasureSection::GetList( void ) +{ + return s_pSections; +} + +//----------------------------------------------------------------------------- +// Purpose: Compares accumulated time for two sections +// Input : ppms1 - +// ppms2 - +// Output : static int +//----------------------------------------------------------------------------- +static int SectionCompare( const void* ppms1,const void* ppms2 ) +{ + CMeasureSection* pms1 = *(CMeasureSection**)ppms1; + CMeasureSection* pms2 = *(CMeasureSection**)ppms2; + + if ( pms1->GetTotalTime().IsLessThan(pms2->GetTotalTime()) ) + return 1; + else + return -1; +} + +//----------------------------------------------------------------------------- +// Purpose: Sorts sections by time usage +//----------------------------------------------------------------------------- +void CMeasureSection::SortSections( void ) +{ + // Not enough to be sortable + if ( s_nCount <= 1 ) + return; + + CMeasureSection *sortarray[ 128 ]; + CMeasureSection *ms; + + memset(sortarray,sizeof(CMeasureSection*)*128,0); + + ms = GetList(); + int i; + int c = 0; + while ( ms ) + { + sortarray[ c++ ] = ms; + ms = ms->GetNext(); + } + + // Sort the array alphabetically + qsort( sortarray, c , sizeof( CMeasureSection * ), SectionCompare ); + + // Fix next pointers + for ( i = 0; i < c-1; i++ ) + { + sortarray[ i ]->m_pNext = sortarray[ i + 1 ]; + } + sortarray[i]->m_pNext = NULL; + + // Point head of list at it + s_pSections = sortarray[ 0 ]; +} + +//----------------------------------------------------------------------------- +// Purpose: Creates an instance for timing a section +// Input : *ms - +//----------------------------------------------------------------------------- +CMeasureSectionInstance::CMeasureSectionInstance( CMeasureSection *ms ) +{ + // Remember where to put accumulated time + m_pMS = ms; + + if ( host_speeds.GetInt() < 3 && !game_speeds.GetInt()) + return; + + // Get initial timestamp + m_Timer.Start(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CMeasureSectionInstance::~CMeasureSectionInstance( void ) +{ + if ( host_speeds.GetInt() < 3 && !game_speeds.GetInt()) + return; + + // Get final timestamp + m_Timer.End(); + + // Add time to section + m_pMS->AddTime( m_Timer.GetDuration() ); +} + +//----------------------------------------------------------------------------- +// Purpose: Re-sort all data and determine whether sort keys should be reset too ( after +// re-doing sort if needed ). +//----------------------------------------------------------------------------- +void ResetTimeMeasurements( void ) +{ +#if defined( _DEBUG ) || defined( FORCE_MEASURE ) + bool sort_reset = false; + + // Time to redo sort? + if ( measure_resort.GetFloat() > 0.0 && + GetRealTime() >= CMeasureSection::m_dNextResort ) + { + // Redo it + CMeasureSection::SortSections(); + // Set next time + CMeasureSection::m_dNextResort = GetRealTime() + measure_resort.GetFloat(); + // Flag to reset sort accumulator, too + sort_reset = true; + } + + // Iterate through the sections now + CMeasureSection *p = CMeasureSection::GetList(); + while ( p ) + { + // Reset regular accum. + p->Reset(); + // Reset sort accum less often + if ( sort_reset ) + { + p->SortReset(); + } + p = p->GetNext(); + } +#endif +} diff --git a/public/measure_section.h b/public/measure_section.h index 619063eb..6922b6a7 100644 --- a/public/measure_section.h +++ b/public/measure_section.h @@ -1,127 +1,127 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $Workfile: $ -// $Date: $ -// $NoKeywords: $ -//=============================================================================// -#if !defined( MEASURE_SECTION_H ) -#define MEASURE_SECTION_H -#ifdef _WIN32 -#pragma once -#endif - - -#include "tier0/fasttimer.h" -#include "convar.h" - - -// This is the macro to use in your code to measure until the code goes -// out of scope -#if defined( _DEBUG ) || defined( FORCE_MEASURE ) -#define MEASURECODE( description ) \ - static CMeasureSection _xxx_ms( description ); \ - CMeasureSectionInstance _xxx_ms_inst( &_xxx_ms ); -#else -#define MEASURECODE( description ) -#endif - - -// ------------------------------------------------------------------------------------ // -// These things must exist in the executable for the CMeasureSection code to work. -// ------------------------------------------------------------------------------------ // -float GetRealTime(); // Get the clock's time. - -extern ConVar game_speeds; -extern ConVar measure_resort; -// ------------------------------------------------------------------------------------ // - - - -// Called once per frame to allow any necessary measurements to latch -void ResetTimeMeasurements( void ); - -//----------------------------------------------------------------------------- -// Purpose: Accumulates time for the named section -//----------------------------------------------------------------------------- -class CMeasureSection -{ -public: - // Allows for measuring named section - CMeasureSection( const char *name ); - virtual ~CMeasureSection( void ); - - - // Update max value hit - void UpdateMax( void ); - // Reset totals - void Reset( void ); - // Reset sortable totals - void SortReset( void ); - // Get static name of section - const char *GetName( void ); - - // Get accumulated time - CCycleCount const& GetTotalTime( void ); - - CCycleCount const& GetTime(); - - CCycleCount const& GetMaxTime(); - - // Add in some time - void AddTime( CCycleCount const &rCount ); - - // Get next section in chain - CMeasureSection *GetNext( void ); - - // Get head of list of all sections - static CMeasureSection *GetList( void ); - // Sort all sections by most time consuming - static void SortSections( void ); - -public: - // Time when list should be sorted again - static double m_dNextResort; - -private: - // Accumulated time for section - CCycleCount m_dAccumulatedTime; - - // Max time for section - CCycleCount m_dMaxTime; - - // Elapsed time for section - CCycleCount m_dTotalTime; - - // Name of section - const char *m_pszName; - // Next section in chain - CMeasureSection *m_pNext; - // Head of section list - static CMeasureSection *s_pSections; - // Quick total for doing sorts faster - static int s_nCount; -}; - -//----------------------------------------------------------------------------- -// Purpose: On construction marks time and on destruction adds time to -// parent CMeasureSection object -//----------------------------------------------------------------------------- -class CMeasureSectionInstance -{ -public: - // Constructor: Points to object to accumulate time into - CMeasureSectionInstance( CMeasureSection *ms ); - // Destructor: Latches accumulated time - virtual ~CMeasureSectionInstance( void ); - -private: - // Time of construction - CFastTimer m_Timer; - - // Where to place elapsed time - CMeasureSection *m_pMS; -}; - -#endif // MEASURE_SECTION_H \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $Workfile: $ +// $Date: $ +// $NoKeywords: $ +//=============================================================================// +#if !defined( MEASURE_SECTION_H ) +#define MEASURE_SECTION_H +#ifdef _WIN32 +#pragma once +#endif + + +#include "tier0/fasttimer.h" +#include "convar.h" + + +// This is the macro to use in your code to measure until the code goes +// out of scope +#if defined( _DEBUG ) || defined( FORCE_MEASURE ) +#define MEASURECODE( description ) \ + static CMeasureSection _xxx_ms( description ); \ + CMeasureSectionInstance _xxx_ms_inst( &_xxx_ms ); +#else +#define MEASURECODE( description ) +#endif + + +// ------------------------------------------------------------------------------------ // +// These things must exist in the executable for the CMeasureSection code to work. +// ------------------------------------------------------------------------------------ // +float GetRealTime(); // Get the clock's time. + +extern ConVar game_speeds; +extern ConVar measure_resort; +// ------------------------------------------------------------------------------------ // + + + +// Called once per frame to allow any necessary measurements to latch +void ResetTimeMeasurements( void ); + +//----------------------------------------------------------------------------- +// Purpose: Accumulates time for the named section +//----------------------------------------------------------------------------- +class CMeasureSection +{ +public: + // Allows for measuring named section + CMeasureSection( const char *name ); + virtual ~CMeasureSection( void ); + + + // Update max value hit + void UpdateMax( void ); + // Reset totals + void Reset( void ); + // Reset sortable totals + void SortReset( void ); + // Get static name of section + const char *GetName( void ); + + // Get accumulated time + CCycleCount const& GetTotalTime( void ); + + CCycleCount const& GetTime(); + + CCycleCount const& GetMaxTime(); + + // Add in some time + void AddTime( CCycleCount const &rCount ); + + // Get next section in chain + CMeasureSection *GetNext( void ); + + // Get head of list of all sections + static CMeasureSection *GetList( void ); + // Sort all sections by most time consuming + static void SortSections( void ); + +public: + // Time when list should be sorted again + static double m_dNextResort; + +private: + // Accumulated time for section + CCycleCount m_dAccumulatedTime; + + // Max time for section + CCycleCount m_dMaxTime; + + // Elapsed time for section + CCycleCount m_dTotalTime; + + // Name of section + const char *m_pszName; + // Next section in chain + CMeasureSection *m_pNext; + // Head of section list + static CMeasureSection *s_pSections; + // Quick total for doing sorts faster + static int s_nCount; +}; + +//----------------------------------------------------------------------------- +// Purpose: On construction marks time and on destruction adds time to +// parent CMeasureSection object +//----------------------------------------------------------------------------- +class CMeasureSectionInstance +{ +public: + // Constructor: Points to object to accumulate time into + CMeasureSectionInstance( CMeasureSection *ms ); + // Destructor: Latches accumulated time + virtual ~CMeasureSectionInstance( void ); + +private: + // Time of construction + CFastTimer m_Timer; + + // Where to place elapsed time + CMeasureSection *m_pMS; +}; + +#endif // MEASURE_SECTION_H diff --git a/public/model_types.h b/public/model_types.h index ef5b05c7..7573577b 100644 --- a/public/model_types.h +++ b/public/model_types.h @@ -1,37 +1,37 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $Workfile: $ -// $Date: $ -// $NoKeywords: $ -//=============================================================================// -#if !defined( MODEL_TYPES_H ) -#define MODEL_TYPES_H -#ifdef _WIN32 -#pragma once -#endif - -#define STUDIO_NONE 0x00000000 -#define STUDIO_RENDER 0x00000001 -#define STUDIO_VIEWXFORMATTACHMENTS 0x00000002 -#define STUDIO_DRAWTRANSLUCENTSUBMODELS 0x00000004 -#define STUDIO_TWOPASS 0x00000008 -#define STUDIO_STATIC_LIGHTING 0x00000010 -#define STUDIO_WIREFRAME 0x00000020 -#define STUDIO_ITEM_BLINK 0x00000040 -#define STUDIO_NOSHADOWS 0x00000080 - -// Not a studio flag, but used to flag model as a non-sorting brush model -#define STUDIO_TRANSPARENCY 0x80000000 - - -enum modtype_t -{ - mod_bad = 0, - mod_brush, - mod_sprite, - mod_studio -}; - -#endif // MODEL_TYPES_H \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $Workfile: $ +// $Date: $ +// $NoKeywords: $ +//=============================================================================// +#if !defined( MODEL_TYPES_H ) +#define MODEL_TYPES_H +#ifdef _WIN32 +#pragma once +#endif + +#define STUDIO_NONE 0x00000000 +#define STUDIO_RENDER 0x00000001 +#define STUDIO_VIEWXFORMATTACHMENTS 0x00000002 +#define STUDIO_DRAWTRANSLUCENTSUBMODELS 0x00000004 +#define STUDIO_TWOPASS 0x00000008 +#define STUDIO_STATIC_LIGHTING 0x00000010 +#define STUDIO_WIREFRAME 0x00000020 +#define STUDIO_ITEM_BLINK 0x00000040 +#define STUDIO_NOSHADOWS 0x00000080 + +// Not a studio flag, but used to flag model as a non-sorting brush model +#define STUDIO_TRANSPARENCY 0x80000000 + + +enum modtype_t +{ + mod_bad = 0, + mod_brush, + mod_sprite, + mod_studio +}; + +#endif // MODEL_TYPES_H diff --git a/public/networkvar.h b/public/networkvar.h index d19258e9..da360bb8 100644 --- a/public/networkvar.h +++ b/public/networkvar.h @@ -19,8 +19,9 @@ #endif +#ifdef _MSC_VER #pragma warning( disable : 4284 ) // warning C4284: return type for 'CNetworkVarT::operator ->' is 'int *' (ie; not a UDT or reference to a UDT. Will produce errors if applied using infix notation) - +#endif #define MyOffsetOf( type, var ) ( (int)&((type*)0)->var ) @@ -352,7 +353,7 @@ protected: { if ( out != in ) { - NetworkStateChanged(); + this->NetworkStateChanged(); out = in; } } @@ -436,7 +437,7 @@ private: { if ( out != in ) { - NetworkStateChanged(); + this->NetworkStateChanged(); out = in; } } @@ -523,7 +524,7 @@ private: { if ( out != in ) { - NetworkStateChanged(); + this->NetworkStateChanged(); out = in; } } @@ -561,7 +562,7 @@ private: { if ( CNetworkHandleBase::m_Value != val ) { - NetworkStateChanged(); + this->NetworkStateChanged(); CNetworkHandleBase::m_Value = val; } return val; diff --git a/public/resourcetags.cpp b/public/resourcetags.cpp index e4685953..278e5d1a 100644 --- a/public/resourcetags.cpp +++ b/public/resourcetags.cpp @@ -1,28 +1,28 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -//=============================================================================// -#include "resourcetags.h" -#include "tier0/dbg.h" -// memdbgon must be the last include file in a .cpp file!!! -#include "tier0/memdbgon.h" - -#ifdef _XBOX -static int s_resourceTag = RESOURCE_TAG_NONE; - -void Sys_SetResourceTag( int tag ) -{ - s_resourceTag = tag; -} - -int Sys_GetResourceTag( void ) -{ -#if 0 - // control flow issue, no resource should be instanced during an indeterminate state - Assert( s_resourceTag != RESOURCE_TAG_NONE ); -#endif - - return s_resourceTag; -} -#endif \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// +#include "resourcetags.h" +#include "tier0/dbg.h" +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +#ifdef _XBOX +static int s_resourceTag = RESOURCE_TAG_NONE; + +void Sys_SetResourceTag( int tag ) +{ + s_resourceTag = tag; +} + +int Sys_GetResourceTag( void ) +{ +#if 0 + // control flow issue, no resource should be instanced during an indeterminate state + Assert( s_resourceTag != RESOURCE_TAG_NONE ); +#endif + + return s_resourceTag; +} +#endif diff --git a/public/riff.cpp b/public/riff.cpp index f0064868..c7e55a28 100644 --- a/public/riff.cpp +++ b/public/riff.cpp @@ -1,504 +1,506 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -// -//=============================================================================// -// Avoid these warnings: -#pragma warning(disable : 4512) // warning C4512: 'InFileRIFF' : assignment operator could not be generated -#pragma warning(disable : 4514) // warning C4514: 'RIFFName' : unreferenced inline function has been removed - -#include "riff.h" -#include -#include -#include "tier0/dbg.h" - -// memdbgon must be the last include file in a .cpp file!!! -#include "tier0/memdbgon.h" - -#if 0 -//----------------------------------------------------------------------------- -// Purpose: Test code that implements the interface on stdio -//----------------------------------------------------------------------------- -class StdIOReadBinary : public IFileReadBinary -{ -public: - int open( const char *pFileName ) - { - return (int)fopen( pFileName, "rb" ); - } - - int read( void *pOutput, int size, int file ) - { - FILE *fp = (FILE *)file; - - return fread( pOutput, size, 1, fp ); - } - - void seek( int file, int pos ) - { - fseek( (FILE *)file, pos, SEEK_SET ); - } - - unsigned int tell( int file ) - { - return ftell( (FILE *)file ); - } - - unsigned int size( int file ) - { - FILE *fp = (FILE *)file; - if ( !fp ) - return 0; - - unsigned int pos = ftell( fp ); - fseek( fp, 0, SEEK_END ); - unsigned int size = ftell( fp ); - - fseek( fp, pos, SEEK_SET ); - return size; - } - - void close( int file ) - { - FILE *fp = (FILE *)file; - - fclose( fp ); - } -}; -#endif - - -#define RIFF_ID MAKEID('R','I','F','F') - - -//----------------------------------------------------------------------------- -// Purpose: Opens a RIFF file using the given I/O mechanism -// Input : *pFileName -// &io - I/O interface -//----------------------------------------------------------------------------- -InFileRIFF::InFileRIFF( const char *pFileName, IFileReadBinary &io ) : m_io(io) -{ - m_file = m_io.open( pFileName ); - - int riff = 0; - if ( !m_file ) - { - m_riffSize = 0; - m_riffName = 0; - return; - } - m_io.read( &riff, 4, m_file ); - if ( riff != RIFF_ID ) - { - printf("Not a RIFF File\n" ); - m_riffSize = 0; - } - else - { - // we store size as size of all chunks - // subtract off the RIFF form type (e.g. 'WAVE', 4 bytes) - m_riffSize = ReadInt() - 4; - m_riffName = ReadInt(); - - // HACKHACK: LWV files don't obey the RIFF format!!! - // Do this or miss the linguistic chunks at the end. Lame! - // subtract off 12 bytes for (RIFF, size, WAVE) - m_riffSize = m_io.size( m_file ) - 12; - } -} - - -//----------------------------------------------------------------------------- -// Purpose: Close the file -//----------------------------------------------------------------------------- -InFileRIFF::~InFileRIFF( void ) -{ - m_io.close( m_file ); -} - - -//----------------------------------------------------------------------------- -// Purpose: read a 4-byte int out of the stream -// Output : int = read value, default is zero -//----------------------------------------------------------------------------- -int InFileRIFF::ReadInt( void ) -{ - int tmp = 0; - m_io.read( &tmp, sizeof(int), m_file ); - - return tmp; -} - -//----------------------------------------------------------------------------- -// Purpose: Read a block of binary data -// Input : *pOutput - pointer to destination memory -// dataSize - size of block to read -// Output : int - number of bytes read -//----------------------------------------------------------------------------- -int InFileRIFF::ReadData( void *pOutput, int dataSize ) -{ - int count = m_io.read( pOutput, dataSize, m_file ); - - return count; -} - - -//----------------------------------------------------------------------------- -// Purpose: Gets the file position -// Output : int (bytes from start of file) -//----------------------------------------------------------------------------- -int InFileRIFF::PositionGet( void ) -{ - return m_io.tell( m_file ); -} - - -//----------------------------------------------------------------------------- -// Purpose: Seek to file position -// Input : position - bytes from start of file -//----------------------------------------------------------------------------- -void InFileRIFF::PositionSet( int position ) -{ - m_io.seek( m_file, position ); -} - -//----------------------------------------------------------------------------- -// Purpose: Used to write a RIFF format file -//----------------------------------------------------------------------------- -OutFileRIFF::OutFileRIFF( const char *pFileName, IFileWriteBinary &io ) : m_io( io ) -{ - m_file = m_io.create( pFileName ); - - if ( !m_file ) - return; - - int riff = RIFF_ID; - m_io.write( &riff, 4, m_file ); - - m_riffSize = 0; - m_nNamePos = m_io.tell( m_file ); - - // Save room for the size and name now - WriteInt( 0 ); - - // Write out the name - WriteInt( RIFF_WAVE ); - - m_bUseIncorrectLISETLength = false; - m_nLISETSize = 0; -} - -OutFileRIFF::~OutFileRIFF( void ) -{ - if ( !IsValid() ) - return; - - unsigned int size = m_io.tell( m_file ) -8; - m_io.seek( m_file, m_nNamePos ); - - if ( m_bUseIncorrectLISETLength ) - { - size = m_nLISETSize - 8; - } - - WriteInt( size ); - m_io.close( m_file ); -} - -void OutFileRIFF::HasLISETData( int position ) -{ - m_bUseIncorrectLISETLength = true; - m_nLISETSize = position; -} - -bool OutFileRIFF::WriteInt( int number ) -{ - if ( !IsValid() ) - return false; - - m_io.write( &number, sizeof( int ), m_file ); - return true; -} - -bool OutFileRIFF::WriteData( void *pOutput, int dataSize ) -{ - if ( !IsValid() ) - return false; - - m_io.write( pOutput, dataSize, m_file ); - return true; -} - -int OutFileRIFF::PositionGet( void ) -{ - if ( !IsValid() ) - return 0; - - return m_io.tell( m_file ); -} - -void OutFileRIFF::PositionSet( int position ) -{ - if ( !IsValid() ) - return; - - m_io.seek( m_file, position ); -} - - -//----------------------------------------------------------------------------- -// Purpose: Create an iterator for the given file -// Input : &riff - riff file -// size - size of file or sub-chunk -//----------------------------------------------------------------------------- -IterateRIFF::IterateRIFF( InFileRIFF &riff, int size ) - : m_riff(riff), m_size(size) -{ - if ( !m_riff.RIFFSize() ) - { - // bad file, just be an empty iterator - ChunkClear(); - return; - } - - // get the position and parse a chunk - m_start = riff.PositionGet(); - ChunkSetup(); -} - - -//----------------------------------------------------------------------------- -// Purpose: Set up a sub-chunk iterator -// Input : &parent - parent iterator -//----------------------------------------------------------------------------- -IterateRIFF::IterateRIFF( IterateRIFF &parent ) - : m_riff(parent.m_riff), m_size(parent.ChunkSize()) -{ - m_start = parent.ChunkFilePosition(); - ChunkSetup(); -} - -//----------------------------------------------------------------------------- -// Purpose: Parse the chunk at the current file position -// This object will iterate over the sub-chunks of this chunk. -// This makes for easy hierarchical parsing -//----------------------------------------------------------------------------- -void IterateRIFF::ChunkSetup( void ) -{ - m_chunkPosition = m_riff.PositionGet(); - - m_chunkName = m_riff.ReadInt(); - m_chunkSize = m_riff.ReadInt(); -} - -//----------------------------------------------------------------------------- -// Purpose: clear chunk setup, ChunkAvailable will return false -//----------------------------------------------------------------------------- -void IterateRIFF::ChunkClear( void ) -{ - m_chunkSize = -1; -} - -//----------------------------------------------------------------------------- -// Purpose: If there are chunks left to read beyond this one, return true -//----------------------------------------------------------------------------- -bool IterateRIFF::ChunkAvailable( void ) -{ - if ( m_chunkSize != -1 && m_chunkSize < 0x10000000 ) - return true; - - return false; -} - - -//----------------------------------------------------------------------------- -// Purpose: Go to the next chunk in the file, return true if there is one. -//----------------------------------------------------------------------------- -bool IterateRIFF::ChunkNext( void ) -{ - if ( !ChunkAvailable() ) - return false; - - int nextPos = m_chunkPosition + 8 + m_chunkSize; - - // chunks are aligned - nextPos += m_chunkSize & 1; - - if ( nextPos >= (m_start + m_size) ) - { - ChunkClear(); - return false; - } - - m_riff.PositionSet( nextPos ); - - ChunkSetup(); - return ChunkAvailable(); - -} - - -//----------------------------------------------------------------------------- -// Purpose: get the chunk FOURCC as an int -// Output : unsigned int -//----------------------------------------------------------------------------- -unsigned int IterateRIFF::ChunkName( void ) -{ - return m_chunkName; -} - - -//----------------------------------------------------------------------------- -// Purpose: get the size of this chunk -// Output : unsigned int -//----------------------------------------------------------------------------- -unsigned int IterateRIFF::ChunkSize( void ) -{ - return m_chunkSize; -} - -//----------------------------------------------------------------------------- -// Purpose: Read the entire chunk into a buffer -// Input : *pOutput - dest buffer -// Output : int bytes read -//----------------------------------------------------------------------------- -int IterateRIFF::ChunkRead( void *pOutput ) -{ - return m_riff.ReadData( pOutput, ChunkSize() ); -} - - -//----------------------------------------------------------------------------- -// Purpose: Read a partial chunk (updates file position for subsequent partial reads). -// Input : *pOutput - dest buffer -// dataSize - partial size -// Output : int - bytes read -//----------------------------------------------------------------------------- -int IterateRIFF::ChunkReadPartial( void *pOutput, int dataSize ) -{ - return m_riff.ReadData( pOutput, dataSize ); -} - - -//----------------------------------------------------------------------------- -// Purpose: Read a 4-byte int -// Output : int - read int -//----------------------------------------------------------------------------- -int IterateRIFF::ChunkReadInt( void ) -{ - return m_riff.ReadInt(); -} - -//----------------------------------------------------------------------------- -// Purpose: Used to iterate over an InFileRIFF -//----------------------------------------------------------------------------- -IterateOutputRIFF::IterateOutputRIFF( OutFileRIFF &riff ) -: m_riff( riff ) -{ - if ( !m_riff.IsValid() ) - return; - - m_start = m_riff.PositionGet(); - m_chunkPosition = m_start; - m_chunkStart = -1; -} - -IterateOutputRIFF::IterateOutputRIFF( IterateOutputRIFF &parent ) - : m_riff(parent.m_riff) -{ - m_start = parent.ChunkFilePosition(); - m_chunkPosition = m_start; - m_chunkStart = -1; -} - -void IterateOutputRIFF::ChunkWrite( unsigned int chunkname, void *pOutput, int size ) -{ - m_chunkPosition = m_riff.PositionGet(); - - m_chunkName = chunkname; - m_chunkSize = size; - - m_riff.WriteInt( chunkname ); - m_riff.WriteInt( size ); - m_riff.WriteData( pOutput, size ); - - m_chunkPosition = m_riff.PositionGet(); - - m_chunkPosition += m_chunkPosition & 1; - - m_riff.PositionSet( m_chunkPosition ); - - m_chunkStart = -1; -} - -void IterateOutputRIFF::ChunkWriteInt( int number ) -{ - m_riff.WriteInt( number ); -} - -void IterateOutputRIFF::ChunkWriteData( void *pOutput, int size ) -{ - m_riff.WriteData( pOutput, size ); -} - -void IterateOutputRIFF::ChunkFinish( void ) -{ - Assert( m_chunkStart != -1 ); - - m_chunkPosition = m_riff.PositionGet(); - - int size = m_chunkPosition - m_chunkStart - 8; - - m_chunkPosition += m_chunkPosition & 1; - - m_riff.PositionSet( m_chunkStart + sizeof( int ) ); - - m_riff.WriteInt( size ); - - m_riff.PositionSet( m_chunkPosition ); - - m_chunkStart = -1; -} - -void IterateOutputRIFF::ChunkStart( unsigned int chunkname ) -{ - Assert( m_chunkStart == -1 ); - - m_chunkStart = m_riff.PositionGet(); - - m_riff.WriteInt( chunkname ); - m_riff.WriteInt( 0 ); -} - -void IterateOutputRIFF::ChunkSetPosition( int position ) -{ - m_riff.PositionSet( position ); -} - -unsigned int IterateOutputRIFF::ChunkGetPosition( void ) -{ - return m_riff.PositionGet(); -} - -void IterateOutputRIFF::CopyChunkData( IterateRIFF& input ) -{ - if ( input.ChunkSize() > 0 ) - { - char *buffer = new char[ input.ChunkSize() ]; - Assert( buffer ); - - input.ChunkRead( buffer ); - - // Don't copy/write the name or size, just the data itself - ChunkWriteData( buffer, input.ChunkSize() ); - - delete[] buffer; - } -} - -void IterateOutputRIFF::SetLISETData( int position ) -{ - m_riff.HasLISETData( position ); -} \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// +// Avoid these warnings: +#ifdef _MSC_VER +#pragma warning(disable : 4512) // warning C4512: 'InFileRIFF' : assignment operator could not be generated +#pragma warning(disable : 4514) // warning C4514: 'RIFFName' : unreferenced inline function has been removed +#endif + +#include "riff.h" +#include +#include +#include "tier0/dbg.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +#if 0 +//----------------------------------------------------------------------------- +// Purpose: Test code that implements the interface on stdio +//----------------------------------------------------------------------------- +class StdIOReadBinary : public IFileReadBinary +{ +public: + int open( const char *pFileName ) + { + return (int)fopen( pFileName, "rb" ); + } + + int read( void *pOutput, int size, int file ) + { + FILE *fp = (FILE *)file; + + return fread( pOutput, size, 1, fp ); + } + + void seek( int file, int pos ) + { + fseek( (FILE *)file, pos, SEEK_SET ); + } + + unsigned int tell( int file ) + { + return ftell( (FILE *)file ); + } + + unsigned int size( int file ) + { + FILE *fp = (FILE *)file; + if ( !fp ) + return 0; + + unsigned int pos = ftell( fp ); + fseek( fp, 0, SEEK_END ); + unsigned int size = ftell( fp ); + + fseek( fp, pos, SEEK_SET ); + return size; + } + + void close( int file ) + { + FILE *fp = (FILE *)file; + + fclose( fp ); + } +}; +#endif + + +#define RIFF_ID MAKEID('R','I','F','F') + + +//----------------------------------------------------------------------------- +// Purpose: Opens a RIFF file using the given I/O mechanism +// Input : *pFileName +// &io - I/O interface +//----------------------------------------------------------------------------- +InFileRIFF::InFileRIFF( const char *pFileName, IFileReadBinary &io ) : m_io(io) +{ + m_file = m_io.open( pFileName ); + + int riff = 0; + if ( !m_file ) + { + m_riffSize = 0; + m_riffName = 0; + return; + } + m_io.read( &riff, 4, m_file ); + if ( riff != RIFF_ID ) + { + printf("Not a RIFF File\n" ); + m_riffSize = 0; + } + else + { + // we store size as size of all chunks + // subtract off the RIFF form type (e.g. 'WAVE', 4 bytes) + m_riffSize = ReadInt() - 4; + m_riffName = ReadInt(); + + // HACKHACK: LWV files don't obey the RIFF format!!! + // Do this or miss the linguistic chunks at the end. Lame! + // subtract off 12 bytes for (RIFF, size, WAVE) + m_riffSize = m_io.size( m_file ) - 12; + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Close the file +//----------------------------------------------------------------------------- +InFileRIFF::~InFileRIFF( void ) +{ + m_io.close( m_file ); +} + + +//----------------------------------------------------------------------------- +// Purpose: read a 4-byte int out of the stream +// Output : int = read value, default is zero +//----------------------------------------------------------------------------- +int InFileRIFF::ReadInt( void ) +{ + int tmp = 0; + m_io.read( &tmp, sizeof(int), m_file ); + + return tmp; +} + +//----------------------------------------------------------------------------- +// Purpose: Read a block of binary data +// Input : *pOutput - pointer to destination memory +// dataSize - size of block to read +// Output : int - number of bytes read +//----------------------------------------------------------------------------- +int InFileRIFF::ReadData( void *pOutput, int dataSize ) +{ + int count = m_io.read( pOutput, dataSize, m_file ); + + return count; +} + + +//----------------------------------------------------------------------------- +// Purpose: Gets the file position +// Output : int (bytes from start of file) +//----------------------------------------------------------------------------- +int InFileRIFF::PositionGet( void ) +{ + return m_io.tell( m_file ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Seek to file position +// Input : position - bytes from start of file +//----------------------------------------------------------------------------- +void InFileRIFF::PositionSet( int position ) +{ + m_io.seek( m_file, position ); +} + +//----------------------------------------------------------------------------- +// Purpose: Used to write a RIFF format file +//----------------------------------------------------------------------------- +OutFileRIFF::OutFileRIFF( const char *pFileName, IFileWriteBinary &io ) : m_io( io ) +{ + m_file = m_io.create( pFileName ); + + if ( !m_file ) + return; + + int riff = RIFF_ID; + m_io.write( &riff, 4, m_file ); + + m_riffSize = 0; + m_nNamePos = m_io.tell( m_file ); + + // Save room for the size and name now + WriteInt( 0 ); + + // Write out the name + WriteInt( RIFF_WAVE ); + + m_bUseIncorrectLISETLength = false; + m_nLISETSize = 0; +} + +OutFileRIFF::~OutFileRIFF( void ) +{ + if ( !IsValid() ) + return; + + unsigned int size = m_io.tell( m_file ) -8; + m_io.seek( m_file, m_nNamePos ); + + if ( m_bUseIncorrectLISETLength ) + { + size = m_nLISETSize - 8; + } + + WriteInt( size ); + m_io.close( m_file ); +} + +void OutFileRIFF::HasLISETData( int position ) +{ + m_bUseIncorrectLISETLength = true; + m_nLISETSize = position; +} + +bool OutFileRIFF::WriteInt( int number ) +{ + if ( !IsValid() ) + return false; + + m_io.write( &number, sizeof( int ), m_file ); + return true; +} + +bool OutFileRIFF::WriteData( void *pOutput, int dataSize ) +{ + if ( !IsValid() ) + return false; + + m_io.write( pOutput, dataSize, m_file ); + return true; +} + +int OutFileRIFF::PositionGet( void ) +{ + if ( !IsValid() ) + return 0; + + return m_io.tell( m_file ); +} + +void OutFileRIFF::PositionSet( int position ) +{ + if ( !IsValid() ) + return; + + m_io.seek( m_file, position ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Create an iterator for the given file +// Input : &riff - riff file +// size - size of file or sub-chunk +//----------------------------------------------------------------------------- +IterateRIFF::IterateRIFF( InFileRIFF &riff, int size ) + : m_riff(riff), m_size(size) +{ + if ( !m_riff.RIFFSize() ) + { + // bad file, just be an empty iterator + ChunkClear(); + return; + } + + // get the position and parse a chunk + m_start = riff.PositionGet(); + ChunkSetup(); +} + + +//----------------------------------------------------------------------------- +// Purpose: Set up a sub-chunk iterator +// Input : &parent - parent iterator +//----------------------------------------------------------------------------- +IterateRIFF::IterateRIFF( IterateRIFF &parent ) + : m_riff(parent.m_riff), m_size(parent.ChunkSize()) +{ + m_start = parent.ChunkFilePosition(); + ChunkSetup(); +} + +//----------------------------------------------------------------------------- +// Purpose: Parse the chunk at the current file position +// This object will iterate over the sub-chunks of this chunk. +// This makes for easy hierarchical parsing +//----------------------------------------------------------------------------- +void IterateRIFF::ChunkSetup( void ) +{ + m_chunkPosition = m_riff.PositionGet(); + + m_chunkName = m_riff.ReadInt(); + m_chunkSize = m_riff.ReadInt(); +} + +//----------------------------------------------------------------------------- +// Purpose: clear chunk setup, ChunkAvailable will return false +//----------------------------------------------------------------------------- +void IterateRIFF::ChunkClear( void ) +{ + m_chunkSize = -1; +} + +//----------------------------------------------------------------------------- +// Purpose: If there are chunks left to read beyond this one, return true +//----------------------------------------------------------------------------- +bool IterateRIFF::ChunkAvailable( void ) +{ + if ( m_chunkSize != -1 && m_chunkSize < 0x10000000 ) + return true; + + return false; +} + + +//----------------------------------------------------------------------------- +// Purpose: Go to the next chunk in the file, return true if there is one. +//----------------------------------------------------------------------------- +bool IterateRIFF::ChunkNext( void ) +{ + if ( !ChunkAvailable() ) + return false; + + int nextPos = m_chunkPosition + 8 + m_chunkSize; + + // chunks are aligned + nextPos += m_chunkSize & 1; + + if ( nextPos >= (m_start + m_size) ) + { + ChunkClear(); + return false; + } + + m_riff.PositionSet( nextPos ); + + ChunkSetup(); + return ChunkAvailable(); + +} + + +//----------------------------------------------------------------------------- +// Purpose: get the chunk FOURCC as an int +// Output : unsigned int +//----------------------------------------------------------------------------- +unsigned int IterateRIFF::ChunkName( void ) +{ + return m_chunkName; +} + + +//----------------------------------------------------------------------------- +// Purpose: get the size of this chunk +// Output : unsigned int +//----------------------------------------------------------------------------- +unsigned int IterateRIFF::ChunkSize( void ) +{ + return m_chunkSize; +} + +//----------------------------------------------------------------------------- +// Purpose: Read the entire chunk into a buffer +// Input : *pOutput - dest buffer +// Output : int bytes read +//----------------------------------------------------------------------------- +int IterateRIFF::ChunkRead( void *pOutput ) +{ + return m_riff.ReadData( pOutput, ChunkSize() ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Read a partial chunk (updates file position for subsequent partial reads). +// Input : *pOutput - dest buffer +// dataSize - partial size +// Output : int - bytes read +//----------------------------------------------------------------------------- +int IterateRIFF::ChunkReadPartial( void *pOutput, int dataSize ) +{ + return m_riff.ReadData( pOutput, dataSize ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Read a 4-byte int +// Output : int - read int +//----------------------------------------------------------------------------- +int IterateRIFF::ChunkReadInt( void ) +{ + return m_riff.ReadInt(); +} + +//----------------------------------------------------------------------------- +// Purpose: Used to iterate over an InFileRIFF +//----------------------------------------------------------------------------- +IterateOutputRIFF::IterateOutputRIFF( OutFileRIFF &riff ) +: m_riff( riff ) +{ + if ( !m_riff.IsValid() ) + return; + + m_start = m_riff.PositionGet(); + m_chunkPosition = m_start; + m_chunkStart = -1; +} + +IterateOutputRIFF::IterateOutputRIFF( IterateOutputRIFF &parent ) + : m_riff(parent.m_riff) +{ + m_start = parent.ChunkFilePosition(); + m_chunkPosition = m_start; + m_chunkStart = -1; +} + +void IterateOutputRIFF::ChunkWrite( unsigned int chunkname, void *pOutput, int size ) +{ + m_chunkPosition = m_riff.PositionGet(); + + m_chunkName = chunkname; + m_chunkSize = size; + + m_riff.WriteInt( chunkname ); + m_riff.WriteInt( size ); + m_riff.WriteData( pOutput, size ); + + m_chunkPosition = m_riff.PositionGet(); + + m_chunkPosition += m_chunkPosition & 1; + + m_riff.PositionSet( m_chunkPosition ); + + m_chunkStart = -1; +} + +void IterateOutputRIFF::ChunkWriteInt( int number ) +{ + m_riff.WriteInt( number ); +} + +void IterateOutputRIFF::ChunkWriteData( void *pOutput, int size ) +{ + m_riff.WriteData( pOutput, size ); +} + +void IterateOutputRIFF::ChunkFinish( void ) +{ + Assert( m_chunkStart != -1 ); + + m_chunkPosition = m_riff.PositionGet(); + + int size = m_chunkPosition - m_chunkStart - 8; + + m_chunkPosition += m_chunkPosition & 1; + + m_riff.PositionSet( m_chunkStart + sizeof( int ) ); + + m_riff.WriteInt( size ); + + m_riff.PositionSet( m_chunkPosition ); + + m_chunkStart = -1; +} + +void IterateOutputRIFF::ChunkStart( unsigned int chunkname ) +{ + Assert( m_chunkStart == -1 ); + + m_chunkStart = m_riff.PositionGet(); + + m_riff.WriteInt( chunkname ); + m_riff.WriteInt( 0 ); +} + +void IterateOutputRIFF::ChunkSetPosition( int position ) +{ + m_riff.PositionSet( position ); +} + +unsigned int IterateOutputRIFF::ChunkGetPosition( void ) +{ + return m_riff.PositionGet(); +} + +void IterateOutputRIFF::CopyChunkData( IterateRIFF& input ) +{ + if ( input.ChunkSize() > 0 ) + { + char *buffer = new char[ input.ChunkSize() ]; + Assert( buffer ); + + input.ChunkRead( buffer ); + + // Don't copy/write the name or size, just the data itself + ChunkWriteData( buffer, input.ChunkSize() ); + + delete[] buffer; + } +} + +void IterateOutputRIFF::SetLISETData( int position ) +{ + m_riff.HasLISETData( position ); +} diff --git a/public/scratchpad3d.cpp b/public/scratchpad3d.cpp index 55f46d2c..6747834c 100644 --- a/public/scratchpad3d.cpp +++ b/public/scratchpad3d.cpp @@ -12,7 +12,7 @@ // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" -#ifdef _LINUX +#if defined _LINUX && !defined __stdcall #define __stdcall #endif diff --git a/public/sentence.cpp b/public/sentence.cpp index 3ba746c0..d5ff7c5d 100644 --- a/public/sentence.cpp +++ b/public/sentence.cpp @@ -1,1567 +1,1567 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -// -//=============================================================================// - -#if !defined(_STATIC_LINKED) || defined(_SHARED_LIB) - -#include -#include "commonmacros.h" -#include "basetypes.h" -#include "sentence.h" -#include "utlbuffer.h" -#include -#include "vector.h" -#include "mathlib.h" -#include -#include "checksum_crc.h" -// memdbgon must be the last include file in a .cpp file!!! -#include "tier0/memdbgon.h" - -//----------------------------------------------------------------------------- -// Purpose: converts an english string to unicode -//----------------------------------------------------------------------------- -int ConvertANSIToUnicode(const char *ansi, wchar_t *unicode, int unicodeBufferSize); - -#if PHONEME_EDITOR -void CEmphasisSample::SetSelected( bool isSelected ) -{ - selected = isSelected; -} -void CPhonemeTag::SetSelected( bool isSelected ) -{ - m_bSelected = isSelected; -} -bool CPhonemeTag::GetSelected() const -{ - return m_bSelected; -} -void CPhonemeTag::SetStartAndEndBytes( unsigned int start, unsigned int end ) -{ - m_uiStartByte = start; - m_uiEndByte = end; -} -unsigned int CPhonemeTag::GetStartByte() const -{ - return m_uiStartByte; -} -unsigned int CPhonemeTag::GetEndByte() const -{ - return m_uiEndByte; -} -void CWordTag::SetSelected( bool isSelected ) -{ - m_bSelected = isSelected; -} -bool CWordTag::GetSelected() const -{ - return m_bSelected; -} -void CWordTag::SetStartAndEndBytes( unsigned int start, unsigned int end ) -{ - m_uiStartByte = start; - m_uiEndByte = end; -} -unsigned int CWordTag::GetStartByte() const -{ - return m_uiStartByte; -} -unsigned int CWordTag::GetEndByte() const -{ - return m_uiEndByte; -} -#else -// xbox doesn't store this data -void CEmphasisSample::SetSelected( bool isSelected ) {} -void CPhonemeTag::SetSelected( bool isSelected ) {} -bool CPhonemeTag::GetSelected() const { return false; } -void CPhonemeTag::SetStartAndEndBytes( unsigned int start, unsigned int end ) {} -unsigned int CPhonemeTag::GetStartByte() const { return 0; } -unsigned int CPhonemeTag::GetEndByte() const { return 0; } -void CWordTag::SetSelected( bool isSelected ) {} -bool CWordTag::GetSelected() const { return false; } -void CWordTag::SetStartAndEndBytes( unsigned int start, unsigned int end ) {} -unsigned int CWordTag::GetStartByte() const { return 0; } -unsigned int CWordTag::GetEndByte() const { return 0; } -#endif - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -CWordTag::CWordTag( void ) -{ - m_pszWord = NULL; - - SetStartAndEndBytes( 0, 0 ); - - m_flStartTime = 0.0f; - m_flEndTime = 0.0f; - - SetSelected( false ); -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : from - -//----------------------------------------------------------------------------- -CWordTag::CWordTag( const CWordTag& from ) -{ - m_pszWord = NULL; - SetWord( from.m_pszWord ); - - SetStartAndEndBytes( from.GetStartByte(), from.GetEndByte() ); - - m_flStartTime = from.m_flStartTime; - m_flEndTime = from.m_flEndTime; - - SetSelected( from.GetSelected() ); - - for ( int p = 0; p < from.m_Phonemes.Size(); p++ ) - { - CPhonemeTag *newPhoneme = new CPhonemeTag( *from.m_Phonemes[ p ] ); - m_Phonemes.AddToTail( newPhoneme ); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *word - -//----------------------------------------------------------------------------- -CWordTag::CWordTag( const char *word ) -{ - SetStartAndEndBytes( 0, 0 ); - - m_flStartTime = 0.0f; - m_flEndTime = 0.0f; - - m_pszWord = NULL; - - SetSelected( false ); - - SetWord( word ); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -CWordTag::~CWordTag( void ) -{ - delete[] m_pszWord; - - while ( m_Phonemes.Size() > 0 ) - { - delete m_Phonemes[ 0 ]; - m_Phonemes.Remove( 0 ); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *tag - -// Output : int -//----------------------------------------------------------------------------- -int CWordTag::IndexOfPhoneme( CPhonemeTag *tag ) -{ - for ( int i = 0 ; i < m_Phonemes.Size(); i++ ) - { - CPhonemeTag *p = m_Phonemes[ i ]; - if ( p == tag ) - return i; - } - return -1; -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *word - -//----------------------------------------------------------------------------- -void CWordTag::SetWord( const char *word ) -{ - delete[] m_pszWord; - m_pszWord = NULL; - if ( !word || !word[ 0 ] ) - return; - - int len = strlen( word ) + 1; - m_pszWord = new char[ len ]; - Assert( m_pszWord ); - Q_strncpy( m_pszWord, word, len ); -} - -//----------------------------------------------------------------------------- -// Purpose: -// Output : const char -//----------------------------------------------------------------------------- -const char *CWordTag::GetWord() const -{ - return m_pszWord ? m_pszWord : ""; -} - - -unsigned int CWordTag::ComputeDataCheckSum() -{ - int i; - int c; - CRC32_t crc; - CRC32_Init( &crc ); - - // Checksum the text - if ( m_pszWord != NULL ) - { - CRC32_ProcessBuffer( &crc, m_pszWord, Q_strlen( m_pszWord ) ); - } - // Checksum phonemes - c = m_Phonemes.Count(); - for ( i = 0; i < c; ++i ) - { - CPhonemeTag *phoneme = m_Phonemes[ i ]; - unsigned int phonemeCheckSum = phoneme->ComputeDataCheckSum(); - CRC32_ProcessBuffer( &crc, &phonemeCheckSum, sizeof( unsigned int ) ); - } - // Checksum timestamps - CRC32_ProcessBuffer( &crc, &m_flStartTime, sizeof( float ) ); - CRC32_ProcessBuffer( &crc, &m_flEndTime, sizeof( float ) ); - - CRC32_Final( &crc ); - - return ( unsigned int )crc; -} - -CBasePhonemeTag::CBasePhonemeTag() -{ - m_flStartTime = 0; - m_flEndTime = 0; - - m_nPhonemeCode = 0; -} - -CBasePhonemeTag::CBasePhonemeTag( const CBasePhonemeTag& from ) -{ - memcpy( this, &from, sizeof(*this) ); -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -CPhonemeTag::CPhonemeTag( void ) -{ - m_szPhoneme = NULL; - - SetStartAndEndBytes( 0, 0 ); - - SetSelected( false ); -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : from - -//----------------------------------------------------------------------------- -CPhonemeTag::CPhonemeTag( const CPhonemeTag& from ) : - BaseClass( from ) -{ - SetStartAndEndBytes( from.GetStartByte(), from.GetEndByte() ); - - SetSelected( from.GetSelected() ); - - m_szPhoneme = NULL; - SetTag( from.GetTag() ); -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *phoneme - -//----------------------------------------------------------------------------- -CPhonemeTag::CPhonemeTag( const char *phoneme ) -{ - SetStartAndEndBytes( 0, 0 ); - - SetStartTime( 0.0f ); - SetEndTime( 0.0f ); - - SetSelected( false ); - - SetPhonemeCode( 0 ); - - m_szPhoneme = NULL; - SetTag( phoneme ); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -CPhonemeTag::~CPhonemeTag( void ) -{ - delete[] m_szPhoneme; -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *phoneme - -//----------------------------------------------------------------------------- -void CPhonemeTag::SetTag( const char *phoneme ) -{ - delete m_szPhoneme; - m_szPhoneme = NULL; - if ( !phoneme || !phoneme [ 0 ] ) - return; - - int len = Q_strlen( phoneme ) + 1; - m_szPhoneme = new char[ len ]; - Assert( m_szPhoneme ); - Q_strncpy( m_szPhoneme, phoneme, len ); -} - -char const *CPhonemeTag::GetTag() const -{ - return m_szPhoneme ? m_szPhoneme : ""; -} - - -unsigned int CPhonemeTag::ComputeDataCheckSum() -{ - CRC32_t crc; - CRC32_Init( &crc ); - - // Checksum the text - CRC32_ProcessBuffer( &crc, m_szPhoneme, Q_strlen( m_szPhoneme ) ); - int phonemeCode = GetPhonemeCode(); - CRC32_ProcessBuffer( &crc, &phonemeCode, sizeof( int ) ); - - // Checksum timestamps - float startTime = GetStartTime(); - float endTime = GetEndTime(); - CRC32_ProcessBuffer( &crc, &startTime, sizeof( float ) ); - CRC32_ProcessBuffer( &crc, &endTime, sizeof( float ) ); - - CRC32_Final( &crc ); - - return ( unsigned int )crc; -} - -//----------------------------------------------------------------------------- -// Purpose: Simple language to string and string to language lookup dictionary -//----------------------------------------------------------------------------- -#pragma pack(1) - -struct CCLanguage -{ - int type; - char const *name; - unsigned char r, g, b; // For faceposer, indicator color for this language -}; - -static CCLanguage g_CCLanguageLookup[] = -{ - { CC_ENGLISH, "english", 0, 0, 0 }, - { CC_FRENCH, "french", 150, 0, 0 }, - { CC_GERMAN, "german", 0, 150, 0 }, - { CC_ITALIAN, "italian", 0, 150, 150 }, - { CC_KOREAN, "korean", 150, 0, 150 }, - { CC_SCHINESE, "schinese", 150, 0, 150 }, - { CC_SPANISH, "spanish", 0, 0, 150 }, - { CC_TCHINESE, "tchinese", 150, 0, 150 }, - { CC_JAPANESE, "japanese", 250, 150, 0 }, - { CC_RUSSIAN, "russian", 0, 250, 150 }, - { CC_THAI, "thai", 0 , 150, 250 }, - { CC_PORTUGUESE,"portuguese", 0 , 0, 150 }, -}; - -#pragma pack() - -void CSentence::ColorForLanguage( int language, unsigned char& r, unsigned char& g, unsigned char& b ) -{ - r = g = b = 0; - - if ( language < 0 || language >= CC_NUM_LANGUAGES ) - { - return; - } - - r = g_CCLanguageLookup[ language ].r; - g = g_CCLanguageLookup[ language ].g; - b = g_CCLanguageLookup[ language ].b; -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : language - -// Output : char const -//----------------------------------------------------------------------------- -char const *CSentence::NameForLanguage( int language ) -{ - if ( language < 0 || language >= CC_NUM_LANGUAGES ) - return "unknown_language"; - - CCLanguage *entry = &g_CCLanguageLookup[ language ]; - Assert( entry->type == language ); - return entry->name; -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *name - -// Output : int -//----------------------------------------------------------------------------- -int CSentence::LanguageForName( char const *name ) -{ - int l; - for ( l = 0; l < CC_NUM_LANGUAGES; l++ ) - { - CCLanguage *entry = &g_CCLanguageLookup[ l ]; - Assert( entry->type == l ); - if ( !stricmp( entry->name, name ) ) - return l; - } - return -1; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -CSentence::CSentence( void ) -{ -#if PHONEME_EDITOR - m_nResetWordBase = 0; - m_szText = 0; - m_uCheckSum = 0; -#endif - m_bShouldVoiceDuck = false; - m_bStoreCheckSum = false; - m_bIsValid = false; - m_bIsCached = false; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -CSentence::~CSentence( void ) -{ - Reset(); -#if PHONEME_EDITOR - delete[] m_szText; -#endif -} - - -void CSentence::ParsePlaintext( CUtlBuffer& buf ) -{ - char token[ 4096 ]; - char text[ 4096 ]; - text[ 0 ] = 0; - while ( 1 ) - { - buf.GetString( token ); - if ( !stricmp( token, "}" ) ) - break; - - Q_strncat( text, token, sizeof( text ), COPY_ALL_CHARACTERS ); - Q_strncat( text, " ", sizeof( text ), COPY_ALL_CHARACTERS ); - } - - SetText( text ); -} - -void CSentence::ParseWords( CUtlBuffer& buf ) -{ - char token[ 4096 ]; - char word[ 256 ]; - float start, end; - - while ( 1 ) - { - buf.GetString( token ); - if ( !stricmp( token, "}" ) ) - break; - - if ( stricmp( token, "WORD" ) ) - break; - - buf.GetString( token ); - Q_strncpy( word, token, sizeof( word ) ); - - buf.GetString( token ); - start = atof( token ); - buf.GetString( token ); - end = atof( token ); - - CWordTag *wt = new CWordTag( word ); - assert( wt ); - wt->m_flStartTime = start; - wt->m_flEndTime = end; - - AddWordTag( wt ); - - buf.GetString( token ); - if ( stricmp( token, "{" ) ) - break; - - while ( 1 ) - { - buf.GetString( token ); - if ( !stricmp( token, "}" ) ) - break; - - // Parse phoneme - int code; - char phonemename[ 256 ]; - float start, end; - float volume; - - code = atoi( token ); - - buf.GetString( token ); - Q_strncpy( phonemename, token, sizeof( phonemename ) ); - buf.GetString( token ); - start = atof( token ); - buf.GetString( token ); - end = atof( token ); - buf.GetString( token ); - volume = atof( token ); - - CPhonemeTag *pt = new CPhonemeTag(); - assert( pt ); - pt->SetPhonemeCode( code ); - pt->SetTag( phonemename ); - pt->SetStartTime( start ); - pt->SetEndTime( end ); - - AddPhonemeTag( wt, pt ); - } - } -} - -void CSentence::ParseEmphasis( CUtlBuffer& buf ) -{ - char token[ 4096 ]; - while ( 1 ) - { - buf.GetString( token ); - if ( !stricmp( token, "}" ) ) - break; - - char t[ 256 ]; - Q_strncpy( t, token, sizeof( t ) ); - buf.GetString( token ); - - char value[ 256 ]; - Q_strncpy( value, token, sizeof( value ) ); - - CEmphasisSample sample; - sample.SetSelected( false ); - sample.time = atof( t ); - sample.value = atof( value ); - - - m_EmphasisSamples.AddToTail( sample ); - - } -} - -// This is obsolete, so it doesn't do anything with the data which is parsed. -void CSentence::ParseCloseCaption( CUtlBuffer& buf ) -{ - char token[ 4096 ]; - while ( 1 ) - { - // Format is - // language_name - // { - // PHRASE char streamlength "streambytes" starttime endtime - // PHRASE unicode streamlength "streambytes" starttime endtime - // } - buf.GetString( token ); - if ( !stricmp( token, "}" ) ) - break; - - buf.GetString( token ); - if ( stricmp( token, "{" ) ) - break; - - buf.GetString( token ); - while ( 1 ) - { - - if ( !stricmp( token, "}" ) ) - break; - - if ( stricmp( token, "PHRASE" ) ) - break; - - char cc_type[32]; - char cc_stream[ 4096 ]; - int cc_length; - - memset( cc_stream, 0, sizeof( cc_stream ) ); - - buf.GetString( token ); - Q_strncpy( cc_type, token, sizeof( cc_type ) ); - - bool unicode = false; - if ( !stricmp( cc_type, "unicode" ) ) - { - unicode = true; - } - else if ( stricmp( cc_type, "char" ) ) - { - Assert( 0 ); - } - - buf.GetString( token ); - cc_length = atoi( token ); - Assert( cc_length >= 0 && cc_length < sizeof( cc_stream ) ); - // Skip space - buf.GetChar(); - buf.Get( cc_stream, cc_length ); - cc_stream[ cc_length ] = 0; - - // Skip space - buf.GetChar(); - buf.GetString( token ); - buf.GetString( token ); - - - buf.GetString( token ); - } - - } -} - -void CSentence::ParseOptions( CUtlBuffer& buf ) -{ - char token[ 4096 ]; - while ( 1 ) - { - buf.GetString( token ); - if ( !stricmp( token, "}" ) ) - break; - - if ( Q_strlen( token ) == 0 ) - break; - - char key[ 256 ]; - Q_strncpy( key, token, sizeof( key ) ); - char value[ 256 ]; - buf.GetString( token ); - Q_strncpy( value, token, sizeof( value ) ); - - if ( !strcmpi( key, "voice_duck" ) ) - { - SetVoiceDuck( atoi(value) ? true : false ); - } - else if ( !strcmpi( key, "checksum" ) ) - { - SetDataCheckSum( (unsigned int)atoi( value ) ); - } - } -} - - -//----------------------------------------------------------------------------- -// Purpose: VERSION 1.0 parser, need to implement new ones if -// file format changes!!! -// Input : buf - -//----------------------------------------------------------------------------- -void CSentence::ParseDataVersionOnePointZero( CUtlBuffer& buf ) -{ - char token[ 4096 ]; - - while ( 1 ) - { - buf.GetString( token ); - if ( strlen( token ) <= 0 ) - break; - - char section[ 256 ]; - Q_strncpy( section, token, sizeof( section ) ); - - buf.GetString( token ); - if ( stricmp( token, "{" ) ) - break; - - if ( !stricmp( section, "PLAINTEXT" ) ) - { - ParsePlaintext( buf ); - } - else if ( !stricmp( section, "WORDS" ) ) - { - ParseWords( buf ); - } - else if ( !stricmp( section, "EMPHASIS" ) ) - { - ParseEmphasis( buf ); - } - else if ( !stricmp( section, "CLOSECAPTION" ) ) - { - // NOTE: CLOSECAPTION IS NO LONGER VALID - // This just skips the section of data. - ParseCloseCaption( buf ); - } - else if ( !stricmp( section, "OPTIONS" ) ) - { - ParseOptions( buf ); - } - } -} - -#define CACHED_SENTENCE_VERSION 1 - -// This is a compressed save of just the data needed to drive phonemes in the engine (no word / sentence text, etc ) -//----------------------------------------------------------------------------- -// Purpose: -// Input : buf - -//----------------------------------------------------------------------------- -void CSentence::CacheSaveToBuffer( CUtlBuffer& buf ) -{ - Assert( !buf.IsText() ); - Assert( m_bIsCached ); - - buf.PutChar( CACHED_SENTENCE_VERSION ); - - int i; - unsigned short pcount = GetRuntimePhonemeCount(); - - buf.PutShort( pcount ); - - for ( i = 0; i < pcount; ++i ) - { - const CBasePhonemeTag *phoneme = GetRuntimePhoneme( i ); - Assert( phoneme ); - - buf.PutShort( phoneme->GetPhonemeCode() ); - buf.PutFloat( phoneme->GetStartTime() ); - buf.PutFloat( phoneme->GetEndTime() ); - } - - // Now save out emphasis samples - int c = m_EmphasisSamples.Count(); - Assert( c <= 32767 ); - buf.PutShort( c ); - - for ( i = 0; i < c; i++ ) - { - CEmphasisSample *sample = &m_EmphasisSamples[ i ]; - Assert( sample ); - - buf.PutFloat( sample->time ); - short scaledValue = clamp( (short)( sample->value * 32767 ), 0, 32767 ); - - buf.PutShort( scaledValue ); - } - - // And voice duck - buf.PutChar( GetVoiceDuck() ? 1 : 0 ); -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : buf - -//----------------------------------------------------------------------------- -void CSentence::CacheRestoreFromBuffer( CUtlBuffer& buf ) -{ - Assert( !buf.IsText() ); - - Reset(); - - m_bIsCached = true; - - int version = buf.GetChar(); - if ( version != CACHED_SENTENCE_VERSION ) - { - // Uh oh, version changed... - m_bIsValid = false; - return; - } - - unsigned short pcount = (unsigned short)buf.GetShort(); - - CPhonemeTag pt; - int i; - - for ( i = 0; i < pcount; ++i ) - { - unsigned short code = buf.GetShort(); - float st = buf.GetFloat(); - float et = buf.GetFloat(); - - pt.SetPhonemeCode( code ); - pt.SetStartTime( st ); - pt.SetEndTime( et ); - - AddRuntimePhoneme( &pt ); - } - - // Now read emphasis samples - int c = buf.GetShort(); - - for ( i = 0; i < c; i++ ) - { - CEmphasisSample sample; - sample.SetSelected( false ); - - sample.time = buf.GetFloat(); - sample.value = (float)buf.GetShort() / 32767.0f; - - m_EmphasisSamples.AddToTail( sample ); - } - - // And voice duck - SetVoiceDuck( buf.GetChar() == 0 ? false : true ); - m_bIsValid = true; -} - -int CSentence::GetRuntimePhonemeCount() const -{ - return m_RunTimePhonemes.Count(); -} - -const CBasePhonemeTag *CSentence::GetRuntimePhoneme( int i ) const -{ - Assert( m_bIsCached ); - return m_RunTimePhonemes[ i ]; -} - -void CSentence::ClearRuntimePhonemes() -{ - while ( m_RunTimePhonemes.Count() > 0 ) - { - CBasePhonemeTag *tag = m_RunTimePhonemes[ 0 ]; - delete tag; - m_RunTimePhonemes.Remove( 0 ); - } -} - -void CSentence::AddRuntimePhoneme( const CPhonemeTag *src ) -{ - Assert( m_bIsCached ); - - CBasePhonemeTag *tag = new CBasePhonemeTag(); - *tag = *src; - - m_RunTimePhonemes.AddToTail( tag ); -} - -void CSentence::MakeRuntimeOnly() -{ - m_bIsCached = true; -#if PHONEME_EDITOR - delete m_szText; - m_szText = NULL; - - int c = m_Words.Count(); - for ( int i = 0; i < c; ++i ) - { - CWordTag *word = m_Words[ i ]; - Assert( word ); - int pcount = word->m_Phonemes.Count(); - for ( int j = 0; j < pcount; ++j ) - { - CPhonemeTag *phoneme = word->m_Phonemes[ j ]; - assert( phoneme ); - - AddRuntimePhoneme( phoneme ); - } - } - - // Remove all existing words - while ( m_Words.Count() > 0 ) - { - CWordTag *word = m_Words[ 0 ]; - delete word; - m_Words.Remove( 0 ); - } -#endif - m_bIsValid = true; -} - - -void CSentence::SaveToBuffer( CUtlBuffer& buf ) -{ -#if PHONEME_EDITOR - Assert( !m_bIsCached ); - - int i, j; - - buf.Printf( "VERSION 1.0\n" ); - - buf.Printf( "PLAINTEXT\n" ); - buf.Printf( "{\n" ); - buf.Printf( "%s\n", GetText() ); - buf.Printf( "}\n" ); - buf.Printf( "WORDS\n" ); - buf.Printf( "{\n" ); - for ( i = 0; i < m_Words.Size(); i++ ) - { - CWordTag *word = m_Words[ i ]; - Assert( word ); - - buf.Printf( "WORD %s %.3f %.3f\n", - word->GetWord(), - word->m_flStartTime, - word->m_flEndTime ); - - buf.Printf( "{\n" ); - for ( j = 0; j < word->m_Phonemes.Size(); j++ ) - { - CPhonemeTag *phoneme = word->m_Phonemes[ j ]; - Assert( phoneme ); - - buf.Printf( "%i %s %.3f %.3f 1\n", - phoneme->GetPhonemeCode(), - phoneme->GetTag(), - phoneme->GetStartTime(), - phoneme->GetEndTime() ); - } - - buf.Printf( "}\n" ); - } - buf.Printf( "}\n" ); - buf.Printf( "EMPHASIS\n" ); - buf.Printf( "{\n" ); - int c = m_EmphasisSamples.Count(); - for ( i = 0; i < c; i++ ) - { - CEmphasisSample *sample = &m_EmphasisSamples[ i ]; - Assert( sample ); - - buf.Printf( "%f %f\n", sample->time, sample->value ); - } - - buf.Printf( "}\n" ); - buf.Printf( "OPTIONS\n" ); - buf.Printf( "{\n" ); - buf.Printf( "voice_duck %d\n", GetVoiceDuck() ? 1 : 0 ); - if ( m_bStoreCheckSum ) - { - buf.Printf( "checksum %d\n", m_uCheckSum ); - } - buf.Printf( "}\n" ); -#else - Assert( 0 ); -#endif -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *data - -// size - -//----------------------------------------------------------------------------- -void CSentence::InitFromDataChunk( void *data, int size ) -{ - CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER ); - buf.EnsureCapacity( size ); - buf.Put( data, size ); - buf.SeekPut( CUtlBuffer::SEEK_HEAD, size ); - - InitFromBuffer( buf ); -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : buf - -//----------------------------------------------------------------------------- -void CSentence::InitFromBuffer( CUtlBuffer& buf ) -{ - Assert( buf.IsText() ); - - Reset(); - - char token[ 4096 ]; - buf.GetString( token ); - - if ( stricmp( token, "VERSION" ) ) - return; - - buf.GetString( token ); - if ( atof( token ) == 1.0f ) - { - ParseDataVersionOnePointZero( buf ); - m_bIsValid = true; - } - else - { - assert( 0 ); - return; - } -} - -//----------------------------------------------------------------------------- -// Purpose: -// Output : int -//----------------------------------------------------------------------------- -int CSentence::GetWordBase( void ) -{ -#if PHONEME_EDITOR - return m_nResetWordBase; -#else - Assert( 0 ); - return 0; -#endif -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CSentence::ResetToBase( void ) -{ -#if PHONEME_EDITOR - // Delete everything after m_nResetWordBase - while ( m_Words.Size() > m_nResetWordBase ) - { - delete m_Words[ m_Words.Size() - 1 ]; - m_Words.Remove( m_Words.Size() - 1 ); - } -#endif - ClearRuntimePhonemes(); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CSentence::MarkNewPhraseBase( void ) -{ -#if PHONEME_EDITOR - m_nResetWordBase = max( m_Words.Size(), 0 ); -#endif -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CSentence::Reset( void ) -{ -#if PHONEME_EDITOR - m_nResetWordBase = 0; - - while ( m_Words.Size() > 0 ) - { - delete m_Words[ 0 ]; - m_Words.Remove( 0 ); - } -#endif - m_EmphasisSamples.RemoveAll(); - - ClearRuntimePhonemes(); -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *tag - -//----------------------------------------------------------------------------- -void CSentence::AddPhonemeTag( CWordTag *word, CPhonemeTag *tag ) -{ - word->m_Phonemes.AddToTail( tag ); -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *tag - -//----------------------------------------------------------------------------- -void CSentence::AddWordTag( CWordTag *tag ) -{ -#if PHONEME_EDITOR - m_Words.AddToTail( tag ); -#endif -} - -//----------------------------------------------------------------------------- -// Purpose: -// Output : int -//----------------------------------------------------------------------------- -int CSentence::CountPhonemes( void ) -{ - int c = 0; -#if PHONEME_EDITOR - for( int i = 0; i < m_Words.Size(); i++ ) - { - CWordTag *word = m_Words[ i ]; - c += word->m_Phonemes.Size(); - } -#endif - return c; -} - -//----------------------------------------------------------------------------- -// Purpose: // For legacy loading, try to find a word that contains the time -// Input : time - -// Output : CWordTag -//----------------------------------------------------------------------------- -CWordTag *CSentence::EstimateBestWord( float time ) -{ -#if PHONEME_EDITOR - CWordTag *bestWord = NULL; - - for( int i = 0; i < m_Words.Size(); i++ ) - { - CWordTag *word = m_Words[ i ]; - if ( !word ) - continue; - - if ( word->m_flStartTime <= time && word->m_flEndTime >= time ) - return word; - - if ( time < word->m_flStartTime ) - { - bestWord = word; - } - - if ( time > word->m_flEndTime && bestWord ) - return bestWord; - } - - // return best word if we found one - if ( bestWord ) - { - return bestWord; - } - - // Return last word - if ( m_Words.Size() >= 1 ) - { - return m_Words[ m_Words.Size() - 1 ]; - } -#endif - // Oh well - return NULL; -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *phoneme - -// Output : CWordTag -//----------------------------------------------------------------------------- -CWordTag *CSentence::GetWordForPhoneme( CPhonemeTag *phoneme ) -{ -#if PHONEME_EDITOR - for( int i = 0; i < m_Words.Size(); i++ ) - { - CWordTag *word = m_Words[ i ]; - if ( !word ) - continue; - - for ( int j = 0 ; j < word->m_Phonemes.Size() ; j++ ) - { - CPhonemeTag *p = word->m_Phonemes[ j ]; - if ( p == phoneme ) - { - return word; - } - } - - } -#endif - return NULL; -} - -//----------------------------------------------------------------------------- -// Purpose: Assignment operator -// Input : src - -// Output : CSentence& -//----------------------------------------------------------------------------- -CSentence& CSentence::operator=( const CSentence& src ) -{ - int i; - - // Clear current stuff - Reset(); - - int c; - -#if PHONEME_EDITOR - // Copy everything - for ( i = 0 ; i < src.m_Words.Size(); i++ ) - { - CWordTag *word = src.m_Words[ i ]; - - CWordTag *newWord = new CWordTag( *word ); - - AddWordTag( newWord ); - } - - SetText( src.GetText() ); - m_nResetWordBase = src.m_nResetWordBase; - - c = src.m_EmphasisSamples.Size(); - for ( i = 0; i < c; i++ ) - { - CEmphasisSample s = src.m_EmphasisSamples[ i ]; - m_EmphasisSamples.AddToTail( s ); - } -#endif - - m_bIsCached = src.m_bIsCached; - - c = src.GetRuntimePhonemeCount(); - for ( i = 0; i < c; i++ ) - { - Assert( m_bIsCached ); - - const CBasePhonemeTag *tag = src.GetRuntimePhoneme( i ); - CPhonemeTag full; - ((CBasePhonemeTag &)(full)) = *tag; - - AddRuntimePhoneme( &full ); - } - - m_bShouldVoiceDuck = src.m_bShouldVoiceDuck; -#if PHONEME_EDITOR - m_bStoreCheckSum = src.m_bStoreCheckSum; - m_uCheckSum = src.m_uCheckSum; -#endif - m_bIsValid = src.m_bIsValid; - - return (*this); -} - -void CSentence::Append( float starttime, const CSentence& src ) -{ -#if PHONEME_EDITOR - int i; - // Combine - for ( i = 0 ; i < src.m_Words.Size(); i++ ) - { - CWordTag *word = src.m_Words[ i ]; - - CWordTag *newWord = new CWordTag( *word ); - - newWord->m_flStartTime += starttime; - newWord->m_flEndTime += starttime; - - // Offset times - int c = newWord->m_Phonemes.Count(); - for ( int i = 0; i < c; ++i ) - { - CPhonemeTag *tag = newWord->m_Phonemes[ i ]; - tag->AddStartTime( starttime ); - tag->AddEndTime( starttime ); - } - - AddWordTag( newWord ); - } - - if ( src.GetText()[ 0 ] ) - { - char fulltext[ 4096 ]; - if ( GetText()[ 0 ] ) - { - Q_snprintf( fulltext, sizeof( fulltext ), "%s %s", GetText(), src.GetText() ); - } - else - { - Q_strncpy( fulltext, src.GetText(), sizeof( fulltext ) ); - } - SetText( fulltext ); - } - - int c = src.m_EmphasisSamples.Size(); - for ( i = 0; i < c; i++ ) - { - CEmphasisSample s = src.m_EmphasisSamples[ i ]; - - s.time += starttime; - - m_EmphasisSamples.AddToTail( s ); - } - - // Or in voice duck settings - m_bShouldVoiceDuck |= src.m_bShouldVoiceDuck; -#else - Assert( 0 ); -#endif -} - - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *text - -//----------------------------------------------------------------------------- -void CSentence::SetText( const char *text ) -{ -#if PHONEME_EDITOR - delete[] m_szText; - m_szText = NULL; - - if ( !text || !text[ 0 ] ) - { - return; - } - - int len = Q_strlen( text ) + 1; - - m_szText = new char[ len ]; - Assert( m_szText ); - Q_strncpy( m_szText, text, len ); -#endif -} - -//----------------------------------------------------------------------------- -// Purpose: -// Output : const char -//----------------------------------------------------------------------------- -const char *CSentence::GetText( void ) const -{ -#if PHONEME_EDITOR - return m_szText ? m_szText : ""; -#else - return ""; -#endif -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CSentence::SetTextFromWords( void ) -{ -#if PHONEME_EDITOR - char fulltext[ 1024 ]; - fulltext[ 0 ] = 0; - for ( int i = 0 ; i < m_Words.Size(); i++ ) - { - CWordTag *word = m_Words[ i ]; - - Q_strncat( fulltext, word->GetWord(), sizeof( fulltext ), COPY_ALL_CHARACTERS ); - - if ( i != m_Words.Size() ) - { - Q_strncat( fulltext, " ", sizeof( fulltext ), COPY_ALL_CHARACTERS ); - } - } - - SetText( fulltext ); -#endif -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CSentence::Resort( void ) -{ - int c = m_EmphasisSamples.Size(); - for ( int i = 0; i < c; i++ ) - { - for ( int j = i + 1; j < c; j++ ) - { - CEmphasisSample src = m_EmphasisSamples[ i ]; - CEmphasisSample dest = m_EmphasisSamples[ j ]; - - if ( src.time > dest.time ) - { - m_EmphasisSamples[ i ] = dest; - m_EmphasisSamples[ j ] = src; - } - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : number - -// Output : CEmphasisSample -//----------------------------------------------------------------------------- -CEmphasisSample *CSentence::GetBoundedSample( int number, float endtime ) -{ - // Search for two samples which span time f - static CEmphasisSample nullstart; - nullstart.time = 0.0f; - nullstart.value = 0.5f; - static CEmphasisSample nullend; - nullend.time = endtime; - nullend.value = 0.5f; - - if ( number < 0 ) - { - return &nullstart; - } - else if ( number >= GetNumSamples() ) - { - return &nullend; - } - - return GetSample( number ); -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : time - -// type - -// Output : float -//----------------------------------------------------------------------------- -float CSentence::GetIntensity( float time, float endtime ) -{ - float zeroValue = 0.5f; - - int c = GetNumSamples(); - - if ( c <= 0 ) - { - return zeroValue; - } - - int i; - for ( i = -1 ; i < c; i++ ) - { - CEmphasisSample *s = GetBoundedSample( i, endtime ); - CEmphasisSample *n = GetBoundedSample( i + 1, endtime ); - if ( !s || !n ) - continue; - - if ( time >= s->time && time <= n->time ) - { - break; - } - } - - int prev = i - 1; - int start = i; - int end = i + 1; - int next = i + 2; - - prev = max( -1, prev ); - start = max( -1, start ); - end = min( end, GetNumSamples() ); - next = min( next, GetNumSamples() ); - - CEmphasisSample *esPre = GetBoundedSample( prev, endtime ); - CEmphasisSample *esStart = GetBoundedSample( start, endtime ); - CEmphasisSample *esEnd = GetBoundedSample( end, endtime ); - CEmphasisSample *esNext = GetBoundedSample( next, endtime ); - - float dt = esEnd->time - esStart->time; - dt = clamp( dt, 0.01f, 1.0f ); - - Vector vPre( esPre->time, esPre->value, 0 ); - Vector vStart( esStart->time, esStart->value, 0 ); - Vector vEnd( esEnd->time, esEnd->value, 0 ); - Vector vNext( esNext->time, esNext->value, 0 ); - - float f2 = ( time - esStart->time ) / ( dt ); - f2 = clamp( f2, 0.0f, 1.0f ); - - Vector vOut; - Catmull_Rom_Spline( - vPre, - vStart, - vEnd, - vNext, - f2, - vOut ); - - float retval = clamp( vOut.y, 0.0f, 1.0f ); - return retval; -} - -int CSentence::GetNumSamples( void ) -{ - return m_EmphasisSamples.Count(); -} - -CEmphasisSample *CSentence::GetSample( int index ) -{ - if ( index < 0 || index >= GetNumSamples() ) - return NULL; - - return &m_EmphasisSamples[ index ]; -} - -void CSentence::GetEstimatedTimes( float& start, float &end ) -{ -#if PHONEME_EDITOR - float beststart = 100000.0f; - float bestend = -100000.0f; - - int c = m_Words.Count(); - if ( !c ) - { - start = end = 0.0f; - return; - } - - for ( int i = 0; i< c; i++ ) - { - CWordTag *w = m_Words[ i ]; - Assert( w ); - if ( w->m_flStartTime < beststart ) - { - beststart = w->m_flStartTime; - } - if ( w->m_flEndTime > bestend ) - { - bestend = w->m_flEndTime; - } - } - - if ( beststart == 100000.0f ) - { - Assert( 0 ); - beststart = 0.0f; - } - if ( bestend == -100000.0f ) - { - Assert( 0 ); - bestend = 1.0f; - } - start = beststart; - end = bestend; -#endif -} - -void CSentence::SetDataCheckSum( unsigned int chk ) -{ -#if PHONEME_EDITOR - m_bStoreCheckSum = true; - m_uCheckSum = chk; -#endif -} - -unsigned int CSentence::ComputeDataCheckSum() -{ -#if PHONEME_EDITOR - int i; - int c; - CRC32_t crc; - CRC32_Init( &crc ); - - // Checksum the text - CRC32_ProcessBuffer( &crc, GetText(), Q_strlen( GetText() ) ); - // Checsum words and phonemes - c = m_Words.Count(); - for ( i = 0; i < c; ++i ) - { - CWordTag *word = m_Words[ i ]; - unsigned int wordCheckSum = word->ComputeDataCheckSum(); - CRC32_ProcessBuffer( &crc, &wordCheckSum, sizeof( unsigned int ) ); - } - - // Checksum emphasis data - c = m_EmphasisSamples.Count(); - for ( i = 0; i < c; ++i ) - { - CRC32_ProcessBuffer( &crc, &m_EmphasisSamples[ i ].time, sizeof( float ) ); - CRC32_ProcessBuffer( &crc, &m_EmphasisSamples[ i ].value, sizeof( float ) ); - } - - CRC32_Final( &crc ); - - return ( unsigned int )crc; -#else - Assert( 0 ); - return 0; -#endif -} - -unsigned int CSentence::GetDataCheckSum() const -{ -#if PHONEME_EDITOR - Assert( m_bStoreCheckSum ); - Assert( m_uCheckSum != 0 ); - return m_uCheckSum; -#else - Assert( 0 ); - return 0; -#endif -} - -#endif // !_STATIC_LINKED || _SHARED_LIB \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + +#if !defined(_STATIC_LINKED) || defined(_SHARED_LIB) + +#include +#include "commonmacros.h" +#include "basetypes.h" +#include "sentence.h" +#include "utlbuffer.h" +#include +#include "vector.h" +#include "mathlib.h" +#include +#include "checksum_crc.h" +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +//----------------------------------------------------------------------------- +// Purpose: converts an english string to unicode +//----------------------------------------------------------------------------- +int ConvertANSIToUnicode(const char *ansi, wchar_t *unicode, int unicodeBufferSize); + +#if PHONEME_EDITOR +void CEmphasisSample::SetSelected( bool isSelected ) +{ + selected = isSelected; +} +void CPhonemeTag::SetSelected( bool isSelected ) +{ + m_bSelected = isSelected; +} +bool CPhonemeTag::GetSelected() const +{ + return m_bSelected; +} +void CPhonemeTag::SetStartAndEndBytes( unsigned int start, unsigned int end ) +{ + m_uiStartByte = start; + m_uiEndByte = end; +} +unsigned int CPhonemeTag::GetStartByte() const +{ + return m_uiStartByte; +} +unsigned int CPhonemeTag::GetEndByte() const +{ + return m_uiEndByte; +} +void CWordTag::SetSelected( bool isSelected ) +{ + m_bSelected = isSelected; +} +bool CWordTag::GetSelected() const +{ + return m_bSelected; +} +void CWordTag::SetStartAndEndBytes( unsigned int start, unsigned int end ) +{ + m_uiStartByte = start; + m_uiEndByte = end; +} +unsigned int CWordTag::GetStartByte() const +{ + return m_uiStartByte; +} +unsigned int CWordTag::GetEndByte() const +{ + return m_uiEndByte; +} +#else +// xbox doesn't store this data +void CEmphasisSample::SetSelected( bool isSelected ) {} +void CPhonemeTag::SetSelected( bool isSelected ) {} +bool CPhonemeTag::GetSelected() const { return false; } +void CPhonemeTag::SetStartAndEndBytes( unsigned int start, unsigned int end ) {} +unsigned int CPhonemeTag::GetStartByte() const { return 0; } +unsigned int CPhonemeTag::GetEndByte() const { return 0; } +void CWordTag::SetSelected( bool isSelected ) {} +bool CWordTag::GetSelected() const { return false; } +void CWordTag::SetStartAndEndBytes( unsigned int start, unsigned int end ) {} +unsigned int CWordTag::GetStartByte() const { return 0; } +unsigned int CWordTag::GetEndByte() const { return 0; } +#endif + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CWordTag::CWordTag( void ) +{ + m_pszWord = NULL; + + SetStartAndEndBytes( 0, 0 ); + + m_flStartTime = 0.0f; + m_flEndTime = 0.0f; + + SetSelected( false ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : from - +//----------------------------------------------------------------------------- +CWordTag::CWordTag( const CWordTag& from ) +{ + m_pszWord = NULL; + SetWord( from.m_pszWord ); + + SetStartAndEndBytes( from.GetStartByte(), from.GetEndByte() ); + + m_flStartTime = from.m_flStartTime; + m_flEndTime = from.m_flEndTime; + + SetSelected( from.GetSelected() ); + + for ( int p = 0; p < from.m_Phonemes.Size(); p++ ) + { + CPhonemeTag *newPhoneme = new CPhonemeTag( *from.m_Phonemes[ p ] ); + m_Phonemes.AddToTail( newPhoneme ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *word - +//----------------------------------------------------------------------------- +CWordTag::CWordTag( const char *word ) +{ + SetStartAndEndBytes( 0, 0 ); + + m_flStartTime = 0.0f; + m_flEndTime = 0.0f; + + m_pszWord = NULL; + + SetSelected( false ); + + SetWord( word ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CWordTag::~CWordTag( void ) +{ + delete[] m_pszWord; + + while ( m_Phonemes.Size() > 0 ) + { + delete m_Phonemes[ 0 ]; + m_Phonemes.Remove( 0 ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *tag - +// Output : int +//----------------------------------------------------------------------------- +int CWordTag::IndexOfPhoneme( CPhonemeTag *tag ) +{ + for ( int i = 0 ; i < m_Phonemes.Size(); i++ ) + { + CPhonemeTag *p = m_Phonemes[ i ]; + if ( p == tag ) + return i; + } + return -1; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *word - +//----------------------------------------------------------------------------- +void CWordTag::SetWord( const char *word ) +{ + delete[] m_pszWord; + m_pszWord = NULL; + if ( !word || !word[ 0 ] ) + return; + + int len = strlen( word ) + 1; + m_pszWord = new char[ len ]; + Assert( m_pszWord ); + Q_strncpy( m_pszWord, word, len ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : const char +//----------------------------------------------------------------------------- +const char *CWordTag::GetWord() const +{ + return m_pszWord ? m_pszWord : ""; +} + + +unsigned int CWordTag::ComputeDataCheckSum() +{ + int i; + int c; + CRC32_t crc; + CRC32_Init( &crc ); + + // Checksum the text + if ( m_pszWord != NULL ) + { + CRC32_ProcessBuffer( &crc, m_pszWord, Q_strlen( m_pszWord ) ); + } + // Checksum phonemes + c = m_Phonemes.Count(); + for ( i = 0; i < c; ++i ) + { + CPhonemeTag *phoneme = m_Phonemes[ i ]; + unsigned int phonemeCheckSum = phoneme->ComputeDataCheckSum(); + CRC32_ProcessBuffer( &crc, &phonemeCheckSum, sizeof( unsigned int ) ); + } + // Checksum timestamps + CRC32_ProcessBuffer( &crc, &m_flStartTime, sizeof( float ) ); + CRC32_ProcessBuffer( &crc, &m_flEndTime, sizeof( float ) ); + + CRC32_Final( &crc ); + + return ( unsigned int )crc; +} + +CBasePhonemeTag::CBasePhonemeTag() +{ + m_flStartTime = 0; + m_flEndTime = 0; + + m_nPhonemeCode = 0; +} + +CBasePhonemeTag::CBasePhonemeTag( const CBasePhonemeTag& from ) +{ + memcpy( this, &from, sizeof(*this) ); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CPhonemeTag::CPhonemeTag( void ) +{ + m_szPhoneme = NULL; + + SetStartAndEndBytes( 0, 0 ); + + SetSelected( false ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : from - +//----------------------------------------------------------------------------- +CPhonemeTag::CPhonemeTag( const CPhonemeTag& from ) : + BaseClass( from ) +{ + SetStartAndEndBytes( from.GetStartByte(), from.GetEndByte() ); + + SetSelected( from.GetSelected() ); + + m_szPhoneme = NULL; + SetTag( from.GetTag() ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *phoneme - +//----------------------------------------------------------------------------- +CPhonemeTag::CPhonemeTag( const char *phoneme ) +{ + SetStartAndEndBytes( 0, 0 ); + + SetStartTime( 0.0f ); + SetEndTime( 0.0f ); + + SetSelected( false ); + + SetPhonemeCode( 0 ); + + m_szPhoneme = NULL; + SetTag( phoneme ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CPhonemeTag::~CPhonemeTag( void ) +{ + delete[] m_szPhoneme; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *phoneme - +//----------------------------------------------------------------------------- +void CPhonemeTag::SetTag( const char *phoneme ) +{ + delete m_szPhoneme; + m_szPhoneme = NULL; + if ( !phoneme || !phoneme [ 0 ] ) + return; + + int len = Q_strlen( phoneme ) + 1; + m_szPhoneme = new char[ len ]; + Assert( m_szPhoneme ); + Q_strncpy( m_szPhoneme, phoneme, len ); +} + +char const *CPhonemeTag::GetTag() const +{ + return m_szPhoneme ? m_szPhoneme : ""; +} + + +unsigned int CPhonemeTag::ComputeDataCheckSum() +{ + CRC32_t crc; + CRC32_Init( &crc ); + + // Checksum the text + CRC32_ProcessBuffer( &crc, m_szPhoneme, Q_strlen( m_szPhoneme ) ); + int phonemeCode = GetPhonemeCode(); + CRC32_ProcessBuffer( &crc, &phonemeCode, sizeof( int ) ); + + // Checksum timestamps + float startTime = GetStartTime(); + float endTime = GetEndTime(); + CRC32_ProcessBuffer( &crc, &startTime, sizeof( float ) ); + CRC32_ProcessBuffer( &crc, &endTime, sizeof( float ) ); + + CRC32_Final( &crc ); + + return ( unsigned int )crc; +} + +//----------------------------------------------------------------------------- +// Purpose: Simple language to string and string to language lookup dictionary +//----------------------------------------------------------------------------- +#pragma pack(1) + +struct CCLanguage +{ + int type; + char const *name; + unsigned char r, g, b; // For faceposer, indicator color for this language +}; + +static CCLanguage g_CCLanguageLookup[] = +{ + { CC_ENGLISH, "english", 0, 0, 0 }, + { CC_FRENCH, "french", 150, 0, 0 }, + { CC_GERMAN, "german", 0, 150, 0 }, + { CC_ITALIAN, "italian", 0, 150, 150 }, + { CC_KOREAN, "korean", 150, 0, 150 }, + { CC_SCHINESE, "schinese", 150, 0, 150 }, + { CC_SPANISH, "spanish", 0, 0, 150 }, + { CC_TCHINESE, "tchinese", 150, 0, 150 }, + { CC_JAPANESE, "japanese", 250, 150, 0 }, + { CC_RUSSIAN, "russian", 0, 250, 150 }, + { CC_THAI, "thai", 0 , 150, 250 }, + { CC_PORTUGUESE,"portuguese", 0 , 0, 150 }, +}; + +#pragma pack() + +void CSentence::ColorForLanguage( int language, unsigned char& r, unsigned char& g, unsigned char& b ) +{ + r = g = b = 0; + + if ( language < 0 || language >= CC_NUM_LANGUAGES ) + { + return; + } + + r = g_CCLanguageLookup[ language ].r; + g = g_CCLanguageLookup[ language ].g; + b = g_CCLanguageLookup[ language ].b; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : language - +// Output : char const +//----------------------------------------------------------------------------- +char const *CSentence::NameForLanguage( int language ) +{ + if ( language < 0 || language >= CC_NUM_LANGUAGES ) + return "unknown_language"; + + CCLanguage *entry = &g_CCLanguageLookup[ language ]; + Assert( entry->type == language ); + return entry->name; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *name - +// Output : int +//----------------------------------------------------------------------------- +int CSentence::LanguageForName( char const *name ) +{ + int l; + for ( l = 0; l < CC_NUM_LANGUAGES; l++ ) + { + CCLanguage *entry = &g_CCLanguageLookup[ l ]; + Assert( entry->type == l ); + if ( !stricmp( entry->name, name ) ) + return l; + } + return -1; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CSentence::CSentence( void ) +{ +#if PHONEME_EDITOR + m_nResetWordBase = 0; + m_szText = 0; + m_uCheckSum = 0; +#endif + m_bShouldVoiceDuck = false; + m_bStoreCheckSum = false; + m_bIsValid = false; + m_bIsCached = false; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CSentence::~CSentence( void ) +{ + Reset(); +#if PHONEME_EDITOR + delete[] m_szText; +#endif +} + + +void CSentence::ParsePlaintext( CUtlBuffer& buf ) +{ + char token[ 4096 ]; + char text[ 4096 ]; + text[ 0 ] = 0; + while ( 1 ) + { + buf.GetString( token ); + if ( !stricmp( token, "}" ) ) + break; + + Q_strncat( text, token, sizeof( text ), COPY_ALL_CHARACTERS ); + Q_strncat( text, " ", sizeof( text ), COPY_ALL_CHARACTERS ); + } + + SetText( text ); +} + +void CSentence::ParseWords( CUtlBuffer& buf ) +{ + char token[ 4096 ]; + char word[ 256 ]; + float start, end; + + while ( 1 ) + { + buf.GetString( token ); + if ( !stricmp( token, "}" ) ) + break; + + if ( stricmp( token, "WORD" ) ) + break; + + buf.GetString( token ); + Q_strncpy( word, token, sizeof( word ) ); + + buf.GetString( token ); + start = atof( token ); + buf.GetString( token ); + end = atof( token ); + + CWordTag *wt = new CWordTag( word ); + assert( wt ); + wt->m_flStartTime = start; + wt->m_flEndTime = end; + + AddWordTag( wt ); + + buf.GetString( token ); + if ( stricmp( token, "{" ) ) + break; + + while ( 1 ) + { + buf.GetString( token ); + if ( !stricmp( token, "}" ) ) + break; + + // Parse phoneme + int code; + char phonemename[ 256 ]; + float start, end; + float volume; + + code = atoi( token ); + + buf.GetString( token ); + Q_strncpy( phonemename, token, sizeof( phonemename ) ); + buf.GetString( token ); + start = atof( token ); + buf.GetString( token ); + end = atof( token ); + buf.GetString( token ); + volume = atof( token ); + + CPhonemeTag *pt = new CPhonemeTag(); + assert( pt ); + pt->SetPhonemeCode( code ); + pt->SetTag( phonemename ); + pt->SetStartTime( start ); + pt->SetEndTime( end ); + + AddPhonemeTag( wt, pt ); + } + } +} + +void CSentence::ParseEmphasis( CUtlBuffer& buf ) +{ + char token[ 4096 ]; + while ( 1 ) + { + buf.GetString( token ); + if ( !stricmp( token, "}" ) ) + break; + + char t[ 256 ]; + Q_strncpy( t, token, sizeof( t ) ); + buf.GetString( token ); + + char value[ 256 ]; + Q_strncpy( value, token, sizeof( value ) ); + + CEmphasisSample sample; + sample.SetSelected( false ); + sample.time = atof( t ); + sample.value = atof( value ); + + + m_EmphasisSamples.AddToTail( sample ); + + } +} + +// This is obsolete, so it doesn't do anything with the data which is parsed. +void CSentence::ParseCloseCaption( CUtlBuffer& buf ) +{ + char token[ 4096 ]; + while ( 1 ) + { + // Format is + // language_name + // { + // PHRASE char streamlength "streambytes" starttime endtime + // PHRASE unicode streamlength "streambytes" starttime endtime + // } + buf.GetString( token ); + if ( !stricmp( token, "}" ) ) + break; + + buf.GetString( token ); + if ( stricmp( token, "{" ) ) + break; + + buf.GetString( token ); + while ( 1 ) + { + + if ( !stricmp( token, "}" ) ) + break; + + if ( stricmp( token, "PHRASE" ) ) + break; + + char cc_type[32]; + char cc_stream[ 4096 ]; + int cc_length; + + memset( cc_stream, 0, sizeof( cc_stream ) ); + + buf.GetString( token ); + Q_strncpy( cc_type, token, sizeof( cc_type ) ); + + bool unicode = false; + if ( !stricmp( cc_type, "unicode" ) ) + { + unicode = true; + } + else if ( stricmp( cc_type, "char" ) ) + { + Assert( 0 ); + } + + buf.GetString( token ); + cc_length = atoi( token ); + Assert( cc_length >= 0 && cc_length < sizeof( cc_stream ) ); + // Skip space + buf.GetChar(); + buf.Get( cc_stream, cc_length ); + cc_stream[ cc_length ] = 0; + + // Skip space + buf.GetChar(); + buf.GetString( token ); + buf.GetString( token ); + + + buf.GetString( token ); + } + + } +} + +void CSentence::ParseOptions( CUtlBuffer& buf ) +{ + char token[ 4096 ]; + while ( 1 ) + { + buf.GetString( token ); + if ( !stricmp( token, "}" ) ) + break; + + if ( Q_strlen( token ) == 0 ) + break; + + char key[ 256 ]; + Q_strncpy( key, token, sizeof( key ) ); + char value[ 256 ]; + buf.GetString( token ); + Q_strncpy( value, token, sizeof( value ) ); + + if ( !strcmpi( key, "voice_duck" ) ) + { + SetVoiceDuck( atoi(value) ? true : false ); + } + else if ( !strcmpi( key, "checksum" ) ) + { + SetDataCheckSum( (unsigned int)atoi( value ) ); + } + } +} + + +//----------------------------------------------------------------------------- +// Purpose: VERSION 1.0 parser, need to implement new ones if +// file format changes!!! +// Input : buf - +//----------------------------------------------------------------------------- +void CSentence::ParseDataVersionOnePointZero( CUtlBuffer& buf ) +{ + char token[ 4096 ]; + + while ( 1 ) + { + buf.GetString( token ); + if ( strlen( token ) <= 0 ) + break; + + char section[ 256 ]; + Q_strncpy( section, token, sizeof( section ) ); + + buf.GetString( token ); + if ( stricmp( token, "{" ) ) + break; + + if ( !stricmp( section, "PLAINTEXT" ) ) + { + ParsePlaintext( buf ); + } + else if ( !stricmp( section, "WORDS" ) ) + { + ParseWords( buf ); + } + else if ( !stricmp( section, "EMPHASIS" ) ) + { + ParseEmphasis( buf ); + } + else if ( !stricmp( section, "CLOSECAPTION" ) ) + { + // NOTE: CLOSECAPTION IS NO LONGER VALID + // This just skips the section of data. + ParseCloseCaption( buf ); + } + else if ( !stricmp( section, "OPTIONS" ) ) + { + ParseOptions( buf ); + } + } +} + +#define CACHED_SENTENCE_VERSION 1 + +// This is a compressed save of just the data needed to drive phonemes in the engine (no word / sentence text, etc ) +//----------------------------------------------------------------------------- +// Purpose: +// Input : buf - +//----------------------------------------------------------------------------- +void CSentence::CacheSaveToBuffer( CUtlBuffer& buf ) +{ + Assert( !buf.IsText() ); + Assert( m_bIsCached ); + + buf.PutChar( CACHED_SENTENCE_VERSION ); + + int i; + unsigned short pcount = GetRuntimePhonemeCount(); + + buf.PutShort( pcount ); + + for ( i = 0; i < pcount; ++i ) + { + const CBasePhonemeTag *phoneme = GetRuntimePhoneme( i ); + Assert( phoneme ); + + buf.PutShort( phoneme->GetPhonemeCode() ); + buf.PutFloat( phoneme->GetStartTime() ); + buf.PutFloat( phoneme->GetEndTime() ); + } + + // Now save out emphasis samples + int c = m_EmphasisSamples.Count(); + Assert( c <= 32767 ); + buf.PutShort( c ); + + for ( i = 0; i < c; i++ ) + { + CEmphasisSample *sample = &m_EmphasisSamples[ i ]; + Assert( sample ); + + buf.PutFloat( sample->time ); + short scaledValue = static_cast(clamp( sample->value * 32767, 0, 32767 )); + + buf.PutShort( scaledValue ); + } + + // And voice duck + buf.PutChar( GetVoiceDuck() ? 1 : 0 ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : buf - +//----------------------------------------------------------------------------- +void CSentence::CacheRestoreFromBuffer( CUtlBuffer& buf ) +{ + Assert( !buf.IsText() ); + + Reset(); + + m_bIsCached = true; + + int version = buf.GetChar(); + if ( version != CACHED_SENTENCE_VERSION ) + { + // Uh oh, version changed... + m_bIsValid = false; + return; + } + + unsigned short pcount = (unsigned short)buf.GetShort(); + + CPhonemeTag pt; + int i; + + for ( i = 0; i < pcount; ++i ) + { + unsigned short code = buf.GetShort(); + float st = buf.GetFloat(); + float et = buf.GetFloat(); + + pt.SetPhonemeCode( code ); + pt.SetStartTime( st ); + pt.SetEndTime( et ); + + AddRuntimePhoneme( &pt ); + } + + // Now read emphasis samples + int c = buf.GetShort(); + + for ( i = 0; i < c; i++ ) + { + CEmphasisSample sample; + sample.SetSelected( false ); + + sample.time = buf.GetFloat(); + sample.value = (float)buf.GetShort() / 32767.0f; + + m_EmphasisSamples.AddToTail( sample ); + } + + // And voice duck + SetVoiceDuck( buf.GetChar() == 0 ? false : true ); + m_bIsValid = true; +} + +int CSentence::GetRuntimePhonemeCount() const +{ + return m_RunTimePhonemes.Count(); +} + +const CBasePhonemeTag *CSentence::GetRuntimePhoneme( int i ) const +{ + Assert( m_bIsCached ); + return m_RunTimePhonemes[ i ]; +} + +void CSentence::ClearRuntimePhonemes() +{ + while ( m_RunTimePhonemes.Count() > 0 ) + { + CBasePhonemeTag *tag = m_RunTimePhonemes[ 0 ]; + delete tag; + m_RunTimePhonemes.Remove( 0 ); + } +} + +void CSentence::AddRuntimePhoneme( const CPhonemeTag *src ) +{ + Assert( m_bIsCached ); + + CBasePhonemeTag *tag = new CBasePhonemeTag(); + *tag = *src; + + m_RunTimePhonemes.AddToTail( tag ); +} + +void CSentence::MakeRuntimeOnly() +{ + m_bIsCached = true; +#if PHONEME_EDITOR + delete m_szText; + m_szText = NULL; + + int c = m_Words.Count(); + for ( int i = 0; i < c; ++i ) + { + CWordTag *word = m_Words[ i ]; + Assert( word ); + int pcount = word->m_Phonemes.Count(); + for ( int j = 0; j < pcount; ++j ) + { + CPhonemeTag *phoneme = word->m_Phonemes[ j ]; + assert( phoneme ); + + AddRuntimePhoneme( phoneme ); + } + } + + // Remove all existing words + while ( m_Words.Count() > 0 ) + { + CWordTag *word = m_Words[ 0 ]; + delete word; + m_Words.Remove( 0 ); + } +#endif + m_bIsValid = true; +} + + +void CSentence::SaveToBuffer( CUtlBuffer& buf ) +{ +#if PHONEME_EDITOR + Assert( !m_bIsCached ); + + int i, j; + + buf.Printf( "VERSION 1.0\n" ); + + buf.Printf( "PLAINTEXT\n" ); + buf.Printf( "{\n" ); + buf.Printf( "%s\n", GetText() ); + buf.Printf( "}\n" ); + buf.Printf( "WORDS\n" ); + buf.Printf( "{\n" ); + for ( i = 0; i < m_Words.Size(); i++ ) + { + CWordTag *word = m_Words[ i ]; + Assert( word ); + + buf.Printf( "WORD %s %.3f %.3f\n", + word->GetWord(), + word->m_flStartTime, + word->m_flEndTime ); + + buf.Printf( "{\n" ); + for ( j = 0; j < word->m_Phonemes.Size(); j++ ) + { + CPhonemeTag *phoneme = word->m_Phonemes[ j ]; + Assert( phoneme ); + + buf.Printf( "%i %s %.3f %.3f 1\n", + phoneme->GetPhonemeCode(), + phoneme->GetTag(), + phoneme->GetStartTime(), + phoneme->GetEndTime() ); + } + + buf.Printf( "}\n" ); + } + buf.Printf( "}\n" ); + buf.Printf( "EMPHASIS\n" ); + buf.Printf( "{\n" ); + int c = m_EmphasisSamples.Count(); + for ( i = 0; i < c; i++ ) + { + CEmphasisSample *sample = &m_EmphasisSamples[ i ]; + Assert( sample ); + + buf.Printf( "%f %f\n", sample->time, sample->value ); + } + + buf.Printf( "}\n" ); + buf.Printf( "OPTIONS\n" ); + buf.Printf( "{\n" ); + buf.Printf( "voice_duck %d\n", GetVoiceDuck() ? 1 : 0 ); + if ( m_bStoreCheckSum ) + { + buf.Printf( "checksum %d\n", m_uCheckSum ); + } + buf.Printf( "}\n" ); +#else + Assert( 0 ); +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *data - +// size - +//----------------------------------------------------------------------------- +void CSentence::InitFromDataChunk( void *data, int size ) +{ + CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER ); + buf.EnsureCapacity( size ); + buf.Put( data, size ); + buf.SeekPut( CUtlBuffer::SEEK_HEAD, size ); + + InitFromBuffer( buf ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : buf - +//----------------------------------------------------------------------------- +void CSentence::InitFromBuffer( CUtlBuffer& buf ) +{ + Assert( buf.IsText() ); + + Reset(); + + char token[ 4096 ]; + buf.GetString( token ); + + if ( stricmp( token, "VERSION" ) ) + return; + + buf.GetString( token ); + if ( atof( token ) == 1.0f ) + { + ParseDataVersionOnePointZero( buf ); + m_bIsValid = true; + } + else + { + assert( 0 ); + return; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : int +//----------------------------------------------------------------------------- +int CSentence::GetWordBase( void ) +{ +#if PHONEME_EDITOR + return m_nResetWordBase; +#else + Assert( 0 ); + return 0; +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CSentence::ResetToBase( void ) +{ +#if PHONEME_EDITOR + // Delete everything after m_nResetWordBase + while ( m_Words.Size() > m_nResetWordBase ) + { + delete m_Words[ m_Words.Size() - 1 ]; + m_Words.Remove( m_Words.Size() - 1 ); + } +#endif + ClearRuntimePhonemes(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CSentence::MarkNewPhraseBase( void ) +{ +#if PHONEME_EDITOR + m_nResetWordBase = max( m_Words.Size(), 0 ); +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CSentence::Reset( void ) +{ +#if PHONEME_EDITOR + m_nResetWordBase = 0; + + while ( m_Words.Size() > 0 ) + { + delete m_Words[ 0 ]; + m_Words.Remove( 0 ); + } +#endif + m_EmphasisSamples.RemoveAll(); + + ClearRuntimePhonemes(); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *tag - +//----------------------------------------------------------------------------- +void CSentence::AddPhonemeTag( CWordTag *word, CPhonemeTag *tag ) +{ + word->m_Phonemes.AddToTail( tag ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *tag - +//----------------------------------------------------------------------------- +void CSentence::AddWordTag( CWordTag *tag ) +{ +#if PHONEME_EDITOR + m_Words.AddToTail( tag ); +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : int +//----------------------------------------------------------------------------- +int CSentence::CountPhonemes( void ) +{ + int c = 0; +#if PHONEME_EDITOR + for( int i = 0; i < m_Words.Size(); i++ ) + { + CWordTag *word = m_Words[ i ]; + c += word->m_Phonemes.Size(); + } +#endif + return c; +} + +//----------------------------------------------------------------------------- +// Purpose: // For legacy loading, try to find a word that contains the time +// Input : time - +// Output : CWordTag +//----------------------------------------------------------------------------- +CWordTag *CSentence::EstimateBestWord( float time ) +{ +#if PHONEME_EDITOR + CWordTag *bestWord = NULL; + + for( int i = 0; i < m_Words.Size(); i++ ) + { + CWordTag *word = m_Words[ i ]; + if ( !word ) + continue; + + if ( word->m_flStartTime <= time && word->m_flEndTime >= time ) + return word; + + if ( time < word->m_flStartTime ) + { + bestWord = word; + } + + if ( time > word->m_flEndTime && bestWord ) + return bestWord; + } + + // return best word if we found one + if ( bestWord ) + { + return bestWord; + } + + // Return last word + if ( m_Words.Size() >= 1 ) + { + return m_Words[ m_Words.Size() - 1 ]; + } +#endif + // Oh well + return NULL; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *phoneme - +// Output : CWordTag +//----------------------------------------------------------------------------- +CWordTag *CSentence::GetWordForPhoneme( CPhonemeTag *phoneme ) +{ +#if PHONEME_EDITOR + for( int i = 0; i < m_Words.Size(); i++ ) + { + CWordTag *word = m_Words[ i ]; + if ( !word ) + continue; + + for ( int j = 0 ; j < word->m_Phonemes.Size() ; j++ ) + { + CPhonemeTag *p = word->m_Phonemes[ j ]; + if ( p == phoneme ) + { + return word; + } + } + + } +#endif + return NULL; +} + +//----------------------------------------------------------------------------- +// Purpose: Assignment operator +// Input : src - +// Output : CSentence& +//----------------------------------------------------------------------------- +CSentence& CSentence::operator=( const CSentence& src ) +{ + int i; + + // Clear current stuff + Reset(); + + int c; + +#if PHONEME_EDITOR + // Copy everything + for ( i = 0 ; i < src.m_Words.Size(); i++ ) + { + CWordTag *word = src.m_Words[ i ]; + + CWordTag *newWord = new CWordTag( *word ); + + AddWordTag( newWord ); + } + + SetText( src.GetText() ); + m_nResetWordBase = src.m_nResetWordBase; + + c = src.m_EmphasisSamples.Size(); + for ( i = 0; i < c; i++ ) + { + CEmphasisSample s = src.m_EmphasisSamples[ i ]; + m_EmphasisSamples.AddToTail( s ); + } +#endif + + m_bIsCached = src.m_bIsCached; + + c = src.GetRuntimePhonemeCount(); + for ( i = 0; i < c; i++ ) + { + Assert( m_bIsCached ); + + const CBasePhonemeTag *tag = src.GetRuntimePhoneme( i ); + CPhonemeTag full; + ((CBasePhonemeTag &)(full)) = *tag; + + AddRuntimePhoneme( &full ); + } + + m_bShouldVoiceDuck = src.m_bShouldVoiceDuck; +#if PHONEME_EDITOR + m_bStoreCheckSum = src.m_bStoreCheckSum; + m_uCheckSum = src.m_uCheckSum; +#endif + m_bIsValid = src.m_bIsValid; + + return (*this); +} + +void CSentence::Append( float starttime, const CSentence& src ) +{ +#if PHONEME_EDITOR + int i; + // Combine + for ( i = 0 ; i < src.m_Words.Size(); i++ ) + { + CWordTag *word = src.m_Words[ i ]; + + CWordTag *newWord = new CWordTag( *word ); + + newWord->m_flStartTime += starttime; + newWord->m_flEndTime += starttime; + + // Offset times + int c = newWord->m_Phonemes.Count(); + for ( int i = 0; i < c; ++i ) + { + CPhonemeTag *tag = newWord->m_Phonemes[ i ]; + tag->AddStartTime( starttime ); + tag->AddEndTime( starttime ); + } + + AddWordTag( newWord ); + } + + if ( src.GetText()[ 0 ] ) + { + char fulltext[ 4096 ]; + if ( GetText()[ 0 ] ) + { + Q_snprintf( fulltext, sizeof( fulltext ), "%s %s", GetText(), src.GetText() ); + } + else + { + Q_strncpy( fulltext, src.GetText(), sizeof( fulltext ) ); + } + SetText( fulltext ); + } + + int c = src.m_EmphasisSamples.Size(); + for ( i = 0; i < c; i++ ) + { + CEmphasisSample s = src.m_EmphasisSamples[ i ]; + + s.time += starttime; + + m_EmphasisSamples.AddToTail( s ); + } + + // Or in voice duck settings + m_bShouldVoiceDuck |= src.m_bShouldVoiceDuck; +#else + Assert( 0 ); +#endif +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *text - +//----------------------------------------------------------------------------- +void CSentence::SetText( const char *text ) +{ +#if PHONEME_EDITOR + delete[] m_szText; + m_szText = NULL; + + if ( !text || !text[ 0 ] ) + { + return; + } + + int len = Q_strlen( text ) + 1; + + m_szText = new char[ len ]; + Assert( m_szText ); + Q_strncpy( m_szText, text, len ); +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : const char +//----------------------------------------------------------------------------- +const char *CSentence::GetText( void ) const +{ +#if PHONEME_EDITOR + return m_szText ? m_szText : ""; +#else + return ""; +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CSentence::SetTextFromWords( void ) +{ +#if PHONEME_EDITOR + char fulltext[ 1024 ]; + fulltext[ 0 ] = 0; + for ( int i = 0 ; i < m_Words.Size(); i++ ) + { + CWordTag *word = m_Words[ i ]; + + Q_strncat( fulltext, word->GetWord(), sizeof( fulltext ), COPY_ALL_CHARACTERS ); + + if ( i != m_Words.Size() ) + { + Q_strncat( fulltext, " ", sizeof( fulltext ), COPY_ALL_CHARACTERS ); + } + } + + SetText( fulltext ); +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CSentence::Resort( void ) +{ + int c = m_EmphasisSamples.Size(); + for ( int i = 0; i < c; i++ ) + { + for ( int j = i + 1; j < c; j++ ) + { + CEmphasisSample src = m_EmphasisSamples[ i ]; + CEmphasisSample dest = m_EmphasisSamples[ j ]; + + if ( src.time > dest.time ) + { + m_EmphasisSamples[ i ] = dest; + m_EmphasisSamples[ j ] = src; + } + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : number - +// Output : CEmphasisSample +//----------------------------------------------------------------------------- +CEmphasisSample *CSentence::GetBoundedSample( int number, float endtime ) +{ + // Search for two samples which span time f + static CEmphasisSample nullstart; + nullstart.time = 0.0f; + nullstart.value = 0.5f; + static CEmphasisSample nullend; + nullend.time = endtime; + nullend.value = 0.5f; + + if ( number < 0 ) + { + return &nullstart; + } + else if ( number >= GetNumSamples() ) + { + return &nullend; + } + + return GetSample( number ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : time - +// type - +// Output : float +//----------------------------------------------------------------------------- +float CSentence::GetIntensity( float time, float endtime ) +{ + float zeroValue = 0.5f; + + int c = GetNumSamples(); + + if ( c <= 0 ) + { + return zeroValue; + } + + int i; + for ( i = -1 ; i < c; i++ ) + { + CEmphasisSample *s = GetBoundedSample( i, endtime ); + CEmphasisSample *n = GetBoundedSample( i + 1, endtime ); + if ( !s || !n ) + continue; + + if ( time >= s->time && time <= n->time ) + { + break; + } + } + + int prev = i - 1; + int start = i; + int end = i + 1; + int next = i + 2; + + prev = max( -1, prev ); + start = max( -1, start ); + end = min( end, GetNumSamples() ); + next = min( next, GetNumSamples() ); + + CEmphasisSample *esPre = GetBoundedSample( prev, endtime ); + CEmphasisSample *esStart = GetBoundedSample( start, endtime ); + CEmphasisSample *esEnd = GetBoundedSample( end, endtime ); + CEmphasisSample *esNext = GetBoundedSample( next, endtime ); + + float dt = esEnd->time - esStart->time; + dt = clamp( dt, 0.01f, 1.0f ); + + Vector vPre( esPre->time, esPre->value, 0 ); + Vector vStart( esStart->time, esStart->value, 0 ); + Vector vEnd( esEnd->time, esEnd->value, 0 ); + Vector vNext( esNext->time, esNext->value, 0 ); + + float f2 = ( time - esStart->time ) / ( dt ); + f2 = clamp( f2, 0.0f, 1.0f ); + + Vector vOut; + Catmull_Rom_Spline( + vPre, + vStart, + vEnd, + vNext, + f2, + vOut ); + + float retval = clamp( vOut.y, 0.0f, 1.0f ); + return retval; +} + +int CSentence::GetNumSamples( void ) +{ + return m_EmphasisSamples.Count(); +} + +CEmphasisSample *CSentence::GetSample( int index ) +{ + if ( index < 0 || index >= GetNumSamples() ) + return NULL; + + return &m_EmphasisSamples[ index ]; +} + +void CSentence::GetEstimatedTimes( float& start, float &end ) +{ +#if PHONEME_EDITOR + float beststart = 100000.0f; + float bestend = -100000.0f; + + int c = m_Words.Count(); + if ( !c ) + { + start = end = 0.0f; + return; + } + + for ( int i = 0; i< c; i++ ) + { + CWordTag *w = m_Words[ i ]; + Assert( w ); + if ( w->m_flStartTime < beststart ) + { + beststart = w->m_flStartTime; + } + if ( w->m_flEndTime > bestend ) + { + bestend = w->m_flEndTime; + } + } + + if ( beststart == 100000.0f ) + { + Assert( 0 ); + beststart = 0.0f; + } + if ( bestend == -100000.0f ) + { + Assert( 0 ); + bestend = 1.0f; + } + start = beststart; + end = bestend; +#endif +} + +void CSentence::SetDataCheckSum( unsigned int chk ) +{ +#if PHONEME_EDITOR + m_bStoreCheckSum = true; + m_uCheckSum = chk; +#endif +} + +unsigned int CSentence::ComputeDataCheckSum() +{ +#if PHONEME_EDITOR + int i; + int c; + CRC32_t crc; + CRC32_Init( &crc ); + + // Checksum the text + CRC32_ProcessBuffer( &crc, GetText(), Q_strlen( GetText() ) ); + // Checsum words and phonemes + c = m_Words.Count(); + for ( i = 0; i < c; ++i ) + { + CWordTag *word = m_Words[ i ]; + unsigned int wordCheckSum = word->ComputeDataCheckSum(); + CRC32_ProcessBuffer( &crc, &wordCheckSum, sizeof( unsigned int ) ); + } + + // Checksum emphasis data + c = m_EmphasisSamples.Count(); + for ( i = 0; i < c; ++i ) + { + CRC32_ProcessBuffer( &crc, &m_EmphasisSamples[ i ].time, sizeof( float ) ); + CRC32_ProcessBuffer( &crc, &m_EmphasisSamples[ i ].value, sizeof( float ) ); + } + + CRC32_Final( &crc ); + + return ( unsigned int )crc; +#else + Assert( 0 ); + return 0; +#endif +} + +unsigned int CSentence::GetDataCheckSum() const +{ +#if PHONEME_EDITOR + Assert( m_bStoreCheckSum ); + Assert( m_uCheckSum != 0 ); + return m_uCheckSum; +#else + Assert( 0 ); + return 0; +#endif +} + +#endif // !_STATIC_LINKED || _SHARED_LIB diff --git a/public/shattersurfacetypes.h b/public/shattersurfacetypes.h index de088318..ffdee42e 100644 --- a/public/shattersurfacetypes.h +++ b/public/shattersurfacetypes.h @@ -1,21 +1,21 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -// -//=============================================================================// -#if !defined ( SHATTERSURFACETYPES_H ) -#define SHATTERSURFACETYPES_H -#ifdef _WIN32 -#pragma once -#endif - -enum ShatterSurface_t -{ - // Note: This much match with the client entity - SHATTERSURFACE_GLASS = 0, - SHATTERSURFACE_TILE = 1, -}; - -#endif \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// +#if !defined ( SHATTERSURFACETYPES_H ) +#define SHATTERSURFACETYPES_H +#ifdef _WIN32 +#pragma once +#endif + +enum ShatterSurface_t +{ + // Note: This much match with the client entity + SHATTERSURFACE_GLASS = 0, + SHATTERSURFACE_TILE = 1, +}; + +#endif diff --git a/public/string_t.h b/public/string_t.h index 22b46971..c0a76aaa 100644 --- a/public/string_t.h +++ b/public/string_t.h @@ -41,7 +41,7 @@ typedef int string_t; //----------------------------------------------------------------------------- -#define IDENT_STRINGS( s1, s2 ) ( *((void **)&(s1)) == *((void **)&(s2)) ) +#define IDENT_STRINGS( s1, s2 ) ( s1 == s2 ) //----------------------------------------------------------------------------- @@ -92,7 +92,7 @@ struct castable_string_t : public string_t // string_t is used in unions, hence, //----------------------------------------------------------------------------- -#define IDENT_STRINGS( s1, s2 ) ( *((void **)&(s1)) == *((void **)&(s2)) ) +#define IDENT_STRINGS( s1, s2 ) ( s1 == s2 ) //----------------------------------------------------------------------------- diff --git a/public/studio.h b/public/studio.h index 1552195b..37d6cf31 100644 --- a/public/studio.h +++ b/public/studio.h @@ -694,15 +694,15 @@ public: } inline void SetDeltaFixed( const Vector vInput ) { - delta[0] = vInput.x * g_VertAnimFixedPointScaleInv; - delta[1] = vInput.y * g_VertAnimFixedPointScaleInv; - delta[2] = vInput.z * g_VertAnimFixedPointScaleInv; + delta[0] = static_cast(vInput.x * g_VertAnimFixedPointScaleInv); + delta[1] = static_cast(vInput.y * g_VertAnimFixedPointScaleInv); + delta[2] = static_cast(vInput.z * g_VertAnimFixedPointScaleInv); } inline void SetNDeltaFixed( const Vector vInputNormal ) { - ndelta[0] = vInputNormal.x * g_VertAnimFixedPointScaleInv; - ndelta[1] = vInputNormal.y * g_VertAnimFixedPointScaleInv; - ndelta[2] = vInputNormal.z * g_VertAnimFixedPointScaleInv; + ndelta[0] = static_cast(vInputNormal.x * g_VertAnimFixedPointScaleInv); + ndelta[1] = static_cast(vInputNormal.y * g_VertAnimFixedPointScaleInv); + ndelta[2] = static_cast(vInputNormal.z * g_VertAnimFixedPointScaleInv); } // Ick...can also force fp16 data into this structure for writing to file in legacy format... diff --git a/public/studio_generic_io.cpp b/public/studio_generic_io.cpp index 1c1f73a9..e7892540 100644 --- a/public/studio_generic_io.cpp +++ b/public/studio_generic_io.cpp @@ -1,156 +1,156 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -//=============================================================================// - -#include - -#include "studio.h" -#include "UtlRBTree.h" - -extern studiohdr_t *FindOrLoadGroupFile( char const *modelname ); - -virtualmodel_t *studiohdr_t::GetVirtualModel( void ) const -{ - if (numincludemodels == 0) - { - return NULL; - } - - virtualmodel_t *pVModel = (virtualmodel_t *)virtualModel; - - if (pVModel == NULL) - { - pVModel = new virtualmodel_t; - - // !!! Set cache handle? Set pointer to local virtual model?? - virtualModel = (void *)pVModel; - - int group = pVModel->m_group.AddToTail( ); - pVModel->m_group[ group ].cache = (void *)this; - pVModel->AppendModels( 0, this ); - } - - return pVModel; -} - - -const studiohdr_t *studiohdr_t::FindModel( void **cache, char const *modelname ) const -{ - studiohdr_t *hdr = (studiohdr_t *)(*cache); - - if (hdr) - { - return hdr; - } - - hdr = FindOrLoadGroupFile( modelname ); - - *cache = (void *)hdr; - - return hdr; -} - -const studiohdr_t *virtualgroup_t::GetStudioHdr( void ) const -{ - return (studiohdr_t *)cache; -} - - -byte *studiohdr_t::GetAnimBlock( int i ) const -{ - byte *hdr = (byte *)animblockModel; - - if (!hdr) - { - hdr = (byte *)FindOrLoadGroupFile( pszAnimBlockName() ); - animblockModel = hdr; - } - - return hdr + pAnimBlock( i )->datastart; -} - -//----------------------------------------------------------------------------- -// Purpose: Builds up a dictionary of autoplay indices by studiohdr_t * -// NOTE: This list never gets freed even if the model gets unloaded, but we're in a tool so we can probably live with that -//----------------------------------------------------------------------------- -struct AutoPlayGeneric_t -{ -public: - - AutoPlayGeneric_t() : - hdr( 0 ) - { - } - - // Implement copy constructor - AutoPlayGeneric_t( const AutoPlayGeneric_t& src ) - { - hdr = src.hdr; - autoplaylist.EnsureCount( src.autoplaylist.Count() ); - autoplaylist.CopyArray( src.autoplaylist.Base(), src.autoplaylist.Count() ); - } - - static bool AutoPlayGenericLessFunc( const AutoPlayGeneric_t& lhs, const AutoPlayGeneric_t& rhs ) - { - return lhs.hdr < rhs.hdr; - } - -public: - // Data - const studiohdr_t *hdr; - CUtlVector< unsigned short > autoplaylist; -}; - -// A global array to track this data -static CUtlRBTree< AutoPlayGeneric_t, int > g_AutoPlayGeneric( 0, 0, AutoPlayGeneric_t::AutoPlayGenericLessFunc ); - -int studiohdr_t::GetAutoplayList( unsigned short **pAutoplayList ) const -{ - virtualmodel_t *pVirtualModel = GetVirtualModel(); - if ( pVirtualModel ) - { - if ( pAutoplayList && pVirtualModel->m_autoplaySequences.Count() ) - { - *pAutoplayList = pVirtualModel->m_autoplaySequences.Base(); - } - return pVirtualModel->m_autoplaySequences.Count(); - } - - AutoPlayGeneric_t *pData = NULL; - - // Search for this studiohdr_t ptr in the global list - AutoPlayGeneric_t search; - search.hdr = this; - int index = g_AutoPlayGeneric.Find( search ); - if ( index == g_AutoPlayGeneric.InvalidIndex() ) - { - // Not there, so add it - index = g_AutoPlayGeneric.Insert( search ); - pData = &g_AutoPlayGeneric[ index ]; - // And compute the autoplay info this one time - int autoPlayCount = CountAutoplaySequences(); - pData->autoplaylist.EnsureCount( autoPlayCount ); - CopyAutoplaySequences( pData->autoplaylist.Base(), autoPlayCount ); - } - else - { - // Refer to existing data - pData = &g_AutoPlayGeneric[ index ]; - } - - // Oops!!! - if ( !pData ) - { - return 0; - } - - // Give back data if it's being requested - if ( pAutoplayList ) - { - *pAutoplayList = pData->autoplaylist.Base(); - } - return pData->autoplaylist.Count(); -} +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include + +#include "studio.h" +#include "utlrbtree.h" + +extern studiohdr_t *FindOrLoadGroupFile( char const *modelname ); + +virtualmodel_t *studiohdr_t::GetVirtualModel( void ) const +{ + if (numincludemodels == 0) + { + return NULL; + } + + virtualmodel_t *pVModel = (virtualmodel_t *)virtualModel; + + if (pVModel == NULL) + { + pVModel = new virtualmodel_t; + + // !!! Set cache handle? Set pointer to local virtual model?? + virtualModel = (void *)pVModel; + + int group = pVModel->m_group.AddToTail( ); + pVModel->m_group[ group ].cache = (void *)this; + pVModel->AppendModels( 0, this ); + } + + return pVModel; +} + + +const studiohdr_t *studiohdr_t::FindModel( void **cache, char const *modelname ) const +{ + studiohdr_t *hdr = (studiohdr_t *)(*cache); + + if (hdr) + { + return hdr; + } + + hdr = FindOrLoadGroupFile( modelname ); + + *cache = (void *)hdr; + + return hdr; +} + +const studiohdr_t *virtualgroup_t::GetStudioHdr( void ) const +{ + return (studiohdr_t *)cache; +} + + +byte *studiohdr_t::GetAnimBlock( int i ) const +{ + byte *hdr = (byte *)animblockModel; + + if (!hdr) + { + hdr = (byte *)FindOrLoadGroupFile( pszAnimBlockName() ); + animblockModel = hdr; + } + + return hdr + pAnimBlock( i )->datastart; +} + +//----------------------------------------------------------------------------- +// Purpose: Builds up a dictionary of autoplay indices by studiohdr_t * +// NOTE: This list never gets freed even if the model gets unloaded, but we're in a tool so we can probably live with that +//----------------------------------------------------------------------------- +struct AutoPlayGeneric_t +{ +public: + + AutoPlayGeneric_t() : + hdr( 0 ) + { + } + + // Implement copy constructor + AutoPlayGeneric_t( const AutoPlayGeneric_t& src ) + { + hdr = src.hdr; + autoplaylist.EnsureCount( src.autoplaylist.Count() ); + autoplaylist.CopyArray( src.autoplaylist.Base(), src.autoplaylist.Count() ); + } + + static bool AutoPlayGenericLessFunc( const AutoPlayGeneric_t& lhs, const AutoPlayGeneric_t& rhs ) + { + return lhs.hdr < rhs.hdr; + } + +public: + // Data + const studiohdr_t *hdr; + CUtlVector< unsigned short > autoplaylist; +}; + +// A global array to track this data +static CUtlRBTree< AutoPlayGeneric_t, int > g_AutoPlayGeneric( 0, 0, AutoPlayGeneric_t::AutoPlayGenericLessFunc ); + +int studiohdr_t::GetAutoplayList( unsigned short **pAutoplayList ) const +{ + virtualmodel_t *pVirtualModel = GetVirtualModel(); + if ( pVirtualModel ) + { + if ( pAutoplayList && pVirtualModel->m_autoplaySequences.Count() ) + { + *pAutoplayList = pVirtualModel->m_autoplaySequences.Base(); + } + return pVirtualModel->m_autoplaySequences.Count(); + } + + AutoPlayGeneric_t *pData = NULL; + + // Search for this studiohdr_t ptr in the global list + AutoPlayGeneric_t search; + search.hdr = this; + int index = g_AutoPlayGeneric.Find( search ); + if ( index == g_AutoPlayGeneric.InvalidIndex() ) + { + // Not there, so add it + index = g_AutoPlayGeneric.Insert( search ); + pData = &g_AutoPlayGeneric[ index ]; + // And compute the autoplay info this one time + int autoPlayCount = CountAutoplaySequences(); + pData->autoplaylist.EnsureCount( autoPlayCount ); + CopyAutoplaySequences( pData->autoplaylist.Base(), autoPlayCount ); + } + else + { + // Refer to existing data + pData = &g_AutoPlayGeneric[ index ]; + } + + // Oops!!! + if ( !pData ) + { + return 0; + } + + // Give back data if it's being requested + if ( pAutoplayList ) + { + *pAutoplayList = pData->autoplaylist.Base(); + } + return pData->autoplaylist.Count(); +} diff --git a/public/tier0/basetypes.h b/public/tier0/basetypes.h index 6d5750cf..a30fc515 100644 --- a/public/tier0/basetypes.h +++ b/public/tier0/basetypes.h @@ -135,7 +135,8 @@ inline unsigned long const& FloatBits( vec_t const& f ) inline vec_t BitsToFloat( unsigned long i ) { - return *reinterpret_cast(&i); + void *bits = &i; + return *reinterpret_cast(bits); } inline bool IsFinite( vec_t f ) diff --git a/public/tier0/commonmacros.h b/public/tier0/commonmacros.h index dc2fc289..3645c5b0 100644 --- a/public/tier0/commonmacros.h +++ b/public/tier0/commonmacros.h @@ -1,46 +1,46 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -// -//=============================================================================// -#ifndef COMMONMACROS_H -#define COMMONMACROS_H - -#ifdef _WIN32 -#pragma once -#endif - - -// ------------------------------------------------------- -// -// commonmacros.h -// -// This should contain ONLY general purpose macros that are -// appropriate for use in engine/launcher/all tools -// -// ------------------------------------------------------- - -// Makes a 4-byte "packed ID" int out of 4 characters -#define MAKEID(d,c,b,a) ( ((int)(a) << 24) | ((int)(b) << 16) | ((int)(c) << 8) | ((int)(d)) ) - -// Compares a string with a 4-byte packed ID constant -#define STRING_MATCHES_ID( p, id ) ( (*((int *)(p)) == (id) ) ? true : false ) -#define ID_TO_STRING( id, p ) ( (p)[3] = (((id)>>24) & 0xFF), (p)[2] = (((id)>>16) & 0xFF), (p)[1] = (((id)>>8) & 0xFF), (p)[0] = (((id)>>0) & 0xFF) ) - -#ifdef ARRAYSIZE -#undef ARRAYSIZE -#endif -#define ARRAYSIZE(p) (sizeof(p)/sizeof(p[0])) - -#define SETBITS(iBitVector, bits) ((iBitVector) |= (bits)) -#define CLEARBITS(iBitVector, bits) ((iBitVector) &= ~(bits)) -#define FBitSet(iBitVector, bit) ((iBitVector) & (bit)) - -inline bool IsPowerOfTwo( int value ) -{ - return (value & ( value - 1 )) == 0; -} - -#endif // COMMONMACROS_H \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// +#ifndef COMMONMACROS_H +#define COMMONMACROS_H + +#ifdef _WIN32 +#pragma once +#endif + + +// ------------------------------------------------------- +// +// commonmacros.h +// +// This should contain ONLY general purpose macros that are +// appropriate for use in engine/launcher/all tools +// +// ------------------------------------------------------- + +// Makes a 4-byte "packed ID" int out of 4 characters +#define MAKEID(d,c,b,a) ( ((int)(a) << 24) | ((int)(b) << 16) | ((int)(c) << 8) | ((int)(d)) ) + +// Compares a string with a 4-byte packed ID constant +#define STRING_MATCHES_ID( p, id ) ( (*((int *)(p)) == (id) ) ? true : false ) +#define ID_TO_STRING( id, p ) ( (p)[3] = (((id)>>24) & 0xFF), (p)[2] = (((id)>>16) & 0xFF), (p)[1] = (((id)>>8) & 0xFF), (p)[0] = (((id)>>0) & 0xFF) ) + +#ifdef ARRAYSIZE +#undef ARRAYSIZE +#endif +#define ARRAYSIZE(p) (sizeof(p)/sizeof(p[0])) + +#define SETBITS(iBitVector, bits) ((iBitVector) |= (bits)) +#define CLEARBITS(iBitVector, bits) ((iBitVector) &= ~(bits)) +#define FBitSet(iBitVector, bit) ((iBitVector) & (bit)) + +inline bool IsPowerOfTwo( int value ) +{ + return (value & ( value - 1 )) == 0; +} + +#endif // COMMONMACROS_H diff --git a/public/tier0/dbg.h b/public/tier0/dbg.h index adc25ffc..f6b698ad 100644 --- a/public/tier0/dbg.h +++ b/public/tier0/dbg.h @@ -458,11 +458,22 @@ public: // Macro to assist in asserting constant invariants during compilation #ifdef _DEBUG -#define COMPILE_TIME_ASSERT( pred ) switch(0){case 0:case pred:;} -#define ASSERT_INVARIANT( pred ) static void UNIQUE_ID() { COMPILE_TIME_ASSERT( pred ) } + #define COMPILE_TIME_ASSERT( pred ) switch(0){case 0:case pred:;} + #ifdef _MSC_VER + #define ASSERT_INVARIANT( pred ) static void UNIQUE_ID() { COMPILE_TIME_ASSERT( pred ) } + #elif defined __GNUC__ + #define ASSERT_INVARIANT( pred ) __attribute__((unused)) static void UNIQUE_ID() { COMPILE_TIME_ASSERT( pred ) } + #endif #else -#define COMPILE_TIME_ASSERT( pred ) -#define ASSERT_INVARIANT( pred ) + #define COMPILE_TIME_ASSERT( pred ) + #define ASSERT_INVARIANT( pred ) +#endif + +// Member function pointer size +#ifdef _MSC_VER + #define MFP_SIZE 4 +#elif __GNUC__ + #define MFP_SIZE 8 #endif #ifdef _DEBUG @@ -527,6 +538,7 @@ private: // #include "tier0/valve_off.h" + class CDbgFmtMsg { public: diff --git a/public/tier0/fasttimer.h b/public/tier0/fasttimer.h index dfa9d7f0..b63d2f52 100644 --- a/public/tier0/fasttimer.h +++ b/public/tier0/fasttimer.h @@ -256,7 +256,7 @@ inline void CCycleCount::Init() inline void CCycleCount::Init( float initTimeMsec ) { if ( g_ClockSpeedMillisecondsMultiplier > 0 ) - m_Int64 = initTimeMsec / g_ClockSpeedMillisecondsMultiplier; + m_Int64 = static_cast(initTimeMsec / g_ClockSpeedMillisecondsMultiplier); else m_Int64 = 0; } @@ -268,7 +268,7 @@ inline void CCycleCount::Init( int64 cycles ) inline void CCycleCount::Sample() { - unsigned long* pSample = (unsigned long *)&m_Int64; + void* pSample = &m_Int64; #ifdef _WIN32 __asm { diff --git a/public/tier0/icommandline.h b/public/tier0/icommandline.h index f0e0bf07..134c640f 100644 --- a/public/tier0/icommandline.h +++ b/public/tier0/icommandline.h @@ -1,56 +1,56 @@ -//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// -// -// Purpose: -// -// $Workfile: $ -// $NoKeywords: $ -//===========================================================================// - -#ifndef TIER0_ICOMMANDLINE_H -#define TIER0_ICOMMANDLINE_H -#ifdef _WIN32 -#pragma once -#endif - -#include "tier0/platform.h" - - -//----------------------------------------------------------------------------- -// Purpose: Interface to engine command line -//----------------------------------------------------------------------------- -abstract_class ICommandLine -{ -public: - virtual void CreateCmdLine( const char *commandline ) = 0; - virtual void CreateCmdLine( int argc, char **argv ) = 0; - virtual const char *GetCmdLine( void ) const = 0; - - // Check whether a particular parameter exists - virtual const char *CheckParm( const char *psz, const char **ppszValue = 0 ) const = 0; - virtual void RemoveParm( const char *parm ) = 0; - virtual void AppendParm( const char *pszParm, const char *pszValues ) = 0; - - // Returns the argument after the one specified, or the default if not found - virtual const char *ParmValue( const char *psz, const char *pDefaultVal = 0 ) const = 0; - virtual int ParmValue( const char *psz, int nDefaultVal ) const = 0; - virtual float ParmValue( const char *psz, float flDefaultVal ) const = 0; - - // Gets at particular parameters - virtual int ParmCount() const = 0; - virtual int FindParm( const char *psz ) const = 0; // Returns 0 if not found. - virtual const char* GetParm( int nIndex ) const = 0; -}; - - -//----------------------------------------------------------------------------- -// Gets a singleton to the commandline interface -// NOTE: The #define trickery here is necessary for backwards compat: -// this interface used to lie in the vstdlib library. -//----------------------------------------------------------------------------- -PLATFORM_INTERFACE ICommandLine *CommandLine_Tier0(); - -#if !defined( VSTDLIB_BACKWARD_COMPAT ) -#define CommandLine CommandLine_Tier0 -#endif - -#endif // TIER0_ICOMMANDLINE_H \ No newline at end of file +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $Workfile: $ +// $NoKeywords: $ +//===========================================================================// + +#ifndef TIER0_ICOMMANDLINE_H +#define TIER0_ICOMMANDLINE_H +#ifdef _WIN32 +#pragma once +#endif + +#include "tier0/platform.h" + + +//----------------------------------------------------------------------------- +// Purpose: Interface to engine command line +//----------------------------------------------------------------------------- +abstract_class ICommandLine +{ +public: + virtual void CreateCmdLine( const char *commandline ) = 0; + virtual void CreateCmdLine( int argc, char **argv ) = 0; + virtual const char *GetCmdLine( void ) const = 0; + + // Check whether a particular parameter exists + virtual const char *CheckParm( const char *psz, const char **ppszValue = 0 ) const = 0; + virtual void RemoveParm( const char *parm ) = 0; + virtual void AppendParm( const char *pszParm, const char *pszValues ) = 0; + + // Returns the argument after the one specified, or the default if not found + virtual const char *ParmValue( const char *psz, const char *pDefaultVal = 0 ) const = 0; + virtual int ParmValue( const char *psz, int nDefaultVal ) const = 0; + virtual float ParmValue( const char *psz, float flDefaultVal ) const = 0; + + // Gets at particular parameters + virtual int ParmCount() const = 0; + virtual int FindParm( const char *psz ) const = 0; // Returns 0 if not found. + virtual const char* GetParm( int nIndex ) const = 0; +}; + + +//----------------------------------------------------------------------------- +// Gets a singleton to the commandline interface +// NOTE: The #define trickery here is necessary for backwards compat: +// this interface used to lie in the vstdlib library. +//----------------------------------------------------------------------------- +PLATFORM_INTERFACE ICommandLine *CommandLine_Tier0(); + +#if !defined( VSTDLIB_BACKWARD_COMPAT ) +#define CommandLine CommandLine_Tier0 +#endif + +#endif // TIER0_ICOMMANDLINE_H diff --git a/public/tier0/l2cache.h b/public/tier0/l2cache.h index 77636599..4ca2dc59 100644 --- a/public/tier0/l2cache.h +++ b/public/tier0/l2cache.h @@ -1,46 +1,46 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -//=============================================================================// -#ifndef CL2CACHE_H -#define CL2CACHE_H -#ifdef _WIN32 -#pragma once -#endif - -class P4Event_BSQ_cache_reference; - -class CL2Cache -{ -public: - - CL2Cache(); - ~CL2Cache(); - - void Start( void ); - void End( void ); - - //------------------------------------------------------------------------- - // GetL2CacheMisses - //------------------------------------------------------------------------- - int GetL2CacheMisses( void ) - { - return m_iL2CacheMissCount; - } - -#ifdef DBGFLAG_VALIDATE - void Validate( CValidator &validator, tchar *pchName ); // Validate our internal structures -#endif // DBGFLAG_VALIDATE - -private: - - int m_nID; - - P4Event_BSQ_cache_reference *m_pL2CacheEvent; - int64 m_i64Start; - int64 m_i64End; - int m_iL2CacheMissCount; -}; - -#endif // CL2CACHE_H \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// +#ifndef CL2CACHE_H +#define CL2CACHE_H +#ifdef _WIN32 +#pragma once +#endif + +class P4Event_BSQ_cache_reference; + +class CL2Cache +{ +public: + + CL2Cache(); + ~CL2Cache(); + + void Start( void ); + void End( void ); + + //------------------------------------------------------------------------- + // GetL2CacheMisses + //------------------------------------------------------------------------- + int GetL2CacheMisses( void ) + { + return m_iL2CacheMissCount; + } + +#ifdef DBGFLAG_VALIDATE + void Validate( CValidator &validator, tchar *pchName ); // Validate our internal structures +#endif // DBGFLAG_VALIDATE + +private: + + int m_nID; + + P4Event_BSQ_cache_reference *m_pL2CacheEvent; + int64 m_i64Start; + int64 m_i64End; + int m_iL2CacheMissCount; +}; + +#endif // CL2CACHE_H diff --git a/public/tier0/memalloc.h b/public/tier0/memalloc.h index c851c111..c28c7674 100644 --- a/public/tier0/memalloc.h +++ b/public/tier0/memalloc.h @@ -299,7 +299,7 @@ struct MemAllocFileLine_t #define MEM_ALLOC_CREDIT_CLASS() #define MEM_ALLOC_CLASSNAME(type) NULL -#endif !STEAM && NO_MALLOC_OVERRIDE +#endif /* !STEAM && NO_MALLOC_OVERRIDE */ //----------------------------------------------------------------------------- diff --git a/public/tier0/memdbgon.h b/public/tier0/memdbgon.h index 897b8338..f8c8e429 100644 --- a/public/tier0/memdbgon.h +++ b/public/tier0/memdbgon.h @@ -11,7 +11,7 @@ // to include this potentially multiple times (since we can deactivate debugging // by including memdbgoff.h) -#if !defined(STEAM) && !defined(NO_MALLOC_OVERRIDE) +#if !defined(STEAM) && !defined(NO_MALLOC_OVERRIDE) && defined(_WIN32) // SPECIAL NOTE #2: This must be the final include in a .cpp or .h file!!! @@ -172,7 +172,7 @@ inline char *MemAlloc_StrDup(const char *pString) if (!pString) return NULL; - int len = strlen(pString) + 1; + size_t len = strlen(pString) + 1; if ((pMemory = (char *)g_pMemAlloc->Alloc(len)) != NULL) { return strcpy( pMemory, pString ); @@ -188,7 +188,7 @@ inline wchar_t *MemAlloc_WcStrDup(const wchar_t *pString) if (!pString) return NULL; - int len = (wcslen(pString) + 1); + size_t len = (wcslen(pString) + 1); if ((pMemory = (wchar_t *)g_pMemAlloc->Alloc(len * sizeof(wchar_t))) != NULL) { return wcscpy( pMemory, pString ); diff --git a/public/tier0/memoverride-vc7.cpp b/public/tier0/memoverride-vc7.cpp index 23e1e883..8a315f2d 100644 --- a/public/tier0/memoverride-vc7.cpp +++ b/public/tier0/memoverride-vc7.cpp @@ -1,578 +1,578 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: Insert this file into all projects using the memory system -// It will cause that project to use the shader memory allocator -// -// $NoKeywords: $ -//=============================================================================// - - -#if !defined(STEAM) && !defined(NO_MALLOC_OVERRIDE) - -#undef PROTECTED_THINGS_ENABLE // allow use of _vsnprintf - -#if defined(_WIN32) && !defined(_XBOX) -#define WIN_32_LEAN_AND_MEAN -#include -#endif - - - -#include "tier0/dbg.h" -#include "tier0/memalloc.h" -#include -#include -#include "memdbgoff.h" - -// Tags this DLL as debug -#if _DEBUG -DLL_EXPORT void BuiltDebug() {} -#endif - -#ifdef _WIN32 -// ARG: crtdbg is necessary for certain definitions below, -// but it also redefines malloc as a macro in release. -// To disable this, we gotta define _DEBUG before including it.. BLEAH! -#define _DEBUG 1 -#include "crtdbg.h" -#ifdef NDEBUG -#undef _DEBUG -#endif - -// Turn this back off in release mode. -#ifdef NDEBUG -#undef _DEBUG -#endif -#elif _LINUX -#define __cdecl -#endif - - -#if defined(_WIN32) && !defined(_XBOX) -const char *MakeModuleFileName() -{ - if ( g_pMemAlloc->IsDebugHeap() ) - { - char *pszModuleName = (char *)HeapAlloc( GetProcessHeap(), 0, MAX_PATH ); // small leak, debug only - - MEMORY_BASIC_INFORMATION mbi; - static int dummy; - VirtualQuery( &dummy, &mbi, sizeof(mbi) ); - - GetModuleFileName( reinterpret_cast(mbi.AllocationBase), pszModuleName, MAX_PATH ); - char *pDot = strrchr( pszModuleName, '.' ); - if ( pDot ) - { - char *pSlash = strrchr( pszModuleName, '\\' ); - if ( pSlash ) - { - pszModuleName = pSlash + 1; - *pDot = 0; - } - } - - return pszModuleName; - } - return NULL; -} - -static void *AllocUnattributed( size_t nSize ) -{ - static const char *pszOwner = MakeModuleFileName(); - - if ( !pszOwner ) - return g_pMemAlloc->Alloc(nSize); - else - return g_pMemAlloc->Alloc(nSize, pszOwner, 0); -} - -static void *ReallocUnattributed( void *pMem, size_t nSize ) -{ - static const char *pszOwner = MakeModuleFileName(); - - if ( !pszOwner ) - return g_pMemAlloc->Realloc(pMem, nSize); - else - return g_pMemAlloc->Realloc(pMem, nSize, pszOwner, 0); -} - -#else -#define MakeModuleFileName() NULL -inline void *AllocUnattributed( size_t nSize ) -{ - return g_pMemAlloc->Alloc(nSize); -} - -inline void *ReallocUnattributed( void *pMem, size_t nSize ) -{ - return g_pMemAlloc->Realloc(pMem, nSize); -} -#endif - -//----------------------------------------------------------------------------- -// Standard functions in the CRT that we're going to override to call our allocator -//----------------------------------------------------------------------------- -#if defined(_WIN32) && !defined(_STATIC_LINKED) -// this magic only works under win32 -// under linux this malloc() overrides the libc malloc() and so we -// end up in a recursion (as g_pMemAlloc->Alloc() calls malloc) -#if _MSC_VER >= 1400 -#define ALLOC_CALL _CRTNOALIAS _CRTRESTRICT -#define FREE_CALL _CRTNOALIAS -#else -#define ALLOC_CALL -#define FREE_CALL -#endif - -extern "C" -{ - -ALLOC_CALL void *malloc( size_t nSize ) -{ - return AllocUnattributed( nSize ); -} - -FREE_CALL void free( void *pMem ) -{ - g_pMemAlloc->Free(pMem); -} - -ALLOC_CALL void *realloc( void *pMem, size_t nSize ) -{ - return ReallocUnattributed( pMem, nSize ); -} - -ALLOC_CALL void *calloc( size_t nCount, size_t nElementSize ) -{ - void *pMem = AllocUnattributed( nElementSize * nCount ); - memset(pMem, 0, nElementSize * nCount); - return pMem; -} - -} // end extern "C" - -//----------------------------------------------------------------------------- -// Non-standard MSVC functions that we're going to override to call our allocator -//----------------------------------------------------------------------------- -extern "C" -{ - -void *_malloc_base( size_t nSize ) -{ - return AllocUnattributed( nSize ); -} - -void _free_base( void *pMem ) -{ - g_pMemAlloc->Free(pMem); -} - -void *_realloc_base( void *pMem, size_t nSize ) -{ - return ReallocUnattributed( pMem, nSize ); -} - -int _heapchk() -{ - return g_pMemAlloc->heapchk(); -} - -int _heapmin() -{ - return 1; -} - -size_t _msize( void *pMem ) -{ - return g_pMemAlloc->GetSize(pMem); -} - -size_t msize( void *pMem ) -{ - return g_pMemAlloc->GetSize(pMem); -} - -void *__cdecl _heap_alloc( size_t nSize ) -{ - return AllocUnattributed( nSize ); -} - -void *__cdecl _nh_malloc( size_t nSize, int ) -{ - return AllocUnattributed( nSize ); -} - -void *__cdecl _expand( void *pMem, size_t nSize ) -{ - Assert( 0 ); - return NULL; -} - -unsigned int _amblksiz = 16; //BYTES_PER_PARA; - -size_t __cdecl _get_sbh_threshold( void ) -{ - return 0; -} - -int __cdecl _set_sbh_threshold( size_t ) -{ - return 0; -} - -int __cdecl _heapadd( void *, size_t ) -{ - return 0; -} - -int __cdecl _heapset( unsigned int ) -{ - return 0; -} - -size_t __cdecl _heapused( size_t *, size_t * ) -{ - return 0; -} - -#ifdef _WIN32 -int __cdecl _heapwalk( _HEAPINFO * ) -{ - return 0; -} -#endif - -} // end extern "C" - - -//----------------------------------------------------------------------------- -// Debugging functions that we're going to override to call our allocator -// NOTE: These have to be here for release + debug builds in case we -// link to a debug static lib!!! -//----------------------------------------------------------------------------- - -extern "C" -{ - -void *malloc_db( size_t nSize, const char *pFileName, int nLine ) -{ - return g_pMemAlloc->Alloc(nSize, pFileName, nLine); -} - -void free_db( void *pMem, const char *pFileName, int nLine ) -{ - g_pMemAlloc->Free(pMem, pFileName, nLine); -} - -void *realloc_db( void *pMem, size_t nSize, const char *pFileName, int nLine ) -{ - return g_pMemAlloc->Realloc(pMem, nSize, pFileName, nLine); -} - -} // end extern "C" - -//----------------------------------------------------------------------------- -// These methods are standard MSVC heap initialization + shutdown methods -//----------------------------------------------------------------------------- -extern "C" -{ - - int __cdecl _heap_init() - { - return g_pMemAlloc != NULL; - } - - void __cdecl _heap_term() - { - } - -} - -#endif - - -//----------------------------------------------------------------------------- -// Prevents us from using an inappropriate new or delete method, -// ensures they are here even when linking against debug or release static libs -//----------------------------------------------------------------------------- -#ifndef NO_MEMOVERRIDE_NEW_DELETE -void *__cdecl operator new( unsigned int nSize ) -{ - return AllocUnattributed( nSize ); -} - -void *__cdecl operator new( unsigned int nSize, int nBlockUse, const char *pFileName, int nLine ) -{ - return g_pMemAlloc->Alloc(nSize, pFileName, nLine); -} - -void __cdecl operator delete( void *pMem ) -{ - g_pMemAlloc->Free( pMem ); -} - -void *__cdecl operator new[] ( unsigned int nSize ) -{ - return AllocUnattributed( nSize ); -} - -void *__cdecl operator new[] ( unsigned int nSize, int nBlockUse, const char *pFileName, int nLine ) -{ - return g_pMemAlloc->Alloc(nSize, pFileName, nLine); -} - -void __cdecl operator delete[] ( void *pMem ) -{ - g_pMemAlloc->Free( pMem ); -} -#endif - - -//----------------------------------------------------------------------------- -// Override some debugging allocation methods in MSVC -// NOTE: These have to be here for release + debug builds in case we -// link to a debug static lib!!! -//----------------------------------------------------------------------------- -#ifndef _STATIC_LINKED -#ifdef _WIN32 - -// This here just hides the internal file names, etc of allocations -// made in the c runtime library -#define CRT_INTERNAL_FILE_NAME "C-runtime internal" - -class CAttibCRT -{ -public: - CAttibCRT(int nBlockUse) : m_nBlockUse(nBlockUse) - { - if (m_nBlockUse == _CRT_BLOCK) - { - g_pMemAlloc->PushAllocDbgInfo(CRT_INTERNAL_FILE_NAME, 0); - } - } - - ~CAttibCRT() - { - if (m_nBlockUse == _CRT_BLOCK) - { - g_pMemAlloc->PopAllocDbgInfo(); - } - } - -private: - int m_nBlockUse; -}; - - -#define AttribIfCrt() CAttibCRT _attrib(nBlockUse) -#elif defined(_LINUX) -#define AttribIfCrt() -#endif // _WIN32 - - -extern "C" -{ - -void *__cdecl _nh_malloc_dbg( size_t nSize, int nFlag, int nBlockUse, - const char *pFileName, int nLine ) -{ - AttribIfCrt(); - return g_pMemAlloc->Alloc(nSize, pFileName, nLine); -} - -void *__cdecl _malloc_dbg( size_t nSize, int nBlockUse, - const char *pFileName, int nLine ) -{ - AttribIfCrt(); - return g_pMemAlloc->Alloc(nSize, pFileName, nLine); -} - -void *__cdecl _calloc_dbg( size_t nNum, size_t nSize, int nBlockUse, - const char *pFileName, int nLine ) -{ - AttribIfCrt(); - void *pMem = g_pMemAlloc->Alloc(nSize * nNum, pFileName, nLine); - memset(pMem, 0, nSize * nNum); - return pMem; -} - -void *__cdecl _realloc_dbg( void *pMem, size_t nNewSize, int nBlockUse, - const char *pFileName, int nLine ) -{ - AttribIfCrt(); - return g_pMemAlloc->Realloc(pMem, nNewSize, pFileName, nLine); -} - -void *__cdecl _expand_dbg( void *pMem, size_t nNewSize, int nBlockUse, - const char *pFileName, int nLine ) -{ - Assert( 0 ); - return NULL; -} - -void __cdecl _free_dbg( void *pMem, int nBlockUse ) -{ - AttribIfCrt(); - g_pMemAlloc->Free(pMem); -} - -size_t __cdecl _msize_dbg( void *pMem, int nBlockUse ) -{ -#ifdef _WIN32 - return _msize(pMem); -#elif _LINUX - Assert( "_msize_dbg unsupported" ); - return 0; -#endif -} - - -} // end extern "C" - - -//----------------------------------------------------------------------------- -// Override some the _CRT debugging allocation methods in MSVC -//----------------------------------------------------------------------------- -#ifdef _WIN32 - -extern "C" -{ - -int _CrtDumpMemoryLeaks(void) -{ - return 0; -} - -_CRT_DUMP_CLIENT _CrtSetDumpClient( _CRT_DUMP_CLIENT dumpClient ) -{ - return NULL; -} - -int _CrtSetDbgFlag( int nNewFlag ) -{ - return g_pMemAlloc->CrtSetDbgFlag( nNewFlag ); -} - -long _crtBreakAlloc; /* Break on this allocation */ -int _crtDbgFlag = _CRTDBG_ALLOC_MEM_DF; - -void __cdecl _CrtSetDbgBlockType( void *pMem, int nBlockUse ) -{ - DebuggerBreak(); -} - -_CRT_ALLOC_HOOK __cdecl _CrtSetAllocHook( _CRT_ALLOC_HOOK pfnNewHook ) -{ - DebuggerBreak(); - return NULL; -} - -long __cdecl _CrtSetBreakAlloc( long lNewBreakAlloc ) -{ - return g_pMemAlloc->CrtSetBreakAlloc( lNewBreakAlloc ); -} - -int __cdecl _CrtIsValidHeapPointer( const void *pMem ) -{ - return g_pMemAlloc->CrtIsValidHeapPointer( pMem ); -} - -int __cdecl _CrtIsValidPointer( const void *pMem, unsigned int size, int access ) -{ - return g_pMemAlloc->CrtIsValidPointer( pMem, size, access ); -} - -int __cdecl _CrtCheckMemory( void ) -{ - // FIXME: Remove this when we re-implement the heap - return g_pMemAlloc->CrtCheckMemory( ); -} - -int __cdecl _CrtIsMemoryBlock( const void *pMem, unsigned int nSize, - long *plRequestNumber, char **ppFileName, int *pnLine ) -{ - DebuggerBreak(); - return 1; -} - -int __cdecl _CrtMemDifference( _CrtMemState *pState, const _CrtMemState * oldState, const _CrtMemState * newState ) -{ - DebuggerBreak(); - return FALSE; -} - -void __cdecl _CrtMemDumpStatistics( const _CrtMemState *pState ) -{ - DebuggerBreak(); -} - -void __cdecl _CrtMemCheckpoint( _CrtMemState *pState ) -{ - // FIXME: Remove this when we re-implement the heap - g_pMemAlloc->CrtMemCheckpoint( pState ); -} - -void __cdecl _CrtMemDumpAllObjectsSince( const _CrtMemState *pState ) -{ - DebuggerBreak(); -} - -void __cdecl _CrtDoForAllClientObjects( void (*pfn)(void *, void *), void * pContext ) -{ - DebuggerBreak(); -} - - -//----------------------------------------------------------------------------- -// Methods in dbgrpt.cpp -//----------------------------------------------------------------------------- -long _crtAssertBusy = -1; - -int __cdecl _CrtSetReportMode( int nReportType, int nReportMode ) -{ - return g_pMemAlloc->CrtSetReportMode( nReportType, nReportMode ); -} - -_HFILE __cdecl _CrtSetReportFile( int nRptType, _HFILE hFile ) -{ - return (_HFILE)g_pMemAlloc->CrtSetReportFile( nRptType, hFile ); -} - -_CRT_REPORT_HOOK __cdecl _CrtSetReportHook( _CRT_REPORT_HOOK pfnNewHook ) -{ - return (_CRT_REPORT_HOOK)g_pMemAlloc->CrtSetReportHook( pfnNewHook ); -} - -int __cdecl _CrtDbgReport( int nRptType, const char * szFile, - int nLine, const char * szModule, const char * szFormat, ... ) -{ - static char output[1024]; - va_list args; - va_start( args, szFormat ); - _vsnprintf( output, sizeof( output )-1, szFormat, args ); - va_end( args ); - - return g_pMemAlloc->CrtDbgReport( nRptType, szFile, nLine, szModule, output ); -} - -int __cdecl _CrtReportBlockType(const void * pUserData) -{ - return 0; -} - - -} // end extern "C" -#endif // _WIN32 - -// Most files include this file, so when it's used it adds an extra .ValveDbg section, -// to help identify debug binaries. -#ifdef _WIN32 - #ifndef NDEBUG // _DEBUG - #pragma data_seg("ValveDBG") - volatile const char* DBG = "*** DEBUG STUB ***"; - #endif -#endif - -#endif - -#endif // !STEAM && !NO_MALLOC_OVERRIDE \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: Insert this file into all projects using the memory system +// It will cause that project to use the shader memory allocator +// +// $NoKeywords: $ +//=============================================================================// + + +#if !defined(STEAM) && !defined(NO_MALLOC_OVERRIDE) + +#undef PROTECTED_THINGS_ENABLE // allow use of _vsnprintf + +#if defined(_WIN32) && !defined(_XBOX) +#define WIN_32_LEAN_AND_MEAN +#include +#endif + + + +#include "tier0/dbg.h" +#include "tier0/memalloc.h" +#include +#include +#include "memdbgoff.h" + +// Tags this DLL as debug +#if _DEBUG +DLL_EXPORT void BuiltDebug() {} +#endif + +#ifdef _WIN32 +// ARG: crtdbg is necessary for certain definitions below, +// but it also redefines malloc as a macro in release. +// To disable this, we gotta define _DEBUG before including it.. BLEAH! +#define _DEBUG 1 +#include "crtdbg.h" +#ifdef NDEBUG +#undef _DEBUG +#endif + +// Turn this back off in release mode. +#ifdef NDEBUG +#undef _DEBUG +#endif +#elif _LINUX +#define __cdecl +#endif + + +#if defined(_WIN32) && !defined(_XBOX) +const char *MakeModuleFileName() +{ + if ( g_pMemAlloc->IsDebugHeap() ) + { + char *pszModuleName = (char *)HeapAlloc( GetProcessHeap(), 0, MAX_PATH ); // small leak, debug only + + MEMORY_BASIC_INFORMATION mbi; + static int dummy; + VirtualQuery( &dummy, &mbi, sizeof(mbi) ); + + GetModuleFileName( reinterpret_cast(mbi.AllocationBase), pszModuleName, MAX_PATH ); + char *pDot = strrchr( pszModuleName, '.' ); + if ( pDot ) + { + char *pSlash = strrchr( pszModuleName, '\\' ); + if ( pSlash ) + { + pszModuleName = pSlash + 1; + *pDot = 0; + } + } + + return pszModuleName; + } + return NULL; +} + +static void *AllocUnattributed( size_t nSize ) +{ + static const char *pszOwner = MakeModuleFileName(); + + if ( !pszOwner ) + return g_pMemAlloc->Alloc(nSize); + else + return g_pMemAlloc->Alloc(nSize, pszOwner, 0); +} + +static void *ReallocUnattributed( void *pMem, size_t nSize ) +{ + static const char *pszOwner = MakeModuleFileName(); + + if ( !pszOwner ) + return g_pMemAlloc->Realloc(pMem, nSize); + else + return g_pMemAlloc->Realloc(pMem, nSize, pszOwner, 0); +} + +#else +#define MakeModuleFileName() NULL +inline void *AllocUnattributed( size_t nSize ) +{ + return g_pMemAlloc->Alloc(nSize); +} + +inline void *ReallocUnattributed( void *pMem, size_t nSize ) +{ + return g_pMemAlloc->Realloc(pMem, nSize); +} +#endif + +//----------------------------------------------------------------------------- +// Standard functions in the CRT that we're going to override to call our allocator +//----------------------------------------------------------------------------- +#if defined(_WIN32) && !defined(_STATIC_LINKED) +// this magic only works under win32 +// under linux this malloc() overrides the libc malloc() and so we +// end up in a recursion (as g_pMemAlloc->Alloc() calls malloc) +#if _MSC_VER >= 1400 +#define ALLOC_CALL _CRTNOALIAS _CRTRESTRICT +#define FREE_CALL _CRTNOALIAS +#else +#define ALLOC_CALL +#define FREE_CALL +#endif + +extern "C" +{ + +ALLOC_CALL void *malloc( size_t nSize ) +{ + return AllocUnattributed( nSize ); +} + +FREE_CALL void free( void *pMem ) +{ + g_pMemAlloc->Free(pMem); +} + +ALLOC_CALL void *realloc( void *pMem, size_t nSize ) +{ + return ReallocUnattributed( pMem, nSize ); +} + +ALLOC_CALL void *calloc( size_t nCount, size_t nElementSize ) +{ + void *pMem = AllocUnattributed( nElementSize * nCount ); + memset(pMem, 0, nElementSize * nCount); + return pMem; +} + +} // end extern "C" + +//----------------------------------------------------------------------------- +// Non-standard MSVC functions that we're going to override to call our allocator +//----------------------------------------------------------------------------- +extern "C" +{ + +void *_malloc_base( size_t nSize ) +{ + return AllocUnattributed( nSize ); +} + +void _free_base( void *pMem ) +{ + g_pMemAlloc->Free(pMem); +} + +void *_realloc_base( void *pMem, size_t nSize ) +{ + return ReallocUnattributed( pMem, nSize ); +} + +int _heapchk() +{ + return g_pMemAlloc->heapchk(); +} + +int _heapmin() +{ + return 1; +} + +size_t _msize( void *pMem ) +{ + return g_pMemAlloc->GetSize(pMem); +} + +size_t msize( void *pMem ) +{ + return g_pMemAlloc->GetSize(pMem); +} + +void *__cdecl _heap_alloc( size_t nSize ) +{ + return AllocUnattributed( nSize ); +} + +void *__cdecl _nh_malloc( size_t nSize, int ) +{ + return AllocUnattributed( nSize ); +} + +void *__cdecl _expand( void *pMem, size_t nSize ) +{ + Assert( 0 ); + return NULL; +} + +unsigned int _amblksiz = 16; //BYTES_PER_PARA; + +size_t __cdecl _get_sbh_threshold( void ) +{ + return 0; +} + +int __cdecl _set_sbh_threshold( size_t ) +{ + return 0; +} + +int __cdecl _heapadd( void *, size_t ) +{ + return 0; +} + +int __cdecl _heapset( unsigned int ) +{ + return 0; +} + +size_t __cdecl _heapused( size_t *, size_t * ) +{ + return 0; +} + +#ifdef _WIN32 +int __cdecl _heapwalk( _HEAPINFO * ) +{ + return 0; +} +#endif + +} // end extern "C" + + +//----------------------------------------------------------------------------- +// Debugging functions that we're going to override to call our allocator +// NOTE: These have to be here for release + debug builds in case we +// link to a debug static lib!!! +//----------------------------------------------------------------------------- + +extern "C" +{ + +void *malloc_db( size_t nSize, const char *pFileName, int nLine ) +{ + return g_pMemAlloc->Alloc(nSize, pFileName, nLine); +} + +void free_db( void *pMem, const char *pFileName, int nLine ) +{ + g_pMemAlloc->Free(pMem, pFileName, nLine); +} + +void *realloc_db( void *pMem, size_t nSize, const char *pFileName, int nLine ) +{ + return g_pMemAlloc->Realloc(pMem, nSize, pFileName, nLine); +} + +} // end extern "C" + +//----------------------------------------------------------------------------- +// These methods are standard MSVC heap initialization + shutdown methods +//----------------------------------------------------------------------------- +extern "C" +{ + + int __cdecl _heap_init() + { + return g_pMemAlloc != NULL; + } + + void __cdecl _heap_term() + { + } + +} + +#endif + + +//----------------------------------------------------------------------------- +// Prevents us from using an inappropriate new or delete method, +// ensures they are here even when linking against debug or release static libs +//----------------------------------------------------------------------------- +#ifndef NO_MEMOVERRIDE_NEW_DELETE +void *__cdecl operator new( unsigned int nSize ) +{ + return AllocUnattributed( nSize ); +} + +void *__cdecl operator new( unsigned int nSize, int nBlockUse, const char *pFileName, int nLine ) +{ + return g_pMemAlloc->Alloc(nSize, pFileName, nLine); +} + +void __cdecl operator delete( void *pMem ) +{ + g_pMemAlloc->Free( pMem ); +} + +void *__cdecl operator new[] ( unsigned int nSize ) +{ + return AllocUnattributed( nSize ); +} + +void *__cdecl operator new[] ( unsigned int nSize, int nBlockUse, const char *pFileName, int nLine ) +{ + return g_pMemAlloc->Alloc(nSize, pFileName, nLine); +} + +void __cdecl operator delete[] ( void *pMem ) +{ + g_pMemAlloc->Free( pMem ); +} +#endif + + +//----------------------------------------------------------------------------- +// Override some debugging allocation methods in MSVC +// NOTE: These have to be here for release + debug builds in case we +// link to a debug static lib!!! +//----------------------------------------------------------------------------- +#ifndef _STATIC_LINKED +#ifdef _WIN32 + +// This here just hides the internal file names, etc of allocations +// made in the c runtime library +#define CRT_INTERNAL_FILE_NAME "C-runtime internal" + +class CAttibCRT +{ +public: + CAttibCRT(int nBlockUse) : m_nBlockUse(nBlockUse) + { + if (m_nBlockUse == _CRT_BLOCK) + { + g_pMemAlloc->PushAllocDbgInfo(CRT_INTERNAL_FILE_NAME, 0); + } + } + + ~CAttibCRT() + { + if (m_nBlockUse == _CRT_BLOCK) + { + g_pMemAlloc->PopAllocDbgInfo(); + } + } + +private: + int m_nBlockUse; +}; + + +#define AttribIfCrt() CAttibCRT _attrib(nBlockUse) +#elif defined(_LINUX) +#define AttribIfCrt() +#endif // _WIN32 + + +extern "C" +{ + +void *__cdecl _nh_malloc_dbg( size_t nSize, int nFlag, int nBlockUse, + const char *pFileName, int nLine ) +{ + AttribIfCrt(); + return g_pMemAlloc->Alloc(nSize, pFileName, nLine); +} + +void *__cdecl _malloc_dbg( size_t nSize, int nBlockUse, + const char *pFileName, int nLine ) +{ + AttribIfCrt(); + return g_pMemAlloc->Alloc(nSize, pFileName, nLine); +} + +void *__cdecl _calloc_dbg( size_t nNum, size_t nSize, int nBlockUse, + const char *pFileName, int nLine ) +{ + AttribIfCrt(); + void *pMem = g_pMemAlloc->Alloc(nSize * nNum, pFileName, nLine); + memset(pMem, 0, nSize * nNum); + return pMem; +} + +void *__cdecl _realloc_dbg( void *pMem, size_t nNewSize, int nBlockUse, + const char *pFileName, int nLine ) +{ + AttribIfCrt(); + return g_pMemAlloc->Realloc(pMem, nNewSize, pFileName, nLine); +} + +void *__cdecl _expand_dbg( void *pMem, size_t nNewSize, int nBlockUse, + const char *pFileName, int nLine ) +{ + Assert( 0 ); + return NULL; +} + +void __cdecl _free_dbg( void *pMem, int nBlockUse ) +{ + AttribIfCrt(); + g_pMemAlloc->Free(pMem); +} + +size_t __cdecl _msize_dbg( void *pMem, int nBlockUse ) +{ +#ifdef _WIN32 + return _msize(pMem); +#elif _LINUX + Assert( "_msize_dbg unsupported" ); + return 0; +#endif +} + + +} // end extern "C" + + +//----------------------------------------------------------------------------- +// Override some the _CRT debugging allocation methods in MSVC +//----------------------------------------------------------------------------- +#ifdef _WIN32 + +extern "C" +{ + +int _CrtDumpMemoryLeaks(void) +{ + return 0; +} + +_CRT_DUMP_CLIENT _CrtSetDumpClient( _CRT_DUMP_CLIENT dumpClient ) +{ + return NULL; +} + +int _CrtSetDbgFlag( int nNewFlag ) +{ + return g_pMemAlloc->CrtSetDbgFlag( nNewFlag ); +} + +long _crtBreakAlloc; /* Break on this allocation */ +int _crtDbgFlag = _CRTDBG_ALLOC_MEM_DF; + +void __cdecl _CrtSetDbgBlockType( void *pMem, int nBlockUse ) +{ + DebuggerBreak(); +} + +_CRT_ALLOC_HOOK __cdecl _CrtSetAllocHook( _CRT_ALLOC_HOOK pfnNewHook ) +{ + DebuggerBreak(); + return NULL; +} + +long __cdecl _CrtSetBreakAlloc( long lNewBreakAlloc ) +{ + return g_pMemAlloc->CrtSetBreakAlloc( lNewBreakAlloc ); +} + +int __cdecl _CrtIsValidHeapPointer( const void *pMem ) +{ + return g_pMemAlloc->CrtIsValidHeapPointer( pMem ); +} + +int __cdecl _CrtIsValidPointer( const void *pMem, unsigned int size, int access ) +{ + return g_pMemAlloc->CrtIsValidPointer( pMem, size, access ); +} + +int __cdecl _CrtCheckMemory( void ) +{ + // FIXME: Remove this when we re-implement the heap + return g_pMemAlloc->CrtCheckMemory( ); +} + +int __cdecl _CrtIsMemoryBlock( const void *pMem, unsigned int nSize, + long *plRequestNumber, char **ppFileName, int *pnLine ) +{ + DebuggerBreak(); + return 1; +} + +int __cdecl _CrtMemDifference( _CrtMemState *pState, const _CrtMemState * oldState, const _CrtMemState * newState ) +{ + DebuggerBreak(); + return FALSE; +} + +void __cdecl _CrtMemDumpStatistics( const _CrtMemState *pState ) +{ + DebuggerBreak(); +} + +void __cdecl _CrtMemCheckpoint( _CrtMemState *pState ) +{ + // FIXME: Remove this when we re-implement the heap + g_pMemAlloc->CrtMemCheckpoint( pState ); +} + +void __cdecl _CrtMemDumpAllObjectsSince( const _CrtMemState *pState ) +{ + DebuggerBreak(); +} + +void __cdecl _CrtDoForAllClientObjects( void (*pfn)(void *, void *), void * pContext ) +{ + DebuggerBreak(); +} + + +//----------------------------------------------------------------------------- +// Methods in dbgrpt.cpp +//----------------------------------------------------------------------------- +long _crtAssertBusy = -1; + +int __cdecl _CrtSetReportMode( int nReportType, int nReportMode ) +{ + return g_pMemAlloc->CrtSetReportMode( nReportType, nReportMode ); +} + +_HFILE __cdecl _CrtSetReportFile( int nRptType, _HFILE hFile ) +{ + return (_HFILE)g_pMemAlloc->CrtSetReportFile( nRptType, hFile ); +} + +_CRT_REPORT_HOOK __cdecl _CrtSetReportHook( _CRT_REPORT_HOOK pfnNewHook ) +{ + return (_CRT_REPORT_HOOK)g_pMemAlloc->CrtSetReportHook( pfnNewHook ); +} + +int __cdecl _CrtDbgReport( int nRptType, const char * szFile, + int nLine, const char * szModule, const char * szFormat, ... ) +{ + static char output[1024]; + va_list args; + va_start( args, szFormat ); + _vsnprintf( output, sizeof( output )-1, szFormat, args ); + va_end( args ); + + return g_pMemAlloc->CrtDbgReport( nRptType, szFile, nLine, szModule, output ); +} + +int __cdecl _CrtReportBlockType(const void * pUserData) +{ + return 0; +} + + +} // end extern "C" +#endif // _WIN32 + +// Most files include this file, so when it's used it adds an extra .ValveDbg section, +// to help identify debug binaries. +#ifdef _WIN32 + #ifndef NDEBUG // _DEBUG + #pragma data_seg("ValveDBG") + volatile const char* DBG = "*** DEBUG STUB ***"; + #endif +#endif + +#endif + +#endif // !STEAM && !NO_MALLOC_OVERRIDE diff --git a/public/tier0/platform.h b/public/tier0/platform.h index 6a200aa5..cc3b6f29 100644 --- a/public/tier0/platform.h +++ b/public/tier0/platform.h @@ -22,7 +22,11 @@ // need this for _alloca #include +#ifdef _MSC_VER #include +#elif defined __GNUC__ +#include +#endif // need this for memset #include @@ -343,13 +347,13 @@ typedef void * HINSTANCE; #define NO_DEFAULT default: UNREACHABLE(); #ifdef _WIN32 -// Alloca defined for this platform -#define stackalloc( _size ) _alloca( _size ) -#define stackfree( _p ) 0 + // Alloca defined for this platform + #define stackalloc( _size ) _alloca( _size ) + #define stackfree( _p ) #elif _LINUX -// Alloca defined for this platform -#define stackalloc( _size ) alloca( _size ) -#define stackfree( _p ) 0 + // Alloca defined for this platform + #define stackalloc( _size ) alloca( _size ) + #define stackfree( _p ) #endif #ifdef _WIN32 @@ -384,7 +388,7 @@ typedef void * HINSTANCE; #endif // When we port to 64 bit, we'll have to resolve the int, ptr vs size_t 32/64 bit problems... -#if !defined( _WIN64 ) +#if !defined( _WIN64 ) && defined _MSC_VER #pragma warning( disable : 4267 ) // conversion from 'size_t' to 'int', possible loss of data #pragma warning( disable : 4311 ) // pointer truncation from 'char *' to 'int' #pragma warning( disable : 4312 ) // conversion from 'unsigned int' to 'memhandle_t' of greater size @@ -528,11 +532,11 @@ inline float DWordSwapAsm( float f ) // The typically used methods. //------------------------------------- -#if defined(__i386__) || defined(_XBOX) +#if (defined(__i386__) || defined(_XBOX)) && !defined LITTLE_ENDIAN #define LITTLE_ENDIAN 1 #endif -#ifdef _SGI_SOURCE +#if defined _SGI_SOURCE && !defined BIG_ENDIAN #define BIG_ENDIAN 1 #endif diff --git a/public/tier0/threadtools.h b/public/tier0/threadtools.h index 17e8c556..a6e806ab 100644 --- a/public/tier0/threadtools.h +++ b/public/tier0/threadtools.h @@ -190,14 +190,14 @@ template class CThreadLocalInt : public CThreadLocal { public: - operator const T() const { return Get(); } + operator const T() const { return this->Get(); } int operator=( T i ) { Set( i ); return i; } - T operator++() { T i = Get(); Set( ++i ); return i; } - T operator++(int) { T i = Get(); Set( i + 1 ); return i; } + T operator++() { T i = this->Get(); Set( ++i ); return i; } + T operator++(int) { T i = this->Get(); Set( i + 1 ); return i; } - T operator--() { T i = Get(); Set( --i ); return i; } - T operator--(int) { T i = Get(); Set( i - 1 ); return i; } + T operator--() { T i = this->Get(); Set( --i ); return i; } + T operator--(int) { T i = this->Get(); Set( i - 1 ); return i; } }; //--------------------------------------------------------- @@ -430,7 +430,8 @@ public: private: bool TryLock( const uint32 threadId ) volatile { - if ( threadId != m_ownerID && ThreadInterlockedCompareExchange( (volatile long *)&m_ownerID, (long)threadId, 0 ) != 0 ) + volatile void *owner = &m_ownerID; + if ( threadId != m_ownerID && ThreadInterlockedCompareExchange( reinterpret_cast(owner), (long)threadId, 0 ) != 0 ) return false; m_depth++; diff --git a/public/tier0/vprof.h b/public/tier0/vprof.h index 5e8d19fe..39265a1a 100644 --- a/public/tier0/vprof.h +++ b/public/tier0/vprof.h @@ -263,8 +263,6 @@ private: int m_iCurL2CacheMiss; int m_iTotalL2CacheMiss; #endif - - int m_nRecursions; unsigned m_nCurFrameCalls; CCycleCount m_CurFrameTime; @@ -277,6 +275,8 @@ private: CCycleCount m_PeakTime; + int m_nRecursions; + CVProfNode *m_pParent; CVProfNode *m_pChild; CVProfNode *m_pSibling; @@ -285,6 +285,7 @@ private: int m_iClientData; int m_iUniqueNodeID; + #if !defined(_WIN32) || defined(_XBOX) void *operator new( size_t ); diff --git a/public/tier0/wchartypes.h b/public/tier0/wchartypes.h index 79bc5f53..229b770c 100644 --- a/public/tier0/wchartypes.h +++ b/public/tier0/wchartypes.h @@ -20,9 +20,12 @@ // Temporarily turn off Valve defines #include "tier0/valve_off.h" -#ifndef _WCHAR_T_DEFINED +#if defined _WIN32 && !defined _WCHAR_T_DEFINED typedef unsigned short wchar_t; #define _WCHAR_T_DEFINED +#elif defined _LINUX && !defined __WCHAR_TYPE__ +typedef unsigned short wchar_t; +#define __WCHAR_TYPE__ #endif // char8 diff --git a/public/tier1/UtlSortVector.h b/public/tier1/UtlSortVector.h index 6dfc0ae6..63136e32 100644 --- a/public/tier1/UtlSortVector.h +++ b/public/tier1/UtlSortVector.h @@ -94,13 +94,13 @@ private: //----------------------------------------------------------------------------- template CUtlSortVector::CUtlSortVector( int nGrowSize, int initSize ) : - m_pLessContext(NULL), CUtlVector( nGrowSize, initSize ), m_bNeedsSort( false ) + CUtlVector( nGrowSize, initSize ), m_pLessContext(NULL), m_bNeedsSort( false ) { } template CUtlSortVector::CUtlSortVector( T* pMemory, int numElements ) : - m_pLessContext(NULL), CUtlVector( pMemory, numElements ), m_bNeedsSort( false ) + CUtlVector( pMemory, numElements ), m_pLessContext(NULL), m_bNeedsSort( false ) { } @@ -122,9 +122,9 @@ int CUtlSortVector::Insert( const T& src ) AssertFatal( !m_bNeedsSort ); int pos = FindLessOrEqual( src ) + 1; - GrowVector(); - ShiftElementsRight(pos); - CopyConstruct( &Element(pos), src ); + this->GrowVector(); + this->ShiftElementsRight(pos); + CopyConstruct( &this->Element(pos), src ); return pos; } @@ -134,18 +134,18 @@ int CUtlSortVector::InsertNoSort( const T& src ) m_bNeedsSort = true; int lastElement = CUtlVector::m_Size; // Just stick the new element at the end of the vector, but don't do a sort - GrowVector(); - ShiftElementsRight(lastElement); - CopyConstruct( &Element(lastElement), src ); + this->GrowVector(); + this->ShiftElementsRight(lastElement); + CopyConstruct( &this->Element(lastElement), src ); return lastElement; } template void CUtlSortVector::Swap( int L, int R ) { - T temp = Element( L ); - Element( L ) = Element( R ); - Element( R ) = temp; + T temp = this->Element( L ); + this->Element( L ) = this->Element( R ); + this->Element( R ) = temp; } template @@ -165,13 +165,13 @@ int CUtlSortVector::SplitList( LessFunc& less, int nLower, int nUpp int nLeft = nLower + 1; int nRight = nUpper; - const T& val = Element( nLower ); + const T& val = this->Element( nLower ); while ( nLeft <= nRight ) { - while ( nLeft <= nRight && !less.Less( val, Element( nLeft ), m_pLessContext ) ) + while ( nLeft <= nRight && !less.Less( val, this->Element( nLeft ), m_pLessContext ) ) ++nLeft; - while ( nLeft <= nRight && !less.Less( Element( nRight ), val, m_pLessContext ) ) + while ( nLeft <= nRight && !less.Less( this->Element( nRight ), val, m_pLessContext ) ) --nRight; if ( nLeft < nRight ) @@ -194,7 +194,7 @@ void CUtlSortVector::RedoSort( bool bForceSort /*= false */ ) m_bNeedsSort = false; LessFunc less; - QuickSort( less, 0, Count() - 1 ); + QuickSort( less, 0, this->Count() - 1 ); } //----------------------------------------------------------------------------- @@ -207,15 +207,15 @@ int CUtlSortVector::Find( const T& src ) const LessFunc less; - int start = 0, end = Count() - 1; + int start = 0, end = this->Count() - 1; while (start <= end) { int mid = (start + end) >> 1; - if ( less.Less( Element(mid), src, m_pLessContext ) ) + if ( less.Less( this->Element(mid), src, m_pLessContext ) ) { start = mid + 1; } - else if ( less.Less( src, Element(mid), m_pLessContext ) ) + else if ( less.Less( src, this->Element(mid), m_pLessContext ) ) { end = mid - 1; } @@ -237,15 +237,15 @@ int CUtlSortVector::FindLessOrEqual( const T& src ) const AssertFatal( !m_bNeedsSort ); LessFunc less; - int start = 0, end = Count() - 1; + int start = 0, end = this->Count() - 1; while (start <= end) { int mid = (start + end) >> 1; - if ( less.Less( Element(mid), src, m_pLessContext ) ) + if ( less.Less( this->Element(mid), src, m_pLessContext ) ) { start = mid + 1; } - else if ( less.Less( src, Element(mid), m_pLessContext ) ) + else if ( less.Less( src, this->Element(mid), m_pLessContext ) ) { end = mid - 1; } diff --git a/public/tier1/bitbuf.h b/public/tier1/bitbuf.h index 620f49f9..15e7cce4 100644 --- a/public/tier1/bitbuf.h +++ b/public/tier1/bitbuf.h @@ -584,7 +584,8 @@ inline float bf_read::ReadBitFloat() if (bit != 0) val |= ((int)m_pData[byte + 4]) << (32 - bit); m_iCurBit += 32; - return *((float*)&val); + void *bits = &val; + return *reinterpret_cast(bits); } diff --git a/public/tier1/interface.h b/public/tier1/interface.h index 0a17b95e..d744602b 100644 --- a/public/tier1/interface.h +++ b/public/tier1/interface.h @@ -1,205 +1,207 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -// -//=============================================================================// - -// This header defines the interface convention used in the valve engine. -// To make an interface and expose it: -// 1. The interface must be ALL pure virtuals, and have no data members. -// 2. Define a name for it. -// 3. In its implementation file, use EXPOSE_INTERFACE or EXPOSE_SINGLE_INTERFACE. - -// Versioning -// There are two versioning cases that are handled by this: -// 1. You add functions to the end of an interface, so it is binary compatible with the previous interface. In this case, -// you need two EXPOSE_INTERFACEs: one to expose your class as the old interface and one to expose it as the new interface. -// 2. You update an interface so it's not compatible anymore (but you still want to be able to expose the old interface -// for legacy code). In this case, you need to make a new version name for your new interface, and make a wrapper interface and -// expose it for the old interface. - -// Static Linking: -// Must mimic unique seperate class 'InterfaceReg' constructors per subsystem. -// Each subsystem can then import and export interfaces as expected. -// This is achieved through unique namespacing 'InterfaceReg' via symbol _SUBSYSTEM. -// Static Linking also needs to generate unique symbols per interface so as to -// provide a 'stitching' method whereby these interface symbols can be referenced -// via the lib's primary module (usually the lib's interface exposure) -// therby stitching all of that lib's code/data together for eventual final exe link inclusion. - -#ifndef INTERFACE_H -#define INTERFACE_H - -#ifdef _WIN32 -#pragma once -#endif - -#ifdef _LINUX -#include // dlopen,dlclose, et al -#include - -#define HMODULE void * -#define GetProcAddress dlsym - -#define _snprintf snprintf -#endif - -// TODO: move interface.cpp into tier0 library. -#include "tier0/platform.h" - -// All interfaces derive from this. -class IBaseInterface -{ -public: - virtual ~IBaseInterface() {} -}; - - -#define CREATEINTERFACE_PROCNAME "CreateInterface" -typedef void* (*CreateInterfaceFn)(const char *pName, int *pReturnCode); -typedef void* (*InstantiateInterfaceFn)(); - - - -// Used internally to register classes. -class InterfaceReg -{ -public: - InterfaceReg(InstantiateInterfaceFn fn, const char *pName); - -public: - InstantiateInterfaceFn m_CreateFn; - const char *m_pName; - - InterfaceReg *m_pNext; // For the global list. - static InterfaceReg *s_pInterfaceRegs; -}; - -#if defined(_STATIC_LINKED) && defined(_SUBSYSTEM) - -#endif - -// Use this to expose an interface that can have multiple instances. -// e.g.: -// EXPOSE_INTERFACE( CInterfaceImp, IInterface, "MyInterface001" ) -// This will expose a class called CInterfaceImp that implements IInterface (a pure class) -// clients can receive a pointer to this class by calling CreateInterface( "MyInterface001" ) -// -// In practice, the shared header file defines the interface (IInterface) and version name ("MyInterface001") -// so that each component can use these names/vtables to communicate -// -// A single class can support multiple interfaces through multiple inheritance -// -// Use this if you want to write the factory function. -#if !defined(_STATIC_LINKED) || !defined(_SUBSYSTEM) -#define EXPOSE_INTERFACE_FN(functionName, interfaceName, versionName) \ - static InterfaceReg __g_Create##interfaceName##_reg(functionName, versionName); -#else -#define EXPOSE_INTERFACE_FN(functionName, interfaceName, versionName) \ - namespace _SUBSYSTEM \ - { \ - static InterfaceReg __g_Create##interfaceName##_reg(functionName, versionName); \ - } -#endif - -#if !defined(_STATIC_LINKED) || !defined(_SUBSYSTEM) -#define EXPOSE_INTERFACE(className, interfaceName, versionName) \ - static void* __Create##className##_interface() {return (interfaceName *)new className;} \ - static InterfaceReg __g_Create##className##_reg(__Create##className##_interface, versionName ); -#else -#define EXPOSE_INTERFACE(className, interfaceName, versionName) \ - namespace _SUBSYSTEM \ - { \ - static void* __Create##className##_interface() {return (interfaceName *)new className;} \ - static InterfaceReg __g_Create##className##_reg(__Create##className##_interface, versionName ); \ - } -#endif - -// Use this to expose a singleton interface with a global variable you've created. -#if !defined(_STATIC_LINKED) || !defined(_SUBSYSTEM) -#define EXPOSE_SINGLE_INTERFACE_GLOBALVAR(className, interfaceName, versionName, globalVarName) \ - static void* __Create##className##interfaceName##_interface() {return (interfaceName *)&globalVarName;} \ - static InterfaceReg __g_Create##className##interfaceName##_reg(__Create##className##interfaceName##_interface, versionName); -#else -#define EXPOSE_SINGLE_INTERFACE_GLOBALVAR(className, interfaceName, versionName, globalVarName) \ - namespace _SUBSYSTEM \ - { \ - static void* __Create##className##interfaceName##_interface() {return (interfaceName *)&globalVarName;} \ - static InterfaceReg __g_Create##className##interfaceName##_reg(__Create##className##interfaceName##_interface, versionName); \ - } -#endif - -// Use this to expose a singleton interface. This creates the global variable for you automatically. -#if !defined(_STATIC_LINKED) || !defined(_SUBSYSTEM) -#define EXPOSE_SINGLE_INTERFACE(className, interfaceName, versionName) \ - static className __g_##className##_singleton; \ - EXPOSE_SINGLE_INTERFACE_GLOBALVAR(className, interfaceName, versionName, __g_##className##_singleton) -#else -#define EXPOSE_SINGLE_INTERFACE(className, interfaceName, versionName) \ - namespace _SUBSYSTEM \ - { \ - static className __g_##className##_singleton; \ - } \ - EXPOSE_SINGLE_INTERFACE_GLOBALVAR(className, interfaceName, versionName, __g_##className##_singleton) -#endif - -// This function is automatically exported and allows you to access any interfaces exposed with the above macros. -// if pReturnCode is set, it will return one of the following values -// extend this for other error conditions/code - -// load/unload components -class CSysModule; - -// interface return status -enum -{ - IFACE_OK = 0, - IFACE_FAILED -}; - -#if defined(_STATIC_LINKED) && defined(_SUBSYSTEM) - -#endif - -//----------------------------------------------------------------------------- -// This function is automatically exported and allows you to access any interfaces exposed with the above macros. -// if pReturnCode is set, it will return one of the following values (IFACE_OK, IFACE_FAILED) -// extend this for other error conditions/code -//----------------------------------------------------------------------------- -DLL_EXPORT void* CreateInterface(const char *pName, int *pReturnCode); - -extern CreateInterfaceFn Sys_GetFactoryThis( void ); - - -//----------------------------------------------------------------------------- -// UNDONE: This is obsolete, use the module load/unload/get instead!!! -//----------------------------------------------------------------------------- -extern CreateInterfaceFn Sys_GetFactory( const char *pModuleName ); - - -//----------------------------------------------------------------------------- -// Load & Unload should be called in exactly one place for each module -// The factory for that module should be passed on to dependent components for -// proper versioning. -//----------------------------------------------------------------------------- -extern CSysModule *Sys_LoadModule( const char *pModuleName ); -extern void Sys_UnloadModule( CSysModule *pModule ); - -extern CreateInterfaceFn Sys_GetFactory( CSysModule *pModule ); - -// This is a helper function to load a module, get its factory, and get a specific interface. -// You are expected to free all of these things. -// Returns false and cleans up if any of the steps fail. -bool Sys_LoadInterface( - const char *pModuleName, - const char *pInterfaceVersionName, - CSysModule **pOutModule, - void **pOutInterface ); - - -#endif - - - +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + +// This header defines the interface convention used in the valve engine. +// To make an interface and expose it: +// 1. The interface must be ALL pure virtuals, and have no data members. +// 2. Define a name for it. +// 3. In its implementation file, use EXPOSE_INTERFACE or EXPOSE_SINGLE_INTERFACE. + +// Versioning +// There are two versioning cases that are handled by this: +// 1. You add functions to the end of an interface, so it is binary compatible with the previous interface. In this case, +// you need two EXPOSE_INTERFACEs: one to expose your class as the old interface and one to expose it as the new interface. +// 2. You update an interface so it's not compatible anymore (but you still want to be able to expose the old interface +// for legacy code). In this case, you need to make a new version name for your new interface, and make a wrapper interface and +// expose it for the old interface. + +// Static Linking: +// Must mimic unique seperate class 'InterfaceReg' constructors per subsystem. +// Each subsystem can then import and export interfaces as expected. +// This is achieved through unique namespacing 'InterfaceReg' via symbol _SUBSYSTEM. +// Static Linking also needs to generate unique symbols per interface so as to +// provide a 'stitching' method whereby these interface symbols can be referenced +// via the lib's primary module (usually the lib's interface exposure) +// therby stitching all of that lib's code/data together for eventual final exe link inclusion. + +#ifndef INTERFACE_H +#define INTERFACE_H + +#ifdef _WIN32 +#pragma once +#endif + +#ifdef _LINUX +#include // dlopen,dlclose, et al +#include + +#define HMODULE void * +#define GetProcAddress dlsym + +#ifndef _snprintf +#define _snprintf snprintf +#endif +#endif + +// TODO: move interface.cpp into tier0 library. +#include "tier0/platform.h" + +// All interfaces derive from this. +class IBaseInterface +{ +public: + virtual ~IBaseInterface() {} +}; + + +#define CREATEINTERFACE_PROCNAME "CreateInterface" +typedef void* (*CreateInterfaceFn)(const char *pName, int *pReturnCode); +typedef void* (*InstantiateInterfaceFn)(); + + + +// Used internally to register classes. +class InterfaceReg +{ +public: + InterfaceReg(InstantiateInterfaceFn fn, const char *pName); + +public: + InstantiateInterfaceFn m_CreateFn; + const char *m_pName; + + InterfaceReg *m_pNext; // For the global list. + static InterfaceReg *s_pInterfaceRegs; +}; + +#if defined(_STATIC_LINKED) && defined(_SUBSYSTEM) + +#endif + +// Use this to expose an interface that can have multiple instances. +// e.g.: +// EXPOSE_INTERFACE( CInterfaceImp, IInterface, "MyInterface001" ) +// This will expose a class called CInterfaceImp that implements IInterface (a pure class) +// clients can receive a pointer to this class by calling CreateInterface( "MyInterface001" ) +// +// In practice, the shared header file defines the interface (IInterface) and version name ("MyInterface001") +// so that each component can use these names/vtables to communicate +// +// A single class can support multiple interfaces through multiple inheritance +// +// Use this if you want to write the factory function. +#if !defined(_STATIC_LINKED) || !defined(_SUBSYSTEM) +#define EXPOSE_INTERFACE_FN(functionName, interfaceName, versionName) \ + static InterfaceReg __g_Create##interfaceName##_reg(functionName, versionName); +#else +#define EXPOSE_INTERFACE_FN(functionName, interfaceName, versionName) \ + namespace _SUBSYSTEM \ + { \ + static InterfaceReg __g_Create##interfaceName##_reg(functionName, versionName); \ + } +#endif + +#if !defined(_STATIC_LINKED) || !defined(_SUBSYSTEM) +#define EXPOSE_INTERFACE(className, interfaceName, versionName) \ + static void* __Create##className##_interface() {return (interfaceName *)new className;} \ + static InterfaceReg __g_Create##className##_reg(__Create##className##_interface, versionName ); +#else +#define EXPOSE_INTERFACE(className, interfaceName, versionName) \ + namespace _SUBSYSTEM \ + { \ + static void* __Create##className##_interface() {return new className;} \ + static InterfaceReg __g_Create##className##_reg(__Create##className##_interface, versionName ); \ + } +#endif + +// Use this to expose a singleton interface with a global variable you've created. +#if !defined(_STATIC_LINKED) || !defined(_SUBSYSTEM) +#define EXPOSE_SINGLE_INTERFACE_GLOBALVAR(className, interfaceName, versionName, globalVarName) \ + static void* __Create##className##interfaceName##_interface() {return &globalVarName;} \ + static InterfaceReg __g_Create##className##interfaceName##_reg(__Create##className##interfaceName##_interface, versionName); +#else +#define EXPOSE_SINGLE_INTERFACE_GLOBALVAR(className, interfaceName, versionName, globalVarName) \ + namespace _SUBSYSTEM \ + { \ + static void* __Create##className##interfaceName##_interface() {return &globalVarName;} \ + static InterfaceReg __g_Create##className##interfaceName##_reg(__Create##className##interfaceName##_interface, versionName); \ + } +#endif + +// Use this to expose a singleton interface. This creates the global variable for you automatically. +#if !defined(_STATIC_LINKED) || !defined(_SUBSYSTEM) +#define EXPOSE_SINGLE_INTERFACE(className, interfaceName, versionName) \ + static className __g_##className##_singleton; \ + EXPOSE_SINGLE_INTERFACE_GLOBALVAR(className, interfaceName, versionName, __g_##className##_singleton) +#else +#define EXPOSE_SINGLE_INTERFACE(className, interfaceName, versionName) \ + namespace _SUBSYSTEM \ + { \ + static className __g_##className##_singleton; \ + } \ + EXPOSE_SINGLE_INTERFACE_GLOBALVAR(className, interfaceName, versionName, __g_##className##_singleton) +#endif + +// This function is automatically exported and allows you to access any interfaces exposed with the above macros. +// if pReturnCode is set, it will return one of the following values +// extend this for other error conditions/code + +// load/unload components +class CSysModule; + +// interface return status +enum +{ + IFACE_OK = 0, + IFACE_FAILED +}; + +#if defined(_STATIC_LINKED) && defined(_SUBSYSTEM) + +#endif + +//----------------------------------------------------------------------------- +// This function is automatically exported and allows you to access any interfaces exposed with the above macros. +// if pReturnCode is set, it will return one of the following values (IFACE_OK, IFACE_FAILED) +// extend this for other error conditions/code +//----------------------------------------------------------------------------- +DLL_EXPORT void* CreateInterface(const char *pName, int *pReturnCode); + +extern CreateInterfaceFn Sys_GetFactoryThis( void ); + + +//----------------------------------------------------------------------------- +// UNDONE: This is obsolete, use the module load/unload/get instead!!! +//----------------------------------------------------------------------------- +extern CreateInterfaceFn Sys_GetFactory( const char *pModuleName ); + + +//----------------------------------------------------------------------------- +// Load & Unload should be called in exactly one place for each module +// The factory for that module should be passed on to dependent components for +// proper versioning. +//----------------------------------------------------------------------------- +extern CSysModule *Sys_LoadModule( const char *pModuleName ); +extern void Sys_UnloadModule( CSysModule *pModule ); + +extern CreateInterfaceFn Sys_GetFactory( CSysModule *pModule ); + +// This is a helper function to load a module, get its factory, and get a specific interface. +// You are expected to free all of these things. +// Returns false and cleans up if any of the steps fail. +bool Sys_LoadInterface( + const char *pModuleName, + const char *pInterfaceVersionName, + CSysModule **pOutModule, + void **pOutInterface ); + + +#endif + + + diff --git a/public/tier1/jobthread.h b/public/tier1/jobthread.h index 43441f62..926da978 100644 --- a/public/tier1/jobthread.h +++ b/public/tier1/jobthread.h @@ -196,9 +196,9 @@ abstract_class CAsyncJob : public CRefCounted1 public: CAsyncJob( AsyncJobPriority_t priority = AJP_NORMAL ) : m_status( ASYNC_STATUS_UNSERVICED ), + m_priority( priority ), m_queueID( -1 ), - m_pFulfiller( NULL ), - m_priority( priority ) + m_pFulfiller( NULL ) { } diff --git a/public/tier1/stringpool.h b/public/tier1/stringpool.h index 37b9edd8..9362f18d 100644 --- a/public/tier1/stringpool.h +++ b/public/tier1/stringpool.h @@ -32,7 +32,7 @@ public: void FreeAll(); // searches for a string already in the pool - const char * CStringPool::Find( const char *pszValue ); + const char * Find( const char *pszValue ); protected: typedef CUtlRBTree CStrSet; diff --git a/public/tier1/tier1.h b/public/tier1/tier1.h index ca0921c3..aeb545ef 100644 --- a/public/tier1/tier1.h +++ b/public/tier1/tier1.h @@ -63,7 +63,7 @@ public: if ( !BaseClass::Connect( factory ) ) return false; - if ( IsPrimaryAppSystem() ) + if ( this->IsPrimaryAppSystem() ) { ConnectTier1Libraries( &factory, 1 ); if ( ConVarFlag && !g_pCVar ) @@ -77,7 +77,7 @@ public: virtual void Disconnect() { - if ( IsPrimaryAppSystem() ) + if ( this->IsPrimaryAppSystem() ) { DisconnectTier1Libraries(); } @@ -90,7 +90,7 @@ public: if ( nRetVal != INIT_OK ) return nRetVal; - if ( g_pCVar && ConVarFlag && IsPrimaryAppSystem() ) + if ( g_pCVar && ConVarFlag && this->IsPrimaryAppSystem() ) { ConCommandBaseMgr::OneTimeInit( this ); } @@ -99,7 +99,7 @@ public: virtual void Shutdown() { - if ( g_pCVar && ConVarFlag && IsPrimaryAppSystem() ) + if ( g_pCVar && ConVarFlag && this->IsPrimaryAppSystem() ) { g_pCVar->UnlinkVariables( ConVarFlag ); } diff --git a/public/tier1/tokenreader.h b/public/tier1/tokenreader.h index 86328e76..67c2d55a 100644 --- a/public/tier1/tokenreader.h +++ b/public/tier1/tokenreader.h @@ -66,18 +66,20 @@ public: inline int GetErrorCount(void); +#ifndef _LINUX inline TokenReader(TokenReader const &) { // prevent vc7 warning. compiler can't generate a copy constructor since descended from // std::ifstream assert(0); } - inline TokenReader& operator=(TokenReader const &) + inline TokenReader operator=(TokenReader const &) { // prevent vc7 warning. compiler can't generate an assignment operator since descended from // std::ifstream assert(0); } +#endif private: trtoken_t GetString(char *pszStore, int nSize); diff --git a/public/tier1/utlfixedlinkedlist.h b/public/tier1/utlfixedlinkedlist.h index c4255cb0..41992a90 100644 --- a/public/tier1/utlfixedlinkedlist.h +++ b/public/tier1/utlfixedlinkedlist.h @@ -506,7 +506,7 @@ void CUtlFixedLinkedList::RemoveAll() // This doesn't actually deallocate the memory... purge does that m_Memory.RemoveAll(); m_TotalElements = 0; - m_FirstFree = NULL; + m_FirstFree = 0; // Clear everything else out m_Head = InvalidIndex(); diff --git a/public/tier1/utlfixedmemory.h b/public/tier1/utlfixedmemory.h index 8a4e05e3..fc8fa8b1 100644 --- a/public/tier1/utlfixedmemory.h +++ b/public/tier1/utlfixedmemory.h @@ -19,14 +19,15 @@ #include "tier0/dbg.h" #include -#include +#include #include "tier0/platform.h" #include "tier0/memdbgon.h" +#ifdef _MSC_VER #pragma warning (disable:4100) #pragma warning (disable:4514) - +#endif //----------------------------------------------------------------------------- // The CUtlFixedMemory class: @@ -92,9 +93,9 @@ private: private: BlockHeader_t *m_pMemory; + T *m_pFirstFree; int m_nAllocationCount; int m_nGrowSize; - T *m_pFirstFree; }; diff --git a/public/tier1/utllinkedlist.h b/public/tier1/utllinkedlist.h index 1dda7f3f..236f13a2 100644 --- a/public/tier1/utllinkedlist.h +++ b/public/tier1/utllinkedlist.h @@ -1,909 +1,909 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: Linked list container class -// -// $Revision: $ -// $NoKeywords: $ -//=============================================================================// - -#ifndef UTLLINKEDLIST_H -#define UTLLINKEDLIST_H - -#ifdef _WIN32 -#pragma once -#endif - -#include "basetypes.h" -#include "utlmemory.h" -#include "tier0/dbg.h" - - -// This is a useful macro to iterate from head to tail in a linked list. -#define FOR_EACH_LL( listName, iteratorName ) \ - for( int iteratorName=listName.Head(); iteratorName != listName.InvalidIndex(); iteratorName = listName.Next( iteratorName ) ) - -#define INVALID_LLIST_IDX ((I)~0) - -//----------------------------------------------------------------------------- -// class CUtlLinkedList: -// description: -// A lovely index-based linked list! T is the class type, I is the index -// type, which usually should be an unsigned short or smaller. -//----------------------------------------------------------------------------- - -template -class CUtlLinkedList -{ -public: - typedef T ElemType_t; - typedef I IndexType_t; - - // constructor, destructor - CUtlLinkedList( int growSize = 0, int initSize = 0 ); - CUtlLinkedList( void *pMemory, int memsize ); - ~CUtlLinkedList( ); - - // gets particular elements - T& Element( I i ); - T const& Element( I i ) const; - T& operator[]( I i ); - T const& operator[]( I i ) const; - - // Make sure we have a particular amount of memory - void EnsureCapacity( int num ); - - // Memory deallocation - void Purge(); - - // Delete all the elements then call Purge. - void PurgeAndDeleteElements(); - - // Insertion methods.... - I InsertBefore( I before ); - I InsertAfter( I after ); - I AddToHead( ); - I AddToTail( ); - - I InsertBefore( I before, T const& src ); - I InsertAfter( I after, T const& src ); - I AddToHead( T const& src ); - I AddToTail( T const& src ); - - // Find an element and return its index or InvalidIndex() if it couldn't be found. - I Find( const T &src ) const; - - // Look for the element. If it exists, remove it and return true. Otherwise, return false. - bool FindAndRemove( const T &src ); - - // Removal methods - void Remove( I elem ); - void RemoveAll(); - - // Allocation/deallocation methods - // If multilist == true, then list list may contain many - // non-connected lists, and IsInList and Head + Tail are meaningless... - I Alloc( bool multilist = false ); - void Free( I elem ); - - // list modification - void LinkBefore( I before, I elem ); - void LinkAfter( I after, I elem ); - void Unlink( I elem ); - void LinkToHead( I elem ); - void LinkToTail( I elem ); - - // invalid index - inline static I InvalidIndex() { return INVALID_LLIST_IDX; } - inline static size_t ElementSize() { return sizeof(ListElem_t); } - - // list statistics - int Count() const; - I MaxElementIndex() const; - - // Traversing the list - I Head() const; - I Tail() const; - I Previous( I i ) const; - I Next( I i ) const; - - // Are nodes in the list or valid? - bool IsValidIndex( I i ) const; - bool IsInList( I i ) const; - -protected: - // What the linked list element looks like - struct ListElem_t - { - T m_Element; - I m_Previous; - I m_Next; - - private: - // No copy constructor for these... - ListElem_t( const ListElem_t& ); - }; - - // constructs the class - I AllocInternal( bool multilist = false ); - void ConstructList(); - - // Gets at the list element.... - ListElem_t& InternalElement( I i ) { return m_Memory[i]; } - ListElem_t const& InternalElement( I i ) const { return m_Memory[i]; } - - // copy constructors not allowed - CUtlLinkedList( CUtlLinkedList const& list ) { Assert(0); } - - CUtlMemory m_Memory; - I m_Head; - I m_Tail; - I m_FirstFree; - I m_ElementCount; // The number actually in the list - I m_TotalElements; // The number allocated - -#if !defined(_XBOX) || defined(_DEBUG) - // For debugging purposes; - // it's in release builds so this can be used in libraries correctly - ListElem_t *m_pElements; - - void ResetDbgInfo() - { - m_pElements = m_Memory.Base(); - } - -#else - void ResetDbgInfo() {} -#endif -}; - - -//----------------------------------------------------------------------------- -// constructor, destructor -//----------------------------------------------------------------------------- - -template -CUtlLinkedList::CUtlLinkedList( int growSize, int initSize ) : - m_Memory(growSize, initSize) -{ - ConstructList(); - ResetDbgInfo(); -} - -template -CUtlLinkedList::CUtlLinkedList( void* pMemory, int memsize ) : - m_Memory((ListElem_t *)pMemory, memsize/sizeof(ListElem_t)) -{ - ConstructList(); - ResetDbgInfo(); -} - -template -CUtlLinkedList::~CUtlLinkedList( ) -{ - RemoveAll(); -} - -template -void CUtlLinkedList::ConstructList() -{ - m_Head = InvalidIndex(); - m_Tail = InvalidIndex(); - m_FirstFree = InvalidIndex(); - m_ElementCount = m_TotalElements = 0; -} - - -//----------------------------------------------------------------------------- -// gets particular elements -//----------------------------------------------------------------------------- - -template -inline T& CUtlLinkedList::Element( I i ) -{ - return m_Memory[i].m_Element; -} - -template -inline T const& CUtlLinkedList::Element( I i ) const -{ - return m_Memory[i].m_Element; -} - -template -inline T& CUtlLinkedList::operator[]( I i ) -{ - return m_Memory[i].m_Element; -} - -template -inline T const& CUtlLinkedList::operator[]( I i ) const -{ - return m_Memory[i].m_Element; -} - -//----------------------------------------------------------------------------- -// list statistics -//----------------------------------------------------------------------------- - -template -inline int CUtlLinkedList::Count() const -{ - return m_ElementCount; -} - -template -inline I CUtlLinkedList::MaxElementIndex() const -{ - return m_Memory.NumAllocated(); -} - - -//----------------------------------------------------------------------------- -// Traversing the list -//----------------------------------------------------------------------------- - -template -inline I CUtlLinkedList::Head() const -{ - return m_Head; -} - -template -inline I CUtlLinkedList::Tail() const -{ - return m_Tail; -} - -template -inline I CUtlLinkedList::Previous( I i ) const -{ - Assert( IsValidIndex(i) ); - return InternalElement(i).m_Previous; -} - -template -inline I CUtlLinkedList::Next( I i ) const -{ - Assert( IsValidIndex(i) ); - return InternalElement(i).m_Next; -} - - -//----------------------------------------------------------------------------- -// Are nodes in the list or valid? -//----------------------------------------------------------------------------- - -template -inline bool CUtlLinkedList::IsValidIndex( I i ) const -{ - return (i < m_TotalElements) && (i >= 0) && - ((m_Memory[i].m_Previous != i) || (m_Memory[i].m_Next == i)); -} - -template -inline bool CUtlLinkedList::IsInList( I i ) const -{ - return (i < m_TotalElements) && (i >= 0) && (Previous(i) != i); -} - -//----------------------------------------------------------------------------- -// Makes sure we have enough memory allocated to store a requested # of elements -//----------------------------------------------------------------------------- - -template< class T, class I > -void CUtlLinkedList::EnsureCapacity( int num ) -{ - MEM_ALLOC_CREDIT_CLASS(); - m_Memory.EnsureCapacity(num); - ResetDbgInfo(); -} - - -//----------------------------------------------------------------------------- -// Deallocate memory -//----------------------------------------------------------------------------- - -template -void CUtlLinkedList::Purge() -{ - // Prevent reentrant calls to Purge() - if( m_ElementCount ) - { - RemoveAll(); - } - - m_Memory.Purge( ); - m_FirstFree = InvalidIndex(); - m_TotalElements = 0; - ResetDbgInfo(); - -} - - -template -void CUtlLinkedList::PurgeAndDeleteElements() -{ - int iNext; - for( int i=Head(); i != InvalidIndex(); i=iNext ) - { - iNext = Next(i); - delete Element(i); - } - - Purge(); -} - - -//----------------------------------------------------------------------------- -// Node allocation/deallocation -//----------------------------------------------------------------------------- -template -I CUtlLinkedList::AllocInternal( bool multilist ) -{ - I elem; - if (m_FirstFree == InvalidIndex()) - { - // Nothing in the free list; add. - // Since nothing is in the free list, m_TotalElements == total # of elements - // the list knows about. - if ((int)m_TotalElements == m_Memory.NumAllocated()) - { - MEM_ALLOC_CREDIT_CLASS(); - m_Memory.Grow(); - } - - Assert( m_TotalElements != InvalidIndex() ); - - elem = (I)m_TotalElements; - ++m_TotalElements; - - if ( elem == InvalidIndex() ) - { - Error("CUtlLinkedList overflow!\n"); - } - } - else - { - elem = m_FirstFree; - m_FirstFree = InternalElement(m_FirstFree).m_Next; - } - - if (!multilist) - InternalElement(elem).m_Next = InternalElement(elem).m_Previous = elem; - else - InternalElement(elem).m_Next = InternalElement(elem).m_Previous = InvalidIndex(); - - ResetDbgInfo(); - - return elem; -} - -template -I CUtlLinkedList::Alloc( bool multilist ) -{ - I elem = AllocInternal( multilist ); - Construct( &Element(elem) ); - - return elem; -} - -template -void CUtlLinkedList::Free( I elem ) -{ - Assert( IsValidIndex(elem) ); - Unlink(elem); - - ListElem_t &internalElem = InternalElement(elem); - Destruct( &internalElem.m_Element ); - internalElem.m_Next = m_FirstFree; - m_FirstFree = elem; -} - -//----------------------------------------------------------------------------- -// Insertion methods; allocates and links (uses default constructor) -//----------------------------------------------------------------------------- - -template -I CUtlLinkedList::InsertBefore( I before ) -{ - // Make a new node - I newNode = AllocInternal(); - - // Link it in - LinkBefore( before, newNode ); - - // Construct the data - Construct( &Element(newNode) ); - - return newNode; -} - -template -I CUtlLinkedList::InsertAfter( I after ) -{ - // Make a new node - I newNode = AllocInternal(); - - // Link it in - LinkAfter( after, newNode ); - - // Construct the data - Construct( &Element(newNode) ); - - return newNode; -} - -template -inline I CUtlLinkedList::AddToHead( ) -{ - return InsertAfter( InvalidIndex() ); -} - -template -inline I CUtlLinkedList::AddToTail( ) -{ - return InsertBefore( InvalidIndex() ); -} - - -//----------------------------------------------------------------------------- -// Insertion methods; allocates and links (uses copy constructor) -//----------------------------------------------------------------------------- - -template -I CUtlLinkedList::InsertBefore( I before, T const& src ) -{ - // Make a new node - I newNode = AllocInternal(); - - // Link it in - LinkBefore( before, newNode ); - - // Construct the data - CopyConstruct( &Element(newNode), src ); - - return newNode; -} - -template -I CUtlLinkedList::InsertAfter( I after, T const& src ) -{ - // Make a new node - I newNode = AllocInternal(); - - // Link it in - LinkAfter( after, newNode ); - - // Construct the data - CopyConstruct( &Element(newNode), src ); - - return newNode; -} - -template -inline I CUtlLinkedList::AddToHead( T const& src ) -{ - return InsertAfter( InvalidIndex(), src ); -} - -template -inline I CUtlLinkedList::AddToTail( T const& src ) -{ - return InsertBefore( InvalidIndex(), src ); -} - - -//----------------------------------------------------------------------------- -// Removal methods -//----------------------------------------------------------------------------- - -template -I CUtlLinkedList::Find( const T &src ) const -{ - for ( I i=Head(); i != InvalidIndex(); i = Next( i ) ) - { - if ( Element( i ) == src ) - return i; - } - return InvalidIndex(); -} - - -template -bool CUtlLinkedList::FindAndRemove( const T &src ) -{ - I i = Find( src ); - if ( i == InvalidIndex() ) - { - return false; - } - else - { - Remove( i ); - return true; - } -} - - -template -void CUtlLinkedList::Remove( I elem ) -{ - Free( elem ); -} - -template -void CUtlLinkedList::RemoveAll() -{ - if (m_TotalElements == 0) - return; - - // Put everything into the free list - I prev = InvalidIndex(); - for (int i = (int)m_TotalElements; --i >= 0; ) - { - // Invoke the destructor - if (IsValidIndex((I)i)) - Destruct( &Element((I)i) ); - - // next points to the next free list item - InternalElement((I)i).m_Next = prev; - - // Indicates it's in the free list - InternalElement((I)i).m_Previous = (I)i; - prev = (I)i; - } - - // First free points to the first element - m_FirstFree = 0; - - // Clear everything else out - m_Head = InvalidIndex(); - m_Tail = InvalidIndex(); - m_ElementCount = 0; - -#ifdef _XBOX - //Purge(); -#endif -} - - -//----------------------------------------------------------------------------- -// list modification -//----------------------------------------------------------------------------- - -template -void CUtlLinkedList::LinkBefore( I before, I elem ) -{ - Assert( IsValidIndex(elem) ); - - // Unlink it if it's in the list at the moment - Unlink(elem); - - ListElem_t& newElem = InternalElement(elem); - - // The element *after* our newly linked one is the one we linked before. - newElem.m_Next = before; - - if (before == InvalidIndex()) - { - // In this case, we're linking to the end of the list, so reset the tail - newElem.m_Previous = m_Tail; - m_Tail = elem; - } - else - { - // Here, we're not linking to the end. Set the prev pointer to point to - // the element we're linking. - Assert( IsInList(before) ); - ListElem_t& beforeElem = InternalElement(before); - newElem.m_Previous = beforeElem.m_Previous; - beforeElem.m_Previous = elem; - } - - // Reset the head if we linked to the head of the list - if (newElem.m_Previous == InvalidIndex()) - m_Head = elem; - else - InternalElement(newElem.m_Previous).m_Next = elem; - - // one more element baby - ++m_ElementCount; -} - -template -void CUtlLinkedList::LinkAfter( I after, I elem ) -{ - Assert( IsValidIndex(elem) ); - - // Unlink it if it's in the list at the moment - if ( IsInList(elem) ) - Unlink(elem); - - ListElem_t& newElem = InternalElement(elem); - - // The element *before* our newly linked one is the one we linked after - newElem.m_Previous = after; - if (after == InvalidIndex()) - { - // In this case, we're linking to the head of the list, reset the head - newElem.m_Next = m_Head; - m_Head = elem; - } - else - { - // Here, we're not linking to the end. Set the next pointer to point to - // the element we're linking. - Assert( IsInList(after) ); - ListElem_t& afterElem = InternalElement(after); - newElem.m_Next = afterElem.m_Next; - afterElem.m_Next = elem; - } - - // Reset the tail if we linked to the tail of the list - if (newElem.m_Next == InvalidIndex()) - m_Tail = elem; - else - InternalElement(newElem.m_Next).m_Previous = elem; - - // one more element baby - ++m_ElementCount; -} - -template -void CUtlLinkedList::Unlink( I elem ) -{ - Assert( IsValidIndex(elem) ); - if (IsInList(elem)) - { - ListElem_t *pBase = m_Memory.Base(); - ListElem_t *pOldElem = &pBase[elem]; - - // If we're the first guy, reset the head - // otherwise, make our previous node's next pointer = our next - if ( pOldElem->m_Previous != INVALID_LLIST_IDX ) - { - pBase[pOldElem->m_Previous].m_Next = pOldElem->m_Next; - } - else - { - m_Head = pOldElem->m_Next; - } - - // If we're the last guy, reset the tail - // otherwise, make our next node's prev pointer = our prev - if ( pOldElem->m_Next != INVALID_LLIST_IDX ) - { - pBase[pOldElem->m_Next].m_Previous = pOldElem->m_Previous; - } - else - { - m_Tail = pOldElem->m_Previous; - } - - // This marks this node as not in the list, - // but not in the free list either - pOldElem->m_Previous = pOldElem->m_Next = elem; - - // One less puppy - --m_ElementCount; - } -} - -template -inline void CUtlLinkedList::LinkToHead( I elem ) -{ - LinkAfter( InvalidIndex(), elem ); -} - -template -inline void CUtlLinkedList::LinkToTail( I elem ) -{ - LinkBefore( InvalidIndex(), elem ); -} - - -//----------------------------------------------------------------------------- -// Class to drop in to replace a CUtlLinkedList that needs to be more memory agressive -//----------------------------------------------------------------------------- - -DECLARE_POINTER_HANDLE( UtlPtrLinkedListIndex_t ); // to enforce correct usage - -template < typename T > -class CUtlPtrLinkedList -{ -public: - CUtlPtrLinkedList() - : m_pFirst( NULL ), - m_nElems( 0 ) - { - COMPILE_TIME_ASSERT( sizeof(IndexType_t) == sizeof(Node_t *) ); - } - - ~CUtlPtrLinkedList() - { - RemoveAll(); - } - - typedef UtlPtrLinkedListIndex_t IndexType_t; - - T &operator[]( IndexType_t i ) - { - return (( Node_t * )i)->elem; - } - - const T &operator[]( IndexType_t i ) const - { - return (( Node_t * )i)->elem; - } - - IndexType_t AddToTail() - { - return DoInsertBefore( (IndexType_t)m_pFirst, NULL ); - } - - IndexType_t AddToTail( T const& src ) - { - return DoInsertBefore( (IndexType_t)m_pFirst, &src ); - } - - IndexType_t AddToHead() - { - IndexType_t result = DoInsertBefore( (IndexType_t)m_pFirst, NULL ); - m_pFirst = ((Node_t *)result); - return result; - } - - IndexType_t AddToHead( T const& src ) - { - IndexType_t result = DoInsertBefore( (IndexType_t)m_pFirst, &src ); - m_pFirst = ((Node_t *)result); - return result; - } - - IndexType_t InsertBefore( IndexType_t before ) - { - return DoInsertBefore( before, NULL ); - } - - IndexType_t InsertAfter( IndexType_t after ) - { - Node_t *pBefore = ((Node_t *)after)->next; - return DoInsertBefore( pBefore, NULL ); - } - - IndexType_t InsertBefore( IndexType_t before, T const& src ) - { - return DoInsertBefore( before, &src ); - } - - IndexType_t InsertAfter( IndexType_t after, T const& src ) - { - Node_t *pBefore = ((Node_t *)after)->next; - return DoInsertBefore( pBefore, &src ); - } - - void Remove( IndexType_t elem ) - { - Node_t *p = (Node_t *)elem; - - if ( p->pNext == p ) - { - m_pFirst = NULL; - } - else - { - if ( m_pFirst == p ) - { - m_pFirst = p->pNext; - } - p->pNext->pPrev = p->pPrev; - p->pPrev->pNext = p->pNext; - } - - delete p; - m_nElems--; - } - - void RemoveAll() - { - Node_t *p = m_pFirst; - if ( p ) - { - do - { - Node_t *pNext = p->pNext; - delete p; - p = pNext; - } while( p != m_pFirst ); - } - - m_pFirst = NULL; - m_nElems = 0; - } - - int Count() const - { - return m_nElems; - } - - IndexType_t Head() const - { - return (IndexType_t)m_pFirst; - } - - IndexType_t Next( IndexType_t i ) const - { - Node_t *p = ((Node_t *)i)->pNext; - if ( p != m_pFirst ) - { - return (IndexType_t)p; - } - return NULL; - } - - bool IsValidIndex( IndexType_t i ) const - { - Node_t *p = ((Node_t *)i); - return ( p && p->pNext && p->pPrev ); - } - - inline static IndexType_t InvalidIndex() - { - return NULL; - } -private: - - struct Node_t - { - Node_t() {} - Node_t( const T &elem ) : elem( elem ) {} - - T elem; - Node_t *pPrev, *pNext; - }; - - Node_t *AllocNode( const T *pCopyFrom ) - { - MEM_ALLOC_CREDIT_CLASS(); - Node_t *p; - - if ( !pCopyFrom ) - { - p = new Node_t; - } - else - { - p = new Node_t( *pCopyFrom ); - } - - return p; - } - - IndexType_t DoInsertBefore( IndexType_t before, const T *pCopyFrom ) - { - Node_t *p = AllocNode( pCopyFrom ); - Node_t *pBefore = (Node_t *)before; - if ( pBefore ) - { - p->pNext = pBefore; - p->pPrev = pBefore->pPrev; - pBefore->pPrev = p; - p->pPrev->pNext = p; - } - else - { - Assert( !m_pFirst ); - m_pFirst = p->pNext = p->pPrev = p; - } - - m_nElems++; - return (IndexType_t)p; - } - - Node_t *m_pFirst; - unsigned m_nElems; -}; - -//----------------------------------------------------------------------------- - -#endif // UTLLINKEDLIST_H +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: Linked list container class +// +// $Revision: $ +// $NoKeywords: $ +//=============================================================================// + +#ifndef UTLLINKEDLIST_H +#define UTLLINKEDLIST_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "basetypes.h" +#include "utlmemory.h" +#include "tier0/dbg.h" + + +// This is a useful macro to iterate from head to tail in a linked list. +#define FOR_EACH_LL( listName, iteratorName ) \ + for( int iteratorName=listName.Head(); iteratorName != listName.InvalidIndex(); iteratorName = listName.Next( iteratorName ) ) + +#define INVALID_LLIST_IDX ((I)~0) + +//----------------------------------------------------------------------------- +// class CUtlLinkedList: +// description: +// A lovely index-based linked list! T is the class type, I is the index +// type, which usually should be an unsigned short or smaller. +//----------------------------------------------------------------------------- + +template +class CUtlLinkedList +{ +public: + typedef T ElemType_t; + typedef I IndexType_t; + + // constructor, destructor + CUtlLinkedList( int growSize = 0, int initSize = 0 ); + CUtlLinkedList( void *pMemory, int memsize ); + ~CUtlLinkedList( ); + + // gets particular elements + T& Element( I i ); + T const& Element( I i ) const; + T& operator[]( I i ); + T const& operator[]( I i ) const; + + // Make sure we have a particular amount of memory + void EnsureCapacity( int num ); + + // Memory deallocation + void Purge(); + + // Delete all the elements then call Purge. + void PurgeAndDeleteElements(); + + // Insertion methods.... + I InsertBefore( I before ); + I InsertAfter( I after ); + I AddToHead( ); + I AddToTail( ); + + I InsertBefore( I before, T const& src ); + I InsertAfter( I after, T const& src ); + I AddToHead( T const& src ); + I AddToTail( T const& src ); + + // Find an element and return its index or InvalidIndex() if it couldn't be found. + I Find( const T &src ) const; + + // Look for the element. If it exists, remove it and return true. Otherwise, return false. + bool FindAndRemove( const T &src ); + + // Removal methods + void Remove( I elem ); + void RemoveAll(); + + // Allocation/deallocation methods + // If multilist == true, then list list may contain many + // non-connected lists, and IsInList and Head + Tail are meaningless... + I Alloc( bool multilist = false ); + void Free( I elem ); + + // list modification + void LinkBefore( I before, I elem ); + void LinkAfter( I after, I elem ); + void Unlink( I elem ); + void LinkToHead( I elem ); + void LinkToTail( I elem ); + + // invalid index + inline static I InvalidIndex() { return INVALID_LLIST_IDX; } + inline static size_t ElementSize() { return sizeof(ListElem_t); } + + // list statistics + int Count() const; + I MaxElementIndex() const; + + // Traversing the list + I Head() const; + I Tail() const; + I Previous( I i ) const; + I Next( I i ) const; + + // Are nodes in the list or valid? + bool IsValidIndex( I i ) const; + bool IsInList( I i ) const; + +protected: + // What the linked list element looks like + struct ListElem_t + { + T m_Element; + I m_Previous; + I m_Next; + + private: + // No copy constructor for these... + ListElem_t( const ListElem_t& ); + }; + + // constructs the class + I AllocInternal( bool multilist = false ); + void ConstructList(); + + // Gets at the list element.... + ListElem_t& InternalElement( I i ) { return m_Memory[i]; } + ListElem_t const& InternalElement( I i ) const { return m_Memory[i]; } + + // copy constructors not allowed + CUtlLinkedList( CUtlLinkedList const& list ) { Assert(0); } + + CUtlMemory m_Memory; + I m_Head; + I m_Tail; + I m_FirstFree; + I m_ElementCount; // The number actually in the list + I m_TotalElements; // The number allocated + +#if !defined(_XBOX) || defined(_DEBUG) + // For debugging purposes; + // it's in release builds so this can be used in libraries correctly + ListElem_t *m_pElements; + + void ResetDbgInfo() + { + m_pElements = m_Memory.Base(); + } + +#else + void ResetDbgInfo() {} +#endif +}; + + +//----------------------------------------------------------------------------- +// constructor, destructor +//----------------------------------------------------------------------------- + +template +CUtlLinkedList::CUtlLinkedList( int growSize, int initSize ) : + m_Memory(growSize, initSize) +{ + ConstructList(); + ResetDbgInfo(); +} + +template +CUtlLinkedList::CUtlLinkedList( void* pMemory, int memsize ) : + m_Memory((ListElem_t *)pMemory, memsize/sizeof(ListElem_t)) +{ + ConstructList(); + ResetDbgInfo(); +} + +template +CUtlLinkedList::~CUtlLinkedList( ) +{ + RemoveAll(); +} + +template +void CUtlLinkedList::ConstructList() +{ + m_Head = InvalidIndex(); + m_Tail = InvalidIndex(); + m_FirstFree = InvalidIndex(); + m_ElementCount = m_TotalElements = 0; +} + + +//----------------------------------------------------------------------------- +// gets particular elements +//----------------------------------------------------------------------------- + +template +inline T& CUtlLinkedList::Element( I i ) +{ + return m_Memory[i].m_Element; +} + +template +inline T const& CUtlLinkedList::Element( I i ) const +{ + return m_Memory[i].m_Element; +} + +template +inline T& CUtlLinkedList::operator[]( I i ) +{ + return m_Memory[i].m_Element; +} + +template +inline T const& CUtlLinkedList::operator[]( I i ) const +{ + return m_Memory[i].m_Element; +} + +//----------------------------------------------------------------------------- +// list statistics +//----------------------------------------------------------------------------- + +template +inline int CUtlLinkedList::Count() const +{ + return m_ElementCount; +} + +template +inline I CUtlLinkedList::MaxElementIndex() const +{ + return m_Memory.NumAllocated(); +} + + +//----------------------------------------------------------------------------- +// Traversing the list +//----------------------------------------------------------------------------- + +template +inline I CUtlLinkedList::Head() const +{ + return m_Head; +} + +template +inline I CUtlLinkedList::Tail() const +{ + return m_Tail; +} + +template +inline I CUtlLinkedList::Previous( I i ) const +{ + Assert( IsValidIndex(i) ); + return InternalElement(i).m_Previous; +} + +template +inline I CUtlLinkedList::Next( I i ) const +{ + Assert( IsValidIndex(i) ); + return InternalElement(i).m_Next; +} + + +//----------------------------------------------------------------------------- +// Are nodes in the list or valid? +//----------------------------------------------------------------------------- + +template +inline bool CUtlLinkedList::IsValidIndex( I i ) const +{ + return (i < m_TotalElements) && (i > 0) && + ((m_Memory[i].m_Previous != i) || (m_Memory[i].m_Next == i)); +} + +template +inline bool CUtlLinkedList::IsInList( I i ) const +{ + return (i < m_TotalElements) && (i > 0) && (Previous(i) != i); +} + +//----------------------------------------------------------------------------- +// Makes sure we have enough memory allocated to store a requested # of elements +//----------------------------------------------------------------------------- + +template< class T, class I > +void CUtlLinkedList::EnsureCapacity( int num ) +{ + MEM_ALLOC_CREDIT_CLASS(); + m_Memory.EnsureCapacity(num); + ResetDbgInfo(); +} + + +//----------------------------------------------------------------------------- +// Deallocate memory +//----------------------------------------------------------------------------- + +template +void CUtlLinkedList::Purge() +{ + // Prevent reentrant calls to Purge() + if( m_ElementCount ) + { + RemoveAll(); + } + + m_Memory.Purge( ); + m_FirstFree = InvalidIndex(); + m_TotalElements = 0; + ResetDbgInfo(); + +} + + +template +void CUtlLinkedList::PurgeAndDeleteElements() +{ + int iNext; + for( int i=Head(); i != InvalidIndex(); i=iNext ) + { + iNext = Next(i); + delete Element(i); + } + + Purge(); +} + + +//----------------------------------------------------------------------------- +// Node allocation/deallocation +//----------------------------------------------------------------------------- +template +I CUtlLinkedList::AllocInternal( bool multilist ) +{ + I elem; + if (m_FirstFree == InvalidIndex()) + { + // Nothing in the free list; add. + // Since nothing is in the free list, m_TotalElements == total # of elements + // the list knows about. + if ((int)m_TotalElements == m_Memory.NumAllocated()) + { + MEM_ALLOC_CREDIT_CLASS(); + m_Memory.Grow(); + } + + Assert( m_TotalElements != InvalidIndex() ); + + elem = (I)m_TotalElements; + ++m_TotalElements; + + if ( elem == InvalidIndex() ) + { + Error("CUtlLinkedList overflow!\n"); + } + } + else + { + elem = m_FirstFree; + m_FirstFree = InternalElement(m_FirstFree).m_Next; + } + + if (!multilist) + InternalElement(elem).m_Next = InternalElement(elem).m_Previous = elem; + else + InternalElement(elem).m_Next = InternalElement(elem).m_Previous = InvalidIndex(); + + ResetDbgInfo(); + + return elem; +} + +template +I CUtlLinkedList::Alloc( bool multilist ) +{ + I elem = AllocInternal( multilist ); + Construct( &Element(elem) ); + + return elem; +} + +template +void CUtlLinkedList::Free( I elem ) +{ + Assert( IsValidIndex(elem) ); + Unlink(elem); + + ListElem_t &internalElem = InternalElement(elem); + Destruct( &internalElem.m_Element ); + internalElem.m_Next = m_FirstFree; + m_FirstFree = elem; +} + +//----------------------------------------------------------------------------- +// Insertion methods; allocates and links (uses default constructor) +//----------------------------------------------------------------------------- + +template +I CUtlLinkedList::InsertBefore( I before ) +{ + // Make a new node + I newNode = AllocInternal(); + + // Link it in + LinkBefore( before, newNode ); + + // Construct the data + Construct( &Element(newNode) ); + + return newNode; +} + +template +I CUtlLinkedList::InsertAfter( I after ) +{ + // Make a new node + I newNode = AllocInternal(); + + // Link it in + LinkAfter( after, newNode ); + + // Construct the data + Construct( &Element(newNode) ); + + return newNode; +} + +template +inline I CUtlLinkedList::AddToHead( ) +{ + return InsertAfter( InvalidIndex() ); +} + +template +inline I CUtlLinkedList::AddToTail( ) +{ + return InsertBefore( InvalidIndex() ); +} + + +//----------------------------------------------------------------------------- +// Insertion methods; allocates and links (uses copy constructor) +//----------------------------------------------------------------------------- + +template +I CUtlLinkedList::InsertBefore( I before, T const& src ) +{ + // Make a new node + I newNode = AllocInternal(); + + // Link it in + LinkBefore( before, newNode ); + + // Construct the data + CopyConstruct( &Element(newNode), src ); + + return newNode; +} + +template +I CUtlLinkedList::InsertAfter( I after, T const& src ) +{ + // Make a new node + I newNode = AllocInternal(); + + // Link it in + LinkAfter( after, newNode ); + + // Construct the data + CopyConstruct( &Element(newNode), src ); + + return newNode; +} + +template +inline I CUtlLinkedList::AddToHead( T const& src ) +{ + return InsertAfter( InvalidIndex(), src ); +} + +template +inline I CUtlLinkedList::AddToTail( T const& src ) +{ + return InsertBefore( InvalidIndex(), src ); +} + + +//----------------------------------------------------------------------------- +// Removal methods +//----------------------------------------------------------------------------- + +template +I CUtlLinkedList::Find( const T &src ) const +{ + for ( I i=Head(); i != InvalidIndex(); i = Next( i ) ) + { + if ( Element( i ) == src ) + return i; + } + return InvalidIndex(); +} + + +template +bool CUtlLinkedList::FindAndRemove( const T &src ) +{ + I i = Find( src ); + if ( i == InvalidIndex() ) + { + return false; + } + else + { + Remove( i ); + return true; + } +} + + +template +void CUtlLinkedList::Remove( I elem ) +{ + Free( elem ); +} + +template +void CUtlLinkedList::RemoveAll() +{ + if (m_TotalElements == 0) + return; + + // Put everything into the free list + I prev = InvalidIndex(); + for (int i = (int)m_TotalElements; --i >= 0; ) + { + // Invoke the destructor + if (IsValidIndex((I)i)) + Destruct( &Element((I)i) ); + + // next points to the next free list item + InternalElement((I)i).m_Next = prev; + + // Indicates it's in the free list + InternalElement((I)i).m_Previous = (I)i; + prev = (I)i; + } + + // First free points to the first element + m_FirstFree = 0; + + // Clear everything else out + m_Head = InvalidIndex(); + m_Tail = InvalidIndex(); + m_ElementCount = 0; + +#ifdef _XBOX + //Purge(); +#endif +} + + +//----------------------------------------------------------------------------- +// list modification +//----------------------------------------------------------------------------- + +template +void CUtlLinkedList::LinkBefore( I before, I elem ) +{ + Assert( IsValidIndex(elem) ); + + // Unlink it if it's in the list at the moment + Unlink(elem); + + ListElem_t& newElem = InternalElement(elem); + + // The element *after* our newly linked one is the one we linked before. + newElem.m_Next = before; + + if (before == InvalidIndex()) + { + // In this case, we're linking to the end of the list, so reset the tail + newElem.m_Previous = m_Tail; + m_Tail = elem; + } + else + { + // Here, we're not linking to the end. Set the prev pointer to point to + // the element we're linking. + Assert( IsInList(before) ); + ListElem_t& beforeElem = InternalElement(before); + newElem.m_Previous = beforeElem.m_Previous; + beforeElem.m_Previous = elem; + } + + // Reset the head if we linked to the head of the list + if (newElem.m_Previous == InvalidIndex()) + m_Head = elem; + else + InternalElement(newElem.m_Previous).m_Next = elem; + + // one more element baby + ++m_ElementCount; +} + +template +void CUtlLinkedList::LinkAfter( I after, I elem ) +{ + Assert( IsValidIndex(elem) ); + + // Unlink it if it's in the list at the moment + if ( IsInList(elem) ) + Unlink(elem); + + ListElem_t& newElem = InternalElement(elem); + + // The element *before* our newly linked one is the one we linked after + newElem.m_Previous = after; + if (after == InvalidIndex()) + { + // In this case, we're linking to the head of the list, reset the head + newElem.m_Next = m_Head; + m_Head = elem; + } + else + { + // Here, we're not linking to the end. Set the next pointer to point to + // the element we're linking. + Assert( IsInList(after) ); + ListElem_t& afterElem = InternalElement(after); + newElem.m_Next = afterElem.m_Next; + afterElem.m_Next = elem; + } + + // Reset the tail if we linked to the tail of the list + if (newElem.m_Next == InvalidIndex()) + m_Tail = elem; + else + InternalElement(newElem.m_Next).m_Previous = elem; + + // one more element baby + ++m_ElementCount; +} + +template +void CUtlLinkedList::Unlink( I elem ) +{ + Assert( IsValidIndex(elem) ); + if (IsInList(elem)) + { + ListElem_t *pBase = m_Memory.Base(); + ListElem_t *pOldElem = &pBase[elem]; + + // If we're the first guy, reset the head + // otherwise, make our previous node's next pointer = our next + if ( pOldElem->m_Previous != INVALID_LLIST_IDX ) + { + pBase[pOldElem->m_Previous].m_Next = pOldElem->m_Next; + } + else + { + m_Head = pOldElem->m_Next; + } + + // If we're the last guy, reset the tail + // otherwise, make our next node's prev pointer = our prev + if ( pOldElem->m_Next != INVALID_LLIST_IDX ) + { + pBase[pOldElem->m_Next].m_Previous = pOldElem->m_Previous; + } + else + { + m_Tail = pOldElem->m_Previous; + } + + // This marks this node as not in the list, + // but not in the free list either + pOldElem->m_Previous = pOldElem->m_Next = elem; + + // One less puppy + --m_ElementCount; + } +} + +template +inline void CUtlLinkedList::LinkToHead( I elem ) +{ + LinkAfter( InvalidIndex(), elem ); +} + +template +inline void CUtlLinkedList::LinkToTail( I elem ) +{ + LinkBefore( InvalidIndex(), elem ); +} + + +//----------------------------------------------------------------------------- +// Class to drop in to replace a CUtlLinkedList that needs to be more memory agressive +//----------------------------------------------------------------------------- + +DECLARE_POINTER_HANDLE( UtlPtrLinkedListIndex_t ); // to enforce correct usage + +template < typename T > +class CUtlPtrLinkedList +{ +public: + CUtlPtrLinkedList() + : m_pFirst( NULL ), + m_nElems( 0 ) + { + COMPILE_TIME_ASSERT( sizeof(IndexType_t) == sizeof(Node_t *) ); + } + + ~CUtlPtrLinkedList() + { + RemoveAll(); + } + + typedef UtlPtrLinkedListIndex_t IndexType_t; + + T &operator[]( IndexType_t i ) + { + return (( Node_t * )i)->elem; + } + + const T &operator[]( IndexType_t i ) const + { + return (( Node_t * )i)->elem; + } + + IndexType_t AddToTail() + { + return DoInsertBefore( (IndexType_t)m_pFirst, NULL ); + } + + IndexType_t AddToTail( T const& src ) + { + return DoInsertBefore( (IndexType_t)m_pFirst, &src ); + } + + IndexType_t AddToHead() + { + IndexType_t result = DoInsertBefore( (IndexType_t)m_pFirst, NULL ); + m_pFirst = ((Node_t *)result); + return result; + } + + IndexType_t AddToHead( T const& src ) + { + IndexType_t result = DoInsertBefore( (IndexType_t)m_pFirst, &src ); + m_pFirst = ((Node_t *)result); + return result; + } + + IndexType_t InsertBefore( IndexType_t before ) + { + return DoInsertBefore( before, NULL ); + } + + IndexType_t InsertAfter( IndexType_t after ) + { + Node_t *pBefore = ((Node_t *)after)->next; + return DoInsertBefore( pBefore, NULL ); + } + + IndexType_t InsertBefore( IndexType_t before, T const& src ) + { + return DoInsertBefore( before, &src ); + } + + IndexType_t InsertAfter( IndexType_t after, T const& src ) + { + Node_t *pBefore = ((Node_t *)after)->next; + return DoInsertBefore( pBefore, &src ); + } + + void Remove( IndexType_t elem ) + { + Node_t *p = (Node_t *)elem; + + if ( p->pNext == p ) + { + m_pFirst = NULL; + } + else + { + if ( m_pFirst == p ) + { + m_pFirst = p->pNext; + } + p->pNext->pPrev = p->pPrev; + p->pPrev->pNext = p->pNext; + } + + delete p; + m_nElems--; + } + + void RemoveAll() + { + Node_t *p = m_pFirst; + if ( p ) + { + do + { + Node_t *pNext = p->pNext; + delete p; + p = pNext; + } while( p != m_pFirst ); + } + + m_pFirst = NULL; + m_nElems = 0; + } + + int Count() const + { + return m_nElems; + } + + IndexType_t Head() const + { + return (IndexType_t)m_pFirst; + } + + IndexType_t Next( IndexType_t i ) const + { + Node_t *p = ((Node_t *)i)->pNext; + if ( p != m_pFirst ) + { + return (IndexType_t)p; + } + return NULL; + } + + bool IsValidIndex( IndexType_t i ) const + { + Node_t *p = ((Node_t *)i); + return ( p && p->pNext && p->pPrev ); + } + + inline static IndexType_t InvalidIndex() + { + return NULL; + } +private: + + struct Node_t + { + Node_t() {} + Node_t( const T &elem ) : elem( elem ) {} + + T elem; + Node_t *pPrev, *pNext; + }; + + Node_t *AllocNode( const T *pCopyFrom ) + { + MEM_ALLOC_CREDIT_CLASS(); + Node_t *p; + + if ( !pCopyFrom ) + { + p = new Node_t; + } + else + { + p = new Node_t( *pCopyFrom ); + } + + return p; + } + + IndexType_t DoInsertBefore( IndexType_t before, const T *pCopyFrom ) + { + Node_t *p = AllocNode( pCopyFrom ); + Node_t *pBefore = (Node_t *)before; + if ( pBefore ) + { + p->pNext = pBefore; + p->pPrev = pBefore->pPrev; + pBefore->pPrev = p; + p->pPrev->pNext = p; + } + else + { + Assert( !m_pFirst ); + m_pFirst = p->pNext = p->pPrev = p; + } + + m_nElems++; + return (IndexType_t)p; + } + + Node_t *m_pFirst; + unsigned m_nElems; +}; + +//----------------------------------------------------------------------------- + +#endif // UTLLINKEDLIST_H diff --git a/public/tier1/utlmemory.h b/public/tier1/utlmemory.h index a4249249..acbab9dd 100644 --- a/public/tier1/utlmemory.h +++ b/public/tier1/utlmemory.h @@ -21,8 +21,10 @@ #include "tier0/memalloc.h" #include "tier0/memdbgon.h" +#ifdef _MSC_VER #pragma warning (disable:4100) #pragma warning (disable:4514) +#endif //----------------------------------------------------------------------------- @@ -627,11 +629,11 @@ CUtlMemoryAligned::CUtlMemoryAligned( int nGrowSize, int nInitAll CUtlMemory::m_pMemory = 0; CUtlMemory::m_nAllocationCount = nInitAllocationCount; CUtlMemory::m_nGrowSize = nGrowSize; - ValidateGrowSize(); + this->ValidateGrowSize(); // Alignment must be a power of two COMPILE_TIME_ASSERT( (nAlignment & (nAlignment-1)) == 0 ); - Assert( (nGrowSize >= 0) && (nGrowSize != EXTERNAL_BUFFER_MARKER) ); + Assert( (nGrowSize >= 0) && (nGrowSize != this->EXTERNAL_BUFFER_MARKER) ); if ( CUtlMemory::m_nAllocationCount ) { UTLMEMORY_TRACK_ALLOC(); @@ -708,7 +710,7 @@ void CUtlMemoryAligned::Grow( int num ) { Assert( num > 0 ); - if ( IsExternallyAllocated() ) + if ( this->IsExternallyAllocated() ) { // Can't grow a buffer whose memory was externally allocated Assert(0); @@ -750,7 +752,7 @@ inline void CUtlMemoryAligned::EnsureCapacity( int num ) if ( CUtlMemory::m_nAllocationCount >= num ) return; - if ( IsExternallyAllocated() ) + if ( this->IsExternallyAllocated() ) { // Can't grow a buffer whose memory was externally allocated Assert(0); @@ -783,7 +785,7 @@ inline void CUtlMemoryAligned::EnsureCapacity( int num ) template< class T, int nAlignment > void CUtlMemoryAligned::Purge() { - if ( !IsExternallyAllocated() ) + if ( !this->IsExternallyAllocated() ) { if (m_pMemoryBase) { diff --git a/public/tier1/utlmultilist.h b/public/tier1/utlmultilist.h index a5ac7293..29d8735f 100644 --- a/public/tier1/utlmultilist.h +++ b/public/tier1/utlmultilist.h @@ -1,729 +1,729 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: Multiple linked list container class -// -// $Revision: $ -// $NoKeywords: $ -//=============================================================================// - -#ifndef UTLMULTILIST_H -#define UTLMULTILIST_H - -#ifdef _WIN32 -#pragma once -#endif - -#include "utllinkedlist.h" - -// memdbgon must be the last include file in a .h file!!! -#include "tier0/memdbgon.h" - - -// This is a useful macro to iterate from head to tail in a linked list. -#define FOR_EACH_LL( listName, iteratorName ) \ - for( int iteratorName=listName.Head(); iteratorName != listName.InvalidIndex(); iteratorName = listName.Next( iteratorName ) ) - -//----------------------------------------------------------------------------- -// class CUtlMultiList: -// description: -// A lovely index-based linked list! T is the class type, I is the index -// type, which usually should be an unsigned short or smaller. -// This list can contain multiple lists -//----------------------------------------------------------------------------- -template -class CUtlMultiList -{ -public: - typedef I ListHandle_t; - - // constructor, destructor - CUtlMultiList( int growSize = 0, int initSize = 0 ); - CUtlMultiList( void *pMemory, int memsize ); - ~CUtlMultiList( ); - - // gets particular elements - T& Element( I i ); - T const& Element( I i ) const; - T& operator[]( I i ); - T const& operator[]( I i ) const; - - // Make sure we have a particular amount of memory - void EnsureCapacity( int num ); - - // Memory deallocation - void Purge(); - - // List Creation/deletion - ListHandle_t CreateList(); - void DestroyList( ListHandle_t list ); - bool IsValidList( ListHandle_t list ) const; - - // Insertion methods (call default constructor).... - I InsertBefore( ListHandle_t list, I before ); - I InsertAfter( ListHandle_t list, I after ); - I AddToHead( ListHandle_t list ); - I AddToTail( ListHandle_t list ); - - // Insertion methods (call copy constructor).... - I InsertBefore( ListHandle_t list, I before, T const& src ); - I InsertAfter( ListHandle_t list, I after, T const& src ); - I AddToHead( ListHandle_t list, T const& src ); - I AddToTail( ListHandle_t list, T const& src ); - - // Removal methods - void Remove( ListHandle_t list, I elem ); - - // Removes all items in a single list - void RemoveAll( ListHandle_t list ); - - // Removes all items in all lists - void RemoveAll(); - - // Allocation/deallocation methods - // NOTE: To free, it must *not* be in a list! - I Alloc( ); - void Free( I elem ); - - // list modification - void LinkBefore( ListHandle_t list, I before, I elem ); - void LinkAfter( ListHandle_t list, I after, I elem ); - void Unlink( ListHandle_t list, I elem ); - void LinkToHead( ListHandle_t list, I elem ); - void LinkToTail( ListHandle_t list, I elem ); - - // invalid index - static I InvalidIndex() { return (I)~0; } - static size_t ElementSize() { return sizeof(ListElem_t); } - - // list statistics - int Count( ListHandle_t list ) const; - int TotalCount( ) const; - I MaxElementIndex() const; - - // Traversing the list - I Head( ListHandle_t list ) const; - I Tail( ListHandle_t list ) const; - I Previous( I element ) const; - I Next( I element ) const; - - // Are nodes in a list or valid? - bool IsValidIndex( I i ) const; - bool IsInList( I i ) const; - -protected: - // What the linked list element looks like - struct ListElem_t - { - T m_Element; - I m_Previous; - I m_Next; - }; - - struct List_t - { - I m_Head; - I m_Tail; - I m_Count; - }; - - // constructs the class - void ConstructList( ); - - // Gets at the list element.... - ListElem_t& InternalElement( I i ) { return m_Memory[i]; } - ListElem_t const& InternalElement( I i ) const { return m_Memory[i]; } - - // A test for debug mode only... - bool IsElementInList( ListHandle_t list, I elem ) const; - - // copy constructors not allowed - CUtlMultiList( CUtlMultiList const& list ) { Assert(0); } - - CUtlMemory m_Memory; - CUtlLinkedList m_List; - I* m_pElementList; - - I m_FirstFree; - I m_TotalElements; - I m_MaxElementIndex; // The number allocated - -#if !defined(_XBOX) || defined(_DEBUG) - void ResetDbgInfo() - { - m_pElements = m_Memory.Base(); - -#ifdef _DEBUG - // Allocate space for the element list (which list is each element in) - if (m_Memory.NumAllocated() > 0) - { - if (!m_pElementList) - { - m_pElementList = (I*)malloc( m_Memory.NumAllocated() * sizeof(I) ); - } - else - { - m_pElementList = (I*)realloc( m_pElementList, m_Memory.NumAllocated() * sizeof(I) ); - } - } -#endif - } - - // For debugging purposes; - // it's in release builds so this can be used in libraries correctly - ListElem_t *m_pElements; -#else - void ResetDbgInfo() {} -#endif -}; - - -//----------------------------------------------------------------------------- -// constructor, destructor -//----------------------------------------------------------------------------- - -template -CUtlMultiList::CUtlMultiList( int growSize, int initSize ) : - m_Memory(growSize, initSize), m_pElementList(0) -{ - ConstructList(); -} - -template -CUtlMultiList::CUtlMultiList( void* pMemory, int memsize ) : - m_Memory((ListElem_t *)pMemory, memsize/sizeof(ListElem_t)), m_pElementList(0) -{ - ConstructList(); -} - -template -CUtlMultiList::~CUtlMultiList( ) -{ - RemoveAll(); - if (m_pElementList) - free(m_pElementList); -} - -template -void CUtlMultiList::ConstructList( ) -{ - m_FirstFree = InvalidIndex(); - m_TotalElements = 0; - m_MaxElementIndex = 0; - ResetDbgInfo(); -} - - -//----------------------------------------------------------------------------- -// gets particular elements -//----------------------------------------------------------------------------- -template -inline T& CUtlMultiList::Element( I i ) -{ - return m_Memory[i].m_Element; -} - -template -inline T const& CUtlMultiList::Element( I i ) const -{ - return m_Memory[i].m_Element; -} - -template -inline T& CUtlMultiList::operator[]( I i ) -{ - return m_Memory[i].m_Element; -} - -template -inline T const& CUtlMultiList::operator[]( I i ) const -{ - return m_Memory[i].m_Element; -} - - -//----------------------------------------------------------------------------- -// list creation/destruction -//----------------------------------------------------------------------------- -template -typename CUtlMultiList::ListHandle_t CUtlMultiList::CreateList() -{ - ListHandle_t l = m_List.AddToTail(); - m_List[l].m_Head = m_List[l].m_Tail = InvalidIndex(); - m_List[l].m_Count = 0; - return l; -} - -template -void CUtlMultiList::DestroyList( ListHandle_t list ) -{ - Assert( IsValidList(list) ); - RemoveAll( list ); - m_List.Remove(list); -} - -template -bool CUtlMultiList::IsValidList( ListHandle_t list ) const -{ - return m_List.IsValidIndex(list); -} - - -//----------------------------------------------------------------------------- -// list statistics -//----------------------------------------------------------------------------- -template -inline int CUtlMultiList::TotalCount() const -{ - return m_TotalElements; -} - -template -inline int CUtlMultiList::Count( ListHandle_t list ) const -{ - Assert( IsValidList(list) ); - return m_List[list].m_Count; -} - -template -inline I CUtlMultiList::MaxElementIndex() const -{ - return m_MaxElementIndex; -} - - -//----------------------------------------------------------------------------- -// Traversing the list -//----------------------------------------------------------------------------- -template -inline I CUtlMultiList::Head(ListHandle_t list) const -{ - Assert( IsValidList(list) ); - return m_List[list].m_Head; -} - -template -inline I CUtlMultiList::Tail(ListHandle_t list) const -{ - Assert( IsValidList(list) ); - return m_List[list].m_Tail; -} - -template -inline I CUtlMultiList::Previous( I i ) const -{ - Assert( IsValidIndex(i) ); - return InternalElement(i).m_Previous; -} - -template -inline I CUtlMultiList::Next( I i ) const -{ - Assert( IsValidIndex(i) ); - return InternalElement(i).m_Next; -} - - -//----------------------------------------------------------------------------- -// Are nodes in the list or valid? -//----------------------------------------------------------------------------- -template -inline bool CUtlMultiList::IsValidIndex( I i ) const -{ - return (i < m_MaxElementIndex) && (i >= 0) && - ((m_Memory[i].m_Previous != i) || (m_Memory[i].m_Next == i)); -} - -template -inline bool CUtlMultiList::IsInList( I i ) const -{ - return (i < m_MaxElementIndex) && (i >= 0) && (Previous(i) != i); -} - - -//----------------------------------------------------------------------------- -// Makes sure we have enough memory allocated to store a requested # of elements -//----------------------------------------------------------------------------- -template< class T, class I > -void CUtlMultiList::EnsureCapacity( int num ) -{ - m_Memory.EnsureCapacity(num); - ResetDbgInfo(); -} - - -//----------------------------------------------------------------------------- -// Deallocate memory -//----------------------------------------------------------------------------- -template -void CUtlMultiList::Purge() -{ - RemoveAll(); - m_List.Purge(); - m_Memory.Purge( ); - m_List.Purge(); - m_FirstFree = InvalidIndex(); - m_TotalElements = 0; - m_MaxElementIndex = 0; - ResetDbgInfo(); -} - - -//----------------------------------------------------------------------------- -// Node allocation/deallocation -//----------------------------------------------------------------------------- -template -I CUtlMultiList::Alloc( ) -{ - I elem; - if (m_FirstFree == InvalidIndex()) - { - // Nothing in the free list; add. - // Since nothing is in the free list, m_TotalElements == total # of elements - // the list knows about. - if (m_MaxElementIndex == m_Memory.NumAllocated()) - { - m_Memory.Grow(); - ResetDbgInfo(); - } - - elem = (I)m_MaxElementIndex; - ++m_MaxElementIndex; - } - else - { - elem = m_FirstFree; - m_FirstFree = InternalElement(m_FirstFree).m_Next; - } - - // Mark the element as not being in a list - InternalElement(elem).m_Next = InternalElement(elem).m_Previous = elem; - - ++m_TotalElements; - - Construct( &Element(elem) ); - - return elem; -} - -template -void CUtlMultiList::Free( I elem ) -{ - Assert( IsValidIndex(elem) && !IsInList(elem) ); - Destruct( &Element(elem) ); - InternalElement(elem).m_Next = m_FirstFree; - m_FirstFree = elem; - --m_TotalElements; -} - - -//----------------------------------------------------------------------------- -// A test for debug mode only... -//----------------------------------------------------------------------------- -template -inline bool CUtlMultiList::IsElementInList( ListHandle_t list, I elem ) const -{ - if (!m_pElementList) - return true; - - return m_pElementList[elem] == list; -} - - -//----------------------------------------------------------------------------- -// list modification -//----------------------------------------------------------------------------- -template -void CUtlMultiList::LinkBefore( ListHandle_t list, I before, I elem ) -{ - Assert( IsValidIndex(elem) && IsValidList(list) ); - - // Unlink it if it's in the list at the moment - Unlink(list, elem); - - ListElem_t& newElem = InternalElement(elem); - - // The element *after* our newly linked one is the one we linked before. - newElem.m_Next = before; - - if (before == InvalidIndex()) - { - // In this case, we're linking to the end of the list, so reset the tail - newElem.m_Previous = m_List[list].m_Tail; - m_List[list].m_Tail = elem; - } - else - { - // Here, we're not linking to the end. Set the prev pointer to point to - // the element we're linking. - Assert( IsInList(before) ); - ListElem_t& beforeElem = InternalElement(before); - newElem.m_Previous = beforeElem.m_Previous; - beforeElem.m_Previous = elem; - } - - // Reset the head if we linked to the head of the list - if (newElem.m_Previous == InvalidIndex()) - m_List[list].m_Head = elem; - else - InternalElement(newElem.m_Previous).m_Next = elem; - - // one more element baby - ++m_List[list].m_Count; - - // Store the element into the list - if (m_pElementList) - m_pElementList[elem] = list; -} - -template -void CUtlMultiList::LinkAfter( ListHandle_t list, I after, I elem ) -{ - Assert( IsValidIndex(elem) ); - - // Unlink it if it's in the list at the moment - Unlink(list, elem); - - ListElem_t& newElem = InternalElement(elem); - - // The element *before* our newly linked one is the one we linked after - newElem.m_Previous = after; - if (after == InvalidIndex()) - { - // In this case, we're linking to the head of the list, reset the head - newElem.m_Next = m_List[list].m_Head; - m_List[list].m_Head = elem; - } - else - { - // Here, we're not linking to the end. Set the next pointer to point to - // the element we're linking. - Assert( IsInList(after) ); - ListElem_t& afterElem = InternalElement(after); - newElem.m_Next = afterElem.m_Next; - afterElem.m_Next = elem; - } - - // Reset the tail if we linked to the tail of the list - if (newElem.m_Next == InvalidIndex()) - m_List[list].m_Tail = elem; - else - InternalElement(newElem.m_Next).m_Previous = elem; - - // one more element baby - ++m_List[list].m_Count; - - // Store the element into the list - if (m_pElementList) - m_pElementList[elem] = list; -} - -template -void CUtlMultiList::Unlink( ListHandle_t list, I elem ) -{ - Assert( IsValidIndex(elem) && IsValidList(list) ); - - if (IsInList(elem)) - { - // Make sure the element is in the right list - Assert( IsElementInList( list, elem ) ); - ListElem_t& oldElem = InternalElement(elem); - - // If we're the first guy, reset the head - // otherwise, make our previous node's next pointer = our next - if (oldElem.m_Previous != InvalidIndex()) - InternalElement(oldElem.m_Previous).m_Next = oldElem.m_Next; - else - m_List[list].m_Head = oldElem.m_Next; - - // If we're the last guy, reset the tail - // otherwise, make our next node's prev pointer = our prev - if (oldElem.m_Next != InvalidIndex()) - InternalElement(oldElem.m_Next).m_Previous = oldElem.m_Previous; - else - m_List[list].m_Tail = oldElem.m_Previous; - - // This marks this node as not in the list, - // but not in the free list either - oldElem.m_Previous = oldElem.m_Next = elem; - - // One less puppy - --m_List[list].m_Count; - - // Store the element into the list - if (m_pElementList) - m_pElementList[elem] = m_List.InvalidIndex(); - } -} - -template -inline void CUtlMultiList::LinkToHead( ListHandle_t list, I elem ) -{ - LinkAfter( list, InvalidIndex(), elem ); -} - -template -inline void CUtlMultiList::LinkToTail( ListHandle_t list, I elem ) -{ - LinkBefore( list, InvalidIndex(), elem ); -} - - -//----------------------------------------------------------------------------- -// Insertion methods; allocates and links (uses default constructor) -//----------------------------------------------------------------------------- -template -I CUtlMultiList::InsertBefore( ListHandle_t list, I before ) -{ - // Make a new node - I newNode = Alloc(); - - // Link it in - LinkBefore( list, before, newNode ); - - // Construct the data - Construct( &Element(newNode) ); - - return newNode; -} - -template -I CUtlMultiList::InsertAfter( ListHandle_t list, I after ) -{ - // Make a new node - I newNode = Alloc(); - - // Link it in - LinkAfter( list, after, newNode ); - - // Construct the data - Construct( &Element(newNode) ); - - return newNode; -} - -template -inline I CUtlMultiList::AddToHead( ListHandle_t list ) -{ - return InsertAfter( list, InvalidIndex() ); -} - -template -inline I CUtlMultiList::AddToTail( ListHandle_t list ) -{ - return InsertBefore( list, InvalidIndex() ); -} - - -//----------------------------------------------------------------------------- -// Insertion methods; allocates and links (uses copy constructor) -//----------------------------------------------------------------------------- -template -I CUtlMultiList::InsertBefore( ListHandle_t list, I before, T const& src ) -{ - // Make a new node - I newNode = Alloc(); - - // Link it in - LinkBefore( list, before, newNode ); - - // Construct the data - CopyConstruct( &Element(newNode), src ); - - return newNode; -} - -template -I CUtlMultiList::InsertAfter( ListHandle_t list, I after, T const& src ) -{ - // Make a new node - I newNode = Alloc(); - - // Link it in - LinkAfter( list, after, newNode ); - - // Construct the data - CopyConstruct( &Element(newNode), src ); - - return newNode; -} - -template -inline I CUtlMultiList::AddToHead( ListHandle_t list, T const& src ) -{ - return InsertAfter( list, InvalidIndex(), src ); -} - -template -inline I CUtlMultiList::AddToTail( ListHandle_t list, T const& src ) -{ - return InsertBefore( list, InvalidIndex(), src ); -} - - -//----------------------------------------------------------------------------- -// Removal methods -//----------------------------------------------------------------------------- -template -void CUtlMultiList::Remove( ListHandle_t list, I elem ) -{ - if (IsInList(elem)) - Unlink(list, elem); - Free( elem ); -} - -// Removes all items in a single list -template -void CUtlMultiList::RemoveAll( ListHandle_t list ) -{ - Assert( IsValidList(list) ); - I i = Head(list); - I next; - while( i != InvalidIndex() ) - { - next = Next(i); - Remove(list, i); - i = next; - } -} - - -template -void CUtlMultiList::RemoveAll() -{ - if (m_MaxElementIndex == 0) - return; - - // Put everything into the free list - I prev = InvalidIndex(); - for (int i = (int)m_MaxElementIndex; --i >= 0; ) - { - // Invoke the destructor - if (IsValidIndex((I)i)) - Destruct( &Element((I)i) ); - - // next points to the next free list item - InternalElement((I)i).m_Next = prev; - - // Indicates it's in the free list - InternalElement((I)i).m_Previous = (I)i; - prev = (I)i; - } - - // First free points to the first element - m_FirstFree = 0; - - // Clear everything else out - for (I list = m_List.Head(); list != m_List.InvalidIndex(); list = m_List.Next(list) ) - { - m_List[list].m_Head = InvalidIndex(); - m_List[list].m_Tail = InvalidIndex(); - m_List[list].m_Count = 0; - } - - m_TotalElements = 0; -} - - -#include "tier0/memdbgoff.h" - -#endif // UTLMULTILIST_H +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: Multiple linked list container class +// +// $Revision: $ +// $NoKeywords: $ +//=============================================================================// + +#ifndef UTLMULTILIST_H +#define UTLMULTILIST_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "utllinkedlist.h" + +// memdbgon must be the last include file in a .h file!!! +#include "tier0/memdbgon.h" + + +// This is a useful macro to iterate from head to tail in a linked list. +#define FOR_EACH_LL( listName, iteratorName ) \ + for( int iteratorName=listName.Head(); iteratorName != listName.InvalidIndex(); iteratorName = listName.Next( iteratorName ) ) + +//----------------------------------------------------------------------------- +// class CUtlMultiList: +// description: +// A lovely index-based linked list! T is the class type, I is the index +// type, which usually should be an unsigned short or smaller. +// This list can contain multiple lists +//----------------------------------------------------------------------------- +template +class CUtlMultiList +{ +public: + typedef I ListHandle_t; + + // constructor, destructor + CUtlMultiList( int growSize = 0, int initSize = 0 ); + CUtlMultiList( void *pMemory, int memsize ); + ~CUtlMultiList( ); + + // gets particular elements + T& Element( I i ); + T const& Element( I i ) const; + T& operator[]( I i ); + T const& operator[]( I i ) const; + + // Make sure we have a particular amount of memory + void EnsureCapacity( int num ); + + // Memory deallocation + void Purge(); + + // List Creation/deletion + ListHandle_t CreateList(); + void DestroyList( ListHandle_t list ); + bool IsValidList( ListHandle_t list ) const; + + // Insertion methods (call default constructor).... + I InsertBefore( ListHandle_t list, I before ); + I InsertAfter( ListHandle_t list, I after ); + I AddToHead( ListHandle_t list ); + I AddToTail( ListHandle_t list ); + + // Insertion methods (call copy constructor).... + I InsertBefore( ListHandle_t list, I before, T const& src ); + I InsertAfter( ListHandle_t list, I after, T const& src ); + I AddToHead( ListHandle_t list, T const& src ); + I AddToTail( ListHandle_t list, T const& src ); + + // Removal methods + void Remove( ListHandle_t list, I elem ); + + // Removes all items in a single list + void RemoveAll( ListHandle_t list ); + + // Removes all items in all lists + void RemoveAll(); + + // Allocation/deallocation methods + // NOTE: To free, it must *not* be in a list! + I Alloc( ); + void Free( I elem ); + + // list modification + void LinkBefore( ListHandle_t list, I before, I elem ); + void LinkAfter( ListHandle_t list, I after, I elem ); + void Unlink( ListHandle_t list, I elem ); + void LinkToHead( ListHandle_t list, I elem ); + void LinkToTail( ListHandle_t list, I elem ); + + // invalid index + static I InvalidIndex() { return (I)~0; } + static size_t ElementSize() { return sizeof(ListElem_t); } + + // list statistics + int Count( ListHandle_t list ) const; + int TotalCount( ) const; + I MaxElementIndex() const; + + // Traversing the list + I Head( ListHandle_t list ) const; + I Tail( ListHandle_t list ) const; + I Previous( I element ) const; + I Next( I element ) const; + + // Are nodes in a list or valid? + bool IsValidIndex( I i ) const; + bool IsInList( I i ) const; + +protected: + // What the linked list element looks like + struct ListElem_t + { + T m_Element; + I m_Previous; + I m_Next; + }; + + struct List_t + { + I m_Head; + I m_Tail; + I m_Count; + }; + + // constructs the class + void ConstructList( ); + + // Gets at the list element.... + ListElem_t& InternalElement( I i ) { return m_Memory[i]; } + ListElem_t const& InternalElement( I i ) const { return m_Memory[i]; } + + // A test for debug mode only... + bool IsElementInList( ListHandle_t list, I elem ) const; + + // copy constructors not allowed + CUtlMultiList( CUtlMultiList const& list ) { Assert(0); } + + CUtlMemory m_Memory; + CUtlLinkedList m_List; + I* m_pElementList; + + I m_FirstFree; + I m_TotalElements; + I m_MaxElementIndex; // The number allocated + +#if !defined(_XBOX) || defined(_DEBUG) + void ResetDbgInfo() + { + m_pElements = m_Memory.Base(); + +#ifdef _DEBUG + // Allocate space for the element list (which list is each element in) + if (m_Memory.NumAllocated() > 0) + { + if (!m_pElementList) + { + m_pElementList = (I*)malloc( m_Memory.NumAllocated() * sizeof(I) ); + } + else + { + m_pElementList = (I*)realloc( m_pElementList, m_Memory.NumAllocated() * sizeof(I) ); + } + } +#endif + } + + // For debugging purposes; + // it's in release builds so this can be used in libraries correctly + ListElem_t *m_pElements; +#else + void ResetDbgInfo() {} +#endif +}; + + +//----------------------------------------------------------------------------- +// constructor, destructor +//----------------------------------------------------------------------------- + +template +CUtlMultiList::CUtlMultiList( int growSize, int initSize ) : + m_Memory(growSize, initSize), m_pElementList(0) +{ + ConstructList(); +} + +template +CUtlMultiList::CUtlMultiList( void* pMemory, int memsize ) : + m_Memory((ListElem_t *)pMemory, memsize/sizeof(ListElem_t)), m_pElementList(0) +{ + ConstructList(); +} + +template +CUtlMultiList::~CUtlMultiList( ) +{ + RemoveAll(); + if (m_pElementList) + free(m_pElementList); +} + +template +void CUtlMultiList::ConstructList( ) +{ + m_FirstFree = InvalidIndex(); + m_TotalElements = 0; + m_MaxElementIndex = 0; + ResetDbgInfo(); +} + + +//----------------------------------------------------------------------------- +// gets particular elements +//----------------------------------------------------------------------------- +template +inline T& CUtlMultiList::Element( I i ) +{ + return m_Memory[i].m_Element; +} + +template +inline T const& CUtlMultiList::Element( I i ) const +{ + return m_Memory[i].m_Element; +} + +template +inline T& CUtlMultiList::operator[]( I i ) +{ + return m_Memory[i].m_Element; +} + +template +inline T const& CUtlMultiList::operator[]( I i ) const +{ + return m_Memory[i].m_Element; +} + + +//----------------------------------------------------------------------------- +// list creation/destruction +//----------------------------------------------------------------------------- +template +typename CUtlMultiList::ListHandle_t CUtlMultiList::CreateList() +{ + ListHandle_t l = m_List.AddToTail(); + m_List[l].m_Head = m_List[l].m_Tail = InvalidIndex(); + m_List[l].m_Count = 0; + return l; +} + +template +void CUtlMultiList::DestroyList( ListHandle_t list ) +{ + Assert( IsValidList(list) ); + RemoveAll( list ); + m_List.Remove(list); +} + +template +bool CUtlMultiList::IsValidList( ListHandle_t list ) const +{ + return m_List.IsValidIndex(list); +} + + +//----------------------------------------------------------------------------- +// list statistics +//----------------------------------------------------------------------------- +template +inline int CUtlMultiList::TotalCount() const +{ + return m_TotalElements; +} + +template +inline int CUtlMultiList::Count( ListHandle_t list ) const +{ + Assert( IsValidList(list) ); + return m_List[list].m_Count; +} + +template +inline I CUtlMultiList::MaxElementIndex() const +{ + return m_MaxElementIndex; +} + + +//----------------------------------------------------------------------------- +// Traversing the list +//----------------------------------------------------------------------------- +template +inline I CUtlMultiList::Head(ListHandle_t list) const +{ + Assert( IsValidList(list) ); + return m_List[list].m_Head; +} + +template +inline I CUtlMultiList::Tail(ListHandle_t list) const +{ + Assert( IsValidList(list) ); + return m_List[list].m_Tail; +} + +template +inline I CUtlMultiList::Previous( I i ) const +{ + Assert( IsValidIndex(i) ); + return InternalElement(i).m_Previous; +} + +template +inline I CUtlMultiList::Next( I i ) const +{ + Assert( IsValidIndex(i) ); + return InternalElement(i).m_Next; +} + + +//----------------------------------------------------------------------------- +// Are nodes in the list or valid? +//----------------------------------------------------------------------------- +template +inline bool CUtlMultiList::IsValidIndex( I i ) const +{ + return (i < m_MaxElementIndex) && (i > 0) && + ((m_Memory[i].m_Previous != i) || (m_Memory[i].m_Next == i)); +} + +template +inline bool CUtlMultiList::IsInList( I i ) const +{ + return (i < m_MaxElementIndex) && (i > 0) && (Previous(i) != i); +} + + +//----------------------------------------------------------------------------- +// Makes sure we have enough memory allocated to store a requested # of elements +//----------------------------------------------------------------------------- +template< class T, class I > +void CUtlMultiList::EnsureCapacity( int num ) +{ + m_Memory.EnsureCapacity(num); + ResetDbgInfo(); +} + + +//----------------------------------------------------------------------------- +// Deallocate memory +//----------------------------------------------------------------------------- +template +void CUtlMultiList::Purge() +{ + RemoveAll(); + m_List.Purge(); + m_Memory.Purge( ); + m_List.Purge(); + m_FirstFree = InvalidIndex(); + m_TotalElements = 0; + m_MaxElementIndex = 0; + ResetDbgInfo(); +} + + +//----------------------------------------------------------------------------- +// Node allocation/deallocation +//----------------------------------------------------------------------------- +template +I CUtlMultiList::Alloc( ) +{ + I elem; + if (m_FirstFree == InvalidIndex()) + { + // Nothing in the free list; add. + // Since nothing is in the free list, m_TotalElements == total # of elements + // the list knows about. + if (m_MaxElementIndex == m_Memory.NumAllocated()) + { + m_Memory.Grow(); + ResetDbgInfo(); + } + + elem = (I)m_MaxElementIndex; + ++m_MaxElementIndex; + } + else + { + elem = m_FirstFree; + m_FirstFree = InternalElement(m_FirstFree).m_Next; + } + + // Mark the element as not being in a list + InternalElement(elem).m_Next = InternalElement(elem).m_Previous = elem; + + ++m_TotalElements; + + Construct( &Element(elem) ); + + return elem; +} + +template +void CUtlMultiList::Free( I elem ) +{ + Assert( IsValidIndex(elem) && !IsInList(elem) ); + Destruct( &Element(elem) ); + InternalElement(elem).m_Next = m_FirstFree; + m_FirstFree = elem; + --m_TotalElements; +} + + +//----------------------------------------------------------------------------- +// A test for debug mode only... +//----------------------------------------------------------------------------- +template +inline bool CUtlMultiList::IsElementInList( ListHandle_t list, I elem ) const +{ + if (!m_pElementList) + return true; + + return m_pElementList[elem] == list; +} + + +//----------------------------------------------------------------------------- +// list modification +//----------------------------------------------------------------------------- +template +void CUtlMultiList::LinkBefore( ListHandle_t list, I before, I elem ) +{ + Assert( IsValidIndex(elem) && IsValidList(list) ); + + // Unlink it if it's in the list at the moment + Unlink(list, elem); + + ListElem_t& newElem = InternalElement(elem); + + // The element *after* our newly linked one is the one we linked before. + newElem.m_Next = before; + + if (before == InvalidIndex()) + { + // In this case, we're linking to the end of the list, so reset the tail + newElem.m_Previous = m_List[list].m_Tail; + m_List[list].m_Tail = elem; + } + else + { + // Here, we're not linking to the end. Set the prev pointer to point to + // the element we're linking. + Assert( IsInList(before) ); + ListElem_t& beforeElem = InternalElement(before); + newElem.m_Previous = beforeElem.m_Previous; + beforeElem.m_Previous = elem; + } + + // Reset the head if we linked to the head of the list + if (newElem.m_Previous == InvalidIndex()) + m_List[list].m_Head = elem; + else + InternalElement(newElem.m_Previous).m_Next = elem; + + // one more element baby + ++m_List[list].m_Count; + + // Store the element into the list + if (m_pElementList) + m_pElementList[elem] = list; +} + +template +void CUtlMultiList::LinkAfter( ListHandle_t list, I after, I elem ) +{ + Assert( IsValidIndex(elem) ); + + // Unlink it if it's in the list at the moment + Unlink(list, elem); + + ListElem_t& newElem = InternalElement(elem); + + // The element *before* our newly linked one is the one we linked after + newElem.m_Previous = after; + if (after == InvalidIndex()) + { + // In this case, we're linking to the head of the list, reset the head + newElem.m_Next = m_List[list].m_Head; + m_List[list].m_Head = elem; + } + else + { + // Here, we're not linking to the end. Set the next pointer to point to + // the element we're linking. + Assert( IsInList(after) ); + ListElem_t& afterElem = InternalElement(after); + newElem.m_Next = afterElem.m_Next; + afterElem.m_Next = elem; + } + + // Reset the tail if we linked to the tail of the list + if (newElem.m_Next == InvalidIndex()) + m_List[list].m_Tail = elem; + else + InternalElement(newElem.m_Next).m_Previous = elem; + + // one more element baby + ++m_List[list].m_Count; + + // Store the element into the list + if (m_pElementList) + m_pElementList[elem] = list; +} + +template +void CUtlMultiList::Unlink( ListHandle_t list, I elem ) +{ + Assert( IsValidIndex(elem) && IsValidList(list) ); + + if (IsInList(elem)) + { + // Make sure the element is in the right list + Assert( IsElementInList( list, elem ) ); + ListElem_t& oldElem = InternalElement(elem); + + // If we're the first guy, reset the head + // otherwise, make our previous node's next pointer = our next + if (oldElem.m_Previous != InvalidIndex()) + InternalElement(oldElem.m_Previous).m_Next = oldElem.m_Next; + else + m_List[list].m_Head = oldElem.m_Next; + + // If we're the last guy, reset the tail + // otherwise, make our next node's prev pointer = our prev + if (oldElem.m_Next != InvalidIndex()) + InternalElement(oldElem.m_Next).m_Previous = oldElem.m_Previous; + else + m_List[list].m_Tail = oldElem.m_Previous; + + // This marks this node as not in the list, + // but not in the free list either + oldElem.m_Previous = oldElem.m_Next = elem; + + // One less puppy + --m_List[list].m_Count; + + // Store the element into the list + if (m_pElementList) + m_pElementList[elem] = m_List.InvalidIndex(); + } +} + +template +inline void CUtlMultiList::LinkToHead( ListHandle_t list, I elem ) +{ + LinkAfter( list, InvalidIndex(), elem ); +} + +template +inline void CUtlMultiList::LinkToTail( ListHandle_t list, I elem ) +{ + LinkBefore( list, InvalidIndex(), elem ); +} + + +//----------------------------------------------------------------------------- +// Insertion methods; allocates and links (uses default constructor) +//----------------------------------------------------------------------------- +template +I CUtlMultiList::InsertBefore( ListHandle_t list, I before ) +{ + // Make a new node + I newNode = Alloc(); + + // Link it in + LinkBefore( list, before, newNode ); + + // Construct the data + Construct( &Element(newNode) ); + + return newNode; +} + +template +I CUtlMultiList::InsertAfter( ListHandle_t list, I after ) +{ + // Make a new node + I newNode = Alloc(); + + // Link it in + LinkAfter( list, after, newNode ); + + // Construct the data + Construct( &Element(newNode) ); + + return newNode; +} + +template +inline I CUtlMultiList::AddToHead( ListHandle_t list ) +{ + return InsertAfter( list, InvalidIndex() ); +} + +template +inline I CUtlMultiList::AddToTail( ListHandle_t list ) +{ + return InsertBefore( list, InvalidIndex() ); +} + + +//----------------------------------------------------------------------------- +// Insertion methods; allocates and links (uses copy constructor) +//----------------------------------------------------------------------------- +template +I CUtlMultiList::InsertBefore( ListHandle_t list, I before, T const& src ) +{ + // Make a new node + I newNode = Alloc(); + + // Link it in + LinkBefore( list, before, newNode ); + + // Construct the data + CopyConstruct( &Element(newNode), src ); + + return newNode; +} + +template +I CUtlMultiList::InsertAfter( ListHandle_t list, I after, T const& src ) +{ + // Make a new node + I newNode = Alloc(); + + // Link it in + LinkAfter( list, after, newNode ); + + // Construct the data + CopyConstruct( &Element(newNode), src ); + + return newNode; +} + +template +inline I CUtlMultiList::AddToHead( ListHandle_t list, T const& src ) +{ + return InsertAfter( list, InvalidIndex(), src ); +} + +template +inline I CUtlMultiList::AddToTail( ListHandle_t list, T const& src ) +{ + return InsertBefore( list, InvalidIndex(), src ); +} + + +//----------------------------------------------------------------------------- +// Removal methods +//----------------------------------------------------------------------------- +template +void CUtlMultiList::Remove( ListHandle_t list, I elem ) +{ + if (IsInList(elem)) + Unlink(list, elem); + Free( elem ); +} + +// Removes all items in a single list +template +void CUtlMultiList::RemoveAll( ListHandle_t list ) +{ + Assert( IsValidList(list) ); + I i = Head(list); + I next; + while( i != InvalidIndex() ) + { + next = Next(i); + Remove(list, i); + i = next; + } +} + + +template +void CUtlMultiList::RemoveAll() +{ + if (m_MaxElementIndex == 0) + return; + + // Put everything into the free list + I prev = InvalidIndex(); + for (int i = (int)m_MaxElementIndex; --i >= 0; ) + { + // Invoke the destructor + if (IsValidIndex((I)i)) + Destruct( &Element((I)i) ); + + // next points to the next free list item + InternalElement((I)i).m_Next = prev; + + // Indicates it's in the free list + InternalElement((I)i).m_Previous = (I)i; + prev = (I)i; + } + + // First free points to the first element + m_FirstFree = 0; + + // Clear everything else out + for (I list = m_List.Head(); list != m_List.InvalidIndex(); list = m_List.Next(list) ) + { + m_List[list].m_Head = InvalidIndex(); + m_List[list].m_Tail = InvalidIndex(); + m_List[list].m_Count = 0; + } + + m_TotalElements = 0; +} + + +#include "tier0/memdbgoff.h" + +#endif // UTLMULTILIST_H diff --git a/public/tier1/utlrbtree.h b/public/tier1/utlrbtree.h index 658f0cac..334071c0 100644 --- a/public/tier1/utlrbtree.h +++ b/public/tier1/utlrbtree.h @@ -10,6 +10,8 @@ #define UTLRBTREE_H #include "tier1/utlmemory.h" +#undef MINMAX_H +#include "minmax.h" //----------------------------------------------------------------------------- // Tool to generate a default compare function for any type that implements @@ -42,7 +44,7 @@ void SetDefLessFunc( RBTREE_T &RBTree ) { #ifdef _WIN32 RBTree.SetLessFunc( DefLessFunc( RBTREE_T::KeyType_t ) ); -#elif _LINUX +#elif defined _LINUX RBTree.SetLessFunc( DefLessFunc( typename RBTREE_T::KeyType_t ) ); #endif } @@ -250,22 +252,23 @@ protected: template inline CUtlRBTree::CUtlRBTree( int growSize, int initSize, const LessFunc_t &lessfunc ) : -m_Elements( growSize, initSize ), m_LessFunc( lessfunc ), +m_Elements( growSize, initSize ), m_Root( InvalidIndex() ), -m_NumElements( 0 ), m_TotalElements( 0 ), -m_FirstFree( InvalidIndex() ) +m_NumElements( 0 ), +m_FirstFree( InvalidIndex() ), +m_TotalElements( 0 ) { ResetDbgInfo(); } template inline CUtlRBTree::CUtlRBTree( const LessFunc_t &lessfunc ) : -m_Elements( 0, 0 ), m_LessFunc( lessfunc ), -m_Root( InvalidIndex() ), -m_NumElements( 0 ), m_TotalElements( 0 ), -m_FirstFree( InvalidIndex() ) +m_Elements( 0, 0 ), +m_Root( InvalidIndex() ), +m_FirstFree( InvalidIndex() ), +m_NumElements( 0 ), m_TotalElements( 0 ) { ResetDbgInfo(); } @@ -1189,6 +1192,7 @@ int CUtlRBTree::Depth( I node ) const int depthright = Depth( RightChild(node) ); int depthleft = Depth( LeftChild(node) ); + return max(depthright, depthleft) + 1; } diff --git a/public/tools/bonelist.cpp b/public/tools/bonelist.cpp index d42810de..fd1bb94d 100644 --- a/public/tools/bonelist.cpp +++ b/public/tools/bonelist.cpp @@ -1,72 +1,72 @@ -//====== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======= -// -// Purpose: -// -//============================================================================= - -#include "cbase.h" -#include "bonelist.h" - -// memdbgon must be the last include file in a .cpp file!!! -#include "tier0/memdbgon.h" - -CBoneList::CBoneList() -{ - m_bShouldDelete = false; - m_nBones = 0; - Q_memset( m_vecPos, 0, sizeof( m_vecPos ) ); - Q_memset( m_quatRot, 0, sizeof( m_quatRot ) ); -} - -void CBoneList::Release() -{ - if (m_bShouldDelete ) - { - delete this; - } - else - { - Warning( "Called Release() on CBoneList not allocated via Alloc() method\n" ); - } -} - -CBoneList *CBoneList::Alloc() -{ - CBoneList *newList = new CBoneList; - Assert( newList ); - if ( newList ) - { - newList->m_bShouldDelete = true; - } - return newList; -} - -CFlexList::CFlexList() -{ - m_bShouldDelete = false; - m_nNumFlexes = 0; - Q_memset( m_flexWeights, 0, sizeof( m_flexWeights ) ); -} - -void CFlexList::Release() -{ - if (m_bShouldDelete ) - { - delete this; - } - else - { - Warning( "Called Release() on CFlexList not allocated via Alloc() method\n" ); - } -} - -CFlexList *CFlexList::Alloc() -{ - CFlexList *newList = new CFlexList; - Assert( newList ); - if ( newList ) - { - newList->m_bShouldDelete = true; - } - return newList; -} \ No newline at end of file +//====== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======= +// +// Purpose: +// +//============================================================================= + +#include "cbase.h" +#include "bonelist.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +CBoneList::CBoneList() +{ + m_bShouldDelete = false; + m_nBones = 0; + Q_memset( m_vecPos, 0, sizeof( m_vecPos ) ); + Q_memset( m_quatRot, 0, sizeof( m_quatRot ) ); +} + +void CBoneList::Release() +{ + if (m_bShouldDelete ) + { + delete this; + } + else + { + Warning( "Called Release() on CBoneList not allocated via Alloc() method\n" ); + } +} + +CBoneList *CBoneList::Alloc() +{ + CBoneList *newList = new CBoneList; + Assert( newList ); + if ( newList ) + { + newList->m_bShouldDelete = true; + } + return newList; +} + +CFlexList::CFlexList() +{ + m_bShouldDelete = false; + m_nNumFlexes = 0; + Q_memset( m_flexWeights, 0, sizeof( m_flexWeights ) ); +} + +void CFlexList::Release() +{ + if (m_bShouldDelete ) + { + delete this; + } + else + { + Warning( "Called Release() on CFlexList not allocated via Alloc() method\n" ); + } +} + +CFlexList *CFlexList::Alloc() +{ + CFlexList *newList = new CFlexList; + Assert( newList ); + if ( newList ) + { + newList->m_bShouldDelete = true; + } + return newList; +} diff --git a/public/trace.h b/public/trace.h index 3c21bddb..ec2dc072 100644 --- a/public/trace.h +++ b/public/trace.h @@ -1,69 +1,69 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $Workfile: $ -// $Date: $ -// -//----------------------------------------------------------------------------- -// $Log: $ -// -// $NoKeywords: $ -//=============================================================================// - -#ifndef TRACE_H -#define TRACE_H - -#ifdef _WIN32 -#pragma once -#endif - - -#include "mathlib.h" - -// Note: These flags need to match the bspfile.h DISPTRI_TAG_* flags. -#define DISPSURF_FLAG_SURFACE (1<<0) -#define DISPSURF_FLAG_WALKABLE (1<<1) -#define DISPSURF_FLAG_BUILDABLE (1<<2) -#define DISPSURF_FLAG_SURFPROP1 (1<<3) -#define DISPSURF_FLAG_SURFPROP2 (1<<4) - -//============================================================================= -// Base Trace Structure -// - shared between engine/game dlls and tools (vrad) -//============================================================================= - -class CBaseTrace -{ -public: - - // Displacement flags tests. - bool IsDispSurface( void ) { return ( ( dispFlags & DISPSURF_FLAG_SURFACE ) != 0 ); } - bool IsDispSurfaceWalkable( void ) { return ( ( dispFlags & DISPSURF_FLAG_WALKABLE ) != 0 ); } - bool IsDispSurfaceBuildable( void ) { return ( ( dispFlags & DISPSURF_FLAG_BUILDABLE ) != 0 ); } - bool IsDispSurfaceProp1( void ) { return ( ( dispFlags & DISPSURF_FLAG_SURFPROP1 ) != 0 ); } - bool IsDispSurfaceProp2( void ) { return ( ( dispFlags & DISPSURF_FLAG_SURFPROP2 ) != 0 ); } - -public: - - // these members are aligned!! - Vector startpos; // start position - Vector endpos; // final position - cplane_t plane; // surface normal at impact - - float fraction; // time completed, 1.0 = didn't hit anything - - int contents; // contents on other side of surface hit - unsigned short dispFlags; // displacement flags for marking surfaces with data - - bool allsolid; // if true, plane is not valid - bool startsolid; // if true, the initial point was in a solid area - - CBaseTrace() {} - -private: - // No copy constructors allowed - CBaseTrace(const CBaseTrace& vOther); -}; - -#endif // TRACE_H \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $Workfile: $ +// $Date: $ +// +//----------------------------------------------------------------------------- +// $Log: $ +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef TRACE_H +#define TRACE_H + +#ifdef _WIN32 +#pragma once +#endif + + +#include "mathlib.h" + +// Note: These flags need to match the bspfile.h DISPTRI_TAG_* flags. +#define DISPSURF_FLAG_SURFACE (1<<0) +#define DISPSURF_FLAG_WALKABLE (1<<1) +#define DISPSURF_FLAG_BUILDABLE (1<<2) +#define DISPSURF_FLAG_SURFPROP1 (1<<3) +#define DISPSURF_FLAG_SURFPROP2 (1<<4) + +//============================================================================= +// Base Trace Structure +// - shared between engine/game dlls and tools (vrad) +//============================================================================= + +class CBaseTrace +{ +public: + + // Displacement flags tests. + bool IsDispSurface( void ) { return ( ( dispFlags & DISPSURF_FLAG_SURFACE ) != 0 ); } + bool IsDispSurfaceWalkable( void ) { return ( ( dispFlags & DISPSURF_FLAG_WALKABLE ) != 0 ); } + bool IsDispSurfaceBuildable( void ) { return ( ( dispFlags & DISPSURF_FLAG_BUILDABLE ) != 0 ); } + bool IsDispSurfaceProp1( void ) { return ( ( dispFlags & DISPSURF_FLAG_SURFPROP1 ) != 0 ); } + bool IsDispSurfaceProp2( void ) { return ( ( dispFlags & DISPSURF_FLAG_SURFPROP2 ) != 0 ); } + +public: + + // these members are aligned!! + Vector startpos; // start position + Vector endpos; // final position + cplane_t plane; // surface normal at impact + + float fraction; // time completed, 1.0 = didn't hit anything + + int contents; // contents on other side of surface hit + unsigned short dispFlags; // displacement flags for marking surfaces with data + + bool allsolid; // if true, plane is not valid + bool startsolid; // if true, the initial point was in a solid area + + CBaseTrace() {} + +private: + // No copy constructors allowed + CBaseTrace(const CBaseTrace& vOther); +}; + +#endif // TRACE_H diff --git a/public/vector.h b/public/vector.h index 334a9ed2..40e51f90 100644 --- a/public/vector.h +++ b/public/vector.h @@ -54,7 +54,7 @@ #ifdef VECTOR_PARANOIA #define CHECK_VALID( _v) Assert( (_v).IsValid() ) #else -#define CHECK_VALID( _v) 0 +#define CHECK_VALID( _v) #endif #ifdef __cplusplus @@ -231,8 +231,8 @@ public: // Initialization void Init(short ix = 0, short iy = 0, short iz = 0, short iw = 0 ); - __m64 &AsM64() { return *(__m64*)&x; } - const __m64 &AsM64() const { return *(const __m64*)&x; } + __m64 &AsM64() { void *addr = &x; return *reinterpret_cast<__m64 *>(addr); } + const __m64 &AsM64() const { const void *addr = &x; return *reinterpret_cast(addr); } // Setter void Set( const ShortVector& vOther ); @@ -405,12 +405,9 @@ Vector RandomVector( vec_t minVal, vec_t maxVal ); //----------------------------------------------------------------------------- inline Vector::Vector(void) { -#ifdef _DEBUG -#ifdef VECTOR_PARANOIA // Initialize to NAN to catch errors x = y = z = VEC_T_NAN; -#endif -#endif + } inline Vector::Vector(vec_t X, vec_t Y, vec_t Z) @@ -724,10 +721,10 @@ FORCEINLINE_VECTOR ShortVector& ShortVector::operator-=(const ShortVector& v) FORCEINLINE_VECTOR ShortVector& ShortVector::operator*=(float fl) { - x *= fl; - y *= fl; - z *= fl; - w *= fl; + x *= static_cast(fl); + y *= static_cast(fl); + z *= static_cast(fl); + w *= static_cast(fl); return *this; } @@ -744,10 +741,10 @@ FORCEINLINE_VECTOR ShortVector& ShortVector::operator/=(float fl) { Assert( fl != 0.0f ); float oofl = 1.0f / fl; - x *= oofl; - y *= oofl; - z *= oofl; - w *= oofl; + x *= static_cast(oofl); + y *= static_cast(oofl); + z *= static_cast(oofl); + w *= static_cast(oofl); return *this; } @@ -764,10 +761,10 @@ FORCEINLINE_VECTOR ShortVector& ShortVector::operator/=(const ShortVector& v) FORCEINLINE_VECTOR void ShortVectorMultiply( const ShortVector& src, float fl, ShortVector& res ) { Assert( IsFinite(fl) ); - res.x = src.x * fl; - res.y = src.y * fl; - res.z = src.z * fl; - res.w = src.w * fl; + res.x = src.x * static_cast(fl); + res.y = src.y * static_cast(fl); + res.z = src.z * static_cast(fl); + res.w = src.w * static_cast(fl); } FORCEINLINE_VECTOR ShortVector ShortVector::operator*(float fl) const diff --git a/public/vector2d.h b/public/vector2d.h index a3335c32..f18e0d7a 100644 --- a/public/vector2d.h +++ b/public/vector2d.h @@ -206,10 +206,8 @@ void Vector2DLerp(const Vector2D& src1, const Vector2D& src2, vec_t t, Vector2D& inline Vector2D::Vector2D(void) { -#ifdef _DEBUG // Initialize to NAN to catch errors x = y = VEC_T_NAN; -#endif } inline Vector2D::Vector2D(vec_t X, vec_t Y) diff --git a/public/vector4d.h b/public/vector4d.h index b95b367f..a1d5e459 100644 --- a/public/vector4d.h +++ b/public/vector4d.h @@ -219,10 +219,8 @@ void Vector4DLerp(Vector4D const& src1, Vector4D const& src2, vec_t t, Vector4D& inline Vector4D::Vector4D(void) { -#ifdef _DEBUG // Initialize to NAN to catch errors x = y = z = w = VEC_T_NAN; -#endif } inline Vector4D::Vector4D(vec_t X, vec_t Y, vec_t Z, vec_t W ) diff --git a/public/vgui_controls/MessageMap.h b/public/vgui_controls/MessageMap.h index ddee80cc..ed768a96 100644 --- a/public/vgui_controls/MessageMap.h +++ b/public/vgui_controls/MessageMap.h @@ -258,10 +258,10 @@ PanelMessageMap *FindOrAddPanelMessageMap( char const *className ); /////////////////////////////////////////////////////////////////////////////////////////////////////////// // no parameters -#define MAP_MESSAGE( type, name, func ) { name, (vgui::MessageFunc_t)(type::func), 0 } +#define MAP_MESSAGE( type, name, func ) { name, (vgui::MessageFunc_t)(&type::func), 0 } // implicit single parameter (params is the data store) -#define MAP_MESSAGE_PARAMS( type, name, func ) { name, (vgui::MessageFunc_t)(type::func), 1, vgui::DATATYPE_KEYVALUES, NULL } +#define MAP_MESSAGE_PARAMS( type, name, func ) { name, (vgui::MessageFunc_t)(&type::func), 1, vgui::DATATYPE_KEYVALUES, NULL } // single parameter #define MAP_MESSAGE_PTR( type, name, func, param1 ) { name, (vgui::MessageFunc_t)(&type::func), 1, vgui::DATATYPE_PTR, param1 } @@ -273,12 +273,12 @@ PanelMessageMap *FindOrAddPanelMessageMap( char const *className ); #define MAP_MESSAGE_CONSTWCHARPTR( type, name, func, param1) { name, (vgui::MessageFunc_t)(&type::func), 1, vgui::DATATYPE_CONSTWCHARPTR, param1 } // two parameters -#define MAP_MESSAGE_INT_INT( type, name, func, param1, param2 ) { name, (vgui::MessageFunc_t)&type::func, 2, vgui::DATATYPE_INT, param1, vgui::DATATYPE_INT, param2 } -#define MAP_MESSAGE_PTR_INT( type, name, func, param1, param2 ) { name, (vgui::MessageFunc_t)&type::func, 2, vgui::DATATYPE_PTR, param1, vgui::DATATYPE_INT, param2 } -#define MAP_MESSAGE_INT_CONSTCHARPTR( type, name, func, param1, param2 ) { name, (vgui::MessageFunc_t)&type::func, 2, vgui::DATATYPE_INT, param1, vgui::DATATYPE_CONSTCHARPTR, param2 } -#define MAP_MESSAGE_PTR_CONSTCHARPTR( type, name, func, param1, param2 ) { name, (vgui::MessageFunc_t)&type::func, 2, vgui::DATATYPE_PTR, param1, vgui::DATATYPE_CONSTCHARPTR, param2 } -#define MAP_MESSAGE_PTR_CONSTWCHARPTR( type, name, func, param1, param2 ) { name, (vgui::MessageFunc_t)&type::func, 2, vgui::DATATYPE_PTR, param1, vgui::DATATYPE_CONSTWCHARPTR, param2 } -#define MAP_MESSAGE_CONSTCHARPTR_CONSTCHARPTR( type, name, func, param1, param2 ) { name, (vgui::MessageFunc_t)&type::func, 2, vgui::DATATYPE_CONSTCHARPTR, param1, vgui::DATATYPE_CONSTCHARPTR, param2 } +#define MAP_MESSAGE_INT_INT( type, name, func, param1, param2 ) { name, (vgui::MessageFunc_t)(&type::func), 2, vgui::DATATYPE_INT, param1, vgui::DATATYPE_INT, param2 } +#define MAP_MESSAGE_PTR_INT( type, name, func, param1, param2 ) { name, (vgui::MessageFunc_t)(&type::func), 2, vgui::DATATYPE_PTR, param1, vgui::DATATYPE_INT, param2 } +#define MAP_MESSAGE_INT_CONSTCHARPTR( type, name, func, param1, param2 ) { name, (vgui::MessageFunc_t)(&type::func), 2, vgui::DATATYPE_INT, param1, vgui::DATATYPE_CONSTCHARPTR, param2 } +#define MAP_MESSAGE_PTR_CONSTCHARPTR( type, name, func, param1, param2 ) { name, (vgui::MessageFunc_t)(&type::func), 2, vgui::DATATYPE_PTR, param1, vgui::DATATYPE_CONSTCHARPTR, param2 } +#define MAP_MESSAGE_PTR_CONSTWCHARPTR( type, name, func, param1, param2 ) { name, (vgui::MessageFunc_t)(&type::func), 2, vgui::DATATYPE_PTR, param1, vgui::DATATYPE_CONSTWCHARPTR, param2 } +#define MAP_MESSAGE_CONSTCHARPTR_CONSTCHARPTR( type, name, func, param1, param2 ) { name, (vgui::MessageFunc_t)(&type::func), 2, vgui::DATATYPE_CONSTCHARPTR, param1, vgui::DATATYPE_CONSTCHARPTR, param2 } // if more parameters are needed, just use MAP_MESSAGE_PARAMS() and pass the keyvalue set into the function diff --git a/public/vmatrix.cpp b/public/vmatrix.cpp index 19118554..6640a197 100644 --- a/public/vmatrix.cpp +++ b/public/vmatrix.cpp @@ -18,7 +18,9 @@ // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" +#ifdef _MSC_VER #pragma warning (disable : 4700) // local variable 'x' used without having been initialized +#endif // ------------------------------------------------------------------------------------------- // // Helper functions. diff --git a/public/vphysics/collision_set.h b/public/vphysics/collision_set.h index b5db3c79..249aef40 100644 --- a/public/vphysics/collision_set.h +++ b/public/vphysics/collision_set.h @@ -1,20 +1,20 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -// -//=============================================================================// - -// A set of collision rules -// NOTE: Defaults to all indices disabled -class IPhysicsCollisionSet -{ -public: - ~IPhysicsCollisionSet() {} - - virtual void EnableCollisions( int index0, int index1 ) = 0; - virtual void DisableCollisions( int index0, int index1 ) = 0; - - virtual bool ShouldCollide( int index0, int index1 ) = 0; -}; \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + +// A set of collision rules +// NOTE: Defaults to all indices disabled +class IPhysicsCollisionSet +{ +public: + ~IPhysicsCollisionSet() {} + + virtual void EnableCollisions( int index0, int index1 ) = 0; + virtual void DisableCollisions( int index0, int index1 ) = 0; + + virtual bool ShouldCollide( int index0, int index1 ) = 0; +}; diff --git a/public/worldsize.h b/public/worldsize.h index e16522e2..4f9bba0c 100644 --- a/public/worldsize.h +++ b/public/worldsize.h @@ -1,42 +1,42 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -// -//=============================================================================// -// worldsize.h -- extent of world and resolution/size of coordinate messages used in engine - -#ifndef WORLDSIZE_H -#define WORLDSIZE_H -#pragma once - - -// These definitions must match the coordinate message sizes in coordsize.h - -// Following values should be +16384, -16384, +15/16, -15/16 -// NOTE THAT IF THIS GOES ANY BIGGER THEN DISK NODES/LEAVES CANNOT USE SHORTS TO STORE THE BOUNDS -#define MAX_COORD_INTEGER (16384) -#define MIN_COORD_INTEGER (-MAX_COORD_INTEGER) -#define MAX_COORD_FRACTION (1.0-(1.0/16.0)) -#define MIN_COORD_FRACTION (-1.0+(1.0/16.0)) - -#define MAX_COORD_FLOAT (16384.0f) -#define MIN_COORD_FLOAT (-MAX_COORD_FLOAT) - -// Width of the coord system, which is TOO BIG to send as a client/server coordinate value -#define COORD_EXTENT (2*MAX_COORD_INTEGER) - -// Maximum traceable distance ( assumes cubic world and trace from one corner to opposite ) -// COORD_EXTENT * sqrt(3) -#define MAX_TRACE_LENGTH ( 1.732050807569 * COORD_EXTENT ) - -// This value is the LONGEST possible range (limited by max valid coordinate number, not 2x) -#define MAX_COORD_RANGE (MAX_COORD_INTEGER) - -#define ASSERT_COORD( v ) Assert( (v.x>=MIN_COORD_INTEGER*2) && (v.x<=MAX_COORD_INTEGER*2) && \ - (v.y>=MIN_COORD_INTEGER*2) && (v.y<=MAX_COORD_INTEGER*2) && \ - (v.z>=MIN_COORD_INTEGER*2) && (v.z<=MAX_COORD_INTEGER*2) ); \ - - -#endif // WORLDSIZE_H \ No newline at end of file +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// +// worldsize.h -- extent of world and resolution/size of coordinate messages used in engine + +#ifndef WORLDSIZE_H +#define WORLDSIZE_H +#pragma once + + +// These definitions must match the coordinate message sizes in coordsize.h + +// Following values should be +16384, -16384, +15/16, -15/16 +// NOTE THAT IF THIS GOES ANY BIGGER THEN DISK NODES/LEAVES CANNOT USE SHORTS TO STORE THE BOUNDS +#define MAX_COORD_INTEGER (16384) +#define MIN_COORD_INTEGER (-MAX_COORD_INTEGER) +#define MAX_COORD_FRACTION (1.0-(1.0/16.0)) +#define MIN_COORD_FRACTION (-1.0+(1.0/16.0)) + +#define MAX_COORD_FLOAT (16384.0f) +#define MIN_COORD_FLOAT (-MAX_COORD_FLOAT) + +// Width of the coord system, which is TOO BIG to send as a client/server coordinate value +#define COORD_EXTENT (2*MAX_COORD_INTEGER) + +// Maximum traceable distance ( assumes cubic world and trace from one corner to opposite ) +// COORD_EXTENT * sqrt(3) +#define MAX_TRACE_LENGTH ( 1.732050807569 * COORD_EXTENT ) + +// This value is the LONGEST possible range (limited by max valid coordinate number, not 2x) +#define MAX_COORD_RANGE (MAX_COORD_INTEGER) + +#define ASSERT_COORD( v ) Assert( (v.x>=MIN_COORD_INTEGER*2) && (v.x<=MAX_COORD_INTEGER*2) && \ + (v.y>=MIN_COORD_INTEGER*2) && (v.y<=MAX_COORD_INTEGER*2) && \ + (v.z>=MIN_COORD_INTEGER*2) && (v.z<=MAX_COORD_INTEGER*2) ); \ + + +#endif // WORLDSIZE_H diff --git a/tier1/KeyValues.cpp b/tier1/KeyValues.cpp index 76d7da88..fea17654 100644 --- a/tier1/KeyValues.cpp +++ b/tier1/KeyValues.cpp @@ -373,7 +373,9 @@ int KeyValues::GetNameSymbol() const //----------------------------------------------------------------------------- // Purpose: Read a single token from buffer (0 terminated) //----------------------------------------------------------------------------- +#ifdef _MSC_VER #pragma warning (disable:4706) +#endif const char *KeyValues::ReadToken( CUtlBuffer &buf, bool &wasQuoted ) { wasQuoted = false; @@ -418,7 +420,7 @@ const char *KeyValues::ReadToken( CUtlBuffer &buf, bool &wasQuoted ) // read in the token until we hit a whitespace or a control character bool bReportedError = false; int nCount = 0; - while ( c = (const char*)buf.PeekGet( sizeof(char), 0 ) ) + while (( c = (const char*)buf.PeekGet( sizeof(char), 0 )) ) { // end of file if ( *c == 0 ) @@ -447,8 +449,9 @@ const char *KeyValues::ReadToken( CUtlBuffer &buf, bool &wasQuoted ) s_pTokenBuf[ nCount ] = 0; return s_pTokenBuf; } +#ifdef _MSC_VER #pragma warning (default:4706) - +#endif //----------------------------------------------------------------------------- @@ -466,8 +469,9 @@ void KeyValues::UsesEscapeSequences(bool state) bool KeyValues::LoadFromFile( IBaseFileSystem *filesystem, const char *resourceName, const char *pathID ) { Assert(filesystem); + #ifndef _LINUX Assert( IsXbox() || ( IsPC() && _heapchk() == _HEAPOK ) ); - + #endif FileHandle_t f = filesystem->Open(resourceName, "rb", pathID); if (!f) return false; @@ -1146,8 +1150,8 @@ const char *KeyValues::GetString( const char *keyName, const char *defaultValue const wchar_t *KeyValues::GetWString( const char *keyName, const wchar_t *defaultValue) { - KeyValues *dat = FindKey( keyName, false ); #ifdef _WIN32 + KeyValues *dat = FindKey( keyName, false ); if ( dat ) { wchar_t wbuf[64]; @@ -1215,7 +1219,7 @@ Color KeyValues::GetColor( const char *keyName ) } else if ( dat->m_iDataType == TYPE_FLOAT ) { - color[0] = dat->m_flValue; + color[0] = static_cast(dat->m_flValue); } else if ( dat->m_iDataType == TYPE_INT ) { diff --git a/tier1/NetAdr.cpp b/tier1/NetAdr.cpp index 603aef67..6700e32b 100644 --- a/tier1/NetAdr.cpp +++ b/tier1/NetAdr.cpp @@ -297,7 +297,7 @@ void netadr_t::SetFromSocket( int hSocket ) struct sockaddr address; int namelen = sizeof(address); - if ( getsockname( hSocket, (struct sockaddr *)&address, (int *)&namelen) == 0 ) + if ( getsockname( hSocket, (struct sockaddr *)&address, (socklen_t *)&namelen) == 0 ) { SetFromSockadr( &address ); } diff --git a/tier1/bitbuf.cpp b/tier1/bitbuf.cpp index 6e3c792d..d986d85c 100644 --- a/tier1/bitbuf.cpp +++ b/tier1/bitbuf.cpp @@ -330,7 +330,7 @@ void bf_write::WriteBitCoord (const float f) VPROF( "bf_write::WriteBitCoord" ); #endif int signbit = (f <= -COORD_RESOLUTION); - int intval = (int)abs(f); + int intval = abs(static_cast(f)); int fractval = abs((int)(f*COORD_DENOMINATOR)) & (COORD_DENOMINATOR-1); @@ -366,7 +366,8 @@ void bf_write::WriteBitFloat(float val) Assert(sizeof(long) == sizeof(float)); Assert(sizeof(float) == 4); - intVal = *((long*)&val); + void *v = &val; + intVal = *reinterpret_cast(v); WriteUBitLong( intVal, 32 ); } diff --git a/tier1/byteswap.cpp b/tier1/byteswap.cpp index 2415f3d9..344acfd1 100644 --- a/tier1/byteswap.cpp +++ b/tier1/byteswap.cpp @@ -102,7 +102,7 @@ template T LowLevelByteSwap( T input ) { T output = input; // To solve the "output may not have been initialized" warning. - for( int i = 0; i < sizeof(T); i++ ) + for( unsigned int i = 0; i < sizeof(T); i++ ) { ((unsigned char* )&output)[i] = ((unsigned char*)&input)[sizeof(T)-(i+1)]; } diff --git a/tier1/characterset.cpp b/tier1/characterset.cpp index 0fa3fced..3914ae0a 100644 --- a/tier1/characterset.cpp +++ b/tier1/characterset.cpp @@ -34,7 +34,7 @@ void CharacterSetBuild( characterset_t *pSetBuffer, const char *pszSetString ) while ( pszSetString[i] ) { - pSetBuffer->set[ pszSetString[i] ] = 1; + pSetBuffer->set[ static_cast(pszSetString[i]) ] = 1; i++; } diff --git a/tier1/generichash.cpp b/tier1/generichash.cpp index 54acefa3..7c07ec39 100644 --- a/tier1/generichash.cpp +++ b/tier1/generichash.cpp @@ -9,7 +9,14 @@ #include "tier0/basetypes.h" #include "tier0/platform.h" #include "generichash.h" + +#if defined _WIN32 && !defined __CTYPE_H_ #include +#endif + +#ifdef _LINUX +#include +#endif //----------------------------------------------------------------------------- // diff --git a/tier1/mempool.cpp b/tier1/mempool.cpp index ab9e1908..8251ba96 100644 --- a/tier1/mempool.cpp +++ b/tier1/mempool.cpp @@ -41,7 +41,7 @@ CMemoryPool::CMemoryPool(int blockSize, int numElements, int growMode, const cha } #endif - m_BlockSize = blockSize < sizeof(void*) ? sizeof(void*) : blockSize; + m_BlockSize = blockSize < static_cast(sizeof(void*)) ? sizeof(void*) : blockSize; m_BlocksPerBlob = numElements; m_PeakAlloc = 0; m_GrowMode = growMode; diff --git a/tier1/memstack.cpp b/tier1/memstack.cpp index d1ea4d4c..5e1a1008 100644 --- a/tier1/memstack.cpp +++ b/tier1/memstack.cpp @@ -37,16 +37,18 @@ inline T MemAlign( T val, unsigned alignment ) //----------------------------------------------------------------------------- CMemoryStack::CMemoryStack() - : m_pBase( NULL ), - m_pNextAlloc( NULL ), - m_pAllocLimit( NULL ), + : m_pNextAlloc( NULL ), m_pCommitLimit( NULL ), + m_pAllocLimit( NULL ), + m_pBase( NULL ), + m_maxSize( 0 ), +#if defined (_LINUX) + m_alignment( 16 ) +#elif defined(_WIN32) m_alignment( 16 ), -#if defined(_WIN32) m_commitSize( 0 ), - m_minCommit( 0 ), + m_minCommit( 0 ) #endif - m_maxSize( 0 ) { } diff --git a/tier1/strtools.cpp b/tier1/strtools.cpp index 9ecdf0db..374b7b67 100644 --- a/tier1/strtools.cpp +++ b/tier1/strtools.cpp @@ -183,7 +183,7 @@ char *_V_strstr(const char* file, int line, const char *s1, const char *search AssertValidStringPtr( s1 ); AssertValidStringPtr( search ); - // Casting the args to (char*) in order to get the overload of strstr() that returns a (char*) instead of (const char *). This is a changed brought about by VS2005. + // Casting the args to (char*) in order to get the overload of strstr() that returns a (char*) instead of (const char *). This is a change brought about by VS2005. return strstr( (char*)s1, (char*)search ); } @@ -829,52 +829,52 @@ char *V_pretifynum( int64 value ) } // Render quadrillions - if ( value >= 1000000000000 ) + if ( value >= 1000000000000LL ) { char *pchRender = out + V_strlen( out ); - V_snprintf( pchRender, 32, "%d,", value / 1000000000000 ); + V_snprintf( pchRender, 32, "%d,", value / 1000000000000LL ); } // Render trillions - if ( value >= 1000000000000 ) + if ( value >= 1000000000000LL ) { char *pchRender = out + V_strlen( out ); - V_snprintf( pchRender, 32, "%d,", value / 1000000000000 ); + V_snprintf( pchRender, 32, "%d,", value / 1000000000000LL ); } // Render billions - if ( value >= 1000000000 ) + if ( value >= 1000000000LL ) { char *pchRender = out + V_strlen( out ); - V_snprintf( pchRender, 32, "%d,", value / 1000000000 ); + V_snprintf( pchRender, 32, "%d,", value / 1000000000LL ); } // Render millions - if ( value >= 1000000 ) + if ( value >= 1000000LL ) { char *pchRender = out + V_strlen( out ); if ( value >= 1000000000 ) - V_snprintf( pchRender, 32, "%03d,", ( value / 1000000 ) % 1000 ); + V_snprintf( pchRender, 32, "%03d,", ( value / 1000000LL ) % 1000LL ); else - V_snprintf( pchRender, 32, "%d,", ( value / 1000000 ) % 1000 ); + V_snprintf( pchRender, 32, "%d,", ( value / 1000000LL ) % 1000LL ); } // Render thousands - if ( value >= 1000 ) + if ( value >= 1000LL ) { char *pchRender = out + V_strlen( out ); if ( value >= 1000000 ) - V_snprintf( pchRender, 32, "%03d,", ( value / 1000 ) % 1000 ); + V_snprintf( pchRender, 32, "%03d,", ( value / 1000LL ) % 1000LL ); else - V_snprintf( pchRender, 32, "%d,", ( value / 1000 ) % 1000 ); + V_snprintf( pchRender, 32, "%d,", ( value / 1000LL ) % 1000LL ); } // Render units char *pchRender = out + V_strlen( out ); - if ( value > 1000 ) - V_snprintf( pchRender, 32, "%03d", value % 1000 ); + if ( value > 1000LL ) + V_snprintf( pchRender, 32, "%03d", value % 1000LL ); else - V_snprintf( pchRender, 32, "%d", value % 1000 ); + V_snprintf( pchRender, 32, "%d", value % 1000LL ); return out; } @@ -1338,7 +1338,7 @@ bool V_ExtractFilePath (const char *path, char *dest, int destSize ) //----------------------------------------------------------------------------- void V_ExtractFileExtension( const char *path, char *dest, int destSize ) { - *dest = NULL; + *dest = '\0'; const char * extension = V_GetFileExtension( path ); if ( NULL != extension ) V_strncpy( dest, extension, destSize ); diff --git a/tier1/utlbuffer.cpp b/tier1/utlbuffer.cpp index deae90f8..cb10a3b9 100644 --- a/tier1/utlbuffer.cpp +++ b/tier1/utlbuffer.cpp @@ -6,7 +6,7 @@ // Serialization buffer //===========================================================================// -#ifndef _XBOX +#if !defined _XBOX && defined _MSC_VER #pragma warning (disable : 4514) #endif #include "utlbuffer.h" @@ -92,14 +92,14 @@ CUtlCStringConversion::CUtlCStringConversion( char nEscapeChar, const char *pDel memset( m_pConversion, 0x0, sizeof(m_pConversion) ); for ( int i = 0; i < nCount; ++i ) { - m_pConversion[ pArray[i].m_pReplacementString[0] ] = pArray[i].m_nActualChar; + m_pConversion[ static_cast(pArray[i].m_pReplacementString[0]) ] = pArray[i].m_nActualChar; } } // Finds a conversion for the passed-in string, returns length char CUtlCStringConversion::FindConversion( const char *pString, int *pLength ) { - char c = m_pConversion[ pString[0] ]; + char c = m_pConversion[ static_cast(pString[0]) ]; *pLength = (c != '\0') ? 1 : 0; return c; } @@ -122,7 +122,7 @@ CUtlCharConversion::CUtlCharConversion( char nEscapeChar, const char *pDelimiter for ( int i = 0; i < nCount; ++i ) { m_pList[i] = pArray[i].m_nActualChar; - ConversionInfo_t &info = m_pReplacements[ m_pList[i] ]; + ConversionInfo_t &info = m_pReplacements[ static_cast(m_pList[i]) ]; Assert( info.m_pReplacementString == 0 ); info.m_pReplacementString = pArray[i].m_pReplacementString; info.m_nLength = Q_strlen( info.m_pReplacementString ); @@ -158,12 +158,12 @@ int CUtlCharConversion::GetDelimiterLength() const //----------------------------------------------------------------------------- const char *CUtlCharConversion::GetConversionString( char c ) const { - return m_pReplacements[ c ].m_pReplacementString; + return m_pReplacements[ static_cast(c) ].m_pReplacementString; } int CUtlCharConversion::GetConversionLength( char c ) const { - return m_pReplacements[ c ].m_nLength; + return m_pReplacements[ static_cast(c) ].m_nLength; } int CUtlCharConversion::MaxConversionLength() const @@ -179,9 +179,9 @@ char CUtlCharConversion::FindConversion( const char *pString, int *pLength ) { for ( int i = 0; i < m_nCount; ++i ) { - if ( !Q_strcmp( pString, m_pReplacements[ m_pList[i] ].m_pReplacementString ) ) + if ( !Q_strcmp( pString, m_pReplacements[ static_cast(m_pList[i]) ].m_pReplacementString ) ) { - *pLength = m_pReplacements[ m_pList[i] ].m_nLength; + *pLength = m_pReplacements[ static_cast(m_pList[i]) ].m_nLength; return m_pList[i]; } } @@ -872,7 +872,9 @@ void CUtlBuffer::SeekGet( SeekType_t type, int offset ) // Parse... //----------------------------------------------------------------------------- +#ifdef _MSC_VER #pragma warning ( disable : 4706 ) +#endif int CUtlBuffer::VaScanf( const char* pFmt, va_list list ) { @@ -884,7 +886,7 @@ int CUtlBuffer::VaScanf( const char* pFmt, va_list list ) int nLength; char c; char* pEnd; - while ( c = *pFmt++ ) + while (( c = *pFmt++ )) { // Stop if we hit the end of the buffer if ( m_Get >= TellMaxPut() ) @@ -1043,7 +1045,9 @@ int CUtlBuffer::VaScanf( const char* pFmt, va_list list ) return numScanned; } +#ifdef _MSC_VER #pragma warning ( default : 4706 ) +#endif int CUtlBuffer::Scanf( const char* pFmt, ... ) { diff --git a/tier1/utlsymbol.cpp b/tier1/utlsymbol.cpp index 4c8e98fa..92b2f2a5 100644 --- a/tier1/utlsymbol.cpp +++ b/tier1/utlsymbol.cpp @@ -6,7 +6,7 @@ // $NoKeywords: $ //=============================================================================// -#ifndef _XBOX +#if !defined _XBOX && defined _MSC_VER #pragma warning (disable:4514) #endif @@ -300,7 +300,8 @@ FileNameHandle_t CUtlFilenameSymbolTable::FindOrAddFileName( char const *pFileNa handle.path = g_CountedStringPool.ReferenceStringHandle(basepath); handle.file = g_CountedStringPool.ReferenceStringHandle(filename ); - return *( FileNameHandle_t * )( &handle ); + void *h = &handle; + return *reinterpret_cast(h); } FileNameHandle_t CUtlFilenameSymbolTable::FindFileName( char const *pFileName ) @@ -329,10 +330,11 @@ FileNameHandle_t CUtlFilenameSymbolTable::FindFileName( char const *pFileName ) handle.path = g_CountedStringPool.FindStringHandle(basepath); handle.file = g_CountedStringPool.FindStringHandle(filename); - if( handle.path == NULL || handle.file == NULL ) + if( handle.path == 0 || handle.file == 0 ) return NULL; - return *( FileNameHandle_t * )( &handle ); + void *h = &handle; + return *reinterpret_cast(h); } //----------------------------------------------------------------------------- diff --git a/utils/demoinfo/demoinfo-2003.vcproj b/utils/demoinfo/demoinfo-2003.vcproj index 4212cfa0..10a46b88 100644 --- a/utils/demoinfo/demoinfo-2003.vcproj +++ b/utils/demoinfo/demoinfo-2003.vcproj @@ -37,19 +37,19 @@ CompileAs="0"/> + Outputs="..\..\..\bin\demoinfo.exe;..\..\..\bin\demoinfo.pdb"/> @@ -105,12 +105,12 @@ copy "$(TargetPath)" ..\..\..\game\bin\"$(TargetName)".pdb CompileAs="0"/> + Outputs="..\..\..\bin\demoinfo.exe;..\..\..\bin\demoinfo.pdb"/> + Name="Link Libraries" + Filter=""> + + + + + + + + - - - - - - - - diff --git a/utils/demoinfo/demoinfo-2005.vcproj b/utils/demoinfo/demoinfo-2005.vcproj index e9421391..82cd263a 100644 --- a/utils/demoinfo/demoinfo-2005.vcproj +++ b/utils/demoinfo/demoinfo-2005.vcproj @@ -4,6 +4,7 @@ Version="8.00" Name="demoinfo" ProjectGUID="{4FE3FDCA-9571-44B3-A521-C81448434490}" + RootNamespace="demoinfo" > + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - diff --git a/utils/glview/glview-2003.vcproj b/utils/glview/glview-2003.vcproj index 53ab2fc3..65869660 100644 --- a/utils/glview/glview-2003.vcproj +++ b/utils/glview/glview-2003.vcproj @@ -37,21 +37,21 @@ CompileAs="0"/> + Outputs="..\..\..\bin\glview.exe;..\..\..\bin\glview.pdb"/> + Outputs="..\..\..\bin\glview.exe;..\..\..\bin\glview.pdb"/> + + + + + + + + + + - - - - - - - - - - diff --git a/utils/glview/glview-2005.vcproj b/utils/glview/glview-2005.vcproj index a701a1f4..18b810c6 100644 --- a/utils/glview/glview-2005.vcproj +++ b/utils/glview/glview-2005.vcproj @@ -4,6 +4,7 @@ Version="8.00" Name="glview" ProjectGUID="{BD1604CA-F401-4C4B-821C-251F5AE157FE}" + RootNamespace="glview" > + Outputs="..\..\..\bin\$(TargetName).exe;..\..\..\bin\$(TargetName).pdb"/> + Outputs="..\..\..\bin\$(TargetName).exe;..\..\..\bin\$(TargetName).pdb"/> - - diff --git a/utils/height2normal/height2normal-2005.vcproj b/utils/height2normal/height2normal-2005.vcproj index 5ab57e89..a3506458 100644 --- a/utils/height2normal/height2normal-2005.vcproj +++ b/utils/height2normal/height2normal-2005.vcproj @@ -4,6 +4,7 @@ Version="8.00" Name="height2normal" ProjectGUID="{0FDD99E4-130F-493C-8202-4C0236CC47EA}" + RootNamespace="height2normal" > - - diff --git a/utils/hlmv/controlpanel.cpp b/utils/hlmv/controlpanel.cpp index 04efc749..94581b59 100644 --- a/utils/hlmv/controlpanel.cpp +++ b/utils/hlmv/controlpanel.cpp @@ -22,6 +22,11 @@ // email: mete@swissquake.ch // web: http://www.swissquake.ch/chumbalum-soft/ // +#include +#include +#include +#include +#include #include "ControlPanel.h" #include "ViewerSettings.h" #include "StudioModel.h" @@ -29,11 +34,6 @@ #include "vphysics/constraints.h" #include "physmesh.h" #include "sys.h" -#include -#include -#include -#include -#include #include "vphysics_interface.h" #include "UtlVector.h" #include "UtlSymbol.h" diff --git a/utils/hlmv/hlmv-2005.vcproj b/utils/hlmv/hlmv-2005.vcproj index c10527d2..bc5f2217 100644 --- a/utils/hlmv/hlmv-2005.vcproj +++ b/utils/hlmv/hlmv-2005.vcproj @@ -4,6 +4,7 @@ Version="8.00" Name="hlmv" ProjectGUID="{F3704DBF-8055-413C-B027-399E5DBCA4DD}" + RootNamespace="hlmv" > #include #include +#include "mx/mx.h" #include "StudioModel.h" #include "vphysics/constraints.h" #include "physmesh.h" @@ -21,7 +22,6 @@ #include "ViewerSettings.h" #include "bone_setup.h" #include "UtlMemory.h" -#include "mx/mx.h" #include "filesystem.h" #include "IStudioRender.h" #include "materialsystem/IMaterialSystemHardwareConfig.h" diff --git a/utils/hlmv/viewersettings.cpp b/utils/hlmv/viewersettings.cpp index fdf8d25b..451ecb86 100644 --- a/utils/hlmv/viewersettings.cpp +++ b/utils/hlmv/viewersettings.cpp @@ -22,12 +22,12 @@ // email: mete@swissquake.ch // web: http://www.swissquake.ch/chumbalum-soft/ // -#include "ViewerSettings.h" -#include "studiomodel.h" #include #include #include -#include "windows.h" +#include +#include "ViewerSettings.h" +#include "studiomodel.h" ViewerSettings g_viewerSettings; diff --git a/utils/motionmapper/motionmapper-2003.vcproj b/utils/motionmapper/motionmapper-2003.vcproj index 1777b6b1..0248d36b 100644 --- a/utils/motionmapper/motionmapper-2003.vcproj +++ b/utils/motionmapper/motionmapper-2003.vcproj @@ -36,12 +36,12 @@ + Outputs="..\..\..\bin\motionmapper.exe;..\..\..\bin\motionmapper.pdb"/> + Outputs="..\..\..\bin\motionmapper.exe;..\..\..\bin\motionmapper.pdb"/> @@ -285,42 +286,38 @@ if exist "$(TargetDir)""$(TargetName)".map copy "$(Targ + Name="Link Libraries" + Filter=""> + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - diff --git a/utils/phonemeextractor/phonemeextractor-2005.vcproj b/utils/phonemeextractor/phonemeextractor-2005.vcproj index 49a15c96..99489bed 100644 --- a/utils/phonemeextractor/phonemeextractor-2005.vcproj +++ b/utils/phonemeextractor/phonemeextractor-2005.vcproj @@ -4,6 +4,7 @@ Version="8.00" Name="phonemeextractor" ProjectGUID="{8A35F55B-B5C2-47A0-8C4A-5857A8E20385}" + RootNamespace="phonemeextractor" > - - - - - - - - - - - - - - - - @@ -270,18 +239,6 @@ RelativePath="..\common\filesystem_tools.cpp" > - - - - - - @@ -290,10 +247,6 @@ RelativePath=".\phonemeconverter.cpp" > - - @@ -314,6 +267,121 @@ /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -334,10 +402,6 @@ RelativePath="..\sapi51\Include\sapiddk.h" > - - @@ -395,70 +459,6 @@ > - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/utils/qc_eyes/QC_Eyes-2003.vcproj b/utils/qc_eyes/QC_Eyes-2003.vcproj index 0866fc2a..11289d4d 100644 --- a/utils/qc_eyes/QC_Eyes-2003.vcproj +++ b/utils/qc_eyes/QC_Eyes-2003.vcproj @@ -39,17 +39,18 @@ + Outputs="..\..\..\bin\qc_eyes.exe;..\..\..\bin\qc_eyes.pdb"/> @@ -108,17 +109,18 @@ copy "$(TargetPath)" ..\..\..\game\bin\"$(TargetName)".pdb + Outputs="..\..\..\bin\qc_eyes.exe;..\..\..\bin\qc_eyes.pdb"/> - - diff --git a/utils/qc_eyes/QC_Eyes-2005.vcproj b/utils/qc_eyes/QC_Eyes-2005.vcproj index 46903ba4..da1ad193 100644 --- a/utils/qc_eyes/QC_Eyes-2005.vcproj +++ b/utils/qc_eyes/QC_Eyes-2005.vcproj @@ -4,6 +4,7 @@ Version="8.00" Name="QC_Eyes" ProjectGUID="{D373436F-7DBF-468B-A3E4-601FB8556544}" + RootNamespace="QC_Eyes" Keyword="MFCProj" > @@ -29,8 +30,8 @@ /> - - diff --git a/utils/qc_eyes/QC_Eyes.cpp b/utils/qc_eyes/QC_Eyes.cpp index 16e06ed5..b50bcc5f 100644 --- a/utils/qc_eyes/QC_Eyes.cpp +++ b/utils/qc_eyes/QC_Eyes.cpp @@ -43,17 +43,6 @@ BOOL CQC_EyesApp::InitInstance() { AfxEnableControlContainer(); - // Standard initialization - // If you are not using these features and wish to reduce the size - // of your final executable, you should remove from the following - // the specific initialization routines you do not need. - -#ifdef _AFXDLL - Enable3dControls(); // Call this when using MFC in a shared DLL -#else - Enable3dControlsStatic(); // Call this when linking to MFC statically -#endif - CQC_EyesDlg dlg; m_pMainWnd = &dlg; int nResponse = dlg.DoModal(); diff --git a/utils/serverplugin_sample/serverplugin_bot.cpp b/utils/serverplugin_sample/serverplugin_bot.cpp index db58a68e..67e44db7 100644 --- a/utils/serverplugin_sample/serverplugin_bot.cpp +++ b/utils/serverplugin_sample/serverplugin_bot.cpp @@ -185,7 +185,7 @@ void Bot_UpdateDirection( CPluginBot *pBot ) { float angledelta = 15.0; - int maxtries = (int)360.0/angledelta; + int maxtries = static_cast(360.0 / angledelta); if ( pBot->m_bLastTurnToRight ) { diff --git a/utils/serverplugin_sample/serverplugin_empty-2003.vcproj b/utils/serverplugin_sample/serverplugin_empty-2003.vcproj index 51fe3cba..ab715fc7 100644 --- a/utils/serverplugin_sample/serverplugin_empty-2003.vcproj +++ b/utils/serverplugin_sample/serverplugin_empty-2003.vcproj @@ -40,10 +40,10 @@ CompileAs="0"/> + Outputs="..\..\..\bin\serverplugin_empty.dll"/> + Outputs="..\..\..\bin\serverplugin_empty.dll"/> + Outputs="..\..\..\bin\studiomdl.exe;..\..\..\bin\studiomdl.pdb"/> + Outputs="..\..\..\bin\studiomdl.exe;..\..\..\bin\studiomdl.pdb"/> + Outputs="..\..\..\bin\$(TargetName).exe;..\..\..\bin\$(TargetName).pdb"/> @@ -116,18 +116,18 @@ copy "$(TargetPath)" ..\..\..\game\bin\"$(TargetName)".pdb CompileAs="0"/> + Outputs="..\..\..\bin\$(TargetName).exe;..\..\..\bin\$(TargetName).pdb"/> + Outputs="..\..\..\bin\$(TargetName).exe;..\..\..\bin\$(TargetName).pdb"/> + Outputs="..\..\..\bin\$(TargetName).exe;..\..\..\bin\$(TargetName).pdb"/> - - diff --git a/utils/vbsp/vbsp-2005.vcproj b/utils/vbsp/vbsp-2005.vcproj index c4c884c0..01b8509d 100644 --- a/utils/vbsp/vbsp-2005.vcproj +++ b/utils/vbsp/vbsp-2005.vcproj @@ -4,6 +4,7 @@ Version="8.00" Name="vbsp" ProjectGUID="{B78B6271-B19A-4CF6-926E-40B643548E23}" + RootNamespace="vbsp" > - - diff --git a/utils/vice/vice-2003.vcproj b/utils/vice/vice-2003.vcproj index da86e1d4..82592d9a 100644 --- a/utils/vice/vice-2003.vcproj +++ b/utils/vice/vice-2003.vcproj @@ -39,18 +39,18 @@ CompileAs="0"/> + Outputs="..\..\..\bin\$(TargetName).exe;..\..\..\bin\$(TargetName).pdb"/> + Outputs="..\..\..\bin\$(TargetName).exe..\..\..\bin\$(TargetName).pdb"/> @@ -190,10 +190,6 @@ copy "$(TargetPath)" ..\..\..\game\bin\"$(TargetName)".pdb RelativePath="..\..\public\IceKey.H"> - - @@ -210,9 +206,6 @@ copy "$(TargetPath)" ..\..\..\game\bin\"$(TargetName)".pdb RelativePath="..\..\lib-vc7\public\vstdlib.lib"> - - diff --git a/utils/vice/vice-2005.vcproj b/utils/vice/vice-2005.vcproj index 4ab6caeb..99199e84 100644 --- a/utils/vice/vice-2005.vcproj +++ b/utils/vice/vice-2005.vcproj @@ -4,6 +4,7 @@ Version="8.00" Name="vice" ProjectGUID="{3CE6E7A9-89EC-4304-8D72-5B602B4DBD09}" + RootNamespace="vice" > - - @@ -312,10 +308,6 @@ - - diff --git a/utils/vice/vice.cpp b/utils/vice/vice.cpp index 4cd1fe77..b24bf1ca 100644 --- a/utils/vice/vice.cpp +++ b/utils/vice/vice.cpp @@ -8,10 +8,13 @@ // vice.cpp : Defines the entry point for the console application. // +#undef GetCurrentDirectory + #include #include #include #include +#include "windows.h" #include "vstdlib/strtools.h" #include #include "conio.h" @@ -21,7 +24,6 @@ #include "tier0/dbg.h" #include "cmdlib.h" #include "vstdlib/icommandline.h" -#include "windows.h" #include #include @@ -29,8 +31,6 @@ #define FF_TRYAGAIN 1 #define FF_DONTPROCESS 2 -#undef GetCurrentDirectory - static bool g_NoPause = false; static bool g_Quiet = false; static bool g_Encrypt = false; diff --git a/utils/vprojtomake/makefilecreator.cpp b/utils/vprojtomake/makefilecreator.cpp index 2d2c9f55..33d6f1ba 100644 --- a/utils/vprojtomake/makefilecreator.cpp +++ b/utils/vprojtomake/makefilecreator.cpp @@ -1,279 +1,279 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -//=============================================================================// -#ifdef _WIN32 -#include -#endif -#include "stdafx.h" -#include "makefilecreator.h" -#include "cmdlib.h" - -//----------------------------------------------------------------------------- -// Purpose: constructor -//----------------------------------------------------------------------------- -CMakefileCreator::CMakefileCreator() { - m_FileToBaseDirMapping.SetLessFunc( DefLessFunc( int ) ); -} - -//----------------------------------------------------------------------------- -// Purpose: destructor -//----------------------------------------------------------------------------- -CMakefileCreator::~CMakefileCreator() -{ -} - -void CMakefileCreator::CreateMakefiles( CVCProjConvert & proj ) -{ - m_ProjName = proj.GetName(); - m_BaseDir = proj.GetBaseDir(); - for ( int i = 0; i < proj.GetNumConfigurations(); i++ ) - { - m_FileToBaseDirMapping.RemoveAll(); - m_BuildDirectories.RemoveAll(); - m_BaseDirs.RemoveAll(); - - CreateMakefileName( proj.GetName().String(), proj.GetConfiguration(i) ); - CreateBaseDirs( proj.GetConfiguration(i) ); - FileHandle_t f = g_pFileSystem->Open( m_MakefileName.String(), "w+" ); - if ( !f ) - { - Warning( "failed to open %s for writing.\n", m_MakefileName.String() ); - continue; - } - OutputDirs(f); - OutputIncludes( proj.GetConfiguration(i), f ); - OutputObjLists( proj.GetConfiguration(i), f ); - OutputMainBuilder(f); - OutputBuildTarget(f); - g_pFileSystem->Close(f); - } -} - - -void CMakefileCreator::CreateBaseDirs( CVCProjConvert::CConfiguration & config ) -{ -// m_BaseDirs.Insert( "" ); - for ( int i = 0; i < config.GetNumFileNames(); i++ ) - { - if ( config.GetFileType(i) == CVCProjConvert::CConfiguration::FILE_SOURCE ) - { - char basedir[ MAX_PATH ]; - char fulldir[ MAX_PATH ]; - Q_snprintf( fulldir, sizeof(fulldir), "%s/%s", m_BaseDir.String(), config.GetFileName(i) ); - if ( Q_ExtractFilePath( fulldir, basedir, sizeof(basedir) ) ) - { - Q_FixSlashes( basedir ); - Q_StripTrailingSlash( basedir ); - int index = m_BaseDirs.Find( basedir ); - if ( index == m_BaseDirs.InvalidIndex() ) - { - index = m_BaseDirs.Insert( basedir ); - } - m_FileToBaseDirMapping.Insert(i, index ); - } - else - { - m_FileToBaseDirMapping.Insert(i, 0 ); - } - } - } -} - -void CMakefileCreator::CleanupFileName( char *name ) -{ - for ( int i = Q_strlen( name ) - 1; i >= 0; --i ) - { - if ( name[i] == ' ' || name[i] == '|' || name[i] == '\\' || name[i] == '/' || ( name[i] == '.' && i>=1 && name[i-1] == '.' )) - { - Q_memmove( &name[i], &name[i+1], Q_strlen( name ) - i - 1 ); - name[ Q_strlen( name ) - 1 ] = 0; - } - } -} - -void CMakefileCreator::CreateMakefileName( const char *projectName, CVCProjConvert::CConfiguration & config ) -{ - char makefileName[ MAX_PATH ]; - Q_snprintf( makefileName, sizeof(makefileName), "Makefile.%s_%s", projectName, config.GetName().String() ); - CleanupFileName( makefileName ); - m_MakefileName = makefileName; -} - -void CMakefileCreator::CreateDirectoryFriendlyName( const char *dirName, char *friendlyDirName, int friendlyDirNameSize ) -{ - Q_strncpy( friendlyDirName, dirName, friendlyDirNameSize ); - - int i; - for ( i = Q_strlen( friendlyDirName ) - 1; i >= 0; --i ) - { - if ( friendlyDirName[i] == '/' || friendlyDirName[i] == '\\' ) - { - friendlyDirName[i] = '_'; - } - if ( isalpha( friendlyDirName[i] ) ) - { - friendlyDirName[i] = toupper(friendlyDirName[i]); - } - if ( friendlyDirName[i] == '.' ) - { - Q_memmove( &friendlyDirName[i], &friendlyDirName[i+1], Q_strlen( friendlyDirName ) - i - 1 ); - friendlyDirName[ Q_strlen( friendlyDirName ) - 1 ] = 0; - } - } - - // strip any leading/trailing underscores - while ( friendlyDirName[0] == '_' && Q_strlen(friendlyDirName)>0 ) - { - Q_memmove( &friendlyDirName[0], &friendlyDirName[1], Q_strlen( friendlyDirName )- 1 ); - friendlyDirName[ Q_strlen( friendlyDirName ) - 1 ] = 0; - } - while ( Q_strlen(friendlyDirName)>0 && friendlyDirName[Q_strlen(friendlyDirName)-1] == '_' ) - { - friendlyDirName[ Q_strlen( friendlyDirName ) - 1 ] = 0; - } - - CleanupFileName( friendlyDirName ); -} - -void CMakefileCreator::CreateObjDirectoryFriendlyName ( char *name ) -{ -#ifdef _WIN32 - char *updir = "..\\"; -#else - char *updir = "../"; -#endif - - char *sep = Q_strstr( name, updir ); - while ( sep ) - { - Q_strcpy( sep, sep + strlen(updir) ); - sep = Q_strstr( sep, updir ); - } -} - -void CMakefileCreator::FileWrite( FileHandle_t f, const char *fmt, ... ) -{ - va_list args; - va_start(args, fmt); - char stringBuf[ 4096 ]; - Q_vsnprintf( stringBuf, sizeof(stringBuf), fmt, args ); - va_end(args); - g_pFileSystem->Write( stringBuf, Q_strlen(stringBuf), f ); -} - -void CMakefileCreator::OutputIncludes( CVCProjConvert::CConfiguration & config, FileHandle_t f ) -{ - FileWrite( f, "INCLUDES=" ); - for ( int i = 0; i < config.GetNumIncludes(); i++ ) - { - FileWrite( f, "-I%s ", config.GetInclude(i) ); - } - - for ( int i = 0; i < config.GetNumDefines(); i++ ) - { - FileWrite( f, "-D%s ", config.GetDefine(i) ); - } - FileWrite( f, "\n\n" ); -} - -void CMakefileCreator::OutputDirs( FileHandle_t f ) -{ - for ( int i = m_BaseDirs.First(); i != m_BaseDirs.InvalidIndex(); i = m_BaseDirs.Next(i) ) - { - const char *dirName = m_BaseDirs.GetElementName(i); - if ( !dirName || !Q_strlen(dirName) ) - { - dirName = m_BaseDir.String(); - } - - char friendlyDirName[ MAX_PATH ]; - CreateDirectoryFriendlyName( dirName, friendlyDirName, sizeof(friendlyDirName) ); - int dirLen = Q_strlen(friendlyDirName); - Q_strncat( friendlyDirName, "_SRC_DIR", sizeof(friendlyDirName), COPY_ALL_CHARACTERS ); - struct OutputDirMapping_t dirs; - dirs.m_SrcDir = friendlyDirName; - dirs.m_iBaseDirIndex = i; - friendlyDirName[ dirLen ] = 0; - Q_strncat( friendlyDirName, "_OBJ_DIR", sizeof(friendlyDirName), COPY_ALL_CHARACTERS ); - dirs.m_ObjDir = friendlyDirName; - friendlyDirName[ dirLen ] = 0; - Q_strncat( friendlyDirName, "_OBJS", sizeof(friendlyDirName), COPY_ALL_CHARACTERS ); - dirs.m_ObjName = friendlyDirName; - - char objDirName[ MAX_PATH ]; - Q_snprintf( objDirName, sizeof(objDirName) , "obj%c$(NAME)_$(ARCH)%c", CORRECT_PATH_SEPARATOR, CORRECT_PATH_SEPARATOR ); - Q_strncat( objDirName, dirName, sizeof(objDirName), COPY_ALL_CHARACTERS ); - CreateObjDirectoryFriendlyName( objDirName ); - dirs.m_ObjOutputDir = objDirName; - - m_BuildDirectories.AddToTail( dirs ); - - FileWrite( f, "%s=%s\n", dirs.m_SrcDir.String(), dirName ); - FileWrite( f, "%s=%s\n", dirs.m_ObjDir.String(), objDirName ); - } - FileWrite( f, "\n\n" ); -} - -void CMakefileCreator::OutputMainBuilder( FileHandle_t f ) -{ - int i; - FileWrite( f, "\n\nall: dirs $(NAME)_$(ARCH).$(SHLIBEXT)\n\n" ); - FileWrite( f, "dirs:\n" ); - for ( i = 0; i < m_BuildDirectories.Count(); i++ ) - { - FileWrite( f, "\t-mkdir -p $(%s)\n", m_BuildDirectories[i].m_ObjDir.String() ); - } - FileWrite( f, "\n\n" ); - - - FileWrite( f, "\n\n$(NAME)_$(ARCH).$(SHLIBEXT): " ); - for ( i = 0; i < m_BuildDirectories.Count(); i++ ) - { - FileWrite( f, "$(%s) ", m_BuildDirectories[i].m_ObjName.String() ); - } - FileWrite( f, "\n\t$(CLINK) $(SHLIBLDFLAGS) $(DEBUG) -o $(BUILD_DIR)/$@ " ); - for ( i = 0; i < m_BuildDirectories.Count(); i++ ) - { - FileWrite( f, "$(%s) ", m_BuildDirectories[i].m_ObjName.String() ); - } - FileWrite( f, "$(LDFLAGS) $(CPP_LIB)\n\n" ); -} - -void CMakefileCreator::OutputObjLists( CVCProjConvert::CConfiguration & config, FileHandle_t f ) -{ - for ( int buildDirIndex = 0; buildDirIndex < m_BuildDirectories.Count(); buildDirIndex++ ) - { - struct OutputDirMapping_t & dirs = m_BuildDirectories[buildDirIndex]; - FileWrite( f, "%s= \\\n", dirs.m_ObjName.String() ); - - for ( int j = m_FileToBaseDirMapping.FirstInorder(); j != m_FileToBaseDirMapping.InvalidIndex(); j = m_FileToBaseDirMapping.NextInorder(j) ) - { - if ( dirs.m_iBaseDirIndex == m_FileToBaseDirMapping[j] ) - { - char baseName[ MAX_PATH ]; - const char *fileName = config.GetFileName(m_FileToBaseDirMapping.Key(j)); - Q_FileBase( fileName, baseName, sizeof(baseName) ); - Q_SetExtension( baseName, ".o", sizeof(baseName) ); - - FileWrite( f, "\t$(%s)/%s \\\n", dirs.m_ObjDir.String(), baseName ); - } - } - FileWrite( f, "\n\n" ); - } - -} - -void CMakefileCreator::OutputBuildTarget( FileHandle_t f ) -{ - for( int i = 0; i < m_BuildDirectories.Count(); i++ ) - { - struct OutputDirMapping_t & dirs = m_BuildDirectories[i]; - FileWrite( f, "$(%s)/%%.o: $(%s)/%%.cpp\n", dirs.m_ObjDir.String(), dirs.m_SrcDir.String() ); - FileWrite( f, "\t$(DO_CC)\n\n"); - } - -} - +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// +#ifdef _WIN32 +#include +#endif +#include "stdafx.h" +#include "makefilecreator.h" +#include "cmdlib.h" + +//----------------------------------------------------------------------------- +// Purpose: constructor +//----------------------------------------------------------------------------- +CMakefileCreator::CMakefileCreator() { + m_FileToBaseDirMapping.SetLessFunc( DefLessFunc( int ) ); +} + +//----------------------------------------------------------------------------- +// Purpose: destructor +//----------------------------------------------------------------------------- +CMakefileCreator::~CMakefileCreator() +{ +} + +void CMakefileCreator::CreateMakefiles( CVCProjConvert & proj ) +{ + m_ProjName = proj.GetName(); + m_BaseDir = proj.GetBaseDir(); + for ( int i = 0; i < proj.GetNumConfigurations(); i++ ) + { + m_FileToBaseDirMapping.RemoveAll(); + m_BuildDirectories.RemoveAll(); + m_BaseDirs.RemoveAll(); + + CreateMakefileName( proj.GetName().String(), proj.GetConfiguration(i) ); + CreateBaseDirs( proj.GetConfiguration(i) ); + FileHandle_t f = g_pFileSystem->Open( m_MakefileName.String(), "w+" ); + if ( !f ) + { + Warning( "failed to open %s for writing.\n", m_MakefileName.String() ); + continue; + } + OutputDirs(f); + OutputIncludes( proj.GetConfiguration(i), f ); + OutputObjLists( proj.GetConfiguration(i), f ); + OutputMainBuilder(f); + OutputBuildTarget(f); + g_pFileSystem->Close(f); + } +} + + +void CMakefileCreator::CreateBaseDirs( CVCProjConvert::CConfiguration & config ) +{ +// m_BaseDirs.Insert( "" ); + for ( int i = 0; i < config.GetNumFileNames(); i++ ) + { + if ( config.GetFileType(i) == CVCProjConvert::CConfiguration::FILE_SOURCE ) + { + char basedir[ MAX_PATH ]; + char fulldir[ MAX_PATH ]; + Q_snprintf( fulldir, sizeof(fulldir), "%s/%s", m_BaseDir.String(), config.GetFileName(i) ); + if ( Q_ExtractFilePath( fulldir, basedir, sizeof(basedir) ) ) + { + Q_FixSlashes( basedir ); + Q_StripTrailingSlash( basedir ); + int index = m_BaseDirs.Find( basedir ); + if ( index == m_BaseDirs.InvalidIndex() ) + { + index = m_BaseDirs.Insert( basedir ); + } + m_FileToBaseDirMapping.Insert(i, index ); + } + else + { + m_FileToBaseDirMapping.Insert(i, 0 ); + } + } + } +} + +void CMakefileCreator::CleanupFileName( char *name ) +{ + for ( int i = Q_strlen( name ) - 1; i >= 0; --i ) + { + if ( name[i] == ' ' || name[i] == '|' || name[i] == '\\' || name[i] == '/' || ( name[i] == '.' && i>=1 && name[i-1] == '.' )) + { + Q_memmove( &name[i], &name[i+1], Q_strlen( name ) - i - 1 ); + name[ Q_strlen( name ) - 1 ] = 0; + } + } +} + +void CMakefileCreator::CreateMakefileName( const char *projectName, CVCProjConvert::CConfiguration & config ) +{ + char makefileName[ MAX_PATH ]; + Q_snprintf( makefileName, sizeof(makefileName), "Makefile.%s_%s", projectName, config.GetName().String() ); + CleanupFileName( makefileName ); + m_MakefileName = makefileName; +} + +void CMakefileCreator::CreateDirectoryFriendlyName( const char *dirName, char *friendlyDirName, int friendlyDirNameSize ) +{ + Q_strncpy( friendlyDirName, dirName, friendlyDirNameSize ); + + int i; + for ( i = Q_strlen( friendlyDirName ) - 1; i >= 0; --i ) + { + if ( friendlyDirName[i] == '/' || friendlyDirName[i] == '\\' ) + { + friendlyDirName[i] = '_'; + } + if ( isalpha( friendlyDirName[i] ) ) + { + friendlyDirName[i] = toupper(friendlyDirName[i]); + } + if ( friendlyDirName[i] == '.' ) + { + Q_memmove( &friendlyDirName[i], &friendlyDirName[i+1], Q_strlen( friendlyDirName ) - i - 1 ); + friendlyDirName[ Q_strlen( friendlyDirName ) - 1 ] = 0; + } + } + + // strip any leading/trailing underscores + while ( friendlyDirName[0] == '_' && Q_strlen(friendlyDirName)>0 ) + { + Q_memmove( &friendlyDirName[0], &friendlyDirName[1], Q_strlen( friendlyDirName )- 1 ); + friendlyDirName[ Q_strlen( friendlyDirName ) - 1 ] = 0; + } + while ( Q_strlen(friendlyDirName)>0 && friendlyDirName[Q_strlen(friendlyDirName)-1] == '_' ) + { + friendlyDirName[ Q_strlen( friendlyDirName ) - 1 ] = 0; + } + + CleanupFileName( friendlyDirName ); +} + +void CMakefileCreator::CreateObjDirectoryFriendlyName ( char *name ) +{ +#ifdef _WIN32 + char *updir = "..\\"; +#else + char *updir = "../"; +#endif + + char *sep = Q_strstr( name, updir ); + while ( sep ) + { + Q_strcpy( sep, sep + strlen(updir) ); + sep = Q_strstr( sep, updir ); + } +} + +void CMakefileCreator::FileWrite( FileHandle_t f, const char *fmt, ... ) +{ + va_list args; + va_start(args, fmt); + char stringBuf[ 4096 ]; + Q_vsnprintf( stringBuf, sizeof(stringBuf), fmt, args ); + va_end(args); + g_pFileSystem->Write( stringBuf, Q_strlen(stringBuf), f ); +} + +void CMakefileCreator::OutputIncludes( CVCProjConvert::CConfiguration & config, FileHandle_t f ) +{ + FileWrite( f, "INCLUDES=" ); + for ( int i = 0; i < config.GetNumIncludes(); i++ ) + { + FileWrite( f, " -I%s", config.GetInclude(i) ); + } + + for ( int i = 0; i < config.GetNumDefines(); i++ ) + { + FileWrite( f, " -D%s", config.GetDefine(i) ); + } + FileWrite( f, "\n\n" ); +} + +void CMakefileCreator::OutputDirs( FileHandle_t f ) +{ + for ( int i = m_BaseDirs.First(); i != m_BaseDirs.InvalidIndex(); i = m_BaseDirs.Next(i) ) + { + const char *dirName = m_BaseDirs.GetElementName(i); + if ( !dirName || !Q_strlen(dirName) ) + { + dirName = m_BaseDir.String(); + } + + char friendlyDirName[ MAX_PATH ]; + CreateDirectoryFriendlyName( dirName, friendlyDirName, sizeof(friendlyDirName) ); + int dirLen = Q_strlen(friendlyDirName); + Q_strncat( friendlyDirName, "_SRC_DIR", sizeof(friendlyDirName), COPY_ALL_CHARACTERS ); + struct OutputDirMapping_t dirs; + dirs.m_SrcDir = friendlyDirName; + dirs.m_iBaseDirIndex = i; + friendlyDirName[ dirLen ] = 0; + Q_strncat( friendlyDirName, "_OBJ_DIR", sizeof(friendlyDirName), COPY_ALL_CHARACTERS ); + dirs.m_ObjDir = friendlyDirName; + friendlyDirName[ dirLen ] = 0; + Q_strncat( friendlyDirName, "_OBJS", sizeof(friendlyDirName), COPY_ALL_CHARACTERS ); + dirs.m_ObjName = friendlyDirName; + + char objDirName[ MAX_PATH ]; + Q_snprintf( objDirName, sizeof(objDirName) , "obj%c$(NAME)_$(ARCH)%c", CORRECT_PATH_SEPARATOR, CORRECT_PATH_SEPARATOR ); + Q_strncat( objDirName, dirName, sizeof(objDirName), COPY_ALL_CHARACTERS ); + CreateObjDirectoryFriendlyName( objDirName ); + dirs.m_ObjOutputDir = objDirName; + + m_BuildDirectories.AddToTail( dirs ); + + FileWrite( f, "%s=%s\n", dirs.m_SrcDir.String(), dirName ); + FileWrite( f, "%s=%s\n", dirs.m_ObjDir.String(), objDirName ); + } + FileWrite( f, "\n\n" ); +} + +void CMakefileCreator::OutputMainBuilder( FileHandle_t f ) +{ + int i; + FileWrite( f, "\n\nall: dirs $(NAME)_$(ARCH).$(SHLIBEXT)\n\n" ); + FileWrite( f, "dirs:\n" ); + for ( i = 0; i < m_BuildDirectories.Count(); i++ ) + { + FileWrite( f, "\t-mkdir -p $(%s)\n", m_BuildDirectories[i].m_ObjDir.String() ); + } + FileWrite( f, "\n\n" ); + + + FileWrite( f, "\n\n$(NAME)_$(ARCH).$(SHLIBEXT): " ); + for ( i = 0; i < m_BuildDirectories.Count(); i++ ) + { + FileWrite( f, "$(%s) ", m_BuildDirectories[i].m_ObjName.String() ); + } + FileWrite( f, "\n\t$(CLINK) $(SHLIBLDFLAGS) $(DEBUG) -o $(BUILD_DIR)/$@ " ); + for ( i = 0; i < m_BuildDirectories.Count(); i++ ) + { + FileWrite( f, "$(%s) ", m_BuildDirectories[i].m_ObjName.String() ); + } + FileWrite( f, "$(LDFLAGS) $(CPP_LIB)\n\n" ); +} + +void CMakefileCreator::OutputObjLists( CVCProjConvert::CConfiguration & config, FileHandle_t f ) +{ + for ( int buildDirIndex = 0; buildDirIndex < m_BuildDirectories.Count(); buildDirIndex++ ) + { + struct OutputDirMapping_t & dirs = m_BuildDirectories[buildDirIndex]; + FileWrite( f, "%s= \\\n", dirs.m_ObjName.String() ); + + for ( int j = m_FileToBaseDirMapping.FirstInorder(); j != m_FileToBaseDirMapping.InvalidIndex(); j = m_FileToBaseDirMapping.NextInorder(j) ) + { + if ( dirs.m_iBaseDirIndex == m_FileToBaseDirMapping[j] ) + { + char baseName[ MAX_PATH ]; + const char *fileName = config.GetFileName(m_FileToBaseDirMapping.Key(j)); + Q_FileBase( fileName, baseName, sizeof(baseName) ); + Q_SetExtension( baseName, ".o", sizeof(baseName) ); + + FileWrite( f, "\t$(%s)/%s \\\n", dirs.m_ObjDir.String(), baseName ); + } + } + FileWrite( f, "\n\n" ); + } + +} + +void CMakefileCreator::OutputBuildTarget( FileHandle_t f ) +{ + for( int i = 0; i < m_BuildDirectories.Count(); i++ ) + { + struct OutputDirMapping_t & dirs = m_BuildDirectories[i]; + FileWrite( f, "$(%s)/%%.o: $(%s)/%%.cpp\n", dirs.m_ObjDir.String(), dirs.m_SrcDir.String() ); + FileWrite( f, "\t$(DO_CC)\n\n"); + } + +} + diff --git a/utils/vprojtomake/vcprojconvert.cpp b/utils/vprojtomake/vcprojconvert.cpp index 4b8ca511..1e016ee2 100644 --- a/utils/vprojtomake/vcprojconvert.cpp +++ b/utils/vprojtomake/vcprojconvert.cpp @@ -163,17 +163,17 @@ bool CVCProjConvert::LoadProject( const char *project ) char* message = XMLString::transcode(toCatch.getMessage()); Error( "Exception message is: %s\n", message ); XMLString::release(&message); - return; + return false; } catch (const DOMException& toCatch) { char* message = XMLString::transcode(toCatch.msg); Error( "Exception message is: %s\n", message ); XMLString::release(&message); - return; + return false; } catch (...) { Error( "Unexpected Exception \n" ); - return; + return false; } DOMDocument *pXMLDoc = parser->getDocument(); @@ -263,7 +263,7 @@ CUtlSymbol CVCProjConvert::GetXMLAttribValue( IXMLDOMElement *p, const char *att CUtlSymbol name( static_cast( _bstr_t( vtValue.bstrVal ) ) ); ::SysFreeString(vtValue.bstrVal); #elif _LINUX - const XMLCh *xAttrib = XMLString::transcode( attribName ); + XMLCh *xAttrib = XMLString::transcode( attribName ); const XMLCh *value = p->getAttribute( xAttrib ); if ( value == NULL ) { @@ -346,7 +346,7 @@ bool CVCProjConvert::ExtractProjectName( IXMLDOMDocument *pDoc ) DOMNode *node = nodes->item(0); if ( node ) { - m_Name = GetXMLAttribValue( node, "Name" ); + m_Name = GetXMLAttribValue( static_cast(node), "Name" ); } } } @@ -401,13 +401,13 @@ bool CVCProjConvert::ExtractConfigurations( IXMLDOMDocument *pDoc ) DOMNode *node = nodes->item(i); if (node) { - CUtlSymbol configName = GetXMLAttribValue( node, "Name" ); + CUtlSymbol configName = GetXMLAttribValue( static_cast(node), "Name" ); if ( configName.IsValid() ) { int newIndex = m_Configurations.AddToTail(); CConfiguration & config = m_Configurations[newIndex]; config.SetName( configName ); - ExtractIncludes( node, config ); + ExtractIncludes( static_cast(node), config ); } } } @@ -501,10 +501,10 @@ bool CVCProjConvert::ExtractIncludes( IXMLDOMElement *pDoc, CConfiguration & con DOMNode *node = nodes->item(i); if (node) { - CUtlSymbol toolName = GetXMLAttribValue( node, "Name" ); + CUtlSymbol toolName = GetXMLAttribValue( static_cast(node), "Name" ); if ( toolName == "VCCLCompilerTool" ) { - CUtlSymbol defines = GetXMLAttribValue( node, "PreprocessorDefinitions" ); + CUtlSymbol defines = GetXMLAttribValue( static_cast(node), "PreprocessorDefinitions" ); char *str = (char *)_alloca( Q_strlen( defines.String() + 1 )); Assert( str ); Q_strcpy( str, defines.String() ); @@ -529,7 +529,7 @@ bool CVCProjConvert::ExtractIncludes( IXMLDOMElement *pDoc, CConfiguration & con config.AddDefine( curpos ); } - CUtlSymbol includes = GetXMLAttribValue( node, "AdditionalIncludeDirectories" ); + CUtlSymbol includes = GetXMLAttribValue( static_cast(node), "AdditionalIncludeDirectories" ); char *str2 = (char *)_alloca( Q_strlen( includes.String() + 1 )); Assert( str2 ); Q_strcpy( str2, includes.String() ); @@ -614,8 +614,8 @@ bool CVCProjConvert::IterateFileConfigurations( IXMLDOMElement *pFile, CUtlSymbo DOMNode *node = nodes->item(i); if (node) { - CUtlSymbol configName = GetXMLAttribValue( node, "Name"); - CUtlSymbol excluded = GetXMLAttribValue( node ,"ExcludedFromBuild"); + CUtlSymbol configName = GetXMLAttribValue( static_cast(node), "Name"); + CUtlSymbol excluded = GetXMLAttribValue( static_cast(node) ,"ExcludedFromBuild"); if ( configName.IsValid() && excluded.IsValid() ) { int index = FindConfiguration( configName ); @@ -683,7 +683,7 @@ bool CVCProjConvert::ExtractFiles( IXMLDOMDocument *pDoc ) DOMNode *node = nodes->item(i); if (node) { - CUtlSymbol fileName = GetXMLAttribValue(node,"RelativePath"); + CUtlSymbol fileName = GetXMLAttribValue(static_cast(node),"RelativePath"); if ( fileName.IsValid() ) { char fixedFileName[ MAX_PATH ]; @@ -702,7 +702,7 @@ bool CVCProjConvert::ExtractFiles( IXMLDOMDocument *pDoc ) CConfiguration & config = m_Configurations[i]; config.InsertFile( fileEntry ); } - IterateFileConfigurations( node, fixedFileName ); // now remove the excluded ones + IterateFileConfigurations( static_cast(node), fixedFileName ); // now remove the excluded ones } } }//for diff --git a/utils/vprojtomake/vprojtomake-2003.vcproj b/utils/vprojtomake/vprojtomake-2003.vcproj index 4ca9044a..8fa830c4 100644 --- a/utils/vprojtomake/vprojtomake-2003.vcproj +++ b/utils/vprojtomake/vprojtomake-2003.vcproj @@ -41,10 +41,10 @@ CompileAs="0"/> + Outputs="..\..\..\bin\vcpm.exe"/> @@ -110,10 +110,10 @@ if exist "$(TargetPath)" copy "$(TargetPath)" ..\..\..\game\ CompileAs="0"/> + Outputs="..\..\..\bin\vcpm.exe"/> + + @@ -400,24 +403,21 @@ if exist "$(TargetPath)" copy "$(TargetPath)" ..\..\..\game\ + Name="Link Libraries" + Filter=""> + + + + + + + + - - - - - - - - - - diff --git a/utils/vprojtomake/vprojtomake-2005.vcproj b/utils/vprojtomake/vprojtomake-2005.vcproj index 2ee442a3..2b1da322 100644 --- a/utils/vprojtomake/vprojtomake-2005.vcproj +++ b/utils/vprojtomake/vprojtomake-2005.vcproj @@ -4,6 +4,7 @@ Version="8.00" Name="vprojtomake" ProjectGUID="{EA55446E-BC04-491C-A9F0-605DFCBB213A}" + RootNamespace="vprojtomake" > + + @@ -513,66 +518,61 @@ + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - diff --git a/utils/vrad/vrad-2003.vcproj b/utils/vrad/vrad-2003.vcproj index e77d4071..14dd6f4d 100644 --- a/utils/vrad/vrad-2003.vcproj +++ b/utils/vrad/vrad-2003.vcproj @@ -43,16 +43,16 @@ + Outputs="..\..\..\bin\$(TargetName).dll;..\..\..\bin\$(TargetName).pdb"/> + Outputs="..\..\..\bin\$(TargetName).dll;..\..\..\bin\$(TargetName).pdb"/> - - diff --git a/utils/vrad/vrad-2005.vcproj b/utils/vrad/vrad-2005.vcproj index 1abf42a2..cb88f69a 100644 --- a/utils/vrad/vrad-2005.vcproj +++ b/utils/vrad/vrad-2005.vcproj @@ -4,6 +4,7 @@ Version="8.00" Name="vrad" ProjectGUID="{FC0F5DE3-F09F-4EF6-98F9-BA762FFF268D}" + RootNamespace="vrad" > - - diff --git a/utils/vrad_launcher/vrad_launcher-2003.vcproj b/utils/vrad_launcher/vrad_launcher-2003.vcproj index b644efc8..ebaff50a 100644 --- a/utils/vrad_launcher/vrad_launcher-2003.vcproj +++ b/utils/vrad_launcher/vrad_launcher-2003.vcproj @@ -49,16 +49,16 @@ CompileAs="0"/> + Outputs="..\..\..\bin\$(TargetName).exe"/> + Outputs="..\..\..\bin\$(TargetName).exe"/> + Name="Link Libraries" + Filter=""> + + + + + + - - - - - - diff --git a/utils/vrad_launcher/vrad_launcher-2005.vcproj b/utils/vrad_launcher/vrad_launcher-2005.vcproj index 6f368a5f..aa653550 100644 --- a/utils/vrad_launcher/vrad_launcher-2005.vcproj +++ b/utils/vrad_launcher/vrad_launcher-2005.vcproj @@ -4,6 +4,7 @@ Version="8.00" Name="vrad_launcher" ProjectGUID="{914F19DF-64EC-4E7D-8B01-76477BF06479}" + RootNamespace="vrad_launcher" > + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - diff --git a/utils/vtf2tga/vtf2tga-2003.vcproj b/utils/vtf2tga/vtf2tga-2003.vcproj index 24784200..f493f989 100644 --- a/utils/vtf2tga/vtf2tga-2003.vcproj +++ b/utils/vtf2tga/vtf2tga-2003.vcproj @@ -49,18 +49,18 @@ CompileAs="0"/> + Outputs="..\..\..\bin\$(TargetName).exe;..\..\..\bin\$(TargetName).pdb"/> + Outputs="..\..\..\bin\$(TargetName).exe;..\..\..\bin\$(TargetName).pdb"/> - - diff --git a/utils/vtf2tga/vtf2tga-2005.vcproj b/utils/vtf2tga/vtf2tga-2005.vcproj index f1f687f3..a971cfa4 100644 --- a/utils/vtf2tga/vtf2tga-2005.vcproj +++ b/utils/vtf2tga/vtf2tga-2005.vcproj @@ -4,6 +4,7 @@ Version="8.00" Name="vtf2tga" ProjectGUID="{2A1F656C-053C-46A4-AE33-304C1D865E0C}" + RootNamespace="vtf2tga" > - - diff --git a/utils/vtfdiff/vtfdiff-2003.vcproj b/utils/vtfdiff/vtfdiff-2003.vcproj index 96ae5ed3..7392124c 100644 --- a/utils/vtfdiff/vtfdiff-2003.vcproj +++ b/utils/vtfdiff/vtfdiff-2003.vcproj @@ -40,18 +40,18 @@ CompileAs="0"/> + Outputs="..\..\..\bin\$(TargetName).exe;..\..\..\bin\$(TargetName).pdb"/> + Outputs="..\..\..\bin\$(TargetName).exe;..\..\..\bin\$(TargetName).pdb"/> @@ -169,14 +169,6 @@ copy "$(TargetPath)" ..\..\..\game\bin\"$(TargetName)".pdb RelativePath="vtfdiff.cpp"> - - - - diff --git a/utils/vtfdiff/vtfdiff-2005.vcproj b/utils/vtfdiff/vtfdiff-2005.vcproj index 9afc39c3..1c0492c2 100644 --- a/utils/vtfdiff/vtfdiff-2005.vcproj +++ b/utils/vtfdiff/vtfdiff-2005.vcproj @@ -4,6 +4,7 @@ Version="8.00" Name="vtfdiff" ProjectGUID="{0A368DE7-D34A-48D3-B517-996BFF2D0D5D}" + RootNamespace="vtfdiff" > - - - - diff --git a/utils/vvis/mpivis.cpp b/utils/vvis/mpivis.cpp index 66f15808..39c802ee 100644 --- a/utils/vvis/mpivis.cpp +++ b/utils/vvis/mpivis.cpp @@ -535,7 +535,7 @@ void RunMPIPortalFlow() g_PortalMCAddr.ip[3] = (unsigned char)RandomInt( 3, 255 ); g_pPortalMCSocket = CreateIPSocket(); - int i = 0; + int i; for ( i=0; i < 5; i++ ) { if ( g_pPortalMCSocket->BindToAny( randomStream.RandomInt( 20000, 30000 ) ) ) diff --git a/utils/vvis/vvis-2003.vcproj b/utils/vvis/vvis-2003.vcproj index 9441d699..58cff9d7 100644 --- a/utils/vvis/vvis-2003.vcproj +++ b/utils/vvis/vvis-2003.vcproj @@ -44,16 +44,16 @@ + Outputs="..\..\..\bin\$(TargetName).dll;..\..\..\bin\$(TargetName).pdb"/> + Outputs="..\..\..\bin\$(TargetName).dll;..\..\..\bin\$(TargetName).pdb"/> - - + RelativePath="..\..\lib-vc7\public\mathlib.lib"> - - - - - - + RelativePath="..\..\lib-vc7\public\tier0.lib"> + RelativePath="..\..\lib-vc7\public\tier1.lib"> + RelativePath="..\..\lib-vc7\public\tier2.lib"> - - - - - - + RelativePath="..\..\lib-vc7\public\vmpi.lib"> - - - - - - + RelativePath="..\..\lib-vc7\public\vstdlib.lib"> diff --git a/utils/vvis/vvis-2005.vcproj b/utils/vvis/vvis-2005.vcproj index f1e3f399..1ad51bbe 100644 --- a/utils/vvis/vvis-2005.vcproj +++ b/utils/vvis/vvis-2005.vcproj @@ -30,8 +30,8 @@ - - diff --git a/utils/vvis_launcher/vvis_launcher-2003.vcproj b/utils/vvis_launcher/vvis_launcher-2003.vcproj index 846749ae..df046ead 100644 --- a/utils/vvis_launcher/vvis_launcher-2003.vcproj +++ b/utils/vvis_launcher/vvis_launcher-2003.vcproj @@ -41,15 +41,16 @@ CompileAs="0"/> + Outputs="..\..\..\bin\$(TargetName).exe"/> + Outputs="..\..\..\bin\$(TargetName).exe"/> @@ -204,21 +205,18 @@ if exist "$(TargetPath)" copy "$(TargetPath)" ..\..\..\game\ + Name="Link Libraries" + Filter=""> + + + + + + - - - - - - - - diff --git a/utils/vvis_launcher/vvis_launcher-2005.vcproj b/utils/vvis_launcher/vvis_launcher-2005.vcproj index e6a93c0e..6b6cd1cf 100644 --- a/utils/vvis_launcher/vvis_launcher-2005.vcproj +++ b/utils/vvis_launcher/vvis_launcher-2005.vcproj @@ -4,6 +4,7 @@ Version="8.00" Name="vvis_launcher" ProjectGUID="{4C0B9915-E8FF-4089-8927-FC934BFC1E4A}" + RootNamespace="vvis_launcher" > + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - diff --git a/utils/xwad/goldsrc_standin.h b/utils/xwad/goldsrc_standin.h index a2ea5e72..842a314e 100644 --- a/utils/xwad/goldsrc_standin.h +++ b/utils/xwad/goldsrc_standin.h @@ -10,6 +10,14 @@ #pragma once #endif +#if defined _MSC_VER && _MSC_VER >= 1400 + #ifndef _CRT_SECURE_NO_WARNINGS + #define _CRT_SECURE_NO_WARNINGS + #endif + + #pragma warning(disable : 4996) // functions declared deprecated +#endif + typedef float vec_t; typedef float vec3_t[3]; diff --git a/utils/xwad/wadlib.h b/utils/xwad/wadlib.h index e73505bc..3d4bc237 100644 --- a/utils/xwad/wadlib.h +++ b/utils/xwad/wadlib.h @@ -12,6 +12,14 @@ // wad reading // +#if defined _MSC_VER && _MSC_VER >= 1400 + #ifndef _CRT_SECURE_NO_WARNINGS + #define _CRT_SECURE_NO_WARNINGS + #endif + + #pragma warning(disable : 4996) // functions declared deprecated +#endif + #define CMP_NONE 0 #define CMP_LZSS 1 diff --git a/utils/xwad/xwad-2003.vcproj b/utils/xwad/xwad-2003.vcproj index 2c6d37b6..4d5a6cd1 100644 --- a/utils/xwad/xwad-2003.vcproj +++ b/utils/xwad/xwad-2003.vcproj @@ -26,7 +26,7 @@ Optimization="0" PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE" BasicRuntimeChecks="3" - RuntimeLibrary="5" + RuntimeLibrary="1" UsePrecompiledHeader="2" PrecompiledHeaderFile=".\Debug/xwad.pch" AssemblerListingLocation=".\Debug/" @@ -37,17 +37,18 @@ DebugInformationFormat="4"/> + Outputs="..\..\..\bin\xwad.exe;..\..\..\bin\xwad.pdb"/> + Outputs="..\..\..\bin\xwad.exe;..\..\..\bin\xwad.pdb"/> @@ -148,38 +150,34 @@ copy "$(TargetPath)" ..\..\..\game\bin\"$(TargetName)".pdb - - - - - - - - - - + Filter=""> + + + + + + + + diff --git a/utils/xwad/xwad-2005.vcproj b/utils/xwad/xwad-2005.vcproj index aae62fd7..b001827a 100644 --- a/utils/xwad/xwad-2005.vcproj +++ b/utils/xwad/xwad-2005.vcproj @@ -4,6 +4,7 @@ Version="8.00" Name="xwad" ProjectGUID="{B850012C-98A2-42F7-B023-9F65C448D938}" + RootNamespace="xwad" > - - - - - - - - @@ -238,11 +225,22 @@ Name="Header Files" Filter="h;hpp;hxx;hm;inl" > - - + + + + + + + +