From 5a0c406dbfdc7d10986f492921a116d089227f3d Mon Sep 17 00:00:00 2001 From: Peter Covington Date: Sat, 16 Apr 2022 07:19:42 -0400 Subject: [PATCH 01/90] Added an entity factory manager for custom weapons --- .../server/mapbase/custom_weapon_factory.cpp | 178 ++++++++++++++++++ .../server/mapbase/custom_weapon_factory.h | 76 ++++++++ sp/src/game/server/server_mapbase.vpc | 2 + sp/src/game/server/util.cpp | 9 + sp/src/game/server/util.h | 3 + .../shared/mapbase/weapon_custom_scripted.cpp | 6 + .../shared/mapbase/weapon_custom_scripted.h | 8 + 7 files changed, 282 insertions(+) create mode 100644 sp/src/game/server/mapbase/custom_weapon_factory.cpp create mode 100644 sp/src/game/server/mapbase/custom_weapon_factory.h diff --git a/sp/src/game/server/mapbase/custom_weapon_factory.cpp b/sp/src/game/server/mapbase/custom_weapon_factory.cpp new file mode 100644 index 00000000..bb919e97 --- /dev/null +++ b/sp/src/game/server/mapbase/custom_weapon_factory.cpp @@ -0,0 +1,178 @@ +#include "cbase.h" +#include "custom_weapon_factory.h" + +#define GENERIC_MANIFEST_FILE "scripts/mapbase_default_manifest.txt" +#define AUTOLOADED_MANIFEST_FILE UTIL_VarArgs("maps/%s_manifest.txt", MapName()) + +extern ConVar mapbase_load_default_manifest; + + + +CCustomWeaponSystem::CCustomWeaponSystem() : CAutoGameSystem("CustomWeaponFactorySystem") +{ +} + +void CCustomWeaponSystem::LevelInitPreEntity() +{ + // Check for a generic "mapname_manifest.txt" file and load it. + if (filesystem->FileExists(AUTOLOADED_MANIFEST_FILE, "GAME")) + { + AddManifestFile(AUTOLOADED_MANIFEST_FILE); + } + else + { + // Load the generic script instead. + ParseGenericManifest(); + } +} + +// Get a generic, hardcoded manifest with hardcoded names. +void CCustomWeaponSystem::ParseGenericManifest() +{ + if (!mapbase_load_default_manifest.GetBool()) + return; + + KeyValues* pKV = new KeyValues("DefaultManifest"); + pKV->LoadFromFile(filesystem, GENERIC_MANIFEST_FILE); + + AddManifestFile(pKV/*, true*/); + + pKV->deleteThis(); +} + +void CCustomWeaponSystem::AddManifestFile(const char* file) +{ + KeyValues* pKV = new KeyValues(file); + if (!pKV->LoadFromFile(filesystem, file)) + { + Warning("Mapbase Manifest: \"%s\" is unreadable or missing (can't load KV, check for syntax errors)\n", file); + pKV->deleteThis(); + return; + } + + CGMsg(1, CON_GROUP_MAPBASE_MISC, "===== Mapbase Manifest: Loading manifest file %s =====\n", file); + + AddManifestFile(pKV, false); + + CGMsg(1, CON_GROUP_MAPBASE_MISC, "==============================================================================\n"); + + pKV->deleteThis(); +} + +void CCustomWeaponSystem::AddManifestFile(KeyValues* pKV, bool bDontWarn) +{ + KeyValues* pKey = pKV->FindKey("weapons"); + + if (pKey) + { + char value[MAX_PATH]; + // Parse %mapname%, etc. + bool inparam = false; + CUtlStringList outStrings; + V_SplitString(pKey->GetString(), "%", outStrings); + for (int i = 0; i < outStrings.Count(); i++) + { + if (inparam) + { + if (FStrEq(outStrings[i], "mapname")) + { + Q_strncat(value, MapName(), sizeof(value)); + } + else if (FStrEq(outStrings[i], "language")) + { +#ifdef CLIENT_DLL + char uilanguage[64]; + engine->GetUILanguage(uilanguage, sizeof(uilanguage)); + Q_strncat(value, uilanguage, sizeof(value)); +#else + // Give up, use English + Q_strncat(value, "english", sizeof(value)); +#endif + } + } + else + { + Q_strncat(value, outStrings[i], sizeof(value)); + } + + inparam = !inparam; + } + + outStrings.PurgeAndDeleteElements(); + bDontWarn = pKV->GetBool("NoErrors", bDontWarn); + + LoadCustomWeaponsManifest(value, bDontWarn); + } +} + +#define Factory CustomWeaponsFactoryDictionary() +void CCustomWeaponSystem::LoadCustomWeaponsManifest(const char* file, bool bDontWarn) +{ + KeyValuesAD pKV("weapons_manifest"); + if (pKV->LoadFromFile(filesystem, file, "GAME")) + { + for (KeyValues *pkvWeapon = pKV->GetFirstValue(); pkvWeapon != nullptr; pkvWeapon = pkvWeapon->GetNextValue()) + { + const char* pszClassname = pkvWeapon->GetName(); + KeyValuesAD pkvWeaponScript("WeaponData"); + if (pkvWeaponScript->LoadFromFile(filesystem, pkvWeapon->GetString(), "GAME")) + { + const char* pszFactory = pkvWeaponScript->GetString("custom_factory", nullptr); + unsigned short FactoryIndex = Factory.Find(pszFactory); + if (Factory.IsValidIndex(FactoryIndex)) + { + unsigned short ClassIndex = m_ClassFactories.Find(pszClassname); + if (!m_ClassFactories.IsValidIndex(ClassIndex)) + { + ClassIndex = m_ClassFactories.Insert(pszClassname); + m_ClassFactories[ClassIndex].pOldFactory = EntityFactoryDictionary()->FindFactory(pszClassname); + } + + m_ClassFactories[ClassIndex].sDataFile = file; + m_ClassFactories[ClassIndex].pNewFactory = Factory.Element(FactoryIndex); + EntityFactoryDictionary()->UninstallFactory(pszClassname); + EntityFactoryDictionary()->InstallFactory(m_ClassFactories[ClassIndex].pNewFactory, pszClassname); + } + } + } + } +} +#undef Factory + +void CCustomWeaponSystem::LevelShutdownPostEntity() +{ + for (int i = 0; i < m_ClassFactories.Count(); i++) + { + EntityFactoryDictionary()->UninstallFactory(m_ClassFactories.GetElementName(i)); + const CustomClassName_t& entry = m_ClassFactories.Element(i); + if (entry.pOldFactory) + EntityFactoryDictionary()->InstallFactory(entry.pOldFactory, m_ClassFactories.GetElementName(i)); + } + + m_ClassFactories.Purge(); +} + +void CCustomWeaponSystem::ParseWeapon(CBaseCombatWeapon* pWeapon, const char* pClassName) +{ + ICustomWeapon* pCustom = dynamic_cast (pWeapon); + if (!pCustom) + return; + + unsigned short i = m_ClassFactories.Find(pClassName); + if (!m_ClassFactories.IsValidIndex(i)) + return; + + pCustom->ParseCustomFromWeaponFile(m_ClassFactories[i].sDataFile.String()); +} + +CUtlDict& CustomWeaponsFactoryDictionary() +{ + static CUtlDict dict; + return dict; +} + +static CCustomWeaponSystem g_CustomWeaponsSystem; +CCustomWeaponSystem* CustomWeaponSystem() +{ + return &g_CustomWeaponsSystem; +} diff --git a/sp/src/game/server/mapbase/custom_weapon_factory.h b/sp/src/game/server/mapbase/custom_weapon_factory.h new file mode 100644 index 00000000..c1af48c6 --- /dev/null +++ b/sp/src/game/server/mapbase/custom_weapon_factory.h @@ -0,0 +1,76 @@ +#ifndef CUSTOM_WEAPON_FACTORY_H +#define CUSTOM_WEAPON_FACTORY_H +#pragma once +#include "utldict.h" +#include "utlsymbol.h" + +CUtlDict< IEntityFactory*, unsigned short >& CustomWeaponsFactoryDictionary(); + +class ICustomWeapon +{ +public: + virtual void ParseCustomFromWeaponFile(const char* pFileName) = 0; +}; + +class CCustomWeaponSystem : public CAutoGameSystem +{ +public: + CCustomWeaponSystem(); + + // Level init, shutdown + virtual void LevelInitPreEntity(); + virtual void LevelShutdownPostEntity(); + + void ParseWeapon(CBaseCombatWeapon* pWeapon, const char* pClassName); + +private: + void ParseGenericManifest(); + void AddManifestFile(const char* file); + void AddManifestFile(KeyValues* pKV, bool bDontWarn = false); + void LoadCustomWeaponsManifest(const char* file, bool bDontWarn = false); + + typedef struct CustomClassName_s + { + CUtlSymbol sDataFile; + IEntityFactory* pNewFactory; + IEntityFactory* pOldFactory; + } CustomClassName_t; + CUtlDict m_ClassFactories; +}; + +CCustomWeaponSystem* CustomWeaponSystem(); + +template +class CCustomWeaponEntityFactory : public IEntityFactory +{ +public: + CCustomWeaponEntityFactory(const char* pFactoryClass) + { + CustomWeaponsFactoryDictionary().Insert(pFactoryClass, this); + } + + IServerNetworkable* Create(const char* pClassName) + { + T* pEnt = _CreateEntityTemplate((T*)NULL, pClassName); + CustomWeaponSystem()->ParseWeapon(pEnt, pClassName); + return pEnt->NetworkProp(); + } + + void Destroy(IServerNetworkable* pNetworkable) + { + if (pNetworkable) + { + pNetworkable->Release(); + } + } + + virtual size_t GetEntitySize() + { + return sizeof(T); + } +}; + +#define DEFINE_CUSTOM_WEAPON_FACTORY(factoryName, DLLClassName) \ + static CCustomWeaponEntityFactory custom_weapon_##factoryName##_factory( #factoryName ); + +#endif // !CUSTOM_WEAPON_FACTORY_H diff --git a/sp/src/game/server/server_mapbase.vpc b/sp/src/game/server/server_mapbase.vpc index b04706d4..345f5084 100644 --- a/sp/src/game/server/server_mapbase.vpc +++ b/sp/src/game/server/server_mapbase.vpc @@ -63,6 +63,8 @@ $Project $File "mapbase\ai_grenade.h" $File "mapbase\ai_monitor.cpp" $File "mapbase\ai_weaponmodifier.cpp" + $File "mapbase\custom_weapon_factory.cpp" + $File "mapbase\custom_weapon_factory.h" $File "mapbase\closecaption_entity.cpp" $File "mapbase\datadesc_mod.cpp" $File "mapbase\datadesc_mod.h" diff --git a/sp/src/game/server/util.cpp b/sp/src/game/server/util.cpp index 8a97695a..6a44cec2 100644 --- a/sp/src/game/server/util.cpp +++ b/sp/src/game/server/util.cpp @@ -76,6 +76,10 @@ public: virtual const char *GetCannonicalName( const char *pClassName ); void ReportEntitySizes(); +#ifdef MAPBASE + virtual void UninstallFactory(const char* pClassName); +#endif // MAPBASE + private: IEntityFactory *FindFactory( const char *pClassName ); public: @@ -203,6 +207,11 @@ void CEntityFactoryDictionary::ReportEntitySizes() } #ifdef MAPBASE +void CEntityFactoryDictionary::UninstallFactory(const char* pClassName) +{ + m_Factories.Remove(pClassName); +} + int EntityFactory_AutoComplete( const char *cmdname, CUtlVector< CUtlString > &commands, CUtlRBTree< CUtlString > &symbols, char *substring, int checklen = 0 ) { CEntityFactoryDictionary *pFactoryDict = (CEntityFactoryDictionary*)EntityFactoryDictionary(); diff --git a/sp/src/game/server/util.h b/sp/src/game/server/util.h index 82f485ba..525ce341 100644 --- a/sp/src/game/server/util.h +++ b/sp/src/game/server/util.h @@ -100,6 +100,9 @@ public: virtual void Destroy( const char *pClassName, IServerNetworkable *pNetworkable ) = 0; virtual IEntityFactory *FindFactory( const char *pClassName ) = 0; virtual const char *GetCannonicalName( const char *pClassName ) = 0; +#ifdef MAPBASE + virtual void UninstallFactory(const char* pClassName) = 0; +#endif // MAPBASE }; IEntityFactoryDictionary *EntityFactoryDictionary(); diff --git a/sp/src/game/shared/mapbase/weapon_custom_scripted.cpp b/sp/src/game/shared/mapbase/weapon_custom_scripted.cpp index d9155f32..a0dcbda2 100644 --- a/sp/src/game/shared/mapbase/weapon_custom_scripted.cpp +++ b/sp/src/game/shared/mapbase/weapon_custom_scripted.cpp @@ -581,6 +581,12 @@ int CWeaponCustomScripted::WeaponMeleeAttack2Condition( float flDot, float flDis return BaseClass::WeaponMeleeAttack2Condition( flDot, flDist ); } + +DEFINE_CUSTOM_WEAPON_FACTORY(vscript, CWeaponCustomScripted); +void CWeaponCustomScripted::ParseCustomFromWeaponFile(const char* pFileName) +{ + Q_FileBase(pFileName, m_iszWeaponScriptName.GetForModify(), 256); +} #endif //----------------------------------------------------------------------------- diff --git a/sp/src/game/shared/mapbase/weapon_custom_scripted.h b/sp/src/game/shared/mapbase/weapon_custom_scripted.h index 924f1031..f2a2a77d 100644 --- a/sp/src/game/shared/mapbase/weapon_custom_scripted.h +++ b/sp/src/game/shared/mapbase/weapon_custom_scripted.h @@ -14,6 +14,8 @@ #include "basecombatweapon_shared.h" #ifdef CLIENT_DLL #include "vscript_client.h" +#else +#include "mapbase/custom_weapon_factory.h" #endif // The base class of the scripted weapon is game-specific. @@ -32,6 +34,9 @@ HSCRIPT m_Func_##name; class CWeaponCustomScripted : public SCRIPTED_WEAPON_DERIVED_FROM +#ifndef CLIENT_DLL + , public ICustomWeapon +#endif // !CLIENT_DLL { public: DECLARE_CLASS( CWeaponCustomScripted, SCRIPTED_WEAPON_DERIVED_FROM ); @@ -106,6 +111,9 @@ public: int WeaponRangeAttack2Condition( float flDot, float flDist ); int WeaponMeleeAttack1Condition( float flDot, float flDist ); int WeaponMeleeAttack2Condition( float flDot, float flDist ); + + // Inherited via ICustomWeapon + virtual void ParseCustomFromWeaponFile(const char* pFileName) override; #endif ALLOW_SCRIPT_ACCESS(); From 7877953b6cff641fa2fecd69d99b06430b4daa03 Mon Sep 17 00:00:00 2001 From: Peter Covington Date: Sat, 16 Apr 2022 09:18:06 -0400 Subject: [PATCH 02/90] Fixes --- sp/src/game/server/mapbase/custom_weapon_factory.cpp | 8 +++++--- sp/src/game/shared/mapbase/weapon_custom_scripted.cpp | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/sp/src/game/server/mapbase/custom_weapon_factory.cpp b/sp/src/game/server/mapbase/custom_weapon_factory.cpp index bb919e97..dea0380c 100644 --- a/sp/src/game/server/mapbase/custom_weapon_factory.cpp +++ b/sp/src/game/server/mapbase/custom_weapon_factory.cpp @@ -66,6 +66,8 @@ void CCustomWeaponSystem::AddManifestFile(KeyValues* pKV, bool bDontWarn) if (pKey) { char value[MAX_PATH]; + value[0] = '\0'; + // Parse %mapname%, etc. bool inparam = false; CUtlStringList outStrings; @@ -76,7 +78,7 @@ void CCustomWeaponSystem::AddManifestFile(KeyValues* pKV, bool bDontWarn) { if (FStrEq(outStrings[i], "mapname")) { - Q_strncat(value, MapName(), sizeof(value)); + Q_strncat(value, STRING(gpGlobals->mapname), sizeof(value)); } else if (FStrEq(outStrings[i], "language")) { @@ -128,7 +130,7 @@ void CCustomWeaponSystem::LoadCustomWeaponsManifest(const char* file, bool bDont m_ClassFactories[ClassIndex].pOldFactory = EntityFactoryDictionary()->FindFactory(pszClassname); } - m_ClassFactories[ClassIndex].sDataFile = file; + m_ClassFactories[ClassIndex].sDataFile = pkvWeapon->GetString(); m_ClassFactories[ClassIndex].pNewFactory = Factory.Element(FactoryIndex); EntityFactoryDictionary()->UninstallFactory(pszClassname); EntityFactoryDictionary()->InstallFactory(m_ClassFactories[ClassIndex].pNewFactory, pszClassname); @@ -141,7 +143,7 @@ void CCustomWeaponSystem::LoadCustomWeaponsManifest(const char* file, bool bDont void CCustomWeaponSystem::LevelShutdownPostEntity() { - for (int i = 0; i < m_ClassFactories.Count(); i++) + for (unsigned short i = 0; i < m_ClassFactories.Count(); i++) { EntityFactoryDictionary()->UninstallFactory(m_ClassFactories.GetElementName(i)); const CustomClassName_t& entry = m_ClassFactories.Element(i); diff --git a/sp/src/game/shared/mapbase/weapon_custom_scripted.cpp b/sp/src/game/shared/mapbase/weapon_custom_scripted.cpp index a0dcbda2..a3ca9d57 100644 --- a/sp/src/game/shared/mapbase/weapon_custom_scripted.cpp +++ b/sp/src/game/shared/mapbase/weapon_custom_scripted.cpp @@ -427,7 +427,7 @@ void CWeaponCustomScripted::SecondaryAttack( void ) // Purpose: //----------------------------------------------------------------------------- #define ACTIVITY_FUNC_OVERRIDE( name ) ScriptVariant_t retVal; \ - if (RunWeaponHook( g_Hook_##name, m_Func_##name, &retVal ) && retVal.m_bool == false) \ + if (RunWeaponHook( g_Hook_##name, m_Func_##name, &retVal ) && !retVal.IsNull()) \ { \ if (retVal.m_type == FIELD_INTEGER) \ { \ From 749f9ffae001a161b45c89a77f27fb341b11c541 Mon Sep 17 00:00:00 2001 From: Peter Covington Date: Sun, 17 Apr 2022 15:57:37 -0400 Subject: [PATCH 03/90] Finished parser for vscript weapon --- .../server/mapbase/custom_weapon_factory.cpp | 5 +- .../shared/mapbase/weapon_custom_scripted.cpp | 118 ++++++++++++++++-- .../shared/mapbase/weapon_custom_scripted.h | 4 + 3 files changed, 119 insertions(+), 8 deletions(-) diff --git a/sp/src/game/server/mapbase/custom_weapon_factory.cpp b/sp/src/game/server/mapbase/custom_weapon_factory.cpp index dea0380c..5a5d74cb 100644 --- a/sp/src/game/server/mapbase/custom_weapon_factory.cpp +++ b/sp/src/game/server/mapbase/custom_weapon_factory.cpp @@ -2,7 +2,8 @@ #include "custom_weapon_factory.h" #define GENERIC_MANIFEST_FILE "scripts/mapbase_default_manifest.txt" -#define AUTOLOADED_MANIFEST_FILE UTIL_VarArgs("maps/%s_manifest.txt", MapName()) +#define AUTOLOADED_MANIFEST_FILE UTIL_VarArgs("maps/%s_manifest.txt", STRING(gpGlobals->mapname)) +#define GLOBAL_WEAPONS_MANIFEST "scripts/custom_weapon_manifest.txt" extern ConVar mapbase_load_default_manifest; @@ -14,6 +15,8 @@ CCustomWeaponSystem::CCustomWeaponSystem() : CAutoGameSystem("CustomWeaponFactor void CCustomWeaponSystem::LevelInitPreEntity() { + AddManifestFile(GLOBAL_WEAPONS_MANIFEST); + // Check for a generic "mapname_manifest.txt" file and load it. if (filesystem->FileExists(AUTOLOADED_MANIFEST_FILE, "GAME")) { diff --git a/sp/src/game/shared/mapbase/weapon_custom_scripted.cpp b/sp/src/game/shared/mapbase/weapon_custom_scripted.cpp index a3ca9d57..10553de5 100644 --- a/sp/src/game/shared/mapbase/weapon_custom_scripted.cpp +++ b/sp/src/game/shared/mapbase/weapon_custom_scripted.cpp @@ -7,6 +7,7 @@ #include "cbase.h" #include "tier1/fmtstr.h" +#include "tier1/utlvector.h" #include "weapon_custom_scripted.h" // memdbgon must be the last include file in a .cpp file!!! @@ -193,13 +194,6 @@ bool CWeaponCustomScripted::RunWeaponHook( ScriptHook_t &hook, HSCRIPT &cached, //----------------------------------------------------------------------------- void CWeaponCustomScripted::Spawn( void ) { -#ifdef CLIENT_DLL - if (m_iszClientScripts[0] != '\0' && ValidateScriptScope()) - { - RunScriptFile( m_iszClientScripts ); - } -#endif - BaseClass::Spawn(); } @@ -586,6 +580,23 @@ DEFINE_CUSTOM_WEAPON_FACTORY(vscript, CWeaponCustomScripted); void CWeaponCustomScripted::ParseCustomFromWeaponFile(const char* pFileName) { Q_FileBase(pFileName, m_iszWeaponScriptName.GetForModify(), 256); + KeyValuesAD pKVWeapon("WeaponData"); + if (pKVWeapon->LoadFromFile(filesystem, pFileName, "GAME")) + { + Q_strncpy(m_iszClientScripts.GetForModify(), pKVWeapon->GetString("vscript_file"), 256); + } +} + +extern ConVar sv_script_think_interval; +#else +void CWeaponCustomScripted::OnDataChanged(DataUpdateType_t type) +{ + BaseClass::OnDataChanged(type); + + if (!m_ScriptScope.IsInitialized()) + { + RunVScripts(); + } } #endif @@ -605,3 +616,96 @@ int CWeaponCustomScripted::ActivityListCount( void ) return BaseClass::ActivityListCount(); } + +void CWeaponCustomScripted::RunVScripts() +{ +#ifdef CLIENT_DLL + if (m_iszClientScripts[0] != '\0' && ValidateScriptScope()) + { + RunScriptFile(m_iszClientScripts); + } +#else + if (m_iszVScripts == NULL_STRING && m_iszClientScripts[0] == '\0') + { + return; + } + +#ifdef MAPBASE_VSCRIPT + if (g_pScriptVM == NULL) + { + return; + } +#endif + + ValidateScriptScope(); + + // All functions we want to have call chained instead of overwritten + // by other scripts in this entities list. + static const char* sCallChainFunctions[] = + { + "OnPostSpawn", + "Precache" + }; + + ScriptLanguage_t language = g_pScriptVM->GetLanguage(); + + // Make a call chainer for each in this entities scope + for (int j = 0; j < ARRAYSIZE(sCallChainFunctions); ++j) + { + + if (language == SL_PYTHON) + { + // UNDONE - handle call chaining in python + ; + } + else if (language == SL_SQUIRREL) + { + //TODO: For perf, this should be precompiled and the %s should be passed as a parameter + HSCRIPT hCreateChainScript = g_pScriptVM->CompileScript(CFmtStr("%sCallChain <- CSimpleCallChainer(\"%s\", self.GetScriptScope(), true)", sCallChainFunctions[j], sCallChainFunctions[j])); + g_pScriptVM->Run(hCreateChainScript, (HSCRIPT)m_ScriptScope); + } + } + + CUtlStringList szScripts; + if (m_iszVScripts != NULL_STRING) + { + V_SplitString(STRING(m_iszVScripts), " ", szScripts); + } + + if (m_iszClientScripts[0] != '\0') + { + szScripts.AddToHead(strdup(m_iszClientScripts.Get())); + } + + for (int i = 0; i < szScripts.Count(); i++) + { +#ifdef MAPBASE + CGMsg(0, CON_GROUP_VSCRIPT, "%s executing script: %s\n", GetDebugName(), szScripts[i]); +#else + Log("%s executing script: %s\n", GetDebugName(), szScripts[i]); +#endif + + RunScriptFile(szScripts[i], IsWorld()); + + for (int j = 0; j < ARRAYSIZE(sCallChainFunctions); ++j) + { + if (language == SL_PYTHON) + { + // UNDONE - handle call chaining in python + ; + } + else if (language == SL_SQUIRREL) + { + //TODO: For perf, this should be precompiled and the %s should be passed as a parameter. + HSCRIPT hRunPostScriptExecute = g_pScriptVM->CompileScript(CFmtStr("%sCallChain.PostScriptExecute()", sCallChainFunctions[j])); + g_pScriptVM->Run(hRunPostScriptExecute, (HSCRIPT)m_ScriptScope); + } + } + } + + if (m_iszScriptThinkFunction != NULL_STRING) + { + SetContextThink(&CBaseEntity::ScriptThink, gpGlobals->curtime + sv_script_think_interval.GetFloat(), "ScriptThink"); + } +#endif +} \ No newline at end of file diff --git a/sp/src/game/shared/mapbase/weapon_custom_scripted.h b/sp/src/game/shared/mapbase/weapon_custom_scripted.h index f2a2a77d..8cc05cd3 100644 --- a/sp/src/game/shared/mapbase/weapon_custom_scripted.h +++ b/sp/src/game/shared/mapbase/weapon_custom_scripted.h @@ -50,6 +50,8 @@ public: bool KeyValue( const char *szKeyName, const char *szValue ); bool GetKeyValue( const char *szKeyName, char *szValue, int iMaxLen ); + void RunVScripts(); + // Base script has a function for this //void Precache( void ); @@ -114,6 +116,8 @@ public: // Inherited via ICustomWeapon virtual void ParseCustomFromWeaponFile(const char* pFileName) override; +#else + void OnDataChanged(DataUpdateType_t type); #endif ALLOW_SCRIPT_ACCESS(); From 332856e37efd4e310c61acded9ef8dcd9911a8b0 Mon Sep 17 00:00:00 2001 From: Peter Covington Date: Mon, 18 Apr 2022 15:09:57 -0400 Subject: [PATCH 04/90] Added factory for melee weapons --- sp/src/game/client/client_mapbase.vpc | 1 + .../client/mapbase/c_weapon_custom_hl2.cpp | 34 ++ sp/src/game/server/basebludgeonweapon.cpp | 7 +- sp/src/game/server/basebludgeonweapon.h | 4 + .../game/server/mapbase/weapon_custom_hl2.cpp | 296 ++++++++++++++++++ sp/src/game/server/server_mapbase.vpc | 1 + 6 files changed, 342 insertions(+), 1 deletion(-) create mode 100644 sp/src/game/client/mapbase/c_weapon_custom_hl2.cpp create mode 100644 sp/src/game/server/mapbase/weapon_custom_hl2.cpp diff --git a/sp/src/game/client/client_mapbase.vpc b/sp/src/game/client/client_mapbase.vpc index 97a2217d..2a8f2afe 100644 --- a/sp/src/game/client/client_mapbase.vpc +++ b/sp/src/game/client/client_mapbase.vpc @@ -65,6 +65,7 @@ $Project $File "mapbase\c_func_fake_worldportal.h" $File "mapbase\c_point_glow.cpp" $File "mapbase\c_vgui_text_display.cpp" + $File "mapbase\c_weapon_custom_hl2.cpp" $File "mapbase\mapbase_autocubemap.cpp" } diff --git a/sp/src/game/client/mapbase/c_weapon_custom_hl2.cpp b/sp/src/game/client/mapbase/c_weapon_custom_hl2.cpp new file mode 100644 index 00000000..58c46e25 --- /dev/null +++ b/sp/src/game/client/mapbase/c_weapon_custom_hl2.cpp @@ -0,0 +1,34 @@ +#include "cbase.h" +#include "c_weapon__stubs.h" +#include "basehlcombatweapon_shared.h" +#include "c_basehlcombatweapon.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +#pragma region Melee +class C_HLCustomWeaponMelee : public C_BaseHLBludgeonWeapon +{ +public: + DECLARE_CLASS(C_HLCustomWeaponMelee, C_BaseHLBludgeonWeapon); + DECLARE_CLIENTCLASS(); + DECLARE_PREDICTABLE(); + + C_HLCustomWeaponMelee(); + + virtual const char* GetWeaponScriptName() { return m_iszWeaponScriptName; } +private: + char m_iszWeaponScriptName[128]; +}; + +STUB_WEAPON_CLASS_IMPLEMENT(weapon_hlcustommelee, C_HLCustomWeaponMelee); + +IMPLEMENT_CLIENTCLASS_DT(C_HLCustomWeaponMelee, DT_HLCustomWeaponMelee, CHLCustomWeaponMelee) +RecvPropString(RECVINFO(m_iszWeaponScriptName)), +END_RECV_TABLE(); + +C_HLCustomWeaponMelee::C_HLCustomWeaponMelee() +{ + m_iszWeaponScriptName[0] = '\0'; +} +#pragma endregion \ No newline at end of file diff --git a/sp/src/game/server/basebludgeonweapon.cpp b/sp/src/game/server/basebludgeonweapon.cpp index 57683a19..93903616 100644 --- a/sp/src/game/server/basebludgeonweapon.cpp +++ b/sp/src/game/server/basebludgeonweapon.cpp @@ -162,7 +162,12 @@ void CBaseHLBludgeonWeapon::Hit( trace_t &traceHit, Activity nHitActivity, bool pPlayer->EyeVectors( &hitDirection, NULL, NULL ); VectorNormalize( hitDirection ); - CTakeDamageInfo info( GetOwner(), GetOwner(), GetDamageForActivity( nHitActivity ), DMG_CLUB ); +#ifdef MAPBASE + CTakeDamageInfo info(GetOwner(), GetOwner(), GetDamageForActivity(nHitActivity), GetDamageType()); +#else + CTakeDamageInfo info(GetOwner(), GetOwner(), GetDamageForActivity(nHitActivity), DMG_CLUB); +#endif // MAPBASE + if( pPlayer && pHitEntity->IsNPC() ) { diff --git a/sp/src/game/server/basebludgeonweapon.h b/sp/src/game/server/basebludgeonweapon.h index 6f2a7eaf..52f02f9b 100644 --- a/sp/src/game/server/basebludgeonweapon.h +++ b/sp/src/game/server/basebludgeonweapon.h @@ -44,6 +44,10 @@ public: virtual int CapabilitiesGet( void ); virtual int WeaponMeleeAttack1Condition( float flDot, float flDist ); +#ifdef MAPBASE + virtual int GetDamageType() { return DMG_CLUB; } +#endif // MAPBASE + protected: virtual void ImpactEffect( trace_t &trace ); diff --git a/sp/src/game/server/mapbase/weapon_custom_hl2.cpp b/sp/src/game/server/mapbase/weapon_custom_hl2.cpp new file mode 100644 index 00000000..5d966d29 --- /dev/null +++ b/sp/src/game/server/mapbase/weapon_custom_hl2.cpp @@ -0,0 +1,296 @@ +#include "cbase.h" +#include "custom_weapon_factory.h" +#include "basebludgeonweapon.h" +#include "ai_basenpc.h" +#include "player.h" +#include "npcevent.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +#pragma region Melee +const char* g_ppszDamageClasses[] = { + "BLUNT", + "SLASH", + "STUN", + "BURN", +}; + +int g_nDamageClassTypeBits[ARRAYSIZE(g_ppszDamageClasses)] = { + DMG_CLUB, + DMG_SLASH, + DMG_CLUB|DMG_SHOCK, + DMG_CLUB|DMG_BURN, +}; + +class CHLCustomWeaponMelee : public CBaseHLBludgeonWeapon, public ICustomWeapon +{ +public: + DECLARE_CLASS(CHLCustomWeaponMelee, CBaseHLBludgeonWeapon); + + DECLARE_SERVERCLASS(); + DECLARE_ACTTABLE(); + + CHLCustomWeaponMelee(); + + float GetRange(void) { return m_flMeleeRange; } + float GetFireRate(void) { return m_flRefireRate; } + + void AddViewKick(void); + float GetDamageForActivity(Activity hitActivity); + + virtual int WeaponMeleeAttack1Condition(float flDot, float flDist); + void SecondaryAttack(void) { return; } + + // Animation event + virtual void Operator_HandleAnimEvent(animevent_t* pEvent, CBaseCombatCharacter* pOperator); + + // Don't use backup activities + acttable_t* GetBackupActivityList() { return NULL; } + int GetBackupActivityListCount() { return 0; } + + const char* GetWeaponScriptName() { return m_iszWeaponScriptName.Get(); } + virtual int GetDamageType() { return g_nDamageClassTypeBits[m_nDamageClass]; } + + virtual void ParseCustomFromWeaponFile(const char* pFileName); + +private: + // Animation event handlers + void HandleAnimEventMeleeHit(animevent_t* pEvent, CBaseCombatCharacter* pOperator); + +private: + float m_flMeleeRange; + float m_flRefireRate; + float m_flDamage; + float m_flNPCDamage; + byte m_nDamageClass; + + CNetworkString(m_iszWeaponScriptName, 128); +}; + +IMPLEMENT_SERVERCLASS_ST(CHLCustomWeaponMelee, DT_HLCustomWeaponMelee) +SendPropString(SENDINFO(m_iszWeaponScriptName)), +END_SEND_TABLE(); + +DEFINE_CUSTOM_WEAPON_FACTORY(hl2_melee, CHLCustomWeaponMelee); + +acttable_t CHLCustomWeaponMelee::m_acttable[] = +{ + { ACT_MELEE_ATTACK1, ACT_MELEE_ATTACK_SWING, true }, + { ACT_GESTURE_MELEE_ATTACK1, ACT_GESTURE_MELEE_ATTACK_SWING, false}, + + { ACT_IDLE_ANGRY, ACT_IDLE_ANGRY_MELEE, false }, +#if EXPANDED_HL2_WEAPON_ACTIVITIES + { ACT_IDLE, ACT_IDLE_MELEE, false }, + { ACT_RUN, ACT_RUN_MELEE, false }, + { ACT_WALK, ACT_WALK_MELEE, false }, + + { ACT_ARM, ACT_ARM_MELEE, false }, + { ACT_DISARM, ACT_DISARM_MELEE, false }, +#else + { ACT_IDLE, ACT_IDLE_ANGRY_MELEE, false }, +#endif + +#ifdef MAPBASE + // HL2:DM activities (for third-person animations in SP) + { 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 }, +#if EXPANDED_HL2DM_ACTIVITIES + { ACT_HL2MP_GESTURE_RANGE_ATTACK2, ACT_HL2MP_GESTURE_RANGE_ATTACK2_MELEE, false }, + { ACT_HL2MP_WALK, ACT_HL2MP_WALK_MELEE, false }, +#endif +#endif +}; + +IMPLEMENT_ACTTABLE(CHLCustomWeaponMelee); + +CHLCustomWeaponMelee::CHLCustomWeaponMelee() +{ + m_nDamageClass = 0; +} + +//----------------------------------------------------------------------------- +// Purpose: Get the damage amount for the animation we're doing +// Input : hitActivity - currently played activity +// Output : Damage amount +//----------------------------------------------------------------------------- +float CHLCustomWeaponMelee::GetDamageForActivity(Activity hitActivity) +{ + if ((GetOwner() != NULL) && (GetOwner()->IsPlayer())) + return m_flDamage; + + return m_flNPCDamage; +} + +//----------------------------------------------------------------------------- +// Purpose: Add in a view kick for this weapon +//----------------------------------------------------------------------------- +void CHLCustomWeaponMelee::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); +} + + +//----------------------------------------------------------------------------- +// Attempt to lead the target (needed because citizens can't hit manhacks with the crowbar!) +//----------------------------------------------------------------------------- +extern ConVar sk_crowbar_lead_time; + +int CHLCustomWeaponMelee::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; +} + + +//----------------------------------------------------------------------------- +// Animation event handlers +//----------------------------------------------------------------------------- +void CHLCustomWeaponMelee::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); + + 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(), 50, vecDirection, vecEnd); + CBaseEntity* pHurt = pOperator->CheckTraceHullAttack(pOperator->Weapon_ShootPosition(), vecEnd, + Vector(-16, -16, -16), Vector(36, 36, 36), m_flNPCDamage, GetDamageType(), 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 CHLCustomWeaponMelee::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; + } +} + +void CHLCustomWeaponMelee::ParseCustomFromWeaponFile(const char* pFileName) +{ + Q_FileBase(pFileName, m_iszWeaponScriptName.GetForModify(), 128); + KeyValuesAD pKVWeapon("WeaponData"); + if (pKVWeapon->LoadFromFile(filesystem, pFileName, "GAME")) + { + KeyValues* pkvData = pKVWeapon->FindKey("CustomData"); + if (pkvData) + { + m_flDamage = pkvData->GetFloat("damage"); + m_flNPCDamage = pkvData->GetFloat("damage_npc", m_flDamage); + m_flMeleeRange = pkvData->GetFloat("range", 70.f); + m_flRefireRate = pkvData->GetFloat("rate", 0.7f); + + const char* pszDamageClass = pkvData->GetString("damage_type", nullptr); + if (pszDamageClass) + { + for (byte i = 0; i < ARRAYSIZE(g_ppszDamageClasses); i++) + { + if (V_stricmp(pszDamageClass, g_ppszDamageClasses[i]) == 0) + { + m_nDamageClass = i; + break; + } + } + } + } + } +} +#pragma endregion \ No newline at end of file diff --git a/sp/src/game/server/server_mapbase.vpc b/sp/src/game/server/server_mapbase.vpc index 345f5084..7d74847e 100644 --- a/sp/src/game/server/server_mapbase.vpc +++ b/sp/src/game/server/server_mapbase.vpc @@ -88,6 +88,7 @@ $Project $File "mapbase\SystemConvarMod.h" $File "mapbase\variant_tools.h" $File "mapbase\vgui_text_display.cpp" + $File "mapbase\weapon_custom_hl2.cpp" $File "mapbase\logic_eventlistener.cpp" $File "mapbase\logic_register_activator.cpp" From f58b5990206eeda5f73d902aefd6123864cecb05 Mon Sep 17 00:00:00 2001 From: Peter Covington Date: Mon, 18 Apr 2022 17:24:41 -0400 Subject: [PATCH 05/90] Fixed custom weapon scripts on client --- sp/src/game/client/c_basecombatweapon.cpp | 7 +++++++ sp/src/game/shared/basecombatweapon_shared.cpp | 6 ++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/sp/src/game/client/c_basecombatweapon.cpp b/sp/src/game/client/c_basecombatweapon.cpp index 3ae13cde..bdb71f02 100644 --- a/sp/src/game/client/c_basecombatweapon.cpp +++ b/sp/src/game/client/c_basecombatweapon.cpp @@ -151,6 +151,13 @@ int C_BaseCombatWeapon::GetWorldModelIndex( void ) //----------------------------------------------------------------------------- void C_BaseCombatWeapon::OnDataChanged( DataUpdateType_t updateType ) { +#ifdef MAPBASE + if (updateType == DATA_UPDATE_CREATED) + { + Precache(); + } +#endif // MAPBASE + BaseClass::OnDataChanged(updateType); CHandle< C_BaseCombatWeapon > handle = this; diff --git a/sp/src/game/shared/basecombatweapon_shared.cpp b/sp/src/game/shared/basecombatweapon_shared.cpp index 22fe8b97..f505fafc 100644 --- a/sp/src/game/shared/basecombatweapon_shared.cpp +++ b/sp/src/game/shared/basecombatweapon_shared.cpp @@ -164,7 +164,9 @@ void CBaseCombatWeapon::GiveDefaultAmmo( void ) //----------------------------------------------------------------------------- void CBaseCombatWeapon::Spawn( void ) { +#if !defined(CLIENT_DLL) || !defined(MAPBASE) Precache(); +#endif // !defined(CLIENT_DLL) || !defined(MAPBASE) BaseClass::Spawn(); @@ -239,7 +241,7 @@ const unsigned char *CBaseCombatWeapon::GetEncryptionKey( void ) void CBaseCombatWeapon::Precache( void ) { #if defined( CLIENT_DLL ) - Assert( Q_strlen( GetClassname() ) > 0 ); + Assert( Q_strlen(GetWeaponScriptName() ) > 0 ); // Msg( "Client got %s\n", GetClassname() ); #endif m_iPrimaryAmmoType = m_iSecondaryAmmoType = -1; @@ -321,7 +323,7 @@ void CBaseCombatWeapon::Precache( void ) else { // Couldn't read data file, remove myself - Warning( "Error reading weapon data file for: %s\n", GetClassname() ); + Warning( "Error reading weapon data file for: %s\n", GetWeaponScriptName() ); // Remove( ); //don't remove, this gets released soon! } } From e77180547de4a96f952b73fb242d776a41ee5e65 Mon Sep 17 00:00:00 2001 From: Peter Covington Date: Mon, 18 Apr 2022 17:25:25 -0400 Subject: [PATCH 06/90] Readiness activities for custom melee weapon --- sp/src/game/server/mapbase/weapon_custom_hl2.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/sp/src/game/server/mapbase/weapon_custom_hl2.cpp b/sp/src/game/server/mapbase/weapon_custom_hl2.cpp index 5d966d29..9249198a 100644 --- a/sp/src/game/server/mapbase/weapon_custom_hl2.cpp +++ b/sp/src/game/server/mapbase/weapon_custom_hl2.cpp @@ -87,6 +87,17 @@ acttable_t CHLCustomWeaponMelee::m_acttable[] = { ACT_ARM, ACT_ARM_MELEE, false }, { ACT_DISARM, ACT_DISARM_MELEE, false }, + + // Readiness activities (not aiming) + { ACT_IDLE_RELAXED, ACT_IDLE_MELEE, false },//never aims + { ACT_IDLE_STIMULATED, ACT_IDLE_MELEE, false }, + { ACT_IDLE_AGITATED, ACT_IDLE_ANGRY_MELEE, false },//always aims + + // Readiness activities (aiming) + { ACT_IDLE_AIM_RELAXED, ACT_IDLE_MELEE, false },//never aims + { ACT_IDLE_AIM_STIMULATED, ACT_IDLE_ANGRY_MELEE, false }, + { ACT_IDLE_AIM_AGITATED, ACT_IDLE_ANGRY_MELEE, false },//always aims + //End readiness activities #else { ACT_IDLE, ACT_IDLE_ANGRY_MELEE, false }, #endif From ee46bc4bd11ad6baffde36dc30de0e267df0d70b Mon Sep 17 00:00:00 2001 From: Peter Covington Date: Mon, 18 Apr 2022 18:12:40 -0400 Subject: [PATCH 07/90] Fixes --- .../server/mapbase/custom_weapon_factory.cpp | 2 +- .../game/server/mapbase/weapon_custom_hl2.cpp | 28 +++++++++++++++---- 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/sp/src/game/server/mapbase/custom_weapon_factory.cpp b/sp/src/game/server/mapbase/custom_weapon_factory.cpp index 5a5d74cb..e010bc3f 100644 --- a/sp/src/game/server/mapbase/custom_weapon_factory.cpp +++ b/sp/src/game/server/mapbase/custom_weapon_factory.cpp @@ -15,7 +15,7 @@ CCustomWeaponSystem::CCustomWeaponSystem() : CAutoGameSystem("CustomWeaponFactor void CCustomWeaponSystem::LevelInitPreEntity() { - AddManifestFile(GLOBAL_WEAPONS_MANIFEST); + LoadCustomWeaponsManifest(GLOBAL_WEAPONS_MANIFEST); // Check for a generic "mapname_manifest.txt" file and load it. if (filesystem->FileExists(AUTOLOADED_MANIFEST_FILE, "GAME")) diff --git a/sp/src/game/server/mapbase/weapon_custom_hl2.cpp b/sp/src/game/server/mapbase/weapon_custom_hl2.cpp index 9249198a..22c2aad7 100644 --- a/sp/src/game/server/mapbase/weapon_custom_hl2.cpp +++ b/sp/src/game/server/mapbase/weapon_custom_hl2.cpp @@ -89,14 +89,30 @@ acttable_t CHLCustomWeaponMelee::m_acttable[] = { ACT_DISARM, ACT_DISARM_MELEE, false }, // Readiness activities (not aiming) - { ACT_IDLE_RELAXED, ACT_IDLE_MELEE, false },//never aims - { ACT_IDLE_STIMULATED, ACT_IDLE_MELEE, false }, - { ACT_IDLE_AGITATED, ACT_IDLE_ANGRY_MELEE, false },//always aims + { ACT_IDLE_RELAXED, ACT_IDLE_MELEE, false },//never aims + { ACT_IDLE_STIMULATED, ACT_IDLE_MELEE, false }, + { ACT_IDLE_AGITATED, ACT_IDLE_ANGRY_MELEE, false },//always aims + + { ACT_WALK_RELAXED, ACT_WALK_MELEE, false },//never aims + { ACT_WALK_STIMULATED, ACT_WALK_MELEE, false }, + { ACT_WALK_AGITATED, ACT_WALK_MELEE, false },//always aims + + { ACT_RUN_RELAXED, ACT_RUN_MELEE, false },//never aims + { ACT_RUN_STIMULATED, ACT_RUN_MELEE, false }, + { ACT_RUN_AGITATED, ACT_RUN_MELEE, false },//always aims // Readiness activities (aiming) - { ACT_IDLE_AIM_RELAXED, ACT_IDLE_MELEE, false },//never aims - { ACT_IDLE_AIM_STIMULATED, ACT_IDLE_ANGRY_MELEE, false }, - { ACT_IDLE_AIM_AGITATED, ACT_IDLE_ANGRY_MELEE, false },//always aims + { ACT_IDLE_AIM_RELAXED, ACT_IDLE_MELEE, false },//never aims + { ACT_IDLE_AIM_STIMULATED, ACT_IDLE_ANGRY_MELEE, false }, + { ACT_IDLE_AIM_AGITATED, ACT_IDLE_ANGRY_MELEE, false },//always aims + + { ACT_WALK_AIM_RELAXED, ACT_WALK_MELEE, false },//never aims + { ACT_WALK_AIM_STIMULATED, ACT_WALK_MELEE, false }, + { ACT_WALK_AIM_AGITATED, ACT_WALK_MELEE, false },//always aims + + { ACT_RUN_AIM_RELAXED, ACT_RUN_MELEE, false },//never aims + { ACT_RUN_AIM_STIMULATED, ACT_RUN_MELEE, false }, + { ACT_RUN_AIM_AGITATED, ACT_RUN_MELEE, false },//always aims //End readiness activities #else { ACT_IDLE, ACT_IDLE_ANGRY_MELEE, false }, From 179b7a529881359edff5b812d69557dbc12034f2 Mon Sep 17 00:00:00 2001 From: Peter Covington Date: Mon, 18 Apr 2022 23:46:23 -0400 Subject: [PATCH 08/90] Added factory for bullet-firing guns --- .../client/mapbase/c_weapon_custom_hl2.cpp | 29 +- sp/src/game/server/basecombatcharacter.cpp | 1 - sp/src/game/server/hl2/weapon_annabelle.cpp | 46 + sp/src/game/server/hl2/weapon_crossbow.cpp | 10 + .../server/mapbase/custom_weapon_factory.cpp | 3 +- .../server/mapbase/custom_weapon_factory.h | 4 +- .../game/server/mapbase/weapon_custom_hl2.cpp | 1173 ++++++++++++++++- .../game/shared/basecombatweapon_shared.cpp | 9 + sp/src/game/shared/basecombatweapon_shared.h | 2 +- sp/src/game/shared/weapon_parse.cpp | 22 + sp/src/game/shared/weapon_parse.h | 13 + 11 files changed, 1304 insertions(+), 8 deletions(-) diff --git a/sp/src/game/client/mapbase/c_weapon_custom_hl2.cpp b/sp/src/game/client/mapbase/c_weapon_custom_hl2.cpp index 58c46e25..8c89f2d7 100644 --- a/sp/src/game/client/mapbase/c_weapon_custom_hl2.cpp +++ b/sp/src/game/client/mapbase/c_weapon_custom_hl2.cpp @@ -6,7 +6,6 @@ // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" -#pragma region Melee class C_HLCustomWeaponMelee : public C_BaseHLBludgeonWeapon { public: @@ -31,4 +30,30 @@ C_HLCustomWeaponMelee::C_HLCustomWeaponMelee() { m_iszWeaponScriptName[0] = '\0'; } -#pragma endregion \ No newline at end of file + + + +class C_HLCustomWeaponGun : public C_BaseHLBludgeonWeapon +{ +public: + DECLARE_CLASS(C_HLCustomWeaponGun, C_BaseHLBludgeonWeapon); + DECLARE_CLIENTCLASS(); + DECLARE_PREDICTABLE(); + + C_HLCustomWeaponGun(); + + virtual const char* GetWeaponScriptName() { return m_iszWeaponScriptName; } +private: + char m_iszWeaponScriptName[128]; +}; + +STUB_WEAPON_CLASS_IMPLEMENT(weapon_hlcustomgun, C_HLCustomWeaponGun); + +IMPLEMENT_CLIENTCLASS_DT(C_HLCustomWeaponGun, DT_HLCustomWeaponGun, CHLCustomWeaponGun) +RecvPropString(RECVINFO(m_iszWeaponScriptName)), +END_RECV_TABLE(); + +C_HLCustomWeaponGun::C_HLCustomWeaponGun() +{ + m_iszWeaponScriptName[0] = '\0'; +} \ No newline at end of file diff --git a/sp/src/game/server/basecombatcharacter.cpp b/sp/src/game/server/basecombatcharacter.cpp index f4b35392..5ea5c65d 100644 --- a/sp/src/game/server/basecombatcharacter.cpp +++ b/sp/src/game/server/basecombatcharacter.cpp @@ -2789,7 +2789,6 @@ Activity CBaseCombatCharacter::Weapon_BackupActivity( Activity activity, bool we if (pTable && GetModelPtr()) { - int actCount = pWeapon->GetBackupActivityListCount(); return Weapon_BackupActivityFromList( this, pTable, actCount, activity, weaponTranslationWasRequired, pWeapon ); } diff --git a/sp/src/game/server/hl2/weapon_annabelle.cpp b/sp/src/game/server/hl2/weapon_annabelle.cpp index 6b7ecdd9..835fcc6a 100644 --- a/sp/src/game/server/hl2/weapon_annabelle.cpp +++ b/sp/src/game/server/hl2/weapon_annabelle.cpp @@ -121,6 +121,33 @@ acttable_t CWeaponAnnabelle::m_acttable[] = { ACT_RELOAD_LOW, ACT_RELOAD_ANNABELLE_LOW, false }, { ACT_GESTURE_RELOAD, ACT_GESTURE_RELOAD_ANNABELLE, false }, + // Readiness activities (not aiming) + { ACT_IDLE_RELAXED, ACT_IDLE_AR2_RELAXED, false },//never aims + { ACT_IDLE_STIMULATED, ACT_IDLE_AR2_STIMULATED, false }, + { ACT_IDLE_AGITATED, ACT_IDLE_ANGRY_AR2, false },//always aims + + { ACT_WALK_RELAXED, ACT_WALK_AR2_RELAXED, false },//never aims + { ACT_WALK_STIMULATED, ACT_WALK_AR2_STIMULATED, false }, + { ACT_WALK_AGITATED, ACT_WALK_AIM_AR2, false },//always aims + + { ACT_RUN_RELAXED, ACT_RUN_AR2_RELAXED, false },//never aims + { ACT_RUN_STIMULATED, ACT_RUN_AR2_STIMULATED, false }, + { ACT_RUN_AGITATED, ACT_RUN_AIM_RIFLE, false },//always aims + +// Readiness activities (aiming) + { ACT_IDLE_AIM_RELAXED, ACT_IDLE_AR2_RELAXED, false },//never aims + { ACT_IDLE_AIM_STIMULATED, ACT_IDLE_AIM_AR2_STIMULATED, false }, + { ACT_IDLE_AIM_AGITATED, ACT_IDLE_ANGRY_AR2, false },//always aims + + { ACT_WALK_AIM_RELAXED, ACT_WALK_AR2_RELAXED, false },//never aims + { ACT_WALK_AIM_STIMULATED, ACT_WALK_AIM_AR2_STIMULATED, false }, + { ACT_WALK_AIM_AGITATED, ACT_WALK_AIM_AR2, false },//always aims + + { ACT_RUN_AIM_RELAXED, ACT_RUN_AR2_RELAXED, false },//never aims + { ACT_RUN_AIM_STIMULATED, ACT_RUN_AIM_AR2_STIMULATED, false }, + { ACT_RUN_AIM_AGITATED, ACT_RUN_AIM_RIFLE, false },//always aims +//End readiness activities + { ACT_ARM, ACT_ARM_RIFLE, true }, { ACT_DISARM, ACT_DISARM_RIFLE, true }, #else @@ -143,6 +170,13 @@ acttable_t CWeaponAnnabelle::m_acttable[] = { ACT_GESTURE_RELOAD, ACT_GESTURE_RELOAD_SMG1, false }, #endif +#if EXPANDED_HL2_COVER_ACTIVITIES + { ACT_COVER_WALL_R, ACT_COVER_WALL_R_RIFLE, false }, + { ACT_COVER_WALL_L, ACT_COVER_WALL_L_RIFLE, false }, + { ACT_COVER_WALL_LOW_R, ACT_COVER_WALL_LOW_R_RIFLE, false }, + { ACT_COVER_WALL_LOW_L, ACT_COVER_WALL_LOW_L_RIFLE, false }, +#endif + #ifdef MAPBASE // HL2:DM activities (for third-person animations in SP) { ACT_HL2MP_IDLE, ACT_HL2MP_IDLE_AR2, false }, @@ -161,6 +195,18 @@ acttable_t CWeaponAnnabelle::m_acttable[] = IMPLEMENT_ACTTABLE(CWeaponAnnabelle); +#ifdef MAPBASE +acttable_t* GetAnnabelleActtable() +{ + return CWeaponAnnabelle::m_acttable; +} + +int GetAnnabelleActtableCount() +{ + return ARRAYSIZE(CWeaponAnnabelle::m_acttable); +} +#endif // MAPBASE + void CWeaponAnnabelle::Precache( void ) { CBaseCombatWeapon::Precache(); diff --git a/sp/src/game/server/hl2/weapon_crossbow.cpp b/sp/src/game/server/hl2/weapon_crossbow.cpp index 8a0f306b..aaed3de0 100644 --- a/sp/src/game/server/hl2/weapon_crossbow.cpp +++ b/sp/src/game/server/hl2/weapon_crossbow.cpp @@ -762,6 +762,16 @@ acttable_t CWeaponCrossbow::m_acttable[] = }; IMPLEMENT_ACTTABLE(CWeaponCrossbow); + +acttable_t* GetCrossbowActtable() +{ + return CWeaponCrossbow::m_acttable; +} + +int GetCrossbowActtableCount() +{ + return ARRAYSIZE(CWeaponCrossbow::m_acttable); +} #endif //----------------------------------------------------------------------------- diff --git a/sp/src/game/server/mapbase/custom_weapon_factory.cpp b/sp/src/game/server/mapbase/custom_weapon_factory.cpp index e010bc3f..a8e17146 100644 --- a/sp/src/game/server/mapbase/custom_weapon_factory.cpp +++ b/sp/src/game/server/mapbase/custom_weapon_factory.cpp @@ -7,7 +7,7 @@ extern ConVar mapbase_load_default_manifest; - +IMPLEMENT_PRIVATE_SYMBOLTYPE(CustomWeaponSymbol); CCustomWeaponSystem::CCustomWeaponSystem() : CAutoGameSystem("CustomWeaponFactorySystem") { @@ -155,6 +155,7 @@ void CCustomWeaponSystem::LevelShutdownPostEntity() } m_ClassFactories.Purge(); + g_CustomWeaponSymbolSymbolTable.RemoveAll(); } void CCustomWeaponSystem::ParseWeapon(CBaseCombatWeapon* pWeapon, const char* pClassName) diff --git a/sp/src/game/server/mapbase/custom_weapon_factory.h b/sp/src/game/server/mapbase/custom_weapon_factory.h index c1af48c6..fc01045d 100644 --- a/sp/src/game/server/mapbase/custom_weapon_factory.h +++ b/sp/src/game/server/mapbase/custom_weapon_factory.h @@ -4,6 +4,8 @@ #include "utldict.h" #include "utlsymbol.h" +DECLARE_PRIVATE_SYMBOLTYPE(CustomWeaponSymbol); + CUtlDict< IEntityFactory*, unsigned short >& CustomWeaponsFactoryDictionary(); class ICustomWeapon @@ -31,7 +33,7 @@ private: typedef struct CustomClassName_s { - CUtlSymbol sDataFile; + CustomWeaponSymbol sDataFile; IEntityFactory* pNewFactory; IEntityFactory* pOldFactory; } CustomClassName_t; diff --git a/sp/src/game/server/mapbase/weapon_custom_hl2.cpp b/sp/src/game/server/mapbase/weapon_custom_hl2.cpp index 22c2aad7..c2881c6f 100644 --- a/sp/src/game/server/mapbase/weapon_custom_hl2.cpp +++ b/sp/src/game/server/mapbase/weapon_custom_hl2.cpp @@ -4,11 +4,29 @@ #include "ai_basenpc.h" #include "player.h" #include "npcevent.h" +#include "in_buttons.h" // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" -#pragma region Melee +// Acttables +extern acttable_t* GetSMG1Acttable(); +extern int GetSMG1ActtableCount(); +extern acttable_t* GetPistolActtable(); +extern int GetPistolActtableCount(); +extern acttable_t* GetShotgunActtable(); +extern int GetShotgunActtableCount(); +extern acttable_t* Get357Acttable(); +extern int Get357ActtableCount(); +extern acttable_t* GetAR2Acttable(); +extern int GetAR2ActtableCount(); +extern acttable_t* GetCrossbowActtable(); +extern int GetCrossbowActtableCount(); +extern acttable_t* GetAnnabelleActtable(); +extern int GetAnnabelleActtableCount(); + + + const char* g_ppszDamageClasses[] = { "BLUNT", "SLASH", @@ -320,4 +338,1155 @@ void CHLCustomWeaponMelee::ParseCustomFromWeaponFile(const char* pFileName) } } } -#pragma endregion \ No newline at end of file + +//-------------------------------------------------------------------------- +// +// Custom ranged weapon +// +//-------------------------------------------------------------------------- + +class CHLCustomWeaponGun : public CBaseHLCombatWeapon, public ICustomWeapon +{ +public: + DECLARE_CLASS(CHLCustomWeaponGun, CBaseHLCombatWeapon); + DECLARE_SERVERCLASS(); + DECLARE_DATADESC(); + + CHLCustomWeaponGun(); + virtual void ParseCustomFromWeaponFile(const char* pFileName); + + // Weapon behaviour + virtual void ItemPostFrame(void); // called each frame by the player PostThink + virtual void ItemBusyFrame(void); // called each frame by the player PostThink, if the player's not ready to attack yet + virtual bool ReloadOrSwitchWeapons(void); + + // Bullet launch information + virtual const Vector& GetBulletSpread(void); + virtual float GetFireRate(void) { return m_flFireRate; } + virtual int GetMinBurst() { return m_nMinBurst; } + virtual int GetMaxBurst() { return m_nMaxBurst; } + virtual float GetMinRestTime() { return m_RestInterval.start; } + virtual float GetMaxRestTime() { return m_RestInterval.start + m_RestInterval.range; } + + // Autoaim + virtual float GetMaxAutoAimDeflection() { return 0.99f; } + virtual float WeaponAutoAimScale() { return m_flAutoAimScale; } // allows a weapon to influence the perceived size of the target's autoaim radius. + + virtual void AddViewKick(void); + int WeaponSoundRealtime(WeaponSound_t shoot_type); + + bool StartReload(void); + bool Reload(void); + void FillClip(void); + void FinishReload(void); + void Pump(void); + + void PrimaryAttack(); + + void FireNPCPrimaryAttack(CBaseCombatCharacter* pOperator, bool bUseWeaponAngles); + void FireNPCSecondaryAttack(CBaseCombatCharacter* pOperator, bool bUseWeaponAngles); + void Operator_ForceNPCFire(CBaseCombatCharacter* pOperator, bool bSecondary); + void Operator_HandleAnimEvent(animevent_t* pEvent, CBaseCombatCharacter* pOperator); + int CapabilitiesGet(void) { return bits_CAP_WEAPON_RANGE_ATTACK1; } + + Activity GetPrimaryAttackActivity(void); + + virtual acttable_t* ActivityList(void); + virtual int ActivityListCount(void); + + virtual acttable_t* GetBackupActivityList(); + virtual int GetBackupActivityListCount(); +private: + void CheckZoomToggle(void); + void ToggleZoom(void); + +private: + CNetworkString(m_iszWeaponScriptName, 128); + + float m_flFireRate; + int m_nMinBurst; + int m_nMaxBurst; + interval_t m_RestInterval; + + float m_flAutoAimScale; + + Vector m_vPlayerSpread; + Vector m_vAllySpread; + Vector m_vNPCSpread; + int m_nBulletsPerShot; // For shotguns + + // Viewkick + float m_flMaxVerticalKick; + float m_flSlideLimit; + interval_t m_VerticalPunchRange; + + int m_nActTableIndex; + + bool m_bUseRecoilAnims; + bool m_bFullAuto; // True for machine gun, false for semi-auto + bool m_bNextAttackFromSequence; + bool m_bUsePumpAnimation; + bool m_bHasSecondaryFire; + bool m_bHasZoom; + bool m_bZoomDuringReload; + + bool m_bNeedPump; // When emptied completely + bool m_bDelayedFire1; // Fire primary when finished reloading + bool m_bDelayedFire2; // Fire secondary when finished reloading + bool m_bInZoom; + bool m_bMustReload; + + int m_nShotsFired; // Number of consecutive shots fired + float m_flNextSoundTime; // real-time clock of when to make next sound +public: + enum WeaponActTable_e + { + ACTTABLE_SMG1 = 0, + ACTTABLE_PISTOL, + ACTTABLE_REVOLVER, + ACTTABLE_SHOTGUN, + ACTTABLE_AR2, + ACTTABLE_CROSSBOW, + ACTTABLE_ANNABELLE, + + NUM_GUN_ACT_TABLES + }; +}; + +IMPLEMENT_SERVERCLASS_ST(CHLCustomWeaponGun, DT_HLCustomWeaponGun) +SendPropString(SENDINFO(m_iszWeaponScriptName)), +END_SEND_TABLE(); + +BEGIN_DATADESC(CHLCustomWeaponGun) +DEFINE_FIELD(m_nShotsFired, FIELD_INTEGER), +DEFINE_FIELD(m_flNextSoundTime, FIELD_TIME), +DEFINE_FIELD(m_bNeedPump, FIELD_BOOLEAN), +DEFINE_FIELD(m_bDelayedFire1, FIELD_BOOLEAN), +DEFINE_FIELD(m_bDelayedFire2, FIELD_BOOLEAN), +DEFINE_FIELD(m_bInZoom, FIELD_BOOLEAN), +DEFINE_FIELD(m_bMustReload, FIELD_BOOLEAN), +END_DATADESC(); + +DEFINE_CUSTOM_WEAPON_FACTORY(hl2_gun, CHLCustomWeaponGun); + +CHLCustomWeaponGun::CHLCustomWeaponGun() +{ + m_flFireRate = 0.5f; + m_nMinBurst = 1; + m_nMaxBurst = 1; + m_RestInterval.start = .3f; + m_RestInterval.range = .3f; + + m_flAutoAimScale = 1.f; + m_nBulletsPerShot = 1; + + m_bUseRecoilAnims = false; + m_bFullAuto = false; + m_bNextAttackFromSequence = false; + m_bUsePumpAnimation = false; + m_bHasSecondaryFire = false; + m_bHasZoom = false; + m_bZoomDuringReload = false; + m_bFiresUnderwater = false; + + m_bNeedPump = false; + m_bDelayedFire1 = false; + m_bDelayedFire2 = false; + m_bInZoom = false; + m_bMustReload = false; + m_nShotsFired = 0; +} + +acttable_t* CHLCustomWeaponGun::ActivityList(void) +{ + switch (m_nActTableIndex) + { + default: + case ACTTABLE_SMG1: + return GetSMG1Acttable(); + break; + case ACTTABLE_PISTOL: + return GetPistolActtable(); + break; + case ACTTABLE_REVOLVER: + return Get357Acttable(); + break; + case ACTTABLE_SHOTGUN: + return GetShotgunActtable(); + break; + case ACTTABLE_AR2: + return GetAR2Acttable(); + break; + case ACTTABLE_CROSSBOW: + return GetCrossbowActtable(); + break; + case ACTTABLE_ANNABELLE: + return GetAnnabelleActtable(); + break; + } +} + +int CHLCustomWeaponGun::ActivityListCount(void) +{ + switch (m_nActTableIndex) + { + default: + case ACTTABLE_SMG1: + return GetSMG1ActtableCount(); + break; + case ACTTABLE_PISTOL: + return GetPistolActtableCount(); + break; + case ACTTABLE_REVOLVER: + return Get357ActtableCount(); + break; + case ACTTABLE_SHOTGUN: + return GetShotgunActtableCount(); + break; + case ACTTABLE_AR2: + return GetAR2ActtableCount(); + break; + case ACTTABLE_CROSSBOW: + return GetCrossbowActtableCount(); + break; + case ACTTABLE_ANNABELLE: + return GetAnnabelleActtableCount(); + break; + } +} + +acttable_t* CHLCustomWeaponGun::GetBackupActivityList(void) +{ + switch (m_nActTableIndex) + { + default: + case ACTTABLE_SMG1: + case ACTTABLE_CROSSBOW: + case ACTTABLE_AR2: + return GetSMG1Acttable(); + break; + case ACTTABLE_PISTOL: + case ACTTABLE_REVOLVER: + return GetPistolActtable(); + break; + case ACTTABLE_SHOTGUN: + case ACTTABLE_ANNABELLE: + return GetShotgunActtable(); + break; + } +} + +int CHLCustomWeaponGun::GetBackupActivityListCount(void) +{ + switch (m_nActTableIndex) + { + default: + case ACTTABLE_SMG1: + case ACTTABLE_CROSSBOW: + case ACTTABLE_AR2: + return GetSMG1ActtableCount(); + break; + case ACTTABLE_PISTOL: + case ACTTABLE_REVOLVER: + return GetPistolActtableCount(); + break; + case ACTTABLE_SHOTGUN: + case ACTTABLE_ANNABELLE: + return GetShotgunActtableCount(); + break; + } +} + +void ReadIntervalInt(const char* pString, int &iMin, int &iMax) +{ + char tempString[128]; + Q_strncpy(tempString, pString, sizeof(tempString)); + + char* token = strtok(tempString, ","); + if (token) + { + iMin = atoi(token); + token = strtok(NULL, ","); + if (token) + { + iMax = atoi(token); + } + else + { + iMax = iMin; + } + } +} + +void CHLCustomWeaponGun::ParseCustomFromWeaponFile(const char* pFileName) +{ + static const char* ppszCustomGunAnimTypes[NUM_GUN_ACT_TABLES] = { + "smg", + "pistol", + "revolver", + "shotgun", + "ar2", + "crossbow", + "annabelle", + }; + + Q_FileBase(pFileName, m_iszWeaponScriptName.GetForModify(), 128); + KeyValuesAD pKVWeapon("WeaponData"); + if (pKVWeapon->LoadFromFile(filesystem, pFileName, "GAME")) + { + KeyValues* pkvData = pKVWeapon->FindKey("CustomData"); + if (pkvData) + { + m_flFireRate = pkvData->GetFloat("fire_rate", 0.5f); + ReadIntervalInt(pkvData->GetString("npc_burst", "1"), m_nMinBurst, m_nMaxBurst); + m_RestInterval = ReadInterval(pkvData->GetString("npc_rest_time", "0.3,0.6")); + m_flAutoAimScale = pkvData->GetFloat("autoaim_scale", 1.f); + m_bFullAuto = pkvData->GetBool("auto_fire"); + m_nBulletsPerShot = pkvData->GetInt("bullets", 1); + m_bUseRecoilAnims = pkvData->GetBool("recoil_anims", true); + m_bReloadsSingly = pkvData->GetBool("reload_singly"); + m_bFiresUnderwater = pkvData->GetBool("fires_underwater"); + m_bHasZoom = pkvData->GetBool("zoom_enable"); + m_bZoomDuringReload = m_bHasZoom && pkvData->GetBool("zoom_in_reload"); + + m_fMinRange1 = pkvData->GetFloat("range1_min", 65.f); + m_fMinRange2 = pkvData->GetFloat("range2_min", 65.f); + m_fMaxRange1 = pkvData->GetFloat("range1_max", 1024.f); + m_fMaxRange2 = pkvData->GetFloat("range2_max", 1024.f); + + if (m_bFullAuto) + { + m_flMaxVerticalKick = pkvData->GetFloat("viewkick_vertical_max", 1.f); + m_flSlideLimit = pkvData->GetFloat("viewkick_slide_limit", 2.f); + } + else + { + m_flSlideLimit = pkvData->GetFloat("viewpunch_side_max", .6f); + m_VerticalPunchRange = ReadInterval(pkvData->GetString("viewpunch_vertical", "0.25,0.5")); + + m_bNextAttackFromSequence = pkvData->GetBool("next_attack_time_from_sequence"); + m_bUsePumpAnimation = pkvData->GetBool("use_pump_anim"); + } + + // NOTE: The way these are calculated is that each component == sin (degrees/2) + float flSpread = pkvData->GetFloat("spread", 5.f); + float flNPCSpread = pkvData->GetFloat("spread_npc", flSpread); + float flAllySperad = pkvData->GetFloat("spread_ally", flNPCSpread); + m_vPlayerSpread = Vector(sin(flSpread * 0.5f)); + m_vNPCSpread = Vector(sin(flNPCSpread * 0.5f)); + m_vAllySpread = Vector(sin(flAllySperad * 0.5f)); + + const char* pszAnimType = pkvData->GetString("anim_type", nullptr); + if (pszAnimType) + { + for (int i = 0; i < NUM_GUN_ACT_TABLES; i++) + { + if (V_stricmp(pszAnimType, ppszCustomGunAnimTypes[i]) == 0) + { + m_nActTableIndex = i; + break; + } + } + } + } + } +} + +const Vector& CHLCustomWeaponGun::GetBulletSpread() +{ + if (!GetOwner() || !GetOwner()->IsNPC()) + return m_vPlayerSpread; + + if (GetOwner()->MyNPCPointer()->IsPlayerAlly()) + { + // 357 allies should be cooler + return m_vAllySpread; + } + + return m_vNPCSpread; +} + +void CHLCustomWeaponGun::AddViewKick(void) +{ + //Get the view kick + CBasePlayer* pPlayer = ToBasePlayer(GetOwner()); + + if (!pPlayer) + return; + + if (m_bFullAuto) + { + float flDuration = m_fFireDuration; + + if (g_pGameRules->GetAutoAimMode() == AUTOAIM_ON_CONSOLE) + { + // On the 360 (or in any configuration using the 360 aiming scheme), don't let the + // AR2 progressive into the late, highly inaccurate stages of its kick. Just + // spoof the time to make it look (to the kicking code) like we haven't been + // firing for very long. + flDuration = MIN(flDuration, 0.75f); + } + + CHLMachineGun::DoMachineGunKick(pPlayer, 0.5f, m_flMaxVerticalKick, flDuration, m_flSlideLimit); + } + else + { + QAngle viewPunch; + viewPunch.x = RandomInterval(m_VerticalPunchRange); + viewPunch.y = RandomFloat(-m_flSlideLimit, m_flSlideLimit); + viewPunch.z = 0.0f; + + //Add it to the view punch + pPlayer->ViewPunch(viewPunch); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CHLCustomWeaponGun::CheckZoomToggle(void) +{ + if (!m_bHasZoom) + return; + + CBasePlayer* pPlayer = ToBasePlayer(GetOwner()); + + int iButtonsTest = IN_ATTACK3; + if (!m_bHasSecondaryFire) + iButtonsTest |= IN_ATTACK2; + + if (pPlayer->m_afButtonPressed & iButtonsTest) + { + ToggleZoom(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CHLCustomWeaponGun::ToggleZoom(void) +{ + CBasePlayer* pPlayer = ToBasePlayer(GetOwner()); + + if (pPlayer == NULL) + return; + + if (m_bInZoom) + { + if (pPlayer->SetFOV(this, 0, 0.2f)) + { + m_bInZoom = false; + } + } + else + { + if (pPlayer->SetFOV(this, 20, 0.1f)) + { + m_bInZoom = true; + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Override so only reload one shell at a time +// Input : +// Output : +//----------------------------------------------------------------------------- +bool CHLCustomWeaponGun::StartReload(void) +{ + CBaseCombatCharacter* pOwner = GetOwner(); + + if (pOwner == NULL) + return false; + + if (pOwner->GetAmmoCount(m_iPrimaryAmmoType) <= 0) + return false; + + if (m_iClip1 >= GetMaxClip1()) + return false; + + // If shotgun totally emptied then a pump animation is needed + + //NOTENOTE: This is kinda lame because the player doesn't get strong feedback on when the reload has finished, + // without the pump. Technically, it's incorrect, but it's good for feedback... + + if (m_bUsePumpAnimation && m_iClip1 <= 0) + { + m_bNeedPump = true; + } + + int j = MIN(1, pOwner->GetAmmoCount(m_iPrimaryAmmoType)); + + if (j <= 0) + return false; + + SendWeaponAnim(ACT_SHOTGUN_RELOAD_START); + + // Make shotgun shell visible + SetBodygroup(1, 0); + + pOwner->m_flNextAttack = gpGlobals->curtime; + m_flNextPrimaryAttack = gpGlobals->curtime + SequenceDuration(); + +#ifdef MAPBASE + if (pOwner->IsPlayer()) + { + static_cast(pOwner)->SetAnimation(PLAYER_RELOAD); + } +#endif + + if (m_bInZoom && !m_bZoomDuringReload) + { + ToggleZoom(); + } + + m_bInReload = true; + m_bMustReload = false; + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: Override so only reload one shell at a time +// Input : +// Output : +//----------------------------------------------------------------------------- +bool CHLCustomWeaponGun::Reload(void) +{ + if (m_bReloadsSingly) + { + // Check that StartReload was called first + if (!m_bInReload) + { + Warning("ERROR: Shotgun Reload called incorrectly!\n"); + } + + CBaseCombatCharacter* pOwner = GetOwner(); + + if (pOwner == NULL) + return false; + + if (pOwner->GetAmmoCount(m_iPrimaryAmmoType) <= 0) + return false; + + if (m_iClip1 >= GetMaxClip1()) + return false; + + int j = MIN(1, pOwner->GetAmmoCount(m_iPrimaryAmmoType)); + + if (j <= 0) + return false; + + FillClip(); + // Play reload on different channel as otherwise steals channel away from fire sound + WeaponSound(RELOAD); + SendWeaponAnim(ACT_VM_RELOAD); + + pOwner->m_flNextAttack = gpGlobals->curtime; + m_flNextPrimaryAttack = gpGlobals->curtime + SequenceDuration(); + + return true; + } + else if (BaseClass::Reload()) + { + if (m_bInZoom && !m_bZoomDuringReload) + { + ToggleZoom(); + } + + m_bMustReload = false; + return true; + } + + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: Play finish reload anim and fill clip +// Input : +// Output : +//----------------------------------------------------------------------------- +void CHLCustomWeaponGun::FinishReload(void) +{ + if (m_bReloadsSingly) + { + // Make shotgun shell invisible + SetBodygroup(1, 1); + + CBaseCombatCharacter* pOwner = GetOwner(); + + if (pOwner == NULL) + return; + + m_bInReload = false; + + // Finish reload animation + SendWeaponAnim(ACT_SHOTGUN_RELOAD_FINISH); + + pOwner->m_flNextAttack = gpGlobals->curtime; + m_flNextPrimaryAttack = gpGlobals->curtime + SequenceDuration(); + } + else + { + BaseClass::FinishReload(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Play finish reload anim and fill clip +// Input : +// Output : +//----------------------------------------------------------------------------- +void CHLCustomWeaponGun::FillClip(void) +{ + CBaseCombatCharacter* pOwner = GetOwner(); + + if (pOwner == NULL) + return; + + // Add them to the clip + if (pOwner->GetAmmoCount(m_iPrimaryAmmoType) > 0) + { + if (Clip1() < GetMaxClip1()) + { + m_iClip1++; + pOwner->RemoveAmmo(1, m_iPrimaryAmmoType); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Play weapon pump anim +// Input : +// Output : +//----------------------------------------------------------------------------- +void CHLCustomWeaponGun::Pump(void) +{ + CBaseCombatCharacter* pOwner = GetOwner(); + + if (pOwner == NULL) + return; + + m_bNeedPump = false; + + WeaponSound(SPECIAL1); + + // Finish reload animation + SendWeaponAnim(ACT_SHOTGUN_PUMP); + + pOwner->m_flNextAttack = gpGlobals->curtime + SequenceDuration(); + m_flNextPrimaryAttack = gpGlobals->curtime + SequenceDuration(); +} + +//----------------------------------------------------------------------------- +// Purpose: If the current weapon has more ammo, reload it. Otherwise, switch +// to the next best weapon we've got. Returns true if it took any action. +//----------------------------------------------------------------------------- +bool CHLCustomWeaponGun::ReloadOrSwitchWeapons(void) +{ + CBasePlayer* pOwner = ToBasePlayer(GetOwner()); + Assert(pOwner); + + m_bFireOnEmpty = false; + + // If we don't have any ammo, switch to the next best weapon + if (!HasAnyAmmo() && m_flNextPrimaryAttack < gpGlobals->curtime && m_flNextSecondaryAttack < gpGlobals->curtime) + { + // weapon isn't useable, switch. + // Ammo might be overridden to 0, in which case we shouldn't do this + if (((GetWeaponFlags() & ITEM_FLAG_NOAUTOSWITCHEMPTY) == false) && !HasSpawnFlags(SF_WEAPON_NO_AUTO_SWITCH_WHEN_EMPTY) && (g_pGameRules->SwitchToNextBestWeapon(pOwner, this))) + { + m_flNextPrimaryAttack = gpGlobals->curtime + 0.3; + return true; + } + } + else + { + // Weapon is useable. Reload if empty and weapon has waited as long as it has to after firing + if (UsesClipsForAmmo1() && !AutoFiresFullClip() && + (m_iClip1 == 0) && + (GetWeaponFlags() & ITEM_FLAG_NOAUTORELOAD) == false && + m_flNextPrimaryAttack < gpGlobals->curtime && + m_flNextSecondaryAttack < gpGlobals->curtime) + { + // if we're successfully reloading, we're done + if (m_bReloadsSingly) + return StartReload(); + else + return Reload(); + } + } + + return false; +} + +void CHLCustomWeaponGun::ItemBusyFrame(void) +{ + BaseClass::ItemBusyFrame(); + + if (m_bZoomDuringReload) + CheckZoomToggle(); +} + +void CHLCustomWeaponGun::ItemPostFrame(void) +{ + CBasePlayer* pOwner = ToBasePlayer(GetOwner()); + if (!pOwner) + return; + + // Debounce the recoiling counter + if ((pOwner->m_nButtons & IN_ATTACK) == false) + { + m_nShotsFired = 0; + } + + UpdateAutoFire(); + + if (m_bZoomDuringReload || !m_bInReload) + CheckZoomToggle(); + + if (m_bReloadsSingly) + { + if (m_bInReload) + { + m_fFireDuration = 0.f; + + // If I'm primary firing and have one round stop reloading and fire + if ((pOwner->m_nButtons & IN_ATTACK) && (m_iClip1 >= 1)) + { + m_bInReload = false; + m_bNeedPump = false; + m_bDelayedFire1 = true; + } + // If I'm secondary firing and have one round stop reloading and fire + else if (m_bHasSecondaryFire && (pOwner->m_nButtons & IN_ATTACK2) && (m_iClip1 >= 2)) + { + m_bInReload = false; + m_bNeedPump = false; + m_bDelayedFire2 = true; + } + else if (m_flNextPrimaryAttack <= gpGlobals->curtime) + { + // If out of ammo end reload + if (pOwner->GetAmmoCount(m_iPrimaryAmmoType) <= 0) + { + FinishReload(); + return; + } + // If clip not full reload again + if (m_iClip1 < GetMaxClip1()) + { + Reload(); + return; + } + // Clip full, stop reloading + else + { + FinishReload(); + return; + } + } + } + else + { + // Make shotgun shell invisible + SetBodygroup(1, 1); + } + } + else if (UsesClipsForAmmo1()) + { + CheckReload(); + } + + if (m_bUsePumpAnimation && (m_bNeedPump) && (m_flNextPrimaryAttack <= gpGlobals->curtime)) + { + m_fFireDuration = 0.f; + Pump(); + return; + } + + //Track the duration of the fire + //FIXME: Check for IN_ATTACK2 as well? + //FIXME: What if we're calling ItemBusyFrame? + m_fFireDuration = (pOwner->m_nButtons & IN_ATTACK) ? (m_fFireDuration + gpGlobals->frametime) : 0.0f; + + bool bFired = false; + + // Secondary attack has priority + if (m_bHasSecondaryFire && !m_bMustReload && (m_bDelayedFire2 || pOwner->m_nButtons & IN_ATTACK2) && (m_flNextSecondaryAttack <= gpGlobals->curtime)) + { + m_bDelayedFire2 = false; + + if (pOwner->HasSpawnFlags(SF_PLAYER_SUPPRESS_FIRING)) + { + // Don't do anything, just cancel the whole function + return; + } + else if (UsesSecondaryAmmo() && pOwner->GetAmmoCount(m_iSecondaryAmmoType) <= 0) + { + if (m_flNextEmptySoundTime < gpGlobals->curtime) + { + WeaponSound(EMPTY); + m_flNextSecondaryAttack = m_flNextEmptySoundTime = gpGlobals->curtime + 0.5; + } + } + else if (pOwner->GetWaterLevel() == 3 && m_bAltFiresUnderwater == false) + { + // This weapon doesn't fire underwater + WeaponSound(EMPTY); + m_flNextPrimaryAttack = gpGlobals->curtime + 0.2; + return; + } + else + { + // FIXME: This isn't necessarily true if the weapon doesn't have a secondary fire! + // For instance, the crossbow doesn't have a 'real' secondary fire, but it still + // stops the crossbow from firing on the 360 if the player chooses to hold down their + // zoom button. (sjb) Orange Box 7/25/2007 +#if !defined(CLIENT_DLL) + if (!IsX360() || !ClassMatches("weapon_crossbow")) +#endif + { + bFired = ShouldBlockPrimaryFire(); + } + + SecondaryAttack(); + + // Secondary ammo doesn't have a reload animation + if (UsesClipsForAmmo2()) + { + // reload clip2 if empty + if (m_iClip2 < 1) + { + pOwner->RemoveAmmo(1, m_iSecondaryAmmoType); + m_iClip2 = m_iClip2 + 1; + } + } + } + } + + if (!bFired && !m_bMustReload && (m_bDelayedFire1 || pOwner->m_nButtons & IN_ATTACK) && (m_flNextPrimaryAttack <= gpGlobals->curtime)) + { + m_bDelayedFire1 = false; + + if (pOwner->HasSpawnFlags(SF_PLAYER_SUPPRESS_FIRING)) + { + // Don't do anything, just cancel the whole function + return; + } + // Clip empty? Or out of ammo on a no-clip weapon? + else if ((UsesClipsForAmmo1() && m_iClip1 <= 0) || (!UsesClipsForAmmo1() && pOwner->GetAmmoCount(m_iPrimaryAmmoType) <= 0)) + { + HandleFireOnEmpty(); + } + else if (pOwner->GetWaterLevel() == 3 && m_bFiresUnderwater == false) + { + // This weapon doesn't fire underwater + WeaponSound(EMPTY); + m_flNextPrimaryAttack = gpGlobals->curtime + 0.2; + return; + } + else + { + //NOTENOTE: There is a bug with this code with regards to the way machine guns catch the leading edge trigger + // on the player hitting the attack key. It relies on the gun catching that case in the same frame. + // However, because the player can also be doing a secondary attack, the edge trigger may be missed. + // We really need to hold onto the edge trigger and only clear the condition when the gun has fired its + // first shot. Right now that's too much of an architecture change -- jdw + + // If the firing button was just pressed, or the alt-fire just released, reset the firing time + if ((pOwner->m_afButtonPressed & IN_ATTACK) || (pOwner->m_afButtonReleased & IN_ATTACK2)) + { + m_flNextPrimaryAttack = gpGlobals->curtime; + } + + PrimaryAttack(); + + if (AutoFiresFullClip()) + { + m_bFiringWholeClip = true; + } + +#ifdef CLIENT_DLL + pOwner->SetFiredWeapon(true); +#endif + } + } + + // ----------------------- + // Reload pressed / Clip Empty + // ----------------------- + if ((pOwner->m_nButtons & IN_RELOAD || m_bMustReload) && UsesClipsForAmmo1() && !m_bInReload) + { + // reload when reload is pressed, or if no buttons are down and weapon is empty. + if (m_bReloadsSingly) + StartReload(); + else + Reload(); + + m_fFireDuration = 0.0f; + } + + // ----------------------- + // No buttons down + // ----------------------- + else if (!((pOwner->m_nButtons & IN_ATTACK) || (pOwner->m_nButtons & IN_ATTACK2) || (CanReload() && pOwner->m_nButtons & IN_RELOAD))) + { + // no fire buttons down or reloading + if (!ReloadOrSwitchWeapons() && (m_bInReload == false)) + { + WeaponIdle(); + } + } +} + +void CHLCustomWeaponGun::PrimaryAttack() +{ + // Only the player fires this way so we can cast + CBasePlayer* pPlayer = ToBasePlayer(GetOwner()); + if (!pPlayer) + return; + + // Abort here to handle burst and auto fire modes + if ((UsesClipsForAmmo1() && m_iClip1 == 0) || (!UsesClipsForAmmo1() && !pPlayer->GetAmmoCount(m_iPrimaryAmmoType))) + return; + + if (m_bFullAuto) + { + m_nShotsFired++; + + pPlayer->DoMuzzleFlash(); + + // To make the firing framerate independent, we may have to fire more than one bullet here on low-framerate systems, + // especially if the weapon we're firing has a really fast rate of fire. + int iBulletsToFire = 0; + float fireRate = GetFireRate(); + + // MUST call sound before removing a round from the clip of a CHLMachineGun + while (m_flNextPrimaryAttack <= gpGlobals->curtime) + { + WeaponSound(SINGLE, m_flNextPrimaryAttack); + m_flNextPrimaryAttack = m_flNextPrimaryAttack + fireRate; + iBulletsToFire++; + } + + // Make sure we don't fire more than the amount in the clip, if this weapon uses clips + if (UsesClipsForAmmo1()) + { + if (iBulletsToFire > m_iClip1) + iBulletsToFire = m_iClip1; + m_iClip1 -= iBulletsToFire; + } + + // Fire the bullets + FireBulletsInfo_t info; + info.m_iShots = iBulletsToFire * m_nBulletsPerShot; + info.m_vecSrc = pPlayer->Weapon_ShootPosition(); + info.m_vecDirShooting = pPlayer->GetAutoaimVector(AUTOAIM_SCALE_DEFAULT); + info.m_vecSpread = pPlayer->GetAttackSpread(this); + info.m_flDistance = MAX_TRACE_LENGTH; + info.m_iAmmoType = m_iPrimaryAmmoType; + info.m_iTracerFreq = 2; + FireBullets(info); + } + else + { + if (!m_bNextAttackFromSequence && !m_bUsePumpAnimation && !(pPlayer->m_afButtonPressed & IN_ATTACK)) + return; + + m_nShotsFired++; + + // MUST call sound before removing a round from the clip of a CMachineGun + WeaponSound(SINGLE); + + pPlayer->DoMuzzleFlash(); + + m_flNextPrimaryAttack = gpGlobals->curtime + ((m_bNextAttackFromSequence || m_bUsePumpAnimation) ? GetViewModelSequenceDuration() : GetFireRate()); + m_iClip1 -= 1; + + Vector vecSrc = pPlayer->Weapon_ShootPosition(); + Vector vecAiming = pPlayer->GetAutoaimVector(AUTOAIM_SCALE_DEFAULT); + + pPlayer->SetMuzzleFlashTime(gpGlobals->curtime + 1.0); + + // Fire the bullets, and force the first shot to be perfectly accuracy + pPlayer->FireBullets(m_nBulletsPerShot, vecSrc, vecAiming, GetBulletSpread(), MAX_TRACE_LENGTH, m_iPrimaryAmmoType, 0, -1, -1, 0, NULL, (m_nBulletsPerShot > 1), true); + + if (m_bUsePumpAnimation && m_iClip1) + { + // pump so long as some rounds are left. + m_bNeedPump = true; + } + } + + m_iPrimaryAttacks++; + + //Factor in the view kick + AddViewKick(); + + CSoundEnt::InsertSound(SOUND_COMBAT, GetAbsOrigin(), SOUNDENT_VOLUME_MACHINEGUN, 0.2, pPlayer); + + if (!m_iClip1 && pPlayer->GetAmmoCount(m_iPrimaryAmmoType) <= 0) + { + // HEV suit - indicate out of ammo condition + pPlayer->SetSuitUpdate("!HEV_AMO0", FALSE, 0); + } + + SendWeaponAnim(GetPrimaryAttackActivity()); + pPlayer->SetAnimation(PLAYER_ATTACK1); + + // Register a muzzleflash for the AI + pPlayer->SetMuzzleFlashTime(gpGlobals->curtime + 0.5); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : Activity +//----------------------------------------------------------------------------- +Activity CHLCustomWeaponGun::GetPrimaryAttackActivity(void) +{ + if (!m_bUseRecoilAnims || m_nShotsFired < 2) + return ACT_VM_PRIMARYATTACK; + + if (m_nShotsFired < 3) + return ACT_VM_RECOIL1; + + if (m_nShotsFired < 4) + return ACT_VM_RECOIL2; + + return ACT_VM_RECOIL3; +} + +//----------------------------------------------------------------------------- +// Purpose: Make enough sound events to fill the estimated think interval +// returns: number of shots needed +//----------------------------------------------------------------------------- +int CHLCustomWeaponGun::WeaponSoundRealtime(WeaponSound_t shoot_type) +{ + int numBullets = 0; + + // ran out of time, clamp to current + if (m_flNextSoundTime < gpGlobals->curtime) + { + m_flNextSoundTime = gpGlobals->curtime; + } + + // make enough sound events to fill up the next estimated think interval + float dt = Clamp(m_flAnimTime - m_flPrevAnimTime, 0.f, 0.2f); + if (m_flNextSoundTime < gpGlobals->curtime + dt) + { + WeaponSound(SINGLE_NPC, m_flNextSoundTime); + m_flNextSoundTime += GetFireRate(); + numBullets++; + } + if (m_flNextSoundTime < gpGlobals->curtime + dt) + { + WeaponSound(SINGLE_NPC, m_flNextSoundTime); + m_flNextSoundTime += GetFireRate(); + numBullets++; + } + + return numBullets; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *pOperator - +//----------------------------------------------------------------------------- +void CHLCustomWeaponGun::FireNPCPrimaryAttack(CBaseCombatCharacter* pOperator, bool bUseWeaponAngles) +{ + Vector vecShootOrigin, vecShootDir; + CAI_BaseNPC* npc = pOperator->MyNPCPointer(); + int iMuzzle = LookupAttachment("muzzle"); + + ASSERT(npc != NULL); + + if (bUseWeaponAngles) + { + QAngle angShootDir; + GetAttachment(iMuzzle, vecShootOrigin, angShootDir); + AngleVectors(angShootDir, &vecShootDir); + } + else + { + vecShootOrigin = pOperator->Weapon_ShootPosition(); + vecShootDir = npc->GetActualShootTrajectory(vecShootOrigin); + } + + CSoundEnt::InsertSound(SOUND_COMBAT | SOUND_CONTEXT_GUNFIRE, pOperator->GetAbsOrigin(), SOUNDENT_VOLUME_MACHINEGUN, 0.2f, pOperator, SOUNDENT_CHANNEL_WEAPON, pOperator->GetEnemy()); + + const Vector& vecSpread = (bUseWeaponAngles || m_nBulletsPerShot > 1) ? GetBulletSpread() : VECTOR_CONE_PRECALCULATED; + if (m_bFullAuto) + { + int nShots = WeaponSoundRealtime(SINGLE_NPC); + pOperator->FireBullets(nShots * m_nBulletsPerShot, vecShootOrigin, vecShootDir, vecSpread, MAX_TRACE_LENGTH, m_iPrimaryAmmoType, 2, entindex(), iMuzzle); + pOperator->DoMuzzleFlash(); + m_iClip1 = m_iClip1 - nShots; + } + else + { + WeaponSound(SINGLE_NPC); + pOperator->FireBullets(m_nBulletsPerShot, vecShootOrigin, vecShootDir, vecSpread, MAX_TRACE_LENGTH, m_iPrimaryAmmoType, 2, entindex(), iMuzzle); + pOperator->DoMuzzleFlash(); + m_iClip1 = m_iClip1 - 1; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CHLCustomWeaponGun::FireNPCSecondaryAttack(CBaseCombatCharacter* pOperator, bool bUseWeaponAngles) +{ + +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CHLCustomWeaponGun::Operator_ForceNPCFire(CBaseCombatCharacter* pOperator, bool bSecondary) +{ + if (bSecondary) + { + FireNPCSecondaryAttack(pOperator, true); + } + else + { + // Ensure we have enough rounds in the clip + m_iClip1++; + + FireNPCPrimaryAttack(pOperator, true); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *pEvent - +// *pOperator - +//----------------------------------------------------------------------------- +void CHLCustomWeaponGun::Operator_HandleAnimEvent(animevent_t* pEvent, CBaseCombatCharacter* pOperator) +{ + switch (pEvent->event) + { + case EVENT_WEAPON_SMG1: + case EVENT_WEAPON_SHOTGUN_FIRE: + case EVENT_WEAPON_AR1: + case EVENT_WEAPON_AR2: + case EVENT_WEAPON_HMG1: + case EVENT_WEAPON_SMG2: + case EVENT_WEAPON_SNIPER_RIFLE_FIRE: + case EVENT_WEAPON_PISTOL_FIRE: + { + FireNPCPrimaryAttack(pOperator, false); + } + break; + + case EVENT_WEAPON_AR2_ALTFIRE: + { + FireNPCSecondaryAttack(pOperator, false); + } + break; + + default: + CBaseCombatWeapon::Operator_HandleAnimEvent(pEvent, pOperator); + break; + } +} diff --git a/sp/src/game/shared/basecombatweapon_shared.cpp b/sp/src/game/shared/basecombatweapon_shared.cpp index f505fafc..a187f1a0 100644 --- a/sp/src/game/shared/basecombatweapon_shared.cpp +++ b/sp/src/game/shared/basecombatweapon_shared.cpp @@ -2888,6 +2888,15 @@ bool CBaseCombatWeapon::IsLocked( CBaseEntity *pAsker ) return ( m_flUnlockTime > gpGlobals->curtime && m_hLocker != pAsker ); } +bool CBaseCombatWeapon::CanBePickedUpByNPCs(void) +{ +#ifdef MAPBASE + return GetWpnData().m_nWeaponRestriction != WPNRESTRICT_PLAYER_ONLY; +#else + return true; +#endif // MAPBASE +} + //----------------------------------------------------------------------------- // Purpose: // Input : diff --git a/sp/src/game/shared/basecombatweapon_shared.h b/sp/src/game/shared/basecombatweapon_shared.h index bd439b8c..1b411999 100644 --- a/sp/src/game/shared/basecombatweapon_shared.h +++ b/sp/src/game/shared/basecombatweapon_shared.h @@ -390,7 +390,7 @@ public: bool IsLocked( CBaseEntity *pAsker ); //All weapons can be picked up by NPCs by default - virtual bool CanBePickedUpByNPCs( void ) { return true; } + virtual bool CanBePickedUpByNPCs(void); virtual int GetSkinOverride() const { return -1; } diff --git a/sp/src/game/shared/weapon_parse.cpp b/sp/src/game/shared/weapon_parse.cpp index d8844916..872c6ad0 100644 --- a/sp/src/game/shared/weapon_parse.cpp +++ b/sp/src/game/shared/weapon_parse.cpp @@ -408,6 +408,7 @@ FileWeaponInfo_t::FileWeaponInfo_t() m_flSwaySpeedScale = 1.0f; szDroppedModel[0] = 0; m_bUsesHands = false; + m_nWeaponRestriction = WPNRESTRICT_NONE; #endif } @@ -415,6 +416,14 @@ FileWeaponInfo_t::FileWeaponInfo_t() extern ConVar hud_fastswitch; #endif +#ifdef MAPBASE +const char* pWeaponRestrictions[NUM_WEAPON_RESTRICTION_TYPES] = { + "none", + "player_only", + "npc_only", +}; +#endif // MAPBASE + void FileWeaponInfo_t::Parse( KeyValues *pKeyValuesData, const char *szWeaponName ) { // Okay, we tried at least once to look this up... @@ -483,6 +492,19 @@ void FileWeaponInfo_t::Parse( KeyValues *pKeyValuesData, const char *szWeaponNam Q_strncpy( szDroppedModel, pKeyValuesData->GetString( "droppedmodel" ), MAX_WEAPON_STRING ); m_bUsesHands = ( pKeyValuesData->GetInt( "uses_hands", 0 ) != 0 ) ? true : false; + + const char* pszRestrictString = pKeyValuesData->GetString("usage_restriction", nullptr); + if (pszRestrictString) + { + for (int i = 0; i < NUM_WEAPON_RESTRICTION_TYPES; i++) + { + if (V_stricmp(pszRestrictString, pWeaponRestrictions[i]) == 0) + { + m_nWeaponRestriction = i; + break; + } + } + } #endif #ifndef MAPBASE // Mapbase makes weapons in the same slot & position swap each other out, which is a feature mods can intentionally use. diff --git a/sp/src/game/shared/weapon_parse.h b/sp/src/game/shared/weapon_parse.h index 28032744..4ba7b02c 100644 --- a/sp/src/game/shared/weapon_parse.h +++ b/sp/src/game/shared/weapon_parse.h @@ -58,6 +58,17 @@ int GetWeaponSoundFromString( const char *pszString ); class CHudTexture; class KeyValues; +#ifdef MAPBASE +enum WeaponUsageRestricions_e +{ + WPNRESTRICT_NONE = 0, + WPNRESTRICT_PLAYER_ONLY, + WPNRESTRICT_NPCS_ONLY, + + NUM_WEAPON_RESTRICTION_TYPES +}; +#endif // MAPBASE + //----------------------------------------------------------------------------- // Purpose: Contains the data read from the weapon's script file. // It's cached so we only read each weapon's script file once. @@ -125,6 +136,8 @@ public: char szDroppedModel[MAX_WEAPON_STRING]; // Model of this weapon when dropped on the ground bool m_bUsesHands; + + int m_nWeaponRestriction; #endif // CLIENT DLL From 13f422f4c42d4c7613538ddd4bf766f65be258b0 Mon Sep 17 00:00:00 2001 From: Peter Covington Date: Tue, 19 Apr 2022 00:57:05 -0400 Subject: [PATCH 09/90] Fix issues --- sp/src/game/client/mapbase/c_weapon_custom_hl2.cpp | 4 ++-- sp/src/game/server/mapbase/weapon_custom_hl2.cpp | 7 ++++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/sp/src/game/client/mapbase/c_weapon_custom_hl2.cpp b/sp/src/game/client/mapbase/c_weapon_custom_hl2.cpp index 8c89f2d7..9f97a12f 100644 --- a/sp/src/game/client/mapbase/c_weapon_custom_hl2.cpp +++ b/sp/src/game/client/mapbase/c_weapon_custom_hl2.cpp @@ -33,10 +33,10 @@ C_HLCustomWeaponMelee::C_HLCustomWeaponMelee() -class C_HLCustomWeaponGun : public C_BaseHLBludgeonWeapon +class C_HLCustomWeaponGun : public C_BaseHLCombatWeapon { public: - DECLARE_CLASS(C_HLCustomWeaponGun, C_BaseHLBludgeonWeapon); + DECLARE_CLASS(C_HLCustomWeaponGun, C_BaseHLCombatWeapon); DECLARE_CLIENTCLASS(); DECLARE_PREDICTABLE(); diff --git a/sp/src/game/server/mapbase/weapon_custom_hl2.cpp b/sp/src/game/server/mapbase/weapon_custom_hl2.cpp index c2881c6f..7a374386 100644 --- a/sp/src/game/server/mapbase/weapon_custom_hl2.cpp +++ b/sp/src/game/server/mapbase/weapon_custom_hl2.cpp @@ -354,6 +354,7 @@ public: CHLCustomWeaponGun(); virtual void ParseCustomFromWeaponFile(const char* pFileName); + const char* GetWeaponScriptName() { return m_iszWeaponScriptName.Get(); } // Weapon behaviour virtual void ItemPostFrame(void); // called each frame by the player PostThink @@ -672,9 +673,9 @@ void CHLCustomWeaponGun::ParseCustomFromWeaponFile(const char* pFileName) float flSpread = pkvData->GetFloat("spread", 5.f); float flNPCSpread = pkvData->GetFloat("spread_npc", flSpread); float flAllySperad = pkvData->GetFloat("spread_ally", flNPCSpread); - m_vPlayerSpread = Vector(sin(flSpread * 0.5f)); - m_vNPCSpread = Vector(sin(flNPCSpread * 0.5f)); - m_vAllySpread = Vector(sin(flAllySperad * 0.5f)); + m_vPlayerSpread = Vector(sin(DEG2RAD(flSpread * 0.5f))); + m_vNPCSpread = Vector(sin(DEG2RAD(flNPCSpread * 0.5f))); + m_vAllySpread = Vector(sin(DEG2RAD(flAllySperad * 0.5f))); const char* pszAnimType = pkvData->GetString("anim_type", nullptr); if (pszAnimType) From 0ae65a5a64eefeaf2e8db2a838964a1b1015a47f Mon Sep 17 00:00:00 2001 From: Peter Covington Date: Tue, 19 Apr 2022 02:53:35 -0400 Subject: [PATCH 10/90] Fix weapon selection --- sp/src/game/client/prediction.cpp | 2 +- sp/src/game/server/player_command.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sp/src/game/client/prediction.cpp b/sp/src/game/client/prediction.cpp index d7dfbff7..6d3a0439 100644 --- a/sp/src/game/client/prediction.cpp +++ b/sp/src/game/client/prediction.cpp @@ -855,7 +855,7 @@ void CPrediction::RunCommand( C_BasePlayer *player, CUserCmd *ucmd, IMoveHelper C_BaseCombatWeapon *weapon = dynamic_cast< C_BaseCombatWeapon * >( CBaseEntity::Instance( ucmd->weaponselect ) ); if ( weapon ) { - player->SelectItem( weapon->GetName(), ucmd->weaponsubtype ); + player->SelectItem( weapon->GetClassname(), ucmd->weaponsubtype ); } } diff --git a/sp/src/game/server/player_command.cpp b/sp/src/game/server/player_command.cpp index d5a6a1f0..7614babe 100644 --- a/sp/src/game/server/player_command.cpp +++ b/sp/src/game/server/player_command.cpp @@ -393,7 +393,7 @@ void CPlayerMove::RunCommand ( CBasePlayer *player, CUserCmd *ucmd, IMoveHelper if ( weapon ) { VPROF( "player->SelectItem()" ); - player->SelectItem( weapon->GetName(), ucmd->weaponsubtype ); + player->SelectItem( weapon->GetClassname(), ucmd->weaponsubtype ); } } From d871c6f8fdbdfb159c5035545b297ea6317f1225 Mon Sep 17 00:00:00 2001 From: Peter Covington Date: Tue, 19 Apr 2022 03:16:54 -0400 Subject: [PATCH 11/90] Add Mapbase file headers --- sp/src/game/client/mapbase/c_weapon_custom_hl2.cpp | 8 ++++++++ sp/src/game/server/mapbase/custom_weapon_factory.cpp | 8 ++++++++ sp/src/game/server/mapbase/custom_weapon_factory.h | 8 ++++++++ sp/src/game/server/mapbase/weapon_custom_hl2.cpp | 8 ++++++++ 4 files changed, 32 insertions(+) diff --git a/sp/src/game/client/mapbase/c_weapon_custom_hl2.cpp b/sp/src/game/client/mapbase/c_weapon_custom_hl2.cpp index 9f97a12f..71675b06 100644 --- a/sp/src/game/client/mapbase/c_weapon_custom_hl2.cpp +++ b/sp/src/game/client/mapbase/c_weapon_custom_hl2.cpp @@ -1,3 +1,11 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// +// +// Purpose: Client classes for Half-Life 2 based custom weapons. +// +// Author: Peter Covington (petercov@outlook.com) +// +//==================================================================================// + #include "cbase.h" #include "c_weapon__stubs.h" #include "basehlcombatweapon_shared.h" diff --git a/sp/src/game/server/mapbase/custom_weapon_factory.cpp b/sp/src/game/server/mapbase/custom_weapon_factory.cpp index a8e17146..f7424412 100644 --- a/sp/src/game/server/mapbase/custom_weapon_factory.cpp +++ b/sp/src/game/server/mapbase/custom_weapon_factory.cpp @@ -1,3 +1,11 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// +// +// Purpose: The central manager of the custom weapons system. +// +// Author: Peter Covington (petercov@outlook.com) +// +//==================================================================================// + #include "cbase.h" #include "custom_weapon_factory.h" diff --git a/sp/src/game/server/mapbase/custom_weapon_factory.h b/sp/src/game/server/mapbase/custom_weapon_factory.h index fc01045d..f027ea86 100644 --- a/sp/src/game/server/mapbase/custom_weapon_factory.h +++ b/sp/src/game/server/mapbase/custom_weapon_factory.h @@ -1,3 +1,11 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// +// +// Purpose: See custom_weapon_factory.cpp +// +// Author: Peter Covington (petercov@outlook.com) +// +//==================================================================================// + #ifndef CUSTOM_WEAPON_FACTORY_H #define CUSTOM_WEAPON_FACTORY_H #pragma once diff --git a/sp/src/game/server/mapbase/weapon_custom_hl2.cpp b/sp/src/game/server/mapbase/weapon_custom_hl2.cpp index 7a374386..af29357c 100644 --- a/sp/src/game/server/mapbase/weapon_custom_hl2.cpp +++ b/sp/src/game/server/mapbase/weapon_custom_hl2.cpp @@ -1,3 +1,11 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// +// +// Purpose: Custom weapon classes for Half-Life 2 based weapons. +// +// Author: Peter Covington (petercov@outlook.com) +// +//==================================================================================// + #include "cbase.h" #include "custom_weapon_factory.h" #include "basebludgeonweapon.h" From fa41a327e7753a3b948eac0ca60f9b351db10fe9 Mon Sep 17 00:00:00 2001 From: Peter Covington Date: Wed, 20 Apr 2022 01:48:32 -0400 Subject: [PATCH 12/90] Added a data cache to the custom weapons system --- .../server/mapbase/custom_weapon_factory.cpp | 24 +- .../server/mapbase/custom_weapon_factory.h | 45 +- .../game/server/mapbase/weapon_custom_hl2.cpp | 387 ++++++++++-------- .../shared/mapbase/weapon_custom_scripted.cpp | 19 +- .../shared/mapbase/weapon_custom_scripted.h | 2 +- 5 files changed, 278 insertions(+), 199 deletions(-) diff --git a/sp/src/game/server/mapbase/custom_weapon_factory.cpp b/sp/src/game/server/mapbase/custom_weapon_factory.cpp index f7424412..2feaec16 100644 --- a/sp/src/game/server/mapbase/custom_weapon_factory.cpp +++ b/sp/src/game/server/mapbase/custom_weapon_factory.cpp @@ -134,15 +134,28 @@ void CCustomWeaponSystem::LoadCustomWeaponsManifest(const char* file, bool bDont unsigned short FactoryIndex = Factory.Find(pszFactory); if (Factory.IsValidIndex(FactoryIndex)) { + auto* pFactory = Factory.Element(FactoryIndex); + const void* pData = pFactory->ParseDataFromWeaponFile(pkvWeaponScript); + if (!pData) + continue; + unsigned short ClassIndex = m_ClassFactories.Find(pszClassname); if (!m_ClassFactories.IsValidIndex(ClassIndex)) { ClassIndex = m_ClassFactories.Insert(pszClassname); m_ClassFactories[ClassIndex].pOldFactory = EntityFactoryDictionary()->FindFactory(pszClassname); } + else + { + Assert(m_ClassFactories[ClassIndex].pNewFactory); + Assert(m_ClassFactories[ClassIndex].pData); + + m_ClassFactories[ClassIndex].pNewFactory->ReleaseData(m_ClassFactories[ClassIndex].pData); + } m_ClassFactories[ClassIndex].sDataFile = pkvWeapon->GetString(); - m_ClassFactories[ClassIndex].pNewFactory = Factory.Element(FactoryIndex); + m_ClassFactories[ClassIndex].pNewFactory = pFactory; + m_ClassFactories[ClassIndex].pData = pData; EntityFactoryDictionary()->UninstallFactory(pszClassname); EntityFactoryDictionary()->InstallFactory(m_ClassFactories[ClassIndex].pNewFactory, pszClassname); } @@ -160,6 +173,9 @@ void CCustomWeaponSystem::LevelShutdownPostEntity() const CustomClassName_t& entry = m_ClassFactories.Element(i); if (entry.pOldFactory) EntityFactoryDictionary()->InstallFactory(entry.pOldFactory, m_ClassFactories.GetElementName(i)); + + Assert(entry.pData); + entry.pNewFactory->ReleaseData(entry.pData); } m_ClassFactories.Purge(); @@ -176,12 +192,12 @@ void CCustomWeaponSystem::ParseWeapon(CBaseCombatWeapon* pWeapon, const char* pC if (!m_ClassFactories.IsValidIndex(i)) return; - pCustom->ParseCustomFromWeaponFile(m_ClassFactories[i].sDataFile.String()); + pCustom->InitCustomWeaponFromData(m_ClassFactories[i].pData, m_ClassFactories[i].sDataFile.String()); } -CUtlDict& CustomWeaponsFactoryDictionary() +CUtlDict& CustomWeaponsFactoryDictionary() { - static CUtlDict dict; + static CUtlDict dict; return dict; } diff --git a/sp/src/game/server/mapbase/custom_weapon_factory.h b/sp/src/game/server/mapbase/custom_weapon_factory.h index f027ea86..838fd695 100644 --- a/sp/src/game/server/mapbase/custom_weapon_factory.h +++ b/sp/src/game/server/mapbase/custom_weapon_factory.h @@ -14,12 +14,17 @@ DECLARE_PRIVATE_SYMBOLTYPE(CustomWeaponSymbol); -CUtlDict< IEntityFactory*, unsigned short >& CustomWeaponsFactoryDictionary(); +class ICustomWeaponDataLoader : public IEntityFactory +{ +public: + virtual const void* ParseDataFromWeaponFile(KeyValues* pKV) const = 0; + virtual void ReleaseData(const void* pData) const = 0; +}; class ICustomWeapon { public: - virtual void ParseCustomFromWeaponFile(const char* pFileName) = 0; + virtual void InitCustomWeaponFromData(const void* pData, const char *pszWeaponScript) = 0; }; class CCustomWeaponSystem : public CAutoGameSystem @@ -42,19 +47,22 @@ private: typedef struct CustomClassName_s { CustomWeaponSymbol sDataFile; - IEntityFactory* pNewFactory; + ICustomWeaponDataLoader* pNewFactory; IEntityFactory* pOldFactory; + const void* pData; } CustomClassName_t; CUtlDict m_ClassFactories; }; CCustomWeaponSystem* CustomWeaponSystem(); +CUtlDict< ICustomWeaponDataLoader*, unsigned short >& CustomWeaponsFactoryDictionary(); + template -class CCustomWeaponEntityFactory : public IEntityFactory +class CCustomWeaponEntityFactoryBase : public ICustomWeaponDataLoader { public: - CCustomWeaponEntityFactory(const char* pFactoryClass) + CCustomWeaponEntityFactoryBase(const char* pFactoryClass) { CustomWeaponsFactoryDictionary().Insert(pFactoryClass, this); } @@ -80,7 +88,30 @@ public: } }; -#define DEFINE_CUSTOM_WEAPON_FACTORY(factoryName, DLLClassName) \ - static CCustomWeaponEntityFactory custom_weapon_##factoryName##_factory( #factoryName ); +template +class CDefaultCustomWeaponEntityFactory : public CCustomWeaponEntityFactoryBase +{ +public: + CDefaultCustomWeaponEntityFactory(const char *pFactoryClass) : CCustomWeaponEntityFactoryBase(pFactoryClass) + {} + + virtual const void* ParseDataFromWeaponFile(KeyValues* pKV) const + { + Data* pData = new Data; + if (pData && pData->Parse(pKV)) + return pData; + + delete pData; + return nullptr; + } + + virtual void ReleaseData(const void* pData) const + { + delete pData; + } +}; + +#define DEFINE_CUSTOM_WEAPON_FACTORY(factoryName, DLLClassName, DataStruct) \ + static CDefaultCustomWeaponEntityFactory custom_weapon_##factoryName##_factory( #factoryName ); #endif // !CUSTOM_WEAPON_FACTORY_H diff --git a/sp/src/game/server/mapbase/weapon_custom_hl2.cpp b/sp/src/game/server/mapbase/weapon_custom_hl2.cpp index af29357c..86c9733b 100644 --- a/sp/src/game/server/mapbase/weapon_custom_hl2.cpp +++ b/sp/src/game/server/mapbase/weapon_custom_hl2.cpp @@ -49,6 +49,17 @@ int g_nDamageClassTypeBits[ARRAYSIZE(g_ppszDamageClasses)] = { DMG_CLUB|DMG_BURN, }; +typedef struct HL2CustomMeleeData_s +{ + float m_flMeleeRange; + float m_flRefireRate; + float m_flDamage; + float m_flNPCDamage; + byte m_nDamageClass; + + bool Parse(KeyValues*); +} HL2CustomMeleeData_t; + class CHLCustomWeaponMelee : public CBaseHLBludgeonWeapon, public ICustomWeapon { public: @@ -59,8 +70,8 @@ public: CHLCustomWeaponMelee(); - float GetRange(void) { return m_flMeleeRange; } - float GetFireRate(void) { return m_flRefireRate; } + float GetRange(void) { return m_CustomData.m_flMeleeRange; } + float GetFireRate(void) { return m_CustomData.m_flRefireRate; } void AddViewKick(void); float GetDamageForActivity(Activity hitActivity); @@ -76,20 +87,16 @@ public: int GetBackupActivityListCount() { return 0; } const char* GetWeaponScriptName() { return m_iszWeaponScriptName.Get(); } - virtual int GetDamageType() { return g_nDamageClassTypeBits[m_nDamageClass]; } + virtual int GetDamageType() { return g_nDamageClassTypeBits[m_CustomData.m_nDamageClass]; } - virtual void ParseCustomFromWeaponFile(const char* pFileName); + virtual void InitCustomWeaponFromData(const void* pData, const char* pszWeaponScript); private: // Animation event handlers void HandleAnimEventMeleeHit(animevent_t* pEvent, CBaseCombatCharacter* pOperator); private: - float m_flMeleeRange; - float m_flRefireRate; - float m_flDamage; - float m_flNPCDamage; - byte m_nDamageClass; + HL2CustomMeleeData_t m_CustomData; CNetworkString(m_iszWeaponScriptName, 128); }; @@ -98,7 +105,41 @@ IMPLEMENT_SERVERCLASS_ST(CHLCustomWeaponMelee, DT_HLCustomWeaponMelee) SendPropString(SENDINFO(m_iszWeaponScriptName)), END_SEND_TABLE(); -DEFINE_CUSTOM_WEAPON_FACTORY(hl2_melee, CHLCustomWeaponMelee); +DEFINE_CUSTOM_WEAPON_FACTORY(hl2_melee, CHLCustomWeaponMelee, HL2CustomMeleeData_t); + +bool HL2CustomMeleeData_s::Parse(KeyValues* pKVWeapon) +{ + KeyValues* pkvData = pKVWeapon->FindKey("CustomData"); + if (pkvData) + { + m_flDamage = pkvData->GetFloat("damage"); + m_flNPCDamage = pkvData->GetFloat("damage_npc", m_flDamage); + m_flMeleeRange = pkvData->GetFloat("range", 70.f); + m_flRefireRate = pkvData->GetFloat("rate", 0.7f); + + const char* pszDamageClass = pkvData->GetString("damage_type", nullptr); + if (pszDamageClass) + { + for (byte i = 0; i < ARRAYSIZE(g_ppszDamageClasses); i++) + { + if (V_stricmp(pszDamageClass, g_ppszDamageClasses[i]) == 0) + { + m_nDamageClass = i; + break; + } + } + } + return true; + } + + return false; +} + +void CHLCustomWeaponMelee::InitCustomWeaponFromData(const void* pData, const char* pszWeaponScript) +{ + Q_FileBase(pszWeaponScript, m_iszWeaponScriptName.GetForModify(), 128); + V_memcpy(&m_CustomData, pData, sizeof(HL2CustomMeleeData_t)); +} acttable_t CHLCustomWeaponMelee::m_acttable[] = { @@ -165,7 +206,6 @@ IMPLEMENT_ACTTABLE(CHLCustomWeaponMelee); CHLCustomWeaponMelee::CHLCustomWeaponMelee() { - m_nDamageClass = 0; } //----------------------------------------------------------------------------- @@ -176,9 +216,9 @@ CHLCustomWeaponMelee::CHLCustomWeaponMelee() float CHLCustomWeaponMelee::GetDamageForActivity(Activity hitActivity) { if ((GetOwner() != NULL) && (GetOwner()->IsPlayer())) - return m_flDamage; + return m_CustomData.m_flDamage; - return m_flNPCDamage; + return m_CustomData.m_flNPCDamage; } //----------------------------------------------------------------------------- @@ -280,7 +320,7 @@ void CHLCustomWeaponMelee::HandleAnimEventMeleeHit(animevent_t* pEvent, CBaseCom 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), m_flNPCDamage, GetDamageType(), 0.75); + Vector(-16, -16, -16), Vector(36, 36, 36), m_CustomData.m_flNPCDamage, GetDamageType(), 0.75); // did I hit someone? if (pHurt) @@ -317,36 +357,6 @@ void CHLCustomWeaponMelee::Operator_HandleAnimEvent(animevent_t* pEvent, CBaseCo } } -void CHLCustomWeaponMelee::ParseCustomFromWeaponFile(const char* pFileName) -{ - Q_FileBase(pFileName, m_iszWeaponScriptName.GetForModify(), 128); - KeyValuesAD pKVWeapon("WeaponData"); - if (pKVWeapon->LoadFromFile(filesystem, pFileName, "GAME")) - { - KeyValues* pkvData = pKVWeapon->FindKey("CustomData"); - if (pkvData) - { - m_flDamage = pkvData->GetFloat("damage"); - m_flNPCDamage = pkvData->GetFloat("damage_npc", m_flDamage); - m_flMeleeRange = pkvData->GetFloat("range", 70.f); - m_flRefireRate = pkvData->GetFloat("rate", 0.7f); - - const char* pszDamageClass = pkvData->GetString("damage_type", nullptr); - if (pszDamageClass) - { - for (byte i = 0; i < ARRAYSIZE(g_ppszDamageClasses); i++) - { - if (V_stricmp(pszDamageClass, g_ppszDamageClasses[i]) == 0) - { - m_nDamageClass = i; - break; - } - } - } - } - } -} - //-------------------------------------------------------------------------- // // Custom ranged weapon @@ -361,7 +371,7 @@ public: DECLARE_DATADESC(); CHLCustomWeaponGun(); - virtual void ParseCustomFromWeaponFile(const char* pFileName); + virtual void InitCustomWeaponFromData(const void* pData, const char* pszWeaponScript); const char* GetWeaponScriptName() { return m_iszWeaponScriptName.Get(); } // Weapon behaviour @@ -371,15 +381,15 @@ public: // Bullet launch information virtual const Vector& GetBulletSpread(void); - virtual float GetFireRate(void) { return m_flFireRate; } - virtual int GetMinBurst() { return m_nMinBurst; } - virtual int GetMaxBurst() { return m_nMaxBurst; } - virtual float GetMinRestTime() { return m_RestInterval.start; } - virtual float GetMaxRestTime() { return m_RestInterval.start + m_RestInterval.range; } + virtual float GetFireRate(void) { return m_CustomData.m_flFireRate; } + virtual int GetMinBurst() { return m_CustomData.m_nMinBurst; } + virtual int GetMaxBurst() { return m_CustomData.m_nMaxBurst; } + virtual float GetMinRestTime() { return m_CustomData.m_RestInterval.start; } + virtual float GetMaxRestTime() { return m_CustomData.m_RestInterval.start + m_CustomData.m_RestInterval.range; } // Autoaim virtual float GetMaxAutoAimDeflection() { return 0.99f; } - virtual float WeaponAutoAimScale() { return m_flAutoAimScale; } // allows a weapon to influence the perceived size of the target's autoaim radius. + virtual float WeaponAutoAimScale() { return m_CustomData.m_flAutoAimScale; } // allows a weapon to influence the perceived size of the target's autoaim radius. virtual void AddViewKick(void); int WeaponSoundRealtime(WeaponSound_t shoot_type); @@ -409,35 +419,54 @@ private: void CheckZoomToggle(void); void ToggleZoom(void); +public: + typedef struct Data_s + { + float m_flFireRate; + int m_nMinBurst; + int m_nMaxBurst; + interval_t m_RestInterval; + + float m_flAutoAimScale; + + Vector m_vPlayerSpread; + Vector m_vAllySpread; + Vector m_vNPCSpread; + int m_nBulletsPerShot; // For shotguns + + // Viewkick + float m_flMaxVerticalKick; + float m_flSlideLimit; + interval_t m_VerticalPunchRange; + + int m_nActTableIndex; + + bool m_bUseRecoilAnims; + bool m_bFullAuto; // True for machine gun, false for semi-auto + bool m_bNextAttackFromSequence; + bool m_bUsePumpAnimation; + bool m_bHasSecondaryFire; + bool m_bHasZoom; + bool m_bZoomDuringReload; + } Data_t; + + struct Cache_s : public Data_s + { + bool m_bFiresUnderwater; // true if this weapon can fire underwater + bool m_bAltFiresUnderwater; // true if this weapon can fire underwater + float m_fMinRange1; // What's the closest this weapon can be used? + float m_fMinRange2; // What's the closest this weapon can be used? + float m_fMaxRange1; // What's the furthest this weapon can be used? + float m_fMaxRange2; // What's the furthest this weapon can be used? + bool m_bReloadsSingly; // True if this weapon reloads 1 round at a time + + bool Parse(KeyValues*); + }; + private: CNetworkString(m_iszWeaponScriptName, 128); - float m_flFireRate; - int m_nMinBurst; - int m_nMaxBurst; - interval_t m_RestInterval; - - float m_flAutoAimScale; - - Vector m_vPlayerSpread; - Vector m_vAllySpread; - Vector m_vNPCSpread; - int m_nBulletsPerShot; // For shotguns - - // Viewkick - float m_flMaxVerticalKick; - float m_flSlideLimit; - interval_t m_VerticalPunchRange; - - int m_nActTableIndex; - - bool m_bUseRecoilAnims; - bool m_bFullAuto; // True for machine gun, false for semi-auto - bool m_bNextAttackFromSequence; - bool m_bUsePumpAnimation; - bool m_bHasSecondaryFire; - bool m_bHasZoom; - bool m_bZoomDuringReload; + Data_t m_CustomData; bool m_bNeedPump; // When emptied completely bool m_bDelayedFire1; // Fire primary when finished reloading @@ -476,28 +505,10 @@ DEFINE_FIELD(m_bInZoom, FIELD_BOOLEAN), DEFINE_FIELD(m_bMustReload, FIELD_BOOLEAN), END_DATADESC(); -DEFINE_CUSTOM_WEAPON_FACTORY(hl2_gun, CHLCustomWeaponGun); +DEFINE_CUSTOM_WEAPON_FACTORY(hl2_gun, CHLCustomWeaponGun, CHLCustomWeaponGun::Cache_s); CHLCustomWeaponGun::CHLCustomWeaponGun() { - m_flFireRate = 0.5f; - m_nMinBurst = 1; - m_nMaxBurst = 1; - m_RestInterval.start = .3f; - m_RestInterval.range = .3f; - - m_flAutoAimScale = 1.f; - m_nBulletsPerShot = 1; - - m_bUseRecoilAnims = false; - m_bFullAuto = false; - m_bNextAttackFromSequence = false; - m_bUsePumpAnimation = false; - m_bHasSecondaryFire = false; - m_bHasZoom = false; - m_bZoomDuringReload = false; - m_bFiresUnderwater = false; - m_bNeedPump = false; m_bDelayedFire1 = false; m_bDelayedFire2 = false; @@ -508,7 +519,7 @@ CHLCustomWeaponGun::CHLCustomWeaponGun() acttable_t* CHLCustomWeaponGun::ActivityList(void) { - switch (m_nActTableIndex) + switch (m_CustomData.m_nActTableIndex) { default: case ACTTABLE_SMG1: @@ -537,7 +548,7 @@ acttable_t* CHLCustomWeaponGun::ActivityList(void) int CHLCustomWeaponGun::ActivityListCount(void) { - switch (m_nActTableIndex) + switch (m_CustomData.m_nActTableIndex) { default: case ACTTABLE_SMG1: @@ -566,7 +577,7 @@ int CHLCustomWeaponGun::ActivityListCount(void) acttable_t* CHLCustomWeaponGun::GetBackupActivityList(void) { - switch (m_nActTableIndex) + switch (m_CustomData.m_nActTableIndex) { default: case ACTTABLE_SMG1: @@ -587,7 +598,7 @@ acttable_t* CHLCustomWeaponGun::GetBackupActivityList(void) int CHLCustomWeaponGun::GetBackupActivityListCount(void) { - switch (m_nActTableIndex) + switch (m_CustomData.m_nActTableIndex) { default: case ACTTABLE_SMG1: @@ -627,7 +638,7 @@ void ReadIntervalInt(const char* pString, int &iMin, int &iMax) } } -void CHLCustomWeaponGun::ParseCustomFromWeaponFile(const char* pFileName) +bool CHLCustomWeaponGun::Cache_s::Parse(KeyValues* pKVWeapon) { static const char* ppszCustomGunAnimTypes[NUM_GUN_ACT_TABLES] = { "smg", @@ -639,80 +650,93 @@ void CHLCustomWeaponGun::ParseCustomFromWeaponFile(const char* pFileName) "annabelle", }; - Q_FileBase(pFileName, m_iszWeaponScriptName.GetForModify(), 128); - KeyValuesAD pKVWeapon("WeaponData"); - if (pKVWeapon->LoadFromFile(filesystem, pFileName, "GAME")) + KeyValues* pkvData = pKVWeapon->FindKey("CustomData"); + if (pkvData) { - KeyValues* pkvData = pKVWeapon->FindKey("CustomData"); - if (pkvData) + m_flFireRate = pkvData->GetFloat("fire_rate", 0.5f); + ReadIntervalInt(pkvData->GetString("npc_burst", "1"), m_nMinBurst, m_nMaxBurst); + m_RestInterval = ReadInterval(pkvData->GetString("npc_rest_time", "0.3,0.6")); + m_flAutoAimScale = pkvData->GetFloat("autoaim_scale", 1.f); + m_bFullAuto = pkvData->GetBool("auto_fire"); + m_nBulletsPerShot = pkvData->GetInt("bullets", 1); + m_bUseRecoilAnims = pkvData->GetBool("recoil_anims", true); + m_bReloadsSingly = pkvData->GetBool("reload_singly"); + m_bFiresUnderwater = pkvData->GetBool("fires_underwater"); + m_bHasZoom = pkvData->GetBool("zoom_enable"); + m_bZoomDuringReload = m_bHasZoom && pkvData->GetBool("zoom_in_reload"); + + m_fMinRange1 = pkvData->GetFloat("range1_min", 65.f); + m_fMinRange2 = pkvData->GetFloat("range2_min", 65.f); + m_fMaxRange1 = pkvData->GetFloat("range1_max", 1024.f); + m_fMaxRange2 = pkvData->GetFloat("range2_max", 1024.f); + + if (m_bFullAuto) { - m_flFireRate = pkvData->GetFloat("fire_rate", 0.5f); - ReadIntervalInt(pkvData->GetString("npc_burst", "1"), m_nMinBurst, m_nMaxBurst); - m_RestInterval = ReadInterval(pkvData->GetString("npc_rest_time", "0.3,0.6")); - m_flAutoAimScale = pkvData->GetFloat("autoaim_scale", 1.f); - m_bFullAuto = pkvData->GetBool("auto_fire"); - m_nBulletsPerShot = pkvData->GetInt("bullets", 1); - m_bUseRecoilAnims = pkvData->GetBool("recoil_anims", true); - m_bReloadsSingly = pkvData->GetBool("reload_singly"); - m_bFiresUnderwater = pkvData->GetBool("fires_underwater"); - m_bHasZoom = pkvData->GetBool("zoom_enable"); - m_bZoomDuringReload = m_bHasZoom && pkvData->GetBool("zoom_in_reload"); + m_flMaxVerticalKick = pkvData->GetFloat("viewkick_vertical_max", 1.f); + m_flSlideLimit = pkvData->GetFloat("viewkick_slide_limit", 2.f); + } + else + { + m_flSlideLimit = pkvData->GetFloat("viewpunch_side_max", .6f); + m_VerticalPunchRange = ReadInterval(pkvData->GetString("viewpunch_vertical", "0.25,0.5")); - m_fMinRange1 = pkvData->GetFloat("range1_min", 65.f); - m_fMinRange2 = pkvData->GetFloat("range2_min", 65.f); - m_fMaxRange1 = pkvData->GetFloat("range1_max", 1024.f); - m_fMaxRange2 = pkvData->GetFloat("range2_max", 1024.f); + m_bNextAttackFromSequence = pkvData->GetBool("next_attack_time_from_sequence"); + m_bUsePumpAnimation = pkvData->GetBool("use_pump_anim"); + } - if (m_bFullAuto) + // NOTE: The way these are calculated is that each component == sin (degrees/2) + float flSpread = pkvData->GetFloat("spread", 5.f); + float flNPCSpread = pkvData->GetFloat("spread_npc", flSpread); + float flAllySperad = pkvData->GetFloat("spread_ally", flNPCSpread); + m_vPlayerSpread = Vector(sin(DEG2RAD(flSpread * 0.5f))); + m_vNPCSpread = Vector(sin(DEG2RAD(flNPCSpread * 0.5f))); + m_vAllySpread = Vector(sin(DEG2RAD(flAllySperad * 0.5f))); + + const char* pszAnimType = pkvData->GetString("anim_type", nullptr); + if (pszAnimType) + { + for (int i = 0; i < NUM_GUN_ACT_TABLES; i++) { - m_flMaxVerticalKick = pkvData->GetFloat("viewkick_vertical_max", 1.f); - m_flSlideLimit = pkvData->GetFloat("viewkick_slide_limit", 2.f); - } - else - { - m_flSlideLimit = pkvData->GetFloat("viewpunch_side_max", .6f); - m_VerticalPunchRange = ReadInterval(pkvData->GetString("viewpunch_vertical", "0.25,0.5")); - - m_bNextAttackFromSequence = pkvData->GetBool("next_attack_time_from_sequence"); - m_bUsePumpAnimation = pkvData->GetBool("use_pump_anim"); - } - - // NOTE: The way these are calculated is that each component == sin (degrees/2) - float flSpread = pkvData->GetFloat("spread", 5.f); - float flNPCSpread = pkvData->GetFloat("spread_npc", flSpread); - float flAllySperad = pkvData->GetFloat("spread_ally", flNPCSpread); - m_vPlayerSpread = Vector(sin(DEG2RAD(flSpread * 0.5f))); - m_vNPCSpread = Vector(sin(DEG2RAD(flNPCSpread * 0.5f))); - m_vAllySpread = Vector(sin(DEG2RAD(flAllySperad * 0.5f))); - - const char* pszAnimType = pkvData->GetString("anim_type", nullptr); - if (pszAnimType) - { - for (int i = 0; i < NUM_GUN_ACT_TABLES; i++) + if (V_stricmp(pszAnimType, ppszCustomGunAnimTypes[i]) == 0) { - if (V_stricmp(pszAnimType, ppszCustomGunAnimTypes[i]) == 0) - { - m_nActTableIndex = i; - break; - } + m_nActTableIndex = i; + break; } } } + + return true; } + + return false; +} + +void CHLCustomWeaponGun::InitCustomWeaponFromData(const void* pData, const char* pszWeaponScript) +{ + Q_FileBase(pszWeaponScript, m_iszWeaponScriptName.GetForModify(), 128); + const auto* pCache = static_cast (pData); + m_CustomData = *pCache; + m_bFiresUnderwater = pCache->m_bFiresUnderwater; + m_bAltFiresUnderwater = pCache->m_bAltFiresUnderwater; + m_fMinRange1 = pCache->m_fMinRange1; + m_fMinRange2 = pCache->m_fMinRange2; + m_fMaxRange1 = pCache->m_fMaxRange1; + m_fMaxRange2 = pCache->m_fMaxRange2; + m_bReloadsSingly = pCache->m_bReloadsSingly; } const Vector& CHLCustomWeaponGun::GetBulletSpread() { if (!GetOwner() || !GetOwner()->IsNPC()) - return m_vPlayerSpread; + return m_CustomData.m_vPlayerSpread; if (GetOwner()->MyNPCPointer()->IsPlayerAlly()) { // 357 allies should be cooler - return m_vAllySpread; + return m_CustomData.m_vAllySpread; } - return m_vNPCSpread; + return m_CustomData.m_vNPCSpread; } void CHLCustomWeaponGun::AddViewKick(void) @@ -723,7 +747,7 @@ void CHLCustomWeaponGun::AddViewKick(void) if (!pPlayer) return; - if (m_bFullAuto) + if (m_CustomData.m_bFullAuto) { float flDuration = m_fFireDuration; @@ -736,13 +760,13 @@ void CHLCustomWeaponGun::AddViewKick(void) flDuration = MIN(flDuration, 0.75f); } - CHLMachineGun::DoMachineGunKick(pPlayer, 0.5f, m_flMaxVerticalKick, flDuration, m_flSlideLimit); + CHLMachineGun::DoMachineGunKick(pPlayer, 0.5f, m_CustomData.m_flMaxVerticalKick, flDuration, m_CustomData.m_flSlideLimit); } else { QAngle viewPunch; - viewPunch.x = RandomInterval(m_VerticalPunchRange); - viewPunch.y = RandomFloat(-m_flSlideLimit, m_flSlideLimit); + viewPunch.x = RandomInterval(m_CustomData.m_VerticalPunchRange); + viewPunch.y = RandomFloat(-m_CustomData.m_flSlideLimit, m_CustomData.m_flSlideLimit); viewPunch.z = 0.0f; //Add it to the view punch @@ -755,13 +779,13 @@ void CHLCustomWeaponGun::AddViewKick(void) //----------------------------------------------------------------------------- void CHLCustomWeaponGun::CheckZoomToggle(void) { - if (!m_bHasZoom) + if (!m_CustomData.m_bHasZoom) return; CBasePlayer* pPlayer = ToBasePlayer(GetOwner()); int iButtonsTest = IN_ATTACK3; - if (!m_bHasSecondaryFire) + if (!m_CustomData.m_bHasSecondaryFire) iButtonsTest |= IN_ATTACK2; if (pPlayer->m_afButtonPressed & iButtonsTest) @@ -819,7 +843,7 @@ bool CHLCustomWeaponGun::StartReload(void) //NOTENOTE: This is kinda lame because the player doesn't get strong feedback on when the reload has finished, // without the pump. Technically, it's incorrect, but it's good for feedback... - if (m_bUsePumpAnimation && m_iClip1 <= 0) + if (m_CustomData.m_bUsePumpAnimation && m_iClip1 <= 0) { m_bNeedPump = true; } @@ -844,7 +868,7 @@ bool CHLCustomWeaponGun::StartReload(void) } #endif - if (m_bInZoom && !m_bZoomDuringReload) + if (m_bInZoom && !m_CustomData.m_bZoomDuringReload) { ToggleZoom(); } @@ -897,7 +921,7 @@ bool CHLCustomWeaponGun::Reload(void) } else if (BaseClass::Reload()) { - if (m_bInZoom && !m_bZoomDuringReload) + if (m_bInZoom && !m_CustomData.m_bZoomDuringReload) { ToggleZoom(); } @@ -1032,7 +1056,7 @@ void CHLCustomWeaponGun::ItemBusyFrame(void) { BaseClass::ItemBusyFrame(); - if (m_bZoomDuringReload) + if (m_CustomData.m_bZoomDuringReload) CheckZoomToggle(); } @@ -1050,7 +1074,7 @@ void CHLCustomWeaponGun::ItemPostFrame(void) UpdateAutoFire(); - if (m_bZoomDuringReload || !m_bInReload) + if (m_CustomData.m_bZoomDuringReload || !m_bInReload) CheckZoomToggle(); if (m_bReloadsSingly) @@ -1067,7 +1091,7 @@ void CHLCustomWeaponGun::ItemPostFrame(void) m_bDelayedFire1 = true; } // If I'm secondary firing and have one round stop reloading and fire - else if (m_bHasSecondaryFire && (pOwner->m_nButtons & IN_ATTACK2) && (m_iClip1 >= 2)) + else if (m_CustomData.m_bHasSecondaryFire && (pOwner->m_nButtons & IN_ATTACK2) && (m_iClip1 >= 2)) { m_bInReload = false; m_bNeedPump = false; @@ -1106,7 +1130,7 @@ void CHLCustomWeaponGun::ItemPostFrame(void) CheckReload(); } - if (m_bUsePumpAnimation && (m_bNeedPump) && (m_flNextPrimaryAttack <= gpGlobals->curtime)) + if (m_CustomData.m_bUsePumpAnimation && (m_bNeedPump) && (m_flNextPrimaryAttack <= gpGlobals->curtime)) { m_fFireDuration = 0.f; Pump(); @@ -1121,7 +1145,7 @@ void CHLCustomWeaponGun::ItemPostFrame(void) bool bFired = false; // Secondary attack has priority - if (m_bHasSecondaryFire && !m_bMustReload && (m_bDelayedFire2 || pOwner->m_nButtons & IN_ATTACK2) && (m_flNextSecondaryAttack <= gpGlobals->curtime)) + if (m_CustomData.m_bHasSecondaryFire && !m_bMustReload && (m_bDelayedFire2 || pOwner->m_nButtons & IN_ATTACK2) && (m_flNextSecondaryAttack <= gpGlobals->curtime)) { m_bDelayedFire2 = false; @@ -1259,7 +1283,7 @@ void CHLCustomWeaponGun::PrimaryAttack() if ((UsesClipsForAmmo1() && m_iClip1 == 0) || (!UsesClipsForAmmo1() && !pPlayer->GetAmmoCount(m_iPrimaryAmmoType))) return; - if (m_bFullAuto) + if (m_CustomData.m_bFullAuto) { m_nShotsFired++; @@ -1288,7 +1312,7 @@ void CHLCustomWeaponGun::PrimaryAttack() // Fire the bullets FireBulletsInfo_t info; - info.m_iShots = iBulletsToFire * m_nBulletsPerShot; + info.m_iShots = iBulletsToFire * m_CustomData.m_nBulletsPerShot; info.m_vecSrc = pPlayer->Weapon_ShootPosition(); info.m_vecDirShooting = pPlayer->GetAutoaimVector(AUTOAIM_SCALE_DEFAULT); info.m_vecSpread = pPlayer->GetAttackSpread(this); @@ -1296,20 +1320,22 @@ void CHLCustomWeaponGun::PrimaryAttack() info.m_iAmmoType = m_iPrimaryAmmoType; info.m_iTracerFreq = 2; FireBullets(info); + + SendWeaponAnim(GetPrimaryAttackActivity()); } else { - if (!m_bNextAttackFromSequence && !m_bUsePumpAnimation && !(pPlayer->m_afButtonPressed & IN_ATTACK)) + if (!m_CustomData.m_bNextAttackFromSequence && !m_CustomData.m_bUsePumpAnimation && !(pPlayer->m_afButtonPressed & IN_ATTACK)) return; m_nShotsFired++; // MUST call sound before removing a round from the clip of a CMachineGun WeaponSound(SINGLE); - pPlayer->DoMuzzleFlash(); + SendWeaponAnim(GetPrimaryAttackActivity()); - m_flNextPrimaryAttack = gpGlobals->curtime + ((m_bNextAttackFromSequence || m_bUsePumpAnimation) ? GetViewModelSequenceDuration() : GetFireRate()); + m_flNextPrimaryAttack = gpGlobals->curtime + ((m_CustomData.m_bNextAttackFromSequence || m_CustomData.m_bUsePumpAnimation) ? GetViewModelSequenceDuration() : GetFireRate()); m_iClip1 -= 1; Vector vecSrc = pPlayer->Weapon_ShootPosition(); @@ -1318,9 +1344,9 @@ void CHLCustomWeaponGun::PrimaryAttack() pPlayer->SetMuzzleFlashTime(gpGlobals->curtime + 1.0); // Fire the bullets, and force the first shot to be perfectly accuracy - pPlayer->FireBullets(m_nBulletsPerShot, vecSrc, vecAiming, GetBulletSpread(), MAX_TRACE_LENGTH, m_iPrimaryAmmoType, 0, -1, -1, 0, NULL, (m_nBulletsPerShot > 1), true); + pPlayer->FireBullets(m_CustomData.m_nBulletsPerShot, vecSrc, vecAiming, GetBulletSpread(), MAX_TRACE_LENGTH, m_iPrimaryAmmoType, 0, -1, -1, 0, NULL, (m_CustomData.m_nBulletsPerShot > 1), true); - if (m_bUsePumpAnimation && m_iClip1) + if (m_CustomData.m_bUsePumpAnimation && m_iClip1) { // pump so long as some rounds are left. m_bNeedPump = true; @@ -1340,7 +1366,6 @@ void CHLCustomWeaponGun::PrimaryAttack() pPlayer->SetSuitUpdate("!HEV_AMO0", FALSE, 0); } - SendWeaponAnim(GetPrimaryAttackActivity()); pPlayer->SetAnimation(PLAYER_ATTACK1); // Register a muzzleflash for the AI @@ -1353,7 +1378,7 @@ void CHLCustomWeaponGun::PrimaryAttack() //----------------------------------------------------------------------------- Activity CHLCustomWeaponGun::GetPrimaryAttackActivity(void) { - if (!m_bUseRecoilAnims || m_nShotsFired < 2) + if (!m_CustomData.m_bUseRecoilAnims || m_nShotsFired < 2) return ACT_VM_PRIMARYATTACK; if (m_nShotsFired < 3) @@ -1423,18 +1448,18 @@ void CHLCustomWeaponGun::FireNPCPrimaryAttack(CBaseCombatCharacter* pOperator, b CSoundEnt::InsertSound(SOUND_COMBAT | SOUND_CONTEXT_GUNFIRE, pOperator->GetAbsOrigin(), SOUNDENT_VOLUME_MACHINEGUN, 0.2f, pOperator, SOUNDENT_CHANNEL_WEAPON, pOperator->GetEnemy()); - const Vector& vecSpread = (bUseWeaponAngles || m_nBulletsPerShot > 1) ? GetBulletSpread() : VECTOR_CONE_PRECALCULATED; - if (m_bFullAuto) + const Vector& vecSpread = (bUseWeaponAngles || m_CustomData.m_nBulletsPerShot > 1) ? GetBulletSpread() : VECTOR_CONE_PRECALCULATED; + if (m_CustomData.m_bFullAuto) { int nShots = WeaponSoundRealtime(SINGLE_NPC); - pOperator->FireBullets(nShots * m_nBulletsPerShot, vecShootOrigin, vecShootDir, vecSpread, MAX_TRACE_LENGTH, m_iPrimaryAmmoType, 2, entindex(), iMuzzle); + pOperator->FireBullets(nShots * m_CustomData.m_nBulletsPerShot, vecShootOrigin, vecShootDir, vecSpread, MAX_TRACE_LENGTH, m_iPrimaryAmmoType, 2, entindex(), iMuzzle); pOperator->DoMuzzleFlash(); m_iClip1 = m_iClip1 - nShots; } else { WeaponSound(SINGLE_NPC); - pOperator->FireBullets(m_nBulletsPerShot, vecShootOrigin, vecShootDir, vecSpread, MAX_TRACE_LENGTH, m_iPrimaryAmmoType, 2, entindex(), iMuzzle); + pOperator->FireBullets(m_CustomData.m_nBulletsPerShot, vecShootOrigin, vecShootDir, vecSpread, MAX_TRACE_LENGTH, m_iPrimaryAmmoType, 2, entindex(), iMuzzle); pOperator->DoMuzzleFlash(); m_iClip1 = m_iClip1 - 1; } diff --git a/sp/src/game/shared/mapbase/weapon_custom_scripted.cpp b/sp/src/game/shared/mapbase/weapon_custom_scripted.cpp index 10553de5..a4337105 100644 --- a/sp/src/game/shared/mapbase/weapon_custom_scripted.cpp +++ b/sp/src/game/shared/mapbase/weapon_custom_scripted.cpp @@ -576,15 +576,22 @@ int CWeaponCustomScripted::WeaponMeleeAttack2Condition( float flDot, float flDis return BaseClass::WeaponMeleeAttack2Condition( flDot, flDist ); } -DEFINE_CUSTOM_WEAPON_FACTORY(vscript, CWeaponCustomScripted); -void CWeaponCustomScripted::ParseCustomFromWeaponFile(const char* pFileName) +struct VScriptWeaponCustomData_s { - Q_FileBase(pFileName, m_iszWeaponScriptName.GetForModify(), 256); - KeyValuesAD pKVWeapon("WeaponData"); - if (pKVWeapon->LoadFromFile(filesystem, pFileName, "GAME")) + char cScripts[256]; + + bool Parse(KeyValues* pKVWeapon) { - Q_strncpy(m_iszClientScripts.GetForModify(), pKVWeapon->GetString("vscript_file"), 256); + Q_strncpy(cScripts, pKVWeapon->GetString("vscript_file"), 256); + return true; } +}; + +DEFINE_CUSTOM_WEAPON_FACTORY(vscript, CWeaponCustomScripted, VScriptWeaponCustomData_s); +void CWeaponCustomScripted::InitCustomWeaponFromData(const void* pData, const char* pszWeaponScript) +{ + Q_FileBase(pszWeaponScript, m_iszWeaponScriptName.GetForModify(), 256); + Q_strncpy(m_iszClientScripts.GetForModify(), static_cast (pData)->cScripts, 256); } extern ConVar sv_script_think_interval; diff --git a/sp/src/game/shared/mapbase/weapon_custom_scripted.h b/sp/src/game/shared/mapbase/weapon_custom_scripted.h index 8cc05cd3..c227e35c 100644 --- a/sp/src/game/shared/mapbase/weapon_custom_scripted.h +++ b/sp/src/game/shared/mapbase/weapon_custom_scripted.h @@ -115,7 +115,7 @@ public: int WeaponMeleeAttack2Condition( float flDot, float flDist ); // Inherited via ICustomWeapon - virtual void ParseCustomFromWeaponFile(const char* pFileName) override; + virtual void InitCustomWeaponFromData(const void* pData, const char* pszWeaponScript); #else void OnDataChanged(DataUpdateType_t type); #endif From 31cd394cb700c4555bf18560b5b5f2bffede58ea Mon Sep 17 00:00:00 2001 From: Peter Covington Date: Wed, 20 Apr 2022 03:41:32 -0400 Subject: [PATCH 13/90] More features for custom melee weapon --- sp/src/game/server/basebludgeonweapon.cpp | 174 +++++++++++++++--- sp/src/game/server/basebludgeonweapon.h | 13 ++ .../game/server/mapbase/weapon_custom_hl2.cpp | 16 ++ 3 files changed, 182 insertions(+), 21 deletions(-) diff --git a/sp/src/game/server/basebludgeonweapon.cpp b/sp/src/game/server/basebludgeonweapon.cpp index 93903616..c755e2ef 100644 --- a/sp/src/game/server/basebludgeonweapon.cpp +++ b/sp/src/game/server/basebludgeonweapon.cpp @@ -28,6 +28,15 @@ IMPLEMENT_SERVERCLASS_ST( CBaseHLBludgeonWeapon, DT_BaseHLBludgeonWeapon ) END_SEND_TABLE() +#ifdef MAPBASE +BEGIN_DATADESC(CBaseHLBludgeonWeapon) + +DEFINE_FIELD(m_flDelayedFire, FIELD_TIME), +DEFINE_FIELD(m_bShotDelayed, FIELD_BOOLEAN), + +END_DATADESC() +#endif // MAPBASE + #define BLUDGEON_HULL_DIM 16 static const Vector g_bludgeonMins(-BLUDGEON_HULL_DIM,-BLUDGEON_HULL_DIM,-BLUDGEON_HULL_DIM); @@ -39,6 +48,9 @@ static const Vector g_bludgeonMaxs(BLUDGEON_HULL_DIM,BLUDGEON_HULL_DIM,BLUDGEON_ CBaseHLBludgeonWeapon::CBaseHLBludgeonWeapon() { m_bFiresUnderwater = true; +#ifdef MAPBASE + m_bShotDelayed = false; +#endif // MAPBASE } //----------------------------------------------------------------------------- @@ -96,11 +108,19 @@ void CBaseHLBludgeonWeapon::ItemPostFrame( void ) #ifdef MAPBASE if (pOwner->HasSpawnFlags( SF_PLAYER_SUPPRESS_FIRING )) { + m_bShotDelayed = false; WeaponIdle(); return; } -#endif + // See if we need to fire off our secondary round + if (m_bShotDelayed) + { + if (gpGlobals->curtime > m_flDelayedFire) + DelayedAttack(); + } + else +#endif if ( (pOwner->m_nButtons & IN_ATTACK) && (m_flNextPrimaryAttack <= gpGlobals->curtime) ) { PrimaryAttack(); @@ -239,7 +259,7 @@ Activity CBaseHLBludgeonWeapon::ChooseIntersectionPointAndActivity( trace_t &hit } - return ACT_VM_HITCENTER; + return GetPrimaryAttackActivity(); } //----------------------------------------------------------------------------- @@ -297,7 +317,6 @@ void CBaseHLBludgeonWeapon::ImpactEffect( trace_t &traceHit ) UTIL_ImpactTrace( &traceHit, DMG_CLUB ); } - //------------------------------------------------------------------------------ // Purpose : Starts the swing of the weapon and determines the animation // Input : bIsSecondary - is this a secondary attack? @@ -320,10 +339,14 @@ void CBaseHLBludgeonWeapon::Swing( int bIsSecondary ) Vector swingEnd = swingStart + forward * GetRange(); UTIL_TraceLine( swingStart, swingEnd, MASK_SHOT_HULL, pOwner, COLLISION_GROUP_NONE, &traceHit ); - Activity nHitActivity = ACT_VM_HITCENTER; + Activity nHitActivity = GetPrimaryAttackActivity(); // Like bullets, bludgeon traces have to trace against triggers. - CTakeDamageInfo triggerInfo( GetOwner(), GetOwner(), GetDamageForActivity( nHitActivity ), DMG_CLUB ); +#ifdef MAPBASE + CTakeDamageInfo triggerInfo(GetOwner(), GetOwner(), GetDamageForActivity(nHitActivity), GetDamageType()); +#else + CTakeDamageInfo triggerInfo(GetOwner(), GetOwner(), GetDamageForActivity(nHitActivity), DMG_CLUB); +#endif // MAPBASE triggerInfo.SetDamagePosition( traceHit.startpos ); triggerInfo.SetDamageForce( forward ); TraceAttackToTriggers( triggerInfo, traceHit.startpos, traceHit.endpos, forward ); @@ -374,31 +397,20 @@ void CBaseHLBludgeonWeapon::Swing( int bIsSecondary ) { nHitActivity = bIsSecondary ? ACT_VM_MISSCENTER2 : ACT_VM_MISSCENTER; +#ifndef MAPBASE // We want to test the first swing again Vector testEnd = swingStart + forward * GetRange(); -#ifdef MAPBASE - // Sound has been moved here since we're using the other melee sounds now - WeaponSound( SINGLE ); -#endif - // See if we happened to hit water - ImpactWater( swingStart, testEnd ); + ImpactWater(swingStart, testEnd); +#endif // !MAPBASE } +#ifndef MAPBASE else { -#ifdef MAPBASE - // Other melee sounds - if (traceHit.m_pEnt && traceHit.m_pEnt->IsWorld()) - WeaponSound(MELEE_HIT_WORLD); - else if (traceHit.m_pEnt && !traceHit.m_pEnt->PassesDamageFilter(triggerInfo)) - WeaponSound(MELEE_MISS); - else - WeaponSound(MELEE_HIT); -#endif - Hit( traceHit, nHitActivity, bIsSecondary ? true : false ); } +#endif // Send the anim SendWeaponAnim( nHitActivity ); @@ -414,5 +426,125 @@ void CBaseHLBludgeonWeapon::Swing( int bIsSecondary ) #ifdef MAPBASE pOwner->SetAnimation( PLAYER_ATTACK1 ); + + if (GetHitDelay() > 0.f) + { + //Play swing sound + WeaponSound(SINGLE); + + m_flDelayedFire = gpGlobals->curtime + GetHitDelay(); + m_bShotDelayed = true; + } + else + { + if (traceHit.fraction == 1.0f) + { + // We want to test the first swing again + Vector testEnd = swingStart + forward * GetRange(); + + //Play swing sound + WeaponSound(SINGLE); + + // See if we happened to hit water + ImpactWater(swingStart, testEnd); + } + else + { + // Other melee sounds + if (traceHit.m_pEnt && traceHit.m_pEnt->IsWorld()) + WeaponSound(MELEE_HIT_WORLD); + else if (traceHit.m_pEnt && !traceHit.m_pEnt->PassesDamageFilter(triggerInfo)) + WeaponSound(MELEE_MISS); + else + WeaponSound(MELEE_HIT); + + Hit(traceHit, nHitActivity, bIsSecondary ? true : false); + } + } #endif } + +#ifdef MAPBASE +void CBaseHLBludgeonWeapon::DelayedAttack(void) +{ + m_bShotDelayed = false; + + trace_t traceHit; + + // Try a ray + CBasePlayer* pOwner = ToBasePlayer(GetOwner()); + if (!pOwner) + return; + + pOwner->RumbleEffect(RUMBLE_CROWBAR_SWING, 0, RUMBLE_FLAG_RESTART); + + Vector swingStart = pOwner->Weapon_ShootPosition(); + Vector forward; + + forward = pOwner->GetAutoaimVector(AUTOAIM_SCALE_DEFAULT, GetRange()); + + Vector swingEnd = swingStart + forward * GetRange(); + UTIL_TraceLine(swingStart, swingEnd, MASK_SHOT_HULL, pOwner, COLLISION_GROUP_NONE, &traceHit); + + if (traceHit.fraction == 1.0) + { + float bludgeonHullRadius = 1.732f * BLUDGEON_HULL_DIM; // hull is +/- 16, so use cuberoot of 2 to determine how big the hull is from center to the corner point + + // Back off by hull "radius" + swingEnd -= forward * bludgeonHullRadius; + + UTIL_TraceHull(swingStart, swingEnd, g_bludgeonMins, g_bludgeonMaxs, MASK_SHOT_HULL, pOwner, COLLISION_GROUP_NONE, &traceHit); + if (traceHit.fraction < 1.0 && traceHit.m_pEnt) + { + Vector vecToTarget = traceHit.m_pEnt->GetAbsOrigin() - swingStart; + VectorNormalize(vecToTarget); + + float dot = vecToTarget.Dot(forward); + + // YWB: Make sure they are sort of facing the guy at least... + if (dot < 0.70721f) + { + // Force amiss + traceHit.fraction = 1.0f; + } + else + { + ChooseIntersectionPointAndActivity(traceHit, g_bludgeonMins, g_bludgeonMaxs, pOwner); + } + } + } + + if (traceHit.fraction == 1.0f) + { + // We want to test the first swing again + Vector testEnd = swingStart + forward * GetRange(); + + // See if we happened to hit water + ImpactWater(swingStart, testEnd); + } + else + { + CTakeDamageInfo triggerInfo(GetOwner(), GetOwner(), GetDamageForActivity(GetActivity()), GetDamageType()); + triggerInfo.SetDamagePosition(traceHit.startpos); + triggerInfo.SetDamageForce(forward); + + // Other melee sounds + if (traceHit.m_pEnt && traceHit.m_pEnt->IsWorld()) + WeaponSound(MELEE_HIT_WORLD); + else if (traceHit.m_pEnt && !traceHit.m_pEnt->PassesDamageFilter(triggerInfo)) + WeaponSound(MELEE_MISS); + else + WeaponSound(MELEE_HIT); + + Hit(traceHit, GetActivity(), false); + } +} + +bool CBaseHLBludgeonWeapon::CanHolster(void) +{ + if (m_bShotDelayed) + return false; + + return BaseClass::CanHolster(); +} +#endif // MAPBASE diff --git a/sp/src/game/server/basebludgeonweapon.h b/sp/src/game/server/basebludgeonweapon.h index 52f02f9b..57f0ed11 100644 --- a/sp/src/game/server/basebludgeonweapon.h +++ b/sp/src/game/server/basebludgeonweapon.h @@ -23,6 +23,9 @@ public: CBaseHLBludgeonWeapon(); DECLARE_SERVERCLASS(); +#ifdef MAPBASE + DECLARE_DATADESC(); +#endif // MAPBASE virtual void Spawn( void ); virtual void Precache( void ); @@ -30,6 +33,9 @@ public: //Attack functions virtual void PrimaryAttack( void ); virtual void SecondaryAttack( void ); +#ifdef MAPBASE + void DelayedAttack(void); +#endif // MAPBASE virtual void ItemPostFrame( void ); @@ -46,6 +52,8 @@ public: #ifdef MAPBASE virtual int GetDamageType() { return DMG_CLUB; } + virtual float GetHitDelay() { return 0.f; } + virtual bool CanHolster(void); #endif // MAPBASE protected: @@ -56,6 +64,11 @@ private: void Swing( int bIsSecondary ); void Hit( trace_t &traceHit, Activity nHitActivity, bool bIsSecondary ); Activity ChooseIntersectionPointAndActivity( trace_t &hitTrace, const Vector &mins, const Vector &maxs, CBasePlayer *pOwner ); + +#ifdef MAPBASE + float m_flDelayedFire; + bool m_bShotDelayed; +#endif // MAPBASE }; #endif \ No newline at end of file diff --git a/sp/src/game/server/mapbase/weapon_custom_hl2.cpp b/sp/src/game/server/mapbase/weapon_custom_hl2.cpp index 86c9733b..0a5996c5 100644 --- a/sp/src/game/server/mapbase/weapon_custom_hl2.cpp +++ b/sp/src/game/server/mapbase/weapon_custom_hl2.cpp @@ -55,6 +55,8 @@ typedef struct HL2CustomMeleeData_s float m_flRefireRate; float m_flDamage; float m_flNPCDamage; + float m_flHitDelay; + Activity m_nHitActivity = ACT_INVALID; byte m_nDamageClass; bool Parse(KeyValues*); @@ -72,6 +74,7 @@ public: float GetRange(void) { return m_CustomData.m_flMeleeRange; } float GetFireRate(void) { return m_CustomData.m_flRefireRate; } + float GetHitDelay() { return m_CustomData.m_flHitDelay; } void AddViewKick(void); float GetDamageForActivity(Activity hitActivity); @@ -86,6 +89,9 @@ public: acttable_t* GetBackupActivityList() { return NULL; } int GetBackupActivityListCount() { return 0; } + //Functions to select animation sequences + virtual Activity GetPrimaryAttackActivity(void) { return m_CustomData.m_nHitActivity; } + const char* GetWeaponScriptName() { return m_iszWeaponScriptName.Get(); } virtual int GetDamageType() { return g_nDamageClassTypeBits[m_CustomData.m_nDamageClass]; } @@ -116,6 +122,16 @@ bool HL2CustomMeleeData_s::Parse(KeyValues* pKVWeapon) m_flNPCDamage = pkvData->GetFloat("damage_npc", m_flDamage); m_flMeleeRange = pkvData->GetFloat("range", 70.f); m_flRefireRate = pkvData->GetFloat("rate", 0.7f); + m_flHitDelay = pkvData->GetFloat("hitdelay"); + if (pkvData->FindKey("activity_hit")) + { + m_nHitActivity = (Activity)ActivityList_IndexForName(pkvData->GetString("activity_hit")); + } + + if (m_nHitActivity == ACT_INVALID) + { + m_nHitActivity = ACT_VM_HITCENTER; + } const char* pszDamageClass = pkvData->GetString("damage_type", nullptr); if (pszDamageClass) From 37019140bd0ed63816c08303b966f1211a977d14 Mon Sep 17 00:00:00 2001 From: Peter Covington Date: Wed, 20 Apr 2022 04:05:34 -0400 Subject: [PATCH 14/90] Fix --- sp/src/game/server/mapbase/weapon_custom_hl2.cpp | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/sp/src/game/server/mapbase/weapon_custom_hl2.cpp b/sp/src/game/server/mapbase/weapon_custom_hl2.cpp index 0a5996c5..834bad2c 100644 --- a/sp/src/game/server/mapbase/weapon_custom_hl2.cpp +++ b/sp/src/game/server/mapbase/weapon_custom_hl2.cpp @@ -56,8 +56,8 @@ typedef struct HL2CustomMeleeData_s float m_flDamage; float m_flNPCDamage; float m_flHitDelay; - Activity m_nHitActivity = ACT_INVALID; byte m_nDamageClass; + bool m_bHitUsesMissAnim; bool Parse(KeyValues*); } HL2CustomMeleeData_t; @@ -90,7 +90,7 @@ public: int GetBackupActivityListCount() { return 0; } //Functions to select animation sequences - virtual Activity GetPrimaryAttackActivity(void) { return m_CustomData.m_nHitActivity; } + virtual Activity GetPrimaryAttackActivity(void) { return m_CustomData.m_bHitUsesMissAnim ? ACT_VM_MISSCENTER : BaseClass::GetPrimaryAttackActivity(); } const char* GetWeaponScriptName() { return m_iszWeaponScriptName.Get(); } virtual int GetDamageType() { return g_nDamageClassTypeBits[m_CustomData.m_nDamageClass]; } @@ -123,15 +123,7 @@ bool HL2CustomMeleeData_s::Parse(KeyValues* pKVWeapon) m_flMeleeRange = pkvData->GetFloat("range", 70.f); m_flRefireRate = pkvData->GetFloat("rate", 0.7f); m_flHitDelay = pkvData->GetFloat("hitdelay"); - if (pkvData->FindKey("activity_hit")) - { - m_nHitActivity = (Activity)ActivityList_IndexForName(pkvData->GetString("activity_hit")); - } - - if (m_nHitActivity == ACT_INVALID) - { - m_nHitActivity = ACT_VM_HITCENTER; - } + m_bHitUsesMissAnim = pkvData->GetBool("hit_uses_miss_anim"); const char* pszDamageClass = pkvData->GetString("damage_type", nullptr); if (pszDamageClass) From d3978db574ea98093190ae5c99854d69f3899d04 Mon Sep 17 00:00:00 2001 From: Peter Covington Date: Thu, 21 Apr 2022 22:56:24 -0400 Subject: [PATCH 15/90] Fixed custom gun not reseting zoom when holstered --- sp/src/game/server/mapbase/weapon_custom_hl2.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/sp/src/game/server/mapbase/weapon_custom_hl2.cpp b/sp/src/game/server/mapbase/weapon_custom_hl2.cpp index 834bad2c..7c8241bc 100644 --- a/sp/src/game/server/mapbase/weapon_custom_hl2.cpp +++ b/sp/src/game/server/mapbase/weapon_custom_hl2.cpp @@ -386,6 +386,7 @@ public: virtual void ItemPostFrame(void); // called each frame by the player PostThink virtual void ItemBusyFrame(void); // called each frame by the player PostThink, if the player's not ready to attack yet virtual bool ReloadOrSwitchWeapons(void); + virtual bool Holster(CBaseCombatWeapon* pSwitchingTo = NULL); // Bullet launch information virtual const Vector& GetBulletSpread(void); @@ -782,6 +783,17 @@ void CHLCustomWeaponGun::AddViewKick(void) } } +bool CHLCustomWeaponGun::Holster(CBaseCombatWeapon* pSwitchingTo) +{ + // Stop zooming + if (m_bInZoom) + { + ToggleZoom(); + } + + return BaseClass::Holster(pSwitchingTo); +} + //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- From 5e891f85a6f46d038afb7feb7628b6f26ac641ec Mon Sep 17 00:00:00 2001 From: samisalreadytaken <46823719+samisalreadytaken@users.noreply.github.com> Date: Thu, 24 Nov 2022 13:39:59 +0300 Subject: [PATCH 16/90] Update vscript_vgui enabled parenting to clientmode hud viewport added script func `vgui.GetHudViewport()` fixed case sensitivity of panel classname and panel callback inputs fixed script fonts not being reloaded when no panel parented to the root was created --- sp/src/game/client/mapbase/vscript_vgui.cpp | 329 +++++++++++++++----- sp/src/game/client/mapbase/vscript_vgui.nut | 36 +-- 2 files changed, 265 insertions(+), 100 deletions(-) diff --git a/sp/src/game/client/mapbase/vscript_vgui.cpp b/sp/src/game/client/mapbase/vscript_vgui.cpp index 8d08e8be..815679e0 100644 --- a/sp/src/game/client/mapbase/vscript_vgui.cpp +++ b/sp/src/game/client/mapbase/vscript_vgui.cpp @@ -51,7 +51,7 @@ #include "view.h" #include "hudelement.h" -//#include "iclientmode.h" // g_pClientMode->GetViewport() +#include "iclientmode.h" // g_pClientMode->GetViewport() #include "vscript_vgui.h" #include "vscript_vgui.nut" @@ -92,10 +92,18 @@ // Changing this is not backwards compatible, as existing top level script panel depth would then change relative to non-script panels. #define SCRIPT_ENGINE_ROOT_PANELS 1 -// NOTE: causes rendering issues -#define ALLOW_SCRIPT_HUD_VIEWPORT_ROOT_PANEL 0 +// +// Options to restrict where script panels can be parented to. +// The safest options any game can have are HUD viewport and clientdll. +// -#define ALLOW_SCRIPT_GAMEUI_ROOT_PANEL 0 +#define ALLOW_ROOT_PANEL_PARENT 1 + +#define ALLOW_HUD_VIEWPORT_ROOT_PARENT 1 + +#define ALLOW_CLIENTDLL_ROOT_PARENT 1 + +#define ALLOW_GAMEUI_ROOT_PARENT 0 // On level transitions Restore is called up to 4 times in a row (due to .hl? client state files), each time // trying to restore script panels from pre and post transitions, failing every time because script panels are @@ -353,8 +361,10 @@ public: } }; +#if ALLOW_CLIENTDLL_ROOT_PARENT CScriptRootDLLPanel *g_pScriptClientDLLPanel = NULL; -#if ALLOW_SCRIPT_GAMEUI_ROOT_PANEL +#endif +#if ALLOW_GAMEUI_ROOT_PARENT CScriptRootDLLPanel *g_pScriptGameUIDLLPanel = NULL; #endif #endif @@ -367,12 +377,14 @@ void VGUI_DestroyScriptRootPanels() g_pScriptRootPanel = NULL; } #if SCRIPT_ENGINE_ROOT_PANELS +#if ALLOW_CLIENTDLL_ROOT_PARENT if ( g_pScriptClientDLLPanel ) { delete g_pScriptClientDLLPanel; g_pScriptClientDLLPanel = NULL; } -#if ALLOW_SCRIPT_GAMEUI_ROOT_PANEL +#endif +#if ALLOW_GAMEUI_ROOT_PARENT if ( g_pScriptGameUIDLLPanel ) { delete g_pScriptGameUIDLLPanel; @@ -384,30 +396,29 @@ void VGUI_DestroyScriptRootPanels() VPANEL VGUI_GetScriptRootPanel( VGuiPanel_t type ) { -#if !SCRIPT_ENGINE_ROOT_PANELS - if ( !g_pScriptRootPanel ) - g_pScriptRootPanel = new CScriptRootPanel(); - - return enginevgui->GetPanel( type ); -#else +#if SCRIPT_ENGINE_ROOT_PANELS switch ( type ) { case PANEL_ROOT: +#if ALLOW_ROOT_PANEL_PARENT { if ( !g_pScriptRootPanel ) g_pScriptRootPanel = new CScriptRootPanel(); return g_pScriptRootPanel->GetVPanel(); } +#endif case PANEL_CLIENTDLL: +#if ALLOW_CLIENTDLL_ROOT_PARENT { if ( !g_pScriptClientDLLPanel ) g_pScriptClientDLLPanel = new CScriptRootDLLPanel( PANEL_CLIENTDLL, "VScriptClient" ); return g_pScriptClientDLLPanel->GetVPanel(); } -#if ALLOW_SCRIPT_GAMEUI_ROOT_PANEL +#endif case PANEL_GAMEUIDLL: +#if ALLOW_GAMEUI_ROOT_PARENT { if ( !g_pScriptGameUIDLLPanel ) g_pScriptGameUIDLLPanel = new CScriptRootDLLPanel( PANEL_GAMEUIDLL, "VScriptGameUI" ); @@ -415,8 +426,10 @@ VPANEL VGUI_GetScriptRootPanel( VGuiPanel_t type ) return g_pScriptGameUIDLLPanel->GetVPanel(); } #endif + default: return NULL; } - return NULL; +#else + return enginevgui->GetPanel(type); #endif } @@ -663,6 +676,11 @@ int CScriptSurface::GetCharacterWidth( int font, int ch ) void CScriptSurface::CreateFont( const char *customName, const char *windowsFontName, int tall, int weight, int blur, int scanlines, int flags, int yresMin, int yresMax, bool proportional ) { + // Make sure font invalidation callback is established. + // Not necessary if script fonts are reloaded in engine. + if ( !g_pScriptRootPanel ) + g_pScriptRootPanel = new CScriptRootPanel(); + if ( flags & ISurface::FONTFLAG_BITMAP ) { AssertMsg( 0, "Bitmap fonts are not supported!" ); @@ -681,16 +699,41 @@ void CScriptSurface::CreateFont( const char *customName, const char *windowsFont return; } +#if 0 + bool bProportionalFallbackFont = false; + if ( proportional ) + { + // Find if this is a resolution filtered font alias + const char *fontAlias = GetFixedFontName( customName, false ); + int idx = g_ScriptFonts.Find( fontAlias ); + if ( idx != g_ScriptFonts.InvalidIndex() ) + { + fontalias_t &alias = g_ScriptFonts[idx]; + for ( int i = 0; i < alias.Count(); ++i ) + { + FontData_t &data = alias.Element(i); + if ( data.yres_min && data.yres_max ) + { + bProportionalFallbackFont = true; + + // Save this proportional font in non-proportional alias + proportional = false; + break; + } + } + } + } +#endif + const char *fontAlias = GetFixedFontName( customName, proportional ); int idx = g_ScriptFonts.Find( fontAlias ); if ( idx != g_ScriptFonts.InvalidIndex() ) { fontalias_t &alias = g_ScriptFonts[idx]; - // TODO: One proportional font to fall back to amongst resolution filtered fonts. #ifdef _DEBUG - if ( !yresMin && !yresMax ) + if ( !yresMin && !yresMax /*&& !bProportionalFallbackFont*/ ) { // There must be only one font registered. Assert( alias.Count() == 1 ); @@ -703,7 +746,7 @@ void CScriptSurface::CreateFont( const char *customName, const char *windowsFont // Font changes will not be applied. Assert( oldTall == newTall ); if ( oldName ) // can be null - Assert( !V_stricmp( oldName, windowsFontName ) ); + AssertMsg( !V_stricmp( oldName, windowsFontName ), "'%s' != '%s'", oldName, windowsFontName ); } #endif @@ -716,7 +759,10 @@ void CScriptSurface::CreateFont( const char *customName, const char *windowsFont if ( yresMin == data.yres_min && yresMax == data.yres_max ) return; } - +#if 0 + if ( bProportionalFallbackFont ) + proportional = true; +#endif DebugMsg( "Create font add '%s' [%d %d]\n", fontAlias, yresMin, yresMax ); FontData_t &newFont = alias.Element( alias.AddToTail() ); @@ -731,6 +777,22 @@ void CScriptSurface::CreateFont( const char *customName, const char *windowsFont newFont.yres_max = yresMax; newFont.proportional = proportional; +#if 0 + // Put the proportional font in the very end so that it is loaded only when no resolution is matched + struct L + { + static int __cdecl F( const FontData_t* a, const FontData_t* b ) + { + if ( !a->proportional && b->proportional ) + return -1; + if ( a->proportional && !b->proportional ) + return 1; + return 0; + } + }; + alias.Sort( L::F ); +#endif + LoadFont( newFont DBG_PARAM(, fontAlias) ); } else @@ -1067,7 +1129,7 @@ public:\ class CScript_##panelClass : public panelClass\ {\ DECLARE_SCRIPTVGUI_CLASS( panelClass )\ - void Shutdown() {}\ + void ScriptShutdown() {}\ \ public:\ CScript_##panelClass( Panel *parent, const char *name )\ @@ -1085,7 +1147,7 @@ public:\ class CScript_##panelClass : public panelClass\ {\ DECLARE_SCRIPTVGUI_CLASS( panelClass )\ - void Shutdown() {}\ + void ScriptShutdown() {}\ \ public:\ CScript_##panelClass( Panel *parent, const char *name, const char *text )\ @@ -1292,7 +1354,7 @@ public: if ( GetVPanel() ) { DebugMsg( " Destroy panel '%s' %s\n", _base->GetName(), GetDebugName() ); - _base->Shutdown(); + _base->ScriptShutdown(); ResolveChildren_r( _vpanel ); _base->MarkForDeletion(); } @@ -1339,6 +1401,11 @@ public: } AssertMsg( 0, "invalid parent" ); + + g_ScriptPanels.AddToTail( this ); + + // leave me parentless + return; } g_ScriptPanels.AddToTail( this ); @@ -1348,27 +1415,37 @@ public: // // This parameter is hidden in script, and is defined by the return value of dummy functions. VPANEL vparent = 0; + switch ( root ) { + #if ALLOW_ROOT_PANEL_PARENT case 0: vparent = VGUI_GetScriptRootPanel( PANEL_ROOT ); break; -#if ALLOW_SCRIPT_GAMEUI_ROOT_PANEL + #endif + #if ALLOW_GAMEUI_ROOT_PARENT case 1: vparent = VGUI_GetScriptRootPanel( PANEL_GAMEUIDLL ); break; -#endif + #endif + #if ALLOW_CLIENTDLL_ROOT_PARENT case 2: vparent = VGUI_GetScriptRootPanel( PANEL_CLIENTDLL ); break; -#if ALLOW_SCRIPT_HUD_VIEWPORT_ROOT_PANEL - // Hud viewport - case 10: + #endif + #if ALLOW_HUD_VIEWPORT_ROOT_PARENT + case 10: // Hud viewport Assert( g_pClientMode && g_pClientMode->GetViewport() ); vparent = g_pClientMode->GetViewport()->GetVPanel(); break; -#endif - default: UNREACHABLE(); // Invalid parent panel + #endif + default: + #if SCRIPT_ENGINE_ROOT_PANELS + UNREACHABLE(); // Invalid parent panel + #else + // Allow everything defined in vscript_vgui.nut + vparent = VGUI_GetScriptRootPanel( (VGuiPanel_t)root ); + #endif } _base->SetParent( vparent ); @@ -1434,10 +1511,12 @@ public: bool bRootParent = false; #if SCRIPT_ENGINE_ROOT_PANELS if ( ( parent == g_pScriptRootPanel->GetVPanel() ) - #if ALLOW_SCRIPT_GAMEUI_ROOT_PANEL + #if ALLOW_GAMEUI_ROOT_PARENT || ( g_pScriptGameUIDLLPanel && parent == g_pScriptGameUIDLLPanel->GetVPanel() ) #endif + #if ALLOW_CLIENTDLL_ROOT_PARENT || ( g_pScriptClientDLLPanel && parent == g_pScriptClientDLLPanel->GetVPanel() ) + #endif ) { bRootParent = true; @@ -1452,7 +1531,7 @@ public: break; } } -#if ALLOW_SCRIPT_HUD_VIEWPORT_ROOT_PANEL +#if ALLOW_HUD_VIEWPORT_ROOT_PARENT if ( g_pClientMode && g_pClientMode->GetViewport() && ( parent == g_pClientMode->GetViewport()->GetVPanel() ) ) bRootParent = true; #endif @@ -1829,10 +1908,12 @@ public: \ DEFINE_SCRIPTFUNC( SetCursor, "" )\ DEFINE_SCRIPTFUNC( IsCursorOver, "" )\ +\ DEFINE_SCRIPTFUNC( HasFocus, "" )\ DEFINE_SCRIPTFUNC( RequestFocus, "" )\ DEFINE_SCRIPTFUNC( MakePopup, "" )\ DEFINE_SCRIPTFUNC( MoveToFront, "" )\ +\ DEFINE_SCRIPTFUNC( SetMouseInputEnabled, "" )\ DEFINE_SCRIPTFUNC( SetKeyBoardInputEnabled, "" )\ \ @@ -1842,10 +1923,7 @@ public: //-------------------------------------------------------------- //-------------------------------------------------------------- // These need more testing. -// TODO: IScript_Panel::FindChildByName() // TODO: DECLARE_BUILD_FACTORY_SCRIPT() to create overridable script panels from controls file -// TODO: CScript_EditablePanel::ApplySchemeSettings() callback -// (IScheme parameter can be passed as null until schemes are also tested) #if BUILD_GROUPS_ENABLED CLASS_HELPER_INTERFACE( EditablePanel, Panel ) { @@ -1855,11 +1933,27 @@ public: { __base()->LoadControlSettings( resName ); } + + HSCRIPT FindChildByName( const char *childName ) + { + Panel *pPanel = __base()->FindChildByName( childName, false ); + if ( pPanel ) + { + int i; + IScriptVGUIObject* obj = FindInScriptPanels( child, i ); + if ( obj ) + { + return obj->GetScriptInstance(); + } + } + return NULL; + } }; #define DEFINE_VGUI_SCRIPTFUNC_EditablePanel()\ DEFINE_VGUI_SCRIPTFUNC_Panel()\ - DEFINE_SCRIPTFUNC( LoadControlSettings, "" ) + DEFINE_SCRIPTFUNC( LoadControlSettings, "" )\ + DEFINE_SCRIPTFUNC( FindChildByName, "" ) #endif //-------------------------------------------------------------- //-------------------------------------------------------------- @@ -2106,6 +2200,16 @@ public: { __base()->SetShouldScaleImage( state ); } + + void SetRotation( int rotation ) + { + Assert( rotation == ROTATED_UNROTATED || + rotation == ROTATED_CLOCKWISE_90 || + rotation == ROTATED_ANTICLOCKWISE_90 || + rotation == ROTATED_FLIPPED ); + + __base()->SetRotation( rotation ); + } #if 0 void SetFrame( int nFrame ) { @@ -2120,6 +2224,7 @@ public: DEFINE_SCRIPTFUNC( SetDrawColor, "" )\ DEFINE_SCRIPTFUNC( SetTileImage, "" )\ DEFINE_SCRIPTFUNC( SetShouldScaleImage, "" )\ + DEFINE_SCRIPTFUNC( SetRotation, "" )\ //-------------------------------------------------------------- //-------------------------------------------------------------- @@ -2435,10 +2540,10 @@ public: //-------------------------------------------------------------- //-------------------------------------------------------------- #if VGUI_TGA_IMAGE_PANEL -CLASS_HELPER_INTERFACE( TGAImagePanel, Panel ) +CLASS_HELPER_INTERFACE( TGAImage, Panel ) { public: - void SetTGAImage( const char *p ) + void SetImage( const char *p ) { __base()->SetTGAImage( p ); } @@ -2454,9 +2559,37 @@ public: } }; -#define DEFINE_VGUI_SCRIPTFUNC_TGAImagePanel()\ +#define DEFINE_VGUI_SCRIPTFUNC_TGAImage()\ DEFINE_VGUI_SCRIPTFUNC_Panel()\ - DEFINE_SCRIPTFUNC( SetTGAImage, "" )\ + DEFINE_SCRIPTFUNC( SetImage, "" )\ + DEFINE_SCRIPTFUNC( SetDrawColor, "" )\ + DEFINE_SCRIPTFUNC( SetShouldScaleImage, "" ) +#endif +//-------------------------------------------------------------- +//-------------------------------------------------------------- +#if 0 +CLASS_HELPER_INTERFACE( PNGImage, Panel ) +{ +public: + void SetImage( const char *p ) + { + __base()->SetPNGImage( p ); + } + + void SetDrawColor( int r, int g, int b, int a ) + { + __base()->SetDrawColor( r, g, b, a ); + } + + void SetShouldScaleImage( bool i ) + { + __base()->SetShouldScaleImage( i ); + } +}; + +#define DEFINE_VGUI_SCRIPTFUNC_PNGImage()\ + DEFINE_VGUI_SCRIPTFUNC_Panel()\ + DEFINE_SCRIPTFUNC( SetImage, "" )\ DEFINE_SCRIPTFUNC( SetDrawColor, "" )\ DEFINE_SCRIPTFUNC( SetShouldScaleImage, "" ) #endif @@ -2479,7 +2612,7 @@ static inline void SetHScript( HSCRIPT &var, HSCRIPT val ) } #define CheckCallback(s)\ - if ( FStrEq( cb, #s ) )\ + if ( !V_strcmp( cb, #s ) )\ {\ SetHScript( m_hfn##s, fn );\ return;\ @@ -2515,6 +2648,7 @@ private: HSCRIPT m_hfnOnKeyCodePressed; HSCRIPT m_hfnOnKeyCodeReleased; HSCRIPT m_hfnOnKeyCodeTyped; + #if SCRIPT_VGUI_SIGNAL_INTERFACE HSCRIPT m_hfnOnCommand; #endif @@ -2530,7 +2664,9 @@ public: m_hfnPerformLayout(NULL), m_hfnOnTick(NULL), m_hfnOnScreenSizeChanged(NULL), - +#if SCRIPT_VGUI_SIGNAL_INTERFACE + m_hfnOnCommand(NULL), +#endif m_hfnOnCursorEntered(NULL), m_hfnOnCursorExited(NULL), m_hfnOnCursorMoved(NULL), @@ -2543,13 +2679,9 @@ public: m_hfnOnKeyCodePressed(NULL), m_hfnOnKeyCodeReleased(NULL), m_hfnOnKeyCodeTyped(NULL) -#if SCRIPT_VGUI_SIGNAL_INTERFACE - , - m_hfnOnCommand(NULL) -#endif {} - void Shutdown() + void ScriptShutdown() { ivgui()->RemoveTickSignal( GetVPanel() ); @@ -2573,6 +2705,7 @@ public: SetHScript( m_hfnOnKeyCodePressed, NULL ); SetHScript( m_hfnOnKeyCodeReleased, NULL ); SetHScript( m_hfnOnKeyCodeTyped, NULL ); + #if SCRIPT_VGUI_SIGNAL_INTERFACE SetHScript( m_hfnOnCommand, NULL ); #endif @@ -2775,6 +2908,7 @@ public: CheckCallback( OnKeyCodePressed ); CheckCallback( OnKeyCodeReleased ); CheckCallback( OnKeyCodeTyped ); + #if SCRIPT_VGUI_SIGNAL_INTERFACE CheckCallback( OnCommand ); #endif @@ -2810,6 +2944,7 @@ private: HSCRIPT m_hfnOnKeyCodePressed; HSCRIPT m_hfnOnKeyCodeReleased; HSCRIPT m_hfnOnKeyCodeTyped; + #if SCRIPT_VGUI_SIGNAL_INTERFACE HSCRIPT m_hfnOnCommand; #endif @@ -2826,6 +2961,9 @@ public: m_hfnPerformLayout(NULL), m_hfnOnTick(NULL), m_hfnOnScreenSizeChanged(NULL), +#if SCRIPT_VGUI_SIGNAL_INTERFACE + m_hfnOnCommand(NULL), +#endif m_hfnOnCursorEntered(NULL), m_hfnOnCursorExited(NULL), @@ -2839,15 +2977,11 @@ public: m_hfnOnKeyCodePressed(NULL), m_hfnOnKeyCodeReleased(NULL), m_hfnOnKeyCodeTyped(NULL) -#if SCRIPT_VGUI_SIGNAL_INTERFACE - , - m_hfnOnCommand(NULL) -#endif { SetFadeEffectDisableOverride( true ); } - void Shutdown() + void ScriptShutdown() { ivgui()->RemoveTickSignal( GetVPanel() ); @@ -2866,6 +3000,7 @@ public: SetHScript( m_hfnOnKeyCodePressed, NULL ); SetHScript( m_hfnOnKeyCodeReleased, NULL ); SetHScript( m_hfnOnKeyCodeTyped, NULL ); + #if SCRIPT_VGUI_SIGNAL_INTERFACE SetHScript( m_hfnOnCommand, NULL ); #endif @@ -2898,7 +3033,18 @@ public: g_pScriptVM->ExecuteFunction( m_hfnPerformLayout, NULL, 0, NULL, NULL, true ); } } +#if 0 + void ApplySchemeSettings( IScheme *pScheme ) + { + BaseClass::ApplySchemeSettings( pScheme ); + if ( m_hfnApplySchemeSettings ) + { + ScriptVariant_t arg; + g_pScriptVM->ExecuteFunction( m_hfnApplySchemeSettings, &arg, 1, NULL, NULL, true ); + } + } +#endif void OnTick() { g_pScriptVM->ExecuteFunction( m_hfnOnTick, NULL, 0, NULL, NULL, true ); @@ -3072,6 +3218,7 @@ public: CheckCallback( OnKeyCodePressed ); CheckCallback( OnKeyCodeReleased ); CheckCallback( OnKeyCodeTyped ); + #if SCRIPT_VGUI_SIGNAL_INTERFACE CheckCallback( OnCommand ); #endif @@ -3102,7 +3249,7 @@ public: m_hfnDoClick(NULL) {} - void Shutdown() + void ScriptShutdown() { SetHScript( m_hfnPaint, NULL ); SetHScript( m_hfnPaintBackground, NULL ); @@ -3173,7 +3320,7 @@ public: m_hfnTextChanged(NULL) {} - void Shutdown() + void ScriptShutdown() { SetHScript( m_hfnTextChanged, NULL ); } @@ -3216,9 +3363,12 @@ public: SetShouldDrawFriendIcon( false ); } - DEBUG_DESTRUCTOR( ~CScript_AvatarImage, CAvatarImagePanel ) + ~CScript_AvatarImage() + { + DebugDestructor( CAvatarImagePanel ); + } - void Shutdown() {} + void ScriptShutdown() {} }; #endif //-------------------------------------------------------------- @@ -3229,7 +3379,7 @@ class CTGAImagePanel : public Panel DECLARE_SCRIPTVGUI_CLASS_EX( CTGAImagePanel, Panel ); private: - int m_iTextureID; + int m_iTexture; int m_nWidth; int m_nHeight; Color m_ImageColor; @@ -3238,7 +3388,7 @@ private: public: CTGAImagePanel( Panel *parent, const char *name ) : BaseClass( parent, name ), - m_iTextureID(-1), + m_iTexture(-1), m_bScaleImage(0), m_ImageColor( 255, 255, 255, 255 ) { @@ -3249,21 +3399,21 @@ public: { DebugDestructor( CTGAImagePanel ); - if ( m_iTextureID != -1 ) + if ( m_iTexture != -1 ) { - surface()->DestroyTextureID( m_iTextureID ); + surface()->DestroyTextureID( m_iTexture ); } } - void Shutdown() {} + void ScriptShutdown() {} public: void Paint() { - if ( m_iTextureID != -1 ) + if ( m_iTexture != -1 ) { surface()->DrawSetColor( m_ImageColor ); - surface()->DrawSetTexture( m_iTextureID ); + surface()->DrawSetTexture( m_iTexture ); if ( m_bScaleImage ) { @@ -3288,19 +3438,21 @@ public: public: void SetTGAImage( const char *fileName ) { - if ( V_stricmp( V_GetFileExtension( fileName ), "tga" ) != 0 ) + const char *ext = V_GetFileExtension( fileName ); + + if ( ext && V_stricmp( ext, "tga" ) != 0 ) return; CUtlMemory< unsigned char > tga; if ( TGALoader::LoadRGBA8888( fileName, tga, m_nWidth, m_nHeight ) ) { - if ( m_iTextureID == -1 ) + if ( m_iTexture == -1 ) { - m_iTextureID = surface()->CreateNewTextureID( true ); + m_iTexture = surface()->CreateNewTextureID( true ); } - surface()->DrawSetTextureRGBA( m_iTextureID, tga.Base(), m_nWidth, m_nHeight, false, false ); + surface()->DrawSetTextureRGBA( m_iTexture, tga.Base(), m_nWidth, m_nHeight, false, false ); } else { @@ -3393,10 +3545,19 @@ END_SCRIPTDESC() //-------------------------------------------------------------- //-------------------------------------------------------------- #if VGUI_TGA_IMAGE_PANEL -BEGIN_VGUI_HELPER_EX( TGAImagePanel, CTGAImagePanel ) +BEGIN_VGUI_HELPER_EX( TGAImage, CTGAImagePanel ) END_VGUI_HELPER() -BEGIN_SCRIPTDESC_VGUI( TGAImagePanel ) +BEGIN_SCRIPTDESC_VGUI( TGAImage ) +END_SCRIPTDESC() +#endif +//-------------------------------------------------------------- +//-------------------------------------------------------------- +#if 0 +BEGIN_VGUI_HELPER_EX( PNGImage, CPNGImagePanel ) +END_VGUI_HELPER() + +BEGIN_SCRIPTDESC_VGUI( PNGImage ) END_SCRIPTDESC() #endif //-------------------------------------------------------------- @@ -3448,7 +3609,7 @@ HSCRIPT CScriptVGUI::CreatePanel( const char* panelClass, HSCRIPT parent, const } #define Check( _name )\ - if ( FStrEq( panelClass, #_name ) )\ + if ( !V_strcmp( panelClass, #_name ) )\ {\ CScript_##_name##_Helper *helper = AllocScriptPanel< CScript_##_name##_Helper >();\ helper->CreateFromScript< CScript_##_name##_Helper >( (HSCRIPT)parent, panelName, root );\ @@ -3467,7 +3628,7 @@ HSCRIPT CScriptVGUI::CreatePanel( const char* panelClass, HSCRIPT parent, const Check( AvatarImage ); #endif #if VGUI_TGA_IMAGE_PANEL - Check( TGAImagePanel ); + Check( TGAImage ); #endif g_pScriptVM->RaiseException("invalid vgui class"); @@ -3480,25 +3641,33 @@ void CScriptVGUI::LevelShutdownPostEntity() { DebugMsg( "LevelShutdownPostEntity()\n" ); - while ( g_ScriptPanels.Count() ) + if ( g_ScriptPanels.Count() ) { - Assert( g_ScriptPanels.Head() != g_ScriptPanels.InvalidIndex() ); + while ( g_ScriptPanels.Count() ) + { + Assert( g_ScriptPanels.Head() != g_ScriptPanels.InvalidIndex() ); - int head = g_ScriptPanels.Head(); - g_ScriptPanels[ head ]->Destroy( head ); + int head = g_ScriptPanels.Head(); + g_ScriptPanels[ head ]->Destroy( head ); + } + + g_ScriptPanels.Purge(); } - g_ScriptPanels.Purge(); - FOR_EACH_VEC( g_ScriptTextureIDs, i ) + if ( int i = g_ScriptTextureIDs.Count() ) { + while ( i-- ) + { #ifdef _DEBUG - char tex[MAX_PATH]; - surface()->DrawGetTextureFile( g_ScriptTextureIDs[i], tex, sizeof(tex)-1 ); - DebugMsg( "Destroy texture [%i]%s\n", g_ScriptTextureIDs[i], tex ); + char tex[MAX_PATH]; + surface()->DrawGetTextureFile( g_ScriptTextureIDs[i], tex, sizeof(tex)-1 ); + DebugMsg( "Destroy texture [%i]%s\n", g_ScriptTextureIDs[i], tex ); #endif - surface()->DestroyTextureID( g_ScriptTextureIDs[i] ); + surface()->DestroyTextureID( g_ScriptTextureIDs[i] ); + } + + g_ScriptTextureIDs.Purge(); } - g_ScriptTextureIDs.Purge(); // // Reset hud element visibility diff --git a/sp/src/game/client/mapbase/vscript_vgui.nut b/sp/src/game/client/mapbase/vscript_vgui.nut index a4452705..c4727eeb 100644 --- a/sp/src/game/client/mapbase/vscript_vgui.nut +++ b/sp/src/game/client/mapbase/vscript_vgui.nut @@ -1,6 +1,6 @@ static const char* g_Script_vgui_init = R"script( local DoCreateFont = ISurface.CreateFont; -ISurface.CreateFont <- function( name, props ) +function ISurface::CreateFont( name, props ) { if ( !("name" in props) || typeof props.name != "string" ) throw "invalid parameter 'name'"; @@ -86,12 +86,12 @@ ISurface.CreateFont <- function( name, props ) return DoCreateFont( name, props.name, props.tall, props.weight, blur, scanlines, flags, yres_min, yres_max, proportional ); } -local _Schemes = {} local _FontTall = {} +local _Schemes = {} local DoGetFont = ISurface.DoGetFont <- ISurface.GetFont; local DoGetFontTall = ISurface.GetFontTall; -ISurface.GetFont <- function( name, proportional, sch = "" ) +function ISurface::GetFont( name, proportional, sch = "" ) { if ( sch in _Schemes ) { @@ -151,33 +151,28 @@ ISurface.GetTextureID <- function( name ) } // Forward compatibility -IVGui.GetRootPanel <- function() { return 1000 } -//IVGui.GetGameUIRootPanel <- function() { return 1001 } -IVGui.GetClientDLLRootPanel <- function() { return 1002 } -//IVGui.GetHudViewportPanel <- function() { return 1010 } +IVGui.GetRootPanel <- function() { return 0x8888 } +//IVGui.GetGameUIRootPanel <- function() { return 0x8888+1 } +IVGui.GetClientDLLRootPanel <- function() { return 0x8888+2 } +IVGui.GetHudViewport <- function() { return 0x8888+10 } local CreatePanel = IVGui.CreatePanel; -IVGui.CreatePanel <- function( type, parent, name ) +function IVGui::CreatePanel( type, parent, name ) { if ( !parent ) throw "invalid parent"; - local root = 0; - + local root = -1; if ( typeof parent == "integer" ) { - switch ( parent ) + root = parent-0x8888; + switch ( root ) { - case 1000: - root = 0; + case 0: + case 2: + case 10: break; - - case 1002: - root = 2; - break; - - default: - throw "invalid parent"; + default: throw "invalid parent"; } parent = null; } @@ -390,5 +385,6 @@ if ( __Documentation.RegisterHelp != dummy ) __Documentation.RegisterHelp( "IVGui::CreatePanel", "handle IVGui::CreatePanel(string, handle, string)", "" ); __Documentation.RegisterHelp( "IVGui::GetRootPanel", "handle IVGui::GetRootPanel()", "" ); __Documentation.RegisterHelp( "IVGui::GetClientDLLRootPanel", "handle IVGui::GetClientDLLRootPanel()", "" ); + __Documentation.RegisterHelp( "IVGui::GetHudViewport", "handle IVGui::GetHudViewport()", "" ); } )script"; From 1aff4bb9b9966e5985845d9c187c22a4b8e8258c Mon Sep 17 00:00:00 2001 From: samisalreadytaken <46823719+samisalreadytaken@users.noreply.github.com> Date: Sat, 26 Nov 2022 20:12:35 +0300 Subject: [PATCH 17/90] Assert on concommand hook crash --- sp/src/game/shared/mapbase/vscript_singletons.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sp/src/game/shared/mapbase/vscript_singletons.cpp b/sp/src/game/shared/mapbase/vscript_singletons.cpp index c5cca45b..64dcc592 100644 --- a/sp/src/game/shared/mapbase/vscript_singletons.cpp +++ b/sp/src/game/shared/mapbase/vscript_singletons.cpp @@ -2742,6 +2742,11 @@ public: void CScriptConvarAccessor::RegisterCommand( const char *name, HSCRIPT fn, const char *helpString, int flags ) { +#if CLIENT_DLL + // FIXME: This crashes in engine when used as a hook (dispatched from CScriptConCommand::CommandCallback()) + Assert( V_stricmp( name, "load" ) != 0 ); +#endif + unsigned int hash = Hash(name); int idx = g_ScriptConCommands.Find(hash); if ( idx == g_ScriptConCommands.InvalidIndex() ) From f8591316fa4ea8cbe0fab294a626b9e0e0e26b31 Mon Sep 17 00:00:00 2001 From: samisalreadytaken <46823719+samisalreadytaken@users.noreply.github.com> Date: Thu, 1 Dec 2022 13:00:32 +0300 Subject: [PATCH 18/90] Clarify comment --- sp/src/game/server/vscript_server.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sp/src/game/server/vscript_server.cpp b/sp/src/game/server/vscript_server.cpp index 40e35f00..b7e514c7 100644 --- a/sp/src/game/server/vscript_server.cpp +++ b/sp/src/game/server/vscript_server.cpp @@ -606,10 +606,10 @@ bool VScriptServerInit() #ifdef MAPBASE_VSCRIPT // MULTIPLAYER + // NOTE: 'PlayerInstanceFromIndex' and 'GetPlayerFromUserID' are used in L4D2 and Source 2, + // but the GetPlayerBy* names are more consistent. // ScriptRegisterFunctionNamed( g_pScriptVM, UTIL_PlayerByIndex, "GetPlayerByIndex", "PlayerInstanceFromIndex" ); - // ScriptRegisterFunctionNamed( g_pScriptVM, UTIL_PlayerByUserId, "GetPlayerByUserId", "GetPlayerFromUserID" ); - // ScriptRegisterFunctionNamed( g_pScriptVM, UTIL_PlayerByName, "GetPlayerByName", "" ); - // ScriptRegisterFunctionNamed( g_pScriptVM, ScriptGetPlayerByNetworkID, "GetPlayerByNetworkID", "" ); + // ScriptRegisterFunctionNamed( g_pScriptVM, UTIL_PlayerByUserId, "GetPlayerByUserID", "GetPlayerFromUserID" ); ScriptRegisterFunctionNamed( g_pScriptVM, UTIL_ShowMessageAll, "ShowMessage", "Print a hud message on all clients" ); #else From 7011b893af476de00a276c7c6c53fc66a80d2dbb Mon Sep 17 00:00:00 2001 From: samisalreadytaken <46823719+samisalreadytaken@users.noreply.github.com> Date: Fri, 2 Dec 2022 22:31:24 +0300 Subject: [PATCH 19/90] Fix `CHudMessage::MessageDrawScan` ignoring custom font size --- sp/src/game/client/message.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/sp/src/game/client/message.cpp b/sp/src/game/client/message.cpp index dc41bb00..e750da40 100644 --- a/sp/src/game/client/message.cpp +++ b/sp/src/game/client/message.cpp @@ -447,6 +447,8 @@ void CHudMessage::MessageScanStart( void ) break; } + // Font was just set in MessageDrawScan() +#ifndef MAPBASE m_parms.font = g_hFontTrebuchet24; if ( m_parms.vguiFontName != NULL && @@ -455,6 +457,7 @@ void CHudMessage::MessageScanStart( void ) SetFont( vgui::scheme()->GetDefaultScheme(), m_parms.vguiFontName ); } +#endif } //----------------------------------------------------------------------------- @@ -497,7 +500,19 @@ void CHudMessage::MessageDrawScan( client_textmessage_t *pMessage, float time ) m_parms.totalWidth = 0; m_parms.vguiFontName = pMessage->pVGuiSchemeFontName; +#ifdef MAPBASE + if ( m_parms.vguiFontName != NULL && + m_parms.vguiFontName[ 0 ] ) + { + SetFont( vgui::scheme()->GetDefaultScheme(), m_parms.vguiFontName ); + } + else + { + m_parms.font = g_hFontTrebuchet24; + } +#else m_parms.font = g_hFontTrebuchet24; +#endif while ( *pText ) { From 9e47b5a2172c0541db57b277a9eea1344e658e37 Mon Sep 17 00:00:00 2001 From: samisalreadytaken <46823719+samisalreadytaken@users.noreply.github.com> Date: Fri, 2 Dec 2022 22:36:12 +0300 Subject: [PATCH 20/90] Add vscript created custom font support on game_text (CHudMessage) --- sp/src/game/client/message.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/sp/src/game/client/message.cpp b/sp/src/game/client/message.cpp index e750da40..176b88ff 100644 --- a/sp/src/game/client/message.cpp +++ b/sp/src/game/client/message.cpp @@ -505,6 +505,17 @@ void CHudMessage::MessageDrawScan( client_textmessage_t *pMessage, float time ) m_parms.vguiFontName[ 0 ] ) { SetFont( vgui::scheme()->GetDefaultScheme(), m_parms.vguiFontName ); + + #ifdef MAPBASE_VSCRIPT + if ( m_parms.font == vgui::INVALID_FONT ) + { + extern vgui::HFont GetScriptFont( const char *, bool ); + + vgui::HFont font = GetScriptFont( m_parms.vguiFontName, IsProportional() ); + textmessage->SetFont( font ); + m_parms.font = font; + } + #endif } else { From 4a77eb2a0db22174be2d0c292c5e6cdcfb213748 Mon Sep 17 00:00:00 2001 From: samisalreadytaken <46823719+samisalreadytaken@users.noreply.github.com> Date: Sun, 4 Dec 2022 13:40:41 +0300 Subject: [PATCH 21/90] Add vscript created font support on vgui_text_display --- sp/src/game/client/mapbase/c_vgui_text_display.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/sp/src/game/client/mapbase/c_vgui_text_display.cpp b/sp/src/game/client/mapbase/c_vgui_text_display.cpp index f0d2032d..de8e8450 100644 --- a/sp/src/game/client/mapbase/c_vgui_text_display.cpp +++ b/sp/src/game/client/mapbase/c_vgui_text_display.cpp @@ -215,6 +215,13 @@ void C_TextDisplayPanel::UpdateText() if (pszFontName && pszFontName[0] != '\0') { HFont font = scheme()->GetIScheme( GetScheme() )->GetFont( pszFontName ); + + if ( !font ) + { + extern HFont GetScriptFont( const char *, bool ); + font = GetScriptFont( pszFontName, false ); + } + m_pDisplayTextLabel->SetFont( font ); } From 53d6ae68483f7f9ace0b0bae2275dbf1c0f384d2 Mon Sep 17 00:00:00 2001 From: samisalreadytaken <46823719+samisalreadytaken@users.noreply.github.com> Date: Tue, 27 Dec 2022 15:59:19 +0300 Subject: [PATCH 22/90] Explicitly state header bit count in CNetMsgScriptHelper --- sp/src/game/shared/mapbase/vscript_singletons.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sp/src/game/shared/mapbase/vscript_singletons.cpp b/sp/src/game/shared/mapbase/vscript_singletons.cpp index 64dcc592..e313f82f 100644 --- a/sp/src/game/shared/mapbase/vscript_singletons.cpp +++ b/sp/src/game/shared/mapbase/vscript_singletons.cpp @@ -1445,7 +1445,7 @@ void CNetMsgScriptHelper::ReceiveMessage( bf_read &msg ) while ( count-- ) #endif { - int hash = m_MsgIn_()ReadWord(); + int hash = m_MsgIn_()ReadUBitLong( SCRIPT_NETMSG_HEADER_BITS ); #ifdef _DEBUG const char *msgName = GetNetMsgName( hash ); @@ -1514,7 +1514,7 @@ void CNetMsgScriptHelper::Start( const char *msg ) Reset(); #endif - m_MsgOut.WriteWord( Hash( msg ) ); + m_MsgOut.WriteUBitLong( Hash( msg ), SCRIPT_NETMSG_HEADER_BITS ); } #ifdef GAME_DLL From f5837d068f9ac6e49e9ef679f4b5f00f00bc91fc Mon Sep 17 00:00:00 2001 From: samisalreadytaken <46823719+samisalreadytaken@users.noreply.github.com> Date: Tue, 27 Dec 2022 16:00:08 +0300 Subject: [PATCH 23/90] Change vscript net message header size to 4 bytes from 2 bytes to reduce hash collisions --- sp/src/game/shared/mapbase/vscript_singletons.cpp | 5 ++--- sp/src/game/shared/mapbase/vscript_singletons.h | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/sp/src/game/shared/mapbase/vscript_singletons.cpp b/sp/src/game/shared/mapbase/vscript_singletons.cpp index e313f82f..eeb50d84 100644 --- a/sp/src/game/shared/mapbase/vscript_singletons.cpp +++ b/sp/src/game/shared/mapbase/vscript_singletons.cpp @@ -1338,8 +1338,7 @@ static const char *HasNetMsgCollision( int hash, const char *ignore ) inline int CNetMsgScriptHelper::Hash( const char *key ) { - int hash = HashStringCaseless( key ); - Assert( hash < (1 << SCRIPT_NETMSG_HEADER_BITS) ); + int hash = CaselessStringHashFunctor()( key ); return hash; } @@ -1923,7 +1922,7 @@ BEGIN_SCRIPTDESC_ROOT_NAMED( CNetMsgScriptHelper, "CNetMsg", SCRIPT_SINGLETON "N DEFINE_SCRIPTFUNC( Receive, "Set custom network message callback" ) DEFINE_SCRIPTFUNC_NAMED( Receive, "Recieve", SCRIPT_HIDE ) // This was a typo until v6.3 #ifdef GAME_DLL - DEFINE_SCRIPTFUNC( Send, "Send a custom network message from the server to the client (max 252 bytes)" ) + DEFINE_SCRIPTFUNC( Send, "Send a custom network message from the server to the client (max 251 bytes)" ) #else DEFINE_SCRIPTFUNC( Send, "Send a custom network message from the client to the server (max 2044 bytes)" ) #endif diff --git a/sp/src/game/shared/mapbase/vscript_singletons.h b/sp/src/game/shared/mapbase/vscript_singletons.h index e514f4d9..b20cc67c 100644 --- a/sp/src/game/shared/mapbase/vscript_singletons.h +++ b/sp/src/game/shared/mapbase/vscript_singletons.h @@ -24,7 +24,7 @@ void RegisterScriptSingletons(); #endif #define SCRIPT_NETMSG_QUEUE_BITS 3 // determines the number of custom messages client can write to a usercmd -#define SCRIPT_NETMSG_HEADER_BITS (sizeof(word) << 3) +#define SCRIPT_NETMSG_HEADER_BITS (sizeof(unsigned int) << 3) #define SCRIPT_NETMSG_STRING_SIZE 512 From 2d183a8e2a47785331a8d7c6ab8d1009f2a2ae30 Mon Sep 17 00:00:00 2001 From: samisalreadytaken <46823719+samisalreadytaken@users.noreply.github.com> Date: Thu, 5 Jan 2023 20:00:15 +0300 Subject: [PATCH 24/90] Add recursive KV read/write on CScriptKeyValues --- sp/src/vscript/vscript_bindings_base.cpp | 46 +++++++++++++++++++----- 1 file changed, 37 insertions(+), 9 deletions(-) diff --git a/sp/src/vscript/vscript_bindings_base.cpp b/sp/src/vscript/vscript_bindings_base.cpp index 4f20234a..99b6194a 100644 --- a/sp/src/vscript/vscript_bindings_base.cpp +++ b/sp/src/vscript/vscript_bindings_base.cpp @@ -206,19 +206,28 @@ void CScriptKeyValues::ScriptReleaseKeyValues( ) m_pKeyValues = NULL; } -void CScriptKeyValues::TableToSubKeys( HSCRIPT hTable ) +void KeyValues_TableToSubKeys( HSCRIPT hTable, KeyValues *pKV ) { int nIterator = -1; ScriptVariant_t varKey, varValue; while ((nIterator = g_pScriptVM->GetKeyValue( hTable, nIterator, &varKey, &varValue )) != -1) { - switch (varValue.m_type) + if ( varKey.m_type == FIELD_CSTRING ) { - case FIELD_CSTRING: m_pKeyValues->SetString( varKey.m_pszString, varValue.m_pszString ); break; - case FIELD_INTEGER: m_pKeyValues->SetInt( varKey.m_pszString, varValue.m_int ); break; - case FIELD_FLOAT: m_pKeyValues->SetFloat( varKey.m_pszString, varValue.m_float ); break; - case FIELD_BOOLEAN: m_pKeyValues->SetBool( varKey.m_pszString, varValue.m_bool ); break; - case FIELD_VECTOR: m_pKeyValues->SetString( varKey.m_pszString, CFmtStr( "%f %f %f", varValue.m_pVector->x, varValue.m_pVector->y, varValue.m_pVector->z ) ); break; + switch ( varValue.m_type ) + { + case FIELD_CSTRING: pKV->SetString( varKey.m_pszString, varValue.m_pszString ); break; + case FIELD_INTEGER: pKV->SetInt( varKey.m_pszString, varValue.m_int ); break; + case FIELD_FLOAT: pKV->SetFloat( varKey.m_pszString, varValue.m_float ); break; + case FIELD_BOOLEAN: pKV->SetBool( varKey.m_pszString, varValue.m_bool ); break; + case FIELD_VECTOR: pKV->SetString( varKey.m_pszString, CFmtStr( "%f %f %f", varValue.m_pVector->x, varValue.m_pVector->y, varValue.m_pVector->z ) ); break; + case FIELD_HSCRIPT: + { + KeyValues *subKey = pKV->FindKey( varKey.m_pszString, true ); + KeyValues_TableToSubKeys( varValue, subKey ); + break; + } + } } g_pScriptVM->ReleaseValue( varKey ); @@ -226,19 +235,38 @@ void CScriptKeyValues::TableToSubKeys( HSCRIPT hTable ) } } -void CScriptKeyValues::SubKeysToTable( HSCRIPT hTable ) +void KeyValues_SubKeysToTable( KeyValues *pKV, HSCRIPT hTable ) { - FOR_EACH_SUBKEY( m_pKeyValues, key ) + FOR_EACH_SUBKEY( pKV, key ) { switch ( key->GetDataType() ) { case KeyValues::TYPE_STRING: g_pScriptVM->SetValue( hTable, key->GetName(), key->GetString() ); break; case KeyValues::TYPE_INT: g_pScriptVM->SetValue( hTable, key->GetName(), key->GetInt() ); break; case KeyValues::TYPE_FLOAT: g_pScriptVM->SetValue( hTable, key->GetName(), key->GetFloat() ); break; + case KeyValues::TYPE_NONE: + { + ScriptVariant_t subTable; + g_pScriptVM->CreateTable( subTable ); + g_pScriptVM->SetValue( hTable, key->GetName(), subTable ); + KeyValues_SubKeysToTable( key, subTable ); + g_pScriptVM->ReleaseValue( subTable ); + break; + } } } } +void CScriptKeyValues::TableToSubKeys( HSCRIPT hTable ) +{ + KeyValues_TableToSubKeys( hTable, m_pKeyValues ); +} + +void CScriptKeyValues::SubKeysToTable( HSCRIPT hTable ) +{ + KeyValues_SubKeysToTable( m_pKeyValues, hTable ); +} + HSCRIPT CScriptKeyValues::ScriptFindOrCreateKey( const char *pszName ) { KeyValues *pKeyValues = m_pKeyValues->FindKey(pszName, true); From 3f2f562a1eaf8f7feb190305e950956d5d1fa36c Mon Sep 17 00:00:00 2001 From: samisalreadytaken <46823719+samisalreadytaken@users.noreply.github.com> Date: Sun, 22 Jan 2023 17:30:46 +0300 Subject: [PATCH 25/90] Fix image rotation in vgui::ImagePanel --- sp/src/public/vgui_controls/ImagePanel.h | 4 ++++ sp/src/vgui2/vgui_controls/ImagePanel.cpp | 19 +++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/sp/src/public/vgui_controls/ImagePanel.h b/sp/src/public/vgui_controls/ImagePanel.h index bba8f88b..f4f5ce85 100644 --- a/sp/src/public/vgui_controls/ImagePanel.h +++ b/sp/src/public/vgui_controls/ImagePanel.h @@ -61,7 +61,11 @@ public: int GetNumFrames(); void SetFrame( int nFrame ); +#ifdef MAPBASE + void SetRotation( int iRotation ); +#else void SetRotation( int iRotation ) { m_iRotation = iRotation; } +#endif protected: virtual void PaintBackground(); diff --git a/sp/src/vgui2/vgui_controls/ImagePanel.cpp b/sp/src/vgui2/vgui_controls/ImagePanel.cpp index 1a77bd2a..e045666e 100644 --- a/sp/src/vgui2/vgui_controls/ImagePanel.cpp +++ b/sp/src/vgui2/vgui_controls/ImagePanel.cpp @@ -73,6 +73,13 @@ void ImagePanel::OnSizeChanged(int newWide, int newTall) //----------------------------------------------------------------------------- void ImagePanel::SetImage(IImage *image) { +#ifdef MAPBASE + if ( image ) + { + image->SetRotation( m_iRotation ); + } +#endif + m_pImage = image; Repaint(); } @@ -471,3 +478,15 @@ void ImagePanel::SetFrame( int nFrame ) return m_pImage->SetFrame( nFrame ); } + +#ifdef MAPBASE +void ImagePanel::SetRotation( int iRotation ) +{ + m_iRotation = iRotation; + + if ( m_pImage ) + { + m_pImage->SetRotation( m_iRotation ); + } +} +#endif From 63a9dce4d4dc08578edeb86ec407485da30398d1 Mon Sep 17 00:00:00 2001 From: samisalreadytaken <46823719+samisalreadytaken@users.noreply.github.com> Date: Tue, 24 Jan 2023 21:42:45 +0300 Subject: [PATCH 26/90] Disable vscript entity listeners on level shutdown --- sp/src/game/client/vscript_client.cpp | 2 ++ sp/src/game/server/vscript_server.cpp | 2 ++ 2 files changed, 4 insertions(+) diff --git a/sp/src/game/client/vscript_client.cpp b/sp/src/game/client/vscript_client.cpp index 6c8c5371..de10156a 100644 --- a/sp/src/game/client/vscript_client.cpp +++ b/sp/src/game/client/vscript_client.cpp @@ -761,6 +761,8 @@ public: virtual void LevelShutdownPostEntity( void ) { #ifdef MAPBASE_VSCRIPT + g_ScriptEntityIterator.DisableEntityListening(); + g_ScriptNetMsg->LevelShutdownPreVM(); GetScriptHookManager().OnShutdown(); diff --git a/sp/src/game/server/vscript_server.cpp b/sp/src/game/server/vscript_server.cpp index b7e514c7..aa14a1b4 100644 --- a/sp/src/game/server/vscript_server.cpp +++ b/sp/src/game/server/vscript_server.cpp @@ -841,6 +841,8 @@ public: virtual void LevelShutdownPostEntity( void ) { #ifdef MAPBASE_VSCRIPT + g_ScriptEntityIterator.DisableEntityListening(); + g_ScriptNetMsg->LevelShutdownPreVM(); GetScriptHookManager().OnShutdown(); From 172417867b8f475dab0b31de0c221c115156d1dc Mon Sep 17 00:00:00 2001 From: Arbab Ahmed Date: Sun, 19 Feb 2023 20:01:32 +1100 Subject: [PATCH 27/90] add mins and maxs to point_bugbait --- sp/src/game/server/hl2/grenade_bugbait.cpp | 49 ++++++++++++++++++---- sp/src/game/server/hl2/grenade_bugbait.h | 28 +++++++++++++ 2 files changed, 70 insertions(+), 7 deletions(-) diff --git a/sp/src/game/server/hl2/grenade_bugbait.cpp b/sp/src/game/server/hl2/grenade_bugbait.cpp index 7d5d7a47..5480a980 100644 --- a/sp/src/game/server/hl2/grenade_bugbait.cpp +++ b/sp/src/game/server/hl2/grenade_bugbait.cpp @@ -50,10 +50,21 @@ BEGIN_DATADESC( CBugBaitSensor ) DEFINE_KEYFIELD( m_bEnabled, FIELD_BOOLEAN, "Enabled" ), DEFINE_KEYFIELD( m_flRadius, FIELD_FLOAT, "radius" ), +#ifdef MAPBASE + DEFINE_KEYFIELD( m_bUseRadius, FIELD_BOOLEAN, "useradius" ), + DEFINE_KEYFIELD( m_vecMins, FIELD_VECTOR, "bmins" ), + DEFINE_KEYFIELD( m_vecMaxs, FIELD_VECTOR, "bmaxs" ), +#endif + DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ), DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ), DEFINE_INPUTFUNC( FIELD_VOID, "Toggle", InputToggle ), +#ifdef MAPBASE + DEFINE_INPUTFUNC( FIELD_VOID, "EnableRadius", InputEnableRadius ), + DEFINE_INPUTFUNC( FIELD_VOID, "DisableRadius", InputDisableRadius ), +#endif + // Function Pointers DEFINE_OUTPUT( m_OnBaited, "OnBaited" ), @@ -267,18 +278,42 @@ bool CGrenadeBugBait::ActivateBugbaitTargets( CBaseEntity *pOwner, Vector vecOri continue; //Make sure we're within range of the sensor - if ( pSensor->GetRadius() > ( pSensor->GetAbsOrigin() - vecOrigin ).Length() ) - { - //Tell the sensor it's been hit - if ( pSensor->Baited( pOwner ) ) +#ifdef MAPBASE + if ( pSensor->UsesRadius() ){ +#endif + if ( pSensor->GetRadius() > (pSensor->GetAbsOrigin() - vecOrigin).Length() ) { - //If we're suppressing the call to antlions, then don't make a bugbait sound - if ( pSensor->SuppressCall() ) + //Tell the sensor it's been hit + if ( pSensor->Baited( pOwner ) ) { - suppressCall = true; + //If we're suppressing the call to antlions, then don't make a bugbait sound + if ( pSensor->SuppressCall() ) + { + suppressCall = true; + } + } + } +#ifdef MAPBASE + } + else{ + Vector vMins = pSensor->GetAbsMins(); + Vector vMaxs = pSensor->GetAbsMaxs(); + bool inBox = ((vecOrigin.x >= vMins.x && vecOrigin.x <= vMaxs.x) && + (vecOrigin.y >= vMins.y && vecOrigin.y <= vMaxs.y) && + (vecOrigin.z >= vMins.z && vecOrigin.z <= vMaxs.z)); + if ( inBox ){ + //Tell the sensor it's been hit + if ( pSensor->Baited( pOwner ) ) + { + //If we're suppressing the call to antlions, then don't make a bugbait sound + if ( pSensor->SuppressCall() ) + { + suppressCall = true; + } } } } +#endif } return suppressCall; diff --git a/sp/src/game/server/hl2/grenade_bugbait.h b/sp/src/game/server/hl2/grenade_bugbait.h index 7a5df993..af89657a 100644 --- a/sp/src/game/server/hl2/grenade_bugbait.h +++ b/sp/src/game/server/hl2/grenade_bugbait.h @@ -63,6 +63,16 @@ public: m_bEnabled = !m_bEnabled; } +#ifdef MAPBASE + void InputEnableRadius( inputdata_t &data ){ + m_bUseRadius = true; + } + + void InputDisableRadius( inputdata_t &data ){ + m_bUseRadius = false; + } +#endif + bool SuppressCall( void ) { return ( HasSpawnFlags( SF_BUGBAIT_SUPPRESS_CALL ) ); @@ -91,10 +101,28 @@ public: return !m_bEnabled; } +#ifdef MAPBASE + bool UsesRadius( void ) const { + return m_bUseRadius; + } + + Vector GetAbsMins( void ) const { + return GetAbsOrigin() + m_vecMins; + } + Vector GetAbsMaxs( void ) const { + return GetAbsOrigin() + m_vecMaxs; + } +#endif + protected: float m_flRadius; bool m_bEnabled; +#ifdef MAPBASE + bool m_bUseRadius; + Vector m_vecMins; + Vector m_vecMaxs; +#endif COutputEvent m_OnBaited; public: From 54cd6f7eb3982143eb301c41d723fdf40292a692 Mon Sep 17 00:00:00 2001 From: Peter Covington Date: Thu, 4 Aug 2022 23:31:21 -0400 Subject: [PATCH 28/90] Fixed captions not loading from vpks --- sp/src/game/client/hud_closecaption.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/sp/src/game/client/hud_closecaption.cpp b/sp/src/game/client/hud_closecaption.cpp index e2d718c9..eb4a5ee4 100644 --- a/sp/src/game/client/hud_closecaption.cpp +++ b/sp/src/game/client/hud_closecaption.cpp @@ -2626,8 +2626,13 @@ void CHudCloseCaption::InitCaptionDictionary( const char *dbfile ) } char fullpath[MAX_PATH]; - Q_snprintf( fullpath, sizeof( fullpath ), "%s%s", path, dbfile ); - Q_FixSlashes( fullpath ); +#ifndef MAPBASE + Q_snprintf(fullpath, sizeof(fullpath), "%s%s", path, dbfile); + Q_FixSlashes(fullpath); +#else + V_ComposeFileName(path, dbfile, fullpath, sizeof(fullpath)); +#endif // !MAPBASE + if ( IsX360() ) { @@ -2692,8 +2697,7 @@ void CHudCloseCaption::AddAdditionalCaptionDictionary( const char *dbfile, CUtlV } char fullpath[MAX_PATH]; - Q_snprintf( fullpath, sizeof( fullpath ), "%s%s", path, dbfile ); - Q_FixSlashes( fullpath ); + V_ComposeFileName(path, dbfile, fullpath, sizeof(fullpath)); if ( IsX360() ) { From e672c3521cf022eab142d4090b01c2af9d21195c Mon Sep 17 00:00:00 2001 From: Peter Covington Date: Thu, 29 Sep 2022 23:16:45 -0400 Subject: [PATCH 29/90] Fixed missing some closecaptions --- sp/src/game/client/hud_closecaption.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sp/src/game/client/hud_closecaption.cpp b/sp/src/game/client/hud_closecaption.cpp index eb4a5ee4..82b71236 100644 --- a/sp/src/game/client/hud_closecaption.cpp +++ b/sp/src/game/client/hud_closecaption.cpp @@ -2614,8 +2614,14 @@ void CHudCloseCaption::InitCaptionDictionary( const char *dbfile ) g_AsyncCaptionResourceManager.Clear(); +#ifdef MAPBASE + int iBufferSize = filesystem->GetSearchPath("GAME", true, nullptr, 0); + char* searchPaths = (char*)stackalloc(iBufferSize); + filesystem->GetSearchPath("GAME", true, searchPaths, iBufferSize); +#else char searchPaths[4096]; filesystem->GetSearchPath( "GAME", true, searchPaths, sizeof( searchPaths ) ); +#endif for ( char *path = strtok( searchPaths, ";" ); path; path = strtok( NULL, ";" ) ) { From b5deefa0880444f32bbb4b4026d7c5dea2a6d139 Mon Sep 17 00:00:00 2001 From: Peter Covington Date: Sun, 19 Feb 2023 19:26:55 -0500 Subject: [PATCH 30/90] Fixed delayed closed captions --- sp/src/game/client/hud_closecaption.cpp | 74 ++++++++++++++++++++++++- 1 file changed, 71 insertions(+), 3 deletions(-) diff --git a/sp/src/game/client/hud_closecaption.cpp b/sp/src/game/client/hud_closecaption.cpp index 82b71236..5fdfdfa1 100644 --- a/sp/src/game/client/hud_closecaption.cpp +++ b/sp/src/game/client/hud_closecaption.cpp @@ -1496,9 +1496,23 @@ void CHudCloseCaption::Process( const wchar_t *stream, float duration, const cha if ( m_Items.Count() > 0 ) { +#ifndef MAPBASE // Get the remaining life span of the last item - CCloseCaptionItem *final = m_Items[ m_Items.Count() - 1 ]; + CCloseCaptionItem* final = m_Items[m_Items.Count() - 1]; float prevlife = final->GetTimeToLive(); +#else + float prevlife = 0.f; + // Get the remaining life span of the last displayed item + for (int i = m_Items.Count() - 1; i >= 0; i--) + { + if (m_Items[i]->GetPreDisplayTime() > cc_predisplay_time.GetFloat()) + continue; + + prevlife = m_Items[i]->GetTimeToLive(); + break; + } +#endif // !MAPBASE + if ( prevlife > lifespan ) { @@ -1532,7 +1546,31 @@ void CHudCloseCaption::Process( const wchar_t *stream, float duration, const cha if ( wcslen( phrase ) > 0 ) { CCloseCaptionItem *item = new CCloseCaptionItem( phrase, lifespan, addedlife, delay, valid, fromplayer ); - m_Items.AddToTail( item ); +#ifdef MAPBASE + if (m_Items.Count()) + { + // Add it where it will appear + for (int i = m_Items.Count() - 1; i >= 0; i--) + { + if (m_Items[i]->GetPreDisplayTime() > delay + cc_predisplay_time.GetFloat()) + { + if (i == 0) + { + m_Items.AddToHead(item); + break; + } + else + continue; + } + + m_Items.InsertAfter(i, item); + break; + } + } + else +#endif // MAPBASE + m_Items.AddToTail(item); + if ( StreamHasCommand( phrase, L"sfx" ) ) { // SFX show up instantly. @@ -1541,6 +1579,9 @@ void CHudCloseCaption::Process( const wchar_t *stream, float duration, const cha if ( GetFloatCommandValue( phrase, L"len", override_duration ) ) { +#ifdef MAPBASE + override_duration += cc_linger_time.GetFloat(); +#endif // MAPBASE item->SetTimeToLive( override_duration ); } } @@ -1569,7 +1610,30 @@ void CHudCloseCaption::Process( const wchar_t *stream, float duration, const cha if ( wcslen( phrase ) > 0 ) { CCloseCaptionItem *item = new CCloseCaptionItem( phrase, lifespan, addedlife, delay, valid, fromplayer ); - m_Items.AddToTail( item ); +#ifdef MAPBASE + if (m_Items.Count()) + { + // Add it where it will appear + for (int i = m_Items.Count() - 1; i >= 0; i--) + { + if (m_Items[i]->GetPreDisplayTime() > delay + cc_predisplay_time.GetFloat()) + { + if (i == 0) + { + m_Items.AddToHead(item); + break; + } + else + continue; + } + + m_Items.InsertAfter(i, item); + break; + } + } + else +#endif // MAPBASE + m_Items.AddToTail(item); if ( StreamHasCommand( phrase, L"sfx" ) ) { @@ -1579,6 +1643,10 @@ void CHudCloseCaption::Process( const wchar_t *stream, float duration, const cha if ( GetFloatCommandValue( phrase, L"len", override_duration ) ) { +#ifdef MAPBASE + override_duration += cc_linger_time.GetFloat(); +#endif // MAPBASE + item->SetTimeToLive( override_duration ); item->SetInitialLifeSpan( override_duration ); } From 1ff5d61d4f3c0886678ed556fcf32a2e007410e0 Mon Sep 17 00:00:00 2001 From: Arbab Ahmed Date: Mon, 20 Feb 2023 23:21:09 +1100 Subject: [PATCH 31/90] Add new inputs for the new functionality --- sp/src/game/server/hl2/grenade_bugbait.cpp | 3 +++ sp/src/game/server/hl2/grenade_bugbait.h | 12 ++++++++++++ 2 files changed, 15 insertions(+) diff --git a/sp/src/game/server/hl2/grenade_bugbait.cpp b/sp/src/game/server/hl2/grenade_bugbait.cpp index 5480a980..0f054d57 100644 --- a/sp/src/game/server/hl2/grenade_bugbait.cpp +++ b/sp/src/game/server/hl2/grenade_bugbait.cpp @@ -63,6 +63,9 @@ BEGIN_DATADESC( CBugBaitSensor ) #ifdef MAPBASE DEFINE_INPUTFUNC( FIELD_VOID, "EnableRadius", InputEnableRadius ), DEFINE_INPUTFUNC( FIELD_VOID, "DisableRadius", InputDisableRadius ), + DEFINE_INPUTFUNC( FIELD_INTEGER, "SetRadius", InputSetRadius ), + DEFINE_INPUTFUNC( FIELD_VECTOR, "SetMins", InputSetMins ), + DEFINE_INPUTFUNC( FIELD_VECTOR, "SetMaxs", InputSetMaxs ), #endif // Function Pointers diff --git a/sp/src/game/server/hl2/grenade_bugbait.h b/sp/src/game/server/hl2/grenade_bugbait.h index af89657a..fb2a68cc 100644 --- a/sp/src/game/server/hl2/grenade_bugbait.h +++ b/sp/src/game/server/hl2/grenade_bugbait.h @@ -71,6 +71,18 @@ public: void InputDisableRadius( inputdata_t &data ){ m_bUseRadius = false; } + + void InputSetRadius( inputdata_t &data ){ + m_flRadius = data.value.Int(); + } + + void InputSetMins( inputdata_t &data ){ + data.value.Vector3D( m_vecMins ); + } + + void InputSetMaxs( inputdata_t &data ){ + data.value.Vector3D( m_vecMaxs ); + } #endif bool SuppressCall( void ) From ebe9a8eb3daa10cda2e0ed777482994b8eb4b5e4 Mon Sep 17 00:00:00 2001 From: Peter Covington Date: Fri, 24 Feb 2023 18:33:49 -0500 Subject: [PATCH 32/90] Fixed HL1 style lipsync --- sp/src/game/server/baseentity.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sp/src/game/server/baseentity.h b/sp/src/game/server/baseentity.h index 1f2f75e1..1453d859 100644 --- a/sp/src/game/server/baseentity.h +++ b/sp/src/game/server/baseentity.h @@ -1575,7 +1575,7 @@ public: float flVolume, soundlevel_t iSoundlevel, int iFlags = 0, int iPitch = PITCH_NORM, const Vector *pOrigin = NULL, const Vector *pDirection = NULL, bool bUpdatePositions = true, float soundtime = 0.0f #ifdef MAPBASE - , int iSpecialDSP = 0, int iSpeakerIndex = 0 // Needed for env_microphone + , int iSpecialDSP = 0, int iSpeakerIndex = -1 // Needed for env_microphone #endif ); From f4c2cb47956581d340dd2b5cb72623a620e921ec Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Thu, 30 Mar 2023 19:10:44 -0500 Subject: [PATCH 33/90] Fix vgui_controls not working outside of client.dll --- sp/src/game/shared/mapbase/mapbase_shared.cpp | 2 +- sp/src/vgui2/vgui_controls/AnimationController.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sp/src/game/shared/mapbase/mapbase_shared.cpp b/sp/src/game/shared/mapbase/mapbase_shared.cpp index 79c4f51e..8c364b62 100644 --- a/sp/src/game/shared/mapbase/mapbase_shared.cpp +++ b/sp/src/game/shared/mapbase/mapbase_shared.cpp @@ -70,7 +70,7 @@ ConVar mapbase_version_client( "mapbase_version_client", MAPBASE_VERSION, FCVAR_ // This is from the vgui_controls library extern vgui::HScheme g_iCustomClientSchemeOverride; -bool g_bUsingCustomHudAnimations = false; +extern bool g_bUsingCustomHudAnimations; bool g_bUsingCustomHudLayout = false; #endif diff --git a/sp/src/vgui2/vgui_controls/AnimationController.cpp b/sp/src/vgui2/vgui_controls/AnimationController.cpp index bc2b1774..ba51d93c 100644 --- a/sp/src/vgui2/vgui_controls/AnimationController.cpp +++ b/sp/src/vgui2/vgui_controls/AnimationController.cpp @@ -33,7 +33,7 @@ static CUtlSymbolTable g_ScriptSymbols(0, 128, true); #ifdef MAPBASE // Allows animation sequences to be overridden by map-specific files -extern bool g_bUsingCustomHudAnimations; +bool g_bUsingCustomHudAnimations = false; #endif // singleton accessor for animation controller for use by the vgui controls From f8d3bea87ed1ff7cfed34091632cc4aa6f38f41c Mon Sep 17 00:00:00 2001 From: Crimson-X1 Date: Mon, 3 Apr 2023 14:24:20 -0700 Subject: [PATCH 34/90] Initial commit --- sp/src/game/server/hl2/weapon_physcannon.cpp | 47 ++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/sp/src/game/server/hl2/weapon_physcannon.cpp b/sp/src/game/server/hl2/weapon_physcannon.cpp index 2e985460..c40ecb14 100644 --- a/sp/src/game/server/hl2/weapon_physcannon.cpp +++ b/sp/src/game/server/hl2/weapon_physcannon.cpp @@ -68,6 +68,10 @@ ConVar player_throwforce( "player_throwforce", "1000" ); ConVar physcannon_dmg_glass( "physcannon_dmg_glass", "15" ); ConVar physcannon_right_turrets( "physcannon_right_turrets", "0" ); +#ifdef MAPBASE +ConVar sv_player_enable_propsprint("sv_player_enable_propsprint", "0", FCVAR_REPLICATED, "If enabled, allows the player to sprint while holding a physics object" ); +ConVar sv_player_enable_gravgun_sprint("sv_player_enable_gravgun_sprint", "0", FCVAR_REPLICATED, "Enables the player to sprint while holding a phys. object with the gravity gun" ); +#endif extern ConVar hl2_normspeed; extern ConVar hl2_walkspeed; @@ -1042,11 +1046,22 @@ void CPlayerPickupController::Init( CBasePlayer *pPlayer, CBaseEntity *pObject ) } CHL2_Player *pOwner = (CHL2_Player *)ToBasePlayer( pPlayer ); +#ifndef MAPBASE if ( pOwner ) { pOwner->EnableSprint( false ); } +#else + if ( pOwner && sv_player_enable_propsprint.GetBool() == false ) + { + pOwner->EnableSprint( false ); + } + else + { + pOwner->EnableSprint( true ); + } +#endif // If the target is debris, convert it to non-debris if ( pObject->GetCollisionGroup() == COLLISION_GROUP_DEBRIS ) { @@ -1102,10 +1117,17 @@ void CPlayerPickupController::Shutdown( bool bThrown ) if ( m_pPlayer ) { CHL2_Player *pOwner = (CHL2_Player *)ToBasePlayer( m_pPlayer ); +#ifndef MAPBASE if ( pOwner ) { pOwner->EnableSprint( true ); } +#else + if ( pOwner && sv_player_enable_propsprint.GetBool() == false ) + { + pOwner->EnableSprint( true ); + } +#endif m_pPlayer->SetUseEntity( NULL ); if ( m_pPlayer->GetActiveWeapon() ) @@ -2497,6 +2519,7 @@ bool CWeaponPhysCannon::AttachObject( CBaseEntity *pObject, const Vector &vPosit // NVNT set the players constant force to simulate holding mass HapticSetConstantForce(pOwner,clamp(m_grabController.GetLoadWeight()*0.05,1,5)*Vector(0,-1,0)); #endif +#ifndef MAPBASE pOwner->EnableSprint( false ); float loadWeight = ( 1.0f - GetLoadPercentage() ); @@ -2504,6 +2527,22 @@ bool CWeaponPhysCannon::AttachObject( CBaseEntity *pObject, const Vector &vPosit //Msg( "Load perc: %f -- Movement speed: %f/%f\n", loadWeight, maxSpeed, hl2_normspeed.GetFloat() ); pOwner->SetMaxSpeed( maxSpeed ); +#else + if ( sv_player_enable_gravgun_sprint.GetBool() == false ) + { + 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 ); + } + else + { + pOwner->EnableSprint( true ); + } +#endif } // Don't drop again for a slight delay, in case they were pulling objects near them @@ -2950,9 +2989,17 @@ void CWeaponPhysCannon::DetachObject( bool playSound, bool wasLaunched ) CHL2_Player *pOwner = (CHL2_Player *)ToBasePlayer( GetOwner() ); if( pOwner != NULL ) { +#ifndef MAPBASE pOwner->EnableSprint( true ); pOwner->SetMaxSpeed( hl2_normspeed.GetFloat() ); +#else + if (sv_player_enable_gravgun_sprint.GetBool() == false) + { + pOwner->EnableSprint( true ); + pOwner->SetMaxSpeed( hl2_normspeed.GetFloat() ); + } +#endif if( wasLaunched ) { pOwner->RumbleEffect( RUMBLE_357, 0, RUMBLE_FLAG_RESTART ); From 1876d1abf423924fa4109bb9abba34032665330c Mon Sep 17 00:00:00 2001 From: Agent Agrimar Date: Mon, 15 May 2023 00:39:26 -0400 Subject: [PATCH 35/90] Properly assign held physcannon entity to pass into commander goal trace --- sp/src/game/server/hl2/hl2_player.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sp/src/game/server/hl2/hl2_player.cpp b/sp/src/game/server/hl2/hl2_player.cpp index 1cd610bb..cefdeeb4 100644 --- a/sp/src/game/server/hl2/hl2_player.cpp +++ b/sp/src/game/server/hl2/hl2_player.cpp @@ -1814,7 +1814,7 @@ bool CHL2_Player::CommanderFindGoal( commandgoal_t *pGoal ) // Get either our +USE entity or the gravity gun entity CBaseEntity *pHeldEntity = GetPlayerHeldEntity(this); if ( !pHeldEntity ) - PhysCannonGetHeldEntity( GetActiveWeapon() ); + pHeldEntity = PhysCannonGetHeldEntity( GetActiveWeapon() ); CTraceFilterSkipTwoEntities filter( this, pHeldEntity, COLLISION_GROUP_INTERACTIVE_DEBRIS ); #else From cbb119b0ad6ccaddb0fef21c9d76fc639ceea6ac Mon Sep 17 00:00:00 2001 From: Mr0maks Date: Fri, 25 Aug 2023 22:50:18 +0500 Subject: [PATCH 36/90] Fix ViewPunch random for AR2 --- mp/src/game/shared/hl2mp/weapon_ar2.cpp | 2 +- sp/src/game/shared/hl2mp/weapon_ar2.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mp/src/game/shared/hl2mp/weapon_ar2.cpp b/mp/src/game/shared/hl2mp/weapon_ar2.cpp index 5086db1b..faa5b14c 100644 --- a/mp/src/game/shared/hl2mp/weapon_ar2.cpp +++ b/mp/src/game/shared/hl2mp/weapon_ar2.cpp @@ -208,7 +208,7 @@ void CWeaponAR2::DelayedAttack( void ) // pOwner->SnapEyeAngles( angles ); - pOwner->ViewPunch( QAngle( SharedRandomInt( "ar2pax", -8, -12 ), SharedRandomInt( "ar2pay", 1, 2 ), 0 ) ); + pOwner->ViewPunch( QAngle( SharedRandomInt( "ar2pax", -12, -8 ), SharedRandomInt( "ar2pay", 1, 2 ), 0 ) ); // Decrease ammo pOwner->RemoveAmmo( 1, m_iSecondaryAmmoType ); diff --git a/sp/src/game/shared/hl2mp/weapon_ar2.cpp b/sp/src/game/shared/hl2mp/weapon_ar2.cpp index 5086db1b..faa5b14c 100644 --- a/sp/src/game/shared/hl2mp/weapon_ar2.cpp +++ b/sp/src/game/shared/hl2mp/weapon_ar2.cpp @@ -208,7 +208,7 @@ void CWeaponAR2::DelayedAttack( void ) // pOwner->SnapEyeAngles( angles ); - pOwner->ViewPunch( QAngle( SharedRandomInt( "ar2pax", -8, -12 ), SharedRandomInt( "ar2pay", 1, 2 ), 0 ) ); + pOwner->ViewPunch( QAngle( SharedRandomInt( "ar2pax", -12, -8 ), SharedRandomInt( "ar2pay", 1, 2 ), 0 ) ); // Decrease ammo pOwner->RemoveAmmo( 1, m_iSecondaryAmmoType ); From 2fe3adfe6e87d0d5235138e5b47536fbcccccb56 Mon Sep 17 00:00:00 2001 From: Philip Muzzall Date: Sun, 24 Sep 2023 16:30:22 -0700 Subject: [PATCH 37/90] Fixed warnings about conversions between soundlevel_t and float This replaces the macros ATTN_TO_SNDLVL and SNDLVL_TO_ATTN with inline functions that properly handle conversions between float and soundlevel_t. Eliminates a large number of warnings when compiling with GCC. --- sp/src/public/soundflags.h | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/sp/src/public/soundflags.h b/sp/src/public/soundflags.h index 71ae1192..9fab2308 100644 --- a/sp/src/public/soundflags.h +++ b/sp/src/public/soundflags.h @@ -102,8 +102,22 @@ enum soundlevel_t #define MAX_SNDLVL_VALUE ((1< 50) ? (20.0f / (float)(a - 50)) : 4.0 ) +inline soundlevel_t ATTN_TO_SNDLVL(float a) +{ + soundlevel_t sndlvl = soundlevel_t::SNDLVL_NONE; + + if (a >= 0.0f) + { + sndlvl = soundlevel_t(float(soundlevel_t::SNDLVL_50dB) + float(soundlevel_t::SNDLVL_20dB) / a); + } + + return sndlvl; +} + +inline float SNDLVL_TO_ATTN(soundlevel_t s) +{ + return (s > soundlevel_t::SNDLVL_50dB)? (20.0f / float(s - soundlevel_t::SNDLVL_50dB)) : 4.0f; +} // This is a limit due to network encoding. // It encodes attenuation * 64 in 8 bits, so the maximum is (255 / 64) From 8c338156e0bd513177b68db5e73f21ec739e6fa8 Mon Sep 17 00:00:00 2001 From: Blixibon Date: Tue, 24 Oct 2023 21:33:01 -0500 Subject: [PATCH 38/90] Fix existing point_bugbaits not using radius --- sp/src/game/server/hl2/grenade_bugbait.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sp/src/game/server/hl2/grenade_bugbait.cpp b/sp/src/game/server/hl2/grenade_bugbait.cpp index 0f054d57..feddcd56 100644 --- a/sp/src/game/server/hl2/grenade_bugbait.cpp +++ b/sp/src/game/server/hl2/grenade_bugbait.cpp @@ -35,6 +35,9 @@ CBugBaitSensor* GetBugBaitSensorList() CBugBaitSensor::CBugBaitSensor( void ) { g_BugBaitSensorList.Insert( this ); +#ifdef MAPBASE + m_bUseRadius = true; +#endif } CBugBaitSensor::~CBugBaitSensor( void ) From 8729bebe5f4cb4854ad8178eb3ea39b3e5b2110c Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Tue, 24 Oct 2023 22:07:27 -0500 Subject: [PATCH 39/90] Swap clientside weapon precache back to fix ammo not showing up correctly on HUD --- sp/src/game/client/c_basecombatweapon.cpp | 7 ----- .../client/mapbase/c_weapon_custom_hl2.cpp | 26 ++++++++++++++++++- .../game/shared/basecombatweapon_shared.cpp | 2 -- 3 files changed, 25 insertions(+), 10 deletions(-) diff --git a/sp/src/game/client/c_basecombatweapon.cpp b/sp/src/game/client/c_basecombatweapon.cpp index bdb71f02..3ae13cde 100644 --- a/sp/src/game/client/c_basecombatweapon.cpp +++ b/sp/src/game/client/c_basecombatweapon.cpp @@ -151,13 +151,6 @@ int C_BaseCombatWeapon::GetWorldModelIndex( void ) //----------------------------------------------------------------------------- void C_BaseCombatWeapon::OnDataChanged( DataUpdateType_t updateType ) { -#ifdef MAPBASE - if (updateType == DATA_UPDATE_CREATED) - { - Precache(); - } -#endif // MAPBASE - BaseClass::OnDataChanged(updateType); CHandle< C_BaseCombatWeapon > handle = this; diff --git a/sp/src/game/client/mapbase/c_weapon_custom_hl2.cpp b/sp/src/game/client/mapbase/c_weapon_custom_hl2.cpp index 71675b06..784ba9ea 100644 --- a/sp/src/game/client/mapbase/c_weapon_custom_hl2.cpp +++ b/sp/src/game/client/mapbase/c_weapon_custom_hl2.cpp @@ -23,6 +23,8 @@ public: C_HLCustomWeaponMelee(); + void OnDataChanged( DataUpdateType_t updateType ); + virtual const char* GetWeaponScriptName() { return m_iszWeaponScriptName; } private: char m_iszWeaponScriptName[128]; @@ -39,6 +41,16 @@ C_HLCustomWeaponMelee::C_HLCustomWeaponMelee() m_iszWeaponScriptName[0] = '\0'; } +void C_HLCustomWeaponMelee::OnDataChanged( DataUpdateType_t updateType ) +{ + if (updateType == DATA_UPDATE_CREATED) + { + Precache(); + } + + BaseClass::OnDataChanged( updateType ); +} + class C_HLCustomWeaponGun : public C_BaseHLCombatWeapon @@ -50,6 +62,8 @@ public: C_HLCustomWeaponGun(); + void OnDataChanged( DataUpdateType_t updateType ); + virtual const char* GetWeaponScriptName() { return m_iszWeaponScriptName; } private: char m_iszWeaponScriptName[128]; @@ -64,4 +78,14 @@ END_RECV_TABLE(); C_HLCustomWeaponGun::C_HLCustomWeaponGun() { m_iszWeaponScriptName[0] = '\0'; -} \ No newline at end of file +} + +void C_HLCustomWeaponGun::OnDataChanged( DataUpdateType_t updateType ) +{ + if (updateType == DATA_UPDATE_CREATED) + { + Precache(); + } + + BaseClass::OnDataChanged( updateType ); +} diff --git a/sp/src/game/shared/basecombatweapon_shared.cpp b/sp/src/game/shared/basecombatweapon_shared.cpp index f7dc9d50..c67ab35a 100644 --- a/sp/src/game/shared/basecombatweapon_shared.cpp +++ b/sp/src/game/shared/basecombatweapon_shared.cpp @@ -164,9 +164,7 @@ void CBaseCombatWeapon::GiveDefaultAmmo( void ) //----------------------------------------------------------------------------- void CBaseCombatWeapon::Spawn( void ) { -#if !defined(CLIENT_DLL) || !defined(MAPBASE) Precache(); -#endif // !defined(CLIENT_DLL) || !defined(MAPBASE) BaseClass::Spawn(); From 75e39c33fd0229d0787d33d7732a1dfe2ce77ac5 Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Tue, 24 Oct 2023 22:07:35 -0500 Subject: [PATCH 40/90] Fix Linux compile for custom weapons --- sp/src/game/server/mapbase/custom_weapon_factory.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sp/src/game/server/mapbase/custom_weapon_factory.h b/sp/src/game/server/mapbase/custom_weapon_factory.h index 838fd695..3abb17ed 100644 --- a/sp/src/game/server/mapbase/custom_weapon_factory.h +++ b/sp/src/game/server/mapbase/custom_weapon_factory.h @@ -92,7 +92,7 @@ template class CDefaultCustomWeaponEntityFactory : public CCustomWeaponEntityFactoryBase { public: - CDefaultCustomWeaponEntityFactory(const char *pFactoryClass) : CCustomWeaponEntityFactoryBase(pFactoryClass) + CDefaultCustomWeaponEntityFactory(const char *pFactoryClass) : CCustomWeaponEntityFactoryBase(pFactoryClass) {} virtual const void* ParseDataFromWeaponFile(KeyValues* pKV) const From c406a6702fc9fd740205787d4e087a27c27c0e17 Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Tue, 24 Oct 2023 23:25:01 -0500 Subject: [PATCH 41/90] Fix ViewPunch random for singleplayer AR2 --- sp/src/game/server/hl2/weapon_ar2.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sp/src/game/server/hl2/weapon_ar2.cpp b/sp/src/game/server/hl2/weapon_ar2.cpp index 6e7d4cac..deee7973 100644 --- a/sp/src/game/server/hl2/weapon_ar2.cpp +++ b/sp/src/game/server/hl2/weapon_ar2.cpp @@ -346,7 +346,7 @@ void CWeaponAR2::DelayedAttack( void ) pOwner->SnapEyeAngles( angles ); - pOwner->ViewPunch( QAngle( random->RandomInt( -8, -12 ), random->RandomInt( 1, 2 ), 0 ) ); + pOwner->ViewPunch( QAngle( random->RandomInt( -12, -8 ), random->RandomInt( 1, 2 ), 0 ) ); // Decrease ammo pOwner->RemoveAmmo( 1, m_iSecondaryAmmoType ); From 715acf4ff51266dff828551e2028063a4332ebe0 Mon Sep 17 00:00:00 2001 From: Blixibon Date: Wed, 25 Oct 2023 00:26:29 -0500 Subject: [PATCH 42/90] Apply suggestions from code review --- sp/src/game/server/hl2/weapon_physcannon.cpp | 25 ++++++++++---------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/sp/src/game/server/hl2/weapon_physcannon.cpp b/sp/src/game/server/hl2/weapon_physcannon.cpp index c40ecb14..66991e45 100644 --- a/sp/src/game/server/hl2/weapon_physcannon.cpp +++ b/sp/src/game/server/hl2/weapon_physcannon.cpp @@ -69,8 +69,8 @@ ConVar physcannon_dmg_glass( "physcannon_dmg_glass", "15" ); ConVar physcannon_right_turrets( "physcannon_right_turrets", "0" ); #ifdef MAPBASE -ConVar sv_player_enable_propsprint("sv_player_enable_propsprint", "0", FCVAR_REPLICATED, "If enabled, allows the player to sprint while holding a physics object" ); -ConVar sv_player_enable_gravgun_sprint("sv_player_enable_gravgun_sprint", "0", FCVAR_REPLICATED, "Enables the player to sprint while holding a phys. object with the gravity gun" ); +ConVar sv_player_enable_propsprint("sv_player_enable_propsprint", "0", FCVAR_NONE, "If enabled, allows the player to sprint while holding a physics object" ); +ConVar sv_player_enable_gravgun_sprint("sv_player_enable_gravgun_sprint", "0", FCVAR_NONE, "Enables the player to sprint while holding a phys. object with the gravity gun" ); #endif extern ConVar hl2_normspeed; extern ConVar hl2_walkspeed; @@ -1046,22 +1046,21 @@ void CPlayerPickupController::Init( CBasePlayer *pPlayer, CBaseEntity *pObject ) } CHL2_Player *pOwner = (CHL2_Player *)ToBasePlayer( pPlayer ); -#ifndef MAPBASE if ( pOwner ) { +#ifndef MAPBASE pOwner->EnableSprint( false ); - } - #else - if ( pOwner && sv_player_enable_propsprint.GetBool() == false ) - { - pOwner->EnableSprint( false ); - } - else - { - pOwner->EnableSprint( true ); - } + if ( sv_player_enable_propsprint.GetBool() == false ) + { + pOwner->EnableSprint( false ); + } + else + { + pOwner->EnableSprint( true ); + } #endif + } // If the target is debris, convert it to non-debris if ( pObject->GetCollisionGroup() == COLLISION_GROUP_DEBRIS ) { From bdef83449e6273b18a7d10e8b58225f51a0d0212 Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Wed, 25 Oct 2023 10:46:34 -0500 Subject: [PATCH 43/90] Fix mapspawn_addon not loading from VPK paths --- sp/src/game/shared/vscript_shared.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/sp/src/game/shared/vscript_shared.cpp b/sp/src/game/shared/vscript_shared.cpp index 9f1d610c..8ab2ac97 100644 --- a/sp/src/game/shared/vscript_shared.cpp +++ b/sp/src/game/shared/vscript_shared.cpp @@ -453,8 +453,7 @@ void RunAddonScripts() // mapspawn_addon char fullpath[MAX_PATH]; - Q_snprintf( fullpath, sizeof( fullpath ), "%sscripts/vscripts/mapspawn_addon", path ); - Q_FixSlashes( fullpath ); + Q_ComposeFileName( path, "scripts/vscripts/mapspawn_addon", fullpath, sizeof( fullpath ) ); VScriptRunScriptAbsolute( fullpath, NULL, false, folderName ); } From 8194d80831b3a644f50cb743a9343bf964b8713c Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Wed, 25 Oct 2023 10:49:23 -0500 Subject: [PATCH 44/90] Fix typo in logic_branch removal signal for logic_branch_listener --- sp/src/game/server/logicentities.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sp/src/game/server/logicentities.cpp b/sp/src/game/server/logicentities.cpp index aa838a35..0d6bb006 100644 --- a/sp/src/game/server/logicentities.cpp +++ b/sp/src/game/server/logicentities.cpp @@ -3112,7 +3112,7 @@ void CLogicBranch::UpdateOnRemove() CBaseEntity *pEntity = m_Listeners.Element( i ).Get(); if ( pEntity ) { - g_EventQueue.AddEvent( this, "_OnLogicBranchRemoved", 0, this, this ); + g_EventQueue.AddEvent( pEntity, "_OnLogicBranchRemoved", 0, this, this ); } } From cae8a7d28a85959f17e05674a1f7a99f4a04db85 Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Fri, 3 Nov 2023 16:25:29 -0500 Subject: [PATCH 45/90] AI grenade throw hints --- sp/src/game/server/ai_hint.cpp | 7 ++++- sp/src/game/server/ai_hint.h | 1 + sp/src/game/server/mapbase/ai_grenade.h | 37 +++++++++++++++++++++++++ 3 files changed, 44 insertions(+), 1 deletion(-) diff --git a/sp/src/game/server/ai_hint.cpp b/sp/src/game/server/ai_hint.cpp index 2e8e90af..e9844a01 100644 --- a/sp/src/game/server/ai_hint.cpp +++ b/sp/src/game/server/ai_hint.cpp @@ -1325,7 +1325,7 @@ bool CAI_Hint::HintMatchesCriteria( CAI_BaseNPC *pNPC, const CHintCriteria &hint if ( distance > nRadius * nRadius ) { - REPORTFAILURE( "NPC is not within the node's radius." ); + REPORTFAILURE( "Not within the node's radius." ); return false; } } @@ -1794,6 +1794,11 @@ hinttypedescs_t g_pszHintDescriptions[] = { HINT_HL1_WORLD_ALIEN_BLOOD, "HL1: World: Alien Blood" }, { HINT_CSTRIKE_HOSTAGE_ESCAPE, "CS Port: Hostage Escape" }, + +#ifdef MAPBASE + { HINT_TACTICAL_COVER_CUSTOM, "Mapbase: Custom Cover" }, + { HINT_TACTICAL_GRENADE_THROW, "Mapbase: Grenade Throw Hint" }, +#endif }; //----------------------------------------------------------------------------- diff --git a/sp/src/game/server/ai_hint.h b/sp/src/game/server/ai_hint.h index 570f85ef..fc23f9c3 100644 --- a/sp/src/game/server/ai_hint.h +++ b/sp/src/game/server/ai_hint.h @@ -118,6 +118,7 @@ enum Hint_e // (these start at a high number to avoid potential conflicts with mod hints) HINT_TACTICAL_COVER_CUSTOM = 10000, // Cover node with a custom hint activity (NPCs can take cover and reload here while playing said activity) + HINT_TACTICAL_GRENADE_THROW, // Pre-determined position for NPCs to throw grenades at when their target in combat is near it #endif }; const char *GetHintTypeDescription( Hint_e iHintType ); diff --git a/sp/src/game/server/mapbase/ai_grenade.h b/sp/src/game/server/mapbase/ai_grenade.h index 543969c2..d2e02839 100644 --- a/sp/src/game/server/mapbase/ai_grenade.h +++ b/sp/src/game/server/mapbase/ai_grenade.h @@ -138,6 +138,8 @@ public: void ClearAttackConditions( void ); + bool FValidateHintType( CAI_Hint *pHint ); + Vector GetAltFireTarget() { return m_vecAltFireTarget; } virtual bool CanAltFireEnemy( bool bUseFreeKnowledge ); void DelayAltFireAttack( float flDelay ); @@ -558,6 +560,29 @@ bool CAI_GrenadeUser::CanThrowGrenade( const Vector &vecTarget ) } } + CHintCriteria hintCriteria; + hintCriteria.SetHintType( HINT_TACTICAL_GRENADE_THROW ); + hintCriteria.SetFlag( bits_HINT_NPC_IN_NODE_FOV ); + hintCriteria.SetGroup( this->GetHintGroup() ); + hintCriteria.AddIncludePosition( this->GetAbsOrigin(), 1024 ); + + if (this->m_debugOverlays & OVERLAY_NPC_SELECTED_BIT) + hintCriteria.SetFlag( bits_HINT_NODE_REPORT_FAILURES ); + + // If there's a grenade throw hint nearby, try using it + CAI_Hint *pHint = CAI_HintManager::FindHint( this, vecTarget, hintCriteria ); + if ( pHint ) + { + if ( CheckCanThrowGrenade( pHint->GetAbsOrigin() ) ) + { + return true; + } + else + { + DevMsg( this, "Unable to throw grenade at hint %s\n", pHint->GetDebugName() ); + } + } + return CheckCanThrowGrenade( vecTarget ); } @@ -636,6 +661,18 @@ void CAI_GrenadeUser::ClearAttackConditions() } } +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +template +bool CAI_GrenadeUser::FValidateHintType( CAI_Hint *pHint ) +{ + if ( pHint->HintType() == HINT_TACTICAL_GRENADE_THROW ) + return true; + + return BaseClass::FValidateHintType( pHint ); +} + //----------------------------------------------------------------------------- // Purpose: Drops grenades and alt-fire items on death. Based on code from npc_combines.cpp and npc_combine.cpp //----------------------------------------------------------------------------- From d18b5a21447788c8e8a3a8360f53ad2671a07287 Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Fri, 3 Nov 2023 16:27:42 -0500 Subject: [PATCH 46/90] Fix clientside C_BaseCombatCharacter hierarchy in VScript --- sp/src/game/client/c_basecombatcharacter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sp/src/game/client/c_basecombatcharacter.cpp b/sp/src/game/client/c_basecombatcharacter.cpp index 1656f567..0afe4cbf 100644 --- a/sp/src/game/client/c_basecombatcharacter.cpp +++ b/sp/src/game/client/c_basecombatcharacter.cpp @@ -181,7 +181,7 @@ END_PREDICTION_DATA() #ifdef MAPBASE_VSCRIPT -BEGIN_ENT_SCRIPTDESC( C_BaseCombatCharacter, CBaseEntity, "" ) +BEGIN_ENT_SCRIPTDESC( C_BaseCombatCharacter, C_BaseAnimating, "" ) DEFINE_SCRIPTFUNC_NAMED( ScriptGetAmmoCount, "GetAmmoCount", "" ) DEFINE_SCRIPTFUNC_NAMED( ScriptGetActiveWeapon, "GetActiveWeapon", "" ) DEFINE_SCRIPTFUNC_NAMED( ScriptGetWeapon, "GetWeapon", "" ) From 032cfdaec38c37738c8c0156d755cf4534b8eeb0 Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Fri, 3 Nov 2023 16:29:02 -0500 Subject: [PATCH 47/90] New Get/SetGroundEntity, Get/SetModelScale functions for VScript --- sp/src/game/client/c_baseentity.cpp | 3 +++ sp/src/game/client/c_baseentity.h | 5 +++++ sp/src/game/server/baseanimating.cpp | 2 ++ sp/src/game/server/baseentity.cpp | 3 +++ sp/src/game/server/baseentity.h | 5 +++++ sp/src/game/shared/baseentity_shared.cpp | 12 ++++++++++++ 6 files changed, 30 insertions(+) diff --git a/sp/src/game/client/c_baseentity.cpp b/sp/src/game/client/c_baseentity.cpp index 43471e9d..1d3de004 100644 --- a/sp/src/game/client/c_baseentity.cpp +++ b/sp/src/game/client/c_baseentity.cpp @@ -502,6 +502,9 @@ BEGIN_ENT_SCRIPTDESC_ROOT( C_BaseEntity, "Root class of all client-side entities DEFINE_SCRIPTFUNC_NAMED( GetScriptOwnerEntity, "GetOwner", "Gets this entity's owner" ) DEFINE_SCRIPTFUNC_NAMED( SetScriptOwnerEntity, "SetOwner", "Sets this entity's owner" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetGroundEntity, "GetGroundEntity", "Get the entity we're standing on." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptSetGroundEntity, "SetGroundEntity", "Set the entity we're standing on." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetColorVector, "GetRenderColorVector", "Get the render color as a vector" ) DEFINE_SCRIPTFUNC_NAMED( ScriptGetColorR, "GetRenderColorR", "Get the render color's R value" ) DEFINE_SCRIPTFUNC_NAMED( ScriptGetColorG, "GetRenderColorG", "Get the render color's G value" ) diff --git a/sp/src/game/client/c_baseentity.h b/sp/src/game/client/c_baseentity.h index fe159afe..422a59d4 100644 --- a/sp/src/game/client/c_baseentity.h +++ b/sp/src/game/client/c_baseentity.h @@ -288,6 +288,11 @@ public: HSCRIPT GetScriptOwnerEntity(); virtual void SetScriptOwnerEntity(HSCRIPT pOwner); +#ifdef MAPBASE_VSCRIPT + HSCRIPT ScriptGetGroundEntity(); + void ScriptSetGroundEntity( HSCRIPT hGroundEnt ); +#endif + HSCRIPT GetScriptInstance(); HSCRIPT m_hScriptInstance; diff --git a/sp/src/game/server/baseanimating.cpp b/sp/src/game/server/baseanimating.cpp index 38b1606a..be4d6b73 100644 --- a/sp/src/game/server/baseanimating.cpp +++ b/sp/src/game/server/baseanimating.cpp @@ -335,6 +335,8 @@ BEGIN_ENT_SCRIPTDESC( CBaseAnimating, CBaseEntity, "Animating models" ) DEFINE_SCRIPTFUNC( FindBodygroupByName, "Finds a bodygroup by name" ) DEFINE_SCRIPTFUNC( GetBodygroupCount, "Gets the number of models in a bodygroup" ) DEFINE_SCRIPTFUNC( GetNumBodyGroups, "Gets the number of bodygroups" ) + DEFINE_SCRIPTFUNC( GetModelScale, "Gets the model's scale" ) + DEFINE_SCRIPTFUNC( SetModelScale, "Sets the model's scale with the specified change duration" ) DEFINE_SCRIPTFUNC( Dissolve, "Use 'sprites/blueglow1.vmt' for the default material, Time() for the default start time, false for npcOnly if you don't want it to check if the entity is a NPC first, 0 for the default dissolve type, Vector(0,0,0) for the default dissolver origin, and 0 for the default magnitude." ) DEFINE_SCRIPTFUNC( Ignite, "'NPCOnly' only lets this fall through if the entity is a NPC and 'CalledByLevelDesigner' determines whether to treat this like the Ignite input or just an internal ignition call." ) diff --git a/sp/src/game/server/baseentity.cpp b/sp/src/game/server/baseentity.cpp index 67c3b17e..d8c4b63e 100644 --- a/sp/src/game/server/baseentity.cpp +++ b/sp/src/game/server/baseentity.cpp @@ -2378,6 +2378,9 @@ BEGIN_ENT_SCRIPTDESC_ROOT( CBaseEntity, "Root class of all server-side entities" DEFINE_SCRIPTFUNC( GetContextExpireTime, "Get a response context's expiration time" ) DEFINE_SCRIPTFUNC( GetContextCount, "Get the number of response contexts" ) DEFINE_SCRIPTFUNC_NAMED( ScriptGetContextIndex, "GetContextIndex", "Get a response context at a specific index in the form of a table" ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptGetGroundEntity, "GetGroundEntity", "Get the entity we're standing on." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptSetGroundEntity, "SetGroundEntity", "Set the entity we're standing on." ) DEFINE_SCRIPTFUNC_NAMED( ScriptFollowEntity, "FollowEntity", "Begin following the specified entity. This makes this entity non-solid, parents it to the target entity, and teleports it to the specified entity's origin. The second parameter is whether or not to use bonemerging while following." ) DEFINE_SCRIPTFUNC( StopFollowingEntity, "Stops following an entity if we're following one." ) diff --git a/sp/src/game/server/baseentity.h b/sp/src/game/server/baseentity.h index 1453d859..acbffdb2 100644 --- a/sp/src/game/server/baseentity.h +++ b/sp/src/game/server/baseentity.h @@ -1445,6 +1445,11 @@ public: void SetGroundEntity( CBaseEntity *ground ); CBaseEntity *GetGroundEntity( void ); CBaseEntity *GetGroundEntity( void ) const { return const_cast(this)->GetGroundEntity(); } + +#ifdef MAPBASE_VSCRIPT + HSCRIPT ScriptGetGroundEntity(); + void ScriptSetGroundEntity( HSCRIPT hGroundEnt ); +#endif // Gets the velocity we impart to a player standing on us virtual void GetGroundVelocityToApply( Vector &vecGroundVel ) { vecGroundVel = vec3_origin; } diff --git a/sp/src/game/shared/baseentity_shared.cpp b/sp/src/game/shared/baseentity_shared.cpp index 3393afa8..e5588287 100644 --- a/sp/src/game/shared/baseentity_shared.cpp +++ b/sp/src/game/shared/baseentity_shared.cpp @@ -2736,6 +2736,18 @@ void CBaseEntity::SetScriptOwnerEntity(HSCRIPT pOwner) SetOwnerEntity(ToEnt(pOwner)); } +#ifdef MAPBASE_VSCRIPT +HSCRIPT CBaseEntity::ScriptGetGroundEntity() +{ + return ToHScript( m_hGroundEntity.Get() ); +} + +void CBaseEntity::ScriptSetGroundEntity( HSCRIPT hGroundEnt ) +{ + SetGroundEntity( ToEnt( hGroundEnt ) ); +} +#endif + //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- const Vector& CBaseEntity::ScriptGetColorVector() From aa03b6b51ec673bcf5b7d9dda11161701c3de836 Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Fri, 3 Nov 2023 16:29:27 -0500 Subject: [PATCH 48/90] Fix custom procedural crash --- sp/src/game/server/entitylist.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sp/src/game/server/entitylist.cpp b/sp/src/game/server/entitylist.cpp index 55d5614b..29d87f50 100644 --- a/sp/src/game/server/entitylist.cpp +++ b/sp/src/game/server/entitylist.cpp @@ -736,6 +736,12 @@ CBaseEntity *CGlobalEntityList::FindEntityCustomProcedural( CBaseEntity *pStartE g_pScriptVM->ExecuteFunction( g_CustomProcedurals[i].hFunc, args, 5, &functionReturn, NULL, true ); + if (pStartEntity && ToEnt( functionReturn.m_hScript ) == pStartEntity) + { + Warning( "WARNING: Custom procedural %s returned entity identical to start entity (%s), returning null\n", g_CustomProcedurals[i].szName, pStartEntity->GetDebugName() ); + return NULL; + } + return ToEnt( functionReturn.m_hScript ); } } From 0e110b782346abd11b45b265d85a1945f39bd69d Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Fri, 3 Nov 2023 16:31:09 -0500 Subject: [PATCH 49/90] Fix ai_relationship_classify applying before default relationships loaded --- sp/src/game/server/ai_relationship.cpp | 3 +++ sp/src/game/server/basecombatcharacter.cpp | 10 ++++++++++ sp/src/game/server/basecombatcharacter.h | 1 + 3 files changed, 14 insertions(+) diff --git a/sp/src/game/server/ai_relationship.cpp b/sp/src/game/server/ai_relationship.cpp index a700552a..319d5f7e 100644 --- a/sp/src/game/server/ai_relationship.cpp +++ b/sp/src/game/server/ai_relationship.cpp @@ -615,6 +615,9 @@ void CAI_ClassRelationship::ChangeRelationships( int disposition, int iReverting return; } + if ( !CBaseCombatCharacter::DefaultRelationshipsLoaded() ) + return; + if ( m_iPreviousDisposition == -1 && iReverting == NOT_REVERTING ) { // Set previous disposition. diff --git a/sp/src/game/server/basecombatcharacter.cpp b/sp/src/game/server/basecombatcharacter.cpp index 7d45ec4e..f3c9543c 100644 --- a/sp/src/game/server/basecombatcharacter.cpp +++ b/sp/src/game/server/basecombatcharacter.cpp @@ -3239,6 +3239,16 @@ void CBaseCombatCharacter::SetDefaultRelationship(Class_T nClass, Class_T nClass } #ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: Determine whether or not default relationships are loaded +// Input : +// Output : +//----------------------------------------------------------------------------- +bool CBaseCombatCharacter::DefaultRelationshipsLoaded() +{ + return m_DefaultRelationship != NULL; +} + //----------------------------------------------------------------------------- // Purpose: Fetch the default (ignore ai_relationship changes) relationship // Input : diff --git a/sp/src/game/server/basecombatcharacter.h b/sp/src/game/server/basecombatcharacter.h index c7eb9960..ccd5d599 100644 --- a/sp/src/game/server/basecombatcharacter.h +++ b/sp/src/game/server/basecombatcharacter.h @@ -462,6 +462,7 @@ public: static void AllocateDefaultRelationships( ); static void SetDefaultRelationship( Class_T nClass, Class_T nClassTarget, Disposition_t nDisposition, int nPriority ); #ifdef MAPBASE + static bool DefaultRelationshipsLoaded(); static Disposition_t GetDefaultRelationshipDisposition( Class_T nClassSource, Class_T nClassTarget ); static int GetDefaultRelationshipPriority( Class_T nClassSource, Class_T nClassTarget ); int GetDefaultRelationshipPriority( Class_T nClassTarget ); From c6ee15336f63075c99be53ae7359748550c29c6c Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Fri, 3 Nov 2023 16:34:00 -0500 Subject: [PATCH 50/90] Fix CBaseEntity::CanBeHitByMeleeAttack() not blocking damage from CTraceFilterMelee --- sp/src/game/server/basecombatcharacter.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/sp/src/game/server/basecombatcharacter.cpp b/sp/src/game/server/basecombatcharacter.cpp index f3c9543c..15444f50 100644 --- a/sp/src/game/server/basecombatcharacter.cpp +++ b/sp/src/game/server/basecombatcharacter.cpp @@ -1264,6 +1264,11 @@ bool CTraceFilterMelee::ShouldHitEntity( IHandleEntity *pHandleEntity, int conte if ( pEntity->m_takedamage == DAMAGE_NO ) return false; +#ifdef MAPBASE // Moved from CheckTraceHullAttack() + if( m_pPassEnt && !pEntity->CanBeHitByMeleeAttack( const_cast(EntityFromEntityHandle( m_pPassEnt ) ) ) ) + return false; +#endif + // FIXME: Do not translate this to the driver because the driver only accepts damage from the vehicle // Translate the vehicle into its driver for damage /* @@ -1311,6 +1316,10 @@ bool CTraceFilterMelee::ShouldHitEntity( IHandleEntity *pHandleEntity, int conte } else { +#ifdef MAPBASE + // Do not override an existing hit entity + if (!m_pHit) +#endif m_pHit = pEntity; // Make sure if the player is holding this, he drops it @@ -1386,11 +1395,13 @@ CBaseEntity *CBaseCombatCharacter::CheckTraceHullAttack( const Vector &vStart, c pEntity = traceFilter.m_pHit; } +#ifndef MAPBASE // Moved to CTraceFilterMelee if( pEntity && !pEntity->CanBeHitByMeleeAttack(this) ) { // If we touched something, but it shouldn't be hit, return nothing. pEntity = NULL; } +#endif return pEntity; From d981f6a00592c757bdfc2deec52b88588e9c94a0 Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Fri, 3 Nov 2023 19:09:19 -0500 Subject: [PATCH 51/90] Fix Linux compile for AI grenade hints --- sp/src/game/server/mapbase/ai_grenade.h | 1 + 1 file changed, 1 insertion(+) diff --git a/sp/src/game/server/mapbase/ai_grenade.h b/sp/src/game/server/mapbase/ai_grenade.h index d2e02839..cc5238f1 100644 --- a/sp/src/game/server/mapbase/ai_grenade.h +++ b/sp/src/game/server/mapbase/ai_grenade.h @@ -23,6 +23,7 @@ #include "hl2_gamerules.h" #include "weapon_physcannon.h" #include "globalstate.h" +#include "ai_hint.h" #define COMBINE_AE_GREN_TOSS ( 7 ) From ecb58e4246d5818d7eb3ee61aa87bdb1dcd6f048 Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Wed, 25 Oct 2023 10:51:01 -0500 Subject: [PATCH 52/90] Fix game_text and logic_format not functioning properly on Linux --- sp/src/game/server/logicentities.cpp | 8 ++++---- sp/src/game/server/maprules.cpp | 3 ++- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/sp/src/game/server/logicentities.cpp b/sp/src/game/server/logicentities.cpp index 0d6bb006..2445f875 100644 --- a/sp/src/game/server/logicentities.cpp +++ b/sp/src/game/server/logicentities.cpp @@ -4059,7 +4059,7 @@ void CLogicFormat::FormatString(const char *szStringToFormat, char *szOutput, in curparam = atoi(szToken); if (curparam < MAX_LOGIC_FORMAT_PARAMETERS /*&& szParameters[curparam] != NULL*/) //if (curparam < MAX_FORMAT_PARAMETERS) { - Q_snprintf(szFormatted, sizeof(szFormatted), "%s%s", szFormatted, szParameters[curparam]); + Q_strncat(szFormatted, szParameters[curparam], sizeof(szFormatted)); } else { @@ -4068,8 +4068,8 @@ void CLogicFormat::FormatString(const char *szStringToFormat, char *szOutput, in // This might not be the best way to do this, but // reaching it is supposed to be the result of a mistake anyway. m_iszBackupParameter != NULL_STRING ? - Q_snprintf(szFormatted, sizeof(szFormatted), "%s%s", szFormatted, STRING(m_iszBackupParameter)) : - Q_snprintf(szFormatted, sizeof(szFormatted), "%s", szFormatted); + Q_strncat( szFormatted, STRING(m_iszBackupParameter), sizeof( szFormatted ) ) : + Q_strncat( szFormatted, "", sizeof( szFormatted ) ); } inparam = false; @@ -4077,7 +4077,7 @@ void CLogicFormat::FormatString(const char *szStringToFormat, char *szOutput, in } else { - Q_snprintf(szFormatted, sizeof(szFormatted), "%s%s", szFormatted, szToken); + Q_strncat( szFormatted, szToken, sizeof( szFormatted ) ); inparam = true; szToken = strtok(NULL, "}"); diff --git a/sp/src/game/server/maprules.cpp b/sp/src/game/server/maprules.cpp index 91049ad8..60279244 100644 --- a/sp/src/game/server/maprules.cpp +++ b/sp/src/game/server/maprules.cpp @@ -447,7 +447,8 @@ void CGameText::SetText( const char* pszStr ) for (int i = 1; i < vecLines.Count(); i++) { - Q_snprintf( szMsg, sizeof( szMsg ), "%s\n%s", szMsg, vecLines[i] ); + Q_strncat( szMsg, "\n", sizeof( szMsg ) ); + Q_strncat( szMsg, vecLines[i], sizeof( szMsg ) ); } m_iszMessage = AllocPooledString( szMsg ); } From 650ddeecebfa66af0aee4313699a040f308e83e0 Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Tue, 31 Oct 2023 08:12:47 -0500 Subject: [PATCH 53/90] Minor Mapbase workflow fixes --- .github/workflows/mapbase_build-base.yml | 2 +- .github/workflows/mapbase_build-sp-games.yml | 1 + .github/workflows/mapbase_pr.yml | 4 ++-- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/mapbase_build-base.yml b/.github/workflows/mapbase_build-base.yml index e5619bfc..43a4e3ca 100644 --- a/.github/workflows/mapbase_build-base.yml +++ b/.github/workflows/mapbase_build-base.yml @@ -51,7 +51,7 @@ jobs: runs-on: windows-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Add MSBuild to PATH uses: microsoft/setup-msbuild@v1.1 diff --git a/.github/workflows/mapbase_build-sp-games.yml b/.github/workflows/mapbase_build-sp-games.yml index 3e81ca72..dc5511ae 100644 --- a/.github/workflows/mapbase_build-sp-games.yml +++ b/.github/workflows/mapbase_build-sp-games.yml @@ -13,6 +13,7 @@ on: branches: - develop paths: + - '.github/workflows/mapbase_build-base.yml' - '.github/workflows/mapbase_build-sp-rel-games.yml' - 'sp/src/vpc_scripts/**' - 'sp/src/game/**' diff --git a/.github/workflows/mapbase_pr.yml b/.github/workflows/mapbase_pr.yml index c7c6a3e6..b71506ea 100644 --- a/.github/workflows/mapbase_pr.yml +++ b/.github/workflows/mapbase_pr.yml @@ -6,12 +6,12 @@ # https://github.com/actions/labeler name: Pull Request Automation -on: [pull_request] +on: [pull_request] # pull_request_target jobs: label: - if: github.repository_owner == 'mapbase-source' + if: github.repository_owner == 'mapbase-source' # ${{ vars.MAPBASE_LABELS == 'true' }} runs-on: ubuntu-latest permissions: contents: read From e7cdb69c588bbce2816c6c7b41181eb05360819d Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Fri, 24 Nov 2023 10:27:48 -0600 Subject: [PATCH 54/90] Fix ai_relationship_classify not working properly after loading save --- sp/src/game/server/ai_relationship.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/sp/src/game/server/ai_relationship.cpp b/sp/src/game/server/ai_relationship.cpp index 319d5f7e..e5f12d62 100644 --- a/sp/src/game/server/ai_relationship.cpp +++ b/sp/src/game/server/ai_relationship.cpp @@ -532,6 +532,7 @@ public: // Must override CAI_Relationship void Spawn() { m_bIsActive = false; } + void Activate(); bool KeyValue( const char *szKeyName, const char *szValue ); @@ -557,6 +558,19 @@ BEGIN_DATADESC( CAI_ClassRelationship ) END_DATADESC() +//--------------------------------------------------------- +//--------------------------------------------------------- +void CAI_ClassRelationship::Activate() +{ + BaseClass::Activate(); + + // Must re-apply every time a save is loaded + if ( m_bIsActive ) + { + ApplyRelationship(); + } +} + //----------------------------------------------------------------------------- // Purpose: Caches entity key values until spawn is called. // Input : szKeyName - From 424fcf07929c6af3fabed2b374aa90e473333032 Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Fri, 24 Nov 2023 10:29:02 -0600 Subject: [PATCH 55/90] Add extra trace to npc_create for NPCs which should use bbox instead of hull (e.g. npc_rollermine) --- sp/src/game/server/ai_concommands.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/sp/src/game/server/ai_concommands.cpp b/sp/src/game/server/ai_concommands.cpp index e004dca1..6c4370d9 100644 --- a/sp/src/game/server/ai_concommands.cpp +++ b/sp/src/game/server/ai_concommands.cpp @@ -477,6 +477,20 @@ public: AI_TraceHull( baseNPC->GetAbsOrigin(), vUpBit, baseNPC->GetHullMins(), baseNPC->GetHullMaxs(), MASK_NPCSOLID, baseNPC, COLLISION_GROUP_NONE, &tr ); + + // NEW: For vphysics/flying entities, do a second attempt which teleports based on bounding box + if ( (baseNPC->GetMoveType() == MOVETYPE_VPHYSICS || baseNPC->CapabilitiesGet() & bits_CAP_MOVE_FLY) && (tr.startsolid || tr.fraction < 1.0) ) + { + vUpBit.z += baseNPC->BoundingRadius(); + baseNPC->Teleport( &vUpBit, NULL, NULL ); + UTIL_DropToFloor( baseNPC, MASK_NPCSOLID ); + + Vector vUpBit2 = vUpBit; + vUpBit2.z += 1; + AI_TraceHull( vUpBit, vUpBit2, baseNPC->CollisionProp()->OBBMins(), baseNPC->CollisionProp()->OBBMaxs(), + MASK_NPCSOLID, baseNPC, COLLISION_GROUP_NONE, &tr ); + } + if ( tr.startsolid || (tr.fraction < 1.0) ) { baseNPC->SUB_Remove(); From 5fe4621eb6ce8aed76f47da99e0ac7ac6fdada90 Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Fri, 24 Nov 2023 10:45:25 -0600 Subject: [PATCH 56/90] Various comment changes --- sp/src/game/client/vscript_client.cpp | 1 - sp/src/game/server/ai_basenpc.cpp | 7 ++----- sp/src/game/server/basecombatcharacter.cpp | 5 ++--- sp/src/game/server/player.cpp | 2 +- sp/src/game/server/props.cpp | 11 ----------- sp/src/game/server/util.h | 3 +-- 6 files changed, 6 insertions(+), 23 deletions(-) diff --git a/sp/src/game/client/vscript_client.cpp b/sp/src/game/client/vscript_client.cpp index de10156a..7bece925 100644 --- a/sp/src/game/client/vscript_client.cpp +++ b/sp/src/game/client/vscript_client.cpp @@ -431,7 +431,6 @@ const Vector& CScriptMaterialProxy::GetVarVector( int i ) if (m_MaterialVars[i]->GetType() != MATERIAL_VAR_TYPE_VECTOR) return vec3_origin; - // This is really bad. Too bad! return *(reinterpret_cast(m_MaterialVars[i]->GetVecValue())); } diff --git a/sp/src/game/server/ai_basenpc.cpp b/sp/src/game/server/ai_basenpc.cpp index 299aa5c4..acfebf14 100644 --- a/sp/src/game/server/ai_basenpc.cpp +++ b/sp/src/game/server/ai_basenpc.cpp @@ -13200,17 +13200,14 @@ void CAI_BaseNPC::InputSetEnemyFilter( inputdata_t &inputdata ) //----------------------------------------------------------------------------- void CAI_BaseNPC::InputSetHealthFraction( inputdata_t &inputdata ) { - // npc_helicopter uses SetHealth() instead of the regular NPC practice of TakeHealth() and TakeDamage(). - // It also also uses 50, 75, etc. and scales it by 0.01 for some reason. - // We're using the same model as InputSetHealth() and just letting npc_helicopter override it. No big deal. - // We're also adding support for its "whole number * 0.01" thing too. + // npc_helicopter uses an identically named input and scales down whole numbers instead of using fractions directly. + // This function is overridden by npc_helicopter for other reasons, but support for its differing behavior is also available through this input. float flFactor = inputdata.value.Float(); if ( flFactor > 1.0f ) { flFactor *= 0.01f; } - // Excuse the complication... float flNewHealth = (GetMaxHealth() * flFactor); int iNewHealth = (int)flNewHealth; if (flNewHealth < (GetMaxHealth() / 2)) diff --git a/sp/src/game/server/basecombatcharacter.cpp b/sp/src/game/server/basecombatcharacter.cpp index 15444f50..c71d89f4 100644 --- a/sp/src/game/server/basecombatcharacter.cpp +++ b/sp/src/game/server/basecombatcharacter.cpp @@ -3487,7 +3487,7 @@ void CBaseCombatCharacter::AddRelationship( const char *pszRelationship, CBaseEn } else { -#ifdef MAPBASE // I know the extra #ifdef is pointless, but it's there so you know this is new + // NEW: Classify class relationships if (!Q_strnicmp(entityString, "CLASS_", 5)) { // Go through all of the classes and find which one this is @@ -3508,8 +3508,7 @@ void CBaseCombatCharacter::AddRelationship( const char *pszRelationship, CBaseEn } if (!bFoundEntity) -#endif - DevWarning( "Couldn't set relationship to unknown entity or class (%s)!\n", entityString ); + DevWarning( "Couldn't set relationship to unknown entity or class (%s)!\n", entityString ); } } } diff --git a/sp/src/game/server/player.cpp b/sp/src/game/server/player.cpp index a68bf982..f8bf33a2 100644 --- a/sp/src/game/server/player.cpp +++ b/sp/src/game/server/player.cpp @@ -7021,7 +7021,7 @@ bool CBasePlayer::BumpWeapon( CBaseCombatWeapon *pWeapon ) { //Weapon_EquipAmmoOnly( pWeapon ); - // I'm too lazy to make my own version of Weapon_EquipAmmoOnly that doesn't check if we already have the weapon first + // Weapon_EquipAmmoOnly checks the array again, which isn't necessary here int primaryGiven = (pWeapon->UsesClipsForAmmo1()) ? pWeapon->m_iClip1 : pWeapon->GetPrimaryAmmoCount(); int secondaryGiven = (pWeapon->UsesClipsForAmmo2()) ? pWeapon->m_iClip2 : pWeapon->GetSecondaryAmmoCount(); diff --git a/sp/src/game/server/props.cpp b/sp/src/game/server/props.cpp index da632cea..5ad4c11a 100644 --- a/sp/src/game/server/props.cpp +++ b/sp/src/game/server/props.cpp @@ -6308,14 +6308,12 @@ void CPropDoorRotating::Break( CBaseEntity *pBreaker, const CTakeDamageInfo &inf } #endif -#ifdef MAPBASE void CPropDoorRotating::InputSetSpeed(inputdata_t &inputdata) { AssertMsg1(inputdata.value.Float() > 0.0f, "InputSetSpeed on %s called with negative parameter!", GetDebugName() ); m_flSpeed = inputdata.value.Float(); DoorResume(); } -#endif // Debug sphere class CPhysSphere : public CPhysicsProp @@ -6362,15 +6360,6 @@ BEGIN_DATADESC( CPhysSphere ) END_DATADESC() #endif -#ifndef MAPBASE // Yes, all I'm doing is moving this up a few lines and I'm still using the preprocessor. -void CPropDoorRotating::InputSetSpeed(inputdata_t &inputdata) -{ - AssertMsg1(inputdata.value.Float() > 0.0f, "InputSetSpeed on %s called with negative parameter!", GetDebugName() ); - m_flSpeed = inputdata.value.Float(); - DoorResume(); -} -#endif - LINK_ENTITY_TO_CLASS( prop_sphere, CPhysSphere ); diff --git a/sp/src/game/server/util.h b/sp/src/game/server/util.h index 525ce341..bed006e1 100644 --- a/sp/src/game/server/util.h +++ b/sp/src/game/server/util.h @@ -191,8 +191,7 @@ extern CGlobalVars *gpGlobals; inline bool FStrEq(const char *sz1, const char *sz2) { #ifdef MAPBASE - // V_stricmp() already checks if the pointers are equal, so having a pointer comparison here is pointless. - // I'm not sure if this was already automatically phased out by the compiler, but if it wasn't, then this is a very good change. + // V_stricmp() already checks if the pointers are equal, so having a pointer comparison here is unnecessary. return ( V_stricmp(sz1, sz2) == 0 ); #else return ( sz1 == sz2 || V_stricmp(sz1, sz2) == 0 ); From 6a0a0154c676d14d52530e089020af49b0e4e557 Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Fri, 24 Nov 2023 10:45:56 -0600 Subject: [PATCH 57/90] SetClassRelationship function for VScript --- sp/src/game/server/basecombatcharacter.cpp | 8 ++++++++ sp/src/game/server/basecombatcharacter.h | 1 + 2 files changed, 9 insertions(+) diff --git a/sp/src/game/server/basecombatcharacter.cpp b/sp/src/game/server/basecombatcharacter.cpp index c71d89f4..5d2c692c 100644 --- a/sp/src/game/server/basecombatcharacter.cpp +++ b/sp/src/game/server/basecombatcharacter.cpp @@ -175,6 +175,7 @@ BEGIN_ENT_SCRIPTDESC( CBaseCombatCharacter, CBaseFlex, "The base class shared by DEFINE_SCRIPTFUNC_NAMED( ScriptRelationType, "GetRelationship", "Get a character's relationship to a specific entity." ) DEFINE_SCRIPTFUNC_NAMED( ScriptRelationPriority, "GetRelationPriority", "Get a character's relationship priority for a specific entity." ) DEFINE_SCRIPTFUNC_NAMED( ScriptSetRelationship, "SetRelationship", "Set a character's relationship with a specific entity." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptSetClassRelationship, "SetClassRelationship", "Set a character's relationship with a specific Classify() class." ) DEFINE_SCRIPTFUNC_NAMED( ScriptGetVehicleEntity, "GetVehicleEntity", "Get the entity for a character's current vehicle if they're in one." ) @@ -4663,6 +4664,13 @@ void CBaseCombatCharacter::ScriptSetRelationship( HSCRIPT pTarget, int dispositi AddEntityRelationship( ToEnt( pTarget ), (Disposition_t)disposition, priority ); } +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CBaseCombatCharacter::ScriptSetClassRelationship( int classify, int disposition, int priority ) +{ + AddClassRelationship( (Class_T)classify, (Disposition_t)disposition, priority); +} + //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- HSCRIPT CBaseCombatCharacter::ScriptGetVehicleEntity() diff --git a/sp/src/game/server/basecombatcharacter.h b/sp/src/game/server/basecombatcharacter.h index ccd5d599..22f07eb9 100644 --- a/sp/src/game/server/basecombatcharacter.h +++ b/sp/src/game/server/basecombatcharacter.h @@ -440,6 +440,7 @@ public: int ScriptRelationType( HSCRIPT pTarget ); int ScriptRelationPriority( HSCRIPT pTarget ); void ScriptSetRelationship( HSCRIPT pTarget, int disposition, int priority ); + void ScriptSetClassRelationship( int classify, int disposition, int priority ); HSCRIPT ScriptGetVehicleEntity(); From a131bf42a6b539cdaf5cfc6f131dc1364a1d341f Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Fri, 24 Nov 2023 10:46:45 -0600 Subject: [PATCH 58/90] Fix unarmed citizens not picking up weapons from racks --- sp/src/game/server/basecombatcharacter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sp/src/game/server/basecombatcharacter.cpp b/sp/src/game/server/basecombatcharacter.cpp index 5d2c692c..a1033586 100644 --- a/sp/src/game/server/basecombatcharacter.cpp +++ b/sp/src/game/server/basecombatcharacter.cpp @@ -3615,7 +3615,7 @@ CBaseEntity *CBaseCombatCharacter::Weapon_FindUsable( const Vector &range ) else if (hl2_episodic.GetBool() && !GetActiveWeapon()) { // Unarmed citizens are conservative in their weapon finding...in Episode One - if (Classify() != CLASS_PLAYER_ALLY_VITAL && Q_strncmp(STRING(gpGlobals->mapname), "ep1_", 4)) + if (Classify() != CLASS_PLAYER_ALLY_VITAL && Q_strncmp(STRING(gpGlobals->mapname), "ep1_", 4) == 0) bConservative = true; } #endif From 5de0ee49bd2acf6679366bead4e113e9e71c023a Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Fri, 24 Nov 2023 10:56:56 -0600 Subject: [PATCH 59/90] Fix a few syntax mistakes involving bool functions returning NULL --- sp/src/game/server/filters.cpp | 8 ++++---- sp/src/game/server/triggers.h | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/sp/src/game/server/filters.cpp b/sp/src/game/server/filters.cpp index c15f8eee..e95ba310 100644 --- a/sp/src/game/server/filters.cpp +++ b/sp/src/game/server/filters.cpp @@ -162,10 +162,10 @@ void CBaseFilter::InputSetField( inputdata_t& inputdata ) #ifdef MAPBASE_VSCRIPT bool CBaseFilter::ScriptPassesFilter( HSCRIPT pCaller, HSCRIPT pEntity ) { return PassesFilter( ToEnt(pCaller), ToEnt(pEntity) ); } -bool CBaseFilter::ScriptPassesDamageFilter( HSCRIPT pCaller, HSCRIPT pInfo ) { return (pInfo) ? PassesDamageFilter( ToEnt( pCaller ), *const_cast(HScriptToClass( pInfo )) ) : NULL; } -bool CBaseFilter::ScriptPassesFinalDamageFilter( HSCRIPT pCaller, HSCRIPT pInfo ) { return (pInfo) ? PassesFinalDamageFilter( ToEnt( pCaller ), *const_cast(HScriptToClass( pInfo )) ) : NULL; } -bool CBaseFilter::ScriptBloodAllowed( HSCRIPT pCaller, HSCRIPT pInfo ) { return (pInfo) ? BloodAllowed( ToEnt( pCaller ), *const_cast(HScriptToClass( pInfo )) ) : NULL; } -bool CBaseFilter::ScriptDamageMod( HSCRIPT pCaller, HSCRIPT pInfo ) { return (pInfo) ? DamageMod( ToEnt( pCaller ), *HScriptToClass( pInfo ) ) : NULL; } +bool CBaseFilter::ScriptPassesDamageFilter( HSCRIPT pCaller, HSCRIPT pInfo ) { return (pInfo) ? PassesDamageFilter( ToEnt( pCaller ), *const_cast(HScriptToClass( pInfo )) ) : false; } +bool CBaseFilter::ScriptPassesFinalDamageFilter( HSCRIPT pCaller, HSCRIPT pInfo ) { return (pInfo) ? PassesFinalDamageFilter( ToEnt( pCaller ), *const_cast(HScriptToClass( pInfo )) ) : false; } +bool CBaseFilter::ScriptBloodAllowed( HSCRIPT pCaller, HSCRIPT pInfo ) { return (pInfo) ? BloodAllowed( ToEnt( pCaller ), *const_cast(HScriptToClass( pInfo )) ) : false; } +bool CBaseFilter::ScriptDamageMod( HSCRIPT pCaller, HSCRIPT pInfo ) { return (pInfo) ? DamageMod( ToEnt( pCaller ), *HScriptToClass( pInfo ) ) : false; } #endif diff --git a/sp/src/game/server/triggers.h b/sp/src/game/server/triggers.h index 47bf82be..b6760677 100644 --- a/sp/src/game/server/triggers.h +++ b/sp/src/game/server/triggers.h @@ -96,7 +96,7 @@ public: bool PointIsWithin( const Vector &vecPoint ); #ifdef MAPBASE_VSCRIPT - bool ScriptPassesTriggerFilters( HSCRIPT hOther ) { return ToEnt(hOther) ? PassesTriggerFilters( ToEnt(hOther) ) : NULL; } + bool ScriptPassesTriggerFilters( HSCRIPT hOther ) { return ToEnt(hOther) ? PassesTriggerFilters( ToEnt(hOther) ) : false; } HSCRIPT ScriptGetTouchedEntityOfType( const char *sClassName ) { return ToHScript( GetTouchedEntityOfType(sClassName) ); } void ScriptGetTouchingEntities( HSCRIPT hTable ); From e34e0d3b107731b83e79ddc4a0d1ac388e9599fb Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Fri, 24 Nov 2023 11:08:41 -0600 Subject: [PATCH 60/90] Updated README --- README | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/README b/README index 6215f047..ead59b89 100644 --- a/README +++ b/README @@ -1,6 +1,6 @@ //========================================================================================================================= - Mapbase v7.1 - Source 2013 + Mapbase v7.2 - Source 2013 https://github.com/mapbase-source/source-sdk-2013 https://www.moddb.com/mods/mapbase @@ -112,6 +112,12 @@ Direct contributions: - https://github.com/mapbase-source/source-sdk-2013/issues/201 (env_projectedtexture shadow filter keyvalue from celisej567) - https://github.com/mapbase-source/source-sdk-2013/pull/193 (RTB:R info_particle_system_coordinate by arbabf and Iridium77) - https://github.com/mapbase-source/source-sdk-2013/pull/193 (Infinite prop_interactable cooldown by arbabf) +- https://github.com/mapbase-source/source-sdk-2013/pull/229 (Extended point_bugbait functionality by arbabf) +- https://github.com/mapbase-source/source-sdk-2013/pull/236 (Toggleable prop sprinting by Crimson-X1) +- https://github.com/mapbase-source/source-sdk-2013/pull/237 (Commander goal trace fix by Agrimar) +- https://github.com/mapbase-source/source-sdk-2013/pull/245 (ViewPunch random fix by Mr0maks) +- https://github.com/mapbase-source/source-sdk-2013/pull/248 (soundlevel_t conversation warning fix by Mechami) +- https://github.com/mapbase-source/mapbase-game-src/pull/1 (Advanced video options duplicate field name fix by arbabf; This is asset-based and not reflected in the code) - Demo autorecord code provided by Klems - cc_emit crash fix provided by 1upD - Custom HL2 ammo crate models created by Rykah (Textures created by Blixibon; This is asset-based and, aside from the SLAM crate, not reflected in the code) @@ -137,6 +143,7 @@ Direct contributions: =-- https://github.com/mapbase-source/source-sdk-2013/pull/192 (VScript hook manager and fixes) =-- https://github.com/mapbase-source/source-sdk-2013/pull/206 (Fix CScriptNetMsgHelper::WriteEntity()) =-- https://github.com/mapbase-source/source-sdk-2013/pull/213 (VScript HUD visibility control, optimizations for 3D skybox angles/fake worldportals) +=-- https://github.com/mapbase-source/source-sdk-2013/pull/229 (VScript VGUI HUD viewport parenting, game_text and vgui_text_display VScript font fallback) == Contributions from z33ky: =-- https://github.com/mapbase-source/source-sdk-2013/pull/21 (Various GCC/Linux compilation fixes) @@ -156,6 +163,9 @@ Direct contributions: =-- https://github.com/mapbase-source/source-sdk-2013/pull/184 (Projected texture horizontal FOV shadow fix) =-- https://github.com/mapbase-source/source-sdk-2013/pull/185 (Fix enemyfinders becoming visible when they wake) =-- https://github.com/mapbase-source/source-sdk-2013/pull/186 (Fix for brightly glowing teeth) +=-- https://github.com/mapbase-source/source-sdk-2013/pull/183 (Enhanced custom weapons support) +=-- https://github.com/mapbase-source/source-sdk-2013/pull/230 (Caption fixes) +=-- https://github.com/mapbase-source/source-sdk-2013/pull/231 (Sentence source bug fix) //------------------------------------------------------------------------------------------------------------------------- From b97a58e9fa4239af10a98950efb68e66b4dce435 Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Sun, 17 Dec 2023 11:55:51 -0600 Subject: [PATCH 61/90] Add optional upwards viewkick to HL2 weapon_pistol --- sp/src/game/server/hl2/weapon_pistol.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/sp/src/game/server/hl2/weapon_pistol.cpp b/sp/src/game/server/hl2/weapon_pistol.cpp index 9819f748..8a49f366 100644 --- a/sp/src/game/server/hl2/weapon_pistol.cpp +++ b/sp/src/game/server/hl2/weapon_pistol.cpp @@ -526,6 +526,10 @@ bool CWeaponPistol::Reload( void ) return fRet; } +#ifdef MAPBASE +ConVar weapon_pistol_upwards_viewkick( "weapon_pistol_upwards_viewkick", "0" ); +#endif + //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- @@ -538,7 +542,11 @@ void CWeaponPistol::AddViewKick( void ) QAngle viewPunch; +#ifdef MAPBASE + viewPunch.x = weapon_pistol_upwards_viewkick.GetBool() ? random->RandomFloat( -0.5f, -0.25f ) : random->RandomFloat( 0.25f, 0.5f ); +#else viewPunch.x = random->RandomFloat( 0.25f, 0.5f ); +#endif viewPunch.y = random->RandomFloat( -.6f, .6f ); viewPunch.z = 0.0f; From e44f60e8d8c06df37f691e1cd5d976a75c90c82f Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Wed, 3 Jan 2024 12:47:29 -0600 Subject: [PATCH 62/90] Add new VScript and particle anim events --- sp/src/game/client/c_baseanimating.cpp | 102 +++++++++++++++++++++++++ sp/src/game/server/baseanimating.cpp | 14 +++- sp/src/game/shared/eventlist.cpp | 8 ++ sp/src/game/shared/eventlist.h | 8 ++ 4 files changed, 131 insertions(+), 1 deletion(-) diff --git a/sp/src/game/client/c_baseanimating.cpp b/sp/src/game/client/c_baseanimating.cpp index 7afc4d22..edde6282 100644 --- a/sp/src/game/client/c_baseanimating.cpp +++ b/sp/src/game/client/c_baseanimating.cpp @@ -4042,6 +4042,92 @@ void C_BaseAnimating::FireEvent( const Vector& origin, const QAngle& angles, int } break; +#ifdef MAPBASE // From Alien Swarm SDK + case AE_CL_STOP_PARTICLE_EFFECT: + { + char token[256]; + char szParticleEffect[256]; + + // Get the particle effect name + const char *p = options; + p = nexttoken(token, p, ' ', sizeof(token)); + if ( token ) + { + Q_strncpy( szParticleEffect, token, sizeof(szParticleEffect) ); + } + + // Get the attachment point index + p = nexttoken(token, p, ' ', sizeof(token)); + bool bStopInstantly = ( token && !Q_stricmp( token, "instantly" ) ); + + ParticleProp()->StopParticlesNamed( szParticleEffect, bStopInstantly ); + } + break; + + case AE_CL_ADD_PARTICLE_EFFECT_CP: + { + int iControlPoint = 1; + int iAttachment = -1; + int iAttachType = PATTACH_ABSORIGIN_FOLLOW; + int iEffectIndex = -1; + char token[256]; + char szParticleEffect[256]; + + // Get the particle effect name + const char *p = options; + p = nexttoken(token, p, ' ', sizeof(token)); + if ( token ) + { + Q_strncpy( szParticleEffect, token, sizeof(szParticleEffect) ); + } + + // Get the control point number + p = nexttoken(token, p, ' ', sizeof(token)); + if ( token ) + { + iControlPoint = atoi( token ); + } + + // Get the attachment type + p = nexttoken(token, p, ' ', sizeof(token)); + if ( token ) + { + iAttachType = GetAttachTypeFromString( token ); + if ( iAttachType == -1 ) + { + Warning("Invalid attach type specified for particle effect anim event. Trying to spawn effect '%s' with attach type of '%s'\n", szParticleEffect, token ); + return; + } + } + + // Get the attachment point index + p = nexttoken(token, p, ' ', sizeof(token)); + if ( token ) + { + iAttachment = atoi(token); + + // See if we can find any attachment points matching the name + if ( token[0] != '0' && iAttachment == 0 ) + { + iAttachment = LookupAttachment( token ); + if ( iAttachment == -1 ) + { + Warning("Failed to find attachment point specified for particle effect anim event. Trying to spawn effect '%s' on attachment named '%s'\n", szParticleEffect, token ); + return; + } + } + } + iEffectIndex = ParticleProp()->FindEffect( szParticleEffect ); + if ( iEffectIndex == -1 ) + { + Warning("Failed to find specified particle effect. Trying to add CP to '%s' on attachment named '%s'\n", szParticleEffect, token ); + return; + } + ParticleProp()->AddControlPoint( iEffectIndex, iControlPoint, this, (ParticleAttachment_t)iAttachType, iAttachment ); + } + break; +#endif + case AE_CL_PLAYSOUND: { CLocalPlayerFilter filter; @@ -4291,6 +4377,22 @@ void C_BaseAnimating::FireEvent( const Vector& origin, const QAngle& angles, int } break; +#ifdef MAPBASE + case AE_VSCRIPT_RUN: + { + if (!RunScript( options )) + Warning( "%s failed to run AE_VSCRIPT_RUN on client with \"%s\"\n", GetDebugName(), options ); + } + break; + + case AE_VSCRIPT_RUN_FILE: + { + if (!RunScriptFile( options )) + Warning( "%s failed to run AE_VSCRIPT_RUN_FILE on client with \"%s\"\n", GetDebugName(), options ); + } + break; +#endif + default: break; } diff --git a/sp/src/game/server/baseanimating.cpp b/sp/src/game/server/baseanimating.cpp index be4d6b73..0e226209 100644 --- a/sp/src/game/server/baseanimating.cpp +++ b/sp/src/game/server/baseanimating.cpp @@ -1333,7 +1333,7 @@ void CBaseAnimating::HandleAnimEvent( animevent_t *pEvent ) #ifdef MAPBASE else if ( pEvent->event == AE_NPC_RESPONSE ) { - if (!MyNPCPointer()->GetExpresser()->IsSpeaking()) + if (MyNPCPointer() && MyNPCPointer()->GetExpresser() && !MyNPCPointer()->GetExpresser()->IsSpeaking()) { DispatchResponse( pEvent->options ); } @@ -1344,6 +1344,18 @@ void CBaseAnimating::HandleAnimEvent( animevent_t *pEvent ) DispatchResponse( pEvent->options ); return; } + else if ( pEvent->event == AE_VSCRIPT_RUN ) + { + if (!RunScript( pEvent->options )) + Warning( "%s failed to run AE_VSCRIPT_RUN on server with \"%s\"\n", GetDebugName(), pEvent->options ); + return; + } + else if ( pEvent->event == AE_VSCRIPT_RUN_FILE ) + { + if (!RunScriptFile( pEvent->options )) + Warning( "%s failed to run AE_VSCRIPT_RUN_FILE on server with \"%s\"\n", GetDebugName(), pEvent->options ); + return; + } #endif else if ( pEvent->event == AE_RAGDOLL ) { diff --git a/sp/src/game/shared/eventlist.cpp b/sp/src/game/shared/eventlist.cpp index 043293bc..2776aa8f 100644 --- a/sp/src/game/shared/eventlist.cpp +++ b/sp/src/game/shared/eventlist.cpp @@ -233,6 +233,11 @@ void EventList_RegisterSharedEvents( void ) REGISTER_SHARED_ANIMEVENT( AE_SV_DUSTTRAIL, AE_TYPE_SERVER ); REGISTER_SHARED_ANIMEVENT( AE_CL_CREATE_PARTICLE_EFFECT, AE_TYPE_CLIENT ); +#ifdef MAPBASE // From Alien Swarm SDK + REGISTER_SHARED_ANIMEVENT( AE_CL_STOP_PARTICLE_EFFECT, AE_TYPE_CLIENT ); + REGISTER_SHARED_ANIMEVENT( AE_CL_ADD_PARTICLE_EFFECT_CP, AE_TYPE_CLIENT ); + //REGISTER_SHARED_ANIMEVENT( AE_CL_CREATE_PARTICLE_BRASS, AE_TYPE_CLIENT ); +#endif REGISTER_SHARED_ANIMEVENT( AE_RAGDOLL, AE_TYPE_SERVER ); @@ -252,5 +257,8 @@ void EventList_RegisterSharedEvents( void ) #ifdef MAPBASE REGISTER_SHARED_ANIMEVENT( AE_NPC_RESPONSE, AE_TYPE_SERVER ); REGISTER_SHARED_ANIMEVENT( AE_NPC_RESPONSE_FORCED, AE_TYPE_SERVER ); + + REGISTER_SHARED_ANIMEVENT( AE_VSCRIPT_RUN, AE_TYPE_CLIENT | AE_TYPE_SERVER ); + REGISTER_SHARED_ANIMEVENT( AE_VSCRIPT_RUN_FILE, AE_TYPE_CLIENT | AE_TYPE_SERVER ); #endif } \ No newline at end of file diff --git a/sp/src/game/shared/eventlist.h b/sp/src/game/shared/eventlist.h index ee8b38df..7d810bd4 100644 --- a/sp/src/game/shared/eventlist.h +++ b/sp/src/game/shared/eventlist.h @@ -69,6 +69,11 @@ typedef enum AE_SV_DUSTTRAIL, AE_CL_CREATE_PARTICLE_EFFECT, +#ifdef MAPBASE // From Alien Swarm SDK + AE_CL_STOP_PARTICLE_EFFECT, + AE_CL_ADD_PARTICLE_EFFECT_CP, + //AE_CL_CREATE_PARTICLE_BRASS, +#endif AE_RAGDOLL, @@ -88,6 +93,9 @@ typedef enum #ifdef MAPBASE AE_NPC_RESPONSE, // Play a response system concept if we're not speaking AE_NPC_RESPONSE_FORCED, // Always play a response system concept + + AE_VSCRIPT_RUN, // Run vscript code (server + client) + AE_VSCRIPT_RUN_FILE, // Run vscript file (server + client) #endif LAST_SHARED_ANIMEVENT, From 0d37ded0eb0cc0cd3bd716419dc8e72a0865abf2 Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Wed, 3 Jan 2024 12:49:10 -0600 Subject: [PATCH 63/90] Add OnScriptEvent outputs to ai_hint --- sp/src/game/server/ai_basenpc.cpp | 6 ++++++ sp/src/game/server/ai_hint.cpp | 24 ++++++++++++++++++++++++ sp/src/game/server/ai_hint.h | 7 +++++++ 3 files changed, 37 insertions(+) diff --git a/sp/src/game/server/ai_basenpc.cpp b/sp/src/game/server/ai_basenpc.cpp index acfebf14..81229c5d 100644 --- a/sp/src/game/server/ai_basenpc.cpp +++ b/sp/src/game/server/ai_basenpc.cpp @@ -9526,6 +9526,12 @@ void CAI_BaseNPC::HandleAnimEvent( animevent_t *pEvent ) { m_hCine->FireScriptEvent( atoi( pEvent->options ) ); } +#ifdef MAPBASE + else if ( GetHintNode() ) + { + GetHintNode()->FireScriptEvent( atoi( pEvent->options ) ); + } +#endif else { // FIXME: look so see if it's playing a vcd and fire those instead diff --git a/sp/src/game/server/ai_hint.cpp b/sp/src/game/server/ai_hint.cpp index e9844a01..10d9d728 100644 --- a/sp/src/game/server/ai_hint.cpp +++ b/sp/src/game/server/ai_hint.cpp @@ -902,6 +902,17 @@ BEGIN_DATADESC( CAI_Hint ) DEFINE_OUTPUT( m_OnNPCStartedUsing, "OnNPCStartedUsing" ), DEFINE_OUTPUT( m_OnNPCStoppedUsing, "OnNPCStoppedUsing" ), +#ifdef MAPBASE + DEFINE_OUTPUT( m_OnScriptEvent[0], "OnScriptEvent01" ), + DEFINE_OUTPUT( m_OnScriptEvent[1], "OnScriptEvent02" ), + DEFINE_OUTPUT( m_OnScriptEvent[2], "OnScriptEvent03" ), + DEFINE_OUTPUT( m_OnScriptEvent[3], "OnScriptEvent04" ), + DEFINE_OUTPUT( m_OnScriptEvent[4], "OnScriptEvent05" ), + DEFINE_OUTPUT( m_OnScriptEvent[5], "OnScriptEvent06" ), + DEFINE_OUTPUT( m_OnScriptEvent[6], "OnScriptEvent07" ), + DEFINE_OUTPUT( m_OnScriptEvent[7], "OnScriptEvent08" ), +#endif + END_DATADESC( ); #ifdef MAPBASE_VSCRIPT @@ -1705,6 +1716,19 @@ void CAI_Hint::NPCStoppedUsing( CAI_BaseNPC *pNPC ) m_OnNPCStoppedUsing.Set( pNPC, pNPC, this ); } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CAI_Hint::FireScriptEvent( int nEvent ) +{ + if ( ( nEvent >= 1 ) && ( nEvent <= 8 ) ) + { + m_OnScriptEvent[nEvent - 1].FireOutput( m_hHintOwner, this ); + } +} +#endif + CON_COMMAND(ai_dump_hints, "") { diff --git a/sp/src/game/server/ai_hint.h b/sp/src/game/server/ai_hint.h index fc23f9c3..b18be429 100644 --- a/sp/src/game/server/ai_hint.h +++ b/sp/src/game/server/ai_hint.h @@ -323,6 +323,9 @@ public: void FixupTargetNode(); void NPCStartedUsing( CAI_BaseNPC *pNPC ); void NPCStoppedUsing( CAI_BaseNPC *pNPC ); +#ifdef MAPBASE + void FireScriptEvent( int nEvent ); +#endif HintIgnoreFacing_t GetIgnoreFacing() const { return m_NodeData.fIgnoreFacing; } @@ -385,6 +388,10 @@ private: float m_nodeFOV; Vector m_vecForward; +#ifdef MAPBASE + COutputEvent m_OnScriptEvent[8]; +#endif + // The next hint in list of all hints friend class CAI_HintManager; From fd149ff1616b2dd6c923024b700346de4908c5b5 Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Wed, 3 Jan 2024 13:19:38 -0600 Subject: [PATCH 64/90] Add new dynamic interaction keyvalues and utilities --- sp/src/game/server/ai_activity.cpp | 17 ++ sp/src/game/server/ai_basenpc.cpp | 285 +++++++++++++++++++++++------ sp/src/game/server/ai_basenpc.h | 33 +++- 3 files changed, 270 insertions(+), 65 deletions(-) diff --git a/sp/src/game/server/ai_activity.cpp b/sp/src/game/server/ai_activity.cpp index da12eabe..e20bfc48 100644 --- a/sp/src/game/server/ai_activity.cpp +++ b/sp/src/game/server/ai_activity.cpp @@ -76,6 +76,23 @@ int CAI_BaseNPC::GetActivityID(const char* actName) return m_pActivitySR->GetStringID(actName); } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: Gets an activity ID or registers a new private one if it doesn't exist +//----------------------------------------------------------------------------- +int CAI_BaseNPC::GetOrRegisterActivity( const char *actName ) +{ + int actID = GetActivityID( actName ); + if (actID == ACT_INVALID) + { + actID = ActivityList_RegisterPrivateActivity( actName ); + AddActivityToSR( actName, actID ); + } + + return actID; +} +#endif + #define ADD_ACTIVITY_TO_SR(activityname) AddActivityToSR(#activityname,activityname) //----------------------------------------------------------------------------- diff --git a/sp/src/game/server/ai_basenpc.cpp b/sp/src/game/server/ai_basenpc.cpp index acfebf14..acb62cc8 100644 --- a/sp/src/game/server/ai_basenpc.cpp +++ b/sp/src/game/server/ai_basenpc.cpp @@ -12443,7 +12443,11 @@ BEGIN_SIMPLE_DATADESC( ScriptedNPCInteraction_t ) DEFINE_FIELD( bValidOnCurrentEnemy, FIELD_BOOLEAN ), DEFINE_FIELD( flNextAttemptTime, FIELD_TIME ), #ifdef MAPBASE - DEFINE_FIELD( MiscCriteria, FIELD_STRING ),//DEFINE_UTLVECTOR( MiscCriteria, FIELD_EMBEDDED ), + DEFINE_EMBEDDED_ARRAY( sTheirPhases, SNPCINT_NUM_PHASES ), + DEFINE_FIELD( bHasSeparateSequenceNames, FIELD_BOOLEAN ), + DEFINE_FIELD( flMaxAngleDiff, FIELD_FLOAT ), + DEFINE_FIELD( iszRelatedInteractions, FIELD_STRING ), + DEFINE_FIELD( MiscCriteria, FIELD_STRING ), #endif END_DATADESC() @@ -14847,32 +14851,60 @@ void CAI_BaseNPC::ParseScriptedNPCInteractions(void) else if (!Q_strncmp(szName, "entry_sequence", 14)) sInteraction.sPhases[SNPCINT_ENTRY].iszSequence = AllocPooledString(szValue); else if (!Q_strncmp(szName, "entry_activity", 14)) - sInteraction.sPhases[SNPCINT_ENTRY].iActivity = GetActivityID(szValue); + sInteraction.sPhases[SNPCINT_ENTRY].iActivity = GetOrRegisterActivity(szValue); else if (!Q_strncmp(szName, "sequence", 8)) sInteraction.sPhases[SNPCINT_SEQUENCE].iszSequence = AllocPooledString(szValue); else if (!Q_strncmp(szName, "activity", 8)) - sInteraction.sPhases[SNPCINT_SEQUENCE].iActivity = GetActivityID(szValue); + sInteraction.sPhases[SNPCINT_SEQUENCE].iActivity = GetOrRegisterActivity(szValue); else if (!Q_strncmp(szName, "exit_sequence", 13)) sInteraction.sPhases[SNPCINT_EXIT].iszSequence = AllocPooledString(szValue); else if (!Q_strncmp(szName, "exit_activity", 13)) - sInteraction.sPhases[SNPCINT_EXIT].iActivity = GetActivityID(szValue); + sInteraction.sPhases[SNPCINT_EXIT].iActivity = GetOrRegisterActivity(szValue); + + else if (!Q_strncmp(szName, "their_", 6)) + { + szName += 6; + sInteraction.bHasSeparateSequenceNames = true; + + if (!Q_strncmp(szName, "entry_sequence", 14)) + sInteraction.sTheirPhases[SNPCINT_ENTRY].iszSequence = AllocPooledString(szValue); + else if (!Q_strncmp(szName, "entry_activity", 14)) + sInteraction.sTheirPhases[SNPCINT_ENTRY].iActivity = GetOrRegisterActivity(szValue); + + else if (!Q_strncmp(szName, "sequence", 8)) + sInteraction.sTheirPhases[SNPCINT_SEQUENCE].iszSequence = AllocPooledString(szValue); + else if (!Q_strncmp(szName, "activity", 8)) + sInteraction.sTheirPhases[SNPCINT_SEQUENCE].iActivity = GetOrRegisterActivity(szValue); + + else if (!Q_strncmp(szName, "exit_sequence", 13)) + sInteraction.sTheirPhases[SNPCINT_EXIT].iszSequence = AllocPooledString(szValue); + else if (!Q_strncmp(szName, "exit_activity", 13)) + sInteraction.sTheirPhases[SNPCINT_EXIT].iActivity = GetOrRegisterActivity(szValue); + } else if (!Q_strncmp(szName, "delay", 5)) sInteraction.flDelay = atof(szValue); else if (!Q_strncmp(szName, "origin_max_delta", 16)) sInteraction.flDistSqr = atof(szValue); + else if (!Q_strncmp(szName, "angles_max_diff", 15)) + sInteraction.flMaxAngleDiff = atof(szValue); else if (!Q_strncmp(szName, "loop_in_action", 14) && !FStrEq(szValue, "0")) sInteraction.iFlags |= SCNPC_FLAG_LOOP_IN_ACTION; else if (!Q_strncmp(szName, "dont_teleport_at_end", 20)) { - if (!Q_stricmp(szValue, "me") || !Q_stricmp(szValue, "both")) + if (!Q_stricmp(szValue, "me")) sInteraction.iFlags |= SCNPC_FLAG_DONT_TELEPORT_AT_END_ME; - else if (!Q_stricmp(szValue, "them") || !Q_stricmp(szValue, "both")) + else if (!Q_stricmp(szValue, "them")) sInteraction.iFlags |= SCNPC_FLAG_DONT_TELEPORT_AT_END_THEM; + else if (!Q_stricmp( szValue, "both" )) + { + sInteraction.iFlags |= SCNPC_FLAG_DONT_TELEPORT_AT_END_ME; + sInteraction.iFlags |= SCNPC_FLAG_DONT_TELEPORT_AT_END_THEM; + } } else if (!Q_strncmp(szName, "needs_weapon", 12)) @@ -14899,6 +14931,11 @@ void CAI_BaseNPC::ParseScriptedNPCInteractions(void) sInteraction.iszTheirWeapon = AllocPooledString(szValue); } + else if (!Q_strncmp(szName, "related_interactions", 20)) + { + sInteraction.iszRelatedInteractions = AllocPooledString(szValue); + } + // Add anything else to our miscellaneous criteria else { @@ -15130,8 +15167,23 @@ void CAI_BaseNPC::AddScriptedNPCInteraction( ScriptedNPCInteraction_t *pInteract //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- -const char *CAI_BaseNPC::GetScriptedNPCInteractionSequence( ScriptedNPCInteraction_t *pInteraction, int iPhase ) +const char *CAI_BaseNPC::GetScriptedNPCInteractionSequence( ScriptedNPCInteraction_t *pInteraction, int iPhase, bool bOtherNPC ) { +#ifdef MAPBASE + if (bOtherNPC && pInteraction->bHasSeparateSequenceNames) + { + // Check unique phases + if ( pInteraction->sTheirPhases[iPhase].iActivity != ACT_INVALID ) + { + int iSequence = SelectWeightedSequence( (Activity)pInteraction->sTheirPhases[iPhase].iActivity ); + return GetSequenceName( iSequence ); + } + + if ( pInteraction->sTheirPhases[iPhase].iszSequence != NULL_STRING ) + return STRING(pInteraction->sTheirPhases[iPhase].iszSequence); + } +#endif + if ( pInteraction->sPhases[iPhase].iActivity != ACT_INVALID ) { int iSequence = SelectWeightedSequence( (Activity)pInteraction->sPhases[iPhase].iActivity ); @@ -15225,6 +15277,36 @@ void CAI_BaseNPC::StartScriptedNPCInteraction( CAI_BaseNPC *pOtherNPC, ScriptedN // Setup next attempt pInteraction->flNextAttemptTime = gpGlobals->curtime + pInteraction->flDelay + RandomFloat(-2,2); +#ifdef MAPBASE + if (pInteraction->iszRelatedInteractions != NULL_STRING) + { + // Delay related interactions as well + char szRelatedInteractions[256]; + Q_strncpy( szRelatedInteractions, STRING( pInteraction->iszRelatedInteractions ), sizeof( szRelatedInteractions ) ); + + char *pszInteraction = strtok( szRelatedInteractions, "," ); + while (pszInteraction) + { + bool bWildCard = Matcher_ContainsWildcard( pszInteraction ); + + for ( int i = 0; i < m_ScriptedInteractions.Count(); i++ ) + { + ScriptedNPCInteraction_t *pOtherInteraction = &m_ScriptedInteractions[i]; + + if ( Matcher_NamesMatch( pszInteraction, STRING( pOtherInteraction->iszInteractionName ) ) && pOtherInteraction != pInteraction ) + { + pOtherInteraction->flNextAttemptTime = pInteraction->flNextAttemptTime; + + // Not looking for multiple + if (!bWildCard) + break; + } + } + + pszInteraction = strtok( NULL, "," ); + } + } +#endif // Spawn a scripted sequence for this NPC to play the interaction anim CAI_ScriptedSequence *pMySequence = (CAI_ScriptedSequence*)CreateEntityByName( "scripted_sequence" ); @@ -15256,6 +15338,15 @@ void CAI_BaseNPC::StartScriptedNPCInteraction( CAI_BaseNPC *pOtherNPC, ScriptedN CAI_ScriptedSequence *pTheirSequence = NULL; if ( pOtherNPC ) { +#ifdef MAPBASE + if (pInteraction->bHasSeparateSequenceNames) + { + pszEntrySequence = GetScriptedNPCInteractionSequence( pInteraction, SNPCINT_ENTRY, true ); + pszSequence = GetScriptedNPCInteractionSequence( pInteraction, SNPCINT_SEQUENCE, true ); + pszExitSequence = GetScriptedNPCInteractionSequence( pInteraction, SNPCINT_EXIT, true ); + } +#endif + pTheirSequence = (CAI_ScriptedSequence*)CreateEntityByName( "scripted_sequence" ); pTheirSequence->KeyValue( "m_iszEntry", pszEntrySequence ); pTheirSequence->KeyValue( "m_iszPlay", pszSequence ); @@ -15367,7 +15458,7 @@ bool CAI_BaseNPC::CanRunAScriptedNPCInteraction( bool bForced ) return false; // Default AI prevents interactions while melee attacking, but not ranged attacking - if ( IsCurSchedule( SCHED_MELEE_ATTACK1 ) || IsCurSchedule( SCHED_MELEE_ATTACK2 ) ) + if ( ( IsCurSchedule( SCHED_MELEE_ATTACK1 ) || IsCurSchedule( SCHED_MELEE_ATTACK2 ) ) && !CanStartDynamicInteractionDuringMelee() ) return false; } @@ -15556,8 +15647,14 @@ void CAI_BaseNPC::CalculateValidEnemyInteractions( void ) if (bSame) continue; -#endif + // Resolve the activity or sequence, and make sure our enemy has it + const char *pszSequence = GetScriptedNPCInteractionSequence( pInteraction, SNPCINT_SEQUENCE, true ); + if ( !pszSequence ) + continue; + if ( pNPC->LookupSequence( pszSequence ) == -1 ) + continue; +#else // Use sequence? or activity? if ( pInteraction->sPhases[SNPCINT_SEQUENCE].iActivity != ACT_INVALID ) { @@ -15573,53 +15670,6 @@ void CAI_BaseNPC::CalculateValidEnemyInteractions( void ) if ( pNPC->LookupSequence( STRING(pInteraction->sPhases[SNPCINT_SEQUENCE].iszSequence) ) == -1 ) continue; } - -#ifdef MAPBASE - if (pInteraction->MiscCriteria != NULL_STRING) - { - // Test against response system criteria - AI_CriteriaSet set; - ModifyOrAppendCriteria( set ); - CBasePlayer *pPlayer = UTIL_GetLocalPlayer(); - if( pPlayer ) - pPlayer->ModifyOrAppendPlayerCriteria( set ); - ReAppendContextCriteria( set ); - - DevMsg("Testing %s misc criteria\n", STRING(pInteraction->MiscCriteria)); - - int index; - const char *criteriavalue; - char key[128]; - char value[128]; - const char *p = STRING(pInteraction->MiscCriteria); - while ( p ) - { -#ifdef NEW_RESPONSE_SYSTEM - p = SplitContext( p, key, sizeof( key ), value, sizeof( value ), NULL, STRING(pInteraction->MiscCriteria) ); -#else - p = SplitContext( p, key, sizeof( key ), value, sizeof( value ), NULL ); -#endif - - index = set.FindCriterionIndex(key); - if (index != -1) - { - criteriavalue = set.GetValue(index); - if (!Matcher_Match(value, criteriavalue)) - { - continue; - } - } - else - { - // Test with empty string in case our criteria is != or something - criteriavalue = ""; - if (!Matcher_Match(value, criteriavalue)) - { - continue; - } - } - } - } #endif pInteraction->bValidOnCurrentEnemy = true; @@ -15789,7 +15839,95 @@ bool CAI_BaseNPC::InteractionIsAllowed( CAI_BaseNPC *pOtherNPC, ScriptedNPCInter return true; // m_iDynamicInteractionsAllowed == TRS_FALSE case is already handled in CanRunAScriptedNPCInteraction(). - return !(pInteraction->iFlags & SCNPC_FLAG_MAPBASE_ADDITION && m_iDynamicInteractionsAllowed == TRS_NONE); + if (pInteraction->iFlags & SCNPC_FLAG_MAPBASE_ADDITION && m_iDynamicInteractionsAllowed == TRS_NONE) + return false; + + // Test misc. criteria here since some of it may not have been valid on initial calculation, but could be now + if (pInteraction->MiscCriteria != NULL_STRING) + { + // Test against response system criteria + AI_CriteriaSet set; + ModifyOrAppendCriteria( set ); + CBasePlayer *pPlayer = UTIL_GetLocalPlayer(); + if (pPlayer) + pPlayer->ModifyOrAppendPlayerCriteria( set ); + + // Get criteria from target if we want it + if ( V_strstr( STRING( pInteraction->MiscCriteria ), "their_" ) ) + { + // Currently, in order to get everything which might be desired, we call the other NPC's ModifyOrAppendCriteria. + // We put it in a separate criteria set, then assign a prefix and append it to the main set, similar to how contexts are appended. + // This includes a few global criterions which we might not need, so we throw them out before they're merged. + // This isn't a very efficient solution, but there are no better options available without rewriting parts of the response criteria system. + AI_CriteriaSet theirSet; + pOtherNPC->ModifyOrAppendCriteria( theirSet ); + + set.EnsureCapacity( (theirSet.GetCount()-2) + set.GetCount() ); // We know we'll be throwing out 2 global criterions + + char sz[ 128 ]; + for ( int i = 0; i < theirSet.GetCount(); i++ ) + { + const char *name = theirSet.GetName( i ); + const char *value = theirSet.GetValue( i ); + + if (FStrEq( name, "map" ) || FStrEq( name, "episodic" ) || FStrEq( name, "is_console" ) + || FStrEq( name, "month" ) || FStrEq( name, "day" ) + || FStrEq( name, "is_console" ) || FStrEq( name, "is_pc" ) + || V_strnicmp( name, "world", 5 ) == 0) + { + // Global criterion, ignore + continue; + } + + Q_snprintf( sz, sizeof( sz ), "their_%s", name ); + + if (ai_debug_dyninteractions.GetInt() == 3) + Msg( "%i: %s -> %s:%s\n", i, name, sz, value ); + + set.AppendCriteria( sz, value ); + } + + // Append this afterwards because it has its own prefix system + pOtherNPC->AppendContextToCriteria( set, "their_" ); + } + + ReAppendContextCriteria( set ); + + int index; + const char *criteriavalue; + char key[128]; + char value[128]; + const char *p = STRING( pInteraction->MiscCriteria ); + while ( p ) + { +#ifdef NEW_RESPONSE_SYSTEM + p = SplitContext( p, key, sizeof( key ), value, sizeof( value ), NULL, STRING( pInteraction->MiscCriteria ) ); +#else + p = SplitContext( p, key, sizeof( key ), value, sizeof( value ), NULL ); +#endif + + index = set.FindCriterionIndex( key ); + if (index != -1) + { + criteriavalue = set.GetValue( index ); + if (!Matcher_Match( value, criteriavalue )) + { + return false; + } + } + else + { + // Test with empty string in case our criteria is != or something + criteriavalue = ""; + if (!Matcher_Match( value, criteriavalue )) + { + return false; + } + } + } + } + + return true; } #endif @@ -15855,14 +15993,28 @@ bool CAI_BaseNPC::InteractionCouldStart( CAI_BaseNPC *pOtherNPC, ScriptedNPCInte for ( int ang = 0; ang < 3; ang++ ) { float flAngDiff = AngleDiff( angEnemyAngles[ang], angAngles[ang] ); +#ifdef MAPBASE + if ( fabs(flAngDiff) > pInteraction->flMaxAngleDiff ) +#else if ( fabs(flAngDiff) > DSS_MAX_ANGLE_DIFF ) +#endif { bMatches = false; break; } } if ( !bMatches ) + { +#ifdef MAPBASE + if ( bDebug ) + { + Msg(" %s angle not 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) ); + Msg(" diff: (%0.2f, %0.2f, %0.2f)\n", AngleDiff( angEnemyAngles.x, angAngles.x ), AngleDiff( angEnemyAngles.y, angAngles.y ), AngleDiff( angEnemyAngles.z, angAngles.z ) ); + } +#endif return false; + } if ( bDebug ) { @@ -15980,6 +16132,25 @@ bool CAI_BaseNPC::HasInteractionCantDie( void ) return ( m_bCannotDieDuringInteraction && IsRunningDynamicInteraction() ); } +//----------------------------------------------------------------------------- +// Purpose: Return true if this NPC has valid interactions on the current enemy. +//----------------------------------------------------------------------------- +bool CAI_BaseNPC::HasValidInteractionsOnCurrentEnemy( void ) +{ + if ( !GetEnemy() || !GetEnemy()->IsNPC() ) + return false; + + for ( int i = 0; i < m_ScriptedInteractions.Count(); i++ ) + { + ScriptedNPCInteraction_t *pInteraction = &m_ScriptedInteractions[i]; + + if ( pInteraction->bValidOnCurrentEnemy ) + return true; + } + + return false; +} + //----------------------------------------------------------------------------- // Purpose: // Input : &inputdata - diff --git a/sp/src/game/server/ai_basenpc.h b/sp/src/game/server/ai_basenpc.h index 878a6f81..5aa4ed7b 100644 --- a/sp/src/game/server/ai_basenpc.h +++ b/sp/src/game/server/ai_basenpc.h @@ -425,6 +425,9 @@ struct ScriptedNPCInteraction_t iszTheirWeapon = NULL_STRING; #ifdef MAPBASE vecRelativeEndPos = vec3_origin; + bHasSeparateSequenceNames = false; + flMaxAngleDiff = DSS_MAX_ANGLE_DIFF; + iszRelatedInteractions = NULL_STRING; MiscCriteria = NULL_STRING; #endif @@ -432,6 +435,10 @@ struct ScriptedNPCInteraction_t { sPhases[i].iszSequence = NULL_STRING; sPhases[i].iActivity = ACT_INVALID; +#ifdef MAPBASE + sTheirPhases[i].iszSequence = NULL_STRING; + sTheirPhases[i].iActivity = ACT_INVALID; +#endif } } @@ -459,10 +466,14 @@ struct ScriptedNPCInteraction_t float flNextAttemptTime; #ifdef MAPBASE - // Unrecognized keyvalues are tested against response criteria later. - // This was originally a CUtlVector that carries response contexts, but I couldn't get it working due to some CUtlVector-struct shenanigans. - // It works when we use a single string_t that's split and read each time the code runs, but feel free to improve on this. - string_t MiscCriteria; // CUtlVector + ScriptedNPCInteraction_Phases_t sTheirPhases[SNPCINT_NUM_PHASES]; // The animations played by the target NPC, if they are different + bool bHasSeparateSequenceNames; + + float flMaxAngleDiff; + string_t iszRelatedInteractions; // These interactions will be delayed as well when this interaction is used. + + // Unrecognized keyvalues which are tested against response criteria later. + string_t MiscCriteria; #endif DECLARE_SIMPLE_DATADESC(); @@ -1304,10 +1315,14 @@ private: public: float GetInteractionYaw( void ) const { return m_flInteractionYaw; } + bool IsRunningDynamicInteraction( void ) { return (m_iInteractionState != NPCINT_NOT_RUNNING && (m_hCine != NULL)); } + bool IsActiveDynamicInteraction( void ) { return (m_iInteractionState == NPCINT_RUNNING_ACTIVE && (m_hCine != NULL)); } + CAI_BaseNPC *GetInteractionPartner( void ); + protected: void ParseScriptedNPCInteractions( void ); void AddScriptedNPCInteraction( ScriptedNPCInteraction_t *pInteraction ); - const char *GetScriptedNPCInteractionSequence( ScriptedNPCInteraction_t *pInteraction, int iPhase ); + const char *GetScriptedNPCInteractionSequence( ScriptedNPCInteraction_t *pInteraction, int iPhase, bool bOtherNPC = false ); void StartRunningInteraction( CAI_BaseNPC *pOtherNPC, bool bActive ); void StartScriptedNPCInteraction( CAI_BaseNPC *pOtherNPC, ScriptedNPCInteraction_t *pInteraction, Vector vecOtherOrigin, QAngle angOtherAngles ); void CheckForScriptedNPCInteractions( void ); @@ -1320,17 +1335,16 @@ protected: #endif bool InteractionCouldStart( CAI_BaseNPC *pOtherNPC, ScriptedNPCInteraction_t *pInteraction, Vector &vecOrigin, QAngle &angAngles ); virtual bool CanRunAScriptedNPCInteraction( bool bForced = false ); - bool IsRunningDynamicInteraction( void ) { return (m_iInteractionState != NPCINT_NOT_RUNNING && (m_hCine != NULL)); } - bool IsActiveDynamicInteraction( void ) { return (m_iInteractionState == NPCINT_RUNNING_ACTIVE && (m_hCine != NULL)); } ScriptedNPCInteraction_t *GetRunningDynamicInteraction( void ) { return &(m_ScriptedInteractions[m_iInteractionPlaying]); } void SetInteractionCantDie( bool bCantDie ) { m_bCannotDieDuringInteraction = bCantDie; } bool HasInteractionCantDie( void ); + bool HasValidInteractionsOnCurrentEnemy( void ); + virtual bool CanStartDynamicInteractionDuringMelee() { return false; } void InputForceInteractionWithNPC( inputdata_t &inputdata ); void StartForcedInteraction( CAI_BaseNPC *pNPC, int iInteraction ); void CleanupForcedInteraction( void ); void CalculateForcedInteractionPosition( void ); - CAI_BaseNPC *GetInteractionPartner( void ); private: // Forced interactions @@ -2228,6 +2242,9 @@ public: static const char* GetActivityName (int actID); static void AddActivityToSR(const char *actName, int conID); +#ifdef MAPBASE + static int GetOrRegisterActivity( const char *actName ); +#endif static void AddEventToSR(const char *eventName, int conID); static const char* GetEventName (int actID); From a88091a4d9408863d18f5672362e7b89982b3f7b Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Wed, 3 Jan 2024 13:27:46 -0600 Subject: [PATCH 65/90] Enhancements for interactions which don't test angles (including a new pose parameter to change animations based on angle) --- sp/src/game/server/ai_basenpc.cpp | 31 ++++++++++++++++++++++ sp/src/game/server/ai_basenpc.h | 7 +++++ sp/src/game/server/ai_basenpc_schedule.cpp | 17 +++++++++++- 3 files changed, 54 insertions(+), 1 deletion(-) diff --git a/sp/src/game/server/ai_basenpc.cpp b/sp/src/game/server/ai_basenpc.cpp index acb62cc8..0194fb2a 100644 --- a/sp/src/game/server/ai_basenpc.cpp +++ b/sp/src/game/server/ai_basenpc.cpp @@ -3026,6 +3026,10 @@ void CAI_BaseNPC::PopulatePoseParameters( void ) m_poseAim_Yaw = LookupPoseParameter( "aim_yaw" ); m_poseMove_Yaw = LookupPoseParameter( "move_yaw" ); +#ifdef MAPBASE + m_poseInteractionRelativeYaw = LookupPoseParameter( "interaction_relative_yaw" ); +#endif + BaseClass::PopulatePoseParameters(); } @@ -15370,6 +15374,26 @@ void CAI_BaseNPC::StartScriptedNPCInteraction( CAI_BaseNPC *pOtherNPC, ScriptedN // Tell their sequence to keep their position relative to me pTheirSequence->SetupInteractionPosition( this, pInteraction->matDesiredLocalToWorld ); + +#ifdef MAPBASE + if ( !(pInteraction->iFlags & SCNPC_FLAG_TEST_OTHER_ANGLES) ) + { + // Set up interaction yaw pose if it exists + float flYaw = AngleDistance( angDesired.y, angOtherAngles.y ); + + int nInteractionPose = LookupPoseInteractionRelativeYaw(); + if (nInteractionPose > -1) + { + SetPoseParameter( nInteractionPose, flYaw ); + } + + nInteractionPose = pOtherNPC->LookupPoseInteractionRelativeYaw(); + if (nInteractionPose > -1) + { + pOtherNPC->SetPoseParameter( nInteractionPose, flYaw ); + } + } +#endif } // Spawn both sequences at once @@ -16022,6 +16046,13 @@ bool CAI_BaseNPC::InteractionCouldStart( CAI_BaseNPC *pOtherNPC, ScriptedNPCInte anglemod(angEnemyAngles.x), anglemod(angEnemyAngles.y), anglemod(angEnemyAngles.z), anglemod(angAngles.x), anglemod(angAngles.y), anglemod(angAngles.z) ); } } +#ifdef MAPBASE + else + { + // If we're not using angles, then use the NPC's current angles + angAngles = pOtherNPC->GetAbsAngles(); + } +#endif // TODO: Velocity check, if we're supposed to if ( pInteraction->iFlags & SCNPC_FLAG_TEST_OTHER_VELOCITY ) diff --git a/sp/src/game/server/ai_basenpc.h b/sp/src/game/server/ai_basenpc.h index 5aa4ed7b..81150cce 100644 --- a/sp/src/game/server/ai_basenpc.h +++ b/sp/src/game/server/ai_basenpc.h @@ -849,6 +849,9 @@ protected: // pose parameters int m_poseAim_Pitch; int m_poseAim_Yaw; int m_poseMove_Yaw; +#ifdef MAPBASE + int m_poseInteractionRelativeYaw; +#endif virtual void PopulatePoseParameters( void ); public: @@ -856,6 +859,10 @@ public: // Return the stored pose parameter for "move_yaw" inline int LookupPoseMoveYaw() { return m_poseMove_Yaw; } + +#ifdef MAPBASE + inline int LookupPoseInteractionRelativeYaw() { return m_poseInteractionRelativeYaw; } +#endif //----------------------------------------------------- diff --git a/sp/src/game/server/ai_basenpc_schedule.cpp b/sp/src/game/server/ai_basenpc_schedule.cpp index 61fefb3f..5f57b51d 100644 --- a/sp/src/game/server/ai_basenpc_schedule.cpp +++ b/sp/src/game/server/ai_basenpc_schedule.cpp @@ -1610,6 +1610,12 @@ void CAI_BaseNPC::StartTask( const Task_t *pTask ) // as this should only run with the NPC "receiving" the interaction ScriptedNPCInteraction_t *pInteraction = m_hForcedInteractionPartner->GetRunningDynamicInteraction(); + if ( !(pInteraction->iFlags & SCNPC_FLAG_TEST_OTHER_ANGLES) ) + { + TaskComplete(); + return; + } + // Get our target's origin Vector vecTarget = m_hForcedInteractionPartner->GetAbsOrigin(); @@ -1617,7 +1623,7 @@ void CAI_BaseNPC::StartTask( const Task_t *pTask ) float angInteractionAngle = pInteraction->angRelativeAngles.y; angInteractionAngle += 180.0f; - GetMotor()->SetIdealYaw( CalcIdealYaw( vecTarget ) + angInteractionAngle ); + GetMotor()->SetIdealYaw( AngleNormalize( CalcIdealYaw( vecTarget ) + angInteractionAngle ) ); if (FacingIdeal()) TaskComplete(); @@ -4113,6 +4119,15 @@ void CAI_BaseNPC::RunTask( const Task_t *pTask ) m_hCine->SynchronizeSequence( this ); } } + +#ifdef MAPBASE + if ( IsRunningDynamicInteraction() && m_poseInteractionRelativeYaw > -1 ) + { + // Animations in progress require pose parameters to be set every frame, so keep setting the interaction relative yaw pose. + // The random value is added to help it pass server transmit checks. + SetPoseParameter( m_poseInteractionRelativeYaw, GetPoseParameter( m_poseInteractionRelativeYaw ) + RandomFloat( -0.1f, 0.1f ) ); + } +#endif break; } From 869ec6d0dde316cc6d5f5ad345e50f7cd49a1c69 Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Wed, 3 Jan 2024 13:30:10 -0600 Subject: [PATCH 66/90] Improved debugging for dynamic interaction distance --- sp/src/game/server/ai_basenpc.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/sp/src/game/server/ai_basenpc.cpp b/sp/src/game/server/ai_basenpc.cpp index 0194fb2a..545b7ccf 100644 --- a/sp/src/game/server/ai_basenpc.cpp +++ b/sp/src/game/server/ai_basenpc.cpp @@ -15989,6 +15989,11 @@ bool CAI_BaseNPC::InteractionCouldStart( CAI_BaseNPC *pOtherNPC, ScriptedNPCInte { Msg(" %s distsqr: %0.2f (%0.2f %0.2f %0.2f), desired: <%0.2f (%0.2f %0.2f %0.2f)\n", GetDebugName(), flDistSqr, pOtherNPC->GetAbsOrigin().x, pOtherNPC->GetAbsOrigin().y, pOtherNPC->GetAbsOrigin().z, pInteraction->flDistSqr, vecOrigin.x, vecOrigin.y, vecOrigin.z ); +#ifdef MAPBASE + Vector vecForward, vecRight; + GetVectors( &vecForward, &vecRight, NULL ); + NDebugOverlay::Circle( vecOrigin + Vector(0,0,2), vecForward, vecRight, FastSqrt(pInteraction->flDistSqr), 255, 0, 0, 255, true, 0.1f ); +#endif } } } @@ -16001,6 +16006,11 @@ bool CAI_BaseNPC::InteractionCouldStart( CAI_BaseNPC *pOtherNPC, ScriptedNPCInte 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 ); +#ifdef MAPBASE + Vector vecForward, vecRight; + GetVectors( &vecForward, &vecRight, NULL ); + NDebugOverlay::Circle( vecOrigin + Vector( 0, 0, 2 ), vecForward, vecRight, FastSqrt( pInteraction->flDistSqr ), 255, 0, 0, 255, true, 0.1f ); +#endif if ( pOtherNPC ) { From dc8fe6021d017d482fa055bfdff17f7425d1e890 Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Wed, 3 Jan 2024 13:31:38 -0600 Subject: [PATCH 67/90] Fix dynamic interaction problems caused by walkframe sequence movement --- sp/src/game/server/ai_basenpc.cpp | 35 ++++++++++++++++++++++++++++++- sp/src/game/server/scripted.cpp | 22 ++++++++++++++++++- 2 files changed, 55 insertions(+), 2 deletions(-) diff --git a/sp/src/game/server/ai_basenpc.cpp b/sp/src/game/server/ai_basenpc.cpp index 545b7ccf..ac8590c3 100644 --- a/sp/src/game/server/ai_basenpc.cpp +++ b/sp/src/game/server/ai_basenpc.cpp @@ -16142,6 +16142,7 @@ bool CAI_BaseNPC::InteractionCouldStart( CAI_BaseNPC *pOtherNPC, ScriptedNPCInte if ( bDebug ) { NDebugOverlay::Box( vecPos, GetHullMins(), GetHullMaxs(), 255,0,0, 100, 1.0 ); + NDebugOverlay::HorzArrow( GetAbsOrigin(), vecPos, 16.0f, 255, 0, 0, 255, true, 1.0f ); } return false; } @@ -16149,7 +16150,39 @@ bool CAI_BaseNPC::InteractionCouldStart( CAI_BaseNPC *pOtherNPC, ScriptedNPCInte { //NDebugOverlay::Box( vecPos, GetHullMins(), GetHullMaxs(), 0,255,0, 100, 1.0 ); - NDebugOverlay::Axis( vecPos, angAngles, 20, true, 10.0 ); + NDebugOverlay::Axis( vecPos, angAngles, 20, true, 1.0 ); + } + } + else + { + // Instead, make sure we fit into where the sequence movement ends at + const char *pszSequence = GetScriptedNPCInteractionSequence( pInteraction, SNPCINT_SEQUENCE ); + int nSeq = LookupSequence( pszSequence ); + if ( pszSequence && nSeq != -1 ) + { + Vector vecDeltaPos; + QAngle angDeltaAngles; + GetSequenceMovement( nSeq, 0.0f, 1.0f, vecDeltaPos, angDeltaAngles ); + if (!vecDeltaPos.IsZero()) + { + QAngle angInteraction = GetAbsAngles(); + angInteraction[YAW] = m_flInteractionYaw; + + Vector vecPos; + VectorRotate( vecDeltaPos, angInteraction, vecPos ); + vecPos += GetAbsOrigin(); + + AI_TraceHull( vecPos, vecPos, GetHullMins(), GetHullMaxs(), MASK_SOLID, &traceFilter, &tr); + if ( tr.fraction != 1.0 ) + { + if ( bDebug ) + { + NDebugOverlay::Box( vecPos, GetHullMins(), GetHullMaxs(), 255,0,0, 100, 1.0 ); + NDebugOverlay::HorzArrow( GetAbsOrigin(), vecPos, 16.0f, 255, 0, 0, 255, true, 1.0f ); + } + return false; + } + } } } #endif diff --git a/sp/src/game/server/scripted.cpp b/sp/src/game/server/scripted.cpp index 37b45844..f02a1dcd 100644 --- a/sp/src/game/server/scripted.cpp +++ b/sp/src/game/server/scripted.cpp @@ -1396,11 +1396,31 @@ void CAI_ScriptedSequence::ModifyScriptedAutoMovement( Vector *vecNewPos ) } } + VMatrix matInteractionPosition = m_matInteractionPosition; + +#ifdef MAPBASE + // Account for our own sequence movement + pAnimating = m_hTargetEnt->GetBaseAnimating(); + if (pAnimating) + { + Vector vecDeltaPos; + QAngle angDeltaAngles; + + pAnimating->GetSequenceMovement( pAnimating->GetSequence(), 0.0f, pAnimating->GetCycle(), vecDeltaPos, angDeltaAngles ); + if (!vecDeltaPos.IsZero()) + { + VMatrix matLocalMovement; + matLocalMovement.SetupMatrixOrgAngles( vecDeltaPos, angDeltaAngles ); + MatrixMultiply( m_matInteractionPosition, matLocalMovement, matInteractionPosition ); + } + } +#endif + // We've been asked to maintain a specific position relative to the other NPC // we're interacting with. Lerp towards the relative position. VMatrix matMeToWorld, matLocalToWorld; matMeToWorld.SetupMatrixOrgAngles( vecRelativeOrigin, angRelativeAngles ); - MatrixMultiply( matMeToWorld, m_matInteractionPosition, matLocalToWorld ); + MatrixMultiply( matMeToWorld, matInteractionPosition, matLocalToWorld ); // Get the desired NPC position in worldspace Vector vecOrigin; From 591039439c8dd9b71b2728933f747b4be7380e86 Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Wed, 3 Jan 2024 13:32:49 -0600 Subject: [PATCH 68/90] Fixes and hacks for HL2 NPCs performing dynamic interactions --- sp/src/game/server/ai_basenpc.cpp | 4 ++++ sp/src/game/server/hl2/npc_BaseZombie.cpp | 14 ++++++++++++ sp/src/game/server/hl2/npc_BaseZombie.h | 1 + sp/src/game/server/hl2/npc_combine.cpp | 24 +++++++++++++++++++++ sp/src/game/server/physics_prop_ragdoll.cpp | 10 +++++++++ 5 files changed, 53 insertions(+) diff --git a/sp/src/game/server/ai_basenpc.cpp b/sp/src/game/server/ai_basenpc.cpp index ac8590c3..c5be5135 100644 --- a/sp/src/game/server/ai_basenpc.cpp +++ b/sp/src/game/server/ai_basenpc.cpp @@ -13515,6 +13515,10 @@ bool CAI_BaseNPC::CineCleanup() } // Clear interaction partner, because we're not running a scripted sequence anymore +#ifdef MAPBASE + // We need the interaction partner for server ragdoll death cleanup, so don't clear if we're not alive + if (IsAlive()) +#endif m_hInteractionPartner = NULL; CleanupForcedInteraction(); } diff --git a/sp/src/game/server/hl2/npc_BaseZombie.cpp b/sp/src/game/server/hl2/npc_BaseZombie.cpp index fee51dd7..4d2447f4 100644 --- a/sp/src/game/server/hl2/npc_BaseZombie.cpp +++ b/sp/src/game/server/hl2/npc_BaseZombie.cpp @@ -2435,6 +2435,20 @@ void CNPC_BaseZombie::RemoveHead( void ) } +//--------------------------------------------------------- +//--------------------------------------------------------- +void CNPC_BaseZombie::SetModel( const char *szModelName ) +{ +#ifdef MAPBASE + // Zombies setting the same model again is a problem when they should maintain their current sequence (e.g. during dynamic interactions) + if ( IsRunningDynamicInteraction() && GetModelIndex() != 0 && FStrEq( szModelName, STRING(GetModelName()) ) ) + return; +#endif + + BaseClass::SetModel( szModelName ); +} + + bool CNPC_BaseZombie::ShouldPlayFootstepMoan( void ) { if( random->RandomInt( 1, zombie_stepfreq.GetInt() * s_iAngryZombies ) == 1 ) diff --git a/sp/src/game/server/hl2/npc_BaseZombie.h b/sp/src/game/server/hl2/npc_BaseZombie.h index 743186de..56a57d94 100644 --- a/sp/src/game/server/hl2/npc_BaseZombie.h +++ b/sp/src/game/server/hl2/npc_BaseZombie.h @@ -186,6 +186,7 @@ public: // Headcrab releasing/breaking apart void RemoveHead( void ); virtual void SetZombieModel( void ) { }; + virtual void SetModel( const char *szModelName ); virtual void BecomeTorso( const Vector &vecTorsoForce, const Vector &vecLegsForce ); virtual bool CanBecomeLiveTorso() { return false; } virtual bool HeadcrabFits( CBaseAnimating *pCrab ); diff --git a/sp/src/game/server/hl2/npc_combine.cpp b/sp/src/game/server/hl2/npc_combine.cpp index 73bac242..df0af9ea 100644 --- a/sp/src/game/server/hl2/npc_combine.cpp +++ b/sp/src/game/server/hl2/npc_combine.cpp @@ -47,6 +47,8 @@ ConVar npc_combine_protected_run( "npc_combine_protected_run", "0", FCVAR_NONE, ConVar npc_combine_altfire_not_allies_only( "npc_combine_altfire_not_allies_only", "1", FCVAR_NONE, "Mapbase: Elites are normally only allowed to fire their alt-fire attack at the player and the player's allies; This allows elites to alt-fire at other enemies too." ); ConVar npc_combine_new_cover_behavior( "npc_combine_new_cover_behavior", "1", FCVAR_NONE, "Mapbase: Toggles small patches for parts of npc_combine AI related to soldiers failing to take cover. These patches are minimal and only change cases where npc_combine would otherwise look at an enemy without shooting or run up to the player to melee attack when they don't have to. Consult the Mapbase wiki for more information." ); + +ConVar npc_combine_fixed_shootpos( "npc_combine_fixed_shootpos", "0", FCVAR_NONE, "Mapbase: Toggles fixed Combine soldier shoot position." ) #endif #define COMBINE_SKIN_DEFAULT 0 @@ -2959,6 +2961,28 @@ Vector CNPC_Combine::Weapon_ShootPosition( ) // FIXME: rename this "estimated" since it's not based on animation // FIXME: the orientation won't be correct when testing from arbitary positions for arbitary angles +#ifdef MAPBASE + // HACKHACK: This weapon shoot position code does not work properly when in close range, causing the aim + // to drift to the left as the enemy gets closer to it. + // This problem is usually bearable for regular combat, but it causes dynamic interaction yaw to be offset + // as well, preventing most from ever being triggered. + // Ideally, this should be fixed from the root cause, but due to the sensitivity of such a change, this is + // currently being tied to a cvar which is off by default. + // + // If the cvar is disabled but the soldier has valid interactions on its current enemy, then a separate hack + // will still attempt to correct the drift as the enemy gets closer. + if ( npc_combine_fixed_shootpos.GetBool() ) + { + right *= 0.0f; + } + else if ( HasValidInteractionsOnCurrentEnemy() ) + { + float flDistSqr = GetEnemy()->WorldSpaceCenter().DistToSqr( WorldSpaceCenter() ); + if (flDistSqr < Square( 128.0f )) + right *= (flDistSqr / Square( 128.0f )); + } +#endif + if ( bStanding ) { if( HasShotgun() ) diff --git a/sp/src/game/server/physics_prop_ragdoll.cpp b/sp/src/game/server/physics_prop_ragdoll.cpp index 93efddc7..cc788fc6 100644 --- a/sp/src/game/server/physics_prop_ragdoll.cpp +++ b/sp/src/game/server/physics_prop_ragdoll.cpp @@ -1557,6 +1557,16 @@ CBaseEntity *CreateServerRagdoll( CBaseAnimating *pAnimating, int forceBone, con pRagdoll->CollisionProp()->SetCollisionBounds( mins, maxs ); #ifdef MAPBASE + // If this was a NPC running a dynamic interaction, disable collisions with the interaction partner + if (pAnimating->IsNPC() /*&& pAnimating->MyNPCPointer()->IsRunningDynamicInteraction()*/) + { + CAI_BaseNPC *pNPC = pAnimating->MyNPCPointer(); + if (pNPC->GetInteractionPartner() && pNPC->GetInteractionPartner()->VPhysicsGetObject()) + { + PhysDisableEntityCollisions( pRagdoll, pNPC->GetInteractionPartner() ); + } + } + variant_t variant; variant.SetEntity(pRagdoll); pAnimating->FireNamedOutput("OnServerRagdoll", variant, pRagdoll, pAnimating); From 959332b343fa59f821e2931a4368963e5adb8802 Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Wed, 3 Jan 2024 13:43:48 -0600 Subject: [PATCH 69/90] Fix related interactions having greater delays stomped out --- sp/src/game/server/ai_basenpc.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sp/src/game/server/ai_basenpc.cpp b/sp/src/game/server/ai_basenpc.cpp index c5be5135..a7d7383b 100644 --- a/sp/src/game/server/ai_basenpc.cpp +++ b/sp/src/game/server/ai_basenpc.cpp @@ -15303,7 +15303,8 @@ void CAI_BaseNPC::StartScriptedNPCInteraction( CAI_BaseNPC *pOtherNPC, ScriptedN if ( Matcher_NamesMatch( pszInteraction, STRING( pOtherInteraction->iszInteractionName ) ) && pOtherInteraction != pInteraction ) { - pOtherInteraction->flNextAttemptTime = pInteraction->flNextAttemptTime; + if (pOtherInteraction->flNextAttemptTime < pInteraction->flNextAttemptTime) + pOtherInteraction->flNextAttemptTime = pInteraction->flNextAttemptTime; // Not looking for multiple if (!bWildCard) From 3861c6a7423fceb20e721bd9d269b5e240e68414 Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Wed, 3 Jan 2024 14:21:28 -0600 Subject: [PATCH 70/90] Fix semicolon --- sp/src/game/server/hl2/npc_combine.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sp/src/game/server/hl2/npc_combine.cpp b/sp/src/game/server/hl2/npc_combine.cpp index df0af9ea..c2cac902 100644 --- a/sp/src/game/server/hl2/npc_combine.cpp +++ b/sp/src/game/server/hl2/npc_combine.cpp @@ -48,7 +48,7 @@ ConVar npc_combine_altfire_not_allies_only( "npc_combine_altfire_not_allies_only ConVar npc_combine_new_cover_behavior( "npc_combine_new_cover_behavior", "1", FCVAR_NONE, "Mapbase: Toggles small patches for parts of npc_combine AI related to soldiers failing to take cover. These patches are minimal and only change cases where npc_combine would otherwise look at an enemy without shooting or run up to the player to melee attack when they don't have to. Consult the Mapbase wiki for more information." ); -ConVar npc_combine_fixed_shootpos( "npc_combine_fixed_shootpos", "0", FCVAR_NONE, "Mapbase: Toggles fixed Combine soldier shoot position." ) +ConVar npc_combine_fixed_shootpos( "npc_combine_fixed_shootpos", "0", FCVAR_NONE, "Mapbase: Toggles fixed Combine soldier shoot position." ); #endif #define COMBINE_SKIN_DEFAULT 0 From 85c1f7d113f35d2b90d3d26406fad52d755ac3a1 Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Wed, 3 Jan 2024 15:57:18 -0600 Subject: [PATCH 71/90] Add new response concepts + port several Alyx concepts for companion NPCs --- sp/src/game/server/ai_basenpc.cpp | 14 +++ sp/src/game/server/ai_basenpc.h | 3 + sp/src/game/server/ai_playerally.cpp | 6 ++ sp/src/game/server/ai_playerally.h | 7 ++ sp/src/game/server/hl2/npc_alyx_episodic.cpp | 8 ++ .../game/server/hl2/npc_playercompanion.cpp | 87 +++++++++++++++++++ sp/src/game/server/hl2/npc_playercompanion.h | 1 + 7 files changed, 126 insertions(+) diff --git a/sp/src/game/server/ai_basenpc.cpp b/sp/src/game/server/ai_basenpc.cpp index acfebf14..4dff138a 100644 --- a/sp/src/game/server/ai_basenpc.cpp +++ b/sp/src/game/server/ai_basenpc.cpp @@ -671,13 +671,27 @@ void CAI_BaseNPC::Ignite( float flFlameLifetime, bool bNPCOnly, float flSize, bo { BaseClass::Ignite( flFlameLifetime, bNPCOnly, flSize, bCalledByLevelDesigner ); +#ifdef MAPBASE + // Alyx's enemy ignited code from below can now be run on any NPC as long as + // it's our current enemy. + if ( GetEnemy() && GetEnemy()->IsNPC() ) + { + GetEnemy()->MyNPCPointer()->EnemyIgnited( this ); + } +#endif + #ifdef HL2_EPISODIC CBasePlayer *pPlayer = AI_GetSinglePlayer(); if ( pPlayer && pPlayer->IRelationType( this ) != D_LI ) { CNPC_Alyx *alyx = CNPC_Alyx::GetAlyx(); +#ifdef MAPBASE + // Alyx's code continues to run if Alyx was not this NPC's enemy. + if ( alyx && alyx != GetEnemy() ) +#else if ( alyx ) +#endif { alyx->EnemyIgnited( this ); } diff --git a/sp/src/game/server/ai_basenpc.h b/sp/src/game/server/ai_basenpc.h index 878a6f81..75ee095b 100644 --- a/sp/src/game/server/ai_basenpc.h +++ b/sp/src/game/server/ai_basenpc.h @@ -1974,6 +1974,9 @@ public: //--------------------------------- virtual void Ignite( float flFlameLifetime, bool bNPCOnly = true, float flSize = 0.0f, bool bCalledByLevelDesigner = false ); +#ifdef MAPBASE + virtual void EnemyIgnited( CAI_BaseNPC *pVictim ) {} +#endif virtual bool PassesDamageFilter( const CTakeDamageInfo &info ); //--------------------------------- diff --git a/sp/src/game/server/ai_playerally.cpp b/sp/src/game/server/ai_playerally.cpp index f7594feb..5356e95e 100644 --- a/sp/src/game/server/ai_playerally.cpp +++ b/sp/src/game/server/ai_playerally.cpp @@ -128,6 +128,12 @@ ConceptInfo_t g_ConceptInfos[] = // Passenger behaviour { TLK_PASSENGER_NEW_RADAR_CONTACT, SPEECH_IMPORTANT, -1, -1, -1, -1, -1, -1, AICF_DEFAULT, }, + +#ifdef MAPBASE + { TLK_TAKING_FIRE, SPEECH_IMPORTANT,-1, -1, -1, -1, -1, -1, AICF_DEFAULT, }, + { TLK_NEW_ENEMY, SPEECH_IMPORTANT,-1, -1, -1, -1, -1, -1, AICF_DEFAULT, }, + { TLK_COMBAT_IDLE, SPEECH_IMPORTANT,-1, -1, -1, -1, -1, -1, AICF_DEFAULT, }, +#endif }; //----------------------------------------------------------------------------- diff --git a/sp/src/game/server/ai_playerally.h b/sp/src/game/server/ai_playerally.h index 93448ec7..9a675129 100644 --- a/sp/src/game/server/ai_playerally.h +++ b/sp/src/game/server/ai_playerally.h @@ -132,6 +132,13 @@ #define TLK_TGCATCHUP "TLK_TGCATCHUP" #define TLK_TGENDTOUR "TLK_TGENDTOUR" +#ifdef MAPBASE +// Additional concepts for companions in mods +#define TLK_TAKING_FIRE "TLK_TAKING_FIRE" // Someone fired at me (regardless of whether I was hit) +#define TLK_NEW_ENEMY "TLK_NEW_ENEMY" // A new enemy appeared while combat was already in progress +#define TLK_COMBAT_IDLE "TLK_COMBAT_IDLE" // Similar to TLK_ATTACKING, but specifically for when *not* currently attacking (e.g. when in cover or reloading) +#endif + //----------------------------------------------------------------------------- #define TALKRANGE_MIN 500.0 // don't talk to anyone farther away than this diff --git a/sp/src/game/server/hl2/npc_alyx_episodic.cpp b/sp/src/game/server/hl2/npc_alyx_episodic.cpp index ee8b197c..dd5a35b0 100644 --- a/sp/src/game/server/hl2/npc_alyx_episodic.cpp +++ b/sp/src/game/server/hl2/npc_alyx_episodic.cpp @@ -1092,10 +1092,14 @@ void CNPC_Alyx::Event_KilledOther( CBaseEntity *pVictim, const CTakeDamageInfo & //----------------------------------------------------------------------------- void CNPC_Alyx::EnemyIgnited( CAI_BaseNPC *pVictim ) { +#ifdef MAPBASE + BaseClass::EnemyIgnited( pVictim ); +#else if ( FVisible( pVictim ) ) { SpeakIfAllowed( TLK_ENEMY_BURNING ); } +#endif } //----------------------------------------------------------------------------- @@ -1252,6 +1256,7 @@ void CNPC_Alyx::DoCustomSpeechAI( void ) CBasePlayer *pPlayer = AI_GetSinglePlayer(); +#ifndef MAPBASE // Ported to CNPC_PlayerCompanion if ( HasCondition(COND_NEW_ENEMY) && GetEnemy() ) { if ( GetEnemy()->Classify() == CLASS_HEADCRAB ) @@ -1278,6 +1283,7 @@ void CNPC_Alyx::DoCustomSpeechAI( void ) } } } +#endif // Darkness mode speech ClearCondition( COND_ALYX_IN_DARK ); @@ -1917,6 +1923,7 @@ int CNPC_Alyx::SelectSchedule( void ) //----------------------------------------------------------------------------- int CNPC_Alyx::SelectScheduleDanger( void ) { +#ifndef MAPBASE if( HasCondition( COND_HEAR_DANGER ) ) { CSound *pSound; @@ -1929,6 +1936,7 @@ int CNPC_Alyx::SelectScheduleDanger( void ) SpeakIfAllowed( TLK_DANGER_ZOMBINE_GRENADE ); } } +#endif return BaseClass::SelectScheduleDanger(); } diff --git a/sp/src/game/server/hl2/npc_playercompanion.cpp b/sp/src/game/server/hl2/npc_playercompanion.cpp index 68568a72..a1bd6e26 100644 --- a/sp/src/game/server/hl2/npc_playercompanion.cpp +++ b/sp/src/game/server/hl2/npc_playercompanion.cpp @@ -35,6 +35,8 @@ #include "mapbase/GlobalStrings.h" #include "world.h" #include "vehicle_base.h" +#include "npc_headcrab.h" +#include "npc_BaseZombie.h" #endif ConVar ai_debug_readiness("ai_debug_readiness", "0" ); @@ -640,6 +642,49 @@ void CNPC_PlayerCompanion::DoCustomSpeechAI( void ) { SpeakIfAllowed( TLK_PLDEAD ); } + +#ifdef MAPBASE + // Unique new enemy concepts ported from Alyx + // The casts have been changed to dynamic_cast due to the risk of non-CBaseHeadcrab/CNPC_BaseZombie enemies using those classes + if ( HasCondition(COND_NEW_ENEMY) && GetEnemy() ) + { + int nClass = GetEnemy()->Classify(); + if ( nClass == CLASS_HEADCRAB ) + { + CBaseHeadcrab *pHC = dynamic_cast(GetEnemy()); + if ( pHC ) + { + // If we see a headcrab for the first time as he's jumping at me, freak out! + if ( ( GetEnemy()->GetEnemy() == this ) && pHC->IsJumping() && gpGlobals->curtime - GetEnemies()->FirstTimeSeen(GetEnemy()) < 0.5 ) + { + SpeakIfAllowed( "TLK_SPOTTED_INCOMING_HEADCRAB" ); + } + // If we see a headcrab leaving a zombie that just died, mention it + else if ( pHC->GetOwnerEntity() && ( pHC->GetOwnerEntity()->Classify() == CLASS_ZOMBIE ) && !pHC->GetOwnerEntity()->IsAlive() ) + { + SpeakIfAllowed( "TLK_SPOTTED_HEADCRAB_LEAVING_ZOMBIE" ); + } + } + } + else if ( nClass == CLASS_ZOMBIE ) + { + CNPC_BaseZombie *pZombie = dynamic_cast(GetEnemy()); + // If we see a zombie getting up, mention it + if ( pZombie && pZombie->IsGettingUp() ) + { + SpeakIfAllowed( "TLK_SPOTTED_ZOMBIE_WAKEUP" ); + } + } + else if ( gpGlobals->curtime - GetEnemies()->TimeAtFirstHand( GetEnemy() ) <= 1.0f && nClass != CLASS_BULLSEYE ) + { + // New concept which did not originate from Alyx, but is in the same category as the above concepts. + // This is meant to be used when a new enemy enters the arena while combat is already in progress. + // (Note that this can still trigger when combat begins, but unlike TLK_STARTCOMBAT, it has no delay + // between combat engagements.) + SpeakIfAllowed( TLK_NEW_ENEMY ); + } + } +#endif } //----------------------------------------------------------------------------- @@ -910,8 +955,21 @@ int CNPC_PlayerCompanion::SelectScheduleDanger() if ( pSound && (pSound->m_iType & SOUND_DANGER) ) { +#ifdef MAPBASE + if ( pSound->SoundChannel() == SOUNDENT_CHANNEL_ZOMBINE_GRENADE ) + { + SetSpeechTarget( pSound->m_hOwner ); + SpeakIfAllowed( TLK_DANGER_ZOMBINE_GRENADE ); + } + else if (!(pSound->SoundContext() & (SOUND_CONTEXT_MORTAR | SOUND_CONTEXT_FROM_SNIPER)) || IsOkToCombatSpeak()) + { + SetSpeechTarget( pSound->m_hOwner ); + SpeakIfAllowed( TLK_DANGER ); + } +#else if ( !(pSound->SoundContext() & (SOUND_CONTEXT_MORTAR|SOUND_CONTEXT_FROM_SNIPER)) || IsOkToCombatSpeak() ) SpeakIfAllowed( TLK_DANGER ); +#endif if ( HasCondition( COND_PC_SAFE_FROM_MORTAR ) ) { @@ -4309,6 +4367,20 @@ void CNPC_PlayerCompanion::Event_KilledOther( CBaseEntity *pVictim, const CTakeD } } +//----------------------------------------------------------------------------- +// Purpose: Called by enemy NPC's when they are ignited +// Input : pVictim - entity that was ignited +//----------------------------------------------------------------------------- +void CNPC_PlayerCompanion::EnemyIgnited( CAI_BaseNPC *pVictim ) +{ + BaseClass::EnemyIgnited( pVictim ); + + if ( FVisible( pVictim ) ) + { + SpeakIfAllowed( TLK_ENEMY_BURNING ); + } +} + //----------------------------------------------------------------------------- // Purpose: Handles custom combat speech stuff ported from Alyx. //----------------------------------------------------------------------------- @@ -4376,6 +4448,21 @@ void CNPC_PlayerCompanion::DoCustomCombatAI( void ) { SpeakIfAllowed( TLK_MANY_ENEMIES ); } + + // If we're not currently attacking or vulnerable, try speaking + else if ( gpGlobals->curtime - GetLastAttackTime() > 1.0f && (!HasCondition( COND_SEE_ENEMY ) || IsCurSchedule( SCHED_RELOAD ) || IsCurSchedule( SCHED_HIDE_AND_RELOAD )) ) + { + int chance = ( IsMoving() ) ? 20 : 3; + if ( ShouldSpeakRandom( TLK_COMBAT_IDLE, chance ) ) + { + AI_CriteriaSet modifiers; + + modifiers.AppendCriteria( "in_cover", HasMemory( bits_MEMORY_INCOVER ) ? "1" : "0" ); + modifiers.AppendCriteria( "lastseenenemy", UTIL_VarArgs( "%f", gpGlobals->curtime - GetEnemyLastTimeSeen() ) ); + + SpeakIfAllowed( TLK_COMBAT_IDLE, modifiers ); + } + } } #endif diff --git a/sp/src/game/server/hl2/npc_playercompanion.h b/sp/src/game/server/hl2/npc_playercompanion.h index 8dcf1aa1..e0f6769e 100644 --- a/sp/src/game/server/hl2/npc_playercompanion.h +++ b/sp/src/game/server/hl2/npc_playercompanion.h @@ -242,6 +242,7 @@ public: virtual void Event_Killed( const CTakeDamageInfo &info ); virtual void Event_KilledOther( CBaseEntity *pVictim, const CTakeDamageInfo &info ); + virtual void EnemyIgnited( CAI_BaseNPC *pVictim ); virtual void DoCustomCombatAI( void ); #endif From 9070d9a8446f3be2eb8ac5a59bd2c9444cb34775 Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Wed, 3 Jan 2024 16:00:10 -0600 Subject: [PATCH 72/90] Optional support for fired bullets notifying targets (currently only used for NPC companion speech) --- sp/src/game/server/ai_playerally.cpp | 32 +++++++++ sp/src/game/server/ai_playerally.h | 4 ++ sp/src/game/server/basecombatcharacter.h | 4 ++ sp/src/game/server/player.cpp | 87 ++++++++++++++++++++++++ sp/src/game/server/player.h | 5 ++ sp/src/game/shared/baseentity_shared.cpp | 23 +++++++ 6 files changed, 155 insertions(+) diff --git a/sp/src/game/server/ai_playerally.cpp b/sp/src/game/server/ai_playerally.cpp index 5356e95e..39b49739 100644 --- a/sp/src/game/server/ai_playerally.cpp +++ b/sp/src/game/server/ai_playerally.cpp @@ -1265,6 +1265,38 @@ void CAI_PlayerAlly::OnKilledNPC( CBaseCombatCharacter *pKilled ) } } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CAI_PlayerAlly::OnEnemyRangeAttackedMe( CBaseEntity *pEnemy, const Vector &vecDir, const Vector &vecEnd ) +{ + BaseClass::OnEnemyRangeAttackedMe( pEnemy, vecDir, vecEnd ); + + if ( IRelationType( pEnemy ) <= D_FR ) + { + AI_CriteriaSet modifiers; + ModifyOrAppendEnemyCriteria( modifiers, pEnemy ); + + Vector vecEntDir = (pEnemy->EyePosition() - EyePosition()); + float flDot = DotProduct( vecEntDir.Normalized(), vecDir ); + modifiers.AppendCriteria( "shot_dot", CNumStr( flDot ) ); + + if (GetLastDamageTime() == gpGlobals->curtime) + modifiers.AppendCriteria( "missed", "0" ); + else + modifiers.AppendCriteria( "missed", "1" ); + + // Check if they're out of ammo + if ( pEnemy->IsCombatCharacter() && pEnemy->MyCombatCharacterPointer()->GetActiveWeapon() && pEnemy->MyCombatCharacterPointer()->GetActiveWeapon()->Clip1() <= 0 ) + modifiers.AppendCriteria( "last_attack", "1" ); + else + modifiers.AppendCriteria( "last_attack", "0" ); + + SpeakIfAllowed( TLK_TAKING_FIRE, modifiers ); + } +} +#endif + //----------------------------------------------------------------------------- void CAI_PlayerAlly::TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator ) { diff --git a/sp/src/game/server/ai_playerally.h b/sp/src/game/server/ai_playerally.h index 9a675129..a1ff01c8 100644 --- a/sp/src/game/server/ai_playerally.h +++ b/sp/src/game/server/ai_playerally.h @@ -322,6 +322,10 @@ public: //--------------------------------- void OnKilledNPC( CBaseCombatCharacter *pKilled ); +#ifdef MAPBASE + void OnEnemyRangeAttackedMe( CBaseEntity *pEnemy, const Vector &vecDir, const Vector &vecEnd ); +#endif + //--------------------------------- // Damage handling //--------------------------------- diff --git a/sp/src/game/server/basecombatcharacter.h b/sp/src/game/server/basecombatcharacter.h index 22f07eb9..cdae243d 100644 --- a/sp/src/game/server/basecombatcharacter.h +++ b/sp/src/game/server/basecombatcharacter.h @@ -263,6 +263,10 @@ public: virtual bool CanBecomeServerRagdoll( void ) { return true; } +#ifdef MAPBASE + virtual void OnEnemyRangeAttackedMe( CBaseEntity *pEnemy, const Vector &vecDir, const Vector &vecEnd ) {} +#endif + // ----------------------- // Damage // ----------------------- diff --git a/sp/src/game/server/player.cpp b/sp/src/game/server/player.cpp index f8bf33a2..28c767c5 100644 --- a/sp/src/game/server/player.cpp +++ b/sp/src/game/server/player.cpp @@ -7734,6 +7734,93 @@ void CBasePlayer::ResetAutoaim( void ) m_fOnTarget = false; } +#ifdef MAPBASE +ConVar player_debug_probable_aim_target( "player_debug_probable_aim_target", "0", FCVAR_CHEAT, "" ); + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CBaseEntity *CBasePlayer::GetProbableAimTarget( const Vector &vecSrc, const Vector &vecDir ) +{ + trace_t tr; + CBaseEntity *pIgnore = NULL; + if (IsInAVehicle()) + pIgnore = GetVehicleEntity(); + + CTraceFilterSkipTwoEntities traceFilter( this, pIgnore, COLLISION_GROUP_NONE ); + + // Based on dot product and distance + // If we aim directly at something, only return it if there's not a larger entity slightly off-center + // Should be weighted based on whether an entity is a NPC, etc. + CBaseEntity *pBestEnt = NULL; + float flBestWeight = 0.0f; + for (CBaseEntity *pEntity = UTIL_EntitiesInPVS( this, NULL ); pEntity; pEntity = UTIL_EntitiesInPVS( this, pEntity )) + { + // Combat characters can be unviewable if they just died + if (!pEntity->IsViewable() && !pEntity->IsCombatCharacter()) + continue; + + if (pEntity == this || pEntity->GetMoveParent() == this || pEntity == GetVehicleEntity()) + continue; + + Vector vecEntDir = (pEntity->EyePosition() - vecSrc); + float flDot = DotProduct( vecEntDir.Normalized(), vecDir); + + if (flDot < m_flFieldOfView) + continue; + + // Make sure we can see it + UTIL_TraceLine( vecSrc, pEntity->EyePosition(), MASK_SHOT, &traceFilter, &tr ); + if (tr.m_pEnt != pEntity) + { + if (pEntity->IsCombatCharacter()) + { + // Trace between centers as well just in case our eyes are blocked + UTIL_TraceLine( WorldSpaceCenter(), pEntity->WorldSpaceCenter(), MASK_SHOT, &traceFilter, &tr ); + if (tr.m_pEnt != pEntity) + continue; + } + else + continue; + } + + float flWeight = flDot - (vecEntDir.LengthSqr() / Square( 2048.0f )); + + if (pEntity->IsCombatCharacter()) + { + // Hostile NPCs are more likely targets + if (IRelationType( pEntity ) <= D_FR) + flWeight += 0.5f; + } + else if (pEntity->GetFlags() & FL_AIMTARGET) + { + // FL_AIMTARGET is often used for props like explosive barrels + flWeight += 0.25f; + } + + if (player_debug_probable_aim_target.GetBool()) + { + float flWeightClamped = 1.0f - RemapValClamped( flWeight, -2.0f, 2.0f, 0.0f, 1.0f ); + pEntity->EntityText( 0, UTIL_VarArgs( "%f", flWeight ), 2.0f, flWeightClamped * 255.0f, 255.0f, flWeightClamped * 255.0f, 255 ); + } + + if (flWeight > flBestWeight) + { + pBestEnt = pEntity; + flBestWeight = flWeight; + } + } + + if (player_debug_probable_aim_target.GetBool()) + { + Msg( "Best probable aim target is %s\n", pBestEnt->GetDebugName() ); + NDebugOverlay::EntityBounds( pBestEnt, 255, 100, 0, 0, 2.0f ); + } + + return pBestEnt; +} +#endif + // ========================================================================== // > Weapon stuff // ========================================================================== diff --git a/sp/src/game/server/player.h b/sp/src/game/server/player.h index 0bfba8fb..72a1e38e 100644 --- a/sp/src/game/server/player.h +++ b/sp/src/game/server/player.h @@ -608,6 +608,11 @@ public: virtual bool ShouldAutoaim( void ); void SetTargetInfo( Vector &vecSrc, float flDist ); +#ifdef MAPBASE + // Tries to figure out what the player is trying to aim at + CBaseEntity *GetProbableAimTarget( const Vector &vecSrc, const Vector &vecDir ); +#endif + void SetViewEntity( CBaseEntity *pEntity ); CBaseEntity *GetViewEntity( void ) { return m_hViewEntity; } diff --git a/sp/src/game/shared/baseentity_shared.cpp b/sp/src/game/shared/baseentity_shared.cpp index e5588287..b2cd6bdc 100644 --- a/sp/src/game/shared/baseentity_shared.cpp +++ b/sp/src/game/shared/baseentity_shared.cpp @@ -83,6 +83,10 @@ ConVar ai_shot_bias_min( "ai_shot_bias_min", "-1.0", FCVAR_REPLICATED ); ConVar ai_shot_bias_max( "ai_shot_bias_max", "1.0", FCVAR_REPLICATED ); ConVar ai_debug_shoot_positions( "ai_debug_shoot_positions", "0", FCVAR_REPLICATED | FCVAR_CHEAT ); +#if defined(MAPBASE) && defined(GAME_DLL) +ConVar ai_shot_notify_targets( "ai_shot_notify_targets", "0", FCVAR_NONE, "Allows fired bullets to notify the NPCs and players they are targeting, regardless of whether they hit them or not. Can be used for custom AI and speech." ); +#endif + // Utility func to throttle rate at which the "reasonable position" spew goes out static double s_LastEntityReasonableEmitTime; bool CheckEmitReasonablePhysicsSpew() @@ -2081,6 +2085,25 @@ void CBaseEntity::FireBullets( const FireBulletsInfo_t &info ) CTakeDamageInfo dmgInfo( this, pAttacker, flCumulativeDamage, nDamageType ); gamestats->Event_WeaponHit( pPlayer, info.m_bPrimaryAttack, pPlayer->GetActiveWeapon()->GetClassname(), dmgInfo ); } + +#ifdef MAPBASE + if ( ai_shot_notify_targets.GetBool() ) + { + if ( IsPlayer() ) + { + // Look for probable target to notify of attack + CBaseEntity *pAimTarget = static_cast(this)->GetProbableAimTarget( info.m_vecSrc, info.m_vecDirShooting ); + if ( pAimTarget && pAimTarget->IsCombatCharacter() ) + { + pAimTarget->MyCombatCharacterPointer()->OnEnemyRangeAttackedMe( this, vecDir, vecEnd ); + } + } + else if ( GetEnemy() && GetEnemy()->IsCombatCharacter() ) + { + GetEnemy()->MyCombatCharacterPointer()->OnEnemyRangeAttackedMe( this, vecDir, vecEnd ); + } + } +#endif #endif } From a2a4aa3082a9e2957eeb151fb48f949ca07cf37e Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Wed, 3 Jan 2024 16:00:47 -0600 Subject: [PATCH 73/90] Add additional enemy criteria to NPC speech --- sp/src/game/server/ai_basenpc.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/sp/src/game/server/ai_basenpc.cpp b/sp/src/game/server/ai_basenpc.cpp index 4dff138a..3d229f08 100644 --- a/sp/src/game/server/ai_basenpc.cpp +++ b/sp/src/game/server/ai_basenpc.cpp @@ -16227,6 +16227,21 @@ void CAI_BaseNPC::ModifyOrAppendEnemyCriteria( AI_CriteriaSet& set, CBaseEntity set.AppendCriteria( "enemyclass", g_pGameRules->AIClassText( pEnemy->Classify() ) ); // UTIL_VarArgs("%i", pEnemy->Classify()) set.AppendCriteria( "distancetoenemy", UTIL_VarArgs( "%f", EnemyDistance(pEnemy) ) ); set.AppendCriteria( "timesincecombat", "-1" ); + + CAI_BaseNPC *pNPC = pEnemy->MyNPCPointer(); + if (pNPC) + { + set.AppendCriteria("enemy_is_npc", "1"); + + set.AppendCriteria( "enemy_activity", CAI_BaseNPC::GetActivityName( pNPC->GetActivity() ) ); + set.AppendCriteria( "enemy_weapon", pNPC->GetActiveWeapon() ? pNPC->GetActiveWeapon()->GetClassname() : "0" ); + } + else + { + set.AppendCriteria("enemy_is_npc", "0"); + } + + pEnemy->AppendContextToCriteria( set, "enemy_" ); } else { From 3ce8a35567dd16770afa6dca28a3355603916b01 Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Wed, 3 Jan 2024 16:01:07 -0600 Subject: [PATCH 74/90] Add func_tank behavior criteria --- .../game/server/hl2/ai_behavior_functank.cpp | 18 ++++++++++++++++++ sp/src/game/server/hl2/ai_behavior_functank.h | 2 ++ 2 files changed, 20 insertions(+) diff --git a/sp/src/game/server/hl2/ai_behavior_functank.cpp b/sp/src/game/server/hl2/ai_behavior_functank.cpp index 89f79228..105bf6e2 100644 --- a/sp/src/game/server/hl2/ai_behavior_functank.cpp +++ b/sp/src/game/server/hl2/ai_behavior_functank.cpp @@ -190,6 +190,24 @@ int CAI_FuncTankBehavior::SelectSchedule() return SCHED_IDLE_STAND; } +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CAI_FuncTankBehavior::ModifyOrAppendCriteria( AI_CriteriaSet &set ) +{ + BaseClass::ModifyOrAppendCriteria( set ); + +#ifdef MAPBASE + set.AppendCriteria( "ft_mounted", m_bMounted ? "1" : "0" ); + + if (m_hFuncTank) + { + set.AppendCriteria( "ft_classname", m_hFuncTank->GetClassname() ); + m_hFuncTank->AppendContextToCriteria( set, "ft_" ); + } +#endif +} + //----------------------------------------------------------------------------- // Purpose: // Input : activity - diff --git a/sp/src/game/server/hl2/ai_behavior_functank.h b/sp/src/game/server/hl2/ai_behavior_functank.h index 75d5b8df..54f91d1f 100644 --- a/sp/src/game/server/hl2/ai_behavior_functank.h +++ b/sp/src/game/server/hl2/ai_behavior_functank.h @@ -55,6 +55,8 @@ public: bool CanManTank( CFuncTank *pTank, bool bForced ); #endif + void ModifyOrAppendCriteria( AI_CriteriaSet &set ); + Activity NPC_TranslateActivity( Activity activity ); // Conditions: From e1c890a011cdd33de6f2de46800b8baccc5496f9 Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Fri, 5 Jan 2024 13:19:33 -0600 Subject: [PATCH 75/90] New response context-based check for TLK_SPOTTED_HEADCRAB_LEAVING_ZOMBIE (because some death types, such as serverside ragdolling, remove the zombie instantly) --- sp/src/game/server/hl2/npc_BaseZombie.cpp | 3 +++ sp/src/game/server/hl2/npc_playercompanion.cpp | 11 ++++++++--- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/sp/src/game/server/hl2/npc_BaseZombie.cpp b/sp/src/game/server/hl2/npc_BaseZombie.cpp index fee51dd7..4bd4b475 100644 --- a/sp/src/game/server/hl2/npc_BaseZombie.cpp +++ b/sp/src/game/server/hl2/npc_BaseZombie.cpp @@ -2544,6 +2544,9 @@ void CNPC_BaseZombie::ReleaseHeadcrab( const Vector &vecOrigin, const Vector &ve #ifdef MAPBASE // Inherit some misc. properties pGib->m_iViewHideFlags = m_iViewHideFlags; + + // Add response context for companion response (more reliable than checking for post-death zombie entity) + pCrab->AddContext( "from_zombie", "1", 2.0f ); #endif pGib->SetOwnerEntity( this ); diff --git a/sp/src/game/server/hl2/npc_playercompanion.cpp b/sp/src/game/server/hl2/npc_playercompanion.cpp index a1bd6e26..37246915 100644 --- a/sp/src/game/server/hl2/npc_playercompanion.cpp +++ b/sp/src/game/server/hl2/npc_playercompanion.cpp @@ -659,10 +659,15 @@ void CNPC_PlayerCompanion::DoCustomSpeechAI( void ) { SpeakIfAllowed( "TLK_SPOTTED_INCOMING_HEADCRAB" ); } - // If we see a headcrab leaving a zombie that just died, mention it - else if ( pHC->GetOwnerEntity() && ( pHC->GetOwnerEntity()->Classify() == CLASS_ZOMBIE ) && !pHC->GetOwnerEntity()->IsAlive() ) + else { - SpeakIfAllowed( "TLK_SPOTTED_HEADCRAB_LEAVING_ZOMBIE" ); + // If we see a headcrab leaving a zombie that just died, mention it + // (Note that this is now a response context since some death types remove the zombie instantly) + int nContext = pHC->FindContextByName( "from_zombie" ); + if ( nContext > -1 && !ContextExpired( nContext ) ) // pHC->GetOwnerEntity() && ( pHC->GetOwnerEntity()->Classify() == CLASS_ZOMBIE ) && !pHC->GetOwnerEntity()->IsAlive() + { + SpeakIfAllowed( "TLK_SPOTTED_HEADCRAB_LEAVING_ZOMBIE" ); + } } } } From 7c5c90ebeb2bd028fa13409b385a94519b305d26 Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Fri, 5 Jan 2024 13:26:47 -0600 Subject: [PATCH 76/90] Fix TLK_NEW_ENEMY not playing on headcrabs or zombies --- sp/src/game/server/hl2/npc_playercompanion.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sp/src/game/server/hl2/npc_playercompanion.cpp b/sp/src/game/server/hl2/npc_playercompanion.cpp index 37246915..660cb218 100644 --- a/sp/src/game/server/hl2/npc_playercompanion.cpp +++ b/sp/src/game/server/hl2/npc_playercompanion.cpp @@ -680,7 +680,8 @@ void CNPC_PlayerCompanion::DoCustomSpeechAI( void ) SpeakIfAllowed( "TLK_SPOTTED_ZOMBIE_WAKEUP" ); } } - else if ( gpGlobals->curtime - GetEnemies()->TimeAtFirstHand( GetEnemy() ) <= 1.0f && nClass != CLASS_BULLSEYE ) + + if ( gpGlobals->curtime - GetEnemies()->TimeAtFirstHand( GetEnemy() ) <= 1.0f && nClass != CLASS_BULLSEYE ) { // New concept which did not originate from Alyx, but is in the same category as the above concepts. // This is meant to be used when a new enemy enters the arena while combat is already in progress. From 14675578a8a166f868de6c5b7a0b71b59781e3bf Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Fri, 5 Jan 2024 14:33:33 -0600 Subject: [PATCH 77/90] Fix "their_" misc criteria not being registered --- sp/src/game/server/ai_basenpc.cpp | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/sp/src/game/server/ai_basenpc.cpp b/sp/src/game/server/ai_basenpc.cpp index a7d7383b..d6d2a237 100644 --- a/sp/src/game/server/ai_basenpc.cpp +++ b/sp/src/game/server/ai_basenpc.cpp @@ -14873,23 +14873,29 @@ void CAI_BaseNPC::ParseScriptedNPCInteractions(void) else if (!Q_strncmp(szName, "their_", 6)) { - szName += 6; + const char *szTheirName = szName + 6; sInteraction.bHasSeparateSequenceNames = true; - if (!Q_strncmp(szName, "entry_sequence", 14)) + if (!Q_strncmp(szTheirName, "entry_sequence", 14)) sInteraction.sTheirPhases[SNPCINT_ENTRY].iszSequence = AllocPooledString(szValue); - else if (!Q_strncmp(szName, "entry_activity", 14)) + else if (!Q_strncmp(szTheirName, "entry_activity", 14)) sInteraction.sTheirPhases[SNPCINT_ENTRY].iActivity = GetOrRegisterActivity(szValue); - else if (!Q_strncmp(szName, "sequence", 8)) + else if (!Q_strncmp(szTheirName, "sequence", 8)) sInteraction.sTheirPhases[SNPCINT_SEQUENCE].iszSequence = AllocPooledString(szValue); - else if (!Q_strncmp(szName, "activity", 8)) + else if (!Q_strncmp(szTheirName, "activity", 8)) sInteraction.sTheirPhases[SNPCINT_SEQUENCE].iActivity = GetOrRegisterActivity(szValue); - else if (!Q_strncmp(szName, "exit_sequence", 13)) + else if (!Q_strncmp(szTheirName, "exit_sequence", 13)) sInteraction.sTheirPhases[SNPCINT_EXIT].iszSequence = AllocPooledString(szValue); - else if (!Q_strncmp(szName, "exit_activity", 13)) + else if (!Q_strncmp(szTheirName, "exit_activity", 13)) sInteraction.sTheirPhases[SNPCINT_EXIT].iActivity = GetOrRegisterActivity(szValue); + + // Add anything else to our miscellaneous criteria + else + { + szCriteria = UTIL_VarArgs("%s,%s:%s", szCriteria, szName, szValue); + } } else if (!Q_strncmp(szName, "delay", 5)) From 06b27acd74122dd5ad682f3eed97b234229d00df Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Fri, 5 Jan 2024 14:39:26 -0600 Subject: [PATCH 78/90] Fix CNPC_BaseZombie::HeadcrabFits() and blood impacts not working with serverside ragdolls --- sp/src/game/server/hl2/npc_BaseZombie.cpp | 28 +++++++++++++++++++---- sp/src/game/server/hl2/npc_BaseZombie.h | 2 +- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/sp/src/game/server/hl2/npc_BaseZombie.cpp b/sp/src/game/server/hl2/npc_BaseZombie.cpp index fee51dd7..5a1b08d0 100644 --- a/sp/src/game/server/hl2/npc_BaseZombie.cpp +++ b/sp/src/game/server/hl2/npc_BaseZombie.cpp @@ -2450,9 +2450,15 @@ bool CNPC_BaseZombie::ShouldPlayFootstepMoan( void ) #define CRAB_HULL_EXPAND 1.1f //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- -bool CNPC_BaseZombie::HeadcrabFits( CBaseAnimating *pCrab ) +bool CNPC_BaseZombie::HeadcrabFits( CBaseAnimating *pCrab, const Vector *vecOrigin ) { - Vector vecSpawnLoc = pCrab->GetAbsOrigin(); + Vector vecSpawnLoc; +#ifdef MAPBASE + if (vecOrigin) + vecSpawnLoc = *vecOrigin; + else +#endif + vecSpawnLoc = pCrab->GetAbsOrigin(); CTraceFilterSimpleList traceFilter( COLLISION_GROUP_NONE ); traceFilter.AddEntityToIgnore( pCrab ); @@ -2535,7 +2541,12 @@ void CNPC_BaseZombie::ReleaseHeadcrab( const Vector &vecOrigin, const Vector &ve SetHeadcrabSpawnLocation( iCrabAttachment, pAnimatingGib ); } +#ifdef MAPBASE + // Server ragdolls don't have a valid origin on spawn, so we have to use the origin originally passed + if( !HeadcrabFits( pAnimatingGib, m_bForceServerRagdoll ? &vecOrigin : NULL ) ) +#else if( !HeadcrabFits(pAnimatingGib) ) +#endif { UTIL_Remove(pGib); return; @@ -2552,11 +2563,20 @@ void CNPC_BaseZombie::ReleaseHeadcrab( const Vector &vecOrigin, const Vector &ve if( UTIL_ShouldShowBlood(BLOOD_COLOR_YELLOW) ) { - UTIL_BloodImpact( pGib->WorldSpaceCenter(), Vector(0,0,1), BLOOD_COLOR_YELLOW, 1 ); + Vector vecGibCenter; +#ifdef MAPBASE + // Server ragdolls don't have a valid origin on spawn, so we have to use the origin originally passed + if (m_bForceServerRagdoll) + vecGibCenter = vecOrigin; + else +#endif + vecGibCenter = pGib->WorldSpaceCenter(); + + UTIL_BloodImpact( vecGibCenter, Vector(0,0,1), BLOOD_COLOR_YELLOW, 1 ); for ( int i = 0 ; i < 3 ; i++ ) { - Vector vecSpot = pGib->WorldSpaceCenter(); + Vector vecSpot = vecGibCenter; vecSpot.x += random->RandomFloat( -8, 8 ); vecSpot.y += random->RandomFloat( -8, 8 ); diff --git a/sp/src/game/server/hl2/npc_BaseZombie.h b/sp/src/game/server/hl2/npc_BaseZombie.h index 743186de..034ba37f 100644 --- a/sp/src/game/server/hl2/npc_BaseZombie.h +++ b/sp/src/game/server/hl2/npc_BaseZombie.h @@ -188,7 +188,7 @@ public: virtual void SetZombieModel( void ) { }; virtual void BecomeTorso( const Vector &vecTorsoForce, const Vector &vecLegsForce ); virtual bool CanBecomeLiveTorso() { return false; } - virtual bool HeadcrabFits( CBaseAnimating *pCrab ); + virtual bool HeadcrabFits( CBaseAnimating *pCrab, const Vector *vecOrigin = NULL ); void ReleaseHeadcrab( const Vector &vecOrigin, const Vector &vecVelocity, bool fRemoveHead, bool fRagdollBody, bool fRagdollCrab = false ); void SetHeadcrabSpawnLocation( int iCrabAttachment, CBaseAnimating *pCrab ); From 8b7c6952ba6de7ceac454413b39615320f6c1828 Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Fri, 5 Jan 2024 14:40:37 -0600 Subject: [PATCH 79/90] Fix AE_ZOMBIE_POPHEADCRAB using inaccurate headcrab release position --- sp/src/game/server/hl2/npc_BaseZombie.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sp/src/game/server/hl2/npc_BaseZombie.cpp b/sp/src/game/server/hl2/npc_BaseZombie.cpp index 4d2447f4..dc60673b 100644 --- a/sp/src/game/server/hl2/npc_BaseZombie.cpp +++ b/sp/src/game/server/hl2/npc_BaseZombie.cpp @@ -1745,7 +1745,11 @@ void CNPC_BaseZombie::HandleAnimEvent( animevent_t *pEvent ) dmgInfo.SetDamagePosition( vecHeadCrabPosition ); +#ifdef MAPBASE + ReleaseHeadcrab( vecHeadCrabPosition, vVelocity *iSpeed, true, false, true ); +#else ReleaseHeadcrab( EyePosition(), vVelocity * iSpeed, true, false, true ); +#endif GuessDamageForce( &dmgInfo, vVelocity, vecHeadCrabPosition, 0.5f ); TakeDamage( dmgInfo ); From a3fe8b5284dee60974a451ecb4fc093f7a974ab7 Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Fri, 5 Jan 2024 14:55:01 -0600 Subject: [PATCH 80/90] Add cvar to prevent zombies from flinching during actbusies and scripted sequences --- sp/src/game/server/hl2/npc_BaseZombie.cpp | 29 +++++++++++++++++++++++ sp/src/game/server/hl2/npc_BaseZombie.h | 2 ++ 2 files changed, 31 insertions(+) diff --git a/sp/src/game/server/hl2/npc_BaseZombie.cpp b/sp/src/game/server/hl2/npc_BaseZombie.cpp index fee51dd7..37ddb534 100644 --- a/sp/src/game/server/hl2/npc_BaseZombie.cpp +++ b/sp/src/game/server/hl2/npc_BaseZombie.cpp @@ -159,6 +159,10 @@ ConVar zombie_decaymax( "zombie_decaymax", "0.4" ); ConVar zombie_ambushdist( "zombie_ambushdist", "16000" ); +#ifdef MAPBASE +ConVar zombie_no_flinch_during_unique_anim( "zombie_no_flinch_during_unique_anim", "1", FCVAR_NONE, "Prevents zombies from flinching during actbusies and scripted sequences." ); +#endif + //========================================================= // For a couple of reasons, we keep a running count of how // many zombies in the world are angry at any given time. @@ -1927,6 +1931,31 @@ void CNPC_BaseZombie::OnScheduleChange( void ) } +//--------------------------------------------------------- +//--------------------------------------------------------- + +bool CNPC_BaseZombie::CanFlinch( void ) +{ + if (!BaseClass::CanFlinch()) + return false; + +#ifdef MAPBASE + if (zombie_no_flinch_during_unique_anim.GetBool()) + { + // Don't flinch if currently playing actbusy animation (navigating to or from one is fine) + if (m_ActBusyBehavior.IsInsideActBusy()) + return false; + + // Don't flinch if currently playing scripted sequence (navigating to or from one is fine) + if (m_NPCState == NPC_STATE_SCRIPT && (IsCurSchedule( SCHED_SCRIPTED_WAIT, false ) || IsCurSchedule( SCHED_SCRIPTED_FACE, false ))) + return false; + } +#endif + + return true; +} + + //--------------------------------------------------------- //--------------------------------------------------------- int CNPC_BaseZombie::SelectFailSchedule( int failedSchedule, int failedTask, AI_TaskFailureCode_t taskFailCode ) diff --git a/sp/src/game/server/hl2/npc_BaseZombie.h b/sp/src/game/server/hl2/npc_BaseZombie.h index 743186de..90add2d2 100644 --- a/sp/src/game/server/hl2/npc_BaseZombie.h +++ b/sp/src/game/server/hl2/npc_BaseZombie.h @@ -151,6 +151,8 @@ public: int OnTakeDamage_Alive( const CTakeDamageInfo &info ); virtual float GetReactionDelay( CBaseEntity *pEnemy ) { return 0.0; } + bool CanFlinch( void ); + virtual int SelectSchedule ( void ); virtual int SelectFailSchedule( int failedSchedule, int failedTask, AI_TaskFailureCode_t taskFailCode ); virtual void BuildScheduleTestBits( void ); From 4e7814eeeaf00ebc66b4c548b6c82da02d388501 Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Fri, 5 Jan 2024 14:57:43 -0600 Subject: [PATCH 81/90] Fix function call being in the wrong place --- sp/src/game/server/hl2/npc_BaseZombie.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sp/src/game/server/hl2/npc_BaseZombie.cpp b/sp/src/game/server/hl2/npc_BaseZombie.cpp index 4bd4b475..a120e6b4 100644 --- a/sp/src/game/server/hl2/npc_BaseZombie.cpp +++ b/sp/src/game/server/hl2/npc_BaseZombie.cpp @@ -2544,9 +2544,6 @@ void CNPC_BaseZombie::ReleaseHeadcrab( const Vector &vecOrigin, const Vector &ve #ifdef MAPBASE // Inherit some misc. properties pGib->m_iViewHideFlags = m_iViewHideFlags; - - // Add response context for companion response (more reliable than checking for post-death zombie entity) - pCrab->AddContext( "from_zombie", "1", 2.0f ); #endif pGib->SetOwnerEntity( this ); @@ -2593,6 +2590,9 @@ void CNPC_BaseZombie::ReleaseHeadcrab( const Vector &vecOrigin, const Vector &ve // Inherit some misc. properties pCrab->m_bForceServerRagdoll = m_bForceServerRagdoll; pCrab->m_iViewHideFlags = m_iViewHideFlags; + + // Add response context for companion response (more reliable than checking for post-death zombie entity) + pCrab->AddContext( "from_zombie", "1", 2.0f ); #endif // make me the crab's owner to avoid collision issues From a96147a6d8374d72628c1dd159518a9ad3ceecc7 Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Fri, 5 Jan 2024 15:01:44 -0600 Subject: [PATCH 82/90] Add command line parameter to allow VScript file read paths with dot slashes --- sp/src/game/shared/mapbase/vscript_singletons.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/sp/src/game/shared/mapbase/vscript_singletons.cpp b/sp/src/game/shared/mapbase/vscript_singletons.cpp index eeb50d84..86b1248f 100644 --- a/sp/src/game/shared/mapbase/vscript_singletons.cpp +++ b/sp/src/game/shared/mapbase/vscript_singletons.cpp @@ -22,6 +22,7 @@ #include "filesystem.h" #include "igameevents.h" #include "engine/ivdebugoverlay.h" +#include "icommandline.h" #ifdef CLIENT_DLL #include "IEffects.h" @@ -1094,7 +1095,7 @@ const char *CScriptReadWriteFile::FileRead( const char *szFile ) char pszFullName[MAX_PATH]; V_snprintf( pszFullName, sizeof(pszFullName), SCRIPT_RW_FULL_PATH_FMT, szFile ); - if ( !V_RemoveDotSlashes( pszFullName, CORRECT_PATH_SEPARATOR, true ) ) + if ( !CommandLine()->FindParm( "-script_dotslash_read" ) && !V_RemoveDotSlashes( pszFullName, CORRECT_PATH_SEPARATOR, true ) ) { DevWarning( 2, "Invalid file location : %s\n", szFile ); return NULL; @@ -1143,7 +1144,7 @@ bool CScriptReadWriteFile::FileExists( const char *szFile ) char pszFullName[MAX_PATH]; V_snprintf( pszFullName, sizeof(pszFullName), SCRIPT_RW_FULL_PATH_FMT, szFile ); - if ( !V_RemoveDotSlashes( pszFullName, CORRECT_PATH_SEPARATOR, true ) ) + if ( !CommandLine()->FindParm( "-script_dotslash_read" ) && !V_RemoveDotSlashes( pszFullName, CORRECT_PATH_SEPARATOR, true ) ) { DevWarning( 2, "Invalid file location : %s\n", szFile ); return NULL; @@ -1224,7 +1225,7 @@ HSCRIPT CScriptReadWriteFile::KeyValuesRead( const char *szFile ) char pszFullName[MAX_PATH]; V_snprintf( pszFullName, sizeof(pszFullName), SCRIPT_RW_FULL_PATH_FMT, szFile ); - if ( !V_RemoveDotSlashes( pszFullName, CORRECT_PATH_SEPARATOR, true ) ) + if ( !CommandLine()->FindParm( "-script_dotslash_read" ) && !V_RemoveDotSlashes( pszFullName, CORRECT_PATH_SEPARATOR, true ) ) { DevWarning( 2, "Invalid file location : %s\n", szFile ); return NULL; From 4adab159821c53a96e1cf19861e5e572f294d1ad Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Sat, 6 Jan 2024 13:00:41 -0600 Subject: [PATCH 83/90] Add better circumstantial criteria to followup responses --- sp/src/game/server/ai_expresserfollowup.cpp | 10 +++ sp/src/game/server/ai_playerally.cpp | 48 +++++++++++++ sp/src/game/server/ai_playerally.h | 3 + sp/src/game/server/ai_speech_new.h | 19 +++++- sp/src/game/server/ai_speechqueue.cpp | 74 +++++++++++++++++++-- sp/src/game/server/ai_speechqueue.h | 5 ++ 6 files changed, 153 insertions(+), 6 deletions(-) diff --git a/sp/src/game/server/ai_expresserfollowup.cpp b/sp/src/game/server/ai_expresserfollowup.cpp index 65575709..38a83a3d 100644 --- a/sp/src/game/server/ai_expresserfollowup.cpp +++ b/sp/src/game/server/ai_expresserfollowup.cpp @@ -88,6 +88,15 @@ static void DispatchComeback( CAI_ExpresserWithFollowup *pExpress, CBaseEntity * // add in any provided contexts from the parameters onto the ones stored in the followup criteria.Merge( followup.followup_contexts ); +#ifdef MAPBASE + if (CAI_ExpresserSink *pSink = dynamic_cast(pRespondent)) + { + criteria.AppendCriteria( "dist_from_issuer", UTIL_VarArgs( "%f", (pRespondent->GetAbsOrigin() - pSpeaker->GetAbsOrigin()).Length() ) ); + g_ResponseQueueManager.GetQueue()->AppendFollowupCriteria( followup.followup_concept, criteria, pSink->GetSinkExpresser(), pSink, pRespondent, pSpeaker, kDRT_SPECIFIC ); + + pSink->Speak( followup.followup_concept, &criteria ); + } +#else // This is kludgy and needs to be fixed in class hierarchy, but for now, try to guess at the most likely // kinds of targets and dispatch to them. if (CBaseMultiplayerPlayer *pPlayer = dynamic_cast(pRespondent)) @@ -99,6 +108,7 @@ static void DispatchComeback( CAI_ExpresserWithFollowup *pExpress, CBaseEntity * { pActor->Speak( followup.followup_concept, &criteria ); } +#endif } #if 0 diff --git a/sp/src/game/server/ai_playerally.cpp b/sp/src/game/server/ai_playerally.cpp index f7594feb..b264c74a 100644 --- a/sp/src/game/server/ai_playerally.cpp +++ b/sp/src/game/server/ai_playerally.cpp @@ -1762,6 +1762,54 @@ bool CAI_PlayerAlly::IsAllowedToSpeak( AIConcept_t concept, bool bRespondingToPl return true; } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: Specifically for player allies handling followup responses. +// Better-accounts for unknown concepts so that users are free in what they use. +//----------------------------------------------------------------------------- +bool CAI_PlayerAlly::IsAllowedToSpeakFollowup( AIConcept_t concept, CBaseEntity *pIssuer, bool bSpecific ) +{ + CAI_AllySpeechManager * pSpeechManager = GetAllySpeechManager(); + ConceptInfo_t * pInfo = pSpeechManager->GetConceptInfo( concept ); + ConceptCategory_t category = SPEECH_PRIORITY; // Must be SPEECH_PRIORITY to get around semaphore + + if ( !IsOkToSpeak( category, true ) ) + return false; + + // If this followup is specifically targeted towards us, speak if we're not already speaking + // If it's meant to be spoken by anyone, respect speech delay and semaphore + if ( bSpecific ) + { + if ( !GetExpresser()->CanSpeakAfterMyself() ) + return false; + } + else + { + if ( !GetExpresser()->CanSpeak() ) + return false; + + CAI_TimedSemaphore *pSemaphore = GetExpresser()->GetMySpeechSemaphore( this ); + if ( pSemaphore && !pSemaphore->IsAvailable( this ) ) + { + // Only if the semaphore holder isn't the one dispatching the followup + if ( pSemaphore->GetOwner() != pIssuer ) + return false; + } + } + + if ( !pSpeechManager->ConceptDelayExpired( concept ) ) + return false; + + if ( ( pInfo && pInfo->flags & AICF_SPEAK_ONCE ) && GetExpresser()->SpokeConcept( concept ) ) + return false; + + if ( !GetExpresser()->CanSpeakConcept( concept ) ) + return false; + + return true; +} +#endif + //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- bool CAI_PlayerAlly::SpeakIfAllowed( AIConcept_t concept, const char *modifiers, bool bRespondingToPlayer, char *pszOutResponseChosen, size_t bufsize ) diff --git a/sp/src/game/server/ai_playerally.h b/sp/src/game/server/ai_playerally.h index 93448ec7..7ab7f9b2 100644 --- a/sp/src/game/server/ai_playerally.h +++ b/sp/src/game/server/ai_playerally.h @@ -392,6 +392,9 @@ public: bool ShouldSpeakRandom( AIConcept_t concept, int iChance ); bool IsAllowedToSpeak( AIConcept_t concept, bool bRespondingToPlayer = false ); +#ifdef MAPBASE + bool IsAllowedToSpeakFollowup( AIConcept_t concept, CBaseEntity *pIssuer, bool bSpecific ); +#endif virtual bool SpeakIfAllowed( AIConcept_t concept, const char *modifiers = NULL, bool bRespondingToPlayer = false, char *pszOutResponseChosen = NULL, size_t bufsize = 0 ); #ifdef MAPBASE virtual bool SpeakIfAllowed( AIConcept_t concept, AI_CriteriaSet& modifiers, bool bRespondingToPlayer = false, char *pszOutResponseChosen = NULL, size_t bufsize = 0 ); diff --git a/sp/src/game/server/ai_speech_new.h b/sp/src/game/server/ai_speech_new.h index c61f6f03..8290d471 100644 --- a/sp/src/game/server/ai_speech_new.h +++ b/sp/src/game/server/ai_speech_new.h @@ -126,6 +126,13 @@ public: virtual void OnSpokeConcept( AIConcept_t concept, AI_Response *response ) {}; virtual void OnStartSpeaking() {} virtual bool UseSemaphore() { return true; } + +#ifdef MAPBASE + // Works around issues with CAI_ExpresserHost<> class hierarchy + virtual CAI_Expresser *GetSinkExpresser() { return NULL; } + virtual bool IsAllowedToSpeakFollowup( AIConcept_t concept, CBaseEntity *pIssuer, bool bSpecific ) { return true; } + virtual bool Speak( AIConcept_t concept, AI_CriteriaSet *pCriteria, char *pszOutResponseChosen = NULL, size_t bufsize = 0, IRecipientFilter *filter = NULL ) { return false; } +#endif }; struct ConceptHistory_t @@ -244,9 +251,15 @@ public: static bool RunScriptResponse( CBaseEntity *pTarget, const char *response, AI_CriteriaSet *criteria, bool file ); #endif +#ifdef MAPBASE +public: +#else protected: +#endif CAI_TimedSemaphore *GetMySpeechSemaphore( CBaseEntity *pNpc ); +protected: + bool SpeakRawScene( const char *pszScene, float delay, AI_Response *response, IRecipientFilter *filter = NULL ); // This will create a fake .vcd/CChoreoScene to wrap the sound to be played #ifdef MAPBASE @@ -311,11 +324,15 @@ private: // template -class CAI_ExpresserHost : public BASE_NPC, protected CAI_ExpresserSink +class CAI_ExpresserHost : public BASE_NPC, public CAI_ExpresserSink { DECLARE_CLASS_NOFRIEND( CAI_ExpresserHost, BASE_NPC ); public: +#ifdef MAPBASE + CAI_Expresser *GetSinkExpresser() { return this->GetExpresser(); } +#endif + virtual void NoteSpeaking( float duration, float delay ); virtual bool Speak( AIConcept_t concept, const char *modifiers = NULL, char *pszOutResponseChosen = NULL, size_t bufsize = 0, IRecipientFilter *filter = NULL ); diff --git a/sp/src/game/server/ai_speechqueue.cpp b/sp/src/game/server/ai_speechqueue.cpp index 7e8bf055..58fccde6 100644 --- a/sp/src/game/server/ai_speechqueue.cpp +++ b/sp/src/game/server/ai_speechqueue.cpp @@ -11,6 +11,9 @@ #include "ai_baseactor.h" #include "ai_speech.h" //#include "flex_expresser.h" +#ifdef MAPBASE +#include "sceneentity.h" +#endif // memdbgon must be the last include file in a .cpp file!!! #include @@ -170,15 +173,25 @@ void CResponseQueue::RemoveExpresserHost(CBaseEntity *host) } } +#ifdef MAPBASE +/// Get the expresser for a base entity. +static CAI_Expresser *InferExpresserFromBaseEntity(CBaseEntity * RESTRICT pEnt, CAI_ExpresserSink **ppSink = NULL) +{ + if ( CAI_ExpresserSink *pSink = dynamic_cast(pEnt) ) + { + if (ppSink) + *ppSink = pSink; + return pSink->GetSinkExpresser(); + } + + return NULL; +} +#else /// Get the expresser for a base entity. /// TODO: Kind of an ugly hack until I get the class hierarchy straightened out. static CAI_Expresser *InferExpresserFromBaseEntity(CBaseEntity * RESTRICT pEnt) { -#ifdef MAPBASE - if ( CBasePlayer *pPlayer = ToBasePlayer(pEnt) ) -#else if ( CBaseMultiplayerPlayer *pPlayer = dynamic_cast(pEnt) ) -#endif { return pPlayer->GetExpresser(); } @@ -197,6 +210,7 @@ static CAI_Expresser *InferExpresserFromBaseEntity(CBaseEntity * RESTRICT pEnt) return NULL; } } +#endif void CResponseQueue::CDeferredResponse::Quash() @@ -205,6 +219,23 @@ void CResponseQueue::CDeferredResponse::Quash() m_fDispatchTime = 0; } +#ifdef MAPBASE +void CResponseQueue::AppendFollowupCriteria( AIConcept_t concept, AI_CriteriaSet &set, CAI_Expresser *pEx, + CAI_ExpresserSink *pSink, CBaseEntity *pTarget, CBaseEntity *pIssuer, DeferredResponseTarget_t nTargetType ) +{ + // Allows control over which followups interrupt speech routines + set.AppendCriteria( "followup_allowed_to_speak", (pSink->IsAllowedToSpeakFollowup( concept, pIssuer, nTargetType == kDRT_SPECIFIC )) ? "1" : "0" ); + + set.AppendCriteria( "followup_target_type", UTIL_VarArgs( "%i", (int)nTargetType ) ); + + // NOTE: This assumes any expresser entity derived from CBaseFlex is also derived from CBaseCombatCharacter + if (pTarget->IsCombatCharacter()) + set.AppendCriteria( "is_speaking", (pEx->IsSpeaking() || IsRunningScriptedSceneWithSpeechAndNotPaused( assert_cast(pTarget) )) ? "1" : "0" ); + else + set.AppendCriteria( "is_speaking", "0" ); +} +#endif + bool CResponseQueue::DispatchOneResponse(CDeferredResponse &response) { // find the target. @@ -272,9 +303,15 @@ bool CResponseQueue::DispatchOneResponse(CDeferredResponse &response) continue; // too far } +#ifdef MAPBASE + CAI_ExpresserSink *pSink = NULL; + pEx = InferExpresserFromBaseEntity( pTarget, &pSink ); +#else pEx = InferExpresserFromBaseEntity(pTarget); +#endif if ( !pEx || pTarget == pIssuer ) continue; + AI_CriteriaSet characterCriteria; pEx->GatherCriteria(&characterCriteria, response.m_concept, NULL); characterCriteria.Merge(&deferredCriteria); @@ -282,6 +319,11 @@ bool CResponseQueue::DispatchOneResponse(CDeferredResponse &response) { characterCriteria.AppendCriteria( "dist_from_issuer", UTIL_VarArgs( "%f", sqrt(distIssuerToTargetSq) ) ); } + +#ifdef MAPBASE + AppendFollowupCriteria( response.m_concept, characterCriteria, pEx, pSink, pTarget, pIssuer, kDRT_ALL ); +#endif + AI_Response prospectiveResponse; if ( pEx->FindResponse( prospectiveResponse, response.m_concept, &characterCriteria ) ) { @@ -304,14 +346,26 @@ bool CResponseQueue::DispatchOneResponse(CDeferredResponse &response) return false; // we're done right here. // Get the expresser for the target. +#ifdef MAPBASE + CAI_ExpresserSink *pSink = NULL; + pEx = InferExpresserFromBaseEntity( pTarget, &pSink ); +#else pEx = InferExpresserFromBaseEntity(pTarget); +#endif if (!pEx) return false; - AI_CriteriaSet characterCriteria; pEx->GatherCriteria(&characterCriteria, response.m_concept, NULL); characterCriteria.Merge(&deferredCriteria); +#ifdef MAPBASE + if ( pIssuer ) + { + characterCriteria.AppendCriteria( "dist_from_issuer", UTIL_VarArgs( "%f", (pTarget->GetAbsOrigin() - pIssuer->GetAbsOrigin()).Length() ) ); + } + + AppendFollowupCriteria( response.m_concept, characterCriteria, pEx, pSink, pTarget, pIssuer, kDRT_SPECIFIC ); +#endif pEx->Speak( response.m_concept, &characterCriteria ); return true; @@ -364,7 +418,12 @@ bool CResponseQueue::DispatchOneResponse_ThenANY( CDeferredResponse &response, A continue; // too far } +#ifdef MAPBASE + CAI_ExpresserSink *pSink = NULL; + pEx = InferExpresserFromBaseEntity( pTarget, &pSink ); +#else pEx = InferExpresserFromBaseEntity(pTarget); +#endif if ( !pEx ) continue; @@ -376,6 +435,11 @@ bool CResponseQueue::DispatchOneResponse_ThenANY( CDeferredResponse &response, A { characterCriteria.AppendCriteria( "dist_from_issuer", UTIL_VarArgs( "%f", sqrt(distIssuerToTargetSq) ) ); } + +#ifdef MAPBASE + AppendFollowupCriteria( response.m_concept, characterCriteria, pEx, pSink, pTarget, pIssuer, kDRT_ANY ); +#endif + AI_Response prospectiveResponse; #ifdef MAPBASE diff --git a/sp/src/game/server/ai_speechqueue.h b/sp/src/game/server/ai_speechqueue.h index 15101b70..87ab064f 100644 --- a/sp/src/game/server/ai_speechqueue.h +++ b/sp/src/game/server/ai_speechqueue.h @@ -116,6 +116,11 @@ public: inline int GetNumExpresserTargets() const; inline CBaseEntity *GetExpresserHost(int which) const; +#ifdef MAPBASE + void AppendFollowupCriteria( AIConcept_t concept, AI_CriteriaSet &set, CAI_Expresser *pEx, + CAI_ExpresserSink *pSink, CBaseEntity *pTarget, CBaseEntity *pIssuer, DeferredResponseTarget_t nTargetType ); +#endif + protected: /// Actually send off one response to a consumer /// Return true if dispatch succeeded From 19702d067d994595a6f6790420f7955a93461c24 Mon Sep 17 00:00:00 2001 From: samisalreadytaken <46823719+samisalreadytaken@users.noreply.github.com> Date: Fri, 26 Jan 2024 20:27:37 +0300 Subject: [PATCH 84/90] Fix weapon_custom_scripted ItemPreFrame base call --- sp/src/game/shared/mapbase/weapon_custom_scripted.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sp/src/game/shared/mapbase/weapon_custom_scripted.cpp b/sp/src/game/shared/mapbase/weapon_custom_scripted.cpp index 4d5c744d..000fe416 100644 --- a/sp/src/game/shared/mapbase/weapon_custom_scripted.cpp +++ b/sp/src/game/shared/mapbase/weapon_custom_scripted.cpp @@ -334,7 +334,7 @@ void CWeaponCustomScripted::ItemPreFrame( void ) { SIMPLE_VOID_OVERRIDE( ItemPreFrame, NULL ); - BaseClass::ItemPostFrame(); + BaseClass::ItemPreFrame(); } void CWeaponCustomScripted::ItemPostFrame( void ) From a02f3db05bd1d01045b8787e87db5b7b7448c8f4 Mon Sep 17 00:00:00 2001 From: samisalreadytaken <46823719+samisalreadytaken@users.noreply.github.com> Date: Fri, 26 Jan 2024 23:36:39 +0300 Subject: [PATCH 85/90] Fix weapon_custom_scripted hook calls --- sp/src/game/shared/mapbase/weapon_custom_scripted.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sp/src/game/shared/mapbase/weapon_custom_scripted.cpp b/sp/src/game/shared/mapbase/weapon_custom_scripted.cpp index 000fe416..33cf3959 100644 --- a/sp/src/game/shared/mapbase/weapon_custom_scripted.cpp +++ b/sp/src/game/shared/mapbase/weapon_custom_scripted.cpp @@ -179,7 +179,7 @@ bool CWeaponCustomScripted::RunWeaponHook( ScriptHook_t &hook, HSCRIPT &cached, { if ( !cached ) { - if ( hook.CanRunInScope( m_ScriptScope ) ) + if ( m_ScriptScope.IsInitialized() && hook.CanRunInScope( m_ScriptScope ) ) { cached = hook.m_hFunc; } @@ -187,6 +187,7 @@ bool CWeaponCustomScripted::RunWeaponHook( ScriptHook_t &hook, HSCRIPT &cached, if (cached) { + hook.m_hFunc = cached; return hook.Call( m_ScriptScope, retVal, pArgs, false ); } From 976eb23299e88a012bc261098770768b1b5e0078 Mon Sep 17 00:00:00 2001 From: samisalreadytaken <46823719+samisalreadytaken@users.noreply.github.com> Date: Fri, 3 Nov 2023 22:08:28 +0300 Subject: [PATCH 86/90] Add vscript funcs Panel::RemoveTickSignal() CAI_BaseNPC::IsMedic() --- sp/src/game/client/mapbase/vscript_vgui.cpp | 6 ++++++ sp/src/game/server/ai_basenpc.cpp | 1 + sp/src/game/server/hl2/npc_citizen17.cpp | 1 - 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/sp/src/game/client/mapbase/vscript_vgui.cpp b/sp/src/game/client/mapbase/vscript_vgui.cpp index 8d08e8be..60e8aed0 100644 --- a/sp/src/game/client/mapbase/vscript_vgui.cpp +++ b/sp/src/game/client/mapbase/vscript_vgui.cpp @@ -1400,6 +1400,11 @@ public: { ivgui()->AddTickSignal( this->GetVPanel(), i ); } + + void RemoveTickSignal() + { + ivgui()->RemoveTickSignal( this->GetVPanel() ); + } #if SCRIPT_VGUI_SIGNAL_INTERFACE void AddActionSignalTarget( HSCRIPT messageTarget ) { @@ -1788,6 +1793,7 @@ public: DEFINE_SCRIPTFUNC( MakeReadyForUse, "" )\ DEFINE_SCRIPTFUNC( GetName, "" )\ DEFINE_SCRIPTFUNC( AddTickSignal, "" )\ + DEFINE_SCRIPTFUNC( RemoveTickSignal, "" )\ \ DEFINE_SCRIPTFUNC( GetParent, "" )\ DEFINE_SCRIPTFUNC( SetParent, "" )\ diff --git a/sp/src/game/server/ai_basenpc.cpp b/sp/src/game/server/ai_basenpc.cpp index 299aa5c4..5a2596d0 100644 --- a/sp/src/game/server/ai_basenpc.cpp +++ b/sp/src/game/server/ai_basenpc.cpp @@ -12333,6 +12333,7 @@ BEGIN_ENT_SCRIPTDESC( CAI_BaseNPC, CBaseCombatCharacter, "The base class all NPC DEFINE_SCRIPTFUNC( IsCommandable, "Check if the NPC is commandable." ) DEFINE_SCRIPTFUNC( IsInPlayerSquad, "Check if the NPC is in the player's squad." ) + DEFINE_SCRIPTFUNC( IsMedic, "Returns true if this NPC is a medic." ) DEFINE_SCRIPTFUNC_NAMED( VScriptGetCine, "GetCine", "Get the NPC's currently running scripted sequence if it has one." ) DEFINE_SCRIPTFUNC( GetScriptState, "Get the NPC's current scripted sequence state." ) diff --git a/sp/src/game/server/hl2/npc_citizen17.cpp b/sp/src/game/server/hl2/npc_citizen17.cpp index be80179a..02baaf07 100644 --- a/sp/src/game/server/hl2/npc_citizen17.cpp +++ b/sp/src/game/server/hl2/npc_citizen17.cpp @@ -412,7 +412,6 @@ ScriptHook_t CNPC_Citizen::g_Hook_SelectModel; BEGIN_ENT_SCRIPTDESC( CNPC_Citizen, CAI_BaseActor, "npc_citizen from Half-Life 2" ) - DEFINE_SCRIPTFUNC( IsMedic, "Returns true if this citizen is a medic." ) DEFINE_SCRIPTFUNC( IsAmmoResupplier, "Returns true if this citizen is an ammo resupplier." ) DEFINE_SCRIPTFUNC( CanHeal, "Returns true if this citizen is a medic or ammo resupplier currently able to heal/give ammo." ) From a6f9d0a0e29baeb61a53be9555d6e150a4926f73 Mon Sep 17 00:00:00 2001 From: samisalreadytaken <46823719+samisalreadytaken@users.noreply.github.com> Date: Tue, 5 Dec 2023 14:45:07 +0300 Subject: [PATCH 87/90] Fix CBaseEntity::ScriptGetKeyValue() returning cached string on invalid input --- sp/src/game/server/baseentity.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sp/src/game/server/baseentity.cpp b/sp/src/game/server/baseentity.cpp index 67c3b17e..2205a72f 100644 --- a/sp/src/game/server/baseentity.cpp +++ b/sp/src/game/server/baseentity.cpp @@ -10297,8 +10297,9 @@ bool CBaseEntity::ScriptAddOutput( const char *pszOutputName, const char *pszTar const char *CBaseEntity::ScriptGetKeyValue( const char *pszKeyName ) { static char szValue[128]; - GetKeyValue( pszKeyName, szValue, sizeof(szValue) ); - return szValue; + if ( GetKeyValue( pszKeyName, szValue, sizeof(szValue) ) ) + return szValue; + return NULL; } //----------------------------------------------------------------------------- From f1123ed1e74ae3fd278b40e3b9804517e8228f4c Mon Sep 17 00:00:00 2001 From: samisalreadytaken <46823719+samisalreadytaken@users.noreply.github.com> Date: Fri, 2 Feb 2024 17:06:12 +0300 Subject: [PATCH 88/90] Tidy up CSimpleCallChainer::PostScriptExecute() --- sp/src/vscript/vscript_squirrel.nut | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/sp/src/vscript/vscript_squirrel.nut b/sp/src/vscript/vscript_squirrel.nut index 5b76bbe3..54492423 100644 --- a/sp/src/vscript/vscript_squirrel.nut +++ b/sp/src/vscript/vscript_squirrel.nut @@ -118,15 +118,14 @@ class CSimpleCallChainer function PostScriptExecute() { - local func; - try { - func = scope[prefix]; - } catch(e) { - return; + if ( prefix in scope ) + { + local func = scope[prefix]; + if ( typeof func == "function" ) + { + chain.push(func); + } } - if (typeof(func) != "function") - return; - chain.push(func); } function Call() @@ -569,4 +568,4 @@ if (developer) __Documentation.RegisterHelp( "Lerp", "float Lerp(float, float, float)", "" ); __Documentation.RegisterHelp( "SimpleSpline", "float SimpleSpline(float)", "" ); } -)vscript"; \ No newline at end of file +)vscript"; From 8f7b70768e7f48db24cf1040d0afbc4ba9144dcb Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Sun, 4 Feb 2024 22:30:59 -0600 Subject: [PATCH 89/90] Updated README --- README | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README b/README index ead59b89..d9d11024 100644 --- a/README +++ b/README @@ -144,6 +144,8 @@ Direct contributions: =-- https://github.com/mapbase-source/source-sdk-2013/pull/206 (Fix CScriptNetMsgHelper::WriteEntity()) =-- https://github.com/mapbase-source/source-sdk-2013/pull/213 (VScript HUD visibility control, optimizations for 3D skybox angles/fake worldportals) =-- https://github.com/mapbase-source/source-sdk-2013/pull/229 (VScript VGUI HUD viewport parenting, game_text and vgui_text_display VScript font fallback) +=-- https://github.com/mapbase-source/source-sdk-2013/pull/261 (Misc VScript additions) +=-- https://github.com/mapbase-source/source-sdk-2013/pull/279 (weapon_custom_scripted fixes) == Contributions from z33ky: =-- https://github.com/mapbase-source/source-sdk-2013/pull/21 (Various GCC/Linux compilation fixes) From 6bd9bcba5d2c95708e377f8cc8926a31edc6b95a Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Sat, 24 Feb 2024 21:19:52 -0600 Subject: [PATCH 90/90] Increment version numbers to v7.2 --- sp/src/public/tier0/platform.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sp/src/public/tier0/platform.h b/sp/src/public/tier0/platform.h index 6d5751a8..457e5ec9 100644 --- a/sp/src/public/tier0/platform.h +++ b/sp/src/public/tier0/platform.h @@ -1281,8 +1281,8 @@ PLATFORM_INTERFACE bool Is64BitOS(); //----------------------------------------------------------------------------- // General Mapbase version constants compiled into projects for versioning purposes //----------------------------------------------------------------------------- -#define MAPBASE_VERSION "7.1" -#define MAPBASE_VER_INT 7100 // For use in #if in a similar fashion to macros like _MSC_VER +#define MAPBASE_VERSION "7.2" +#define MAPBASE_VER_INT 7200 // For use in #if in a similar fashion to macros like _MSC_VER #endif