Added an entity factory manager for custom weapons

This commit is contained in:
Peter Covington 2022-04-16 07:19:42 -04:00
parent f8a8d49be7
commit 5a0c406dbf
7 changed files with 282 additions and 0 deletions

View File

@ -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<ICustomWeapon*> (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<IEntityFactory*, unsigned short>& CustomWeaponsFactoryDictionary()
{
static CUtlDict<IEntityFactory*, unsigned short> dict;
return dict;
}
static CCustomWeaponSystem g_CustomWeaponsSystem;
CCustomWeaponSystem* CustomWeaponSystem()
{
return &g_CustomWeaponsSystem;
}

View File

@ -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<CustomClassName_t, unsigned short> m_ClassFactories;
};
CCustomWeaponSystem* CustomWeaponSystem();
template <class T>
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<DLLClassName> custom_weapon_##factoryName##_factory( #factoryName );
#endif // !CUSTOM_WEAPON_FACTORY_H

View File

@ -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"

View File

@ -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();

View File

@ -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();

View File

@ -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
//-----------------------------------------------------------------------------

View File

@ -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();