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.

1846 lines
51 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================//
  6. #include "stdafx.h"
  7. #include "ChunkFile.h"
  8. #include "SaveInfo.h"
  9. #include "MapClass.h"
  10. #include "MapEntity.h" // dvs: evil - base knows about the derived class
  11. #include "MapGroup.h" // dvs: evil - base knows about the derived class
  12. #include "MapWorld.h" // dvs: evil - base knows about the derived class
  13. #include "GlobalFunctions.h"
  14. #include "MapDoc.h"
  15. #include "VisGroup.h"
  16. #include "mapdefs.h"
  17. #include "tier0/minidump.h"
  18. int CMapAtom::s_nObjectIDCtr = 1;
  19. static CUtlVector<MCMSTRUCT> s_Classes;
  20. // memdbgon must be the last include file in a .cpp file!!!
  21. #include <tier0/memdbgon.h>
  22. bool CMapClass::s_bLoadingVMF = false;
  23. //-----------------------------------------------------------------------------
  24. // Purpose:
  25. // Input : Type -
  26. // pfnNew -
  27. //-----------------------------------------------------------------------------
  28. CMapClassManager::CMapClassManager(MAPCLASSTYPE Type, CMapClass *(*pfnNew)())
  29. {
  30. MCMSTRUCT mcms;
  31. mcms.Type = Type;
  32. mcms.pfnNew = pfnNew;
  33. s_Classes.AddToTail(mcms);
  34. }
  35. //-----------------------------------------------------------------------------
  36. // Purpose:
  37. //-----------------------------------------------------------------------------
  38. CMapClassManager::~CMapClassManager(void)
  39. {
  40. s_Classes.RemoveAll();
  41. }
  42. //-----------------------------------------------------------------------------
  43. // Purpose:
  44. // Input : Type -
  45. // Output : CMapClass
  46. //-----------------------------------------------------------------------------
  47. CMapClass *CMapClassManager::CreateObject(MAPCLASSTYPE Type)
  48. {
  49. unsigned uLen = strlen(Type)+1;
  50. for (int i = s_Classes.Count() - 1; i >= 0; i--)
  51. {
  52. MCMSTRUCT &mcms = s_Classes[i];
  53. if (!memcmp(mcms.Type, Type, uLen))
  54. {
  55. return (*mcms.pfnNew)();
  56. }
  57. }
  58. Assert(FALSE);
  59. return(NULL);
  60. }
  61. //-----------------------------------------------------------------------------
  62. // Purpose: Constructor. Initializes data members.
  63. //-----------------------------------------------------------------------------
  64. CMapClass::CMapClass(void)
  65. {
  66. m_pSafeObject = CSafeObject<CMapClass>::Create( this );
  67. //
  68. // The document manages the unique object IDs. Eventually all object construction
  69. // should be done through the document, eliminating the need for CMapClass to know
  70. // about CMapDoc.
  71. //
  72. CMapDoc *pDoc = CMapDoc::GetActiveMapDoc();
  73. if (pDoc != NULL)
  74. {
  75. m_nID = pDoc->GetNextMapObjectID();
  76. }
  77. else
  78. {
  79. m_nID = 0;
  80. }
  81. dwKept = 0;
  82. m_bTemporary = FALSE;
  83. m_bVisible = true;
  84. m_bVisible2D = true;
  85. m_bVisGroupShown = true;
  86. m_bVisGroupAutoShown = true;
  87. m_pColorVisGroup = NULL;
  88. r = g = b = 220;
  89. m_pParent = NULL;
  90. m_nRenderFrame = 0;
  91. m_pEditorKeys = NULL;
  92. m_Dependents.Purge();
  93. }
  94. //-----------------------------------------------------------------------------
  95. // Purpose: Destructor. Deletes all children.
  96. //-----------------------------------------------------------------------------
  97. CMapClass::~CMapClass(void)
  98. {
  99. // Delete all of our children.
  100. m_Children.PurgeAndDeleteElements();
  101. delete m_pEditorKeys;
  102. // In case any CMapDocs are pointing at us, let them know we're gone.
  103. m_pSafeObject->m_pObject = NULL;
  104. // Show a warning if anyone is left pointing at us.
  105. static bool bCheckSafeObjects = true;
  106. if ( bCheckSafeObjects && m_pSafeObject->GetRefCount() != 1 )
  107. {
  108. int ret = AfxMessageBox( "Warning: a CMapClass is being deleted but is still referenced by a CMapDoc.\n"
  109. "Please tell a programmer.\n"
  110. "Click Yes to write a minidump and continue.\n"
  111. "Click No to ignore.",
  112. MB_YESNO );
  113. if ( ret == IDYES )
  114. {
  115. WriteMiniDump();
  116. }
  117. else if ( ret == IDNO )
  118. {
  119. // Ignore it and don't get in here again.
  120. bCheckSafeObjects = false;
  121. }
  122. }
  123. }
  124. const CSmartPtr< CSafeObject< CMapClass > >& CMapClass::GetSafeObjectSmartPtr()
  125. {
  126. return m_pSafeObject;
  127. }
  128. //-----------------------------------------------------------------------------
  129. // Purpose:
  130. // Input : pDependent -
  131. //-----------------------------------------------------------------------------
  132. void CMapClass::AddDependent(CMapClass *pDependent)
  133. {
  134. //
  135. // Never add ourselves to our dependents. It creates a circular dependency
  136. // which is bad.
  137. //
  138. if (pDependent == this)
  139. return;
  140. //
  141. // Don't add the same dependent twice.
  142. //
  143. int nIndex = m_Dependents.Find(pDependent);
  144. if (nIndex != -1)
  145. return;
  146. //
  147. // Also, never add one of our ancestors as a dependent. This too creates a
  148. // nasty circular dependency.
  149. //
  150. bool bIsOurAncestor = false;
  151. CMapClass *pTestParent = GetParent();
  152. while (pTestParent != NULL)
  153. {
  154. if (pTestParent == pDependent)
  155. {
  156. bIsOurAncestor = true;
  157. break;
  158. }
  159. pTestParent = pTestParent->GetParent();
  160. }
  161. if (!bIsOurAncestor)
  162. {
  163. m_Dependents.AddToTail(pDependent);
  164. Assert(m_Dependents.Count() < 1000);
  165. }
  166. }
  167. //-----------------------------------------------------------------------------
  168. // Purpose: Returns a copy of this object. We should never call this implementation
  169. // since CMapClass cannot be instantiated.
  170. // Input : bUpdateDependencies - Whether to update object dependencies when copying object pointers.
  171. //-----------------------------------------------------------------------------
  172. CMapClass *CMapClass::Copy(bool bUpdateDependencies)
  173. {
  174. Assert(FALSE);
  175. return(NULL);
  176. }
  177. //-----------------------------------------------------------------------------
  178. // Purpose: Turns this object into a duplicate of the given object.
  179. // Input : pFrom - The object to replicate.
  180. // Output : Returns a pointer to this object.
  181. //-----------------------------------------------------------------------------
  182. CMapClass *CMapClass::CopyFrom(CMapClass *pFrom, bool bUpdateDependencies)
  183. {
  184. // Copy CMapPoint stuff. dvs: should be in CMapPoint implementation!
  185. m_Origin = pFrom->m_Origin;
  186. //
  187. // Copy CMapClass stuff.
  188. //
  189. int nVisGroupCount = pFrom->GetVisGroupCount();
  190. for (int nVisGroup = 0; nVisGroup < nVisGroupCount; nVisGroup++)
  191. {
  192. CVisGroup *pVisGroup = pFrom->GetVisGroup(nVisGroup);
  193. if (!pVisGroup->IsAutoVisGroup())
  194. {
  195. AddVisGroup(pVisGroup);
  196. }
  197. }
  198. //m_bVisible = pFrom->m_bVisible;
  199. //m_bVisGroupShown = pFrom->m_bVisGroupShown;
  200. m_bTemporary = pFrom->m_bTemporary;
  201. m_bVisible2D = pFrom->m_bVisible2D;
  202. m_nRenderFrame = pFrom->m_nRenderFrame;
  203. m_CullBox = pFrom->m_CullBox;
  204. m_BoundingBox = pFrom->m_BoundingBox;
  205. m_Render2DBox = pFrom->m_Render2DBox;
  206. r = pFrom->r;
  207. g = pFrom->g;
  208. b = pFrom->b;
  209. m_Dependents.RemoveAll();
  210. m_Dependents.AddVectorToTail(pFrom->m_Dependents);
  211. // dvs: should I copy m_pEditorKeys?
  212. //
  213. // Don't link to the parent if we're not updating dependencies, just copy the pointer.
  214. //
  215. if (bUpdateDependencies)
  216. {
  217. UpdateParent( pFrom->GetParent() );
  218. }
  219. else
  220. {
  221. m_pParent = pFrom->GetParent();
  222. }
  223. return(this);
  224. }
  225. //-----------------------------------------------------------------------------
  226. // Purpose: Returns the culling bbox of this object.
  227. // Input : mins - receives the minima for culling
  228. // maxs - receives the maxima for culling.
  229. //-----------------------------------------------------------------------------
  230. void CMapClass::GetCullBox(Vector &mins, Vector &maxs)
  231. {
  232. m_CullBox.GetBounds(mins, maxs);
  233. }
  234. //-----------------------------------------------------------------------------
  235. // Purpose: Initialize the cull box with the bounds of the faces.
  236. //-----------------------------------------------------------------------------
  237. void CMapClass::SetCullBoxFromFaceList( CMapFaceList *pFaces )
  238. {
  239. SetBoxFromFaceList( pFaces, m_CullBox );
  240. }
  241. //-----------------------------------------------------------------------------
  242. // Purpose: Returns the bounding bbox of this object.
  243. // Input : mins - receives the minima for culling
  244. // maxs - receives the maxima for culling.
  245. //-----------------------------------------------------------------------------
  246. void CMapClass::GetBoundingBox( Vector &mins, Vector &maxs )
  247. {
  248. m_BoundingBox.GetBounds( mins, maxs );
  249. }
  250. //-----------------------------------------------------------------------------
  251. // Purpose: Initialize the bounding box with the bounds of the faces.
  252. //-----------------------------------------------------------------------------
  253. void CMapClass::SetBoundingBoxFromFaceList( CMapFaceList *pFaces )
  254. {
  255. SetBoxFromFaceList( pFaces, m_BoundingBox );
  256. }
  257. //-----------------------------------------------------------------------------
  258. // Purpose: Initialize box with the bounds of the faces.
  259. //-----------------------------------------------------------------------------
  260. void CMapClass::SetBoxFromFaceList( CMapFaceList *pFaces, BoundBox &Box )
  261. {
  262. //
  263. // Calculate our 3D bounds.
  264. //
  265. Box.ResetBounds();
  266. for (int iFace = 0; iFace < pFaces->Count(); iFace++)
  267. {
  268. CMapFace *pFace = pFaces->Element( iFace );
  269. int nPoints = pFace->GetPointCount();
  270. for (int i = 0; i < nPoints; i++)
  271. {
  272. Vector point;
  273. pFace->GetPoint(point, i);
  274. //
  275. // Push the culling box out in all directions.
  276. // TODO: rotate the culling box based on the cone orientation
  277. //
  278. for (int nDim = 0; nDim < 3; nDim++)
  279. {
  280. Box.bmins[0] = min(Box.bmins[0], m_Origin[0] - point[nDim]);
  281. Box.bmins[1] = min(Box.bmins[1], m_Origin[1] - point[nDim]);
  282. Box.bmins[2] = min(Box.bmins[2], m_Origin[2] - point[nDim]);
  283. Box.bmaxs[0] = max(Box.bmaxs[0], m_Origin[0] + point[nDim]);
  284. Box.bmaxs[1] = max(Box.bmaxs[1], m_Origin[1] + point[nDim]);
  285. Box.bmaxs[2] = max(Box.bmaxs[2], m_Origin[2] + point[nDim]);
  286. }
  287. }
  288. }
  289. }
  290. //-----------------------------------------------------------------------------
  291. // Purpose: Returns the bbox for 2D rendering of this object.
  292. // FIXME: this can be removed if we do all our 2D rendering in this->Render2D.
  293. // Input : mins - receives the minima for culling
  294. // maxs - receives the maxima for culling.
  295. //-----------------------------------------------------------------------------
  296. void CMapClass::GetRender2DBox(Vector &mins, Vector &maxs)
  297. {
  298. m_Render2DBox.GetBounds(mins, maxs);
  299. }
  300. //-----------------------------------------------------------------------------
  301. // Purpose: Returns the number of keys that were loaded from the "editor"
  302. // section of the VMF. These keys are held until they are handled, then
  303. // the memory is freed.
  304. //-----------------------------------------------------------------------------
  305. int CMapClass::GetEditorKeyCount(void)
  306. {
  307. if (m_pEditorKeys == NULL)
  308. {
  309. return NULL;
  310. }
  311. return m_pEditorKeys->GetCount();
  312. }
  313. //-----------------------------------------------------------------------------
  314. // Purpose: Returns the key name for the given editor key index.
  315. //-----------------------------------------------------------------------------
  316. const char *CMapClass::GetEditorKey(int nIndex)
  317. {
  318. if (m_pEditorKeys == NULL)
  319. {
  320. return NULL;
  321. }
  322. return m_pEditorKeys->GetKey(nIndex);
  323. }
  324. //-----------------------------------------------------------------------------
  325. // Purpose: Returns the value for the given editor key index.
  326. //-----------------------------------------------------------------------------
  327. const char *CMapClass::GetEditorKeyValue(int nIndex)
  328. {
  329. if (m_pEditorKeys == NULL)
  330. {
  331. return NULL;
  332. }
  333. return m_pEditorKeys->GetValue(nIndex);
  334. }
  335. //-----------------------------------------------------------------------------
  336. // Purpose: Returns the value for the given editor key name.
  337. // NOTE: this is used for unique keys and will return the value for the
  338. // FIRST key with the given name.
  339. //-----------------------------------------------------------------------------
  340. const char *CMapClass::GetEditorKeyValue(const char *szKey)
  341. {
  342. if (m_pEditorKeys == NULL)
  343. {
  344. return NULL;
  345. }
  346. return m_pEditorKeys->GetValue(szKey);
  347. }
  348. //-----------------------------------------------------------------------------
  349. // Purpose: Begins a depth-first search of the map heirarchy.
  350. // Input : pos - An iterator
  351. // Output : CMapClass
  352. //-----------------------------------------------------------------------------
  353. CMapClass *CMapClass::GetFirstDescendent(EnumChildrenPos_t &pos)
  354. {
  355. pos.nDepth = 0;
  356. pos.Stack[0].pParent = this;
  357. if ( m_Children.Count() )
  358. {
  359. pos.Stack[0].pos = 0;
  360. return(GetNextDescendent(pos));
  361. }
  362. else
  363. {
  364. pos.Stack[0].pos = -1;
  365. return NULL;
  366. }
  367. }
  368. //-----------------------------------------------------------------------------
  369. // Purpose: Continues a depth-first search of the map heirarchy.
  370. // Input : &pos -
  371. // Output : CMapClass
  372. //-----------------------------------------------------------------------------
  373. CMapClass *CMapClass::GetNextDescendent(EnumChildrenPos_t &pos)
  374. {
  375. while (pos.nDepth >= 0)
  376. {
  377. while (pos.Stack[pos.nDepth].pos != -1)
  378. {
  379. //
  380. // Get the next child of the parent on top of the stack.
  381. //
  382. CMapClass *pParent = pos.Stack[pos.nDepth].pParent;
  383. CMapClass *pChild = pParent->m_Children[pos.Stack[pos.nDepth].pos];
  384. pos.Stack[pos.nDepth].pos++;
  385. if ( pos.Stack[pos.nDepth].pos == pParent->m_Children.Count() )
  386. pos.Stack[pos.nDepth].pos= -1;
  387. // If this object has children, push it onto the stack.
  388. if ( pChild->m_Children.Count() )
  389. {
  390. pos.nDepth++;
  391. if (pos.nDepth < MAX_ENUM_CHILD_DEPTH)
  392. {
  393. pos.Stack[pos.nDepth].pParent = pChild;
  394. pos.Stack[pos.nDepth].pos = 0;
  395. }
  396. else
  397. {
  398. // dvs: stack overflow!
  399. pos.nDepth--;
  400. }
  401. }
  402. //
  403. // If this object has no children, return it.
  404. //
  405. else
  406. {
  407. return(pChild);
  408. }
  409. }
  410. //
  411. // Finished with this object's children, pop the stack and return the object.
  412. //
  413. pos.nDepth--;
  414. if (pos.nDepth >= 0)
  415. {
  416. return(pos.Stack[pos.nDepth + 1].pParent);
  417. }
  418. }
  419. return(NULL);
  420. }
  421. //-----------------------------------------------------------------------------
  422. // Purpose: Returns the world object that the given object belongs to.
  423. // Input : pStart - Object to traverse up from to find the world object.
  424. //-----------------------------------------------------------------------------
  425. CMapWorld *CMapClass::GetWorldObject(CMapAtom *pStart)
  426. {
  427. CMapAtom *pObject = pStart;
  428. while (pObject != NULL)
  429. {
  430. if ( IsWorldObject( pObject ) )
  431. {
  432. return (CMapWorld*)pObject;
  433. }
  434. pObject = pObject->GetParent();
  435. }
  436. // has no world:
  437. return NULL;
  438. }
  439. BOOL CMapClass::IsChildOf(CMapAtom *pObject)
  440. {
  441. CMapAtom *pParent = m_pParent;
  442. while( pParent )
  443. {
  444. if( pParent == pObject )
  445. return TRUE;
  446. if( IsWorldObject(pParent) )
  447. return FALSE; // world object, not parent .. return false.
  448. pParent = pParent->GetParent();
  449. }
  450. return FALSE;
  451. }
  452. //-----------------------------------------------------------------------------
  453. // Purpose: Returns whether this object belongs to the given visgroup.
  454. // Input : pVisGroup -
  455. //-----------------------------------------------------------------------------
  456. int CMapClass::IsInVisGroup(CVisGroup *pVisGroup)
  457. {
  458. if (pVisGroup != NULL)
  459. {
  460. if ( m_VisGroups.Find( pVisGroup ) != -1 )
  461. {
  462. return 1;
  463. }
  464. else
  465. {
  466. return 0;
  467. }
  468. }
  469. return 0;
  470. }
  471. //-----------------------------------------------------------------------------
  472. // Purpose:
  473. // Output : Returns true if the color was specified by this call, false if not.
  474. //-----------------------------------------------------------------------------
  475. bool CMapClass::UpdateObjectColor(void)
  476. {
  477. //
  478. // The user can choose a visgroup from which to get the color from.
  479. // If one was chosen, set our color from that visgroup.
  480. //
  481. if (m_pColorVisGroup)
  482. {
  483. color32 rgbColor = m_pColorVisGroup->GetColor();
  484. SetRenderColor(rgbColor);
  485. return true;
  486. }
  487. else if (m_pParent && !IsWorldObject(m_pParent))
  488. {
  489. color32 rgbColor = m_pParent->GetRenderColor();
  490. SetRenderColor(rgbColor);
  491. return true;
  492. }
  493. return false;
  494. }
  495. //-----------------------------------------------------------------------------
  496. // Purpose: Sets the visgroup that this object gets its color from.
  497. //-----------------------------------------------------------------------------
  498. void CMapClass::SetColorVisGroup(CVisGroup *pVisGroup)
  499. {
  500. m_pColorVisGroup = pVisGroup;
  501. UpdateObjectColor();
  502. }
  503. //-----------------------------------------------------------------------------
  504. // Purpose: Adds the given visgroup to the list of visgroups that this object
  505. // belongs to.
  506. //-----------------------------------------------------------------------------
  507. void CMapClass::AddVisGroup(CVisGroup *pVisGroup)
  508. {
  509. if (m_VisGroups.Find(pVisGroup) == -1)
  510. {
  511. m_VisGroups.AddToTail(pVisGroup);
  512. }
  513. }
  514. //-----------------------------------------------------------------------------
  515. // Purpose: Removes the given visgroup from the list of visgroups that this object
  516. // belongs to.
  517. //-----------------------------------------------------------------------------
  518. void CMapClass::RemoveVisGroup(CVisGroup *pVisGroup)
  519. {
  520. int nIndex = m_VisGroups.Find(pVisGroup);
  521. if (nIndex != -1 )
  522. {
  523. m_VisGroups.FastRemove(nIndex);
  524. CheckVisibility();
  525. }
  526. }
  527. //-----------------------------------------------------------------------------
  528. // Purpose:
  529. //-----------------------------------------------------------------------------
  530. int CMapClass::GetVisGroupCount(void)
  531. {
  532. return m_VisGroups.Count();
  533. }
  534. //-----------------------------------------------------------------------------
  535. // Purpose:
  536. //-----------------------------------------------------------------------------
  537. CVisGroup *CMapClass::GetVisGroup(int nIndex)
  538. {
  539. return m_VisGroups.Element(nIndex);
  540. }
  541. //-----------------------------------------------------------------------------
  542. // Purpose:
  543. //-----------------------------------------------------------------------------
  544. void CMapClass::RemoveAllVisGroups(void)
  545. {
  546. m_VisGroups.RemoveAll();
  547. // Remove all visgroups from children as well.
  548. FOR_EACH_OBJ( m_Children, pos )
  549. {
  550. CMapClass *pChild = m_Children.Element(pos);
  551. pChild->RemoveAllVisGroups();
  552. }
  553. // Not in any visgroups; can't be hidden that way.
  554. VisGroupShow(true);
  555. }
  556. //-----------------------------------------------------------------------------
  557. // Purpose: Adds the specified child to this object.
  558. // Input : pChild - Object to add as a child of this object.
  559. //-----------------------------------------------------------------------------
  560. void CMapClass::AddChild(CMapClass *pChild)
  561. {
  562. if ( m_Children.Find(pChild) != -1 )
  563. {
  564. pChild->m_pParent = this;
  565. return;
  566. }
  567. m_Children.AddToTail(pChild);
  568. pChild->m_pParent = this;
  569. //
  570. // Update our bounds with the child's bounds.
  571. //
  572. Vector vecMins;
  573. Vector vecMaxs;
  574. pChild->GetCullBox(vecMins, vecMaxs);
  575. m_CullBox.UpdateBounds(vecMins, vecMaxs);
  576. pChild->GetBoundingBox( vecMins, vecMaxs );
  577. m_BoundingBox.UpdateBounds( vecMins, vecMaxs );
  578. pChild->GetRender2DBox(vecMins, vecMaxs);
  579. m_Render2DBox.UpdateBounds(vecMins, vecMaxs);
  580. if (m_pParent != NULL)
  581. {
  582. GetParent()->UpdateChild(this);
  583. }
  584. }
  585. //-----------------------------------------------------------------------------
  586. // Purpose: Removes all of this object's children.
  587. //-----------------------------------------------------------------------------
  588. void CMapClass::RemoveAllChildren(void)
  589. {
  590. //
  591. // Detach the children from us. They are no longer in our world heirarchy.
  592. //
  593. FOR_EACH_OBJ( m_Children, pos )
  594. {
  595. m_Children[pos]->m_pParent = NULL;
  596. }
  597. //
  598. // Remove them from our list.
  599. //
  600. m_Children.RemoveAll();
  601. }
  602. //-----------------------------------------------------------------------------
  603. // Purpose: Removes the specified child from this object.
  604. // Input : pChild - The child to remove.
  605. // bUpdateBounds - TRUE to calculate new bounds, FALSE not to.
  606. //-----------------------------------------------------------------------------
  607. void CMapClass::RemoveChild(CMapClass *pChild, bool bUpdateBounds)
  608. {
  609. int index = m_Children.Find(pChild);
  610. if (index == -1)
  611. {
  612. pChild->m_pParent = NULL;
  613. return;
  614. }
  615. m_Children.Remove(index);
  616. pChild->m_pParent = NULL;
  617. if (bUpdateBounds)
  618. {
  619. PostUpdate(Notify_Removed);
  620. }
  621. }
  622. //-----------------------------------------------------------------------------
  623. // Purpose: Copies all children of a given object as children of this object.
  624. // NOTE: The child objects are replicated, not merely added as children.
  625. // Input : pobj - The object whose children are to be copied.
  626. //-----------------------------------------------------------------------------
  627. void CMapClass::CopyChildrenFrom(CMapClass *pobj, bool bUpdateDependencies)
  628. {
  629. FOR_EACH_OBJ( pobj->m_Children, pos )
  630. {
  631. CMapClass *pChild = pobj->m_Children.Element(pos);
  632. CMapClass *pChildCopy = pChild->Copy(bUpdateDependencies);
  633. pChildCopy->CopyChildrenFrom(pChild, bUpdateDependencies);
  634. AddChild(pChildCopy);
  635. }
  636. }
  637. //-----------------------------------------------------------------------------
  638. // Purpose: Recalculate's this object's bounding boxes. CMapClass-derived classes
  639. // should call this first, then update using their local data.
  640. // Input : bFullUpdate - When set to TRUE, call CalcBounds on all children
  641. // before updating our bounds.
  642. //-----------------------------------------------------------------------------
  643. void CMapClass::CalcBounds(BOOL bFullUpdate)
  644. {
  645. if ( CMapClass::s_bLoadingVMF )
  646. return;
  647. m_CullBox.ResetBounds();
  648. m_BoundingBox.ResetBounds();
  649. m_Render2DBox.ResetBounds();
  650. FOR_EACH_OBJ( m_Children, pos )
  651. {
  652. CMapClass *pChild = m_Children.Element(pos);
  653. if (bFullUpdate)
  654. {
  655. pChild->CalcBounds(TRUE);
  656. }
  657. m_CullBox.UpdateBounds(&pChild->m_CullBox);
  658. m_BoundingBox.UpdateBounds(&pChild->m_BoundingBox);
  659. m_Render2DBox.UpdateBounds(&pChild->m_Render2DBox);
  660. }
  661. }
  662. //-----------------------------------------------------------------------------
  663. // Purpose: Sets the render color of this object and all its children.
  664. // Input : uchRed, uchGreen, uchBlue - Color components.
  665. //-----------------------------------------------------------------------------
  666. void CMapClass::SetRenderColor(color32 rgbColor)
  667. {
  668. CMapAtom::SetRenderColor(rgbColor);
  669. //
  670. // Set the render color of all our children.
  671. //
  672. FOR_EACH_OBJ( m_Children, pos )
  673. {
  674. CMapClass *pChild = m_Children.Element(pos);
  675. if (pChild != NULL)
  676. {
  677. pChild->SetRenderColor(rgbColor);
  678. }
  679. }
  680. }
  681. //-----------------------------------------------------------------------------
  682. // Purpose: Sets the render color of this object and all its children.
  683. // Input : uchRed, uchGreen, uchBlue - Color components.
  684. //-----------------------------------------------------------------------------
  685. void CMapClass::SetRenderColor(unsigned char uchRed, unsigned char uchGreen, unsigned char uchBlue)
  686. {
  687. CMapAtom::SetRenderColor(uchRed, uchGreen, uchBlue);
  688. //
  689. // Set the render color of all our children.
  690. //
  691. FOR_EACH_OBJ( m_Children, pos )
  692. {
  693. CMapClass *pChild = m_Children.Element(pos);
  694. if (pChild != NULL)
  695. {
  696. pChild->SetRenderColor(uchRed, uchGreen, uchBlue);
  697. }
  698. }
  699. }
  700. //-----------------------------------------------------------------------------
  701. // Purpose: Returns a pointer to the object that should be added to the selection
  702. // list because this object was clicked on with a given selection mode.
  703. // Input : eSelectMode -
  704. //-----------------------------------------------------------------------------
  705. CMapClass *CMapClass::PrepareSelection(SelectMode_t eSelectMode)
  706. {
  707. if ((eSelectMode == selectGroups) && (m_pParent != NULL) && !IsWorldObject(m_pParent))
  708. {
  709. return GetParent()->PrepareSelection(eSelectMode);
  710. }
  711. return this;
  712. }
  713. //-----------------------------------------------------------------------------
  714. // Purpose: Calls an enumerating function for each of our children that are of
  715. // of a given type, recursively enumerating their children also.
  716. // Input : pfn - Enumeration callback function. Called once per child.
  717. // dwParam - User data to pass into the enumerating callback.
  718. // Type - Unless NULL, only objects of the given type will be enumerated.
  719. // Output : Returns FALSE if the enumeration was terminated early, TRUE if it completed.
  720. //-----------------------------------------------------------------------------
  721. BOOL CMapClass::EnumChildren(ENUMMAPCHILDRENPROC pfn, unsigned int dwParam, MAPCLASSTYPE Type)
  722. {
  723. FOR_EACH_OBJ( m_Children, pos )
  724. {
  725. CMapClass *pChild = m_Children.Element(pos);
  726. if (!Type || pChild->IsMapClass(Type))
  727. {
  728. if(!(*pfn)(pChild, dwParam))
  729. {
  730. return FALSE;
  731. }
  732. }
  733. // enum this child's children
  734. if (!pChild->EnumChildren(pfn, dwParam, Type))
  735. {
  736. return FALSE;
  737. }
  738. }
  739. return TRUE;
  740. }
  741. //-----------------------------------------------------------------------------
  742. // Purpose: Enumerates a this object's children, only recursing into groups.
  743. // Children of entities will not be enumerated.
  744. // Input : pfn - Enumeration callback function. Called once per child.
  745. // dwParam - User data to pass into the enumerating callback.
  746. // Type - Unless NULL, only objects of the given type will be enumerated.
  747. // Output : Returns FALSE if the enumeration was terminated early, TRUE if it completed.
  748. //-----------------------------------------------------------------------------
  749. BOOL CMapClass::EnumChildrenRecurseGroupsOnly(ENUMMAPCHILDRENPROC pfn, unsigned int dwParam, MAPCLASSTYPE Type)
  750. {
  751. FOR_EACH_OBJ( m_Children, pos )
  752. {
  753. CMapClass *pChild = m_Children.Element(pos);
  754. if (!Type || pChild->IsMapClass(Type))
  755. {
  756. if (!(*pfn)(pChild, dwParam))
  757. {
  758. return FALSE;
  759. }
  760. }
  761. if (pChild->IsGroup())
  762. {
  763. if (!pChild->EnumChildrenRecurseGroupsOnly(pfn, dwParam, Type))
  764. {
  765. return FALSE;
  766. }
  767. }
  768. }
  769. return TRUE;
  770. }
  771. //-----------------------------------------------------------------------------
  772. // Purpose: Iterates through an object, and all it's children, looking for an
  773. // entity with a matching key and value
  774. // Input : key -
  775. // value -
  776. // Output : CMapEntity - the entity found
  777. //-----------------------------------------------------------------------------
  778. CMapEntity *CMapClass::FindChildByKeyValue( const char* key, const char* value, bool *bIsInInstance, VMatrix *InstanceMatrix )
  779. {
  780. if ( !key || !value )
  781. return NULL;
  782. FOR_EACH_OBJ( m_Children, pos )
  783. {
  784. CMapClass *pChild = m_Children.Element( pos );
  785. CMapEntity *e = pChild->FindChildByKeyValue( key, value, bIsInInstance, InstanceMatrix );
  786. if ( e )
  787. return e;
  788. }
  789. return NULL;
  790. }
  791. //-----------------------------------------------------------------------------
  792. // Purpose: Called after this object is added to the world.
  793. //
  794. // NOTE: This function is NOT called during serialization. Use PostloadWorld
  795. // to do similar bookkeeping after map load.
  796. //
  797. // Input : pWorld - The world that we have been added to.
  798. //-----------------------------------------------------------------------------
  799. void CMapClass::OnAddToWorld(CMapWorld *pWorld)
  800. {
  801. //
  802. // Notify all our children.
  803. //
  804. FOR_EACH_OBJ( m_Children, pos )
  805. {
  806. CMapClass *pChild = m_Children.Element(pos);
  807. pChild->OnAddToWorld(pWorld);
  808. }
  809. }
  810. //-----------------------------------------------------------------------------
  811. // Purpose: Called to notify the object that it has just been cloned
  812. // iterates through and notifies all the children of their cloned state
  813. // NOTE: assumes that the children are in the same order in both the
  814. // original and the clone
  815. // Input : pNewObj - the clone of this object
  816. // OriginalList - The list of objects that were cloned
  817. // NewList - The parallel list of clones of objects in OriginalList
  818. //-----------------------------------------------------------------------------
  819. void CMapClass::OnClone( CMapClass *pNewObj, CMapWorld *pWorld, const CMapObjectList &OriginalList, CMapObjectList &NewList )
  820. {
  821. Assert( m_Children.Count() == pNewObj->m_Children.Count() );
  822. FOR_EACH_OBJ( m_Children, pos )
  823. {
  824. CMapClass *pChild = m_Children.Element( pos );
  825. CMapClass *pNewChild = pNewObj->m_Children.Element( pos );
  826. pChild->OnClone( pNewChild, pWorld, OriginalList, NewList );
  827. }
  828. }
  829. //-----------------------------------------------------------------------------
  830. // Purpose: Called to notify the object that it has just been cloned
  831. // iterates through and notifies all the children of their cloned state
  832. // NOTE: assumes that the children are in the same order in both the
  833. // original and the clone
  834. // Input : pNewObj - the clone of this object
  835. // OriginalList - The list of objects that were cloned
  836. // NewList - The parallel list of clones of objects in OriginalList
  837. //-----------------------------------------------------------------------------
  838. void CMapClass::OnPreClone( CMapClass *pNewObj, CMapWorld *pWorld, const CMapObjectList &OriginalList, CMapObjectList &NewList )
  839. {
  840. Assert( m_Children.Count() == pNewObj->m_Children.Count() );
  841. FOR_EACH_OBJ( m_Children, pos )
  842. {
  843. CMapClass *pChild = m_Children.Element( pos );
  844. CMapClass *pNewChild = pNewObj->m_Children.Element( pos );
  845. pChild->OnPreClone( pNewChild, pWorld, OriginalList, NewList );
  846. }
  847. }
  848. //-----------------------------------------------------------------------------
  849. // Purpose: Notifies this object that a copy of itself is about to be pasted.
  850. // Allows the object to generate new unique IDs in the copy of itself.
  851. // Input : pCopy -
  852. // pSourceWorld -
  853. // pDestWorld -
  854. // OriginalList -
  855. // NewList -
  856. //-----------------------------------------------------------------------------
  857. void CMapClass::OnPrePaste(CMapClass *pCopy, CMapWorld *pSourceWorld, CMapWorld *pDestWorld, const CMapObjectList &OriginalList, CMapObjectList &NewList)
  858. {
  859. Assert( m_Children.Count() == pCopy->m_Children.Count() );
  860. FOR_EACH_OBJ( m_Children, pos )
  861. {
  862. CMapClass *pChild = m_Children.Element(pos);
  863. CMapClass *pCopyChild = pCopy->m_Children.Element(pos);
  864. pChild->OnPrePaste(pCopyChild, pSourceWorld, pDestWorld, OriginalList, NewList);
  865. }
  866. }
  867. //-----------------------------------------------------------------------------
  868. // Purpose: Notifies this object that a copy of itself is being pasted.
  869. // Allows the object to fixup any references to other objects in the
  870. // clipboard with references to their copies.
  871. // Input : pCopy -
  872. // pSourceWorld -
  873. // pDestWorld -
  874. // OriginalList -
  875. // NewList -
  876. //-----------------------------------------------------------------------------
  877. void CMapClass::OnPaste(CMapClass *pCopy, CMapWorld *pSourceWorld, CMapWorld *pDestWorld, const CMapObjectList &OriginalList, CMapObjectList &NewList)
  878. {
  879. Assert( m_Children.Count() == pCopy->m_Children.Count() );
  880. FOR_EACH_OBJ( m_Children, pos )
  881. {
  882. CMapClass *pChild = m_Children.Element(pos);
  883. CMapClass *pCopyChild = pCopy->m_Children.Element(pos);
  884. pChild->OnPaste(pCopyChild, pSourceWorld, pDestWorld, OriginalList, NewList);
  885. }
  886. }
  887. //-----------------------------------------------------------------------------
  888. // Purpose: Called just after this object has been removed from the world so
  889. // that it can unlink itself from other objects in the world.
  890. // Input : pWorld - The world that we were just removed from.
  891. // bNotifyChildren - Whether we should forward notification to our children.
  892. //-----------------------------------------------------------------------------
  893. void CMapClass::OnRemoveFromWorld(CMapWorld *pWorld, bool bNotifyChildren)
  894. {
  895. //
  896. // Since we are being removed from the world, we cannot have any dependents.
  897. // Notify any dependent objects, so they can release pointers to us.
  898. // Our dependencies will be regenerated if we are added back into the world.
  899. //
  900. NotifyDependents(Notify_Removed);
  901. m_Dependents.RemoveAll();
  902. if (bNotifyChildren)
  903. {
  904. FOR_EACH_OBJ( m_Children, pos )
  905. {
  906. CMapClass *pChild = m_Children.Element(pos);
  907. pChild->OnRemoveFromWorld(pWorld, true);
  908. }
  909. }
  910. }
  911. //-----------------------------------------------------------------------------
  912. // Purpose: Called after a map file has been completely loaded.
  913. // Input : pWorld - The world that we are in.
  914. //-----------------------------------------------------------------------------
  915. void CMapClass::PostloadWorld(CMapWorld *pWorld)
  916. {
  917. FOR_EACH_OBJ( m_Children, pos )
  918. {
  919. CMapClass *pChild = m_Children.Element(pos);
  920. pChild->PostloadWorld(pWorld);
  921. }
  922. }
  923. //-----------------------------------------------------------------------------
  924. // Purpose: Called after all visgroups have been completely loaded. Checks for
  925. // objects hidden but without a visgroup.
  926. // Input : void
  927. //-----------------------------------------------------------------------------
  928. bool CMapClass::PostloadVisGroups( bool bLoading )
  929. {
  930. FOR_EACH_OBJ( m_Children, pos )
  931. {
  932. CMapClass *pChild = m_Children.Element(pos);
  933. pChild->PostloadVisGroups( bLoading);
  934. }
  935. return CheckVisibility( bLoading );
  936. }
  937. //-----------------------------------------------------------------------------
  938. // Purpose: Calls RenderPreload for each of our children. This allows them to
  939. // cache any resources that they need for rendering.
  940. // Input : pRender - Pointer to the 3D renderer.
  941. //-----------------------------------------------------------------------------
  942. bool CMapClass::RenderPreload(CRender3D *pRender, bool bNewContext)
  943. {
  944. FOR_EACH_OBJ( m_Children, pos )
  945. {
  946. CMapClass *pChild = m_Children.Element(pos);
  947. pChild->RenderPreload(pRender, bNewContext);
  948. }
  949. return(true);
  950. }
  951. //-----------------------------------------------------------------------------
  952. // Purpose:
  953. // Input : *pRender -
  954. //-----------------------------------------------------------------------------
  955. void CMapClass::Render2D(CRender2D *pRender)
  956. {
  957. // This is not needed because the recursion is performed in CMapView2D::Render
  958. // POSITION pos = Children.GetHeadPosition();
  959. // while (pos != NULL)
  960. // {
  961. // CMapClass *pChild = Children.GetNext(pos);
  962. // if (pChild->IsVisible() && pChild->IsVisible2D())
  963. // {
  964. // pChild->Render2D(pRender);
  965. // }
  966. // }
  967. }
  968. //-----------------------------------------------------------------------------
  969. // Purpose:
  970. // Input : pRender -
  971. //-----------------------------------------------------------------------------
  972. void CMapClass::Render3D(CRender3D *pRender)
  973. {
  974. }
  975. //-----------------------------------------------------------------------------
  976. // Purpose: Transforms all children. Derived implementations should call this,
  977. // then do their own thing.
  978. // Input : t - Pointer to class containing transformation information.
  979. //-----------------------------------------------------------------------------
  980. void CMapClass::DoTransform(const VMatrix &matrix)
  981. {
  982. CMapPoint::DoTransform(matrix);
  983. FOR_EACH_OBJ( m_Children, pos )
  984. {
  985. CMapClass *pChild = m_Children.Element(pos);
  986. pChild->Transform( matrix );
  987. }
  988. }
  989. //-----------------------------------------------------------------------------
  990. // Default logical box
  991. //-----------------------------------------------------------------------------
  992. void CMapClass::GetRenderLogicalBox( Vector2D &mins, Vector2D &maxs )
  993. {
  994. mins.Init( COORD_NOTINIT, COORD_NOTINIT );
  995. maxs.Init( COORD_NOTINIT, COORD_NOTINIT );
  996. }
  997. const Vector2D& CMapClass::GetLogicalPosition( )
  998. {
  999. static Vector2D pos( COORD_NOTINIT, COORD_NOTINIT );
  1000. return pos;
  1001. }
  1002. //-----------------------------------------------------------------------------
  1003. // Purpose:
  1004. //-----------------------------------------------------------------------------
  1005. size_t CMapClass::GetSize(void)
  1006. {
  1007. return(sizeof(*this));
  1008. }
  1009. //-----------------------------------------------------------------------------
  1010. // Purpose:
  1011. //-----------------------------------------------------------------------------
  1012. bool CMapClass::HitTest2D(CMapView2D *pView, const Vector2D &point, HitInfo_t &HitData)
  1013. {
  1014. HitData.pObject = NULL;
  1015. HitData.nDepth = g_MAX_MAP_COORD*3;
  1016. HitData.uData = 0;
  1017. bool bFoundHit = false;
  1018. if ( !IsVisible() )
  1019. return false;
  1020. FOR_EACH_OBJ( m_Children, pos )
  1021. {
  1022. CMapClass *pChild = m_Children.Element(pos);
  1023. HitInfo_t testHitData;
  1024. if ( pChild->HitTest2D(pView, point, testHitData) )
  1025. {
  1026. Assert( testHitData.pObject != NULL );
  1027. if ( testHitData.nDepth < HitData.nDepth )
  1028. {
  1029. HitData = testHitData;
  1030. bFoundHit = true;
  1031. }
  1032. }
  1033. }
  1034. return bFoundHit;
  1035. }
  1036. //-----------------------------------------------------------------------------
  1037. // Purpose:
  1038. //-----------------------------------------------------------------------------
  1039. bool CMapClass::HitTestLogical(CMapViewLogical *pView, const Vector2D &point, HitInfo_t &hitData)
  1040. {
  1041. if ( !IsVisibleLogical() )
  1042. return false;
  1043. FOR_EACH_OBJ( m_Children, pos )
  1044. {
  1045. CMapClass *pChild = m_Children.Element(pos);
  1046. if ( pChild->HitTestLogical(pView, point, hitData) )
  1047. return true;
  1048. }
  1049. return false;
  1050. }
  1051. //-----------------------------------------------------------------------------
  1052. // Purpose: Sets the selection state of this object's children.
  1053. // Input : eSelectionState -
  1054. //-----------------------------------------------------------------------------
  1055. SelectionState_t CMapClass::SetSelectionState(SelectionState_t eSelectionState)
  1056. {
  1057. FOR_EACH_OBJ( m_Children, pos )
  1058. {
  1059. CMapAtom *pObject = m_Children.Element(pos);
  1060. pObject->SetSelectionState(eSelectionState);
  1061. }
  1062. return CMapAtom::SetSelectionState(eSelectionState);
  1063. }
  1064. //-----------------------------------------------------------------------------
  1065. // Purpose: Our child's bounding box has changed - notify our parent. The real
  1066. // work will be done in CMapWorld::UpdateChild.
  1067. // Input : pChild - The child whose bounding box changed.
  1068. //-----------------------------------------------------------------------------
  1069. void CMapClass::UpdateChild(CMapClass *pChild)
  1070. {
  1071. if (m_pParent != NULL)
  1072. {
  1073. GetParent()->UpdateChild(this);
  1074. }
  1075. }
  1076. //-----------------------------------------------------------------------------
  1077. // Purpose: Returns a coordinate frame to render in
  1078. // Input : matrix -
  1079. // Output : returns true if a new matrix is returned, false if it is invalid
  1080. //-----------------------------------------------------------------------------
  1081. bool CMapClass::GetTransformMatrix( VMatrix& matrix )
  1082. {
  1083. // try and get our parents transform matrix
  1084. CMapClass *p = CMapClass::GetParent();
  1085. if ( p )
  1086. {
  1087. return p->GetTransformMatrix( matrix );
  1088. }
  1089. return false;
  1090. }
  1091. //-----------------------------------------------------------------------------
  1092. // Purpose:
  1093. // Input : pLoadInfo -
  1094. // pWorld -
  1095. // Output :
  1096. //-----------------------------------------------------------------------------
  1097. ChunkFileResult_t CMapClass::LoadEditorCallback(CChunkFile *pFile, CMapClass *pObject)
  1098. {
  1099. return(pFile->ReadChunk((KeyHandler_t)LoadEditorKeyCallback, pObject));
  1100. }
  1101. //-----------------------------------------------------------------------------
  1102. // Purpose: Handles keyvalues when loading the editor chunk of an object from the
  1103. // MAP file. Keys are transferred to a special keyvalue list for use after
  1104. // the entire map has been loaded.
  1105. // Input : szKey - Key to handle.
  1106. // szValue - Value of key.
  1107. // pObject - Object being loaded.
  1108. // Output : Returns ChunkFile_Ok.
  1109. //-----------------------------------------------------------------------------
  1110. ChunkFileResult_t CMapClass::LoadEditorKeyCallback(const char *szKey, const char *szValue, CMapClass *pObject)
  1111. {
  1112. if (!stricmp(szKey, "color"))
  1113. {
  1114. CChunkFile::ReadKeyValueColor(szValue, pObject->r, pObject->g, pObject->b);
  1115. }
  1116. else if (!stricmp(szKey, "id"))
  1117. {
  1118. CChunkFile::ReadKeyValueInt(szValue, pObject->m_nID);
  1119. }
  1120. else if (!stricmp(szKey, "comments"))
  1121. {
  1122. //
  1123. // Load the object comments.
  1124. // HACK: upcast to CEditGameClass *
  1125. //
  1126. CEditGameClass *pEdit = dynamic_cast <CEditGameClass *> (pObject);
  1127. if (pEdit != NULL)
  1128. {
  1129. pEdit->SetComments(szValue);
  1130. }
  1131. }
  1132. else if (!stricmp(szKey, "visgroupshown"))
  1133. {
  1134. CChunkFile::ReadKeyValueBool(szValue, pObject->m_bVisGroupShown);
  1135. }
  1136. else if ( !stricmp(szKey, "visgroupautoshown") )
  1137. {
  1138. CChunkFile::ReadKeyValueBool(szValue, pObject->m_bVisGroupAutoShown);
  1139. }
  1140. else
  1141. {
  1142. pObject->SetEditorKeyValue(szKey, szValue);
  1143. }
  1144. return(ChunkFile_Ok);
  1145. }
  1146. //-----------------------------------------------------------------------------
  1147. // Purpose: Call this function after changing this object via transformation,
  1148. // etc. Notifies dependents and updates the parent with this object's
  1149. // new size.
  1150. //-----------------------------------------------------------------------------
  1151. void CMapClass::PostUpdate(Notify_Dependent_t eNotifyType)
  1152. {
  1153. if (m_pParent != NULL)
  1154. {
  1155. GetParent()->UpdateChild(this);
  1156. }
  1157. else if (eNotifyType != Notify_Removed)
  1158. {
  1159. CalcBounds(TRUE);
  1160. }
  1161. NotifyDependents(eNotifyType);
  1162. }
  1163. //-----------------------------------------------------------------------------
  1164. // Purpose: Notifies all our dependents that something about us has changed,
  1165. // giving them the chance to update themselves.
  1166. //-----------------------------------------------------------------------------
  1167. void CMapClass::NotifyDependents(Notify_Dependent_t eNotifyType)
  1168. {
  1169. Assert(m_Dependents.Count() < 1000);
  1170. if (m_Dependents.Count() != 0)
  1171. {
  1172. CMapDoc *pDoc = CMapDoc::GetActiveMapDoc();
  1173. if (pDoc)
  1174. {
  1175. pDoc->NotifyDependents(this, eNotifyType);
  1176. }
  1177. }
  1178. }
  1179. //-----------------------------------------------------------------------------
  1180. // Purpose: Informs us that an object that we are dependent upon has changed,
  1181. // giving us the opportunity to update ourselves accordingly.
  1182. // Input : pObject - Object that we are dependent upon that has changed.
  1183. //-----------------------------------------------------------------------------
  1184. void CMapClass::OnNotifyDependent(CMapClass *pObject, Notify_Dependent_t eNotifyType)
  1185. {
  1186. }
  1187. //-----------------------------------------------------------------------------
  1188. // Purpose: Default implementation for saving editor-specific data. Does nothing.
  1189. // Input : pFile -
  1190. // Output : ChunkFileResult_t
  1191. //-----------------------------------------------------------------------------
  1192. ChunkFileResult_t CMapClass::SaveEditorData(CChunkFile *pFile)
  1193. {
  1194. return(ChunkFile_Ok);
  1195. }
  1196. //-----------------------------------------------------------------------------
  1197. // Purpose:
  1198. // Input : *pFile -
  1199. // Output : ChunkFileResult_t
  1200. //-----------------------------------------------------------------------------
  1201. ChunkFileResult_t CMapClass::SaveVMF(CChunkFile *pFile, CSaveInfo *pSaveInfo)
  1202. {
  1203. //
  1204. // Write the editor chunk.
  1205. //
  1206. ChunkFileResult_t eResult = pFile->BeginChunk("editor");
  1207. //
  1208. // Save the object's color.
  1209. //
  1210. if (eResult == ChunkFile_Ok)
  1211. {
  1212. eResult = pFile->WriteKeyValueColor("color", r, g, b);
  1213. }
  1214. //
  1215. // Save the group ID, if any.
  1216. //
  1217. if (eResult == ChunkFile_Ok)
  1218. {
  1219. CMapGroup *pGroup = dynamic_cast<CMapGroup *>(m_pParent);
  1220. if (pGroup != NULL)
  1221. {
  1222. eResult = pFile->WriteKeyValueInt("groupid", pGroup->GetID());
  1223. }
  1224. }
  1225. //
  1226. // Save the visgroup IDs, if any.
  1227. //
  1228. if (m_VisGroups.Count())
  1229. {
  1230. if ((eResult == ChunkFile_Ok) && m_VisGroups.Count())
  1231. {
  1232. for (int i = 0; i < m_VisGroups.Count(); i++)
  1233. {
  1234. CVisGroup *pVisGroup = m_VisGroups.Element(i);
  1235. if ( !pVisGroup->IsAutoVisGroup() )
  1236. {
  1237. eResult = pFile->WriteKeyValueInt("visgroupid", pVisGroup->GetID());
  1238. if (eResult != ChunkFile_Ok)
  1239. {
  1240. break;
  1241. }
  1242. }
  1243. }
  1244. }
  1245. }
  1246. if (eResult == ChunkFile_Ok)
  1247. {
  1248. eResult = pFile->WriteKeyValueBool("visgroupshown", m_bVisGroupShown);
  1249. }
  1250. if (eResult == ChunkFile_Ok)
  1251. {
  1252. eResult = pFile->WriteKeyValueBool("visgroupautoshown", m_bVisGroupAutoShown);
  1253. }
  1254. //
  1255. // Save the object comments, if any.
  1256. // HACK: upcast to CEditGameClass *
  1257. //
  1258. CEditGameClass *pEdit = dynamic_cast <CEditGameClass *> (this);
  1259. if (pEdit != NULL)
  1260. {
  1261. if ((eResult == ChunkFile_Ok) && (strlen(pEdit->GetComments()) > 0))
  1262. {
  1263. eResult = pFile->WriteKeyValue("comments", pEdit->GetComments());
  1264. }
  1265. }
  1266. //
  1267. // Save any other editor-specific data.
  1268. //
  1269. if (eResult == ChunkFile_Ok)
  1270. {
  1271. eResult = SaveEditorData(pFile);
  1272. }
  1273. if (eResult == ChunkFile_Ok)
  1274. {
  1275. eResult = pFile->EndChunk();
  1276. }
  1277. return(eResult);
  1278. }
  1279. //-----------------------------------------------------------------------------
  1280. // Purpose:
  1281. // Input : *pDependent -
  1282. //-----------------------------------------------------------------------------
  1283. void CMapClass::RemoveDependent(CMapClass *pDependent)
  1284. {
  1285. int nIndex = m_Dependents.Find(pDependent);
  1286. if (nIndex != -1)
  1287. {
  1288. m_Dependents.FastRemove(nIndex);
  1289. }
  1290. }
  1291. //-----------------------------------------------------------------------------
  1292. // Purpose: Frees all the keys that were loaded from the editor chunk of the MAP file.
  1293. //-----------------------------------------------------------------------------
  1294. void CMapClass::RemoveEditorKeys(void)
  1295. {
  1296. delete m_pEditorKeys;
  1297. m_pEditorKeys = NULL;
  1298. }
  1299. //-----------------------------------------------------------------------------
  1300. // Purpose:
  1301. // Input : *szOldName -
  1302. // *szNewName -
  1303. //-----------------------------------------------------------------------------
  1304. void CMapClass::ReplaceTargetname(const char *szOldName, const char *szNewName)
  1305. {
  1306. FOR_EACH_OBJ( m_Children, pos )
  1307. {
  1308. CMapClass *pObject = m_Children.Element(pos);
  1309. pObject->ReplaceTargetname(szOldName, szNewName);
  1310. }
  1311. }
  1312. //-----------------------------------------------------------------------------
  1313. // Purpose: Updates an object attachment, making this object no longer dependent
  1314. // on changes to the old object, and dependent on changes to the new object.
  1315. // Input : pOldAttached - Object that this object was attached to (possibly NULL).
  1316. // pNewAttached - New object being attached to (possibly NULL).
  1317. // Output : Returns pNewAttached.
  1318. //-----------------------------------------------------------------------------
  1319. CMapClass *CMapClass::UpdateDependency(CMapClass *pOldAttached, CMapClass *pNewAttached)
  1320. {
  1321. if (pOldAttached != pNewAttached)
  1322. {
  1323. //
  1324. // If we were attached to another object via this pointer, detach us now.
  1325. //
  1326. if (pOldAttached != NULL)
  1327. {
  1328. pOldAttached->RemoveDependent(this);
  1329. }
  1330. //
  1331. // Attach ourselves as a dependent of the other object. We will now be notified
  1332. // of any changes to that object.
  1333. //
  1334. if (pNewAttached != NULL)
  1335. {
  1336. pNewAttached->AddDependent(this);
  1337. }
  1338. }
  1339. return(pNewAttached);
  1340. }
  1341. //-----------------------------------------------------------------------------
  1342. // Purpose: Updates this object's parent, removing it from it's old parent (if any)
  1343. // attaching it to the new parent (if any).
  1344. // Input : pNewParent - A pointer to the new parent for this object.
  1345. // Output : Returns a pointer to the new parent.
  1346. //-----------------------------------------------------------------------------
  1347. void CMapClass::UpdateParent(CMapClass *pNewParent)
  1348. {
  1349. CMapClass *pOldParent = GetParent();
  1350. if (pOldParent != pNewParent)
  1351. {
  1352. if (pOldParent != NULL)
  1353. {
  1354. pOldParent->RemoveChild(this);
  1355. }
  1356. if (pNewParent != NULL)
  1357. {
  1358. pNewParent->AddChild(this);
  1359. }
  1360. m_pParent = pNewParent;
  1361. UpdateObjectColor();
  1362. }
  1363. }
  1364. //-----------------------------------------------------------------------------
  1365. // Purpose:
  1366. // Input : *szKey -
  1367. // Output : const char
  1368. //-----------------------------------------------------------------------------
  1369. void CMapClass::SetEditorKeyValue(const char *szKey, const char *szValue)
  1370. {
  1371. if (m_pEditorKeys == NULL)
  1372. {
  1373. m_pEditorKeys = new WCKeyValuesVector;
  1374. }
  1375. Assert( m_pEditorKeys != NULL );
  1376. m_pEditorKeys->AddKeyValue(szKey, szValue);
  1377. }
  1378. //-----------------------------------------------------------------------------
  1379. // Purpose: Sets the origin of this object and its children.
  1380. // FIXME: Should our children necessarily have the same origin as us?
  1381. // Seems like we should translate our children by our origin delta
  1382. //-----------------------------------------------------------------------------
  1383. void CMapClass::SetOrigin( Vector &origin )
  1384. {
  1385. CMapPoint::SetOrigin( origin );
  1386. FOR_EACH_OBJ( m_Children, pos )
  1387. {
  1388. CMapClass *pChild = m_Children.Element( pos );
  1389. pChild->SetOrigin( origin );
  1390. }
  1391. PostUpdate(Notify_Changed);
  1392. }
  1393. //-----------------------------------------------------------------------------
  1394. // Purpose:
  1395. // Input : bVisible -
  1396. //-----------------------------------------------------------------------------
  1397. void CMapClass::SetVisible(bool bVisible)
  1398. {
  1399. FOR_EACH_OBJ( m_Children, pos )
  1400. {
  1401. CMapClass *pChild = m_Children.Element(pos);
  1402. pChild->SetVisible(bVisible);
  1403. }
  1404. m_bVisible = bVisible;
  1405. }
  1406. //-----------------------------------------------------------------------------
  1407. // Purpose:
  1408. // Input : bShow -
  1409. //-----------------------------------------------------------------------------
  1410. void CMapClass::VisGroupShow(bool bShow, VisGroupSelection eVisGroup)
  1411. {
  1412. FOR_EACH_OBJ( m_Children, pos )
  1413. {
  1414. CMapClass *pChild = m_Children.Element(pos);
  1415. pChild->VisGroupShow(bShow, eVisGroup);
  1416. }
  1417. if ( eVisGroup == AUTO )
  1418. {
  1419. m_bVisGroupAutoShown = bShow;
  1420. }
  1421. if ( eVisGroup == USER )
  1422. {
  1423. //since user visgroup visibility has precedence over auto, it is possible to change an object's auto
  1424. //visibility through an action in a user visgroup.
  1425. if ( bShow )
  1426. {
  1427. m_bVisGroupAutoShown = bShow;
  1428. }
  1429. m_bVisGroupShown = bShow;
  1430. }
  1431. }
  1432. //-----------------------------------------------------------------------------
  1433. // Purpose: Causes all objects in the world to update any object dependencies (pointers)
  1434. // that they might be holding. This is a static function.
  1435. //-----------------------------------------------------------------------------
  1436. void CMapClass::UpdateAllDependencies(CMapClass *pObject)
  1437. {
  1438. //
  1439. // Try to locate the world object.
  1440. //
  1441. CMapWorld *pWorld;
  1442. if (pObject == NULL)
  1443. {
  1444. CMapDoc *pDoc = CMapDoc::GetActiveMapDoc();
  1445. if ((pDoc == NULL) || (pDoc->IsLoading()))
  1446. {
  1447. return;
  1448. }
  1449. pWorld = pDoc->GetMapWorld();
  1450. }
  1451. else
  1452. {
  1453. pWorld = pObject->GetWorldObject(pObject);
  1454. }
  1455. if (pWorld == NULL)
  1456. {
  1457. return;
  1458. }
  1459. pWorld->UpdateAllDependencies( pObject );
  1460. EnumChildrenPos_t pos;
  1461. CMapClass *pChild = pWorld->GetFirstDescendent( pos );
  1462. while ( pChild != NULL )
  1463. {
  1464. pChild->UpdateDependencies( pWorld, pObject );
  1465. pChild = pWorld->GetNextDescendent( pos );
  1466. }
  1467. }
  1468. //-----------------------------------------------------------------------------
  1469. // Purpose: Returns whether this object should be hidden based on the given
  1470. // cordon bounds.
  1471. // Output : Returns true to cull the object, false to keep it.
  1472. //-----------------------------------------------------------------------------
  1473. bool CMapClass::IsCulledByCordon(const Vector &vecMins, const Vector &vecMaxs)
  1474. {
  1475. return !IsIntersectingBox(vecMins, vecMaxs);
  1476. }
  1477. //-----------------------------------------------------------------------------
  1478. // Purpose: Checks to see if the object is hidden by auto or user visgroups
  1479. // without being assigned to one. This solves the problem of objects
  1480. // being destructively hidden by obsolete visgroups.
  1481. //-----------------------------------------------------------------------------
  1482. bool CMapClass::CheckVisibility( bool bLoading )
  1483. {
  1484. CVisGroup* pVisGroup;
  1485. bool bInUser = false;
  1486. bool bInAuto = false;
  1487. int nVisGroupCount = m_VisGroups.Count();
  1488. bool bFoundOrphans = false;
  1489. CMapDoc *pDoc = CMapDoc::GetActiveMapDoc();
  1490. for ( int i = 0; i < nVisGroupCount; i++ )
  1491. {
  1492. pVisGroup = m_VisGroups.Element( i );
  1493. if ( pVisGroup->IsAutoVisGroup() )
  1494. {
  1495. bInAuto = true;
  1496. }
  1497. else
  1498. {
  1499. bInUser = true;
  1500. }
  1501. }
  1502. if ( !bInAuto && !m_bVisGroupAutoShown )
  1503. {
  1504. VisGroupShow( true, AUTO );
  1505. }
  1506. if ( !bInUser && !m_bVisGroupShown )
  1507. {
  1508. VisGroupShow( true, USER );
  1509. if ( bLoading && pDoc->VisGroups_ObjectCanBelongToVisGroup( this ) )
  1510. {
  1511. //if this object is an orphan, we want it to be hidden but placed in a new visgroup.
  1512. bFoundOrphans = true;
  1513. VisGroupShow( false, USER );
  1514. }
  1515. }
  1516. return bFoundOrphans;
  1517. }
  1518. //-----------------------------------------------------------------------------
  1519. // Purpose: this routine will indicate if the object is editable. Generally it
  1520. // will not be editable if it is located in a separate instance or
  1521. // submap.
  1522. //-----------------------------------------------------------------------------
  1523. bool CMapClass::IsEditable( void )
  1524. {
  1525. if ( GetParent() )
  1526. {
  1527. return GetParent()->IsEditable();
  1528. }
  1529. return true;
  1530. }
  1531. //-----------------------------------------------------------------------------
  1532. // Purpose: this function will notify all children that the instance they belong to has been moved.
  1533. // it will also notify dependents of a translation. this function is currently not
  1534. // used but may be.
  1535. //-----------------------------------------------------------------------------
  1536. void CMapClass::InstanceMoved( void )
  1537. {
  1538. #if 0
  1539. FOR_EACH_OBJ( m_Children, pos )
  1540. {
  1541. CMapClass *pChild = m_Children.Element(pos);
  1542. pChild->InstanceMoved();
  1543. }
  1544. CMapWorld *pThisWorld = GetWorldObject( this );
  1545. for (int i = 0; i < m_Dependents.Count(); i++)
  1546. {
  1547. CMapClass *pDependent = m_Dependents.Element(i);
  1548. CMapWorld *pDependentWorld = GetWorldObject( pDependent );
  1549. if ( pDependentWorld != pThisWorld )
  1550. {
  1551. pDependent->OnNotifyDependent( this, Notify_Transform );
  1552. }
  1553. }
  1554. #endif
  1555. }