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.
688 lines
20 KiB
688 lines
20 KiB
//========= 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); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|