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.

1965 lines
51 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================//
  6. #include "stdafx.h"
  7. #include "generichash.h"
  8. #include "CullTreeNode.h"
  9. #include "GlobalFunctions.h"
  10. #include "MainFrm.h"
  11. #include "MapDefs.h"
  12. #include "MapDoc.h" // dvs: think of a way around the world knowing about the doc
  13. #include "MapEntity.h"
  14. #include "MapGroup.h"
  15. #include "MapSolid.h"
  16. #include "MapWorld.h"
  17. #include "SaveInfo.h"
  18. #include "StatusBarIDs.h"
  19. #include "VisGroup.h"
  20. #include "hammer.h"
  21. #include "Worldsize.h"
  22. #include "MapOverlay.h"
  23. #include "Manifest.h"
  24. // memdbgon must be the last include file in a .cpp file!!!
  25. #include <tier0/memdbgon.h>
  26. #pragma warning(disable:4244)
  27. class CCullTreeNode;
  28. IMPLEMENT_MAPCLASS(CMapWorld)
  29. struct SaveLists_t
  30. {
  31. CMapObjectList Solids;
  32. CMapObjectList Entities;
  33. CMapObjectList Groups;
  34. };
  35. //-----------------------------------------------------------------------------
  36. // Purpose:
  37. // Input : *pSolid -
  38. // *pList -
  39. // Output : static BOOL
  40. //-----------------------------------------------------------------------------
  41. static BOOL AddUsedTextures(CMapSolid *pSolid, CUsedTextureList *pList)
  42. {
  43. if (!pSolid->IsVisible())
  44. return TRUE;
  45. int nFaces = pSolid->GetFaceCount();
  46. IEditorTexture *pLastTex = NULL;
  47. int nLastElement = 0;
  48. for (int i = 0; i < nFaces; i++)
  49. {
  50. CMapFace *pFace = pSolid->GetFace(i);
  51. UsedTexture_t Tex;
  52. Tex.pTex = pFace->GetTexture();
  53. Tex.nUsageCount = 0;
  54. if (Tex.pTex != NULL)
  55. {
  56. if (Tex.pTex != pLastTex)
  57. {
  58. int nElement = pList->Find(Tex.pTex);
  59. if (nElement == -1)
  60. {
  61. nElement = pList->AddToTail(Tex);
  62. }
  63. nLastElement = nElement;
  64. pLastTex = Tex.pTex;
  65. }
  66. pList->Element(nLastElement).nUsageCount++;
  67. }
  68. }
  69. return TRUE;
  70. }
  71. static BOOL AddOverlayTextures(CMapOverlay *pOverlay, CUsedTextureList *pList)
  72. {
  73. if (!pOverlay->IsVisible())
  74. return TRUE;
  75. UsedTexture_t Tex;
  76. Tex.pTex = pOverlay->GetMaterial();
  77. Tex.nUsageCount = 0;
  78. if (Tex.pTex != NULL)
  79. {
  80. int nElement = pList->Find(Tex.pTex);
  81. if (nElement == -1)
  82. nElement = pList->AddToTail(Tex);
  83. pList->Element(nElement).nUsageCount++;
  84. }
  85. return TRUE;
  86. }
  87. //-----------------------------------------------------------------------------
  88. // Purpose: Returns whether the two boxes intersect.
  89. // Input : mins1 -
  90. // maxs1 -
  91. // mins2 -
  92. // maxs2 -
  93. //-----------------------------------------------------------------------------
  94. bool BoxesIntersect(Vector const &mins1, Vector const &maxs1, Vector const &mins2, Vector const &maxs2)
  95. {
  96. if ((maxs1[0] < mins2[0]) || (mins1[0] > maxs2[0]) ||
  97. (maxs1[1] < mins2[1]) || (mins1[1] > maxs2[1]) ||
  98. (maxs1[2] < mins2[2]) || (mins1[2] > maxs2[2]))
  99. {
  100. return(false);
  101. }
  102. return(true);
  103. }
  104. //-----------------------------------------------------------------------------
  105. // Purpose: Constructor. Initializes data members.
  106. //-----------------------------------------------------------------------------
  107. CMapWorld::CMapWorld( void )
  108. {
  109. }
  110. //-----------------------------------------------------------------------------
  111. // Purpose: Constructor. Initializes data members.
  112. //-----------------------------------------------------------------------------
  113. CMapWorld::CMapWorld( CMapDoc *pOwningDocument )
  114. {
  115. //
  116. // Make sure subsequent UpdateBounds() will be effective.
  117. //
  118. m_Render2DBox.ResetBounds();
  119. Vector pt( 0, 0, 0 );
  120. m_Render2DBox.UpdateBounds(pt);
  121. SetClass("worldspawn");
  122. m_pCullTree = NULL;
  123. m_nNextFaceID = 1; // Face IDs start at 1. An ID of 0 means no ID.
  124. // create the world displacement manager
  125. m_pWorldDispMgr = CreateWorldEditDispMgr();
  126. m_pOwningDocument = pOwningDocument;
  127. }
  128. //-----------------------------------------------------------------------------
  129. // Purpose: Destructor. Deletes all paths in the world and the culling tree.
  130. //-----------------------------------------------------------------------------
  131. CMapWorld::~CMapWorld(void)
  132. {
  133. // Delete paths.
  134. m_Paths.PurgeAndDeleteElements();
  135. //
  136. // Delete the culling tree.
  137. //
  138. CullTree_Free();
  139. // destroy the world displacement manager
  140. DestroyWorldEditDispMgr( &m_pWorldDispMgr );
  141. }
  142. //-----------------------------------------------------------------------------
  143. // Purpose: Overridden to maintain the culling tree. Root level children of the
  144. // world are kept in the culling tree.
  145. // Input : pChild - object to add as a child.
  146. //-----------------------------------------------------------------------------
  147. void CMapWorld::AddChild(CMapClass *pChild)
  148. {
  149. CMapClass::AddChild(pChild);
  150. //
  151. // Add the object to the culling tree.
  152. //
  153. if (m_pCullTree != NULL)
  154. {
  155. m_pCullTree->AddCullTreeObjectRecurse(pChild);
  156. }
  157. }
  158. //-----------------------------------------------------------------------------
  159. // Purpose: The sole way to add an object to the world.
  160. //
  161. // NOTE: Do not call this during file load!! Similar (but different)
  162. // bookkeeping is done in PostloadWorld during serialization.
  163. //
  164. // Input : pObject - object to add to the world.
  165. // pParent - object to use as the new object's parent.
  166. //-----------------------------------------------------------------------------
  167. void CMapWorld::AddObjectToWorld(CMapClass *pObject, CMapClass *pParent)
  168. {
  169. Assert(pObject != NULL);
  170. if (pObject == NULL)
  171. {
  172. return;
  173. }
  174. //
  175. // Link the object into the tree.
  176. //
  177. if (pParent == NULL)
  178. {
  179. pParent = this;
  180. }
  181. pParent->AddChild(pObject);
  182. //
  183. // If this object or any of its children are entities, add the entities
  184. // to our optimized list of entities.
  185. //
  186. EntityList_Add(pObject);
  187. //
  188. // Notify the object that it has been added to the world.
  189. //
  190. pObject->OnAddToWorld(this);
  191. }
  192. //-----------------------------------------------------------------------------
  193. // Purpose: Sorts all the objects in the world into three lists: entities, solids,
  194. // and groups. These lists are then serialized in SaveVMF.
  195. // Input : pSaveLists - Receives lists of objects.
  196. //-----------------------------------------------------------------------------
  197. BOOL CMapWorld::BuildSaveListsCallback(CMapClass *pObject, SaveLists_t *pSaveLists)
  198. {
  199. CMapEntity *pEntity = dynamic_cast<CMapEntity *>(pObject);
  200. if (pEntity != NULL)
  201. {
  202. pSaveLists->Entities.AddToTail(pEntity);
  203. return(TRUE);
  204. }
  205. CMapSolid *pSolid = dynamic_cast<CMapSolid *>(pObject);
  206. if (pSolid != NULL)
  207. {
  208. pSaveLists->Solids.AddToTail(pSolid);
  209. return(TRUE);
  210. }
  211. CMapGroup *pGroup = dynamic_cast<CMapGroup *>(pObject);
  212. if (pGroup != NULL)
  213. {
  214. pSaveLists->Groups.AddToTail(pGroup);
  215. return(TRUE);
  216. }
  217. return(TRUE);
  218. }
  219. //-----------------------------------------------------------------------------
  220. // Purpose:
  221. // Input :
  222. // Output : CMapClass
  223. //-----------------------------------------------------------------------------
  224. CMapClass *CMapWorld::Copy(bool bUpdateDependencies)
  225. {
  226. CMapWorld *pWorld = new CMapWorld;
  227. pWorld->CopyFrom(this, bUpdateDependencies);
  228. return pWorld;
  229. }
  230. //-----------------------------------------------------------------------------
  231. // Purpose:
  232. // Input : *pobj -
  233. // Output : CMapClass
  234. //-----------------------------------------------------------------------------
  235. CMapClass *CMapWorld::CopyFrom(CMapClass *pobj, bool bUpdateDependencies)
  236. {
  237. Assert(pobj->IsMapClass(MAPCLASS_TYPE(CMapWorld)));
  238. CMapWorld *pFrom = (CMapWorld *)pobj;
  239. CMapClass::CopyFrom(pobj, bUpdateDependencies);
  240. //
  241. // Copy our keys. If our targetname changed we must relink all targetname pointers.
  242. //
  243. const char *pszOldTargetName = CEditGameClass::GetKeyValue("targetname");
  244. char szOldTargetName[MAX_IO_NAME_LEN];
  245. if (pszOldTargetName != NULL)
  246. {
  247. strcpy(szOldTargetName, pszOldTargetName);
  248. }
  249. CEditGameClass::CopyFrom(pFrom);
  250. const char *pszNewTargetName = CEditGameClass::GetKeyValue("targetname");
  251. if ((bUpdateDependencies) && (pszNewTargetName != NULL))
  252. {
  253. if (stricmp(szOldTargetName, pszNewTargetName) != 0)
  254. {
  255. UpdateAllDependencies(this);
  256. }
  257. }
  258. return this;
  259. }
  260. //-----------------------------------------------------------------------------
  261. // Hash the string to the bucket index where it belongs.
  262. //-----------------------------------------------------------------------------
  263. static inline int EntityBucketForName( const char *pszName )
  264. {
  265. if ( !pszName )
  266. return 0;
  267. unsigned int nHash = HashStringCaseless( pszName );
  268. return nHash % NUM_HASHED_ENTITY_BUCKETS;
  269. }
  270. //-----------------------------------------------------------------------------
  271. //-----------------------------------------------------------------------------
  272. int CMapWorld::FindEntityBucket( CMapEntity *pEntity, int *pnIndex )
  273. {
  274. for ( int i = 0; i < NUM_HASHED_ENTITY_BUCKETS; i++ )
  275. {
  276. int nIndex = m_EntityListByName[ i ].Find( pEntity );
  277. if ( nIndex != -1 )
  278. {
  279. if ( pnIndex )
  280. {
  281. *pnIndex = nIndex;
  282. }
  283. return i;
  284. }
  285. }
  286. return -1;
  287. }
  288. //-----------------------------------------------------------------------------
  289. //-----------------------------------------------------------------------------
  290. void CMapWorld::AddEntity( CMapEntity *pEntity )
  291. {
  292. if ( m_EntityList.Find( pEntity ) != -1 )
  293. return;
  294. // Add it to the flat list.
  295. m_EntityList.AddToTail( pEntity );
  296. // If it has a name, add it to the list of entities hashed by name checksum.
  297. const char *pszName = pEntity->GetKeyValue( "targetname" );
  298. if ( pszName )
  299. {
  300. int nBucket = EntityBucketForName( pszName );
  301. m_EntityListByName[ nBucket ].AddToTail( pEntity );
  302. }
  303. }
  304. //-----------------------------------------------------------------------------
  305. // Purpose: Adds any entities found in the given object tree to the list of
  306. // entities that are in this world. Called whenever an object is added
  307. // to this world.
  308. // Input : pObject - object (and children) to add to the entity list.
  309. //-----------------------------------------------------------------------------
  310. void CMapWorld::EntityList_Add(CMapClass *pObject)
  311. {
  312. CMapEntity *pEntity = dynamic_cast<CMapEntity *>(pObject);
  313. if (pEntity != NULL)
  314. {
  315. AddEntity(pEntity);
  316. }
  317. EnumChildrenPos_t pos;
  318. CMapClass *pChild = pObject->GetFirstDescendent(pos);
  319. while (pChild != NULL)
  320. {
  321. pEntity = dynamic_cast<CMapEntity *>(pChild);
  322. if ((pEntity != NULL) && (m_EntityList.Find(pEntity) == -1))
  323. {
  324. AddEntity(pEntity);
  325. }
  326. pChild = pObject->GetNextDescendent(pos);
  327. }
  328. }
  329. //-----------------------------------------------------------------------------
  330. // Purpose: Removes this object (if it is an entity) or any of its entity
  331. // descendents from this world's entity list. Called when an object is
  332. // removed from this world.
  333. // Input : pObject - Object to remove from the entity list.
  334. //-----------------------------------------------------------------------------
  335. void CMapWorld::EntityList_Remove(CMapClass *pObject, bool bRemoveChildren)
  336. {
  337. //
  338. // Remove the object itself.
  339. //
  340. CMapEntity *pEntity = dynamic_cast<CMapEntity *>(pObject);
  341. if (pEntity != NULL)
  342. {
  343. // Remove the entity from the flat list.
  344. int nIndex = m_EntityList.Find( pEntity );
  345. if ( nIndex != -1 )
  346. {
  347. m_EntityList.FastRemove( nIndex );
  348. }
  349. // Remove the entity from the hashed list.
  350. int nOldBucket = FindEntityBucket( pEntity, &nIndex );
  351. if ( nOldBucket != -1 )
  352. {
  353. m_EntityListByName[ nOldBucket ].FastRemove( nIndex );
  354. }
  355. Assert( m_EntityList.Find( pEntity ) == -1 );
  356. }
  357. //
  358. // Remove entity children.
  359. //
  360. if (bRemoveChildren)
  361. {
  362. EnumChildrenPos_t pos;
  363. CMapClass *pChild = pObject->GetFirstDescendent(pos);
  364. while (pChild != NULL)
  365. {
  366. pEntity = dynamic_cast<CMapEntity *>(pChild);
  367. if (pEntity != NULL)
  368. {
  369. m_EntityList.FindAndRemove(pEntity);
  370. }
  371. pChild = pObject->GetNextDescendent(pos);
  372. }
  373. }
  374. }
  375. //-----------------------------------------------------------------------------
  376. // Purpose: Overridden to maintain the culling tree. Root level children of the
  377. // world are kept in the culling tree.
  378. // Input : pChild - child to remove.
  379. //-----------------------------------------------------------------------------
  380. void CMapWorld::RemoveChild(CMapClass *pChild, bool bUpdateBounds)
  381. {
  382. CMapClass::RemoveChild(pChild, bUpdateBounds);
  383. //
  384. // Remove the object from the culling tree because it is no longer a root-level child.
  385. //
  386. if (m_pCullTree != NULL)
  387. {
  388. m_pCullTree->RemoveCullTreeObjectRecurse(pChild);
  389. }
  390. }
  391. //-----------------------------------------------------------------------------
  392. // Purpose: this function will attempt to find a child. If the bool and matrix
  393. // are supplied, the localized matrix will be built.
  394. // Input : key - the key field to lookup
  395. // value - the value to find
  396. // Output : returns the entity found
  397. // bIsInInstance - optional parameter to indicate if the found entity is inside of an instance
  398. // InstanceMatrix - optional parameter to set the localized matrix of the instance stack
  399. //-----------------------------------------------------------------------------
  400. CMapEntity *CMapWorld::FindChildByKeyValue( const char* key, const char* value, bool *bIsInInstance, VMatrix *InstanceMatrix )
  401. {
  402. if ( bIsInInstance )
  403. {
  404. *bIsInInstance = false;
  405. }
  406. return __super::FindChildByKeyValue( key, value, bIsInInstance, InstanceMatrix );
  407. }
  408. //-----------------------------------------------------------------------------
  409. // Purpose: Removes an object from the world.
  410. // Input : pObject - object to remove from the world.
  411. // bChildren - whether we're removing the object's children as well.
  412. //-----------------------------------------------------------------------------
  413. void CMapWorld::RemoveObjectFromWorld(CMapClass *pObject, bool bRemoveChildren)
  414. {
  415. Assert(pObject != NULL);
  416. if (pObject == NULL)
  417. {
  418. return;
  419. }
  420. //
  421. // Unlink the object from the tree.
  422. //
  423. CMapClass *pParent = pObject->GetParent();
  424. Assert(pParent != NULL);
  425. if (pParent != NULL)
  426. {
  427. pParent->RemoveChild(pObject);
  428. }
  429. //
  430. // If it (or any of its children) is an entity, remove it from this
  431. // world's list of entities.
  432. //
  433. EntityList_Remove(pObject, bRemoveChildren);
  434. //
  435. // Notify the object so it can release any pointers it may have to other
  436. // objects in the world. We don't do this in RemoveChild because the object
  437. // may only be changing parents rather than leaving the world.
  438. //
  439. pObject->OnRemoveFromWorld(this, bRemoveChildren);
  440. }
  441. //-----------------------------------------------------------------------------
  442. // Purpose: Special implementation of UpdateChild for the world object. This
  443. // notifies the document that an object's bounding box has changed.
  444. // Input : pChild -
  445. //-----------------------------------------------------------------------------
  446. void CMapWorld::UpdateChild(CMapClass *pChild)
  447. {
  448. if ( CMapClass::s_bLoadingVMF )
  449. return;
  450. // Recalculate the bounds of this child's branch.
  451. pChild->CalcBounds(TRUE);
  452. // Recalculate own bounds
  453. CalcBounds( FALSE );
  454. //
  455. // Relink the child in the culling tree.
  456. //
  457. if (m_pCullTree != NULL)
  458. {
  459. m_pCullTree->UpdateCullTreeObjectRecurse(pChild);
  460. }
  461. //
  462. // Notify the document that an object in the world has changed.
  463. //
  464. if (!IsTemporary()) // HACK: check to avoid prefab objects ending up in the doc's update list
  465. {
  466. CMapDoc *pDoc = CMapDoc::GetActiveMapDoc();
  467. if (pDoc != NULL)
  468. {
  469. pDoc->UpdateObject(pChild);
  470. }
  471. }
  472. if ( CMapDoc::GetInLevelLoad() == 0 )
  473. {
  474. APP()->pMapDocTemplate->UpdateInstanceMap( m_pOwningDocument );
  475. APP()->pManifestDocTemplate->UpdateInstanceMap( m_pOwningDocument );
  476. }
  477. }
  478. //-----------------------------------------------------------------------------
  479. // Purpose:
  480. // Input : *pList -
  481. //-----------------------------------------------------------------------------
  482. void CMapWorld::GetUsedTextures(CUsedTextureList &List)
  483. {
  484. List.RemoveAll();
  485. EnumChildren((ENUMMAPCHILDRENPROC)AddUsedTextures, (DWORD)&List, MAPCLASS_TYPE(CMapSolid));
  486. EnumChildren((ENUMMAPCHILDRENPROC)AddOverlayTextures, (DWORD)&List, MAPCLASS_TYPE(CMapOverlay));
  487. }
  488. //-----------------------------------------------------------------------------
  489. // Purpose:
  490. // Input : pNode -
  491. //-----------------------------------------------------------------------------
  492. void CMapWorld::CullTree_FreeNode(CCullTreeNode *pNode)
  493. {
  494. if ( pNode == NULL )
  495. {
  496. Assert(pNode != NULL);
  497. return;
  498. }
  499. int nChildCount = pNode->GetChildCount();
  500. if (nChildCount != 0)
  501. {
  502. for (int nChild = 0; nChild < nChildCount; nChild++)
  503. {
  504. CCullTreeNode *pChild = pNode->GetCullTreeChild(nChild);
  505. CullTree_FreeNode(pChild);
  506. }
  507. }
  508. delete pNode;
  509. }
  510. //-----------------------------------------------------------------------------
  511. // Purpose: Recursively deletes the entire culling tree if is it not NULL.
  512. // This does not delete the map objects that the culling tree contains,
  513. // only the leaves and nodes themselves.
  514. //-----------------------------------------------------------------------------
  515. void CMapWorld::CullTree_Free(void)
  516. {
  517. if (m_pCullTree != NULL)
  518. {
  519. CullTree_FreeNode(m_pCullTree);
  520. m_pCullTree = NULL;
  521. }
  522. }
  523. //-----------------------------------------------------------------------------
  524. // Purpose: Determines if this node is a node or a leaf. If it is a node, it will
  525. // be split into eight children and each child will be populated with
  526. // objects whose bounding boxes intersect them, then split recursively.
  527. // If this node is a leaf, no action is taken and recursion terminates.
  528. // Input : pNode -
  529. //-----------------------------------------------------------------------------
  530. #define MIN_NODE_DIM 1024 // Minimum node size of 170 x 170 x 170 feet
  531. #define MIN_NODE_OBJECT_SPLIT 2 // Don't split nodes with fewer than two objects.
  532. void CMapWorld::CullTree_SplitNode(CCullTreeNode *pNode)
  533. {
  534. Vector Mins;
  535. Vector Maxs;
  536. Vector Size;
  537. pNode->GetBounds(Mins, Maxs);
  538. VectorSubtract(Maxs, Mins, Size);
  539. if ((Size[0] > MIN_NODE_DIM) && (Size[1] > MIN_NODE_DIM) && (Size[2] > MIN_NODE_DIM))
  540. {
  541. Vector Mids;
  542. int nChild;
  543. Mids[0] = (Mins[0] + Maxs[0]) / 2.0;
  544. Mids[1] = (Mins[1] + Maxs[1]) / 2.0;
  545. Mids[2] = (Mins[2] + Maxs[2]) / 2.0;
  546. for (nChild = 0; nChild < 8; nChild++)
  547. {
  548. Vector ChildMins;
  549. Vector ChildMaxs;
  550. //
  551. // Create a child and set its bounding box.
  552. //
  553. CCullTreeNode *pChild = new CCullTreeNode;
  554. if (nChild & 1)
  555. {
  556. ChildMins[0] = Mins[0];
  557. ChildMaxs[0] = Mids[0];
  558. }
  559. else
  560. {
  561. ChildMins[0] = Mids[0];
  562. ChildMaxs[0] = Maxs[0];
  563. }
  564. if (nChild & 2)
  565. {
  566. ChildMins[1] = Mins[1];
  567. ChildMaxs[1] = Mids[1];
  568. }
  569. else
  570. {
  571. ChildMins[1] = Mids[1];
  572. ChildMaxs[1] = Maxs[1];
  573. }
  574. if (nChild & 4)
  575. {
  576. ChildMins[2] = Mins[2];
  577. ChildMaxs[2] = Mids[2];
  578. }
  579. else
  580. {
  581. ChildMins[2] = Mids[2];
  582. ChildMaxs[2] = Maxs[2];
  583. }
  584. pChild->UpdateBounds(ChildMins, ChildMaxs);
  585. pNode->AddCullTreeChild(pChild);
  586. Vector mins1;
  587. Vector maxs1;
  588. pChild->GetBounds(mins1, maxs1);
  589. //
  590. // Check all objects in this node against the child's bounding box, adding the
  591. // objects that intersect to the child's object list.
  592. //
  593. int nObjectCount = pNode->GetObjectCount();
  594. for (int nObject = 0; nObject < nObjectCount; nObject++)
  595. {
  596. CMapClass *pObject = pNode->GetCullTreeObject(nObject);
  597. Assert(pObject != NULL);
  598. Vector mins2;
  599. Vector maxs2;
  600. pObject->GetCullBox(mins2, maxs2);
  601. if (BoxesIntersect(mins1, maxs1, mins2, maxs2))
  602. {
  603. pChild->AddCullTreeObject(pObject);
  604. }
  605. }
  606. }
  607. //
  608. // Remove all objects from this node's object list (since we are not a leaf).
  609. //
  610. pNode->RemoveAllCullTreeObjects();
  611. //
  612. // Recurse into all children with at least two objects, splitting them.
  613. //
  614. int nChildCount = pNode->GetChildCount();
  615. for (nChild = 0; nChild < nChildCount; nChild++)
  616. {
  617. CCullTreeNode *pChild = pNode->GetCullTreeChild(nChild);
  618. if (pChild->GetObjectCount() >= MIN_NODE_OBJECT_SPLIT)
  619. {
  620. CullTree_SplitNode(pChild);
  621. }
  622. }
  623. }
  624. }
  625. //-----------------------------------------------------------------------------
  626. // Purpose:
  627. // Input : pNode -
  628. //-----------------------------------------------------------------------------
  629. void CMapWorld::CullTree_DumpNode(CCullTreeNode *pNode, int nDepth)
  630. {
  631. int nChildCount = pNode->GetChildCount();
  632. char szText[100];
  633. if (nChildCount == 0)
  634. {
  635. // Leaf
  636. OutputDebugString("LEAF:\n");
  637. int nObjectCount = pNode->GetObjectCount();
  638. for (int nObject = 0; nObject < nObjectCount; nObject++)
  639. {
  640. CMapClass *pMapClass = pNode->GetCullTreeObject(nObject);
  641. sprintf(szText, "%*c %p %s\n", nDepth, ' ', pMapClass, pMapClass->GetType());
  642. OutputDebugString(szText);
  643. }
  644. }
  645. else
  646. {
  647. // Node
  648. sprintf(szText, "%*s\n", nDepth, "+");
  649. OutputDebugString(szText);
  650. for (int nChild = 0; nChild < nChildCount; nChild++)
  651. {
  652. CCullTreeNode *pChild = pNode->GetCullTreeChild(nChild);
  653. CullTree_DumpNode(pChild, nDepth + 1);
  654. }
  655. OutputDebugString("\n");
  656. }
  657. }
  658. //-----------------------------------------------------------------------------
  659. // Purpose:
  660. //-----------------------------------------------------------------------------
  661. void CMapWorld::CullTree_Build(void)
  662. {
  663. CullTree_Free();
  664. m_pCullTree = new CCullTreeNode;
  665. //
  666. // The top level node in the tree uses the largest possible bounding box.
  667. //
  668. Vector BoxMins( g_MIN_MAP_COORD, g_MIN_MAP_COORD, g_MIN_MAP_COORD );
  669. Vector BoxMaxs( g_MAX_MAP_COORD, g_MAX_MAP_COORD, g_MAX_MAP_COORD );
  670. m_pCullTree->UpdateBounds(BoxMins, BoxMaxs);
  671. //
  672. // Populate the top level node with the contents of the world.
  673. //
  674. FOR_EACH_OBJ( m_Children, pos )
  675. {
  676. CMapClass *pObject = m_Children.Element(pos);
  677. m_pCullTree->AddCullTreeObject(pObject);
  678. }
  679. //
  680. // Recursively split this node into children and populate them.
  681. //
  682. CullTree_SplitNode(m_pCullTree);
  683. //DumpCullTreeNode(m_pCullTree, 1);
  684. //OutputDebugString("\n");
  685. }
  686. //-----------------------------------------------------------------------------
  687. // Purpose: Returns a list of all the groups in the world.
  688. //-----------------------------------------------------------------------------
  689. int CMapWorld::GetGroupList(CUtlVector<CMapGroup *> &GroupList)
  690. {
  691. GroupList.RemoveAll();
  692. EnumChildrenPos_t pos;
  693. CMapClass *pChild = GetFirstDescendent(pos);
  694. while (pChild != NULL)
  695. {
  696. if (pChild->IsGroup())
  697. {
  698. GroupList.AddToTail((CMapGroup *)pChild);
  699. }
  700. pChild = GetNextDescendent(pos);
  701. }
  702. return GroupList.Count();
  703. }
  704. //-----------------------------------------------------------------------------
  705. // Purpose: Called after all objects in the World have been loaded. Calls the
  706. // PostLoadWorld function for every object in the world, then
  707. // builds the culling tree.
  708. //-----------------------------------------------------------------------------
  709. void CMapWorld::PostloadWorld(void)
  710. {
  711. // This causes certain calculations to get delayed until the end.
  712. CMapClass::s_bLoadingVMF = true;
  713. //
  714. // Set the class name from our "classname" key and discard the key.
  715. //
  716. int nIndex;
  717. const char *pszValue = pszValue = m_KeyValues.GetValue("classname", &nIndex);
  718. if (pszValue != NULL)
  719. {
  720. SetClass(pszValue);
  721. RemoveKey(nIndex);
  722. }
  723. //
  724. // Call PostLoadWorld on all our children and add any entities to the
  725. // entity list.
  726. //
  727. FOR_EACH_OBJ( m_Children, pos )
  728. {
  729. CMapClass *pChild = m_Children[pos];
  730. pChild->PostloadWorld(this);
  731. EntityList_Add(pChild);
  732. }
  733. // Since s_bLoadingVMF was on before, a bunch of stuff got delayed. Now let's do that stuff.
  734. CMapClass::s_bLoadingVMF = false;
  735. FOR_EACH_OBJ( m_Children, pos )
  736. {
  737. CMapClass *pChild = m_Children[pos];
  738. pChild->CalcBounds( TRUE );
  739. //
  740. // Relink the child in the culling tree.
  741. //
  742. if (m_pCullTree != NULL)
  743. {
  744. m_pCullTree->UpdateCullTreeObjectRecurse(pChild);
  745. }
  746. pChild->PostUpdate(Notify_Changed);
  747. pChild->SignalChanged();
  748. }
  749. CalcBounds( FALSE ); // Recalculate the world's bounds now that everyone else's bounds are upadted.
  750. }
  751. //-----------------------------------------------------------------------------
  752. // Purpose:
  753. // Input : pFile -
  754. // pData -
  755. // Output : ChunkFileResult_t
  756. //-----------------------------------------------------------------------------
  757. ChunkFileResult_t CMapWorld::LoadGroupCallback(CChunkFile *pFile, CMapWorld *pWorld)
  758. {
  759. CMapGroup *pGroup = new CMapGroup;
  760. ChunkFileResult_t eResult = pGroup->LoadVMF(pFile);
  761. if (eResult == ChunkFile_Ok)
  762. {
  763. pWorld->AddChild(pGroup);
  764. }
  765. return(eResult);
  766. }
  767. //-----------------------------------------------------------------------------
  768. // Purpose:
  769. // Input : *pLoadInfo -
  770. // *pWorld -
  771. // Output : ChunkFileResult_t
  772. //-----------------------------------------------------------------------------
  773. ChunkFileResult_t CMapWorld::LoadHiddenCallback(CChunkFile *pFile, CMapWorld *pWorld)
  774. {
  775. //
  776. // Set up handlers for the subchunks that we are interested in.
  777. //
  778. CChunkHandlerMap Handlers;
  779. Handlers.AddHandler("solid", (ChunkHandler_t)LoadSolidCallback, pWorld);
  780. pFile->PushHandlers(&Handlers);
  781. ChunkFileResult_t eResult = pFile->ReadChunk();
  782. pFile->PopHandlers();
  783. return(eResult);
  784. }
  785. //-----------------------------------------------------------------------------
  786. // Purpose: Handles keyvalues when loading the world chunk of MAP files.
  787. // Input : szKey - Key to handle.
  788. // szValue - Value of key.
  789. // pWorld - World being loaded.
  790. // Output : Returns ChunkFile_Ok if all is well.
  791. //-----------------------------------------------------------------------------
  792. ChunkFileResult_t CMapWorld::LoadKeyCallback(const char *szKey, const char *szValue, CMapWorld *pWorld)
  793. {
  794. if (!stricmp(szKey, "id"))
  795. {
  796. pWorld->SetID(atoi(szValue));
  797. }
  798. else if (stricmp(szKey, "mapversion") != 0)
  799. {
  800. pWorld->SetKeyValue(szKey, szValue);
  801. }
  802. return(ChunkFile_Ok);
  803. }
  804. //-----------------------------------------------------------------------------
  805. // Purpose:
  806. // Input : *pLoadInfo -
  807. // Output : ChunkFileResult_t
  808. //-----------------------------------------------------------------------------
  809. ChunkFileResult_t CMapWorld::LoadVMF(CChunkFile *pFile)
  810. {
  811. //
  812. // Set up handlers for the subchunks that we are interested in.
  813. //
  814. CChunkHandlerMap Handlers;
  815. Handlers.AddHandler("solid", (ChunkHandler_t)LoadSolidCallback, this);
  816. Handlers.AddHandler("hidden", (ChunkHandler_t)LoadHiddenCallback, this);
  817. Handlers.AddHandler("group", (ChunkHandler_t)LoadGroupCallback, this);
  818. Handlers.AddHandler("connections", (ChunkHandler_t)LoadConnectionsCallback, (CEditGameClass *)this);
  819. pFile->PushHandlers(&Handlers);
  820. ChunkFileResult_t eResult = pFile->ReadChunk((KeyHandler_t)LoadKeyCallback, this);
  821. pFile->PopHandlers();
  822. return(eResult);
  823. }
  824. //-----------------------------------------------------------------------------
  825. // Purpose:
  826. // Input : *pLoadInfo -
  827. // *pWorld -
  828. // Output : ChunkFileResult_t
  829. //-----------------------------------------------------------------------------
  830. ChunkFileResult_t CMapWorld::LoadSolidCallback(CChunkFile *pFile, CMapWorld *pWorld)
  831. {
  832. CMapSolid *pSolid = new CMapSolid;
  833. bool bValid;
  834. ChunkFileResult_t eResult = pSolid->LoadVMF(pFile, bValid);
  835. if ((eResult == ChunkFile_Ok) && (bValid))
  836. {
  837. const char *pszValue = pSolid->GetEditorKeyValue("cordonsolid");
  838. if (pszValue == NULL)
  839. {
  840. pWorld->AddChild(pSolid);
  841. }
  842. }
  843. else
  844. {
  845. delete pSolid;
  846. }
  847. return(eResult);
  848. }
  849. //-----------------------------------------------------------------------------
  850. // Purpose: Calls PresaveWorld in all of the world's descendents.
  851. //-----------------------------------------------------------------------------
  852. void CMapWorld::PresaveWorld(void)
  853. {
  854. EnumChildrenPos_t pos;
  855. CMapClass *pChild = GetFirstDescendent(pos);
  856. while (pChild != NULL)
  857. {
  858. pChild->PresaveWorld();
  859. pChild = GetNextDescendent(pos);
  860. }
  861. }
  862. //-----------------------------------------------------------------------------
  863. // Purpose:
  864. // Input :
  865. // Output :
  866. //-----------------------------------------------------------------------------
  867. ChunkFileResult_t CMapWorld::SaveSolids(CChunkFile *pFile, CSaveInfo *pSaveInfo, int saveFlags)
  868. {
  869. PresaveWorld();
  870. SaveLists_t SaveLists;
  871. EnumChildrenRecurseGroupsOnly((ENUMMAPCHILDRENPROC)BuildSaveListsCallback, (DWORD)&SaveLists);
  872. return SaveObjectListVMF(pFile, pSaveInfo, &SaveLists.Solids, saveFlags);
  873. }
  874. //-----------------------------------------------------------------------------
  875. // Purpose: Saves all solids, entities, and groups in the world to a VMF file.
  876. // Input : pFile - File object to use for saving.
  877. // pSaveInfo - Holds rules for which objects to save.
  878. // Output : Returns ChunkFile_Ok if the save was successful, or an error code.
  879. //-----------------------------------------------------------------------------
  880. ChunkFileResult_t CMapWorld::SaveVMF(CChunkFile *pFile, CSaveInfo *pSaveInfo, int saveFlags)
  881. {
  882. PresaveWorld();
  883. //
  884. // Sort the world objects into lists for saving into different chunks.
  885. //
  886. SaveLists_t SaveLists;
  887. EnumChildrenRecurseGroupsOnly((ENUMMAPCHILDRENPROC)BuildSaveListsCallback, (DWORD)&SaveLists);
  888. //
  889. // Begin the world chunk.
  890. //
  891. ChunkFileResult_t eResult = ChunkFile_Ok;
  892. if( !(saveFlags & SAVEFLAGS_LIGHTSONLY) )
  893. {
  894. eResult = pFile->BeginChunk("world");
  895. //
  896. // Save world ID - it's always zero.
  897. //
  898. if (eResult == ChunkFile_Ok)
  899. {
  900. eResult = pFile->WriteKeyValueInt("id", GetID());
  901. }
  902. //
  903. // HACK: Save map version. This is already being saved in the version info block by the doc.
  904. //
  905. if (eResult == ChunkFile_Ok)
  906. {
  907. eResult = pFile->WriteKeyValueInt("mapversion", CMapDoc::GetActiveMapDoc()->GetDocVersion());
  908. }
  909. //
  910. // Save world keys.
  911. //
  912. if (eResult == ChunkFile_Ok)
  913. {
  914. CEditGameClass::SaveVMF(pFile, pSaveInfo);
  915. }
  916. //
  917. // Save world solids.
  918. //
  919. if (eResult == ChunkFile_Ok)
  920. {
  921. eResult = SaveObjectListVMF(pFile, pSaveInfo, &SaveLists.Solids, saveFlags);
  922. }
  923. //
  924. // Save groups.
  925. //
  926. if (eResult == ChunkFile_Ok)
  927. {
  928. eResult = SaveObjectListVMF(pFile, pSaveInfo, &SaveLists.Groups, saveFlags);
  929. }
  930. //
  931. // End the world chunk.
  932. //
  933. if (eResult == ChunkFile_Ok)
  934. {
  935. pFile->EndChunk();
  936. }
  937. }
  938. //
  939. // Save entities and their solid children.
  940. //
  941. if (eResult == ChunkFile_Ok)
  942. {
  943. eResult = SaveObjectListVMF(pFile, pSaveInfo, &SaveLists.Entities, saveFlags);
  944. }
  945. return(eResult);
  946. }
  947. //-----------------------------------------------------------------------------
  948. // Purpose:
  949. // Input : *pFile -
  950. // *pList -
  951. // Output : ChunkFileResult_t
  952. //-----------------------------------------------------------------------------
  953. ChunkFileResult_t CMapWorld::SaveObjectListVMF(CChunkFile *pFile, CSaveInfo *pSaveInfo, const CMapObjectList *pList, int saveFlags)
  954. {
  955. FOR_EACH_OBJ( *pList, pos )
  956. {
  957. CMapClass *pObject = pList->Element(pos);
  958. // Only save lights if that's what they want.
  959. if( saveFlags & SAVEFLAGS_LIGHTSONLY )
  960. {
  961. CMapEntity *pMapEnt = dynamic_cast<CMapEntity*>( pObject );
  962. bool bIsLight = pMapEnt && strncmp( pMapEnt->GetClassName(), "light", 5 ) == 0;
  963. if( !bIsLight )
  964. continue;
  965. }
  966. if (pObject != NULL)
  967. {
  968. ChunkFileResult_t eResult = pObject->SaveVMF(pFile, pSaveInfo);
  969. if (eResult != ChunkFile_Ok)
  970. {
  971. return(eResult);
  972. }
  973. }
  974. }
  975. return(ChunkFile_Ok);
  976. }
  977. //-----------------------------------------------------------------------------
  978. // Purpose: Adds a given character to the end of a string if there isn't one already.
  979. // Input : psz - String to add the backslash to.
  980. // ch - Character to check for (and add if not found).
  981. // nSize - Size of buffer pointer to by psz.
  982. // Output : Returns true if there was enough space in the dest buffer, false if not.
  983. //-----------------------------------------------------------------------------
  984. static bool EnsureTrailingChar(char *psz, char ch, int nSize)
  985. {
  986. int nLen = strlen(psz);
  987. if ((psz[0] != '\0') && (psz[nLen - 1] != ch))
  988. {
  989. if (nLen < (nSize - 1))
  990. {
  991. psz[nLen++] = ch;
  992. psz[nLen] = '\0';
  993. }
  994. else
  995. {
  996. // No room to add the character.
  997. return(false);
  998. }
  999. }
  1000. return(true);
  1001. }
  1002. //-----------------------------------------------------------------------------
  1003. // Purpose: Finds the face with the corresponding face ID.
  1004. // FIXME: AAARGH, slow!! Need to build a table or something.
  1005. // Input : nFaceID -
  1006. //-----------------------------------------------------------------------------
  1007. CMapFace *CMapWorld::FaceID_FaceForID(int nFaceID)
  1008. {
  1009. EnumChildrenPos_t pos;
  1010. CMapClass *pChild = GetFirstDescendent(pos);
  1011. while (pChild != NULL)
  1012. {
  1013. CMapSolid *pSolid = dynamic_cast <CMapSolid *>(pChild);
  1014. if (pSolid != NULL)
  1015. {
  1016. int nFaceCount = pSolid->GetFaceCount();
  1017. for (int nFace = 0; nFace < nFaceCount; nFace++)
  1018. {
  1019. CMapFace *pFace = pSolid->GetFace(nFace);
  1020. if (pFace->GetFaceID() == nFaceID)
  1021. {
  1022. return(pFace);
  1023. }
  1024. }
  1025. }
  1026. pChild = GetNextDescendent(pos);
  1027. }
  1028. return(NULL);
  1029. }
  1030. //-----------------------------------------------------------------------------
  1031. // Purpose: Concatenates strings without overrunning the dest buffer.
  1032. // Input : szDest -
  1033. // szSrc -
  1034. // nDestSize -
  1035. // Output : Returns true if all chars were copied, false if we ran out of room.
  1036. //-----------------------------------------------------------------------------
  1037. static bool AppendString(char *szDest, char const *szSrc, int nDestSize)
  1038. {
  1039. int nDestLen = strlen(szDest);
  1040. int nDestAvail = nDestSize - nDestLen - 1;
  1041. char *pszStart = szDest + nDestLen;
  1042. char *psz = pszStart;
  1043. while ((nDestAvail > 0) && (*szSrc != '\0'))
  1044. {
  1045. *psz++ = *szSrc++;
  1046. nDestAvail--;
  1047. }
  1048. *psz = '\0';
  1049. if (*szSrc != '\0')
  1050. {
  1051. // If we ran out of room, don't append anything. We don't want partial strings.
  1052. *pszStart = '\0';
  1053. }
  1054. return(*szSrc == '\0');
  1055. }
  1056. //-----------------------------------------------------------------------------
  1057. // Purpose: Encode the list of fully selected and partially selected faces in
  1058. // a string of the form: "4 5 12 (1 8)"
  1059. //
  1060. // This is used for multiselect editing of sidelist keyvalues.
  1061. //
  1062. // Input : pszValue - The buffer to receive the face lists encoded as a string.
  1063. // pFullFaceList - the list of faces that are considered fully in the list
  1064. // pPartialFaceList - the list of faces that are partially in the list
  1065. //-----------------------------------------------------------------------------
  1066. bool CMapWorld::FaceID_FaceIDListsToString(char *pszList, int nSize, CMapFaceIDList *pFullFaceIDList, CMapFaceIDList *pPartialFaceIDList)
  1067. {
  1068. if (pszList == NULL)
  1069. {
  1070. return(false);
  1071. }
  1072. pszList[0] = '\0';
  1073. //
  1074. // Add the fully selected faces, space delimited.
  1075. //
  1076. if (pFullFaceIDList != NULL)
  1077. {
  1078. for (int i = 0; i < pFullFaceIDList->Count(); i++)
  1079. {
  1080. int nFace = pFullFaceIDList->Element(i);
  1081. char szID[64];
  1082. itoa(nFace, szID, 10);
  1083. if (!EnsureTrailingChar(pszList, ' ', nSize) || !AppendString(pszList, szID, nSize))
  1084. {
  1085. return(false);
  1086. }
  1087. }
  1088. }
  1089. //
  1090. // Add the partially selected faces inside of parentheses.
  1091. //
  1092. if (pPartialFaceIDList != NULL)
  1093. {
  1094. if (pPartialFaceIDList->Count() > 0)
  1095. {
  1096. if (!EnsureTrailingChar(pszList, ' ', nSize) || !AppendString(pszList, "(", nSize))
  1097. {
  1098. return(false);
  1099. }
  1100. bool bFirst = true;
  1101. for (int i = 0; i < pPartialFaceIDList->Count(); i++)
  1102. {
  1103. int nFace = pPartialFaceIDList->Element(i);
  1104. char szID[64];
  1105. itoa(nFace, szID, 10);
  1106. if (!bFirst)
  1107. {
  1108. if (!EnsureTrailingChar(pszList, ' ', nSize))
  1109. {
  1110. return(false);
  1111. }
  1112. }
  1113. bFirst = false;
  1114. if (!AppendString(pszList, szID, nSize))
  1115. {
  1116. return(false);
  1117. }
  1118. }
  1119. AppendString(pszList, ")", nSize);
  1120. }
  1121. }
  1122. return(true);
  1123. }
  1124. //-----------------------------------------------------------------------------
  1125. // Purpose: Encode the list of fully selected and partially selected faces in
  1126. // a string of the form: "4 5 12 (1 8)"
  1127. //
  1128. // This is used for multiselect editing of sidelist keyvalues.
  1129. //
  1130. // Input : pszValue - The buffer to receive the face lists encoded as a string.
  1131. // pFullFaceList - the list of faces that are considered fully in the list
  1132. // pPartialFaceList - the list of faces that are partially in the list
  1133. //-----------------------------------------------------------------------------
  1134. bool CMapWorld::FaceID_FaceListsToString(char *pszList, int nSize, CMapFaceList *pFullFaceList, CMapFaceList *pPartialFaceList)
  1135. {
  1136. if (pszList == NULL)
  1137. {
  1138. return(false);
  1139. }
  1140. pszList[0] = '\0';
  1141. //
  1142. // Add the fully selected faces, space delimited.
  1143. //
  1144. if (pFullFaceList != NULL)
  1145. {
  1146. for (int i = 0; i < pFullFaceList->Count(); i++)
  1147. {
  1148. CMapFace *pFace = pFullFaceList->Element(i);
  1149. char szID[64];
  1150. itoa(pFace->GetFaceID(), szID, 10);
  1151. if (!EnsureTrailingChar(pszList, ' ', nSize) || !AppendString(pszList, szID, nSize))
  1152. {
  1153. return(false);
  1154. }
  1155. }
  1156. }
  1157. //
  1158. // Add the partially selected faces inside of parentheses.
  1159. //
  1160. if (pPartialFaceList != NULL)
  1161. {
  1162. if (pPartialFaceList->Count() > 0)
  1163. {
  1164. if (!EnsureTrailingChar(pszList, ' ', nSize) || !AppendString(pszList, "(", nSize))
  1165. {
  1166. return(false);
  1167. }
  1168. bool bFirst = true;
  1169. for (int i = 0; i < pPartialFaceList->Count(); i++)
  1170. {
  1171. CMapFace *pFace = pPartialFaceList->Element(i);
  1172. char szID[64];
  1173. itoa(pFace->GetFaceID(), szID, 10);
  1174. if (!bFirst)
  1175. {
  1176. if (!EnsureTrailingChar(pszList, ' ', nSize))
  1177. {
  1178. return(false);
  1179. }
  1180. }
  1181. bFirst = false;
  1182. if (!AppendString(pszList, szID, nSize))
  1183. {
  1184. return(false);
  1185. }
  1186. }
  1187. AppendString(pszList, ")", nSize);
  1188. }
  1189. }
  1190. return(true);
  1191. }
  1192. //-----------------------------------------------------------------------------
  1193. // Purpose: Decode a string of the form: "4 5 12 (1 8)" into a list of fully
  1194. // selected and a list of partially selected face IDs.
  1195. //
  1196. // This is used for multiselect editing of sidelist keyvalues.
  1197. //
  1198. // Input : pszValue - The buffer to receive the face lists encoded as a string.
  1199. // pFullFaceList - the list of faces that are considered fully in the list
  1200. // pPartialFaceList - the list of faces that are partially in the list
  1201. //-----------------------------------------------------------------------------
  1202. void CMapWorld::FaceID_StringToFaceIDLists(CMapFaceIDList *pFullFaceList, CMapFaceIDList *pPartialFaceList, const char *pszValue)
  1203. {
  1204. if (pFullFaceList != NULL)
  1205. {
  1206. pFullFaceList->RemoveAll();
  1207. }
  1208. if (pPartialFaceList != NULL)
  1209. {
  1210. pPartialFaceList->RemoveAll();
  1211. }
  1212. if (pszValue != NULL)
  1213. {
  1214. char szVal[KEYVALUE_MAX_VALUE_LENGTH];
  1215. strcpy(szVal, pszValue);
  1216. int nParens = 0;
  1217. bool bInParens = false;
  1218. char *psz = strtok(szVal, " ");
  1219. while (psz != NULL)
  1220. {
  1221. //
  1222. // Strip leading or trailing parentheses from the substring.
  1223. //
  1224. bool bFirstValid = true;
  1225. char *pszRemoveParens = psz;
  1226. while (*pszRemoveParens != '\0')
  1227. {
  1228. if (*pszRemoveParens == '(')
  1229. {
  1230. nParens++;
  1231. *pszRemoveParens = '\0';
  1232. }
  1233. else if (*pszRemoveParens == ')')
  1234. {
  1235. nParens--;
  1236. *pszRemoveParens = '\0';
  1237. }
  1238. else if (bFirstValid)
  1239. {
  1240. //
  1241. // Note the parentheses depth at the start of this number.
  1242. //
  1243. if (nParens > 0)
  1244. {
  1245. bInParens = true;
  1246. }
  1247. else
  1248. {
  1249. bInParens = false;
  1250. }
  1251. psz = pszRemoveParens;
  1252. bFirstValid = false;
  1253. }
  1254. pszRemoveParens++;
  1255. }
  1256. //
  1257. // The substring should now be a single face ID. Get the corresponding
  1258. // face and add it to the list.
  1259. //
  1260. int nFaceID = atoi(psz);
  1261. if (bInParens)
  1262. {
  1263. if (pPartialFaceList != NULL)
  1264. {
  1265. pPartialFaceList->AddToTail(nFaceID);
  1266. }
  1267. }
  1268. else
  1269. {
  1270. if (pFullFaceList != NULL)
  1271. {
  1272. pFullFaceList->AddToTail(nFaceID);
  1273. }
  1274. }
  1275. //
  1276. // Get the next substring.
  1277. //
  1278. psz = strtok(NULL, " ");
  1279. }
  1280. }
  1281. }
  1282. //-----------------------------------------------------------------------------
  1283. // Purpose: Decode a string of the form: "4 5 12 (1 8)" into a list of fully
  1284. // selected and a list of partially selected faces.
  1285. //
  1286. // This is used for multiselect editing of sidelist keyvalues.
  1287. //
  1288. // Input : pszValue - The buffer to receive the face lists encoded as a string.
  1289. // pFullFaceList - the list of faces that are considered fully in the list
  1290. // pPartialFaceList - the list of faces that are partially in the list
  1291. //-----------------------------------------------------------------------------
  1292. void CMapWorld::FaceID_StringToFaceLists(CMapFaceList *pFullFaceList, CMapFaceList *pPartialFaceList, const char *pszValue)
  1293. {
  1294. CMapFaceIDList FullFaceIDList;
  1295. CMapFaceIDList PartialFaceIDList;
  1296. FaceID_StringToFaceIDLists(&FullFaceIDList, &PartialFaceIDList, pszValue);
  1297. if (pFullFaceList != NULL)
  1298. {
  1299. pFullFaceList->RemoveAll();
  1300. for (int i = 0; i < FullFaceIDList.Count(); i++)
  1301. {
  1302. //
  1303. // Get the corresponding face and add it to the list.
  1304. //
  1305. // FACEID TODO: fix so we only interate the world objects once
  1306. CMapFace *pFace = FaceID_FaceForID(FullFaceIDList.Element(i));
  1307. if (pFace != NULL)
  1308. {
  1309. pFullFaceList->AddToTail(pFace);
  1310. }
  1311. }
  1312. }
  1313. if (pPartialFaceList != NULL)
  1314. {
  1315. pPartialFaceList->RemoveAll();
  1316. for (int i = 0; i < PartialFaceIDList.Count(); i++)
  1317. {
  1318. //
  1319. // Get the corresponding face and add it to the list.
  1320. //
  1321. // FACEID TODO: fix so we only interate the world objects once
  1322. CMapFace *pFace = FaceID_FaceForID(PartialFaceIDList.Element(i));
  1323. if (pFace != NULL)
  1324. {
  1325. pPartialFaceList->AddToTail(pFace);
  1326. }
  1327. }
  1328. }
  1329. }
  1330. //-----------------------------------------------------------------------------
  1331. // Purpose: increments the numerals at the end of a string
  1332. // appends 0 if no numerals exist
  1333. // Input : newName -
  1334. //-----------------------------------------------------------------------------
  1335. static void IncrementStringName( char *str, int nMaxLength )
  1336. {
  1337. // walk backwards through the string looking for where the digits stop
  1338. int orgLen = Q_strlen(str);
  1339. int pos = orgLen;
  1340. while ( (pos > 0) && V_isdigit(str[pos-1]) )
  1341. {
  1342. pos--;
  1343. }
  1344. // if no digits found, append a "1"
  1345. if ( pos == orgLen )
  1346. {
  1347. Q_strncat( str, "1", nMaxLength );
  1348. }
  1349. else
  1350. {
  1351. // get the number
  1352. int iNum = Q_atoi( str+pos );
  1353. // increment the number
  1354. iNum++;
  1355. // cut off old number
  1356. str[pos]=0;
  1357. // add the new number to the string
  1358. Q_snprintf( str, nMaxLength, "%s%d", str, iNum );
  1359. }
  1360. }
  1361. //-----------------------------------------------------------------------------
  1362. // Purpose: Generates a new, unique targetname for the given entity based on an
  1363. // existing entity name.
  1364. // a static function
  1365. // Input : pObject - the entity
  1366. // startName - the name of the original entity - assumed to already exist in the map
  1367. // outputName - the new name based on the original name, guaranteed to be unique
  1368. // szPrefix - a string to prepend to the new name
  1369. // newNameBufferSize -
  1370. // bMakeUnique - if true, the generated name will be unique in this world and pRoot
  1371. // szPrefix - prefix to prepend to the new name
  1372. // pRoot - an optional tree of objects to look in for uniqueness
  1373. //-----------------------------------------------------------------------------
  1374. bool CMapWorld::GenerateNewTargetname( const char *startName, char *outputName, int newNameBufferSize, bool bMakeUnique, const char *szPrefix, CMapClass *pRoot )
  1375. {
  1376. outputName[0] = 0;
  1377. if ( szPrefix )
  1378. {
  1379. // add prefix if any give
  1380. Q_strncpy( outputName, szPrefix, newNameBufferSize );
  1381. }
  1382. // add start name
  1383. Q_strncat( outputName, startName, newNameBufferSize );
  1384. // if new name is still empty, set entity as default
  1385. if ( Q_strlen( outputName ) == 0 )
  1386. {
  1387. Q_strncpy( outputName, "entity", newNameBufferSize );
  1388. }
  1389. // Only append numbers to the name if we need to. It's possible that adding
  1390. // the prefix was sufficient to make the name unique.
  1391. if ( bMakeUnique && FindEntityByName( outputName, false, true ) )
  1392. {
  1393. // try to find entities that match the name
  1394. CMapEntity *pEnt = NULL;
  1395. do
  1396. {
  1397. // increment the entity name
  1398. IncrementStringName( outputName, newNameBufferSize );
  1399. pEnt = FindEntityByName( outputName, false, true );
  1400. if ( !pEnt && pRoot )
  1401. {
  1402. pEnt = pRoot->FindChildByKeyValue( "targetname", outputName );
  1403. }
  1404. } while ( pEnt );
  1405. }
  1406. return true;
  1407. }
  1408. void CMapWorld::PostloadVisGroups()
  1409. {
  1410. bool bFoundOrphans = false;
  1411. CMapObjectList orphans;
  1412. CMapDoc *pDoc = CMapDoc::GetActiveMapDoc();
  1413. FOR_EACH_OBJ( m_Children, pos )
  1414. {
  1415. CMapClass *pChild = m_Children[pos];
  1416. if ( pChild->PostloadVisGroups( true ) == true )
  1417. {
  1418. orphans.AddToTail( pChild );
  1419. bFoundOrphans = true;
  1420. }
  1421. }
  1422. if ( bFoundOrphans == true )
  1423. {
  1424. pDoc->VisGroups_CreateNamedVisGroup( orphans, "_orphaned hidden", true, false );
  1425. GetMainWnd()->MessageBox( "Orphaned objects were found and placed into the \"_orphaned hidden\" visgroup.", "Orphaned Objects Found", MB_OK | MB_ICONEXCLAMATION);
  1426. }
  1427. // Link up all the connections to the entities
  1428. const CMapEntityList *pEntities = EntityList_GetList();
  1429. FOR_EACH_OBJ( *pEntities, pos )
  1430. {
  1431. CMapEntity *pEntity = dynamic_cast< CMapEntity *>( (*pEntities)[pos] );
  1432. #if defined(_DEBUG) && 0
  1433. LPCTSTR pszTargetName = pEntity->GetKeyValue("targetname");
  1434. if ( pszTargetName && !strcmp(pszTargetName, "relay_cancelVCDs") )
  1435. {
  1436. // Set breakpoint here for debugging this entity's visiblity
  1437. int foo = 0;
  1438. }
  1439. #endif
  1440. int nConnections = pEntity->Connections_GetCount();
  1441. for ( int pos2 = 0; pos2 < nConnections; pos2++ )
  1442. {
  1443. CEntityConnection *pEntityConnection = pEntity->Connections_Get(pos2);
  1444. // Link this connection back to the entity
  1445. pEntityConnection->GetSourceEntityList()->AddToTail( pEntity );
  1446. pEntityConnection->LinkTargetEntities();
  1447. }
  1448. }
  1449. }
  1450. //-----------------------------------------------------------------------------
  1451. //-----------------------------------------------------------------------------
  1452. CMapEntity *CMapWorld::FindEntityByName( const char *pszName, bool bVisiblesOnly, bool bSearchInstanceParms )
  1453. {
  1454. if ( !pszName )
  1455. return NULL;
  1456. CMapEntityList *pList = &m_EntityList;
  1457. if ( !strchr( pszName, '*' ) )
  1458. {
  1459. int nBucket = EntityBucketForName( pszName );
  1460. pList = &m_EntityListByName[nBucket];
  1461. }
  1462. int nCount = pList->Count();
  1463. for ( int i = 0; i < nCount; i++ )
  1464. {
  1465. CMapEntity *pEntity = pList->Element( i );
  1466. if ( pEntity->IsVisible() || !bVisiblesOnly )
  1467. {
  1468. if ( pEntity->NameMatches( pszName ) )
  1469. {
  1470. return pEntity;
  1471. }
  1472. }
  1473. }
  1474. if ( bSearchInstanceParms == true )
  1475. {
  1476. const CMapEntityList *pEntities = EntityList_GetList();
  1477. FOR_EACH_OBJ( *pEntities, pos )
  1478. {
  1479. CMapEntity *pEntity = dynamic_cast< CMapEntity *>( (*pEntities)[pos] );
  1480. if ( pEntity->ClassNameMatches( "func_instance" ) == true )
  1481. {
  1482. for ( int j = pEntity->GetFirstKeyValue(); j != pEntity->GetInvalidKeyValue(); j = pEntity->GetNextKeyValue( j ) )
  1483. {
  1484. LPCTSTR pInstanceKey = pEntity->GetKey( j );
  1485. LPCTSTR pInstanceValue = pEntity->GetKeyValue( j );
  1486. if ( strnicmp( pInstanceKey, "replace", strlen( "replace" ) ) == 0 )
  1487. {
  1488. const char *InstancePos = strchr( pInstanceValue, ' ' );
  1489. if ( InstancePos == NULL )
  1490. {
  1491. continue;
  1492. }
  1493. if ( strcmpi( pszName, InstancePos + 1 ) == 0 )
  1494. {
  1495. return pEntity;
  1496. }
  1497. }
  1498. }
  1499. }
  1500. }
  1501. }
  1502. return NULL;
  1503. }
  1504. //-----------------------------------------------------------------------------
  1505. // Purpose: Finds all entities in the map with a given class name.
  1506. // Input : pFound - List of entities with the class name.
  1507. // pszClassName - Class name to match, case insensitive.
  1508. // Output : Returns true if any matches were found, false if not.
  1509. //-----------------------------------------------------------------------------
  1510. bool CMapWorld::FindEntitiesByClassName(CMapEntityList &Found, const char *pszClassName, bool bVisiblesOnly)
  1511. {
  1512. Found.RemoveAll();
  1513. int nCount = EntityList_GetCount();
  1514. for ( int i = 0; i < nCount; i++ )
  1515. {
  1516. CMapEntity *pEntity = EntityList_GetEntity( i );
  1517. if ( pEntity->IsVisible() || !bVisiblesOnly )
  1518. {
  1519. if ( pEntity->ClassNameMatches( pszClassName ) )
  1520. {
  1521. Found.AddToTail( pEntity );
  1522. }
  1523. }
  1524. }
  1525. return( Found.Count() != 0 );
  1526. }
  1527. //-----------------------------------------------------------------------------
  1528. // Purpose:
  1529. // Input : pFound -
  1530. // pszTargetName -
  1531. // Output : Returns true on success, false on failure.
  1532. //-----------------------------------------------------------------------------
  1533. bool CMapWorld::FindEntitiesByKeyValue(CMapEntityList &Found, const char *pszKey, const char *pszValue, bool bVisiblesOnly)
  1534. {
  1535. Found.RemoveAll();
  1536. int nCount = EntityList_GetCount();
  1537. for ( int i = 0; i < nCount; i++ )
  1538. {
  1539. CMapEntity *pEntity = EntityList_GetEntity( i );
  1540. if ( pEntity->IsVisible() || !bVisiblesOnly )
  1541. {
  1542. const char *pszThisValue = pEntity->GetKeyValue( pszKey );
  1543. if ( pszThisValue != NULL )
  1544. {
  1545. if (( pszValue != NULL ) && ( !stricmp( pszValue, pszThisValue )))
  1546. {
  1547. Found.AddToTail( pEntity );
  1548. }
  1549. }
  1550. else if (pszValue == NULL)
  1551. {
  1552. Found.AddToTail( pEntity );
  1553. }
  1554. }
  1555. }
  1556. return( Found.Count() != 0 );
  1557. }
  1558. //-----------------------------------------------------------------------------
  1559. // Purpose:
  1560. // Output : Returns true on success, false on failure.
  1561. //-----------------------------------------------------------------------------
  1562. bool CMapWorld::FindEntitiesByName( CMapEntityList &Found, const char *pszName, bool bVisiblesOnly )
  1563. {
  1564. Found.RemoveAll();
  1565. if ( !pszName )
  1566. return false;
  1567. CMapEntityList *pList = &m_EntityList;
  1568. if ( !strchr( pszName, '*' ) )
  1569. {
  1570. int nBucket = EntityBucketForName( pszName );
  1571. pList = &m_EntityListByName[nBucket];
  1572. }
  1573. int nCount = pList->Count();
  1574. for ( int i = 0; i < nCount; i++ )
  1575. {
  1576. CMapEntity *pEntity = pList->Element( i );
  1577. if ( pEntity->IsVisible() || !bVisiblesOnly )
  1578. {
  1579. if ( pEntity->NameMatches( pszName ) )
  1580. {
  1581. Found.AddToTail( pEntity );
  1582. }
  1583. }
  1584. }
  1585. return( Found.Count() != 0 );
  1586. }
  1587. //-----------------------------------------------------------------------------
  1588. // Purpose:
  1589. // Output : Returns true on success, false on failure.
  1590. //-----------------------------------------------------------------------------
  1591. bool CMapWorld::FindEntitiesByNameOrClassName(CMapEntityList &Found, const char *pszName, bool bVisiblesOnly)
  1592. {
  1593. Found.RemoveAll();
  1594. int nCount = EntityList_GetCount();
  1595. for ( int i = 0; i < nCount; i++ )
  1596. {
  1597. CMapEntity *pEntity = EntityList_GetEntity( i );
  1598. if ( pEntity->IsVisible() || !bVisiblesOnly )
  1599. {
  1600. if ( pEntity->NameMatches( pszName ) || pEntity->ClassNameMatches( pszName ) )
  1601. {
  1602. Found.AddToTail( pEntity );
  1603. }
  1604. }
  1605. }
  1606. return( Found.Count() != 0 );
  1607. }
  1608. //-----------------------------------------------------------------------------
  1609. // Tell all our children to update their dependencies because of the given object.
  1610. //-----------------------------------------------------------------------------
  1611. void CMapWorld::UpdateAllDependencies( CMapClass *pObject )
  1612. {
  1613. //
  1614. // Entities need to be put in their proper hash bucket if the name changed.
  1615. //
  1616. CMapEntity *pEntity = dynamic_cast<CMapEntity *>(pObject);
  1617. if ( pEntity )
  1618. {
  1619. int nNewBucket = -1;
  1620. const char *pszName = pEntity->GetKeyValue( "targetname" );
  1621. if ( pszName )
  1622. {
  1623. nNewBucket = EntityBucketForName( pszName );
  1624. }
  1625. int nIndex;
  1626. int nOldBucket = FindEntityBucket( pEntity, &nIndex );
  1627. if ( nOldBucket != nNewBucket )
  1628. {
  1629. // Remove the entity from the hashed list.
  1630. if ( nOldBucket != -1 )
  1631. {
  1632. m_EntityListByName[ nOldBucket ].FastRemove( nIndex );
  1633. }
  1634. // Add the entity back to the hashed list in the proper bucket.
  1635. if ( nNewBucket != -1 )
  1636. {
  1637. m_EntityListByName[ nNewBucket ].AddToTail( pEntity );
  1638. }
  1639. }
  1640. }
  1641. }
  1642. //-----------------------------------------------------------------------------
  1643. // Purpose: Returns if this map world is editable. If it is not part of an instance
  1644. // or manifest, then it is editable. Otherwise, it lets the owning document
  1645. // determine the editing state.
  1646. //-----------------------------------------------------------------------------
  1647. bool CMapWorld::IsEditable( void )
  1648. {
  1649. if ( !m_pOwningDocument )
  1650. {
  1651. return true;
  1652. }
  1653. return m_pOwningDocument->IsEditable();
  1654. }