mirror of
https://github.com/celisej567/source-sdk-2013.git
synced 2026-09-23 20:09:45 +03:00
Initial Mapbase commit (squashed from mapbase-beta)
This commit is contained in:
897
sp/src/game/shared/mapbase/MapEdit.cpp
Normal file
897
sp/src/game/shared/mapbase/MapEdit.cpp
Normal file
@@ -0,0 +1,897 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: The flimsy MapEdit system that was
|
||||
// heavily inspired by Synergy's MapEdit, completely based on the Commentary System
|
||||
// and originally used for Lambda Fortress.
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#include "cbase.h"
|
||||
|
||||
#include "MapEdit.h"
|
||||
#include "filesystem.h"
|
||||
|
||||
#ifndef CLIENT_DLL
|
||||
#include <KeyValues.h>
|
||||
#include "utldict.h"
|
||||
#include "isaverestore.h"
|
||||
#include "eventqueue.h"
|
||||
#include "saverestore_utlvector.h"
|
||||
#include "ai_basenpc.h"
|
||||
#include "triggers.h"
|
||||
#include "mapbase/SystemConvarMod.h"
|
||||
#endif
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
#ifndef CLIENT_DLL
|
||||
|
||||
#define MAPEDIT_SPAWNED_SEMAPHORE "mapedit_semaphore"
|
||||
#define MAPEDIT_DEFAULT_FILE UTIL_VarArgs("maps/%s_auto.txt", STRING(gpGlobals->mapname))
|
||||
|
||||
ConVar mapedit_enabled("mapedit_enabled", "1", FCVAR_ARCHIVE, "Is automatic MapEdit enabled?");
|
||||
ConVar mapedit_stack("mapedit_stack", "1", FCVAR_ARCHIVE, "If multiple MapEdit scripts are loaded, should they stack or replace each other?");
|
||||
ConVar mapedit_debug("mapedit_debug", "0", FCVAR_NONE, "Should MapEdit give debug messages?");
|
||||
|
||||
inline void DebugMsg(const tchar *pMsg, ...)
|
||||
{
|
||||
if (mapedit_debug.GetBool() == true)
|
||||
{
|
||||
Msg(pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
//bool g_bMapEditAvailable;
|
||||
bool g_bMapEditLoaded = false;
|
||||
bool IsMapEditLoaded( void )
|
||||
{
|
||||
return g_bMapEditLoaded;
|
||||
}
|
||||
|
||||
bool IsAutoMapEditAllowed(void)
|
||||
{
|
||||
return mapedit_enabled.GetBool();
|
||||
}
|
||||
|
||||
void CV_GlobalChange_MapEdit( IConVar *var, const char *pOldString, float flOldValue );
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Game system for MapEdit stuff
|
||||
//-----------------------------------------------------------------------------
|
||||
class CMapEdit : public CAutoGameSystemPerFrame
|
||||
{
|
||||
public:
|
||||
DECLARE_DATADESC();
|
||||
|
||||
virtual void LevelInitPreEntity()
|
||||
{
|
||||
m_bMapEditConvarsChanging = false;
|
||||
CalculateAvailableState();
|
||||
}
|
||||
|
||||
void CalculateAvailableState( void )
|
||||
{
|
||||
// Set the available cvar if we can find commentary data for this level
|
||||
char szFullName[512];
|
||||
Q_snprintf(szFullName,sizeof(szFullName), "maps/%s_auto.txt", STRING( gpGlobals->mapname) );
|
||||
if ( filesystem->FileExists( szFullName ) )
|
||||
{
|
||||
bool bAllowed = IsAutoMapEditAllowed();
|
||||
g_bMapEditLoaded = bAllowed;
|
||||
//if (bAllowed)
|
||||
// gEntList.AddListenerEntity( this );
|
||||
}
|
||||
else
|
||||
{
|
||||
g_bMapEditLoaded = false;
|
||||
//gEntList.RemoveListenerEntity( this );
|
||||
}
|
||||
}
|
||||
|
||||
virtual void LevelShutdownPreEntity()
|
||||
{
|
||||
ShutDownMapEdit();
|
||||
}
|
||||
|
||||
void ParseEntKVBlock( CBaseEntity *pNode, KeyValues *pkvNode )
|
||||
{
|
||||
KeyValues *pkvNodeData = pkvNode->GetFirstSubKey();
|
||||
while ( pkvNodeData )
|
||||
{
|
||||
// Handle the connections block
|
||||
if ( !Q_strcmp(pkvNodeData->GetName(), "connections") )
|
||||
{
|
||||
ParseEntKVBlock( pNode, pkvNodeData );
|
||||
}
|
||||
else
|
||||
{
|
||||
#define MAPEDIT_STRING_LENGTH_MAX 1024
|
||||
|
||||
const char *pszValue = pkvNodeData->GetString();
|
||||
Assert( Q_strlen(pszValue) < MAPEDIT_STRING_LENGTH_MAX );
|
||||
if ( Q_strnchr(pszValue, '^', MAPEDIT_STRING_LENGTH_MAX) )
|
||||
{
|
||||
// We want to support quotes in our strings so that we can specify multiple parameters in
|
||||
// an output inside our commentary files. We convert ^s to "s here.
|
||||
char szTmp[MAPEDIT_STRING_LENGTH_MAX];
|
||||
Q_strncpy( szTmp, pszValue, MAPEDIT_STRING_LENGTH_MAX );
|
||||
int len = Q_strlen( szTmp );
|
||||
for ( int i = 0; i < len; i++ )
|
||||
{
|
||||
if ( szTmp[i] == '^' )
|
||||
{
|
||||
szTmp[i] = '"';
|
||||
}
|
||||
}
|
||||
|
||||
pszValue = szTmp;
|
||||
}
|
||||
|
||||
char cOperatorChar = pszValue[0];
|
||||
if (cOperatorChar == '+')
|
||||
{
|
||||
pszValue++;
|
||||
char szExistingValue[MAPEDIT_STRING_LENGTH_MAX];
|
||||
if (pNode->GetKeyValue(pkvNodeData->GetName(), szExistingValue, sizeof(szExistingValue) ))
|
||||
{
|
||||
// Right now, this only supports adding floats/integers.
|
||||
// Add Vector support later.
|
||||
float flResult = atof(szExistingValue) + atof(pszValue);
|
||||
pszValue = UTIL_VarArgs("%f", flResult);
|
||||
}
|
||||
}
|
||||
else if (cOperatorChar == '-')
|
||||
{
|
||||
pszValue++;
|
||||
char szExistingValue[MAPEDIT_STRING_LENGTH_MAX];
|
||||
if (pNode->GetKeyValue(pkvNodeData->GetName(), szExistingValue, sizeof(szExistingValue) ))
|
||||
{
|
||||
// Right now, this only supports subtracting floats/integers.
|
||||
// Add Vector support later.
|
||||
float flResult = atof(szExistingValue) - atof(pszValue);
|
||||
pszValue = UTIL_VarArgs("%f", flResult);
|
||||
}
|
||||
}
|
||||
else if (cOperatorChar == '|' /*&& pszValue[1] == '='*/)
|
||||
{
|
||||
pszValue++;
|
||||
char szExistingValue[MAPEDIT_STRING_LENGTH_MAX];
|
||||
if (pNode->GetKeyValue(pkvNodeData->GetName(), szExistingValue, sizeof(szExistingValue)))
|
||||
{
|
||||
int iResult = atoi(szExistingValue) | atoi(pszValue);
|
||||
pszValue = UTIL_VarArgs("%i", iResult);
|
||||
}
|
||||
}
|
||||
|
||||
pNode->KeyValue(pkvNodeData->GetName(), pszValue);
|
||||
}
|
||||
|
||||
pkvNodeData = pkvNodeData->GetNextKey();
|
||||
}
|
||||
}
|
||||
|
||||
virtual void LevelInitPostEntity( void )
|
||||
{
|
||||
if ( !IsMapEditLoaded() )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if ( gpGlobals->eLoadType == MapLoad_LoadGame || gpGlobals->eLoadType == MapLoad_Background )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_bMapEditLoadedMidGame = false;
|
||||
InitMapEdit();
|
||||
|
||||
//IGameEvent *event = gameeventmanager->CreateEvent( "playing_mapedit" );
|
||||
//gameeventmanager->FireEventClientSide( event );
|
||||
}
|
||||
|
||||
bool MapEditConvarsChanging( void )
|
||||
{
|
||||
return m_bMapEditConvarsChanging;
|
||||
}
|
||||
|
||||
void SetMapEditConvarsChanging( bool bChanging )
|
||||
{
|
||||
m_bMapEditConvarsChanging = bChanging;
|
||||
}
|
||||
|
||||
void ConvarChanged( IConVar *pConVar, const char *pOldString, float flOldValue )
|
||||
{
|
||||
ConVarRef var( pConVar );
|
||||
|
||||
// A convar has been changed by a commentary node. We need to store
|
||||
// the old state. If the engine shuts down, we need to restore any
|
||||
// convars that the commentary changed to their previous values.
|
||||
for ( int i = 0; i < m_ModifiedConvars.Count(); i++ )
|
||||
{
|
||||
// If we find it, just update the current value
|
||||
if ( !Q_strncmp( var.GetName(), m_ModifiedConvars[i].pszConvar, MAX_MODIFIED_CONVAR_STRING ) )
|
||||
{
|
||||
Q_strncpy( m_ModifiedConvars[i].pszCurrentValue, var.GetString(), MAX_MODIFIED_CONVAR_STRING );
|
||||
//Msg(" Updating Convar %s: value %s (org %s)\n", m_ModifiedConvars[i].pszConvar, m_ModifiedConvars[i].pszCurrentValue, m_ModifiedConvars[i].pszOrgValue );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// We didn't find it in our list, so add it
|
||||
modifiedconvars_t newConvar;
|
||||
Q_strncpy( newConvar.pszConvar, var.GetName(), MAX_MODIFIED_CONVAR_STRING );
|
||||
Q_strncpy( newConvar.pszCurrentValue, var.GetString(), MAX_MODIFIED_CONVAR_STRING );
|
||||
Q_strncpy( newConvar.pszOrgValue, pOldString, MAX_MODIFIED_CONVAR_STRING );
|
||||
m_ModifiedConvars.AddToTail( newConvar );
|
||||
|
||||
/*
|
||||
Msg(" Commentary changed '%s' to '%s' (was '%s')\n", var->GetName(), var->GetString(), pOldString );
|
||||
Msg(" Convars stored: %d\n", m_ModifiedConvars.Count() );
|
||||
for ( int i = 0; i < m_ModifiedConvars.Count(); i++ )
|
||||
{
|
||||
Msg(" Convar %d: %s, value %s (org %s)\n", i, m_ModifiedConvars[i].pszConvar, m_ModifiedConvars[i].pszCurrentValue, m_ModifiedConvars[i].pszOrgValue );
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
CBaseEntity *FindMapEditEntity( CBaseEntity *pStartEntity, const char *szName, const char *szValue = NULL )
|
||||
{
|
||||
CBaseEntity *pEntity = NULL;
|
||||
DebugMsg("MapEdit Find Debug: Starting Search, Name: %s, Value: %s\n", szName, szValue);
|
||||
|
||||
// First, find by targetname/classname
|
||||
pEntity = gEntList.FindEntityGeneric(pStartEntity, szName);
|
||||
|
||||
if (!pEntity)
|
||||
{
|
||||
DebugMsg("MapEdit Find Debug: \"%s\" not found as targetname or classname\n", szName);
|
||||
|
||||
if (szValue)
|
||||
{
|
||||
if (!Q_strnicmp(szName, "#find_", 6))
|
||||
{
|
||||
const char *pName = szName + 6;
|
||||
if (!Q_stricmp(pName, "by_keyfield"))
|
||||
{
|
||||
char key[64];
|
||||
char value[64];
|
||||
|
||||
// Separate key from value
|
||||
char *delimiter = Q_strstr(szValue, " ");
|
||||
if (delimiter)
|
||||
{
|
||||
Q_strncpy(key, szValue, MIN((delimiter - szValue) + 1, sizeof(key)));
|
||||
Q_strncpy(value, delimiter + 1, sizeof(value));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Assume the value doesn't matter and we're just looking for the key
|
||||
Q_strncpy(key, szValue, sizeof(key));
|
||||
}
|
||||
|
||||
if (!key)
|
||||
{
|
||||
Warning("MapEdit: Possible find_by_keyfield syntax error: key not detected in \"%s\"\n", szValue);
|
||||
}
|
||||
|
||||
// Find entities with matching keyfield
|
||||
variant_t variant;
|
||||
const CEntInfo *pInfo = pStartEntity ? gEntList.GetEntInfoPtr(pStartEntity->GetRefEHandle())->m_pNext : gEntList.FirstEntInfo();
|
||||
for (; pInfo; pInfo = pInfo->m_pNext)
|
||||
{
|
||||
CBaseEntity *ent = (CBaseEntity *)pInfo->m_pEntity;
|
||||
if (!ent)
|
||||
{
|
||||
DevWarning("NULL entity in global entity list!\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!ent->edict())
|
||||
continue;
|
||||
|
||||
if (ent->ReadKeyField(key, &variant))
|
||||
{
|
||||
// Does the value matter?
|
||||
if (value)
|
||||
{
|
||||
if (Q_stricmp(variant.String(), value) == 0)
|
||||
{
|
||||
// The entity has the keyfield and it matches the value.
|
||||
return ent;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// The value doesn't matter and the entity has the keyfield.
|
||||
return ent;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (!Q_stricmp(pName, "by_origin"))
|
||||
{
|
||||
// Find entities at this origin
|
||||
Vector vecOrigin;
|
||||
UTIL_StringToVector(vecOrigin.Base(), szValue);
|
||||
if (vecOrigin.IsValid())
|
||||
{
|
||||
DebugMsg("MapEdit Find Debug: \"%s\" is valid vector\n", szValue);
|
||||
const CEntInfo *pInfo = pStartEntity ? gEntList.GetEntInfoPtr(pStartEntity->GetRefEHandle())->m_pNext : gEntList.FirstEntInfo();
|
||||
for (; pInfo; pInfo = pInfo->m_pNext)
|
||||
{
|
||||
CBaseEntity *ent = (CBaseEntity *)pInfo->m_pEntity;
|
||||
if (!ent)
|
||||
{
|
||||
DevWarning("NULL entity in global entity list!\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!ent->edict())
|
||||
continue;
|
||||
|
||||
if (ent->GetLocalOrigin() == vecOrigin)
|
||||
return ent;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (!pStartEntity)
|
||||
{
|
||||
// Try the entity index
|
||||
int iEntIndex = atoi(szName);
|
||||
if (!pStartEntity && UTIL_EntityByIndex(iEntIndex))
|
||||
{
|
||||
DebugMsg("MapEdit Find Debug: \"%s\" is valid index\n", szName);
|
||||
return CBaseEntity::Instance(iEntIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DebugMsg("MapEdit Find Debug: \"%s\" not found\n", szName);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
DebugMsg("MapEdit Find Debug: \"%s\" found as targetname or classname\n", szName);
|
||||
return pEntity;
|
||||
}
|
||||
|
||||
void InitMapEdit( const char* pFile = MAPEDIT_DEFAULT_FILE )
|
||||
{
|
||||
// Install the global cvar callback
|
||||
cvar->InstallGlobalChangeCallback( CV_GlobalChange_MapEdit );
|
||||
|
||||
|
||||
// If we find the commentary semaphore, the commentary entities already exist.
|
||||
// This occurs when you transition back to a map that has saved commentary nodes in it.
|
||||
if ( gEntList.FindEntityByName( NULL, MAPEDIT_SPAWNED_SEMAPHORE ) )
|
||||
return;
|
||||
|
||||
// Spawn the commentary semaphore entity
|
||||
CBaseEntity *pSemaphore = CreateEntityByName( "info_target" );
|
||||
pSemaphore->SetName( MAKE_STRING(MAPEDIT_SPAWNED_SEMAPHORE) );
|
||||
|
||||
bool oldLock = engine->LockNetworkStringTables( false );
|
||||
|
||||
SpawnMapEdit(pFile);
|
||||
|
||||
engine->LockNetworkStringTables( oldLock );
|
||||
}
|
||||
|
||||
void LoadFromFormat_Original(KeyValues *pkvFile)
|
||||
{
|
||||
KeyValues *pkvNode = pkvFile->GetFirstSubKey();
|
||||
while ( pkvNode )
|
||||
{
|
||||
const char *pNodeName = pkvNode->GetName();
|
||||
if (FStrEq(pNodeName, "create"))
|
||||
{
|
||||
KeyValues *pkvClassname = pkvNode->GetFirstSubKey();
|
||||
while (pkvClassname)
|
||||
{
|
||||
pNodeName = pkvClassname->GetName();
|
||||
|
||||
CBaseEntity *pNode = CreateEntityByName(pNodeName);
|
||||
if (pNode)
|
||||
{
|
||||
ParseEntKVBlock(pNode, pkvClassname);
|
||||
|
||||
EHANDLE hHandle;
|
||||
hHandle = pNode;
|
||||
m_hSpawnedEntities.AddToTail(hHandle);
|
||||
DebugMsg("MapEdit Debug: Spawned entity %s\n", pNodeName);
|
||||
}
|
||||
else
|
||||
{
|
||||
Warning("MapEdit: Failed to spawn mapedit entity, type: '%s'\n", pNodeName);
|
||||
}
|
||||
|
||||
pkvClassname = pkvClassname->GetNextKey();
|
||||
}
|
||||
pkvClassname->deleteThis();
|
||||
}
|
||||
else if (FStrEq(pNodeName, "edit"))
|
||||
{
|
||||
KeyValues *pName = pkvNode->GetFirstSubKey();
|
||||
while (pName)
|
||||
{
|
||||
pNodeName = pName->GetName();
|
||||
|
||||
CBaseEntity *pNode = NULL;
|
||||
|
||||
pNode = FindMapEditEntity(NULL, pNodeName, pName->GetString());
|
||||
|
||||
while (pNode)
|
||||
{
|
||||
DebugMsg("MapEdit Debug: Editing %s (%s)\n", pNodeName, pNode->GetDebugName());
|
||||
|
||||
ParseEntKVBlock(pNode, pName);
|
||||
pNode = FindMapEditEntity(pNode, pNodeName, pName->GetString());
|
||||
}
|
||||
|
||||
pName = pName->GetNextKey();
|
||||
}
|
||||
pName->deleteThis();
|
||||
}
|
||||
else if (FStrEq(pNodeName, "delete"))
|
||||
{
|
||||
KeyValues *pName = pkvNode->GetFirstSubKey();
|
||||
while (pName)
|
||||
{
|
||||
pNodeName = pName->GetName();
|
||||
|
||||
CBaseEntity *pNode = NULL;
|
||||
|
||||
pNode = FindMapEditEntity(NULL, pNodeName, pName->GetString());
|
||||
|
||||
while (pNode)
|
||||
{
|
||||
DebugMsg("MapEdit Debug: Deleting %s (%s)\n", pNodeName, pNode->GetDebugName());
|
||||
|
||||
UTIL_Remove(pNode);
|
||||
pNode = FindMapEditEntity(pNode, pNodeName, pName->GetString());
|
||||
}
|
||||
|
||||
pName = pName->GetNextKey();
|
||||
}
|
||||
pName->deleteThis();
|
||||
}
|
||||
else if (FStrEq(pNodeName, "fire"))
|
||||
{
|
||||
KeyValues *pName = pkvNode->GetFirstSubKey();
|
||||
while (pName)
|
||||
{
|
||||
pNodeName = pName->GetName();
|
||||
|
||||
const char *pInputName = NULL;
|
||||
variant_t varInputParam;
|
||||
float flInputDelay = 0.0f;
|
||||
CBaseEntity *pActivator = NULL;
|
||||
CBaseEntity *pCaller = NULL;
|
||||
int iOutputID = 0;
|
||||
|
||||
char *pszValue = strdup(pName->GetString());
|
||||
int iter = 0;
|
||||
char *inputparams = strtok(pszValue, ",");
|
||||
while (inputparams)
|
||||
{
|
||||
switch (iter)
|
||||
{
|
||||
// Input name
|
||||
case 0:
|
||||
pInputName = inputparams; break;
|
||||
// Input parameter
|
||||
case 1:
|
||||
varInputParam.SetString(AllocPooledString(inputparams)); break;
|
||||
// Input delay
|
||||
case 2:
|
||||
flInputDelay = atof(inputparams); break;
|
||||
// Activator
|
||||
case 3:
|
||||
pActivator = gEntList.FindEntityByName(NULL, inputparams); break;
|
||||
// Caller
|
||||
case 4:
|
||||
pCaller = gEntList.FindEntityByName(NULL, inputparams); break;
|
||||
// Output ID
|
||||
case 5:
|
||||
iOutputID = atoi(inputparams); break;
|
||||
}
|
||||
iter++;
|
||||
inputparams = strtok(NULL, ",");
|
||||
}
|
||||
|
||||
DebugMsg("MapEdit Debug: Firing input %s on %s\n", pInputName, pNodeName);
|
||||
g_EventQueue.AddEvent(pNodeName, pInputName, varInputParam, flInputDelay, pActivator, pCaller, iOutputID);
|
||||
|
||||
pName = pName->GetNextKey();
|
||||
}
|
||||
}
|
||||
else if (FStrEq(pNodeName, "console"))
|
||||
{
|
||||
KeyValues *pkvNodeData = pkvNode->GetFirstSubKey();
|
||||
const char *pKey;
|
||||
const char *pValue;
|
||||
while (pkvNodeData)
|
||||
{
|
||||
SetMapEditConvarsChanging(true);
|
||||
|
||||
pKey = pkvNodeData->GetName();
|
||||
pValue = pkvNodeData->GetString();
|
||||
|
||||
engine->ServerCommand(UTIL_VarArgs("%s %s", pKey, pValue));
|
||||
engine->ServerCommand("mapedit_cvarsnotchanging\n");
|
||||
|
||||
pkvNodeData = pkvNodeData->GetNextKey();
|
||||
}
|
||||
pkvNodeData->deleteThis();
|
||||
}
|
||||
|
||||
pkvNode = pkvNode->GetNextKey();
|
||||
}
|
||||
pkvNode->deleteThis();
|
||||
}
|
||||
|
||||
void SpawnMapEdit(const char *pFile = NULL)
|
||||
{
|
||||
// Find the commentary file
|
||||
char szFullName[512];
|
||||
if (pFile == NULL)
|
||||
{
|
||||
DebugMsg("MapEdit Debug: NULL file, loading default\n");
|
||||
Q_snprintf(szFullName,sizeof(szFullName), "maps/%s_auto.txt", STRING( gpGlobals->mapname ));
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugMsg("MapEdit Debug: File not NULL, loading %s\n", pFile);
|
||||
Q_snprintf(szFullName,sizeof(szFullName), pFile);
|
||||
}
|
||||
KeyValues *pkvFile = new KeyValues( "MapEdit" );
|
||||
if ( pkvFile->LoadFromFile( filesystem, szFullName, "MOD" ) )
|
||||
{
|
||||
Msg( "MapEdit: Loading MapEdit data from %s. \n", szFullName );
|
||||
|
||||
if (gpGlobals->eLoadType != MapLoad_LoadGame)
|
||||
{
|
||||
// Support for multiple formats
|
||||
const char *szVersion = pkvFile->GetString("version", "original");
|
||||
if (FStrEq(szVersion, "original"))
|
||||
LoadFromFormat_Original(pkvFile);
|
||||
// TODO: More formats
|
||||
}
|
||||
|
||||
// Then activate all the entities
|
||||
for ( int i = 0; i < m_hSpawnedEntities.Count(); i++ )
|
||||
{
|
||||
DispatchSpawn(m_hSpawnedEntities[i]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Msg( "MapEdit: Could not find MapEdit data file '%s'. \n", szFullName );
|
||||
}
|
||||
|
||||
pkvFile->deleteThis();
|
||||
}
|
||||
|
||||
void ShutDownMapEdit( void )
|
||||
{
|
||||
// Destroy all the entities created by commentary
|
||||
for ( int i = m_hSpawnedEntities.Count()-1; i >= 0; i-- )
|
||||
{
|
||||
if ( m_hSpawnedEntities[i] )
|
||||
{
|
||||
UTIL_Remove( m_hSpawnedEntities[i] );
|
||||
}
|
||||
}
|
||||
m_hSpawnedEntities.Purge();
|
||||
|
||||
// Remove the semaphore
|
||||
CBaseEntity *pSemaphore = gEntList.FindEntityByName( NULL, MAPEDIT_SPAWNED_SEMAPHORE );
|
||||
if ( pSemaphore )
|
||||
{
|
||||
UTIL_Remove( pSemaphore );
|
||||
}
|
||||
|
||||
// Remove our global convar callback
|
||||
cvar->RemoveGlobalChangeCallback( CV_GlobalChange_MapEdit );
|
||||
|
||||
// Reset any convars that have been changed by the commentary
|
||||
for ( int i = 0; i < m_ModifiedConvars.Count(); i++ )
|
||||
{
|
||||
ConVar *pConVar = (ConVar *)cvar->FindVar( m_ModifiedConvars[i].pszConvar );
|
||||
if ( pConVar )
|
||||
{
|
||||
pConVar->SetValue( m_ModifiedConvars[i].pszOrgValue );
|
||||
}
|
||||
}
|
||||
m_ModifiedConvars.Purge();
|
||||
}
|
||||
|
||||
void SetMapEdit( bool bMapEdit, const char *pFile = NULL )
|
||||
{
|
||||
//g_bMapEditLoaded = bMapEdit;
|
||||
//CalculateAvailableState();
|
||||
|
||||
// If we're turning on commentary, create all the entities.
|
||||
if ( bMapEdit )
|
||||
{
|
||||
if (filesystem->FileExists(pFile) || pFile == NULL)
|
||||
{
|
||||
g_bMapEditLoaded = true;
|
||||
m_bMapEditLoadedMidGame = true;
|
||||
InitMapEdit(pFile);
|
||||
}
|
||||
else
|
||||
{
|
||||
Warning("MapEdit: No such file \"%s\"!\n", pFile);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ShutDownMapEdit();
|
||||
}
|
||||
}
|
||||
|
||||
void OnRestore( void )
|
||||
{
|
||||
cvar->RemoveGlobalChangeCallback( CV_GlobalChange_MapEdit );
|
||||
|
||||
if ( !IsMapEditLoaded() )
|
||||
return;
|
||||
|
||||
// Set any convars that have already been changed by the commentary before the save
|
||||
for ( int i = 0; i < m_ModifiedConvars.Count(); i++ )
|
||||
{
|
||||
ConVar *pConVar = (ConVar *)cvar->FindVar( m_ModifiedConvars[i].pszConvar );
|
||||
if ( pConVar )
|
||||
{
|
||||
//Msg(" Restoring Convar %s: value %s (org %s)\n", m_ModifiedConvars[i].pszConvar, m_ModifiedConvars[i].pszCurrentValue, m_ModifiedConvars[i].pszOrgValue );
|
||||
pConVar->SetValue( m_ModifiedConvars[i].pszCurrentValue );
|
||||
}
|
||||
}
|
||||
|
||||
// Install the global cvar callback
|
||||
cvar->InstallGlobalChangeCallback( CV_GlobalChange_MapEdit );
|
||||
}
|
||||
|
||||
bool MapEditLoadedMidGame( void )
|
||||
{
|
||||
return m_bMapEditLoadedMidGame;
|
||||
}
|
||||
|
||||
private:
|
||||
bool m_bMapEditConvarsChanging;
|
||||
bool m_bMapEditLoadedMidGame;
|
||||
|
||||
CUtlVector< modifiedconvars_t > m_ModifiedConvars;
|
||||
CUtlVector<EHANDLE> m_hSpawnedEntities;
|
||||
};
|
||||
|
||||
CMapEdit g_MapEdit;
|
||||
|
||||
BEGIN_DATADESC_NO_BASE( CMapEdit )
|
||||
|
||||
DEFINE_FIELD( m_bMapEditLoadedMidGame, FIELD_BOOLEAN ),
|
||||
|
||||
DEFINE_UTLVECTOR( m_ModifiedConvars, FIELD_EMBEDDED ),
|
||||
DEFINE_UTLVECTOR( m_hSpawnedEntities, FIELD_EHANDLE ),
|
||||
|
||||
//DEFINE_UTLVECTOR( m_SpawnEditLookup, FIELD_EMBEDDED ),
|
||||
|
||||
END_DATADESC()
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: We need to revert back any convar changes that are made by the
|
||||
// commentary system during commentary. This code stores convar changes
|
||||
// made by the commentary system, and reverts them when finished.
|
||||
//-----------------------------------------------------------------------------
|
||||
void CV_GlobalChange_MapEdit( IConVar *var, const char *pOldString, float flOldValue )
|
||||
{
|
||||
if ( !g_MapEdit.MapEditConvarsChanging() )
|
||||
{
|
||||
// A convar has changed, but not due to mapedit. Ignore it.
|
||||
return;
|
||||
}
|
||||
|
||||
g_MapEdit.ConvarChanged( var, pOldString, flOldValue );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CC_MapEditNotChanging( void )
|
||||
{
|
||||
g_MapEdit.SetMapEditConvarsChanging( false );
|
||||
}
|
||||
static ConCommand mapedit_cvarsnotchanging( "mapedit_cvarsnotchanging", CC_MapEditNotChanging, 0 );
|
||||
|
||||
// ========================================================
|
||||
// Static functions that can be accessed from outside
|
||||
// ========================================================
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Reloads automatic MapEdit after cleanup
|
||||
//-----------------------------------------------------------------------------
|
||||
void MapEdit_MapReload( void )
|
||||
{
|
||||
Msg("MapEdit: Map reloading\n");
|
||||
|
||||
g_MapEdit.ShutDownMapEdit();
|
||||
|
||||
g_MapEdit.LevelInitPreEntity();
|
||||
|
||||
g_MapEdit.LevelInitPostEntity();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Loads a specific MapEdit file.
|
||||
//-----------------------------------------------------------------------------
|
||||
void MapEdit_LoadFile(const char *pFile, bool bStack)
|
||||
{
|
||||
if (!filesystem->FileExists(pFile))
|
||||
{
|
||||
Warning("MapEdit: No such file \"%s\"!\n", pFile);
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsMapEditLoaded())
|
||||
{
|
||||
if (bStack)
|
||||
{
|
||||
g_MapEdit.SpawnMapEdit(pFile);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
g_MapEdit.SetMapEdit(false);
|
||||
}
|
||||
}
|
||||
|
||||
g_MapEdit.SetMapEdit(true, pFile);
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: MapEdit specific logic_auto replacement.
|
||||
// Fires outputs based upon how MapEdit has been activated.
|
||||
//-----------------------------------------------------------------------------
|
||||
class CMapEditAuto : public CBaseEntity
|
||||
{
|
||||
DECLARE_CLASS( CMapEditAuto, CBaseEntity );
|
||||
public:
|
||||
DECLARE_DATADESC();
|
||||
|
||||
void Spawn(void);
|
||||
void Think(void);
|
||||
|
||||
private:
|
||||
// fired if loaded due to new map
|
||||
COutputEvent m_OnMapEditNewGame;
|
||||
|
||||
// fired if loaded in the middle of a map
|
||||
COutputEvent m_OnMapEditMidGame;
|
||||
};
|
||||
|
||||
LINK_ENTITY_TO_CLASS(mapedit_auto, CMapEditAuto);
|
||||
|
||||
BEGIN_DATADESC( CMapEditAuto )
|
||||
// Outputs
|
||||
DEFINE_OUTPUT(m_OnMapEditNewGame, "OnMapEditNewGame"),
|
||||
DEFINE_OUTPUT(m_OnMapEditMidGame, "OnMapEditMidGame"),
|
||||
END_DATADESC()
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Purpose : Fire my outputs here if I fire on map reload
|
||||
//------------------------------------------------------------------------------
|
||||
void CMapEditAuto::Spawn(void)
|
||||
{
|
||||
BaseClass::Spawn();
|
||||
SetNextThink( gpGlobals->curtime + 0.1 );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CMapEditAuto::Think(void)
|
||||
{
|
||||
if ( g_MapEdit.MapEditLoadedMidGame() )
|
||||
{
|
||||
m_OnMapEditMidGame.FireOutput(NULL, this);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_OnMapEditNewGame.FireOutput(NULL, this);
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Purpose :
|
||||
// Input :
|
||||
// Output :
|
||||
//------------------------------------------------------------------------------
|
||||
void CC_MapEdit_Load( const CCommand& args )
|
||||
{
|
||||
const char *sFile = args[1] ? args[1] : NULL;
|
||||
|
||||
MapEdit_LoadFile(sFile, mapedit_stack.GetBool());
|
||||
}
|
||||
static ConCommand mapedit_load("mapedit_load", CC_MapEdit_Load, "Forces mapedit to load a specific file. If there is no input value, it will load the map's default file.", FCVAR_CHEAT);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Purpose : Unloads all MapEdit entities. Does not undo modifications or deletions.
|
||||
// Input :
|
||||
// Output :
|
||||
//------------------------------------------------------------------------------
|
||||
void CC_MapEdit_Unload( const CCommand& args )
|
||||
{
|
||||
g_MapEdit.SetMapEdit(false);
|
||||
}
|
||||
static ConCommand mapedit_unload("mapedit_unload", CC_MapEdit_Unload, "Forces mapedit to unload.", FCVAR_CHEAT);
|
||||
|
||||
#else
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Purpose : Prints MapEdit data from a specific file to the console.
|
||||
// Input : The file to print
|
||||
// Output : The file's data
|
||||
//------------------------------------------------------------------------------
|
||||
void CC_MapEdit_Print( const CCommand& args )
|
||||
{
|
||||
const char *szFullName = args[1];
|
||||
if (szFullName && filesystem->FileExists(szFullName))
|
||||
{
|
||||
KeyValues *pkvFile = new KeyValues( "MapEdit" );
|
||||
if ( pkvFile->LoadFromFile( filesystem, szFullName, "MOD" ) )
|
||||
{
|
||||
Msg( "MapEdit: Printing MapEdit data from %s. \n", szFullName );
|
||||
|
||||
// Load each commentary block, and spawn the entities
|
||||
KeyValues *pkvNode = pkvFile->GetFirstSubKey();
|
||||
while ( pkvNode )
|
||||
{
|
||||
// Get node name
|
||||
const char *pNodeName = pkvNode->GetName();
|
||||
Msg("- Section Name: %s\n", pNodeName);
|
||||
|
||||
// Skip the trackinfo
|
||||
if ( !Q_strncmp( pNodeName, "trackinfo", 9 ) )
|
||||
{
|
||||
pkvNode = pkvNode->GetNextKey();
|
||||
continue;
|
||||
}
|
||||
|
||||
KeyValues *pClassname = pkvNode->GetFirstSubKey();
|
||||
while (pClassname)
|
||||
{
|
||||
// Use the classname instead
|
||||
pNodeName = pClassname->GetName();
|
||||
|
||||
Msg(" %s\n", pNodeName);
|
||||
for ( KeyValues *sub = pClassname->GetFirstSubKey(); sub; sub = sub->GetNextKey() )
|
||||
{
|
||||
if (!Q_strcmp(sub->GetName(), "connections"))
|
||||
{
|
||||
Msg("- connections\n");
|
||||
for ( KeyValues *sub2 = sub->GetFirstSubKey(); sub2; sub2 = sub2->GetNextKey() )
|
||||
{
|
||||
Msg("- \"%s\", \"%s\"\n", sub2->GetName(), sub2->GetString());
|
||||
}
|
||||
continue;
|
||||
}
|
||||
Msg("- %s, %s\n", sub->GetName(), sub->GetString());
|
||||
}
|
||||
|
||||
pClassname = pClassname->GetNextKey();
|
||||
}
|
||||
|
||||
pkvNode = pkvNode->GetNextKey();
|
||||
}
|
||||
|
||||
pkvNode->deleteThis();
|
||||
}
|
||||
}
|
||||
}
|
||||
static ConCommand mapedit_print("mapedit_print", CC_MapEdit_Print, "Prints a mapedit file in the console.");
|
||||
|
||||
#endif
|
||||
14
sp/src/game/shared/mapbase/MapEdit.h
Normal file
14
sp/src/game/shared/mapbase/MapEdit.h
Normal file
@@ -0,0 +1,14 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: Accessing MapEdit
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
extern ConVar mapedit_enabled;
|
||||
extern ConVar mapedit_stack;
|
||||
extern ConVar mapedit_debug;
|
||||
|
||||
void MapEdit_MapReload( void );
|
||||
|
||||
void MapEdit_LoadFile( const char *pFile, bool bStack = mapedit_stack.GetBool() );
|
||||
491
sp/src/game/shared/mapbase/mapbase_rpc.cpp
Normal file
491
sp/src/game/shared/mapbase/mapbase_rpc.cpp
Normal file
@@ -0,0 +1,491 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: Carries the Mapbase CAutoGameSystem that loads manifest among other things.
|
||||
// Also includes code that does not fit anywhere else.
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#include "cbase.h"
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
|
||||
#ifdef STEAM_RPC
|
||||
#include "clientsteamcontext.h"
|
||||
#endif
|
||||
|
||||
#ifdef DISCORD_RPC
|
||||
#include "discord_rpc.h"
|
||||
#include <time.h>
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
// From mapbase_shared.cpp
|
||||
extern const char *g_MapName;
|
||||
|
||||
// The game's name found in gameinfo.txt. Mostly used for Discord RPC.
|
||||
extern char g_iszGameName[128];
|
||||
|
||||
#ifdef MAPBASE_RPC
|
||||
void MapbaseRPC_CVarToggle( IConVar *var, const char *pOldString, float flOldValue );
|
||||
|
||||
ConVar mapbase_rpc_enabled("mapbase_rpc_enabled", "1", FCVAR_ARCHIVE, "Controls whether Mapbase's RPC stuff is enabled on this client.", MapbaseRPC_CVarToggle);
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// RPC Stuff
|
||||
//
|
||||
// Mapbase has some special "RPC" integration stuff for things like Discord.
|
||||
// There's a section that goes into more detail below.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void MapbaseRPC_Init();
|
||||
void MapbaseRPC_Shutdown();
|
||||
|
||||
void MapbaseRPC_Update( int iType, const char *pMapName );
|
||||
void MapbaseRPC_Update( int iRPCMask, int iType, const char *pMapName );
|
||||
|
||||
#ifdef STEAM_RPC
|
||||
void MapbaseRPC_UpdateSteam( int iType, const char *pMapName );
|
||||
#endif
|
||||
|
||||
#ifdef DISCORD_RPC
|
||||
void MapbaseRPC_UpdateDiscord( int iType, const char *pMapName );
|
||||
void MapbaseRPC_GetDiscordParameters( DiscordRichPresence &discordPresence, int iType, const char *pMapName );
|
||||
#endif
|
||||
|
||||
enum RPCClients_t
|
||||
{
|
||||
RPC_STEAM,
|
||||
RPC_DISCORD,
|
||||
|
||||
NUM_RPCS,
|
||||
};
|
||||
|
||||
static const char *g_pszRPCNames[] = {
|
||||
"Steam",
|
||||
"Discord",
|
||||
};
|
||||
|
||||
// This is a little dodgy, but it stops us from having to add spawnflag definitions for each RPC.
|
||||
#define RPCFlag(rpc) (1 << rpc)
|
||||
|
||||
// The global game_metadata entity.
|
||||
// There can be only one...for each RPC.
|
||||
static EHANDLE g_Metadata[NUM_RPCS];
|
||||
|
||||
#define CMapbaseMetadata C_MapbaseMetadata
|
||||
|
||||
// Don't update constantly
|
||||
#define RPC_UPDATE_COOLDOWN 5.0f
|
||||
|
||||
// How long to wait before updating in case multiple variables are changing
|
||||
#define RPC_UPDATE_WAIT 0.25f
|
||||
#endif
|
||||
|
||||
class CMapbaseMetadata : public CBaseEntity
|
||||
{
|
||||
public:
|
||||
DECLARE_DATADESC();
|
||||
DECLARE_NETWORKCLASS();
|
||||
DECLARE_CLASS( CMapbaseMetadata, CBaseEntity );
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
~C_MapbaseMetadata()
|
||||
{
|
||||
for (int i = 0; i < NUM_RPCS; i++)
|
||||
{
|
||||
if (g_Metadata[i] == this)
|
||||
{
|
||||
g_Metadata[i] = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OnDataChanged( DataUpdateType_t updateType )
|
||||
{
|
||||
if (updateType == DATA_UPDATE_CREATED)
|
||||
{
|
||||
for (int i = 0; i < NUM_RPCS; i++)
|
||||
{
|
||||
// See if we're updating this RPC.
|
||||
if (m_spawnflags & RPCFlag(i))
|
||||
{
|
||||
if (g_Metadata[i])
|
||||
{
|
||||
Warning("Warning: Metadata entity for %s already exists, replacing with new one\n", g_pszRPCNames[i]);
|
||||
|
||||
// Inherit their update timer
|
||||
m_flRPCUpdateTimer = static_cast<C_MapbaseMetadata*>(g_Metadata[i].Get())->m_flRPCUpdateTimer;
|
||||
|
||||
g_Metadata[i].Get()->Remove();
|
||||
}
|
||||
|
||||
DevMsg("Becoming metadata entity for %s\n", g_pszRPCNames[i]);
|
||||
g_Metadata[i] = this;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Avoid spamming updates
|
||||
if (gpGlobals->curtime > (m_flRPCUpdateTimer - RPC_UPDATE_WAIT))
|
||||
{
|
||||
// Multiple variables might be changing, wait until they're probably all finished
|
||||
m_flRPCUpdateTimer = gpGlobals->curtime + RPC_UPDATE_WAIT;
|
||||
}
|
||||
|
||||
DevMsg("Metadata changed; updating in %f\n", m_flRPCUpdateTimer - gpGlobals->curtime);
|
||||
|
||||
// Update when the cooldown is over
|
||||
SetNextClientThink( m_flRPCUpdateTimer );
|
||||
}
|
||||
|
||||
void ClientThink()
|
||||
{
|
||||
// NOTE: Client thinking should be limited by the update timer!
|
||||
UpdateRPCThink();
|
||||
|
||||
// Wait until our data is changed again
|
||||
SetNextClientThink( CLIENT_THINK_NEVER );
|
||||
}
|
||||
|
||||
void UpdateRPCThink()
|
||||
{
|
||||
DevMsg("Global metadata entity: %s\n", g_Metadata != NULL ? "Valid" : "Invalid!?");
|
||||
|
||||
MapbaseRPC_Update(m_spawnflags, RPCSTATE_UPDATE, g_MapName);
|
||||
|
||||
m_flRPCUpdateTimer = gpGlobals->curtime + RPC_UPDATE_COOLDOWN;
|
||||
}
|
||||
#else
|
||||
int UpdateTransmitState() // always send to all clients
|
||||
{
|
||||
return SetTransmitState( FL_EDICT_ALWAYS );
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
// Built-in update spam limiter
|
||||
float m_flRPCUpdateTimer = RPC_UPDATE_COOLDOWN;
|
||||
|
||||
char m_iszRPCState[128];
|
||||
char m_iszRPCDetails[128];
|
||||
|
||||
int m_spawnflags;
|
||||
#else
|
||||
CNetworkVar( string_t, m_iszRPCState );
|
||||
CNetworkVar( string_t, m_iszRPCDetails );
|
||||
#endif
|
||||
|
||||
// TODO: Player-specific control
|
||||
//CNetworkVar( int, m_iLimitingID );
|
||||
};
|
||||
|
||||
LINK_ENTITY_TO_CLASS( game_metadata, CMapbaseMetadata );
|
||||
|
||||
IMPLEMENT_NETWORKCLASS_ALIASED(MapbaseMetadata, DT_MapbaseMetadata)
|
||||
|
||||
BEGIN_NETWORK_TABLE_NOBASE(CMapbaseMetadata, DT_MapbaseMetadata)
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
RecvPropString(RECVINFO(m_iszRPCState)),
|
||||
RecvPropString(RECVINFO(m_iszRPCDetails)),
|
||||
RecvPropInt( RECVINFO( m_spawnflags ) ),
|
||||
#else
|
||||
SendPropStringT(SENDINFO(m_iszRPCState) ),
|
||||
SendPropStringT(SENDINFO(m_iszRPCDetails) ),
|
||||
SendPropInt( SENDINFO(m_spawnflags), 8, SPROP_UNSIGNED ),
|
||||
#endif
|
||||
|
||||
END_NETWORK_TABLE()
|
||||
|
||||
BEGIN_DATADESC( CMapbaseMetadata )
|
||||
|
||||
// Inputs
|
||||
DEFINE_INPUT( m_iszRPCState, FIELD_STRING, "SetRPCState" ),
|
||||
DEFINE_INPUT( m_iszRPCDetails, FIELD_STRING, "SetRPCDetails" ),
|
||||
|
||||
END_DATADESC()
|
||||
|
||||
#ifdef MAPBASE_RPC
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Mapbase's special integration with rich presence clients, most notably Discord.
|
||||
//
|
||||
// This only has Discord as of writing, but similar/derived implementaton could expand to
|
||||
// other clients in the future, maybe even Steam.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
//-----------------------------------------
|
||||
// !!! FOR MODS !!!
|
||||
//
|
||||
// Create your own Discord "application" if you want to change what info/images show up, etc.
|
||||
// You can find the convar that controls this in cdll_client_int.cpp.
|
||||
//
|
||||
// This code automatically shows the mod's title in the details, but it's easy to change if you want things to be chapter-specific, etc.
|
||||
//
|
||||
//-----------------------------------------
|
||||
|
||||
static ConVar cl_discord_appid("cl_discord_appid", "582595088719413250", FCVAR_DEVELOPMENTONLY | FCVAR_CHEAT);
|
||||
static int64_t startTimestamp = time(0);
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Discord RPC handlers
|
||||
//-----------------------------------------------------------------------------
|
||||
static void HandleDiscordReady(const DiscordUser* connectedUser)
|
||||
{
|
||||
DevMsg("Discord: Connected to user %s#%s - %s\n",
|
||||
connectedUser->username,
|
||||
connectedUser->discriminator,
|
||||
connectedUser->userId);
|
||||
}
|
||||
|
||||
static void HandleDiscordDisconnected(int errcode, const char* message)
|
||||
{
|
||||
DevMsg("Discord: Disconnected (%d: %s)\n", errcode, message);
|
||||
}
|
||||
|
||||
static void HandleDiscordError(int errcode, const char* message)
|
||||
{
|
||||
DevMsg("Discord: Error (%d: %s)\n", errcode, message);
|
||||
}
|
||||
|
||||
static void HandleDiscordJoin(const char* secret)
|
||||
{
|
||||
// Not implemented
|
||||
}
|
||||
|
||||
static void HandleDiscordSpectate(const char* secret)
|
||||
{
|
||||
// Not implemented
|
||||
}
|
||||
|
||||
static void HandleDiscordJoinRequest(const DiscordUser* request)
|
||||
{
|
||||
// Not implemented
|
||||
}
|
||||
|
||||
void MapbaseRPC_Init()
|
||||
{
|
||||
// Steam RPC
|
||||
if (steamapicontext->SteamFriends())
|
||||
steamapicontext->SteamFriends()->ClearRichPresence();
|
||||
|
||||
// Discord RPC
|
||||
DiscordEventHandlers handlers;
|
||||
memset(&handlers, 0, sizeof(handlers));
|
||||
|
||||
handlers.ready = HandleDiscordReady;
|
||||
handlers.disconnected = HandleDiscordDisconnected;
|
||||
handlers.errored = HandleDiscordError;
|
||||
handlers.joinGame = HandleDiscordJoin;
|
||||
handlers.spectateGame = HandleDiscordSpectate;
|
||||
handlers.joinRequest = HandleDiscordJoinRequest;
|
||||
|
||||
char appid[255];
|
||||
sprintf(appid, "%d", engine->GetAppID());
|
||||
Discord_Initialize(cl_discord_appid.GetString(), &handlers, 1, appid);
|
||||
|
||||
if (!g_bTextMode)
|
||||
{
|
||||
DiscordRichPresence discordPresence;
|
||||
memset(&discordPresence, 0, sizeof(discordPresence));
|
||||
|
||||
MapbaseRPC_GetDiscordParameters(discordPresence, RPCSTATE_INIT, NULL);
|
||||
|
||||
discordPresence.startTimestamp = startTimestamp;
|
||||
|
||||
Discord_UpdatePresence(&discordPresence);
|
||||
}
|
||||
}
|
||||
|
||||
void MapbaseRPC_Shutdown()
|
||||
{
|
||||
// Discord RPC
|
||||
Discord_Shutdown();
|
||||
|
||||
// Steam RPC
|
||||
if (steamapicontext->SteamFriends())
|
||||
steamapicontext->SteamFriends()->ClearRichPresence();
|
||||
}
|
||||
|
||||
void MapbaseRPC_Update( int iType, const char *pMapName )
|
||||
{
|
||||
// All RPCs
|
||||
MapbaseRPC_Update( INT_MAX, iType, pMapName );
|
||||
}
|
||||
|
||||
void MapbaseRPC_Update( int iRPCMask, int iType, const char *pMapName )
|
||||
{
|
||||
if (iRPCMask & RPCFlag(RPC_STEAM))
|
||||
MapbaseRPC_UpdateSteam(iType, pMapName);
|
||||
if (iRPCMask & RPCFlag(RPC_DISCORD))
|
||||
MapbaseRPC_UpdateDiscord(iType, pMapName);
|
||||
}
|
||||
|
||||
#ifdef STEAM_RPC
|
||||
void MapbaseRPC_UpdateSteam( int iType, const char *pMapName )
|
||||
{
|
||||
if (!steamapicontext->SteamFriends())
|
||||
{
|
||||
DevMsg("No Friends! :(\n");
|
||||
}
|
||||
|
||||
const char *pszStatus = NULL;
|
||||
|
||||
if (g_Metadata[RPC_STEAM] != NULL)
|
||||
{
|
||||
C_MapbaseMetadata *pMetadata = static_cast<C_MapbaseMetadata*>(g_Metadata[RPC_STEAM].Get());
|
||||
|
||||
if (pMetadata->m_iszRPCDetails[0] != NULL)
|
||||
pszStatus = pMetadata->m_iszRPCDetails;
|
||||
else if (pMetadata->m_iszRPCState[0] != NULL)
|
||||
pszStatus = pMetadata->m_iszRPCState;
|
||||
else
|
||||
{
|
||||
if (engine->IsLevelMainMenuBackground())
|
||||
pszStatus = VarArgs("Main Menu (%s)", pMapName ? pMapName : "N/A");
|
||||
else
|
||||
pszStatus = VarArgs("Map: %s", pMapName ? pMapName : "N/A");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (iType)
|
||||
{
|
||||
case RPCSTATE_INIT:
|
||||
case RPCSTATE_LEVEL_SHUTDOWN:
|
||||
{
|
||||
pszStatus = "Main Menu";
|
||||
} break;
|
||||
case RPCSTATE_LEVEL_INIT:
|
||||
default:
|
||||
{
|
||||
// Say we're in the main menu if it's a background map
|
||||
if (engine->IsLevelMainMenuBackground())
|
||||
{
|
||||
pszStatus = VarArgs("Main Menu (%s)", pMapName ? pMapName : "N/A");
|
||||
}
|
||||
else
|
||||
{
|
||||
pszStatus = VarArgs("Map: %s", pMapName ? pMapName : "N/A");
|
||||
}
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
DevMsg("Updating Steam\n");
|
||||
if (pszStatus)
|
||||
{
|
||||
steamapicontext->SteamFriends()->SetRichPresence( "gamestatus", pszStatus );
|
||||
steamapicontext->SteamFriends()->SetRichPresence( "steam_display", "#SteamRPC_Status" );
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef DISCORD_RPC
|
||||
// Game-Specfic Image
|
||||
// These are specific to the Mapbase Discord application, so you'll want to modify this for your own mod.
|
||||
#if HL2_EPISODIC
|
||||
#define DISCORD_GAME_IMAGE "mb_logo_episodic"
|
||||
#define DISCORD_GAME_IMAGE_TEXT "Half-Life 2 Episodic"
|
||||
#elif HL2_CLIENT_DLL
|
||||
#define DISCORD_GAME_IMAGE "mb_logo_hl2"
|
||||
#define DISCORD_GAME_IMAGE_TEXT "Half-Life 2"
|
||||
#else
|
||||
#define DISCORD_GAME_IMAGE "mb_logo_general"
|
||||
#define DISCORD_GAME_IMAGE_TEXT "Mapbase"
|
||||
#endif
|
||||
|
||||
void MapbaseRPC_GetDiscordParameters( DiscordRichPresence &discordPresence, int iType, const char *pMapName )
|
||||
{
|
||||
if (g_Metadata[RPC_DISCORD] != NULL)
|
||||
{
|
||||
C_MapbaseMetadata *pMetadata = static_cast<C_MapbaseMetadata*>(g_Metadata[RPC_DISCORD].Get());
|
||||
|
||||
if (pMetadata->m_iszRPCState[0] != NULL)
|
||||
discordPresence.state = pMetadata->m_iszRPCState;
|
||||
else
|
||||
discordPresence.state = g_iszGameName;
|
||||
|
||||
if (pMetadata->m_iszRPCDetails[0] != NULL)
|
||||
discordPresence.details = pMetadata->m_iszRPCDetails;
|
||||
else
|
||||
{
|
||||
if (engine->IsLevelMainMenuBackground())
|
||||
discordPresence.details = VarArgs("Main Menu (%s)", pMapName ? pMapName : "N/A");
|
||||
else
|
||||
discordPresence.details = VarArgs("Map: %s", pMapName ? pMapName : "N/A");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (iType)
|
||||
{
|
||||
case RPCSTATE_INIT:
|
||||
case RPCSTATE_LEVEL_SHUTDOWN:
|
||||
{
|
||||
discordPresence.state = g_iszGameName;
|
||||
discordPresence.details = "Main Menu";
|
||||
} break;
|
||||
case RPCSTATE_LEVEL_INIT:
|
||||
default:
|
||||
{
|
||||
// Say we're in the main menu if it's a background map
|
||||
if (engine->IsLevelMainMenuBackground())
|
||||
{
|
||||
discordPresence.state = g_iszGameName;
|
||||
discordPresence.details = VarArgs("Main Menu (%s)", pMapName ? pMapName : "N/A");
|
||||
}
|
||||
else
|
||||
{
|
||||
discordPresence.state = g_iszGameName;
|
||||
discordPresence.details = VarArgs("Map: %s", pMapName ? pMapName : "N/A");
|
||||
}
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
// Generic Mapbase logo. Specific to the Mapbase Discord application.
|
||||
discordPresence.smallImageKey = "mb_logo_general";
|
||||
discordPresence.smallImageText = "Mapbase";
|
||||
|
||||
#ifdef DISCORD_GAME_IMAGE
|
||||
discordPresence.largeImageKey = DISCORD_GAME_IMAGE;
|
||||
discordPresence.largeImageText = DISCORD_GAME_IMAGE_TEXT;
|
||||
#endif
|
||||
}
|
||||
|
||||
void MapbaseRPC_UpdateDiscord( int iType, const char *pMapName )
|
||||
{
|
||||
DiscordRichPresence discordPresence;
|
||||
memset(&discordPresence, 0, sizeof(discordPresence));
|
||||
|
||||
DevMsg("Updating Discord\n");
|
||||
|
||||
discordPresence.startTimestamp = startTimestamp;
|
||||
|
||||
MapbaseRPC_GetDiscordParameters( discordPresence, iType, pMapName );
|
||||
|
||||
Discord_UpdatePresence(&discordPresence);
|
||||
}
|
||||
|
||||
void MapbaseRPC_CVarToggle( IConVar *var, const char *pOldString, float flOldValue )
|
||||
{
|
||||
if (flOldValue <= 0 && mapbase_rpc_enabled.GetInt() > 0)
|
||||
{
|
||||
// Turning on
|
||||
MapbaseRPC_Init();
|
||||
MapbaseRPC_Update( g_MapName != NULL ? RPCSTATE_UPDATE : RPCSTATE_INIT, g_MapName );
|
||||
}
|
||||
else if (mapbase_rpc_enabled.GetInt() <= 0)
|
||||
{
|
||||
// Turning off
|
||||
MapbaseRPC_Shutdown();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
435
sp/src/game/shared/mapbase/mapbase_shared.cpp
Normal file
435
sp/src/game/shared/mapbase/mapbase_shared.cpp
Normal file
@@ -0,0 +1,435 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: Carries the Mapbase CAutoGameSystem that loads manifest among other things.
|
||||
// Also includes code that does not fit anywhere else.
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#include "cbase.h"
|
||||
|
||||
#include "tier0/icommandline.h"
|
||||
#include "igamesystem.h"
|
||||
#include "filesystem.h"
|
||||
#include <vgui_controls/Controls.h>
|
||||
#include <vgui/ILocalize.h>
|
||||
#include "saverestore_utlvector.h"
|
||||
#include "props_shared.h"
|
||||
#include "utlbuffer.h"
|
||||
#ifdef CLIENT_DLL
|
||||
#include "hud_closecaption.h"
|
||||
#include "panelmetaclassmgr.h"
|
||||
#include "c_soundscape.h"
|
||||
#else
|
||||
#include "soundscape_system.h"
|
||||
#include "AI_ResponseSystem.h"
|
||||
#include "mapbase/SystemConvarMod.h"
|
||||
#endif
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
#define GENERIC_MANIFEST_FILE "scripts/mapbase_default_manifest.txt"
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
#define AUTOLOADED_MANIFEST_FILE VarArgs("maps/%s_manifest.txt", g_MapName)
|
||||
#else
|
||||
#define AUTOLOADED_MANIFEST_FILE UTIL_VarArgs("maps/%s_manifest.txt", g_MapName)
|
||||
#endif
|
||||
|
||||
// The soundscape system does not operate in a way that supports soundscape loading after map initialization.
|
||||
// I have decided to make a direct change to the system that loads "maps/%mapname%_soundscapes.txt" instead and suggest the usage of #include and #base to users.
|
||||
// Sorry. It's not broken, so I won't fix it.
|
||||
#define MAPBASE_SOUNDSCAPE_SUPPORT 0
|
||||
|
||||
const char *g_MapName;
|
||||
|
||||
extern ISoundEmitterSystemBase *soundemitterbase;
|
||||
|
||||
ConVar mapbase_load_default_manifest("mapbase_load_default_manifest", "1", FCVAR_ARCHIVE, "Should we automatically load our default manifest file? (\"maps/%mapname%_manifest.txt\")");
|
||||
|
||||
ConVar mapbase_load_soundscripts("mapbase_load_soundscripts", "1", FCVAR_ARCHIVE, "Should we load map-specific soundscripts? e.g. \"maps/mapname_level_sounds.txt\"");
|
||||
|
||||
//ConVar mapbase_load_propdata("mapbase_load_propdata", "1", FCVAR_ARCHIVE, "Should we load map-specific propdata files? e.g. \"maps/mapname_propdata.txt\"");
|
||||
|
||||
#if MAPBASE_SOUNDSCAPE_SUPPORT
|
||||
ConVar mapbase_load_soundscapes("mapbase_load_soundscapes", "1", FCVAR_ARCHIVE, "Should we load map-specific soundscapes? e.g. \"maps/mapname_soundscapes.txt\"");
|
||||
#endif
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
|
||||
//ConVar mapbase_load_cc("mapbase_load_cc", "1", FCVAR_ARCHIVE, "Should we load map-specific closed captioning? e.g. \"maps/mapname_closecaption_english.txt\" and \"maps/mapname_closecaption_english.dat\"");
|
||||
|
||||
ConVar mapbase_load_localization("mapbase_load_localization", "1", FCVAR_ARCHIVE, "Should we load map-specific localized text files? e.g. \"maps/mapname_english.txt\"");
|
||||
|
||||
#else
|
||||
|
||||
ConVar mapbase_load_sentences("mapbase_load_sentences", "1", FCVAR_ARCHIVE, "Should we load map-specific sentences? e.g. \"maps/mapname_sentences.txt\"");
|
||||
|
||||
ConVar mapbase_load_talker("mapbase_load_talker", "1", FCVAR_ARCHIVE, "Should we load map-specific talker files? e.g. \"maps/mapname_talker.txt\"");
|
||||
ConVar mapbase_flush_talker("mapbase_flush_talker", "1", FCVAR_NONE, "Normally, when a map with custom talker files is unloaded, the response system resets to rid itself of the custom file(s). Turn this convar off to prevent that from happening.");
|
||||
|
||||
ConVar mapbase_load_actbusy("mapbase_load_actbusy", "1", FCVAR_ARCHIVE, "Should we load map-specific actbusy files? e.g. \"maps/mapname_actbusy.txt\"");
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef GAME_DLL
|
||||
extern void ParseCustomActbusyFile(const char *file);
|
||||
|
||||
extern bool LoadResponseSystemFile(const char *scriptfile);
|
||||
extern void ReloadResponseSystem();
|
||||
|
||||
// Reloads the response system when the map changes to avoid custom talker leaking
|
||||
static bool g_bMapContainsCustomTalker;
|
||||
#endif
|
||||
|
||||
// Indicates this is a core Mapbase mod and not a mod using its code.
|
||||
static bool g_bMapbaseCore;
|
||||
|
||||
// The game's name found in gameinfo.txt. Mostly used for Discord RPC.
|
||||
char g_iszGameName[128];
|
||||
|
||||
enum
|
||||
{
|
||||
MANIFEST_SOUNDSCRIPTS,
|
||||
//MANIFEST_PROPDATA,
|
||||
#if MAPBASE_SOUNDSCAPE_SUPPORT
|
||||
MANIFEST_SOUNDSCAPES,
|
||||
#endif
|
||||
#ifdef CLIENT_DLL
|
||||
//MANIFEST_CLOSECAPTION,
|
||||
MANIFEST_LOCALIZATION,
|
||||
MANIFEST_VGUI,
|
||||
#else
|
||||
MANIFEST_TALKER,
|
||||
MANIFEST_SENTENCES,
|
||||
MANIFEST_ACTBUSY,
|
||||
#endif
|
||||
|
||||
// Must always be kept below
|
||||
MANIFEST_NUM_TYPES,
|
||||
};
|
||||
|
||||
struct ManifestType_t
|
||||
{
|
||||
//int type;
|
||||
const char *string;
|
||||
ConVar *cvar;
|
||||
};
|
||||
|
||||
// KEEP THS IN SYNC WITH THE ENUM!
|
||||
static const ManifestType_t gm_szManifestFileStrings[MANIFEST_NUM_TYPES] = {
|
||||
{ "soundscripts", &mapbase_load_soundscripts },
|
||||
//{ "propdata", &mapbase_load_propdata },
|
||||
#if MAPBASE_SOUNDSCAPE_SUPPORT
|
||||
{ "soundscapes", &mapbase_load_soundscapes },
|
||||
#endif
|
||||
#ifdef CLIENT_DLL
|
||||
//{ "closecaption", &mapbase_load_cc },
|
||||
{ "localization", &mapbase_load_localization },
|
||||
{ "vgui", NULL },
|
||||
#else
|
||||
{ "talker", &mapbase_load_talker },
|
||||
{ "sentences", &mapbase_load_sentences },
|
||||
{ "actbusy", &mapbase_load_actbusy },
|
||||
#endif
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: System used to load map-specific files, etc.
|
||||
//-----------------------------------------------------------------------------
|
||||
class CMapbaseSystem : public CAutoGameSystem
|
||||
{
|
||||
public:
|
||||
DECLARE_DATADESC();
|
||||
|
||||
CMapbaseSystem() : CAutoGameSystem( "CMapbaseSystem" )
|
||||
{
|
||||
}
|
||||
|
||||
inline bool GetGameInfoKeyValues(KeyValues *pKeyValues)
|
||||
{
|
||||
return pKeyValues->LoadFromFile( filesystem, "gameinfo.txt", "GAME" );
|
||||
}
|
||||
|
||||
virtual void LevelInitPreEntity()
|
||||
{
|
||||
#ifdef GAME_DLL
|
||||
Msg("Mapbase system loaded\n");
|
||||
#endif
|
||||
|
||||
// Checks gameinfo.txt for Mapbase-specific options
|
||||
KeyValues *gameinfo = new KeyValues("GameInfo");
|
||||
if (GetGameInfoKeyValues(gameinfo))
|
||||
{
|
||||
// Indicates this is a core Mapbase mod and not a mod using its code
|
||||
g_bMapbaseCore = gameinfo->GetBool("mapbase_core", false);
|
||||
|
||||
if (!gameinfo->GetBool("hide_mod_name", false))
|
||||
{
|
||||
// Store the game's name
|
||||
Q_strncpy(g_iszGameName, gameinfo->GetString("game"), sizeof(g_iszGameName));
|
||||
}
|
||||
}
|
||||
gameinfo->deleteThis();
|
||||
|
||||
|
||||
#ifdef GAME_DLL
|
||||
if (g_bMapContainsCustomTalker && mapbase_flush_talker.GetBool())
|
||||
{
|
||||
DevMsg("Mapbase: Reloading response system to flush custom talker\n");
|
||||
ReloadResponseSystem();
|
||||
g_bMapContainsCustomTalker = false;
|
||||
}
|
||||
|
||||
g_MapName = STRING(gpGlobals->mapname);
|
||||
#else
|
||||
//char mapname[128];
|
||||
//Q_StripExtension(MapName(), mapname, sizeof(mapname));
|
||||
g_MapName = MapName() - 4; // Removes ".bsp"
|
||||
|
||||
// Shared Mapbase localization file
|
||||
g_pVGuiLocalize->AddFile( "resource/mapbase_%language%.txt" );
|
||||
#endif
|
||||
// Check for a generic "mapname_manifest.txt" file and load it.
|
||||
if (filesystem->FileExists(AUTOLOADED_MANIFEST_FILE, "GAME") /*&& !FStrEq(name, "closecaption")*/)
|
||||
{
|
||||
AddManifestFile(AUTOLOADED_MANIFEST_FILE);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Load the generic script instead.
|
||||
ParseGenericManifest();
|
||||
}
|
||||
}
|
||||
|
||||
virtual void LevelShutdownPreEntity()
|
||||
{
|
||||
// How would we make sure they don't last between maps?
|
||||
// TODO: Investigate ReloadLocalizationFiles()
|
||||
//g_pVGuiLocalize->RemoveAll();
|
||||
//g_pVGuiLocalize->ReloadLocalizationFiles();
|
||||
}
|
||||
|
||||
virtual void LevelShutdownPostEntity()
|
||||
{
|
||||
g_MapName = NULL;
|
||||
}
|
||||
|
||||
// Get a generic, hardcoded manifest with hardcoded names.
|
||||
void 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 AddManifestFile( const char *file, bool bDontStore = false )
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
DevMsg("===== Mapbase Manifest: Loading manifest file %s =====\n", file);
|
||||
|
||||
AddManifestFile(pKV, false);
|
||||
|
||||
DevMsg("==============================================================================\n");
|
||||
|
||||
pKV->deleteThis();
|
||||
}
|
||||
|
||||
void LoadFromValue( const char *value, int type, bool bDontWarn )
|
||||
{
|
||||
if (!filesystem->FileExists(value, "GAME") /*&& !FStrEq(name, "closecaption")*/)
|
||||
{
|
||||
if (!bDontWarn)
|
||||
{
|
||||
Warning("Mapbase Manifest: WARNING! \"%s\" does not exist!\n", value);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case MANIFEST_SOUNDSCRIPTS: { soundemitterbase->AddSoundOverrides(value); } break;
|
||||
//case MANIFEST_PROPDATA: { g_PropDataSystem.ParsePropDataFile(value); } break;
|
||||
#ifdef CLIENT_DLL
|
||||
//case MANIFEST_CLOSECAPTION: { todo } break;
|
||||
case MANIFEST_LOCALIZATION: { g_pVGuiLocalize->AddFile( value, "GAME", true ); } break;
|
||||
case MANIFEST_VGUI: { PanelMetaClassMgr()->LoadMetaClassDefinitionFile( value ); } break;
|
||||
#if MAPBASE_SOUNDSCAPE_SUPPORT
|
||||
case MANIFEST_SOUNDSCAPES: { Soundscape_AddFile(value); } break;
|
||||
#endif
|
||||
#else
|
||||
case MANIFEST_TALKER: {
|
||||
g_bMapContainsCustomTalker = true;
|
||||
LoadResponseSystemFile(value); //PrecacheCustomResponseSystem( value );
|
||||
} break;
|
||||
#if MAPBASE_SOUNDSCAPE_SUPPORT
|
||||
case MANIFEST_SOUNDSCAPES: { g_SoundscapeSystem.AddSoundscapeFile(value); } break;
|
||||
#endif
|
||||
case MANIFEST_SENTENCES: { engine->PrecacheSentenceFile(value); } break;
|
||||
case MANIFEST_ACTBUSY: { ParseCustomActbusyFile(value); } break;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
// This doesn't call deleteThis()!
|
||||
void AddManifestFile(KeyValues *pKV, bool bDontWarn = false)
|
||||
{
|
||||
const char *name;
|
||||
char value[MAX_PATH];
|
||||
char *szToken;
|
||||
bool inparam = false;
|
||||
for (KeyValues *pKey = pKV->GetFirstSubKey(); pKey; pKey = pKey->GetNextKey())
|
||||
{
|
||||
name = pKey->GetName();
|
||||
|
||||
// Parse %mapname%, etc.
|
||||
inparam = false;
|
||||
value[0] = '\0';
|
||||
szToken = strtok(strdup(pKey->GetString()), "%"); // Was tempvalue
|
||||
while (szToken != NULL)
|
||||
{
|
||||
if (inparam)
|
||||
{
|
||||
const char *append = szToken;
|
||||
if (FStrEq(szToken, "mapname"))
|
||||
append = g_MapName;
|
||||
//else if (FStrEq(szToken, "mapdir"))
|
||||
//{
|
||||
// // TODO
|
||||
//}
|
||||
#ifdef CLIENT_DLL
|
||||
else if (FStrEq(szToken, "language"))
|
||||
{
|
||||
char uilanguage[64];
|
||||
engine->GetUILanguage(uilanguage, sizeof(uilanguage));
|
||||
append = uilanguage;
|
||||
}
|
||||
#endif
|
||||
Q_strncat(value, append, sizeof(value));
|
||||
|
||||
inparam = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
Q_strncat(value, szToken, sizeof(value));
|
||||
|
||||
inparam = true;
|
||||
}
|
||||
szToken = strtok(NULL, "%");
|
||||
}
|
||||
|
||||
if (FStrEq(name, "NoErrors"))
|
||||
{
|
||||
bDontWarn = pKey->GetBool();
|
||||
}
|
||||
|
||||
for (int i = 0; i < MANIFEST_NUM_TYPES; i++)
|
||||
{
|
||||
if (FStrEq(name, gm_szManifestFileStrings[i].string))
|
||||
{
|
||||
if (!gm_szManifestFileStrings[i].cvar || gm_szManifestFileStrings[i].cvar->GetBool())
|
||||
{
|
||||
LoadFromValue(value, i, bDontWarn);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
CMapbaseSystem g_MapbaseSystem;
|
||||
|
||||
BEGIN_DATADESC_NO_BASE( CMapbaseSystem )
|
||||
|
||||
//DEFINE_UTLVECTOR( m_StoredManifestFiles, FIELD_STRING ),
|
||||
|
||||
END_DATADESC()
|
||||
|
||||
#ifdef GAME_DLL
|
||||
ThreeState_t Flashlight_GetLegacyVersionKey()
|
||||
{
|
||||
KeyValues *gameinfo = new KeyValues( "GameInfo" );
|
||||
if (g_MapbaseSystem.GetGameInfoKeyValues( gameinfo ))
|
||||
{
|
||||
// -1 = default
|
||||
int iUseLegacyFlashlight = gameinfo->GetInt( "use_legacy_flashlight", -1 );
|
||||
if (iUseLegacyFlashlight > -1)
|
||||
return iUseLegacyFlashlight != 0 ? TRS_TRUE : TRS_FALSE;
|
||||
}
|
||||
gameinfo->deleteThis();
|
||||
|
||||
return TRS_NONE;
|
||||
}
|
||||
|
||||
static void CC_Mapbase_LoadManifestFile( const CCommand& args )
|
||||
{
|
||||
g_MapbaseSystem.AddManifestFile(args[1], args[2]);
|
||||
}
|
||||
|
||||
static ConCommand mapbase_loadmanifestfile("mapbase_loadmanifestfile", CC_Mapbase_LoadManifestFile, "Loads a Mapbase manifest file. If you don't want this to be saved and found when reloaded, type a '1' after the file path." );
|
||||
|
||||
#define SF_MANIFEST_START_ACTIVATED (1 << 0)
|
||||
|
||||
class CMapbaseManifestEntity : public CPointEntity
|
||||
{
|
||||
public:
|
||||
DECLARE_DATADESC();
|
||||
|
||||
DECLARE_CLASS( CMapbaseManifestEntity, CPointEntity );
|
||||
|
||||
void Spawn( void )
|
||||
{
|
||||
BaseClass::Spawn();
|
||||
if (HasSpawnFlags(SF_MANIFEST_START_ACTIVATED))
|
||||
{
|
||||
LoadManifestFile();
|
||||
}
|
||||
}
|
||||
|
||||
void LoadManifestFile( void )
|
||||
{
|
||||
const char *scriptfile = STRING(m_target);
|
||||
if ( filesystem->FileExists( scriptfile, "GAME" ) )
|
||||
{
|
||||
Msg("Mapbase: Adding manifest file \"%s\"\n", scriptfile);
|
||||
g_MapbaseSystem.AddManifestFile(scriptfile);
|
||||
}
|
||||
else
|
||||
{
|
||||
Warning("Mapbase: Manifest file \"%s\" does not exist!", scriptfile);
|
||||
}
|
||||
}
|
||||
|
||||
void InputActivate(inputdata_t &inputdata)
|
||||
{
|
||||
LoadManifestFile();
|
||||
}
|
||||
};
|
||||
|
||||
LINK_ENTITY_TO_CLASS( mapbase_manifest, CMapbaseManifestEntity );
|
||||
|
||||
BEGIN_DATADESC( CMapbaseManifestEntity )
|
||||
|
||||
// Needs to be set up in the Activate methods of derived classes
|
||||
//DEFINE_CUSTOM_FIELD( m_pInstancedResponseSystem, responseSystemSaveRestoreOps ),
|
||||
|
||||
// Function Pointers
|
||||
DEFINE_INPUTFUNC( FIELD_VOID, "Activate", InputActivate ),
|
||||
|
||||
END_DATADESC()
|
||||
#endif
|
||||
Reference in New Issue
Block a user