Team Fortress 2 Source Code as on 22/4/2020
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

//========= 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();
}
}
*/