diff options
| author | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
|---|---|---|
| committer | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
| commit | 3bf9df6b2785fa6d951086978a3e66f49427166a (patch) | |
| tree | 2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /hammer/entityconnection.cpp | |
| download | archived-source-engine-2018-hl2-src-master.tar.xz archived-source-engine-2018-hl2-src-master.zip | |
Diffstat (limited to 'hammer/entityconnection.cpp')
| -rw-r--r-- | hammer/entityconnection.cpp | 688 |
1 files changed, 688 insertions, 0 deletions
diff --git a/hammer/entityconnection.cpp b/hammer/entityconnection.cpp new file mode 100644 index 0000000..6a795b4 --- /dev/null +++ b/hammer/entityconnection.cpp @@ -0,0 +1,688 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Defines a connection (output-to-input) between two entities. +// +// The behavior in-game is as follows: +// +// When the given output in the source entity is triggered, the given +// input in the target entity is called after a specified delay, and +// the parameter override (if any) is passed to the input handler. If +// there is no parameter override, the default parameter is passed. +// +// This behavior will occur a specified number of times before the +// connection between the two entities is removed. +// +//=============================================================================// + +#include "stdafx.h" +#include "EntityConnection.h" +#include "MapEntity.h" +#include "MapDoc.h" +#include "MapWorld.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include <tier0/memdbgon.h> + +//----------------------------------------------------------------------------- +// Purpose: Constructor. +//----------------------------------------------------------------------------- +CEntityConnection::CEntityConnection(void) +{ + memset(m_szSourceEntity, 0, sizeof(m_szSourceEntity)); + memset(m_szTargetEntity, 0, sizeof(m_szTargetEntity)); + memset(m_szOutput, 0, sizeof(m_szOutput)); + memset(m_szInput, 0, sizeof(m_szInput)); + memset(m_szParam, 0, sizeof(m_szParam)); + + m_pSourceEntityList = new CMapEntityList; + m_pTargetEntityList = new CMapEntityList; + + m_fDelay = 0; + m_nTimesToFire = EVENT_FIRE_ALWAYS; +} + +//----------------------------------------------------------------------------- +// Purpose: Copy Constructor. +//----------------------------------------------------------------------------- + +CEntityConnection::CEntityConnection( const CEntityConnection &Other ) +{ + m_pSourceEntityList = new CMapEntityList; + m_pTargetEntityList = new CMapEntityList; + + *this = Other; // Invoke the Operator= to complete the construction job +} + +//----------------------------------------------------------------------------- +// Purpose: Destructor. +//----------------------------------------------------------------------------- +CEntityConnection::~CEntityConnection() +{ + if ( m_pSourceEntityList ) + { + m_pSourceEntityList->RemoveAll(); + delete m_pSourceEntityList; + m_pSourceEntityList = NULL; + } + if ( m_pTargetEntityList ) + { + m_pTargetEntityList->RemoveAll(); + delete m_pTargetEntityList; + m_pTargetEntityList = NULL; + } +} + +//----------------------------------------------------------------------------- +// Purpose: Operator= overload. Makes 'this' identical to 'Other'. +//----------------------------------------------------------------------------- +CEntityConnection &CEntityConnection::operator =(const CEntityConnection &Other) +{ + strcpy(m_szSourceEntity, Other.m_szSourceEntity); + strcpy(m_szTargetEntity, Other.m_szTargetEntity); + strcpy(m_szOutput, Other.m_szOutput); + strcpy(m_szInput, Other.m_szInput); + strcpy(m_szParam, Other.m_szParam); + m_fDelay = Other.m_fDelay; + m_nTimesToFire = Other.m_nTimesToFire; + + // Invoke EntityList operator= to make copies. + *m_pSourceEntityList = *Other.m_pSourceEntityList; + *m_pTargetEntityList = *Other.m_pTargetEntityList; + + return(*this); +} + +//----------------------------------------------------------------------------- +// Purpose: Sets a new Input Name and sets links to any matching entities +//----------------------------------------------------------------------------- + +void CEntityConnection::SetSourceName(const char *pszName) +{ + // Save the name of the entity(ies) + lstrcpyn(m_szSourceEntity, pszName ? pszName : "<<null>>", sizeof(m_szSourceEntity)); + + // Update the source entity list + // LinkSourceEntities(); // Changing the entity connection source name shouldnt change the source entity linkage, right? +} + +//----------------------------------------------------------------------------- +// Purpose: Sets a new Output Name and sets links to any matching entities +//----------------------------------------------------------------------------- + +void CEntityConnection::SetTargetName(const char *pszName) +{ + // Save the name of the entity(ies) + lstrcpyn(m_szTargetEntity, pszName ? pszName : "<<null>>", sizeof(m_szTargetEntity)); + + // Update the target entity list + LinkTargetEntities(); +} + +//----------------------------------------------------------------------------- +// Purpose: Links to any matching Source entities +//----------------------------------------------------------------------------- +void CEntityConnection::LinkSourceEntities() +{ + // Empty out the existing entity list + m_pSourceEntityList->RemoveAll(); + + // Get a list of all the entities in the world + CMapDoc *pDoc = CMapDoc::GetActiveMapDoc(); + + if (pDoc) + { + CMapWorld *pWorld = pDoc->GetMapWorld(); + + if (pWorld) + { + CMapEntityList matches; + pWorld->FindEntitiesByName( matches, m_szSourceEntity, false ); + + for ( int i = 0; i < matches.Count(); i++ ) + { + CMapEntity *pEntity = matches.Element( i ); + + m_pSourceEntityList->AddToTail( pEntity ); + //pEntity->Connection_Add( this ); // This should already be true on creation, investigate need for this func + } + } + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Links to any matching Target entities +//----------------------------------------------------------------------------- +void CEntityConnection::LinkTargetEntities() +{ + // Unlink us from the downstream entities. + FOR_EACH_OBJ( *m_pTargetEntityList, pos ) + { + CMapEntity *pEntity = m_pTargetEntityList->Element( pos ); + pEntity->Upstream_Remove( this ); + } + + // Empty out the existing entity list + m_pTargetEntityList->RemoveAll(); + + // Get a list of all the entities in the world + CMapDoc *pDoc = CMapDoc::GetActiveMapDoc(); + + if (pDoc) + { + CMapWorld *pWorld = pDoc->GetMapWorld(); + + if (pWorld) + { + CMapEntityList matches; + pWorld->FindEntitiesByName( matches, m_szTargetEntity, false ); + + for ( int i = 0; i < matches.Count(); i++ ) + { + CMapEntity *pEntity = matches.Element( i ); + + m_pTargetEntityList->AddToTail( pEntity ); + + // Special -- Add this connection to the target entity connection list + pEntity->Upstream_Add( this ); + } + } + } +} + + +//------------------------------------------------------------------------------ +// Purpose: Tells if any of the target entities are visible. +//------------------------------------------------------------------------------ +bool CEntityConnection::AreAnyTargetEntitiesVisible() +{ + CMapEntityList *pList = GetTargetEntityList(); + for ( int iTarget=0; iTarget < pList->Count(); iTarget++ ) + { + if ( pList->Element( iTarget )->IsVisible() ) + return true; + } + + return false; +} + + +//------------------------------------------------------------------------------ +// Purpose: Returns true if output string is valid for all this entity +//------------------------------------------------------------------------------ +bool CEntityConnection::ValidateOutput(CMapEntity *pEntity, const char* pszOutput) +{ + if (!pEntity) + { + return false; + } + + GDclass* pClass = pEntity->GetClass(); + if (pClass != NULL) + { + if (pClass->FindOutput(pszOutput) == NULL) + { + return false; + } + } + return true; +} + +//------------------------------------------------------------------------------ +// Purpose : Returns true if output string is valid for all the entities in +// the entity list +// Input : +// Output : +//------------------------------------------------------------------------------ +bool CEntityConnection::ValidateOutput(const CMapEntityList *pEntityList, const char* pszOutput) +{ + if (!pEntityList) + { + return false; + } + + FOR_EACH_OBJ( *pEntityList, pos ) + { + CMapEntity* pEntity = pEntityList->Element(pos); + if (!ValidateOutput(pEntity,pszOutput)) + { + return false; + } + } + return true; +} + + +//------------------------------------------------------------------------------ +// Purpose: Returns true if the given entity list contains an entity of the +// given target name +//------------------------------------------------------------------------------ +bool CEntityConnection::ValidateTarget( const CMapEntityList *pEntityList, bool bVisibilityCheck, const char *pszTarget) +{ + if (!pEntityList || !pszTarget) + return false; + + // These procedural names are always assumed to exist. + if (!stricmp(pszTarget, "!activator") || !stricmp(pszTarget, "!caller") || !stricmp(pszTarget, "!player") || !stricmp(pszTarget, "!self")) + return true; + + FOR_EACH_OBJ( *pEntityList, pos ) + { + CMapEntity *pEntity = pEntityList->Element(pos); + if ( bVisibilityCheck && !pEntity->IsVisible() ) + continue; + + if (pEntity->NameMatches(pszTarget)) + return true; + } + + return false; +} + + +//------------------------------------------------------------------------------ +// Purpose: Returns true if all entities with the given target name +// have an input of the given input name +//------------------------------------------------------------------------------ +bool CEntityConnection::ValidateInput(const char* pszTarget, const char *pszInput, bool bVisiblesOnly) +{ + // Allow any input into !activator and !player. + // dvs: TODO: pass in the entity to resolve !self and check input list + if (!stricmp(pszTarget, "!activator") || !stricmp(pszTarget, "!caller") || !stricmp(pszTarget, "!player") || !stricmp(pszTarget, "!self")) + { + return true; + } + + CMapDoc *pDoc = CMapDoc::GetActiveMapDoc(); + CMapEntityList EntityList; + pDoc->FindEntitiesByName(EntityList, pszTarget, bVisiblesOnly); + + if (EntityList.Count() == 0) + { + return false; + } + + if (!MapEntityList_HasInput( &EntityList, pszInput)) + { + return false; + } + + return true; +} + + +//----------------------------------------------------------------------------- +// Purpose: Finds any output connections from this entity that are bad. +// "Bad" is defined as: +// +// 1) An output that this entity doesn't actually have. +// 2) Connecting to a nonexistent entity. +// 3) Connecting to a nonexistent input in an entity that exists. +// +// Input : pEntity - The entity to check for bad connections. +//----------------------------------------------------------------------------- +void CEntityConnection::FindBadConnections(CMapEntity *pEntity, bool bVisibilityCheck, CUtlVector<CEntityConnection *> &BadConnectionList, bool bIgnoreHiddenTargets) +{ + BadConnectionList.RemoveAll(); + + if ((!pEntity) || (pEntity->Connections_GetCount() == 0)) + { + return; + } + + // Get a list of all the entities in the world + const CMapEntityList *pAllWorldEntities = NULL; + CMapDoc *pDoc = CMapDoc::GetActiveMapDoc(); + if (pDoc) + { + CMapWorld *pWorld = pDoc->GetMapWorld(); + if (pWorld) + { + pAllWorldEntities = pWorld->EntityList_GetList(); + } + } + + // For each connection + int nConnCount = pEntity->Connections_GetCount(); + for (int i = 0; i < nConnCount; i++) + { + CEntityConnection *pConnection = pEntity->Connections_Get(i); + if (pConnection != NULL) + { + if ( bIgnoreHiddenTargets ) + { + if ( pConnection->GetTargetEntityList()->Count() > 0 && !pConnection->AreAnyTargetEntitiesVisible() ) + continue; + } + + // Check validity of output for this entity + if (!CEntityConnection::ValidateOutput(pEntity, pConnection->GetOutputName())) + { + BadConnectionList.AddToTail(pConnection); + } + // Check validity of target entity (is it in the map?) + else if (!CEntityConnection::ValidateTarget(pAllWorldEntities, bVisibilityCheck, pConnection->GetTargetName())) + { + BadConnectionList.AddToTail(pConnection); + } + // Check validity of input + else if (!CEntityConnection::ValidateInput(pConnection->GetTargetName(), pConnection->GetInputName(), true)) + { + BadConnectionList.AddToTail(pConnection); + } + } + } +} + + +//------------------------------------------------------------------------------ +// Purpose: Check if all the output connections in the given entity are valid. +// Output : OUTPUTS_NONE if entity has no outputs +// OUTPUTS_GOOD if all entity outputs are good +// OUTPUTS_BAD if any entity output is bad +//------------------------------------------------------------------------------ +int CEntityConnection::ValidateOutputConnections(CMapEntity *pEntity, bool bVisibilityCheck, bool bIgnoreHiddenTargets) +{ + if (!pEntity) + { + return CONNECTION_NONE; + } + + if (pEntity->Connections_GetCount() == 0) + { + return CONNECTION_NONE; + } + + CUtlVector<CEntityConnection *> BadConnectionList; + FindBadConnections(pEntity, bVisibilityCheck, BadConnectionList, bIgnoreHiddenTargets); + + if (BadConnectionList.Count() > 0) + { + return CONNECTION_BAD; + } + + return CONNECTION_GOOD; +} + + +//----------------------------------------------------------------------------- +// Purpose: Fixes any output connections from this entity that are bad. +// Input : pEntity - The entity to fix. +//----------------------------------------------------------------------------- +void CEntityConnection::FixBadConnections(CMapEntity *pEntity, bool bVisibilityCheck ) +{ + CUtlVector<CEntityConnection *> BadConnectionList; + FindBadConnections(pEntity, bVisibilityCheck, BadConnectionList); + + // Remove the bad connections. + int nBadConnCount = BadConnectionList.Count(); + for (int i = 0; i < nBadConnCount; i++) + { + CEntityConnection *pConnection = BadConnectionList.Element(i); + pEntity->Connections_Remove( pConnection ); + + // + // Remove the connection from the upstream list of all entities it targets. + // + CMapEntityList *pTargetList = pConnection->GetTargetEntityList(); + if ( pTargetList ) + { + FOR_EACH_OBJ( *pTargetList, pos ) + { + pEntity = pTargetList->Element( pos ); + pEntity->Upstream_Remove( pConnection ); + } + } + + delete pConnection; + } +} + + +//------------------------------------------------------------------------------ +// Purpose: Check if all the output connections in the given entity are valid. +// Output : INPUTS_NONE, // if entity list has no inputs +// INPUTS_GOOD, // if all entity inputs are good +// INPUTS_BAD, // if any entity input is bad +//------------------------------------------------------------------------------ +int CEntityConnection::ValidateInputConnections(CMapEntity *pEntity, bool bVisibilityCheck) +{ + if (!pEntity) + { + return CONNECTION_NONE; + } + + // No inputs if entity doesn't have a target name + const char *pszTargetName = pEntity->GetKeyValue("targetname"); + if (!pszTargetName) + { + return CONNECTION_NONE; + } + + GDclass *pClass = pEntity->GetClass(); + if (!pClass) + { + return CONNECTION_NONE; + } + + // Get a list of all the entities in the world + const CMapEntityList *pAllWorldEntities = NULL; + CMapDoc *pDoc = CMapDoc::GetActiveMapDoc(); + if (pDoc) + { + CMapWorld *pWorld = pDoc->GetMapWorld(); + if (pWorld) + { + pAllWorldEntities = pWorld->EntityList_GetList(); + } + } + + // Look at outputs from each entity in the world + + bool bHaveConnection = false; + FOR_EACH_OBJ( *pAllWorldEntities, pos ) + { + CMapEntity *pTestEntity = pAllWorldEntities->Element(pos); + if (pTestEntity == NULL) + continue; + + if ( bVisibilityCheck && !pTestEntity->IsVisible() ) + continue; + + int nConnCount = pTestEntity->Connections_GetCount(); + for (int i = 0; i < nConnCount; i++) + { + // If the connection targets me + CEntityConnection *pConnection = pTestEntity->Connections_Get(i); + if ( pConnection && pEntity->NameMatches( pConnection->GetTargetName() ) ) + { + // Validate output + if (!ValidateOutput(pTestEntity, pConnection->GetOutputName())) + { + return CONNECTION_BAD; + } + + // Validate input + if (pClass->FindInput(pConnection->GetInputName()) == NULL) + { + return CONNECTION_BAD; + } + + // FIXME -- Validate the upstream connections the target entities. + bHaveConnection = true; + } + } + } + + + if (bHaveConnection) + { + return CONNECTION_GOOD; + } + return CONNECTION_NONE; +} + +//----------------------------------------------------------------------------- +// Purpose: Compares by delays. Used as a secondary sort by all other columns. +//----------------------------------------------------------------------------- +int CALLBACK CEntityConnection::CompareDelaysSecondary(CEntityConnection *pConn1, CEntityConnection *pConn2, SortDirection_t eDirection) +{ + float fDelay1; + float fDelay2; + + if (eDirection == Sort_Ascending) + { + fDelay1 = pConn1->GetDelay(); + fDelay2 = pConn2->GetDelay(); + } + else + { + fDelay1 = pConn2->GetDelay(); + fDelay2 = pConn1->GetDelay(); + } + + if (fDelay1 < fDelay2) + { + return(-1); + } + else if (fDelay1 > fDelay2) + { + return(1); + } + + return(0); +} + +//----------------------------------------------------------------------------- +// Purpose: Compares by delays, does a secondary compare by output name. +//----------------------------------------------------------------------------- +int CALLBACK CEntityConnection::CompareDelays(CEntityConnection *pConn1, CEntityConnection *pConn2, SortDirection_t eDirection) +{ + int nReturn = CompareDelaysSecondary(pConn1, pConn2, eDirection); + if (nReturn != 0) + { + return(nReturn); + } + + // + // Always do a secondary sort by output name. + // + return(CompareOutputNames(pConn1, pConn2, Sort_Ascending)); +} + + +//----------------------------------------------------------------------------- +// Purpose: Compares by output name, does a secondary compare by delay. +//----------------------------------------------------------------------------- +int CALLBACK CEntityConnection::CompareOutputNames(CEntityConnection *pConn1, CEntityConnection *pConn2, SortDirection_t eDirection) +{ + int nReturn = 0; + + if (eDirection == Sort_Ascending) + { + nReturn = stricmp(pConn1->GetOutputName(), pConn2->GetOutputName()); + } + else + { + nReturn = stricmp(pConn2->GetOutputName(), pConn1->GetOutputName()); + } + + // + // Always do a secondary sort by delay. + // + if (nReturn == 0) + { + nReturn = CompareDelaysSecondary(pConn1, pConn2, Sort_Ascending); + } + + return(nReturn); +} + + +//----------------------------------------------------------------------------- +// Purpose: Compares by input name, does a secondary compare by delay. +//----------------------------------------------------------------------------- +int CALLBACK CEntityConnection::CompareInputNames(CEntityConnection *pConn1, CEntityConnection *pConn2, SortDirection_t eDirection) +{ + int nReturn = 0; + + if (eDirection == Sort_Ascending) + { + nReturn = stricmp(pConn1->GetInputName(), pConn2->GetInputName()); + } + else + { + nReturn = stricmp(pConn2->GetInputName(), pConn1->GetInputName()); + } + + // + // Always do a secondary sort by delay. + // + if (nReturn == 0) + { + nReturn = CompareDelaysSecondary(pConn1, pConn2, Sort_Ascending); + } + + return(nReturn); +} + +//----------------------------------------------------------------------------- +// Purpose: Compares by source name, does a secondary compare by delay. +//----------------------------------------------------------------------------- +int CALLBACK CEntityConnection::CompareSourceNames(CEntityConnection *pConn1, CEntityConnection *pConn2, SortDirection_t eDirection) +{ + int nReturn = 0; + + if (eDirection == Sort_Ascending) + { + nReturn = CompareEntityNames(pConn1->GetSourceName(), pConn2->GetSourceName()); + } + else + { + nReturn = CompareEntityNames(pConn2->GetSourceName(), pConn1->GetSourceName()); + } + + // + // Always do a secondary sort by delay. + // + if (nReturn == 0) + { + nReturn = CompareDelaysSecondary(pConn1, pConn2, Sort_Ascending); + } + + return(nReturn); +} + +//----------------------------------------------------------------------------- +// Purpose: Compares by target name, does a secondary compare by delay. +//----------------------------------------------------------------------------- +int CALLBACK CEntityConnection::CompareTargetNames(CEntityConnection *pConn1, CEntityConnection *pConn2, SortDirection_t eDirection) +{ + int nReturn = 0; + + if (eDirection == Sort_Ascending) + { + nReturn = CompareEntityNames(pConn1->GetTargetName(), pConn2->GetTargetName()); + } + else + { + nReturn = CompareEntityNames(pConn2->GetTargetName(), pConn1->GetTargetName()); + } + + // + // Always do a secondary sort by delay. + // + if (nReturn == 0) + { + nReturn = CompareDelaysSecondary(pConn1, pConn2, Sort_Ascending); + } + + return(nReturn); +} + + + + + + + |