Counter Strike : Global Offensive Source Code
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.

2068 lines
54 KiB

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