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.

680 lines
18 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Implements a class that encapsulates much of the functionality
  4. // of entities. CMapWorld and CMapEntity are both derived from this
  5. // class.
  6. //
  7. // CEditGameClass-derived objects have the following properties:
  8. //
  9. // Key/value pairs - A list of string pairs that hold data properties
  10. // of the object. Each property has a unique name.
  11. //
  12. // Connections - A list of outputs in this object that are connected to
  13. // inputs in another entity.
  14. //
  15. //=============================================================================//
  16. #include "stdafx.h"
  17. #include "ChunkFile.h"
  18. #include "fgdlib/GameData.h"
  19. #include "GameConfig.h"
  20. #include "EditGameClass.h"
  21. #include "MapEntity.h"
  22. #include "mathlib/Mathlib.h"
  23. // memdbgon must be the last include file in a .cpp file!!!
  24. #include <tier0/memdbgon.h>
  25. //
  26. // An empty string returned by GetComments when we have no comments set.
  27. //
  28. char *CEditGameClass::g_pszEmpty = "";
  29. //-----------------------------------------------------------------------------
  30. // Purpose: Constructor. Initializes data members.
  31. //-----------------------------------------------------------------------------
  32. CEditGameClass::CEditGameClass(void)
  33. {
  34. m_pClass = NULL;
  35. m_szClass[0] = '\0';
  36. m_pszComments = NULL;
  37. }
  38. //-----------------------------------------------------------------------------
  39. // Purpose: Destructor. Frees memory.
  40. //-----------------------------------------------------------------------------
  41. CEditGameClass::~CEditGameClass(void)
  42. {
  43. delete m_pszComments;
  44. Connections_RemoveAll();
  45. Upstream_RemoveAll();
  46. }
  47. //-----------------------------------------------------------------------------
  48. // Purpose:
  49. // Input : *pConnection -
  50. //-----------------------------------------------------------------------------
  51. void CEditGameClass::Connections_Add(CEntityConnection *pConnection)
  52. {
  53. #if defined(_DEBUG) && 0
  54. LPCTSTR pszTargetName = GetKeyValue("targetname");
  55. if ( pszTargetName && !strcmp(pszTargetName, "zapperpod7_rotator") )
  56. {
  57. // Set breakpoint here for debugging this entity's visiblity
  58. int foo = 0;
  59. }
  60. #endif
  61. if ( m_Connections.Find(pConnection) == -1 )
  62. m_Connections.AddToTail(pConnection);
  63. }
  64. //-----------------------------------------------------------------------------
  65. // Purpose:
  66. // Input : *pConnection -
  67. // Output : Returns true on success, false on failure.
  68. //-----------------------------------------------------------------------------
  69. bool CEditGameClass::Connections_Remove(CEntityConnection *pConnection)
  70. {
  71. int nIndex = m_Connections.Find(pConnection);
  72. if (nIndex != -1)
  73. {
  74. m_Connections.Remove(nIndex);
  75. return(true);
  76. }
  77. return(false);
  78. }
  79. //-----------------------------------------------------------------------------
  80. // NOTE: unlike Connections_Remove, this actually frees each connection!!
  81. //-----------------------------------------------------------------------------
  82. void CEditGameClass::Connections_RemoveAll()
  83. {
  84. //
  85. // Remove all our connections from their targets' upstream lists.
  86. //
  87. int nConnectionsCount = m_Connections.Count();
  88. for (int nConnection = 0; nConnection < nConnectionsCount; nConnection++)
  89. {
  90. CEntityConnection *pConnection = m_Connections.Element( nConnection );
  91. #if defined( ENTITY_MAINTAIN_UPSTREAM_LISTS )
  92. CMapEntityList *pTargetList = pConnection->GetTargetEntityList();
  93. if ( pTargetList )
  94. {
  95. FOR_EACH_OBJ( *pTargetList, pos )
  96. {
  97. CMapEntity *pEntity = pTargetList->Element( pos );
  98. pEntity->Upstream_Remove( pConnection );
  99. }
  100. }
  101. #endif
  102. delete pConnection;
  103. }
  104. m_Connections.RemoveAll();
  105. }
  106. //-----------------------------------------------------------------------------
  107. // Purpose:
  108. //-----------------------------------------------------------------------------
  109. void CEditGameClass::Connections_FixBad(bool bRelink)
  110. {
  111. int nConnectionsCount = m_Connections.Count();
  112. for (int nConnections = 0; nConnections < nConnectionsCount; nConnections++)
  113. {
  114. CEntityConnection *pConnection = m_Connections.Element(nConnections);
  115. CMapEntityList *pTargetEntities = pConnection->GetTargetEntityList();
  116. int nEntityCount = pTargetEntities->Count();
  117. for ( int nEntities = 0; nEntities < nEntityCount; nEntities++ )
  118. {
  119. CMapEntity *pEntity = pTargetEntities->Element(nEntities);
  120. pEntity->Upstream_Remove( pConnection );
  121. }
  122. if ( bRelink )
  123. pConnection->LinkTargetEntities();
  124. }
  125. }
  126. //-----------------------------------------------------------------------------
  127. // Purpose:
  128. // Input : *pConnection -
  129. //-----------------------------------------------------------------------------
  130. void CEditGameClass::Upstream_Add(CEntityConnection *pConnection)
  131. {
  132. #if defined(_DEBUG) && 0
  133. LPCTSTR pszTargetName = GetKeyValue("targetname");
  134. if ( pszTargetName && !strcmp(pszTargetName, "zapperpod7_rotator") )
  135. {
  136. // Set breakpoint here for debugging this entity's visiblity
  137. int foo = 0;
  138. }
  139. #endif
  140. #if defined( ENTITY_MAINTAIN_UPSTREAM_LISTS )
  141. if ( m_Upstream.Find(pConnection) == -1 )
  142. m_Upstream.AddToTail(pConnection);
  143. #endif
  144. }
  145. //-----------------------------------------------------------------------------
  146. // Purpose:
  147. // Input : *pConnection -
  148. // Output : Returns true on success, false on failure.
  149. //-----------------------------------------------------------------------------
  150. bool CEditGameClass::Upstream_Remove(CEntityConnection *pConnection)
  151. {
  152. int nIndex = m_Upstream.Find(pConnection);
  153. if (nIndex != -1)
  154. {
  155. m_Upstream.Remove(nIndex);
  156. return(true);
  157. }
  158. return(false);
  159. }
  160. //-----------------------------------------------------------------------------
  161. // Purpose:
  162. //-----------------------------------------------------------------------------
  163. void CEditGameClass::Upstream_RemoveAll(void)
  164. {
  165. #if defined( ENTITY_MAINTAIN_UPSTREAM_LISTS )
  166. //
  167. // Remove all our connections from their targets' upstream lists.
  168. //
  169. int nUpstreamCount = m_Upstream.Count();
  170. for (int nConnection = 0; nConnection < nUpstreamCount; nConnection++)
  171. {
  172. CEntityConnection *pConnection = m_Upstream.Element( nConnection );
  173. CMapEntityList *pSourceList = pConnection->GetSourceEntityList();
  174. if ( pSourceList )
  175. {
  176. FOR_EACH_OBJ( *pSourceList, pos )
  177. {
  178. CMapEntity *pEntity = pSourceList->Element( pos );
  179. pEntity->Connection_Remove( pConnection );
  180. }
  181. }
  182. }
  183. #endif
  184. m_Upstream.RemoveAll();
  185. }
  186. //-----------------------------------------------------------------------------
  187. // Purpose:
  188. //-----------------------------------------------------------------------------
  189. void CEditGameClass::Upstream_FixBad()
  190. {
  191. #if defined(_DEBUG) && 0
  192. LPCTSTR pszTargetName = GetKeyValue("targetname");
  193. if ( pszTargetName && !strcmp(pszTargetName, "cave_guard_seq1") )
  194. {
  195. // Set breakpoint here for debugging this entity
  196. int foo = 0;
  197. }
  198. #endif
  199. int nUpstreamCount = m_Upstream.Count();
  200. for (int nUpstream = 0; nUpstream < nUpstreamCount; nUpstream++)
  201. {
  202. CEntityConnection *pUpstream = m_Upstream.Element(nUpstream);
  203. pUpstream->LinkTargetEntities();
  204. }
  205. }
  206. //-----------------------------------------------------------------------------
  207. // Purpose:
  208. // Input : pszClass -
  209. // bLoading -
  210. //-----------------------------------------------------------------------------
  211. void CEditGameClass::SetClass(LPCTSTR pszClass, bool bLoading)
  212. {
  213. extern GameData *pGD;
  214. strcpy(m_szClass, pszClass);
  215. StripEdgeWhiteSpace(m_szClass);
  216. if (pGD)
  217. {
  218. m_pClass = pGD->ClassForName(m_szClass);
  219. }
  220. }
  221. //-----------------------------------------------------------------------------
  222. // Purpose: Copies the data from a given CEditGameClass object into this one.
  223. // Input : pFrom - Object to copy.
  224. // Output : Returns a pointer to this.
  225. //-----------------------------------------------------------------------------
  226. CEditGameClass *CEditGameClass::CopyFrom(CEditGameClass *pFrom)
  227. {
  228. m_pClass = pFrom->m_pClass;
  229. strcpy( m_szClass, pFrom->m_szClass );
  230. //
  231. // Copy all the keys.
  232. //
  233. m_KeyValues.RemoveAll();
  234. for ( int i=pFrom->GetFirstKeyValue(); i != pFrom->GetInvalidKeyValue(); i=pFrom->GetNextKeyValue( i ) )
  235. {
  236. m_KeyValues.SetValue(pFrom->GetKey(i), pFrom->GetKeyValue(i));
  237. }
  238. //
  239. // Copy all the connections objects
  240. //
  241. Connections_RemoveAll();
  242. int nConnCount = pFrom->Connections_GetCount();
  243. for (int i = 0; i < nConnCount; i++)
  244. {
  245. CEntityConnection *pConn = pFrom->Connections_Get(i);
  246. CEntityConnection *pNewConn = new CEntityConnection( *pConn );
  247. Connections_Add(pNewConn);
  248. }
  249. //
  250. // Copy the comments.
  251. //
  252. SetComments(pFrom->GetComments());
  253. return(this);
  254. }
  255. //-----------------------------------------------------------------------------
  256. // Purpose: Applies the default keys for this object's game class. Called when
  257. // the entity's class is changed.
  258. //-----------------------------------------------------------------------------
  259. void CEditGameClass::GetDefaultKeys( void )
  260. {
  261. if ( m_pClass != NULL )
  262. {
  263. //
  264. // For each variable from the base class...
  265. //
  266. int nVariableCount = m_pClass->GetVariableCount();
  267. for ( int i = 0; i < nVariableCount; i++ )
  268. {
  269. GDinputvariable *pVar = m_pClass->GetVariableAt(i);
  270. Assert(pVar != NULL);
  271. if (pVar != NULL)
  272. {
  273. int iIndex;
  274. LPCTSTR p = m_KeyValues.GetValue(pVar->GetName(), &iIndex);
  275. //
  276. // If the variable is not present in this object, set the default value.
  277. //
  278. if (p == NULL)
  279. {
  280. MDkeyvalue tmpkv;
  281. pVar->ResetDefaults();
  282. pVar->ToKeyValue(&tmpkv);
  283. //
  284. // Only set the key value if it is non-zero.
  285. //
  286. if ((tmpkv.szKey[0] != 0) && (tmpkv.szValue[0] != 0) && (stricmp(tmpkv.szValue, "0")))
  287. {
  288. SetKeyValue(tmpkv.szKey, tmpkv.szValue);
  289. }
  290. }
  291. }
  292. }
  293. }
  294. }
  295. //-----------------------------------------------------------------------------
  296. // Purpose: Returns this object's angles as a vector of the form:
  297. // [0] PITCH [1] YAW [2] ROLL
  298. //-----------------------------------------------------------------------------
  299. void CEditGameClass::GetAngles(QAngle &vecAngles)
  300. {
  301. vecAngles = vec3_angle;
  302. const char *pszAngles = GetKeyValue("angles");
  303. if (pszAngles != NULL)
  304. {
  305. sscanf(pszAngles, "%f %f %f", &vecAngles[PITCH], &vecAngles[YAW], &vecAngles[ROLL]);
  306. }
  307. }
  308. //-----------------------------------------------------------------------------
  309. // Purpose: Sets a new yaw, overriding any existing angles. Uses special values
  310. // for yaw to indicate pointing straight up or straight down.
  311. //
  312. // This method of representing orientation is obsolete; this code is
  313. // only for importing old RMF or MAP files.
  314. //
  315. // Input : a - Value for angle.
  316. //-----------------------------------------------------------------------------
  317. void CEditGameClass::ImportAngle(int nAngle)
  318. {
  319. QAngle vecAngles;
  320. vecAngles.Init();
  321. if (nAngle == -1) // UP
  322. {
  323. vecAngles[PITCH] = -90;
  324. }
  325. else if (nAngle == -2) // DOWN
  326. {
  327. vecAngles[PITCH] = 90;
  328. }
  329. else
  330. {
  331. vecAngles[YAW] = nAngle;
  332. }
  333. SetAngles(vecAngles);
  334. }
  335. //-----------------------------------------------------------------------------
  336. // Purpose: Sets this object's angles as a vector of the form:
  337. // [0] PITCH [1] YAW [2] ROLL
  338. //-----------------------------------------------------------------------------
  339. void CEditGameClass::SetAngles(const QAngle &vecAngles)
  340. {
  341. char szAngles[80];
  342. sprintf(szAngles, "%g %g %g", (double)vecAngles[PITCH], (double)vecAngles[YAW], (double)vecAngles[ROLL]);
  343. SetKeyValue("angles", szAngles);
  344. }
  345. //-----------------------------------------------------------------------------
  346. // Purpose:
  347. // Input : *pFile -
  348. // Output : ChunkFileResult_t
  349. //-----------------------------------------------------------------------------
  350. ChunkFileResult_t CEditGameClass::SaveVMF(CChunkFile *pFile, CSaveInfo *pSaveInfo)
  351. {
  352. ChunkFileResult_t eResult = pFile->WriteKeyValue("classname", m_szClass);
  353. if (eResult != ChunkFile_Ok)
  354. {
  355. return(eResult);
  356. }
  357. //
  358. // Determine whether we have a game data class. This will help us decide which keys
  359. // to write.
  360. //
  361. GDclass *pGameDataClass = NULL;
  362. if (pGD != NULL)
  363. {
  364. pGameDataClass = pGD->ClassForName(m_szClass);
  365. }
  366. //
  367. // Consider all the keyvalues in this object for serialization.
  368. //
  369. for ( int z=m_KeyValues.GetFirst(); z != m_KeyValues.GetInvalidIndex(); z=m_KeyValues.GetNext( z ) )
  370. {
  371. MDkeyvalue &KeyValue = m_KeyValues.GetKeyValue(z);
  372. //
  373. // Don't write keys that were already written above.
  374. //
  375. bool bAlreadyWritten = false;
  376. if (!stricmp(KeyValue.szKey, "classname"))
  377. {
  378. bAlreadyWritten = true;
  379. }
  380. //
  381. // If the variable wasn't already written above.
  382. //
  383. if (!bAlreadyWritten)
  384. {
  385. //
  386. // Write it to the MAP file.
  387. //
  388. eResult = pFile->WriteKeyValue(KeyValue.szKey, KeyValue.szValue);
  389. if (eResult != ChunkFile_Ok)
  390. {
  391. return(eResult);
  392. }
  393. }
  394. }
  395. //
  396. // If we have a game data class, for each keyvalue in the class definition, write out all keys
  397. // that are not present in the object and whose defaults are nonzero in the class definition.
  398. //
  399. if (pGameDataClass != NULL)
  400. {
  401. //
  402. // For each variable from the base class...
  403. //
  404. int nVariableCount = pGameDataClass->GetVariableCount();
  405. for (int i = 0; i < nVariableCount; i++)
  406. {
  407. GDinputvariable *pVar = pGameDataClass->GetVariableAt(i);
  408. Assert(pVar != NULL);
  409. if (pVar != NULL)
  410. {
  411. int iIndex;
  412. LPCTSTR p = m_KeyValues.GetValue(pVar->GetName(), &iIndex);
  413. //
  414. // If the variable is not present in this object, write out the default value.
  415. //
  416. if (p == NULL)
  417. {
  418. MDkeyvalue TempKey;
  419. pVar->ResetDefaults();
  420. pVar->ToKeyValue(&TempKey);
  421. //
  422. // Only write the key value if it is non-zero.
  423. //
  424. if ((TempKey.szKey[0] != 0) && (TempKey.szValue[0] != 0) && (stricmp(TempKey.szValue, "0")))
  425. {
  426. eResult = pFile->WriteKeyValue(TempKey.szKey, TempKey.szValue);
  427. if (eResult != ChunkFile_Ok)
  428. {
  429. return(eResult);
  430. }
  431. }
  432. }
  433. }
  434. }
  435. }
  436. //
  437. // Save all the connections.
  438. //
  439. if ((eResult == ChunkFile_Ok) && (Connections_GetCount() > 0))
  440. {
  441. eResult = pFile->BeginChunk("connections");
  442. if (eResult == ChunkFile_Ok)
  443. {
  444. int nConnCount = Connections_GetCount();
  445. for (int i = 0; i < nConnCount; i++)
  446. {
  447. CEntityConnection *pConnection = Connections_Get(i);
  448. if (pConnection != NULL)
  449. {
  450. char szTemp[512];
  451. sprintf(szTemp, "%s,%s,%s,%g,%d", pConnection->GetTargetName(), pConnection->GetInputName(), pConnection->GetParam(), pConnection->GetDelay(), pConnection->GetTimesToFire());
  452. eResult = pFile->WriteKeyValue(pConnection->GetOutputName(), szTemp);
  453. if (eResult != ChunkFile_Ok)
  454. {
  455. return(eResult);
  456. }
  457. }
  458. }
  459. eResult = pFile->EndChunk();
  460. }
  461. }
  462. return(eResult);
  463. }
  464. //-----------------------------------------------------------------------------
  465. // Purpose: Builds a connection from a keyvalue pair.
  466. // Input : szKey - Contains the name of the output.
  467. // szValue - Contains the target, input, delay, and parameter, comma delimited.
  468. // pEditGameClass - Entity to receive the connection.
  469. // Output : Returns ChunkFile_Ok if the data was well-formed, ChunkFile_Fail if not.
  470. //-----------------------------------------------------------------------------
  471. ChunkFileResult_t CEditGameClass::LoadKeyCallback(const char *szKey, const char *szValue, CEditGameClass *pEditGameClass)
  472. {
  473. CEntityConnection *pConnection = new CEntityConnection;
  474. // Set the "source" name to be the name of the pEditGameClass' targetname
  475. pConnection->SetSourceName( pEditGameClass->GetKeyValue("targetname") ); // Use the classname if no targetname is defined?
  476. // Set the "output" from the passed in parameter
  477. pConnection->SetOutputName(szKey);
  478. char szToken[MAX_PATH];
  479. //
  480. // Parse the target name.
  481. //
  482. const char *psz = nexttoken(szToken, szValue, ',');
  483. if (szToken[0] != '\0')
  484. {
  485. pConnection->SetTargetName(szToken);
  486. }
  487. //
  488. // Parse the input name.
  489. //
  490. psz = nexttoken(szToken, psz, ',');
  491. if (szToken[0] != '\0')
  492. {
  493. pConnection->SetInputName(szToken);
  494. }
  495. //
  496. // Parse the parameter override.
  497. //
  498. psz = nexttoken(szToken, psz, ',');
  499. if (szToken[0] != '\0')
  500. {
  501. pConnection->SetParam(szToken);
  502. }
  503. //
  504. // Parse the delay.
  505. //
  506. psz = nexttoken(szToken, psz, ',');
  507. if (szToken[0] != '\0')
  508. {
  509. pConnection->SetDelay((float)atof(szToken));
  510. }
  511. //
  512. // Parse the number of times to fire the output.
  513. //
  514. nexttoken(szToken, psz, ',');
  515. if (szToken[0] != '\0')
  516. {
  517. pConnection->SetTimesToFire(atoi(szToken));
  518. }
  519. pEditGameClass->Connections_Add(pConnection); // Does this belong here or SetSourceName or LinkSourceEntities??
  520. return(ChunkFile_Ok);
  521. }
  522. //-----------------------------------------------------------------------------
  523. // Purpose:
  524. // Input : *pFile -
  525. // *pEntity -
  526. // Output : ChunkFileResult_t
  527. //-----------------------------------------------------------------------------
  528. ChunkFileResult_t CEditGameClass::LoadConnectionsCallback(CChunkFile *pFile, CEditGameClass *pEditGameClass)
  529. {
  530. return(pFile->ReadChunk((KeyHandler_t)LoadKeyCallback, pEditGameClass));
  531. }
  532. //-----------------------------------------------------------------------------
  533. // Purpose: Returns all the spawnflags.
  534. //-----------------------------------------------------------------------------
  535. unsigned long CEditGameClass::GetSpawnFlags(void)
  536. {
  537. LPCTSTR pszVal = GetKeyValue("spawnflags");
  538. if (pszVal == NULL)
  539. {
  540. return(0);
  541. }
  542. unsigned long val = 0;
  543. sscanf( pszVal, "%lu", &val );
  544. return val;
  545. }
  546. //-----------------------------------------------------------------------------
  547. // Purpose: Returns true if a given spawnflag (or flags) is set, false if not.
  548. //-----------------------------------------------------------------------------
  549. bool CEditGameClass::GetSpawnFlag(unsigned long nFlags)
  550. {
  551. unsigned long nSpawnFlags = GetSpawnFlags();
  552. return((nSpawnFlags & nFlags) != 0);
  553. }
  554. //-----------------------------------------------------------------------------
  555. // Purpose: Sets the given spawnflag (or flags) to the given state.
  556. // Input : nFlag - Flag values to set or clear (ie 1, 2, 4, 8, 16, etc.)
  557. // bSet - True to set the flags, false to clear them.
  558. //-----------------------------------------------------------------------------
  559. void CEditGameClass::SetSpawnFlag(unsigned long nFlags, bool bSet)
  560. {
  561. unsigned long nSpawnFlags = GetSpawnFlags();
  562. if (bSet)
  563. {
  564. nSpawnFlags |= nFlags;
  565. }
  566. else
  567. {
  568. nSpawnFlags &= ~nFlags;
  569. }
  570. SetSpawnFlags(nSpawnFlags);
  571. }
  572. //-----------------------------------------------------------------------------
  573. // Purpose: Sets all the spawnflags at once.
  574. // Input : nSpawnFlags - New value for spawnflags.
  575. //-----------------------------------------------------------------------------
  576. void CEditGameClass::SetSpawnFlags(unsigned long nSpawnFlags)
  577. {
  578. char szValue[80];
  579. V_snprintf( szValue, sizeof( szValue ), "%lu", nSpawnFlags );
  580. SetKeyValue("spawnflags", nSpawnFlags);
  581. }