You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
680 lines
18 KiB
680 lines
18 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: Implements a class that encapsulates much of the functionality |
|
// of entities. CMapWorld and CMapEntity are both derived from this |
|
// class. |
|
// |
|
// CEditGameClass-derived objects have the following properties: |
|
// |
|
// Key/value pairs - A list of string pairs that hold data properties |
|
// of the object. Each property has a unique name. |
|
// |
|
// Connections - A list of outputs in this object that are connected to |
|
// inputs in another entity. |
|
// |
|
//=============================================================================// |
|
|
|
#include "stdafx.h" |
|
#include "ChunkFile.h" |
|
#include "fgdlib/GameData.h" |
|
#include "GameConfig.h" |
|
#include "EditGameClass.h" |
|
#include "MapEntity.h" |
|
#include "mathlib/Mathlib.h" |
|
|
|
// memdbgon must be the last include file in a .cpp file!!! |
|
#include <tier0/memdbgon.h> |
|
|
|
|
|
// |
|
// An empty string returned by GetComments when we have no comments set. |
|
// |
|
char *CEditGameClass::g_pszEmpty = ""; |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Constructor. Initializes data members. |
|
//----------------------------------------------------------------------------- |
|
CEditGameClass::CEditGameClass(void) |
|
{ |
|
m_pClass = NULL; |
|
m_szClass[0] = '\0'; |
|
m_pszComments = NULL; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Destructor. Frees memory. |
|
//----------------------------------------------------------------------------- |
|
CEditGameClass::~CEditGameClass(void) |
|
{ |
|
delete m_pszComments; |
|
|
|
Connections_RemoveAll(); |
|
Upstream_RemoveAll(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : *pConnection - |
|
//----------------------------------------------------------------------------- |
|
void CEditGameClass::Connections_Add(CEntityConnection *pConnection) |
|
{ |
|
#if defined(_DEBUG) && 0 |
|
LPCTSTR pszTargetName = GetKeyValue("targetname"); |
|
if ( pszTargetName && !strcmp(pszTargetName, "zapperpod7_rotator") ) |
|
{ |
|
// Set breakpoint here for debugging this entity's visiblity |
|
int foo = 0; |
|
} |
|
#endif |
|
|
|
if ( m_Connections.Find(pConnection) == -1 ) |
|
m_Connections.AddToTail(pConnection); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : *pConnection - |
|
// Output : Returns true on success, false on failure. |
|
//----------------------------------------------------------------------------- |
|
bool CEditGameClass::Connections_Remove(CEntityConnection *pConnection) |
|
{ |
|
int nIndex = m_Connections.Find(pConnection); |
|
if (nIndex != -1) |
|
{ |
|
m_Connections.Remove(nIndex); |
|
return(true); |
|
} |
|
|
|
return(false); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// NOTE: unlike Connections_Remove, this actually frees each connection!! |
|
//----------------------------------------------------------------------------- |
|
void CEditGameClass::Connections_RemoveAll() |
|
{ |
|
// |
|
// Remove all our connections from their targets' upstream lists. |
|
// |
|
int nConnectionsCount = m_Connections.Count(); |
|
for (int nConnection = 0; nConnection < nConnectionsCount; nConnection++) |
|
{ |
|
CEntityConnection *pConnection = m_Connections.Element( nConnection ); |
|
|
|
#if defined( ENTITY_MAINTAIN_UPSTREAM_LISTS ) |
|
CMapEntityList *pTargetList = pConnection->GetTargetEntityList(); |
|
if ( pTargetList ) |
|
{ |
|
FOR_EACH_OBJ( *pTargetList, pos ) |
|
{ |
|
CMapEntity *pEntity = pTargetList->Element( pos ); |
|
pEntity->Upstream_Remove( pConnection ); |
|
} |
|
} |
|
#endif |
|
|
|
delete pConnection; |
|
} |
|
|
|
m_Connections.RemoveAll(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CEditGameClass::Connections_FixBad(bool bRelink) |
|
{ |
|
int nConnectionsCount = m_Connections.Count(); |
|
for (int nConnections = 0; nConnections < nConnectionsCount; nConnections++) |
|
{ |
|
CEntityConnection *pConnection = m_Connections.Element(nConnections); |
|
CMapEntityList *pTargetEntities = pConnection->GetTargetEntityList(); |
|
int nEntityCount = pTargetEntities->Count(); |
|
|
|
for ( int nEntities = 0; nEntities < nEntityCount; nEntities++ ) |
|
{ |
|
CMapEntity *pEntity = pTargetEntities->Element(nEntities); |
|
pEntity->Upstream_Remove( pConnection ); |
|
} |
|
|
|
if ( bRelink ) |
|
pConnection->LinkTargetEntities(); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : *pConnection - |
|
//----------------------------------------------------------------------------- |
|
void CEditGameClass::Upstream_Add(CEntityConnection *pConnection) |
|
{ |
|
#if defined(_DEBUG) && 0 |
|
LPCTSTR pszTargetName = GetKeyValue("targetname"); |
|
if ( pszTargetName && !strcmp(pszTargetName, "zapperpod7_rotator") ) |
|
{ |
|
// Set breakpoint here for debugging this entity's visiblity |
|
int foo = 0; |
|
} |
|
#endif |
|
|
|
#if defined( ENTITY_MAINTAIN_UPSTREAM_LISTS ) |
|
if ( m_Upstream.Find(pConnection) == -1 ) |
|
m_Upstream.AddToTail(pConnection); |
|
#endif |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : *pConnection - |
|
// Output : Returns true on success, false on failure. |
|
//----------------------------------------------------------------------------- |
|
bool CEditGameClass::Upstream_Remove(CEntityConnection *pConnection) |
|
{ |
|
int nIndex = m_Upstream.Find(pConnection); |
|
if (nIndex != -1) |
|
{ |
|
m_Upstream.Remove(nIndex); |
|
return(true); |
|
} |
|
|
|
return(false); |
|
} |
|
|
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CEditGameClass::Upstream_RemoveAll(void) |
|
{ |
|
#if defined( ENTITY_MAINTAIN_UPSTREAM_LISTS ) |
|
// |
|
// Remove all our connections from their targets' upstream lists. |
|
// |
|
int nUpstreamCount = m_Upstream.Count(); |
|
for (int nConnection = 0; nConnection < nUpstreamCount; nConnection++) |
|
{ |
|
CEntityConnection *pConnection = m_Upstream.Element( nConnection ); |
|
|
|
CMapEntityList *pSourceList = pConnection->GetSourceEntityList(); |
|
if ( pSourceList ) |
|
{ |
|
FOR_EACH_OBJ( *pSourceList, pos ) |
|
{ |
|
CMapEntity *pEntity = pSourceList->Element( pos ); |
|
pEntity->Connection_Remove( pConnection ); |
|
} |
|
} |
|
} |
|
#endif |
|
|
|
m_Upstream.RemoveAll(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CEditGameClass::Upstream_FixBad() |
|
{ |
|
#if defined(_DEBUG) && 0 |
|
LPCTSTR pszTargetName = GetKeyValue("targetname"); |
|
if ( pszTargetName && !strcmp(pszTargetName, "cave_guard_seq1") ) |
|
{ |
|
// Set breakpoint here for debugging this entity |
|
int foo = 0; |
|
} |
|
#endif |
|
|
|
int nUpstreamCount = m_Upstream.Count(); |
|
for (int nUpstream = 0; nUpstream < nUpstreamCount; nUpstream++) |
|
{ |
|
CEntityConnection *pUpstream = m_Upstream.Element(nUpstream); |
|
pUpstream->LinkTargetEntities(); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : pszClass - |
|
// bLoading - |
|
//----------------------------------------------------------------------------- |
|
void CEditGameClass::SetClass(LPCTSTR pszClass, bool bLoading) |
|
{ |
|
extern GameData *pGD; |
|
strcpy(m_szClass, pszClass); |
|
|
|
StripEdgeWhiteSpace(m_szClass); |
|
|
|
if (pGD) |
|
{ |
|
m_pClass = pGD->ClassForName(m_szClass); |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Copies the data from a given CEditGameClass object into this one. |
|
// Input : pFrom - Object to copy. |
|
// Output : Returns a pointer to this. |
|
//----------------------------------------------------------------------------- |
|
CEditGameClass *CEditGameClass::CopyFrom(CEditGameClass *pFrom) |
|
{ |
|
m_pClass = pFrom->m_pClass; |
|
strcpy( m_szClass, pFrom->m_szClass ); |
|
|
|
// |
|
// Copy all the keys. |
|
// |
|
m_KeyValues.RemoveAll(); |
|
for ( int i=pFrom->GetFirstKeyValue(); i != pFrom->GetInvalidKeyValue(); i=pFrom->GetNextKeyValue( i ) ) |
|
{ |
|
m_KeyValues.SetValue(pFrom->GetKey(i), pFrom->GetKeyValue(i)); |
|
} |
|
|
|
// |
|
// Copy all the connections objects |
|
// |
|
Connections_RemoveAll(); |
|
int nConnCount = pFrom->Connections_GetCount(); |
|
for (int i = 0; i < nConnCount; i++) |
|
{ |
|
CEntityConnection *pConn = pFrom->Connections_Get(i); |
|
CEntityConnection *pNewConn = new CEntityConnection( *pConn ); |
|
Connections_Add(pNewConn); |
|
} |
|
|
|
// |
|
// Copy the comments. |
|
// |
|
SetComments(pFrom->GetComments()); |
|
|
|
return(this); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Applies the default keys for this object's game class. Called when |
|
// the entity's class is changed. |
|
//----------------------------------------------------------------------------- |
|
void CEditGameClass::GetDefaultKeys( void ) |
|
{ |
|
if ( m_pClass != NULL ) |
|
{ |
|
// |
|
// For each variable from the base class... |
|
// |
|
int nVariableCount = m_pClass->GetVariableCount(); |
|
for ( int i = 0; i < nVariableCount; i++ ) |
|
{ |
|
GDinputvariable *pVar = m_pClass->GetVariableAt(i); |
|
Assert(pVar != NULL); |
|
|
|
if (pVar != NULL) |
|
{ |
|
int iIndex; |
|
LPCTSTR p = m_KeyValues.GetValue(pVar->GetName(), &iIndex); |
|
|
|
// |
|
// If the variable is not present in this object, set the default value. |
|
// |
|
if (p == NULL) |
|
{ |
|
MDkeyvalue tmpkv; |
|
pVar->ResetDefaults(); |
|
pVar->ToKeyValue(&tmpkv); |
|
|
|
// |
|
// Only set the key value if it is non-zero. |
|
// |
|
if ((tmpkv.szKey[0] != 0) && (tmpkv.szValue[0] != 0) && (stricmp(tmpkv.szValue, "0"))) |
|
{ |
|
SetKeyValue(tmpkv.szKey, tmpkv.szValue); |
|
} |
|
} |
|
} |
|
} |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Returns this object's angles as a vector of the form: |
|
// [0] PITCH [1] YAW [2] ROLL |
|
//----------------------------------------------------------------------------- |
|
void CEditGameClass::GetAngles(QAngle &vecAngles) |
|
{ |
|
vecAngles = vec3_angle; |
|
const char *pszAngles = GetKeyValue("angles"); |
|
if (pszAngles != NULL) |
|
{ |
|
sscanf(pszAngles, "%f %f %f", &vecAngles[PITCH], &vecAngles[YAW], &vecAngles[ROLL]); |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Sets a new yaw, overriding any existing angles. Uses special values |
|
// for yaw to indicate pointing straight up or straight down. |
|
// |
|
// This method of representing orientation is obsolete; this code is |
|
// only for importing old RMF or MAP files. |
|
// |
|
// Input : a - Value for angle. |
|
//----------------------------------------------------------------------------- |
|
void CEditGameClass::ImportAngle(int nAngle) |
|
{ |
|
QAngle vecAngles; |
|
vecAngles.Init(); |
|
|
|
if (nAngle == -1) // UP |
|
{ |
|
vecAngles[PITCH] = -90; |
|
} |
|
else if (nAngle == -2) // DOWN |
|
{ |
|
vecAngles[PITCH] = 90; |
|
} |
|
else |
|
{ |
|
vecAngles[YAW] = nAngle; |
|
} |
|
|
|
SetAngles(vecAngles); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Sets this object's angles as a vector of the form: |
|
// [0] PITCH [1] YAW [2] ROLL |
|
//----------------------------------------------------------------------------- |
|
void CEditGameClass::SetAngles(const QAngle &vecAngles) |
|
{ |
|
char szAngles[80]; |
|
sprintf(szAngles, "%g %g %g", (double)vecAngles[PITCH], (double)vecAngles[YAW], (double)vecAngles[ROLL]); |
|
SetKeyValue("angles", szAngles); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : *pFile - |
|
// Output : ChunkFileResult_t |
|
//----------------------------------------------------------------------------- |
|
ChunkFileResult_t CEditGameClass::SaveVMF(CChunkFile *pFile, CSaveInfo *pSaveInfo) |
|
{ |
|
ChunkFileResult_t eResult = pFile->WriteKeyValue("classname", m_szClass); |
|
|
|
if (eResult != ChunkFile_Ok) |
|
{ |
|
return(eResult); |
|
} |
|
|
|
// |
|
// Determine whether we have a game data class. This will help us decide which keys |
|
// to write. |
|
// |
|
GDclass *pGameDataClass = NULL; |
|
if (pGD != NULL) |
|
{ |
|
pGameDataClass = pGD->ClassForName(m_szClass); |
|
} |
|
|
|
// |
|
// Consider all the keyvalues in this object for serialization. |
|
// |
|
for ( int z=m_KeyValues.GetFirst(); z != m_KeyValues.GetInvalidIndex(); z=m_KeyValues.GetNext( z ) ) |
|
{ |
|
MDkeyvalue &KeyValue = m_KeyValues.GetKeyValue(z); |
|
|
|
// |
|
// Don't write keys that were already written above. |
|
// |
|
bool bAlreadyWritten = false; |
|
if (!stricmp(KeyValue.szKey, "classname")) |
|
{ |
|
bAlreadyWritten = true; |
|
} |
|
|
|
// |
|
// If the variable wasn't already written above. |
|
// |
|
if (!bAlreadyWritten) |
|
{ |
|
// |
|
// Write it to the MAP file. |
|
// |
|
eResult = pFile->WriteKeyValue(KeyValue.szKey, KeyValue.szValue); |
|
if (eResult != ChunkFile_Ok) |
|
{ |
|
return(eResult); |
|
} |
|
} |
|
} |
|
|
|
// |
|
// If we have a game data class, for each keyvalue in the class definition, write out all keys |
|
// that are not present in the object and whose defaults are nonzero in the class definition. |
|
// |
|
if (pGameDataClass != NULL) |
|
{ |
|
// |
|
// For each variable from the base class... |
|
// |
|
int nVariableCount = pGameDataClass->GetVariableCount(); |
|
for (int i = 0; i < nVariableCount; i++) |
|
{ |
|
GDinputvariable *pVar = pGameDataClass->GetVariableAt(i); |
|
Assert(pVar != NULL); |
|
|
|
if (pVar != NULL) |
|
{ |
|
int iIndex; |
|
LPCTSTR p = m_KeyValues.GetValue(pVar->GetName(), &iIndex); |
|
|
|
// |
|
// If the variable is not present in this object, write out the default value. |
|
// |
|
if (p == NULL) |
|
{ |
|
MDkeyvalue TempKey; |
|
pVar->ResetDefaults(); |
|
pVar->ToKeyValue(&TempKey); |
|
|
|
// |
|
// Only write the key value if it is non-zero. |
|
// |
|
if ((TempKey.szKey[0] != 0) && (TempKey.szValue[0] != 0) && (stricmp(TempKey.szValue, "0"))) |
|
{ |
|
eResult = pFile->WriteKeyValue(TempKey.szKey, TempKey.szValue); |
|
if (eResult != ChunkFile_Ok) |
|
{ |
|
return(eResult); |
|
} |
|
} |
|
} |
|
} |
|
} |
|
} |
|
|
|
// |
|
// Save all the connections. |
|
// |
|
if ((eResult == ChunkFile_Ok) && (Connections_GetCount() > 0)) |
|
{ |
|
eResult = pFile->BeginChunk("connections"); |
|
if (eResult == ChunkFile_Ok) |
|
{ |
|
int nConnCount = Connections_GetCount(); |
|
for (int i = 0; i < nConnCount; i++) |
|
{ |
|
CEntityConnection *pConnection = Connections_Get(i); |
|
if (pConnection != NULL) |
|
{ |
|
char szTemp[512]; |
|
|
|
sprintf(szTemp, "%s,%s,%s,%g,%d", pConnection->GetTargetName(), pConnection->GetInputName(), pConnection->GetParam(), pConnection->GetDelay(), pConnection->GetTimesToFire()); |
|
eResult = pFile->WriteKeyValue(pConnection->GetOutputName(), szTemp); |
|
|
|
if (eResult != ChunkFile_Ok) |
|
{ |
|
return(eResult); |
|
} |
|
} |
|
} |
|
|
|
eResult = pFile->EndChunk(); |
|
} |
|
} |
|
|
|
return(eResult); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Builds a connection from a keyvalue pair. |
|
// Input : szKey - Contains the name of the output. |
|
// szValue - Contains the target, input, delay, and parameter, comma delimited. |
|
// pEditGameClass - Entity to receive the connection. |
|
// Output : Returns ChunkFile_Ok if the data was well-formed, ChunkFile_Fail if not. |
|
//----------------------------------------------------------------------------- |
|
ChunkFileResult_t CEditGameClass::LoadKeyCallback(const char *szKey, const char *szValue, CEditGameClass *pEditGameClass) |
|
{ |
|
CEntityConnection *pConnection = new CEntityConnection; |
|
|
|
// Set the "source" name to be the name of the pEditGameClass' targetname |
|
pConnection->SetSourceName( pEditGameClass->GetKeyValue("targetname") ); // Use the classname if no targetname is defined? |
|
|
|
// Set the "output" from the passed in parameter |
|
pConnection->SetOutputName(szKey); |
|
|
|
char szToken[MAX_PATH]; |
|
|
|
// |
|
// Parse the target name. |
|
// |
|
const char *psz = nexttoken(szToken, szValue, ','); |
|
if (szToken[0] != '\0') |
|
{ |
|
pConnection->SetTargetName(szToken); |
|
} |
|
|
|
// |
|
// Parse the input name. |
|
// |
|
psz = nexttoken(szToken, psz, ','); |
|
if (szToken[0] != '\0') |
|
{ |
|
pConnection->SetInputName(szToken); |
|
} |
|
|
|
// |
|
// Parse the parameter override. |
|
// |
|
psz = nexttoken(szToken, psz, ','); |
|
if (szToken[0] != '\0') |
|
{ |
|
pConnection->SetParam(szToken); |
|
} |
|
|
|
// |
|
// Parse the delay. |
|
// |
|
psz = nexttoken(szToken, psz, ','); |
|
if (szToken[0] != '\0') |
|
{ |
|
pConnection->SetDelay((float)atof(szToken)); |
|
} |
|
|
|
// |
|
// Parse the number of times to fire the output. |
|
// |
|
nexttoken(szToken, psz, ','); |
|
if (szToken[0] != '\0') |
|
{ |
|
pConnection->SetTimesToFire(atoi(szToken)); |
|
} |
|
|
|
pEditGameClass->Connections_Add(pConnection); // Does this belong here or SetSourceName or LinkSourceEntities?? |
|
|
|
return(ChunkFile_Ok); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : *pFile - |
|
// *pEntity - |
|
// Output : ChunkFileResult_t |
|
//----------------------------------------------------------------------------- |
|
ChunkFileResult_t CEditGameClass::LoadConnectionsCallback(CChunkFile *pFile, CEditGameClass *pEditGameClass) |
|
{ |
|
return(pFile->ReadChunk((KeyHandler_t)LoadKeyCallback, pEditGameClass)); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Returns all the spawnflags. |
|
//----------------------------------------------------------------------------- |
|
unsigned long CEditGameClass::GetSpawnFlags(void) |
|
{ |
|
LPCTSTR pszVal = GetKeyValue("spawnflags"); |
|
if (pszVal == NULL) |
|
{ |
|
return(0); |
|
} |
|
|
|
unsigned long val = 0; |
|
sscanf( pszVal, "%lu", &val ); |
|
return val; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Returns true if a given spawnflag (or flags) is set, false if not. |
|
//----------------------------------------------------------------------------- |
|
bool CEditGameClass::GetSpawnFlag(unsigned long nFlags) |
|
{ |
|
unsigned long nSpawnFlags = GetSpawnFlags(); |
|
return((nSpawnFlags & nFlags) != 0); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Sets the given spawnflag (or flags) to the given state. |
|
// Input : nFlag - Flag values to set or clear (ie 1, 2, 4, 8, 16, etc.) |
|
// bSet - True to set the flags, false to clear them. |
|
//----------------------------------------------------------------------------- |
|
void CEditGameClass::SetSpawnFlag(unsigned long nFlags, bool bSet) |
|
{ |
|
unsigned long nSpawnFlags = GetSpawnFlags(); |
|
|
|
if (bSet) |
|
{ |
|
nSpawnFlags |= nFlags; |
|
} |
|
else |
|
{ |
|
nSpawnFlags &= ~nFlags; |
|
} |
|
|
|
SetSpawnFlags(nSpawnFlags); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Sets all the spawnflags at once. |
|
// Input : nSpawnFlags - New value for spawnflags. |
|
//----------------------------------------------------------------------------- |
|
void CEditGameClass::SetSpawnFlags(unsigned long nSpawnFlags) |
|
{ |
|
char szValue[80]; |
|
V_snprintf( szValue, sizeof( szValue ), "%lu", nSpawnFlags ); |
|
SetKeyValue("spawnflags", nSpawnFlags); |
|
}
|
|
|