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.
721 lines
16 KiB
721 lines
16 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: |
|
// |
|
// $NoKeywords: $ |
|
//=============================================================================// |
|
|
|
#include "stdafx.h" |
|
#include "MapPath.h" |
|
#include "hammer.h" |
|
#include "EditPathDlg.h" |
|
#include "MapEntity.h" |
|
|
|
// memdbgon must be the last include file in a .cpp file!!! |
|
#include <tier0/memdbgon.h> |
|
|
|
|
|
float GetFileVersion(void); |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
CMapPath::CMapPath(void) |
|
{ |
|
m_iDirection = dirOneway; |
|
SetName(""); |
|
SetClass("path_corner"); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
CMapPath::~CMapPath(void) |
|
{ |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
CMapPathNode::CMapPathNode(void) |
|
{ |
|
bSelected = FALSE; |
|
szName[0] = 0; |
|
} |
|
|
|
CMapPathNode::CMapPathNode(const CMapPathNode& src) |
|
{ |
|
*this = src; |
|
} |
|
|
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : src - |
|
// Output : CMapPathNode |
|
//----------------------------------------------------------------------------- |
|
CMapPathNode &CMapPathNode::operator=(const CMapPathNode &src) |
|
{ |
|
// we don't care. |
|
Q_strncpy( szName, src.szName, sizeof(szName) ); |
|
bSelected = src.bSelected; |
|
kv.RemoveAll(); |
|
for ( int i=src.kv.GetFirst(); i != src.kv.GetInvalidIndex(); i=src.kv.GetNext( i ) ) |
|
{ |
|
MDkeyvalue KeyValue = src.kv.GetKeyValue(i); |
|
kv.SetValue(KeyValue.szKey, KeyValue.szValue); |
|
} |
|
pos = src.pos; |
|
dwID = src.dwID; |
|
|
|
return *this; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : dwID - |
|
// piIndex - |
|
// Output : CMapPathNode * |
|
//----------------------------------------------------------------------------- |
|
CMapPathNode *CMapPath::NodeForID(DWORD dwID, int* piIndex) |
|
{ |
|
for(int iNode = 0; iNode < m_Nodes.Count(); iNode++) |
|
{ |
|
if(m_Nodes[iNode].dwID == dwID) |
|
{ |
|
if(piIndex) |
|
piIndex[0] = iNode; |
|
return &m_Nodes[iNode]; |
|
} |
|
} |
|
|
|
return NULL; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Output : DWORD |
|
//----------------------------------------------------------------------------- |
|
DWORD CMapPath::GetNewNodeID(void) |
|
{ |
|
DWORD dwNewID = 1; |
|
while(true) |
|
{ |
|
int iNode; |
|
for(iNode = 0; iNode < m_Nodes.Count(); iNode++) |
|
{ |
|
if(m_Nodes[iNode].dwID == dwNewID) |
|
break; |
|
} |
|
|
|
if(iNode == m_Nodes.Count()) |
|
return dwNewID; |
|
|
|
++dwNewID; |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : dwAfterID - |
|
// vecPos - |
|
// Output : |
|
//----------------------------------------------------------------------------- |
|
DWORD CMapPath::AddNode(DWORD dwAfterID, const Vector &vecPos) |
|
{ |
|
int iPos; |
|
|
|
if(dwAfterID == ADD_START) |
|
iPos = 0; |
|
else if(dwAfterID == ADD_END) |
|
iPos = m_Nodes.Count(); |
|
else if(!NodeForID(dwAfterID, &iPos)) |
|
return 0; // not found! |
|
|
|
CMapPathNode node; |
|
node.pos = vecPos; |
|
node.bSelected = FALSE; |
|
node.dwID = GetNewNodeID(); |
|
|
|
if(iPos == m_Nodes.Count()) |
|
{ |
|
// add at tail |
|
m_Nodes.AddToTail(node); |
|
} |
|
else |
|
{ |
|
m_Nodes.InsertBefore( iPos, node ); |
|
} |
|
|
|
return node.dwID; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : dwID - |
|
// *pt - |
|
//----------------------------------------------------------------------------- |
|
void CMapPath::SetNodePosition(DWORD dwID, Vector& pt) |
|
{ |
|
int iIndex; |
|
NodeForID(dwID, &iIndex); |
|
|
|
m_Nodes[iIndex].pos = pt; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : dwID - |
|
//----------------------------------------------------------------------------- |
|
void CMapPath::DeleteNode(DWORD dwID) |
|
{ |
|
int iIndex; |
|
if ( NodeForID(dwID, &iIndex) ) |
|
{ |
|
m_Nodes.Remove(iIndex); |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : file - |
|
// fIsStoring - |
|
//----------------------------------------------------------------------------- |
|
void CMapPath::SerializeRMF(std::fstream& file, BOOL fIsStoring) |
|
{ |
|
int iSize; |
|
|
|
if(fIsStoring) |
|
{ |
|
// save!! |
|
file.write(m_szName, 128); |
|
file.write(m_szClass, 128); |
|
file.write((char*) &m_iDirection, sizeof(m_iDirection)); |
|
|
|
iSize = m_Nodes.Count(); |
|
file.write((char*) &iSize, sizeof iSize); |
|
for(int i = 0; i < m_Nodes.Count(); i++) |
|
{ |
|
CMapPathNode& node = m_Nodes[i]; |
|
// store each node |
|
file.write((char*) &node.pos[0], 3 * sizeof(float)); |
|
file.write((char*) &node.dwID, sizeof(node.dwID)); |
|
file.write((char*) &node.szName, sizeof(node.szName)); |
|
|
|
// |
|
// Write keyvalue count. |
|
// |
|
WCKeyValues &kv = node.kv; |
|
iSize = 0; |
|
for ( int z=kv.GetFirst(); z != kv.GetInvalidIndex(); z=kv.GetNext( z ) ) |
|
{ |
|
++iSize; |
|
} |
|
file.write((char*) &iSize, sizeof(iSize)); |
|
|
|
// |
|
// Write keyvalues. |
|
// |
|
for (int k = kv.GetFirst(); k != kv.GetInvalidIndex(); k=kv.GetNext( k ) ) |
|
{ |
|
MDkeyvalue &KeyValue = kv.GetKeyValue(k); |
|
if (KeyValue.szKey[0] != '\0') |
|
{ |
|
KeyValue.SerializeRMF(file, TRUE); |
|
} |
|
} |
|
} |
|
} |
|
else |
|
{ |
|
// load!! |
|
file.read(m_szName, 128); |
|
file.read(m_szClass, 128); |
|
file.read((char*) &m_iDirection, sizeof m_iDirection); |
|
|
|
file.read((char*) &iSize, sizeof iSize); |
|
int nNodes = iSize; |
|
m_Nodes.RemoveAll(); |
|
|
|
// read nodes |
|
for(int i = 0; i < nNodes; i++) |
|
{ |
|
CMapPathNode node; |
|
// store each node |
|
file.read((char*) &node.pos[0], 3 * sizeof(float)); |
|
file.read((char*) &node.dwID, sizeof(node.dwID)); |
|
if(GetFileVersion() >= 1.6f) |
|
{ |
|
file.read((char*) &node.szName, sizeof(node.szName)); |
|
|
|
// read keyvalues |
|
file.read((char*) &iSize, sizeof(iSize)); |
|
WCKeyValues &kv = node.kv; |
|
for (int k = 0; k < iSize; k++) |
|
{ |
|
MDkeyvalue KeyValue; |
|
KeyValue.SerializeRMF(file, FALSE); |
|
kv.SetValue( KeyValue.szKey, KeyValue.szValue ); |
|
} |
|
} |
|
|
|
m_Nodes.AddToTail(node); |
|
} |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : iIndex - |
|
// iName - |
|
// str - |
|
//----------------------------------------------------------------------------- |
|
void CMapPath::GetNodeName(int iIndex, int iName, CString& str) |
|
{ |
|
if(m_Nodes[iIndex].szName[0]) |
|
str = m_Nodes[iIndex].szName; |
|
else |
|
{ |
|
if(iName) |
|
str.Format("%s%02d", m_szName, iName); |
|
else |
|
str = m_szName; |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : file - |
|
// fIsStoring - |
|
// *pIntersecting - |
|
//----------------------------------------------------------------------------- |
|
void CMapPath::SerializeMAP(std::fstream& file, BOOL fIsStoring, BoundBox *pIntersecting) |
|
{ |
|
if( m_Nodes.Count() == 0) |
|
return; |
|
|
|
// if saving WITHIN a box, check all nodes to see if they all |
|
// fit within that box. if not, don't save ANY of the path. |
|
if(pIntersecting) |
|
{ |
|
for(int i = 0; i < m_Nodes.Count(); i++) |
|
{ |
|
if (!pIntersecting->ContainsPoint(m_Nodes[i].pos)) |
|
{ |
|
return; // doesn't intersect - don't save path |
|
} |
|
} |
|
} |
|
|
|
|
|
Assert(fIsStoring); |
|
|
|
CString strTemp; |
|
MDkeyvalue kvTemp; |
|
|
|
// initialize nodes for saving |
|
for(int i = 0; i < m_Nodes.Count(); i++) |
|
{ |
|
m_Nodes[i].nTargets = 0; |
|
} |
|
|
|
int iDirec = 1; |
|
int iCurNode = 0; |
|
int iMax = m_Nodes.Count()-1; |
|
int iName = 0; |
|
|
|
// resolve targets |
|
int iLastNodeIndex = -1; |
|
BOOL bFirstPass = TRUE; |
|
|
|
ResolveNamesAgain: |
|
while(1) |
|
{ |
|
// store targetname |
|
GetNodeName(iCurNode, iName, strTemp); |
|
|
|
// store our name in the previous node (if not -1) |
|
if(iLastNodeIndex != -1) |
|
{ |
|
CMapPathNode &prevNode = m_Nodes[iLastNodeIndex]; |
|
strcpy(prevNode.szTargets[prevNode.nTargets++], strTemp); |
|
} |
|
|
|
++iName; |
|
|
|
iLastNodeIndex = iCurNode; |
|
|
|
if(iCurNode == iMax) |
|
break; |
|
iCurNode += iDirec; |
|
} |
|
|
|
if(bFirstPass && m_iDirection == dirPingpong && m_Nodes.Count() > 2) |
|
{ |
|
// redo loop |
|
bFirstPass = FALSE; |
|
iDirec = -1; |
|
iCurNode = m_Nodes.Count()-2; |
|
iMax = 0; |
|
goto ResolveNamesAgain; |
|
} |
|
else if (m_iDirection == dirCircular) |
|
{ |
|
// |
|
// Connect the last node to the first node. |
|
// |
|
CMapPathNode &LastNode = m_Nodes[iMax]; |
|
GetNodeName(iCurNode, 0, strTemp); |
|
strcpy(LastNode.szTargets[LastNode.nTargets], strTemp); |
|
LastNode.nTargets++; |
|
} |
|
|
|
iDirec = 1; |
|
iCurNode = 0; |
|
iMax = m_Nodes.Count()-1; |
|
iName = 0; |
|
|
|
SaveAgain: |
|
while(1) |
|
{ |
|
file << "{" << "\r\n"; |
|
|
|
// store name |
|
kvTemp.Set("classname", m_szClass); |
|
kvTemp.SerializeMAP(file, TRUE); |
|
|
|
CMapPathNode &node = m_Nodes[iCurNode]; |
|
|
|
// store location |
|
strTemp.Format("%.0f %.0f %.0f", node.pos[0], node.pos[1], |
|
node.pos[2]); |
|
kvTemp.Set("origin", strTemp); |
|
kvTemp.SerializeMAP(file, TRUE); |
|
|
|
// store targetname |
|
GetNodeName(iCurNode, iName, strTemp); |
|
kvTemp.Set("targetname", strTemp); |
|
kvTemp.SerializeMAP(file, TRUE); |
|
|
|
// store target (if not last) |
|
BOOL bStoreTarget = TRUE; |
|
if(iCurNode == iMax && m_iDirection == dirOneway) |
|
bStoreTarget = FALSE; |
|
|
|
if (bStoreTarget) |
|
{ |
|
kvTemp.Set("target", (iDirec == 1) ? node.szTargets[0] : node.szTargets[1]); |
|
kvTemp.SerializeMAP(file, TRUE); |
|
} |
|
|
|
// other keyvalues |
|
WCKeyValues &kv = node.kv; |
|
for (int k = kv.GetFirst(); k != kv.GetInvalidIndex(); k=kv.GetNext( k ) ) |
|
{ |
|
MDkeyvalue &KeyValue = kv.GetKeyValue(k); |
|
if (KeyValue.szKey[0] != '\0') |
|
{ |
|
KeyValue.SerializeMAP(file, TRUE); |
|
} |
|
} |
|
|
|
file << "}" << "\r\n"; |
|
|
|
++iName; |
|
iLastNodeIndex = iCurNode; |
|
|
|
if(iCurNode == iMax) |
|
break; |
|
iCurNode += iDirec; |
|
} |
|
|
|
if(iDirec == 1 && m_iDirection == dirPingpong && m_Nodes.Count() > 2) |
|
{ |
|
// redo loop |
|
iDirec = -1; |
|
iCurNode = m_Nodes.Count()-2; |
|
iMax = 1; |
|
goto SaveAgain; |
|
} |
|
} |
|
|
|
// Edit |
|
|
|
void CMapPath::EditInfo() |
|
{ |
|
CEditPathDlg dlg; |
|
dlg.m_strName = m_szName; |
|
dlg.m_strClass = m_szClass; |
|
dlg.m_iDirection = m_iDirection; |
|
|
|
if(dlg.DoModal() != IDOK) |
|
return; |
|
|
|
SetName(dlg.m_strName); |
|
SetClass(dlg.m_strClass); |
|
m_iDirection = dlg.m_iDirection; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : dwNodeID - |
|
// Output : CMapEntity * |
|
//----------------------------------------------------------------------------- |
|
CMapEntity *CMapPath::CreateEntityForNode(DWORD dwNodeID) |
|
{ |
|
int iIndex; |
|
CMapPathNode *pNode = NodeForID(dwNodeID, &iIndex); |
|
if (pNode == NULL) |
|
{ |
|
return NULL; // no node, no entity! |
|
} |
|
|
|
CMapEntity *pEntity = new CMapEntity; |
|
|
|
for (int k = pNode->kv.GetFirst(); k != pNode->kv.GetInvalidIndex(); k=pNode->kv.GetNext( k ) ) |
|
{ |
|
pEntity->SetKeyValue(pNode->kv.GetKey(k), pNode->kv.GetValue(k)); |
|
} |
|
|
|
// store target/targetname properties: |
|
CString str; |
|
str.Format("%s%02d", m_szName, iIndex); |
|
pEntity->SetKeyValue("targetname", str); |
|
|
|
int iNext = iIndex + 1; |
|
if(iNext != -1) |
|
{ |
|
str.Format("%s%02d", m_szName, iNext); |
|
pEntity->SetKeyValue("target", str); |
|
} |
|
|
|
pEntity->SetClass(m_szClass); |
|
|
|
return pEntity; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : dwNodeID - |
|
// *pEntity - |
|
//----------------------------------------------------------------------------- |
|
void CMapPath::CopyNodeFromEntity(DWORD dwNodeID, CMapEntity *pEntity) |
|
{ |
|
CMapPathNode *pNode = NodeForID(dwNodeID); |
|
if (!pNode) |
|
{ |
|
return; // no node, no copy! |
|
} |
|
|
|
pNode->kv.RemoveAll(); |
|
|
|
// |
|
// Copy all the keys except target and targetname from the entity to the pathnode. |
|
// |
|
for ( int i=pEntity->GetFirstKeyValue(); i != pEntity->GetInvalidKeyValue(); i=pEntity->GetNextKeyValue( i ) ) |
|
{ |
|
if (!strcmp(pEntity->GetKey(i), "target") || !strcmp(pEntity->GetKey(i), "targetname")) |
|
{ |
|
continue; |
|
} |
|
|
|
pNode->kv.SetValue(pEntity->GetKey(i), pEntity->GetKeyValue(i)); |
|
} |
|
} |
|
|
|
|
|
/* |
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : *szKey - |
|
// *szValue - |
|
// *pNode - |
|
// Output : CChunkFileResult_t |
|
//----------------------------------------------------------------------------- |
|
|
|
UNDONE: Nobody uses the path tool because the user interface is so poor. |
|
Path support has been pulled until the tool itself can be fixed or replaced. |
|
|
|
CChunkFileResult_t CMapPathNode::LoadKeyCallback(const char *szKey, const char *szValue, CMapPathNode *pNode) |
|
{ |
|
if (!stricmp(szKey, "origin")) |
|
{ |
|
CChunkFile::ReadKeyValueVector3(szValue, pNode->pos); |
|
} |
|
else if (!stricmp(szKey, "id")) |
|
{ |
|
CChunkFile::ReadKeyValueInt(szValue, &pNode->dwID); |
|
} |
|
else if (!stricmp(szKey, "name")) |
|
{ |
|
strcpy(pNode->szName, szValue); |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : *pFile - |
|
// Output : ChunkFileResult_t |
|
//----------------------------------------------------------------------------- |
|
ChunkFileResult_t CMapPathNode::SaveVMF(CChunkFile *pFile, CSaveInfo *pSaveInfo) |
|
{ |
|
ChunkFileResult_t eResult = pFile->BeginChunk("node"); |
|
|
|
if (eResult == ChunkFile_Ok) |
|
{ |
|
eResult = pFile->WriteKeyValueVector3("origin", node.pos); |
|
} |
|
|
|
if (eResult == ChunkFile_Ok) |
|
{ |
|
eResult = pFile->WriteKeyValueInt("id", node.dwID); |
|
} |
|
|
|
if (eResult == ChunkFile_Ok) |
|
{ |
|
eResult = pFile->WriteKeyValue("name", node.szName); |
|
} |
|
|
|
if (eResult == ChunkFile_Ok) |
|
{ |
|
eResult = pFile->BeginChunk("keys"); |
|
} |
|
|
|
// |
|
// Write keyvalues. |
|
// |
|
if (eResult == ChunkFile_Ok) |
|
{ |
|
iSize = kv.GetCount(); |
|
for (int k = 0; k < iSize; k++) |
|
{ |
|
MDkeyvalue &KeyValue = kv.GetKeyValue(k); |
|
if (eResult == ChunkFile_Ok) |
|
{ |
|
eResult = pFile->WriteKeyValue(KeyValue.GetKey(), KeyValue.GetValue()); |
|
} |
|
} |
|
} |
|
|
|
// End the keys chunk. |
|
if (eResult == ChunkFile_Ok) |
|
{ |
|
eResult = pFile->EndChunk(); |
|
} |
|
|
|
// End the node chunk. |
|
if (eResult == ChunkFile_Ok) |
|
{ |
|
eResult = pFile->EndChunk(); |
|
} |
|
|
|
return(eResult); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : *szKey - |
|
// *szValue - |
|
// *pPath - |
|
// Output : CChunkFileResult_t |
|
//----------------------------------------------------------------------------- |
|
CChunkFileResult_t CMapPath::LoadKeyCallback(const char *szKey, const char *szValue, CMapPath *pPath) |
|
{ |
|
if (!stricmp(szKey, "name")) |
|
{ |
|
pPath->SetName(szValue); |
|
} |
|
else if (!stricmp(szKey, "classname")) |
|
{ |
|
pPath->SetClass(szValue); |
|
} |
|
else if (!stricmp(szKey, "direction")) |
|
{ |
|
CChunkFile::ReadKeyValueInt(szValue, &pPath->m_iDirection); |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : *pFile - |
|
//----------------------------------------------------------------------------- |
|
void CMapPath::LoadVMF(CChunkFile *pFile) |
|
{ |
|
file.read((char*) &iSize, sizeof iSize); |
|
m_nNodes = iSize; |
|
m_Nodes.SetSize(m_nNodes); |
|
|
|
// read nodes |
|
for (int i = 0; i < m_nNodes; i++) |
|
{ |
|
CMapPathNode &node = m_Nodes[i]; |
|
|
|
// read keyvalues |
|
file.read((char*) &iSize, sizeof(iSize)); |
|
KeyValues &kv = node.kv; |
|
kv.SetSize(iSize); |
|
for (int k = 0; k < iSize; k++) |
|
{ |
|
MDkeyvalue &KeyValue = kv.GetKeyValue(k); |
|
KeyValue.SerializeRMF(file, FALSE); |
|
} |
|
} |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : *pFile - |
|
//----------------------------------------------------------------------------- |
|
void CMapPath::SaveVMF(CChunkFile *pFile, CSaveInfo *pSaveInfo) |
|
{ |
|
int iSize; |
|
|
|
ChunkFileResult_t eResult = pFile->BeginChunk("path"); |
|
|
|
if (eResult == ChunkFile_Ok) |
|
{ |
|
eResult = pFile-WriteKeyValue("name", m_szName); |
|
} |
|
|
|
if (eResult == ChunkFile_Ok) |
|
{ |
|
eResult = pFile->WriteKeyValue("classname", m_szClass); |
|
} |
|
|
|
if (eResult == ChunkFile_Ok) |
|
{ |
|
eResult = pFile->WriteKeyValueInt("direction", m_iDirection); |
|
} |
|
|
|
if (eResult == ChunkFile_Ok) |
|
{ |
|
for (int i = 0; i < m_nNodes; i++) |
|
{ |
|
CMapPathNode &node = m_Nodes[i]; |
|
eResult = node.SaveVMF(pFile, pSaveInfo); |
|
} |
|
} |
|
|
|
if (eResult == ChunkFile_Ok) |
|
{ |
|
eResult = pFile->EndChunk(); |
|
} |
|
} |
|
*/
|
|
|