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.

2433 lines
68 KiB

  1. //===== Copyright � 1996-2005, Valve Corporation, All rights reserved. ======//
  2. //
  3. // Purpose:
  4. //
  5. //===========================================================================//
  6. #include "stdafx.h"
  7. #include "collisionutils.h"
  8. #include "fgdlib/gdclass.h"
  9. #include "IEditorTexture.h"
  10. #include "GlobalFunctions.h"
  11. #include "hammer_mathlib.h"
  12. #include "HelperFactory.h"
  13. #include "MapAlignedBox.h"
  14. #include "MapSweptPlayerHull.h"
  15. #include "MapDefs.h"
  16. #include "MapDoc.h"
  17. #include "MapEntity.h"
  18. #include "MapAnimator.h"
  19. #include "MapSolid.h"
  20. #include "MapView2D.h" // dvs FIXME: For HitTest2D implementation
  21. #include "MapViewLogical.h"
  22. #include "MapWorld.h"
  23. #include "Options.h"
  24. #include "Render2D.h"
  25. #include "SaveInfo.h"
  26. #include "VisGroup.h"
  27. #include "MapSprite.h"
  28. #include "camera.h"
  29. #include "hammer.h"
  30. #include "vmfentitysupport.h"
  31. // memdbgon must be the last include file in a .cpp file!!!
  32. #include <tier0/memdbgon.h>
  33. IMPLEMENT_MAPCLASS(CMapEntity)
  34. #define LOGICAL_BOX_WIDTH 300
  35. #define LOGICAL_BOX_HEIGHT 300
  36. #define LOGICAL_BOX_INNER_OFFSET 10
  37. #define LOGICAL_BOX_CONNECTOR_INPUT_WIDTH 50
  38. #define LOGICAL_BOX_CONNECTOR_OUTPUT_WIDTH 50
  39. #define LOGICAL_BOX_CONNECTOR_RADIUS 10
  40. #define LOGICAL_BOX_ARROW_LENGTH 25
  41. #define LOGICAL_BOX_ARROW_HEIGHT 10
  42. class CMapAnimator;
  43. class CMapKeyFrame;
  44. bool CMapEntity::s_bShowDotACamera = false;
  45. bool CMapEntity::s_bShowEntityNames = true;
  46. bool CMapEntity::s_bShowEntityConnections = false;
  47. bool CMapEntity::s_bShowUnconnectedEntities = true;
  48. static CMapObjectList FoundEntities;
  49. //-----------------------------------------------------------------------------
  50. // Purpose:
  51. // Input : pEntity -
  52. // pKV -
  53. // Output :
  54. //-----------------------------------------------------------------------------
  55. static BOOL FindKeyValue(CMapEntity *pEntity, MDkeyvalue *pKV)
  56. {
  57. LPCTSTR pszValue = pEntity->GetKeyValue(pKV->szKey);
  58. if (!pszValue || strcmpi(pszValue, pKV->szValue))
  59. {
  60. return TRUE;
  61. }
  62. FoundEntities.AddToTail(pEntity);
  63. return TRUE;
  64. }
  65. //-----------------------------------------------------------------------------
  66. // Purpose: Compares two entity names, allowing wildcards in EITHER string.
  67. // Assumes that the wildcard character '*' marks the end of comparison,
  68. // in other words, these are identical:
  69. //
  70. // test*
  71. // test*stuff
  72. //
  73. // Input : szName1 -
  74. // szName2 -
  75. // Output : int
  76. //-----------------------------------------------------------------------------
  77. int CompareEntityNames(const char *szName1, const char *szName2)
  78. {
  79. int nCompareLen = -1;
  80. const char *pszWildcard1 = strchr(szName1, '*');
  81. if (pszWildcard1)
  82. {
  83. nCompareLen = pszWildcard1 - szName1;
  84. }
  85. const char *pszWildcard2 = strchr(szName2, '*');
  86. if (pszWildcard2)
  87. {
  88. if (nCompareLen == -1)
  89. {
  90. nCompareLen = pszWildcard2 - szName2;
  91. }
  92. else
  93. {
  94. // Wildcards in both strings -- use the shorter pattern.
  95. nCompareLen = min(nCompareLen, pszWildcard2 - szName2);
  96. }
  97. }
  98. if (nCompareLen != -1)
  99. {
  100. if (nCompareLen > 0)
  101. {
  102. return strnicmp(szName1, szName2, nCompareLen);
  103. }
  104. // One of the strings had a wildcard as the first character.
  105. return 0;
  106. }
  107. return stricmp(szName1, szName2);
  108. }
  109. //-----------------------------------------------------------------------------
  110. // Replaces references to the old node ID with references to the new node ID.
  111. //-----------------------------------------------------------------------------
  112. static void ReplaceNodeIDRecursive(CMapClass *pRoot, int nOldNodeID, int nNewNodeID)
  113. {
  114. CMapEntity *pEntity = dynamic_cast <CMapEntity *>(pRoot);
  115. if (pEntity)
  116. {
  117. GDclass *pClass = pEntity->GetClass();
  118. if (!pClass)
  119. return;
  120. int nVarCount = pClass->GetVariableCount();
  121. for (int i = 0; i < nVarCount; i++)
  122. {
  123. GDinputvariable *pVar = pClass->GetVariableAt(i);
  124. if (pVar->GetType() == ivNodeDest)
  125. {
  126. const char *pszValue = pEntity->GetKeyValue(pVar->GetName());
  127. if (pszValue && (atoi(pszValue) == nOldNodeID))
  128. {
  129. char szValue[100];
  130. itoa(nNewNodeID, szValue, 10);
  131. pEntity->SetKeyValue(pVar->GetName(), szValue);
  132. }
  133. }
  134. }
  135. }
  136. else
  137. {
  138. const CMapObjectList *pChildren = pRoot->GetChildren();
  139. FOR_EACH_OBJ( *pChildren, pos )
  140. {
  141. ReplaceNodeIDRecursive((CUtlReference< CMapClass >)pChildren->Element(pos), nOldNodeID, nNewNodeID);
  142. }
  143. }
  144. }
  145. //-----------------------------------------------------------------------------
  146. //-----------------------------------------------------------------------------
  147. static void ReplaceNodeIDRefs(CMapObjectList &newList, int nOldNodeID, int nNewNodeID)
  148. {
  149. // If they are the same, do nothing. This can happen when pasting from one
  150. // map to another map.
  151. if (nOldNodeID == nNewNodeID)
  152. return;
  153. FOR_EACH_OBJ( newList, pos )
  154. {
  155. CMapClass *pNew = newList.Element(pos);
  156. ReplaceNodeIDRecursive(pNew, nOldNodeID, nNewNodeID);
  157. }
  158. }
  159. //-----------------------------------------------------------------------------
  160. // Purpose: Constructor.
  161. //-----------------------------------------------------------------------------
  162. CMapEntity::CMapEntity(void) : flags(0)
  163. {
  164. m_pMoveParent = NULL;
  165. m_pAnimatorChild = NULL;
  166. m_vecLogicalPosition.Init( COORD_NOTINIT, COORD_NOTINIT );
  167. CalculateTypeFlags();
  168. }
  169. //-----------------------------------------------------------------------------
  170. // Purpose: Destructor.
  171. //-----------------------------------------------------------------------------
  172. CMapEntity::~CMapEntity(void)
  173. {
  174. SignalChanged();
  175. }
  176. //-----------------------------------------------------------------------------
  177. // Purpose: Adds a bounding box helper to this entity. If this entity's class
  178. // specifies a bounding box, it will be the correct size.
  179. // Input : pClass -
  180. //-----------------------------------------------------------------------------
  181. void CMapEntity::AddBoundBoxForClass(GDclass *pClass, bool bLoading)
  182. {
  183. Vector Mins;
  184. Vector Maxs;
  185. //
  186. // If we have a class and it specifies a class, use that bounding box.
  187. //
  188. if ((pClass != NULL) && (pClass->HasBoundBox()))
  189. {
  190. pClass->GetBoundBox(Mins, Maxs);
  191. }
  192. //
  193. // Otherwise, use a default bounding box.
  194. //
  195. else
  196. {
  197. VectorFill(Mins, -8);
  198. VectorFill(Maxs, 8);
  199. }
  200. //
  201. // Create the box and add it as one of our children.
  202. //
  203. CMapAlignedBox *pBox = new CMapAlignedBox(Mins, Maxs);
  204. pBox->SetOrigin(m_Origin);
  205. pBox->SetSelectionState(GetSelectionState());
  206. //
  207. // HACK: Make sure that the new child gets properly linked into the world.
  208. // This is not correct because it bypasses the doc's AddObjectToWorld code.
  209. //
  210. // Don't call AddObjectToWorld during VMF load because we don't want to call
  211. // OnAddToWorld during VMF load. We update our helpers during PostloadWorld.
  212. //
  213. CMapWorld *pWorld = (CMapWorld *)GetWorldObject(this);
  214. if ((!bLoading) && (pWorld != NULL))
  215. {
  216. pWorld->AddObjectToWorld(pBox, this);
  217. }
  218. else
  219. {
  220. AddChild(pBox);
  221. }
  222. }
  223. //-----------------------------------------------------------------------------
  224. // Purpose: Sets our child's render color to our render color.
  225. // Input : pChild - Child object being added.
  226. //-----------------------------------------------------------------------------
  227. void CMapEntity::AddChild(CMapClass *pChild)
  228. {
  229. CMapClass::AddChild(pChild);
  230. //
  231. // Notify the new child of all our keys. Don't bother for solids.
  232. //
  233. if (dynamic_cast<CMapSolid*>(pChild) == NULL)
  234. {
  235. for ( int i=GetFirstKeyValue(); i != GetInvalidKeyValue(); i=GetNextKeyValue( i ) )
  236. {
  237. MDkeyvalue KeyValue = m_KeyValues.GetKeyValue(i);
  238. pChild->OnParentKeyChanged( KeyValue.szKey, KeyValue.szValue );
  239. }
  240. }
  241. }
  242. //-----------------------------------------------------------------------------
  243. // Purpose: Adds a helper object as a child of this entity.
  244. // Input : pHelper - The helper object.
  245. // bLoading - True if this is being called from Postload, false otherwise.
  246. //-----------------------------------------------------------------------------
  247. void CMapEntity::AddHelper(CMapClass *pHelper, bool bLoading)
  248. {
  249. if (!IsPlaceholder())
  250. {
  251. //
  252. // Solid entities have no origin, so place the helper at our center.
  253. //
  254. Vector vecCenter;
  255. m_Render2DBox.GetBoundsCenter(vecCenter);
  256. pHelper->SetOrigin(vecCenter);
  257. }
  258. else
  259. {
  260. pHelper->SetOrigin(m_Origin);
  261. }
  262. pHelper->SetSelectionState(GetSelectionState());
  263. //
  264. // If we have a game data class, set the child's render color to the color
  265. // dictated by the game data class.
  266. //
  267. // Note: in AddChild, where it calls OnParentKeyChanged for everything, the color in the helper can
  268. // get set to something else based on the entity's properties (CMapLightCone does this, for example).
  269. //
  270. GDclass *pClass = GetClass();
  271. if ( pClass )
  272. {
  273. color32 rgbColor = pClass->GetColor();
  274. pHelper->SetRenderColor(rgbColor);
  275. }
  276. //
  277. // HACK: Make sure that the new child gets properly linked into the world.
  278. // This is not correct because it bypasses the doc's AddObjectToWorld code.
  279. //
  280. // Don't call AddObjectToWorld during VMF load because we don't want to call
  281. // OnAddToWorld during VMF load. We update our helpers during PostloadWorld.
  282. //
  283. CMapWorld *pWorld = (CMapWorld *)GetWorldObject(this);
  284. if ((!bLoading) && (pWorld != NULL))
  285. {
  286. pWorld->AddObjectToWorld(pHelper, this);
  287. }
  288. else
  289. {
  290. AddChild(pHelper);
  291. }
  292. //
  293. // dvs: HACK for animator children. Better for CMapEntity to have a SetAnimatorChild
  294. // function that the CMapAnimator could call. Better still, eliminate the knowledge
  295. // that CMapEntity has about its animator child.
  296. //
  297. CMapAnimator *pAnim = dynamic_cast<CMapAnimator *>(pHelper);
  298. if (pAnim != NULL)
  299. {
  300. m_pAnimatorChild = pAnim;
  301. }
  302. }
  303. //-----------------------------------------------------------------------------
  304. // Purpose: Creates all helper objects defined by the FGD and adds them as
  305. // children of this entity. Helper objects perform rendering, UI, and
  306. // bookkeeping functions for their parent entities. If the class
  307. // definition does not specify any helpers, or none of the helpers
  308. // could be added, a box helper is added so that the entity has some
  309. // visual representation.
  310. // Inputs : pClass -
  311. // bLoading - True if this is being called from Postload, false otherwise.
  312. //-----------------------------------------------------------------------------
  313. void CMapEntity::AddHelpersForClass(GDclass *pClass, bool bLoading)
  314. {
  315. bool bAddedOneVisual = false;
  316. if (((pClass != NULL) && (pClass->HasBoundBox())))
  317. {
  318. AddBoundBoxForClass(pClass, bLoading);
  319. bAddedOneVisual = true;
  320. }
  321. //
  322. // If we have a game class from the FGD, add whatever helpers are declared in that
  323. // class definition.
  324. //
  325. if (pClass != NULL)
  326. {
  327. //
  328. // Add all the helpers that this class declares in the FGD.
  329. //
  330. GDclass *pClass = GetClass();
  331. //
  332. // For every helper in the class definition...
  333. //
  334. int nHelperCount = pClass->GetHelperCount();
  335. for (int i = 0; i < nHelperCount; i++)
  336. {
  337. CHelperInfo *pHelperInfo = pClass->GetHelper(i);
  338. //
  339. // Create the helper and attach it to this entity.
  340. //
  341. CMapClass *pHelper = CHelperFactory::CreateHelper(pHelperInfo, this);
  342. if (pHelper != NULL)
  343. {
  344. AddHelper(pHelper, bLoading);
  345. if (pHelper->IsVisualElement())
  346. {
  347. bAddedOneVisual = true;
  348. }
  349. }
  350. }
  351. //
  352. // Look for keys that define helpers.
  353. //
  354. // FIXME: make this totally data driven like the helper factory, or better
  355. // yet, like the LINK_ENTITY_TO_CLASS stuff in the game DLL
  356. int nVarCount = pClass->GetVariableCount();
  357. for (int i = 0; i < nVarCount; i++)
  358. {
  359. GDinputvariable *pVar = pClass->GetVariableAt(i);
  360. GDIV_TYPE eType = pVar->GetType();
  361. CHelperInfo HelperInfo;
  362. bool bCreate = false;
  363. switch (eType)
  364. {
  365. case ivOrigin:
  366. {
  367. const char *pszKey = pVar->GetName();
  368. HelperInfo.SetName("origin");
  369. HelperInfo.AddParameter(pszKey);
  370. bCreate = true;
  371. break;
  372. }
  373. case ivVecLine:
  374. {
  375. const char *pszKey = pVar->GetName();
  376. HelperInfo.SetName("vecline");
  377. HelperInfo.AddParameter(pszKey);
  378. bCreate = true;
  379. break;
  380. }
  381. case ivAxis:
  382. {
  383. const char *pszKey = pVar->GetName();
  384. HelperInfo.SetName("axis");
  385. HelperInfo.AddParameter(pszKey);
  386. bCreate = true;
  387. break;
  388. }
  389. }
  390. //
  391. // Create the helper and attach it to this entity.
  392. //
  393. if (bCreate)
  394. {
  395. CMapClass *pHelper = CHelperFactory::CreateHelper(&HelperInfo, this);
  396. if (pHelper != NULL)
  397. {
  398. AddHelper(pHelper, bLoading);
  399. if (pHelper->IsVisualElement())
  400. {
  401. bAddedOneVisual = true;
  402. }
  403. }
  404. }
  405. }
  406. }
  407. //
  408. // Any solid children we have will also work as visual elements.
  409. //
  410. if (!IsPlaceholder())
  411. {
  412. bAddedOneVisual = true;
  413. }
  414. //
  415. // If we have no game class and we are a point entity, add an "obsolete" sprite helper
  416. // so level designers know to update the entity.
  417. //
  418. else if (pClass == NULL)
  419. {
  420. CHelperInfo HelperInfo;
  421. HelperInfo.SetName("iconsprite");
  422. HelperInfo.AddParameter("sprites/obsolete.vmt");
  423. CMapClass *pSprite = CHelperFactory::CreateHelper(&HelperInfo, this);
  424. if (pSprite != NULL)
  425. {
  426. AddHelper(pSprite, bLoading);
  427. bAddedOneVisual = true;
  428. }
  429. }
  430. //
  431. // If we still haven't added any visible helpers, we need to add a bounding box so that there
  432. // is some visual representation for this entity. We also add the bounding box if the
  433. // entity's class specifies a bounding box.
  434. //
  435. if (!bAddedOneVisual)
  436. {
  437. AddBoundBoxForClass(pClass, bLoading);
  438. }
  439. if ( !CMapClass::s_bLoadingVMF )
  440. {
  441. CalcBounds(TRUE);
  442. PostUpdate(Notify_Changed);
  443. }
  444. }
  445. //-----------------------------------------------------------------------------
  446. // Purpose: Returns a deep copy of this object.
  447. // Output : Returns a pointer to the new allocated object.
  448. //-----------------------------------------------------------------------------
  449. CMapClass *CMapEntity::Copy(bool bUpdateDependencies)
  450. {
  451. CMapEntity *pNew = new CMapEntity;
  452. pNew->CopyFrom(this, bUpdateDependencies);
  453. return pNew;
  454. }
  455. //-----------------------------------------------------------------------------
  456. // Purpose: Performs a deep copy of a given object into this object.
  457. // Input : pobj - Object to copy from.
  458. // Output : Returns a pointer to this object.
  459. //-----------------------------------------------------------------------------
  460. CMapClass *CMapEntity::CopyFrom(CMapClass *pobj, bool bUpdateDependencies)
  461. {
  462. Assert(pobj->IsMapClass(MAPCLASS_TYPE(CMapEntity)));
  463. CMapEntity *pFrom = (CMapEntity*) pobj;
  464. flags = pFrom->flags;
  465. m_Origin = pFrom->m_Origin;
  466. m_vecLogicalPosition = pFrom->m_vecLogicalPosition;
  467. CMapClass::CopyFrom(pobj, bUpdateDependencies);
  468. //
  469. // Copy our keys. If our targetname changed we must relink all targetname pointers.
  470. //
  471. const char *pszOldTargetName = CEditGameClass::GetKeyValue("targetname");
  472. char szOldTargetName[MAX_IO_NAME_LEN];
  473. if (pszOldTargetName != NULL)
  474. {
  475. strcpy(szOldTargetName, pszOldTargetName);
  476. }
  477. CEditGameClass::CopyFrom(pFrom);
  478. const char *pszNewTargetName = CEditGameClass::GetKeyValue("targetname");
  479. if ((bUpdateDependencies) && (pszNewTargetName != NULL))
  480. {
  481. if (stricmp(szOldTargetName, pszNewTargetName) != 0)
  482. {
  483. UpdateAllDependencies(this);
  484. }
  485. }
  486. CalculateTypeFlags();
  487. SignalChanged();
  488. return(this);
  489. }
  490. //-----------------------------------------------------------------------------
  491. // Purpose:
  492. // Input : bFullUpdate -
  493. //-----------------------------------------------------------------------------
  494. void CMapEntity::CalcBounds(BOOL bFullUpdate)
  495. {
  496. CMapClass::CalcBounds(bFullUpdate);
  497. //
  498. // If we are a solid entity, set our origin to our bounds center.
  499. //
  500. if (IsSolidClass())
  501. {
  502. m_Render2DBox.GetBoundsCenter(m_Origin);
  503. }
  504. }
  505. //-----------------------------------------------------------------------------
  506. // Purpose: Debugging hook.
  507. //-----------------------------------------------------------------------------
  508. #pragma warning (disable:4189)
  509. void CMapEntity::Debug(void)
  510. {
  511. int i = m_KeyValues.GetFirst();
  512. MDkeyvalue &KeyValue = m_KeyValues.GetKeyValue(i);
  513. }
  514. #pragma warning (default:4189)
  515. //-----------------------------------------------------------------------------
  516. // Purpose: If this entity has a name key, returns a string with "<name> <classname>"
  517. // in it. Otherwise returns a buffer with "<classname>" in it.
  518. // Output : String description of the entity.
  519. //-----------------------------------------------------------------------------
  520. const char* CMapEntity::GetDescription(void)
  521. {
  522. static char szBuf[128];
  523. const char *pszName = GetKeyValue("targetname");
  524. if (pszName != NULL)
  525. {
  526. sprintf(szBuf, "%s - %s", pszName, GetClassName());
  527. }
  528. else
  529. {
  530. strcpy(szBuf, GetClassName());
  531. }
  532. return(szBuf);
  533. }
  534. //-----------------------------------------------------------------------------
  535. // Purpose: Returns the color that this entity should use for rendering.
  536. //-----------------------------------------------------------------------------
  537. void CMapEntity::GetRenderColor( CRender2D *pRender, unsigned char &red, unsigned char &green, unsigned char &blue )
  538. {
  539. if ( IsSelected() )
  540. {
  541. red = GetRValue(Options.colors.clrSelection);
  542. green = GetGValue(Options.colors.clrSelection);
  543. blue = GetBValue(Options.colors.clrSelection);
  544. }
  545. else
  546. {
  547. GDclass *pClass = GetClass();
  548. if (pClass)
  549. {
  550. color32 rgbColor = pClass->GetColor();
  551. red = rgbColor.r;
  552. green = rgbColor.g;
  553. blue = rgbColor.b;
  554. }
  555. else
  556. {
  557. red = GetRValue(Options.colors.clrEntity);
  558. green = GetGValue(Options.colors.clrEntity);
  559. blue = GetBValue(Options.colors.clrEntity);
  560. }
  561. }
  562. }
  563. //-----------------------------------------------------------------------------
  564. // Purpose: Returns the color that this entity should use for rendering.
  565. //-----------------------------------------------------------------------------
  566. color32 CMapEntity::GetRenderColor( CRender2D *pRender )
  567. {
  568. color32 clr;
  569. GetRenderColor( pRender, clr.r, clr.g, clr.b );
  570. return clr;
  571. }
  572. //-----------------------------------------------------------------------------
  573. // Purpose: Returns the size of this object.
  574. // Output : Size, in bytes, of this object, not including any dynamically
  575. // allocated data members.
  576. //-----------------------------------------------------------------------------
  577. size_t CMapEntity::GetSize(void)
  578. {
  579. return(sizeof(*this));
  580. }
  581. //-----------------------------------------------------------------------------
  582. // Purpose:
  583. // Input : *pFile -
  584. // Output : ChunkFileResult_t
  585. //-----------------------------------------------------------------------------
  586. ChunkFileResult_t CMapEntity::LoadVMF(CChunkFile *pFile)
  587. {
  588. //
  589. // Set up handlers for the subchunks that we are interested in.
  590. //
  591. CChunkHandlerMap Handlers;
  592. Handlers.AddHandler("solid", (ChunkHandler_t)LoadSolidCallback, this);
  593. Handlers.AddHandler("hidden", (ChunkHandler_t)LoadHiddenCallback, this);
  594. Handlers.AddHandler("editor", (ChunkHandler_t)LoadEditorCallback, this);
  595. Handlers.AddHandler("connections", (ChunkHandler_t)LoadConnectionsCallback, (CEditGameClass *)this);
  596. VmfAddMapEntityHandlers( &Handlers, static_cast< IMapEntity_Type_t * >( this ) );
  597. pFile->PushHandlers(&Handlers);
  598. ChunkFileResult_t eResult = pFile->ReadChunk((KeyHandler_t)LoadKeyCallback, this);
  599. pFile->PopHandlers();
  600. return(eResult);
  601. }
  602. //-----------------------------------------------------------------------------
  603. // Purpose:
  604. // Input : *szKey -
  605. // *szValue -
  606. // *pEntity -
  607. // Output : ChunkFileResult_t
  608. //-----------------------------------------------------------------------------
  609. ChunkFileResult_t CMapEntity::LoadKeyCallback(const char *szKey, const char *szValue, CMapEntity *pEntity)
  610. {
  611. if (!stricmp(szKey, "id"))
  612. {
  613. pEntity->SetID(atoi(szValue));
  614. // PORTAL2 SHIP: keep track of load order to preserve it on save so that maps can be diffed.
  615. pEntity->m_nLoadID = CMapDoc::GetActiveMapDoc()->GetNextLoadID();
  616. }
  617. else
  618. {
  619. //
  620. // While loading, set key values directly rather than via SetKeyValue. This avoids
  621. // all the unnecessary bookkeeping that goes on in SetKeyValue.
  622. //
  623. pEntity->m_KeyValues.SetValue(szKey, szValue);
  624. }
  625. pEntity->CalculateTypeFlags();
  626. pEntity->SignalChanged();
  627. return(ChunkFile_Ok);
  628. }
  629. //-----------------------------------------------------------------------------
  630. // Purpose:
  631. // Input : bVisible -
  632. // Output : ChunkFileResult_t
  633. //-----------------------------------------------------------------------------
  634. ChunkFileResult_t CMapEntity::LoadHiddenCallback(CChunkFile *pFile, CMapEntity *pEntity)
  635. {
  636. //
  637. // Set up handlers for the subchunks that we are interested in.
  638. //
  639. CChunkHandlerMap Handlers;
  640. Handlers.AddHandler("solid", (ChunkHandler_t)LoadSolidCallback, pEntity);
  641. Handlers.AddHandler("editor", (ChunkHandler_t)LoadEditorCallback, pEntity);
  642. pFile->PushHandlers(&Handlers);
  643. ChunkFileResult_t eResult = pFile->ReadChunk();
  644. pFile->PopHandlers();
  645. return(eResult);
  646. }
  647. ChunkFileResult_t CMapEntity::LoadEditorKeyCallback( const char *szKey, const char *szValue, CMapEntity *pMapEntity )
  648. {
  649. if ( !stricmp( szKey, "logicalpos" ) )
  650. {
  651. CChunkFile::ReadKeyValueVector2(szValue, pMapEntity->m_vecLogicalPosition );
  652. return ChunkFile_Ok;
  653. }
  654. return CMapClass::LoadEditorKeyCallback( szKey, szValue, pMapEntity );
  655. }
  656. //-----------------------------------------------------------------------------
  657. // Purpose:
  658. //-----------------------------------------------------------------------------
  659. ChunkFileResult_t CMapEntity::LoadEditorCallback(CChunkFile *pFile, CMapEntity *pObject)
  660. {
  661. return pFile->ReadChunk( (KeyHandler_t)LoadEditorKeyCallback, pObject );
  662. }
  663. //-----------------------------------------------------------------------------
  664. // Purpose:
  665. // Input : *pFile -
  666. // *pEntity -
  667. // Output : ChunkFileResult_t
  668. //-----------------------------------------------------------------------------
  669. ChunkFileResult_t CMapEntity::LoadSolidCallback(CChunkFile *pFile, CMapEntity *pEntity)
  670. {
  671. CMapSolid *pSolid = new CMapSolid;
  672. bool bValid;
  673. ChunkFileResult_t eResult = pSolid->LoadVMF(pFile, bValid);
  674. if ((eResult == ChunkFile_Ok) && (bValid))
  675. {
  676. pEntity->AddChild(pSolid);
  677. }
  678. else
  679. {
  680. delete pSolid;
  681. }
  682. return(eResult);
  683. }
  684. //-----------------------------------------------------------------------------
  685. // Purpose: Sets this entity's origin and updates the bounding box.
  686. // Input : o - Origin to set.
  687. //-----------------------------------------------------------------------------
  688. void CMapEntity::SetOrigin(Vector& o)
  689. {
  690. Vector vecOrigin;
  691. GetOrigin(vecOrigin);
  692. if (vecOrigin == o)
  693. return;
  694. CMapClass::SetOrigin(o);
  695. // dvs: is this still necessary?
  696. if (!(flags & flagPlaceholder))
  697. {
  698. // not a placeholder.. no origin.
  699. return;
  700. }
  701. if ( !CMapClass::s_bLoadingVMF )
  702. {
  703. CalcBounds( TRUE );
  704. PostUpdate(Notify_Changed);
  705. SignalChanged();
  706. }
  707. }
  708. //-----------------------------------------------------------------------------
  709. // Purpose: Removes all of this entity's helpers.
  710. // Input : bRemoveSolidChildren - Whether to also remove any solid children. This
  711. // is true when changing from a solid entity to a point entity.
  712. //-----------------------------------------------------------------------------
  713. void CMapEntity::RemoveHelpers(bool bRemoveSolids)
  714. {
  715. for( int pos=m_Children.Count()-1; pos>=0; pos-- )
  716. {
  717. CMapClass *pChild = m_Children[pos];
  718. if (bRemoveSolids || ((dynamic_cast <CMapSolid *> (pChild)) == NULL))
  719. {
  720. m_Children.FastRemove(pos);
  721. }
  722. // LEAKLEAK: need to KeepForDestruction to avoid undo crashes, but how? where?
  723. //delete pChild;
  724. }
  725. }
  726. //-----------------------------------------------------------------------------
  727. // Building targetnames which deal with *
  728. //-----------------------------------------------------------------------------
  729. static inline void BuildNewTargetName( const char *pOldName, const char *pNewName, char *pBuffer )
  730. {
  731. strcpy(pBuffer, pNewName);
  732. // If we matched a key value that contains wildcards, preserve the
  733. // wildcards when we replace the name.
  734. //
  735. // For example, "oldname*" would become "newname*" instead of just "newname"
  736. // FIXME: ??? handle different-length names with wildcards, eg. "old_vort*" => "new_weasel*"
  737. const char *pszWildcard = strchr(pOldName, '*');
  738. if (pszWildcard)
  739. {
  740. strcpy(&pBuffer[pszWildcard - pOldName], "*");
  741. }
  742. }
  743. //-----------------------------------------------------------------------------
  744. //-----------------------------------------------------------------------------
  745. void CMapEntity::ReplaceTargetname(const char *szOldName, const char *szNewName)
  746. {
  747. // NOTE: Case-sensitive compare because people might want to replace one case with another.
  748. if ( !Q_strcmp( szOldName, szNewName ) )
  749. {
  750. // The names already match. There is nothing to do!
  751. return;
  752. }
  753. char szTempName[MAX_KEYVALUE_LEN];
  754. //
  755. // Replace any keys whose value matches the old name.
  756. //
  757. for ( int i=GetFirstKeyValue(); i != GetInvalidKeyValue(); i=GetNextKeyValue( i ) )
  758. {
  759. MDkeyvalue KeyValue = m_KeyValues.GetKeyValue(i);
  760. if (!CompareEntityNames(KeyValue.szValue, szOldName))
  761. {
  762. BuildNewTargetName( KeyValue.szValue, szNewName, szTempName );
  763. SetKeyValue( KeyValue.szKey, szTempName );
  764. }
  765. }
  766. //
  767. // Replace any connections that target the old name.
  768. //
  769. int nConnCount = Connections_GetCount();
  770. for (int i = 0; i < nConnCount; i++)
  771. {
  772. CEntityConnection *pConn = Connections_Get(i);
  773. if (!CompareEntityNames( pConn->GetTargetName(), szOldName ))
  774. {
  775. BuildNewTargetName( pConn->GetTargetName(), szNewName, szTempName );
  776. pConn->SetTargetName(szTempName);
  777. }
  778. if (!CompareEntityNames( pConn->GetSourceName(), szOldName ))
  779. {
  780. BuildNewTargetName( pConn->GetSourceName(), szNewName, szTempName );
  781. pConn->SetSourceName(szTempName);
  782. }
  783. if ( !CompareEntityNames( pConn->GetParam(), szOldName ))
  784. {
  785. BuildNewTargetName( pConn->GetParam(), szNewName, szTempName );
  786. pConn->SetParam(szTempName);
  787. }
  788. }
  789. CMapClass::ReplaceTargetname(szOldName, szNewName);
  790. }
  791. //-----------------------------------------------------------------------------
  792. // Purpose:
  793. // Inputs : pszClass -
  794. // bLoading - True if this is being called from Postload, false otherwise.
  795. //-----------------------------------------------------------------------------
  796. void CMapEntity::SetClass(LPCTSTR pszClass, bool bLoading)
  797. {
  798. Assert(pszClass);
  799. //
  800. // If we are just setting to the same class, don't do anything.
  801. //
  802. if (IsClass(pszClass))
  803. {
  804. return;
  805. }
  806. //
  807. // Copy class name & resolve GDclass pointer.
  808. //
  809. CEditGameClass::SetClass(pszClass, bLoading);
  810. UpdateObjectColor();
  811. //
  812. // If our new class is defined in the FGD, set our color and our default keys
  813. // from the class.
  814. //
  815. if (IsClass())
  816. {
  817. SetPlaceholder(!IsSolidClass());
  818. GetDefaultKeys();
  819. if (IsNodeClass() && (GetNodeID() == 0))
  820. {
  821. AssignNodeID();
  822. }
  823. }
  824. //
  825. // If not, use whether or not we have solid children to determine whether
  826. // we are a point entity or a solid entity.
  827. //
  828. else
  829. {
  830. SetPlaceholder(HasSolidChildren() ? FALSE : TRUE);
  831. }
  832. //
  833. // Add whatever helpers our class requires, or a default bounding box if
  834. // our class is unknown and we are a point entity.
  835. //
  836. UpdateHelpers(bLoading);
  837. CMapDoc *pDoc = CMapDoc::GetActiveMapDoc();
  838. if ( !pDoc->IsLoading() )
  839. {
  840. pDoc->RemoveFromAutoVisGroups( this );
  841. pDoc->AddToAutoVisGroup( this );
  842. }
  843. //
  844. // HACK: If we are now a decal, make sure we have a valid texture.
  845. //
  846. if (!strcmp(pszClass, "infodecal"))
  847. {
  848. if (!GetKeyValue("texture"))
  849. {
  850. SetKeyValue("texture", "clip");
  851. }
  852. }
  853. CalculateTypeFlags();
  854. SignalChanged();
  855. }
  856. //-----------------------------------------------------------------------------
  857. // Purpose: Assigns the next unique node ID to this entity.
  858. //-----------------------------------------------------------------------------
  859. void CMapEntity::AssignNodeID(void)
  860. {
  861. char szID[80];
  862. CMapDoc *pDoc = CMapDoc::GetActiveMapDoc();
  863. itoa(pDoc->GetNextNodeID(), szID, 10);
  864. SetKeyValue("nodeid", szID);
  865. }
  866. struct CClassNameFlagsMatcher
  867. {
  868. char const *m_pClassname;
  869. int m_nFlagsToOR;
  870. };
  871. static CClassNameFlagsMatcher s_ClassFlagsTable[]={
  872. { "light_environment", ENTITY_FLAG_IS_LIGHT },
  873. { "light", ENTITY_FLAG_IS_LIGHT },
  874. { "light_spot", ENTITY_FLAG_IS_LIGHT },
  875. { "prop_static", ENTITY_FLAG_SHOW_IN_LPREVIEW2 },
  876. { "func_instance", ENTITY_FLAG_IS_INSTANCE },
  877. };
  878. void CMapEntity::CalculateTypeFlags( void )
  879. {
  880. m_EntityTypeFlags = 0;
  881. const char *pszClassName = GetClassName();
  882. if (pszClassName != NULL)
  883. for(int i=0; i<NELEMS( s_ClassFlagsTable ); i++)
  884. if ( ! stricmp( pszClassName, s_ClassFlagsTable[i].m_pClassname ) )
  885. m_EntityTypeFlags |= s_ClassFlagsTable[i].m_nFlagsToOR;
  886. }
  887. void CMapEntity::SignalChanged( void )
  888. {
  889. if ( m_EntityTypeFlags & ENTITY_FLAG_IS_LIGHT )
  890. SignalUpdate( EVTYPE_LIGHTING_CHANGED );
  891. }
  892. //-----------------------------------------------------------------------------
  893. // Purpose:
  894. //-----------------------------------------------------------------------------
  895. void CMapEntity::EnsureUniqueNodeID(CMapWorld *pWorld)
  896. {
  897. bool bBuildNewNodeID = true;
  898. int nOurNodeID = GetNodeID();
  899. if (nOurNodeID != 0)
  900. {
  901. //
  902. // We already have a node ID. Make sure that it is unique. If not,
  903. // we need to generate a new one.
  904. //
  905. bBuildNewNodeID = false;
  906. EnumChildrenPos_t pos;
  907. CMapClass *pChild = pWorld->GetFirstDescendent(pos);
  908. while (pChild != NULL)
  909. {
  910. CMapEntity *pEntity = dynamic_cast <CMapEntity *> (pChild);
  911. if ((pEntity != NULL) && (pEntity != this))
  912. {
  913. int nThisNodeID = pEntity->GetNodeID();
  914. if (nThisNodeID)
  915. {
  916. if (nThisNodeID == nOurNodeID)
  917. {
  918. bBuildNewNodeID = true;
  919. break;
  920. }
  921. }
  922. }
  923. pChild = pWorld->GetNextDescendent(pos);
  924. }
  925. }
  926. if (bBuildNewNodeID)
  927. {
  928. AssignNodeID();
  929. }
  930. }
  931. //-----------------------------------------------------------------------------
  932. // Purpose: Called after the entire map has been loaded. This allows the object
  933. // to perform any linking with other map objects or to do other operations
  934. // that require all world objects to be present.
  935. //-----------------------------------------------------------------------------
  936. void CMapEntity::PostloadWorld(CMapWorld *pWorld)
  937. {
  938. int nIndex;
  939. //
  940. // Set our origin from our "origin" key and discard the key.
  941. //
  942. const char *pszValue = m_KeyValues.GetValue("origin", &nIndex);
  943. if (pszValue != NULL)
  944. {
  945. Vector Origin;
  946. sscanf(pszValue, "%f %f %f", &Origin[0], &Origin[1], &Origin[2]);
  947. SetOrigin(Origin);
  948. }
  949. //
  950. // Set our angle from our "angle" key and discard the key.
  951. //
  952. pszValue = m_KeyValues.GetValue("angle", &nIndex);
  953. if (pszValue != NULL)
  954. {
  955. ImportAngle(atoi(pszValue));
  956. RemoveKey(nIndex);
  957. }
  958. //
  959. // Set the class name from our "classname" key and discard the key.
  960. // This also adds the helpers appropriate for the class.
  961. //
  962. pszValue = m_KeyValues.GetValue("classname", &nIndex);
  963. if (pszValue != NULL)
  964. {
  965. //
  966. // Copy the classname to a temp buffer because SetClass mucks with the
  967. // keyvalues and our pointer might become bad.
  968. //
  969. char szClassName[MAX_CLASS_NAME_LEN];
  970. strcpy(szClassName, pszValue);
  971. SetClass(szClassName, true);
  972. //
  973. // Need to re-get the index of the classname key since it may have changed
  974. // as a result of the above SetClass call.
  975. //
  976. pszValue = m_KeyValues.GetValue("classname", &nIndex);
  977. if (pszValue != NULL)
  978. {
  979. RemoveKey(nIndex);
  980. }
  981. }
  982. //
  983. // Now that we have set the class, remove the origin key if this entity isn't
  984. // supposed to expose it in the keyvalues list.
  985. //
  986. if (IsPlaceholder() && (!IsClass() || GetClass()->VarForName("origin") == NULL))
  987. {
  988. const char *pszValue = m_KeyValues.GetValue("origin", &nIndex);
  989. if (pszValue != NULL)
  990. {
  991. RemoveKey(nIndex);
  992. }
  993. }
  994. //
  995. // Must do this after assigning the class.
  996. //
  997. if (IsNodeClass() && (GetKeyValue("nodeid") == NULL))
  998. {
  999. AssignNodeID();
  1000. }
  1001. // Set a reasonable default
  1002. Vector2D vecLogicalPos = GetLogicalPosition();
  1003. if ( vecLogicalPos.x == COORD_NOTINIT )
  1004. {
  1005. CMapDoc::GetActiveMapDoc()->GetDefaultNewLogicalPosition( vecLogicalPos );
  1006. SetLogicalPosition( vecLogicalPos );
  1007. }
  1008. //
  1009. // Call in all our children (some of which were created above).
  1010. //
  1011. CMapClass::PostloadWorld(pWorld);
  1012. CalculateTypeFlags();
  1013. }
  1014. //-----------------------------------------------------------------------------
  1015. // Purpose: Insures that the entity has all the helpers that it needs (and no more
  1016. // than it should) given its class.
  1017. //-----------------------------------------------------------------------------
  1018. void CMapEntity::UpdateHelpers(bool bLoading)
  1019. {
  1020. //
  1021. // If we have any helpers, delete them. Delete any solid children if we are
  1022. // a point class.
  1023. //
  1024. RemoveHelpers(IsPlaceholder() == TRUE);
  1025. //
  1026. // Add the helpers appropriate for our current class.
  1027. //
  1028. AddHelpersForClass(GetClass(), bLoading);
  1029. }
  1030. //-----------------------------------------------------------------------------
  1031. // Safely sets the move parent. Will assert and not set it if pEnt is equal to this ent,
  1032. // or if this ent is already a parent of pEnt.
  1033. //-----------------------------------------------------------------------------
  1034. void CMapEntity::SetMoveParent( CMapEntity *pEnt )
  1035. {
  1036. // Make sure pEnt is not already parented to (or identical to) me.
  1037. CMapEntity *pCur = pEnt;
  1038. for ( int i=0; i < 300; i++ )
  1039. {
  1040. if ( pCur == NULL )
  1041. {
  1042. break;
  1043. }
  1044. else if ( pCur == this )
  1045. {
  1046. Assert( !"SetMoveParent: recursive parenting!" );
  1047. m_pMoveParent = NULL;
  1048. return;
  1049. }
  1050. pCur = pCur->m_pMoveParent;
  1051. }
  1052. m_pMoveParent = pEnt;
  1053. }
  1054. //-----------------------------------------------------------------------------
  1055. // Purpose: Allows the entity to update its key values based on a change in one
  1056. // of its children. The child exposes the property as a key value pair.
  1057. // Input : pChild - The child whose property changed.
  1058. // szKey - The name of the property that changed.
  1059. // szValue - The new value of the property.
  1060. //-----------------------------------------------------------------------------
  1061. void CMapEntity::NotifyChildKeyChanged(CMapClass *pChild, const char *szKey, const char *szValue)
  1062. {
  1063. m_KeyValues.SetValue(szKey, szValue);
  1064. //
  1065. // Notify all our other non-solid children that a key has changed.
  1066. //
  1067. FOR_EACH_OBJ( m_Children, pos )
  1068. {
  1069. CMapClass *pObject = m_Children.Element(pos);
  1070. if ((pObject != pChild) && (pChild != NULL) && (dynamic_cast<CMapSolid *>(pObject) == NULL))
  1071. {
  1072. pObject->OnParentKeyChanged(szKey, szValue);
  1073. }
  1074. }
  1075. CalcBounds();
  1076. CalculateTypeFlags();
  1077. SignalChanged();
  1078. }
  1079. //-----------------------------------------------------------------------------
  1080. // Purpose:
  1081. //-----------------------------------------------------------------------------
  1082. void CMapEntity::DeleteKeyValue(LPCSTR pszKey)
  1083. {
  1084. char szOldValue[KEYVALUE_MAX_VALUE_LENGTH];
  1085. const char *pszOld = GetKeyValue(pszKey);
  1086. if (pszOld != NULL)
  1087. {
  1088. strcpy(szOldValue, pszOld);
  1089. }
  1090. else
  1091. {
  1092. szOldValue[0] = '\0';
  1093. }
  1094. CEditGameClass::DeleteKeyValue(pszKey);
  1095. OnKeyValueChanged(pszKey, szOldValue, "");
  1096. CalculateTypeFlags();
  1097. SignalChanged();
  1098. }
  1099. //-----------------------------------------------------------------------------
  1100. // Purpose:
  1101. //-----------------------------------------------------------------------------
  1102. void CMapEntity::SetKeyValue(LPCSTR pszKey, LPCSTR pszValue)
  1103. {
  1104. //
  1105. // Get the current value so we can tell if it is changing.
  1106. //
  1107. char szOldValue[KEYVALUE_MAX_VALUE_LENGTH];
  1108. const char *pszOld = GetKeyValue(pszKey);
  1109. if (pszOld != NULL)
  1110. {
  1111. strcpy(szOldValue, pszOld);
  1112. }
  1113. else
  1114. {
  1115. szOldValue[0] = '\0';
  1116. }
  1117. CEditGameClass::SetKeyValue(pszKey, pszValue);
  1118. OnKeyValueChanged(pszKey, szOldValue, pszValue);
  1119. SignalChanged();
  1120. }
  1121. //-----------------------------------------------------------------------------
  1122. // Purpose: Notifies the entity that it has been cloned.
  1123. // Input : pClone -
  1124. //-----------------------------------------------------------------------------
  1125. void CMapEntity::OnPreClone(CMapClass *pClone, CMapWorld *pWorld, const CMapObjectList &OriginalList, CMapObjectList &NewList)
  1126. {
  1127. CMapClass::OnPreClone(pClone, pWorld, OriginalList, NewList);
  1128. if (OriginalList.Count() == 1)
  1129. {
  1130. // dvs: TODO: make this FGD-driven instead of hardcoded, see also MapKeyFrame.cpp
  1131. // dvs: TODO: use letters of the alphabet between adjacent numbers, ie path2a path2b, etc.
  1132. if (!stricmp(GetClassName(), "path_corner") || !stricmp(GetClassName(), "path_track") || !stricmp(GetClassName(), "info_blob_spit_path") )
  1133. {
  1134. //
  1135. // Generate a new name for the clone.
  1136. //
  1137. CMapEntity *pNewEntity = dynamic_cast<CMapEntity*>(pClone);
  1138. Assert(pNewEntity != NULL);
  1139. if (!pNewEntity)
  1140. return;
  1141. // create a new targetname for the clone
  1142. char newName[128];
  1143. const char *oldName = GetKeyValue("targetname");
  1144. if (!oldName || oldName[0] == 0)
  1145. oldName = "path";
  1146. pWorld->GenerateNewTargetname(oldName, newName, sizeof(newName), true, NULL);
  1147. pNewEntity->SetKeyValue("targetname", newName);
  1148. }
  1149. }
  1150. if (IsNodeClass())
  1151. {
  1152. ((CMapEntity *)pClone)->AssignNodeID();
  1153. }
  1154. }
  1155. //-----------------------------------------------------------------------------
  1156. // Purpose:
  1157. // Input : pClone -
  1158. // pWorld -
  1159. // OriginalList -
  1160. // NewList -
  1161. //-----------------------------------------------------------------------------
  1162. void CMapEntity::OnClone(CMapClass *pClone, CMapWorld *pWorld, const CMapObjectList &OriginalList, CMapObjectList &NewList)
  1163. {
  1164. CMapClass::OnClone(pClone, pWorld, OriginalList, NewList);
  1165. if (OriginalList.Count() == 1)
  1166. {
  1167. if (!stricmp(GetClassName(), "path_corner") || !stricmp(GetClassName(), "path_track") || !stricmp(GetClassName(), "info_blob_spit_path") )
  1168. {
  1169. // dvs: TODO: make this FGD-driven instead of hardcoded, see also MapKeyFrame.cpp
  1170. // dvs: TODO: use letters of the alphabet between adjacent numbers, ie path2a path2b, etc.
  1171. CMapEntity *pNewEntity = dynamic_cast<CMapEntity*>(pClone);
  1172. Assert(pNewEntity != NULL);
  1173. if (!pNewEntity)
  1174. return;
  1175. const char *pFieldName = !stricmp(GetClassName(), "info_blob_spit_path") ? "NextPath" : "target";
  1176. // Point the clone at what we were pointing at.
  1177. const char *pszNext = GetKeyValue( pFieldName );
  1178. if (pszNext)
  1179. {
  1180. pNewEntity->SetKeyValue( pFieldName, pszNext );
  1181. }
  1182. // Point this path corner at the clone.
  1183. SetKeyValue( pFieldName, pNewEntity->GetKeyValue("targetname"));
  1184. }
  1185. }
  1186. if (IsNodeClass())
  1187. {
  1188. ReplaceNodeIDRefs(NewList, GetNodeID(), ((CMapEntity *)pClone)->GetNodeID());
  1189. }
  1190. }
  1191. //-----------------------------------------------------------------------------
  1192. // Purpose: Notifies the object that a copy of it is being pasted from the
  1193. // clipboard before the copy is added to the world.
  1194. // Input : pCopy - The copy of this object that is being added to the world.
  1195. // pSourceWorld - The world that the originals were in.
  1196. // pDestWorld - The world that the copies are being added to.
  1197. // OriginalList - The list of original objects that were copied.
  1198. // NewList - The list of copied.
  1199. //-----------------------------------------------------------------------------
  1200. void CMapEntity::OnPrePaste( CMapClass *pCopy, CMapWorld *pSourceWorld, CMapWorld *pDestWorld, const CMapObjectList &OriginalList, CMapObjectList &NewList )
  1201. {
  1202. if (IsNodeClass())
  1203. {
  1204. // Generate a new node ID.
  1205. ((CMapEntity *)pCopy)->AssignNodeID();
  1206. }
  1207. CMapClass::OnPrePaste(pCopy, pSourceWorld, pDestWorld, OriginalList, NewList);
  1208. }
  1209. //-----------------------------------------------------------------------------
  1210. // Purpose:
  1211. // Input : pCopy -
  1212. // pSourceWorld -
  1213. // pDestWorld -
  1214. // OriginalList -
  1215. // NewList -
  1216. //-----------------------------------------------------------------------------
  1217. void CMapEntity::OnPaste(CMapClass *pCopy, CMapWorld *pSourceWorld, CMapWorld *pDestWorld, const CMapObjectList &OriginalList, CMapObjectList &NewList)
  1218. {
  1219. if (IsNodeClass())
  1220. {
  1221. ReplaceNodeIDRefs(NewList, GetNodeID(), ((CMapEntity *)pCopy)->GetNodeID());
  1222. }
  1223. CMapClass::OnPaste(pCopy, pSourceWorld, pDestWorld, OriginalList, NewList);
  1224. }
  1225. //-----------------------------------------------------------------------------
  1226. // Purpose:
  1227. // Input : pszKey -
  1228. // pszOldValue -
  1229. // pszValue -
  1230. //-----------------------------------------------------------------------------
  1231. void CMapEntity::OnKeyValueChanged(const char *pszKey, const char *pszOldValue, const char *pszValue)
  1232. {
  1233. // notify all our children that a key has changed
  1234. FOR_EACH_OBJ( m_Children, pos )
  1235. {
  1236. CMapClass *pChild = m_Children.Element( pos );
  1237. if ( pChild != NULL )
  1238. {
  1239. pChild->OnParentKeyChanged( pszKey, pszValue );
  1240. }
  1241. }
  1242. //
  1243. // Changing our movement parent. Store a pointer to the movement parent
  1244. // for when we're playing animations.
  1245. //
  1246. if ( !stricmp(pszKey, "parentname") )
  1247. {
  1248. CMapWorld *pWorld = (CMapWorld *)GetWorldObject( this );
  1249. if (pWorld != NULL)
  1250. {
  1251. CMapEntity *pMoveParent = (CMapEntity *)UpdateDependency(m_pMoveParent, pWorld->FindEntityByName( pszValue));
  1252. SetMoveParent( pMoveParent );
  1253. }
  1254. }
  1255. //
  1256. // Changing our model - rebuild the helpers from scratch.
  1257. // dvs: this could probably go away - move support into the helper code.
  1258. //
  1259. else if (!stricmp(pszKey, "model"))
  1260. {
  1261. if (stricmp(pszOldValue, pszValue) != 0)
  1262. {
  1263. // We don't call SetKeyValue during VMF load.
  1264. UpdateHelpers(false);
  1265. }
  1266. }
  1267. //
  1268. // If our targetname has changed, we have to relink EVERYTHING, not
  1269. // just our dependents, because someone else may point to our new targetname.
  1270. //
  1271. else if (!stricmp(pszKey, "targetname") && (stricmp(pszOldValue, pszValue) != 0))
  1272. {
  1273. UpdateAllDependencies(this);
  1274. }
  1275. SignalChanged();
  1276. }
  1277. //-----------------------------------------------------------------------------
  1278. // Purpose: Returns true if this entity has any solid children. Entities of
  1279. // classes that are not in the FGD are considered solid entities if
  1280. // they have at least one solid child, point entities if not.
  1281. //-----------------------------------------------------------------------------
  1282. bool CMapEntity::HasSolidChildren(void)
  1283. {
  1284. FOR_EACH_OBJ( m_Children, pos )
  1285. {
  1286. CMapClass *pChild = m_Children.Element(pos);
  1287. if ((dynamic_cast <CMapSolid *> (pChild)) != NULL)
  1288. {
  1289. return(true);
  1290. }
  1291. }
  1292. return(false);
  1293. }
  1294. //-----------------------------------------------------------------------------
  1295. // Purpose:
  1296. //-----------------------------------------------------------------------------
  1297. bool CMapEntity::OnApply( void )
  1298. {
  1299. FOR_EACH_OBJ( m_Children, pos )
  1300. {
  1301. CMapClass *pChild = m_Children.Element(pos);
  1302. if ( pChild )
  1303. {
  1304. pChild->OnApply();
  1305. }
  1306. }
  1307. return true;
  1308. }
  1309. //-----------------------------------------------------------------------------
  1310. // Purpose: Called after this object is added to the world.
  1311. //
  1312. // NOTE: This function is NOT called during serialization. Use PostloadWorld
  1313. // to do similar bookkeeping after map load.
  1314. //
  1315. // Input : pWorld - The world that we have been added to.
  1316. //-----------------------------------------------------------------------------
  1317. void CMapEntity::OnAddToWorld(CMapWorld *pWorld)
  1318. {
  1319. CMapClass::OnAddToWorld(pWorld);
  1320. //
  1321. // If we are a node class, we must insure that we have a valid unique ID.
  1322. //
  1323. if (IsNodeClass())
  1324. {
  1325. EnsureUniqueNodeID(pWorld);
  1326. }
  1327. //
  1328. // If we have a targetname, relink all the targetname pointers in the world
  1329. // because someone might be looking for our targetname.
  1330. //
  1331. if (GetKeyValue("targetname") != NULL)
  1332. {
  1333. UpdateAllDependencies(this);
  1334. }
  1335. }
  1336. //-----------------------------------------------------------------------------
  1337. // Purpose: Called before this object is deleted from the world.
  1338. //
  1339. // Input : pWorld - The world that we have been added to.
  1340. // b
  1341. //-----------------------------------------------------------------------------
  1342. void CMapEntity::OnRemoveFromWorld(CMapWorld *pWorld, bool bNotifyChildren)
  1343. {
  1344. // Disconnect this now removed entity from the rest of the world
  1345. Connections_FixBad(false);
  1346. Upstream_FixBad();
  1347. CMapClass::OnRemoveFromWorld(pWorld, bNotifyChildren);
  1348. }
  1349. //-----------------------------------------------------------------------------
  1350. // Purpose:
  1351. // Input : pObject - The object that changed.
  1352. //-----------------------------------------------------------------------------
  1353. void CMapEntity::OnNotifyDependent(CMapClass *pObject, Notify_Dependent_t eNotifyType)
  1354. {
  1355. CMapClass::OnNotifyDependent(pObject, eNotifyType);
  1356. if (eNotifyType == Notify_Removed)
  1357. {
  1358. //
  1359. // Check for our move parent going away.
  1360. //
  1361. if (pObject == m_pMoveParent)
  1362. {
  1363. CMapWorld *pWorld = (CMapWorld *)GetWorldObject(this);
  1364. const char *pszParentName = CEditGameClass::GetKeyValue("parentname");
  1365. if ((pWorld != NULL) && (pszParentName != NULL))
  1366. {
  1367. CMapEntity *pMoveParent = (CMapEntity *)UpdateDependency(m_pMoveParent, pWorld->FindEntityByName( pszParentName));
  1368. SetMoveParent( pMoveParent );
  1369. }
  1370. else
  1371. {
  1372. CMapEntity *pMoveParent = (CMapEntity *)UpdateDependency(m_pMoveParent, NULL);
  1373. SetMoveParent( pMoveParent );
  1374. }
  1375. }
  1376. }
  1377. }
  1378. //-----------------------------------------------------------------------------
  1379. // Purpose: Iterates through an object, and all it's children, looking for an
  1380. // entity with a matching key and value
  1381. // Input : key -
  1382. // value -
  1383. // Output : Returns a pointer to the entity found.
  1384. //-----------------------------------------------------------------------------
  1385. CMapEntity *CMapEntity::FindChildByKeyValue( LPCSTR key, LPCSTR value, bool *bIsInInstance, VMatrix *InstanceMatrix )
  1386. {
  1387. if ((key == NULL) || (value == NULL))
  1388. {
  1389. return(NULL);
  1390. }
  1391. int index;
  1392. LPCSTR val = CEditGameClass::GetKeyValue(key, &index);
  1393. if ( val && value && !stricmp(value, val) )
  1394. {
  1395. return this;
  1396. }
  1397. return CMapClass::FindChildByKeyValue( key, value, bIsInInstance, InstanceMatrix );
  1398. }
  1399. //-----------------------------------------------------------------------------
  1400. // Purpose: Returns a coordinate frame to render in, if the entity is animating
  1401. // Input : matrix -
  1402. // Output : returns true if a new matrix is returned, false if it is just the identity
  1403. //-----------------------------------------------------------------------------
  1404. bool CMapEntity::GetTransformMatrix( VMatrix& matrix )
  1405. {
  1406. bool gotMatrix = false;
  1407. // if we have a move parent, get its transformation matrix
  1408. if ( m_pMoveParent )
  1409. {
  1410. if ( m_pMoveParent == this )
  1411. {
  1412. Assert( !"Recursive parenting." );
  1413. }
  1414. else
  1415. {
  1416. gotMatrix = m_pMoveParent->GetTransformMatrix( matrix );
  1417. }
  1418. }
  1419. if ( m_pAnimatorChild )
  1420. {
  1421. // return a matrix that will transform any vector into our (animated) space
  1422. if ( gotMatrix )
  1423. {
  1424. // return ParentMatrix * OurMatrix
  1425. VMatrix tmpMat, animatorMat;
  1426. bool gotAnimMatrix = m_pAnimatorChild->GetTransformMatrix( animatorMat );
  1427. if ( !gotAnimMatrix )
  1428. {
  1429. // since we didn't get a new matrix from our child just return our parent's
  1430. return true;
  1431. }
  1432. matrix = matrix * animatorMat;
  1433. }
  1434. else
  1435. {
  1436. // no parent, we're at the top of the game
  1437. gotMatrix = m_pAnimatorChild->GetTransformMatrix( matrix );
  1438. }
  1439. }
  1440. return gotMatrix;
  1441. }
  1442. //-----------------------------------------------------------------------------
  1443. // Saves editor data
  1444. //-----------------------------------------------------------------------------
  1445. ChunkFileResult_t CMapEntity::SaveEditorData(CChunkFile *pFile)
  1446. {
  1447. #ifndef SDK_BUILD
  1448. ChunkFileResult_t eResult = pFile->WriteKeyValueVector2("logicalpos", m_vecLogicalPosition);
  1449. if (eResult != ChunkFile_Ok)
  1450. return eResult;
  1451. #endif // SDK_BUILD
  1452. return BaseClass::SaveEditorData( pFile );
  1453. }
  1454. //-----------------------------------------------------------------------------
  1455. // Purpose:
  1456. //-----------------------------------------------------------------------------
  1457. ChunkFileResult_t CMapEntity::SaveVMF(CChunkFile *pFile, CSaveInfo *pSaveInfo)
  1458. {
  1459. //
  1460. // Check rules before saving this object.
  1461. //
  1462. if (!pSaveInfo->ShouldSaveObject(this))
  1463. {
  1464. return(ChunkFile_Ok);
  1465. }
  1466. ChunkFileResult_t eResult = ChunkFile_Ok;
  1467. //
  1468. // If it's a solidentity but it doesn't have any solids,
  1469. // don't save it.
  1470. //
  1471. if (!IsPlaceholder() && !m_Children.Count())
  1472. {
  1473. return(ChunkFile_Ok);
  1474. }
  1475. //
  1476. // If we are hidden, place this object inside of a hidden chunk.
  1477. //
  1478. if (!IsVisible())
  1479. {
  1480. eResult = pFile->BeginChunk("hidden");
  1481. }
  1482. //
  1483. // Begin this entity's scope.
  1484. //
  1485. eResult = pFile->BeginChunk("entity");
  1486. //
  1487. // Save the entity's ID.
  1488. //
  1489. if (eResult == ChunkFile_Ok)
  1490. {
  1491. eResult = pFile->WriteKeyValueInt("id", GetID());
  1492. }
  1493. //
  1494. // Save our keys.
  1495. //
  1496. if (eResult == ChunkFile_Ok)
  1497. {
  1498. eResult = CEditGameClass::SaveVMF(pFile, pSaveInfo);
  1499. }
  1500. //
  1501. // If this is a point entity of an unknown type or a point entity that doesn't
  1502. // declare an origin key, save our origin.
  1503. //
  1504. if (IsPlaceholder() && (!IsClass() || GetClass()->VarForName("origin") == NULL))
  1505. {
  1506. char szOrigin[80];
  1507. sprintf(szOrigin, "%g %g %g", (double)m_Origin[0], (double)m_Origin[1], (double)m_Origin[2]);
  1508. pFile->WriteKeyValue("origin", szOrigin);
  1509. }
  1510. //
  1511. // Save all our descendents.
  1512. //
  1513. eResult = ChunkFile_Ok;
  1514. EnumChildrenPos_t pos;
  1515. CMapClass *pChild = GetFirstDescendent(pos);
  1516. while ((pChild != NULL) && (eResult == ChunkFile_Ok))
  1517. {
  1518. if ( pChild->ShouldSerialize() )
  1519. {
  1520. eResult = pChild->SaveVMF(pFile, pSaveInfo);
  1521. }
  1522. pChild = GetNextDescendent(pos);
  1523. }
  1524. //
  1525. // Save our base class' information within our chunk.
  1526. //
  1527. if (eResult == ChunkFile_Ok)
  1528. {
  1529. eResult = CMapClass::SaveVMF(pFile, pSaveInfo);
  1530. }
  1531. //
  1532. // Save custom model information
  1533. //
  1534. eResult = VmfSaveVmfEntityHandlers( pFile, static_cast< IMapEntity_Type_t * >( this ),
  1535. static_cast< IMapEntity_SaveInfo_t * >( pSaveInfo ) );
  1536. //
  1537. // End this entity's scope.
  1538. //
  1539. if (eResult == ChunkFile_Ok)
  1540. {
  1541. pFile->EndChunk();
  1542. }
  1543. //
  1544. // End the hidden chunk if we began it.
  1545. //
  1546. if (!IsVisible())
  1547. {
  1548. eResult = pFile->EndChunk();
  1549. }
  1550. return(eResult);
  1551. }
  1552. //-----------------------------------------------------------------------------
  1553. // Purpose: Overloaded to use the color from our FGD definition.
  1554. // Output : Returns true if the color was specified by this call, false if not.
  1555. //-----------------------------------------------------------------------------
  1556. bool CMapEntity::UpdateObjectColor()
  1557. {
  1558. if (!BaseClass::UpdateObjectColor())
  1559. {
  1560. if (IsClass())
  1561. {
  1562. color32 rgbColor = m_pClass->GetColor();
  1563. SetRenderColor(rgbColor);
  1564. return true;
  1565. }
  1566. }
  1567. else
  1568. {
  1569. return true;
  1570. }
  1571. return false;
  1572. }
  1573. //-----------------------------------------------------------------------------
  1574. // Purpose:
  1575. // Input : pWorld -
  1576. // pObject -
  1577. //-----------------------------------------------------------------------------
  1578. void CMapEntity::UpdateDependencies(CMapWorld *pWorld, CMapClass *pObject)
  1579. {
  1580. CMapClass::UpdateDependencies(pWorld, pObject);
  1581. //
  1582. // If we have a movement parent, relink to our movement parent.
  1583. //
  1584. const char *pszParentName = CEditGameClass::GetKeyValue("parentname");
  1585. if (pszParentName != NULL)
  1586. {
  1587. CMapEntity *pMoveParent = (CMapEntity *)UpdateDependency(m_pMoveParent, pWorld->FindEntityByName( pszParentName));
  1588. SetMoveParent( pMoveParent );
  1589. }
  1590. else
  1591. {
  1592. CMapEntity *pMoveParent = (CMapEntity *)UpdateDependency(m_pMoveParent, NULL);
  1593. SetMoveParent( pMoveParent );
  1594. }
  1595. CMapDoc *pDoc = CMapDoc::GetActiveMapDoc();
  1596. if ( pDoc && !pDoc->IsLoading() )
  1597. {
  1598. // Update any downstream/upstream connections objects associated with this entity
  1599. Connections_FixBad();
  1600. Upstream_FixBad();
  1601. }
  1602. }
  1603. //-----------------------------------------------------------------------------
  1604. // Purpose: Places the entity properly on a plane surface, at a given location
  1605. // Input: pos - position on the plane
  1606. // plane - surface plane to align to
  1607. // align - alignment type (top, bottom)
  1608. // Output:
  1609. //-----------------------------------------------------------------------------
  1610. #define ALIGN_EPSILON 1 // World units
  1611. void CMapEntity::AlignOnPlane( Vector& pos, PLANE *plane, alignType_e align )
  1612. {
  1613. float fOffset = 0.0f;
  1614. Vector vecNewPos;
  1615. //Depending on the alignment type, get the offset from the surface
  1616. switch ( align )
  1617. {
  1618. case ALIGN_BOTTOM:
  1619. fOffset = m_Origin[2] - m_Render2DBox.bmins[2];
  1620. break;
  1621. case ALIGN_TOP:
  1622. fOffset = m_Render2DBox.bmaxs[2] - m_Origin[2];
  1623. break;
  1624. }
  1625. //Push our point out and away from this surface
  1626. VectorMA( pos, fOffset + ALIGN_EPSILON, plane->normal, vecNewPos );
  1627. //Update the entity and children
  1628. SetOrigin( vecNewPos );
  1629. SignalChanged();
  1630. }
  1631. //-----------------------------------------------------------------------------
  1632. // Purpose: Looks for an input with a given name in the entity list. ALL entities
  1633. // in the list must have the given input for a match to be found.
  1634. // Input : szInput - Name of the input.
  1635. // Output : Returns true if the input name was found in all entities, false if not.
  1636. //-----------------------------------------------------------------------------
  1637. bool MapEntityList_HasInput(const CMapEntityList *pList, const char *szInput, InputOutputType_t eType)
  1638. {
  1639. GDclass *pLastClass = NULL;
  1640. FOR_EACH_OBJ( *pList, pos )
  1641. {
  1642. const CMapEntity *pEntity = pList->Element(pos).GetObject();
  1643. GDclass *pClass = pEntity ? pEntity->GetClass() : NULL;
  1644. if ((pClass != pLastClass) && (pClass != NULL))
  1645. {
  1646. CClassInput *pInput = pClass->FindInput(szInput);
  1647. if (!pInput)
  1648. {
  1649. return false;
  1650. }
  1651. if ((eType != iotInvalid) && (pInput->GetType() != eType))
  1652. {
  1653. return false;
  1654. }
  1655. //
  1656. // Cheap optimization to help minimize redundant checks.
  1657. //
  1658. pLastClass = pClass;
  1659. }
  1660. }
  1661. return true;
  1662. }
  1663. //-----------------------------------------------------------------------------
  1664. // Purpose: Returns a pointer to the object that should be added to the selection
  1665. // list because this object was clicked on with a given selection mode.
  1666. // Input : eSelectMode -
  1667. //-----------------------------------------------------------------------------
  1668. CMapClass *CMapEntity::PrepareSelection(SelectMode_t eSelectMode)
  1669. {
  1670. //
  1671. // Select up the hierarchy when in Groups selection mode if we belong to a group.
  1672. //
  1673. if ((eSelectMode == selectGroups) && (m_pParent != NULL) && !IsWorldObject(m_pParent))
  1674. {
  1675. return GetParent()->PrepareSelection(eSelectMode);
  1676. }
  1677. //
  1678. // Don't select solid entities when in Solids selection mode. We'll select
  1679. // their solid children.
  1680. //
  1681. if ((eSelectMode == selectSolids) && !IsPlaceholder())
  1682. {
  1683. return NULL;
  1684. }
  1685. return this;
  1686. }
  1687. //-----------------------------------------------------------------------------
  1688. // Purpose:
  1689. // Input : pRender -
  1690. //-----------------------------------------------------------------------------
  1691. void CMapEntity::Render2D(CRender2D *pRender)
  1692. {
  1693. // Render all our children (helpers & solids)
  1694. BaseClass::Render2D(pRender);
  1695. CMapView2D *pView = (CMapView2D*)pRender->GetView();
  1696. Vector vecMins, vecMaxs;
  1697. GetRender2DBox(vecMins, vecMaxs);
  1698. if ( pRender->GetInstanceRendering() )
  1699. {
  1700. Vector vecExpandedMins, vecExpandedMaxs;
  1701. pRender->TransformInstanceAABB( vecMins, vecMaxs, vecExpandedMins, vecExpandedMaxs );
  1702. vecMins = vecExpandedMins;
  1703. vecMaxs = vecExpandedMaxs;
  1704. }
  1705. Vector2D pt, pt2;
  1706. pView->WorldToClient(pt, vecMins);
  1707. pView->WorldToClient(pt2, vecMaxs);
  1708. color32 rgbColor = GetRenderColor( pRender );
  1709. pRender->SetDrawColor( rgbColor.r, rgbColor.g, rgbColor.b );
  1710. // Render the entity's name and class name if enabled.
  1711. if (s_bShowEntityNames && pView->GetZoom() >= 1)
  1712. {
  1713. pRender->SetTextColor( rgbColor.r, rgbColor.g, rgbColor.b );
  1714. const char *pszTargetName = GetKeyValue("targetname");
  1715. if (pszTargetName != NULL)
  1716. {
  1717. pRender->DrawText(pszTargetName, pt.x, pt.y + 2, CRender2D::TEXT_JUSTIFY_BOTTOM );
  1718. }
  1719. const char *pszClassName = GetClassName();
  1720. if (pszClassName != NULL)
  1721. {
  1722. pRender->DrawText(pszClassName, pt.x, pt2.y - 2, CRender2D::TEXT_JUSTIFY_TOP );
  1723. }
  1724. }
  1725. //
  1726. // Draw the connections between entities and their targets if enabled.
  1727. //
  1728. if (s_bShowEntityConnections)
  1729. {
  1730. LPCTSTR pszTarget = GetKeyValue("target");
  1731. if (pszTarget != NULL)
  1732. {
  1733. CMapWorld *pWorld = GetWorldObject(this);
  1734. MDkeyvalue kv("targetname", pszTarget);
  1735. CMapObjectList FoundEntities;
  1736. FoundEntities.RemoveAll();
  1737. pWorld->EnumChildren((ENUMMAPCHILDRENPROC)FindKeyValue, (DWORD)&kv, MAPCLASS_TYPE(CMapEntity));
  1738. Vector vCenter1,vCenter2;
  1739. GetBoundsCenter( vCenter1 );
  1740. FOR_EACH_OBJ( FoundEntities, p )
  1741. {
  1742. CMapClass *pMapClass = (CUtlReference< CMapClass >)FoundEntities.Element(p);
  1743. CMapClass *pEntity = (CMapEntity *)pMapClass;
  1744. pEntity->GetBoundsCenter(vCenter2);
  1745. pRender->DrawLine( vCenter1, vCenter2 );
  1746. }
  1747. }
  1748. }
  1749. // Draw the forward vector if we have an "angles" key and we're selected.
  1750. // HACK: don't draw the forward vector for lights, they negate pitch. The model helper will handle it.
  1751. if ((GetSelectionState() != SELECT_NONE) &&
  1752. (!GetClassName() || (strnicmp(GetClassName(), "light_", 6) != 0)) &&
  1753. (GetKeyValue("angles") != NULL))
  1754. {
  1755. Vector vecOrigin;
  1756. GetOrigin(vecOrigin);
  1757. QAngle vecAngles;
  1758. GetAngles(vecAngles);
  1759. Vector vecForward;
  1760. AngleVectors(vecAngles, &vecForward);
  1761. pRender->SetDrawColor( 255, 255, 0 );
  1762. pRender->DrawLine(vecOrigin, vecOrigin + vecForward * 24);
  1763. }
  1764. }
  1765. //-----------------------------------------------------------------------------
  1766. // Gets the 2D logical view bounding box
  1767. //-----------------------------------------------------------------------------
  1768. void CMapEntity::GetRenderLogicalBox( Vector2D &mins, Vector2D &maxs )
  1769. {
  1770. mins.x = m_vecLogicalPosition.x;
  1771. maxs.x = m_vecLogicalPosition.x + LOGICAL_BOX_WIDTH + LOGICAL_BOX_CONNECTOR_INPUT_WIDTH + LOGICAL_BOX_CONNECTOR_OUTPUT_WIDTH;
  1772. mins.y = m_vecLogicalPosition.y;
  1773. maxs.y = m_vecLogicalPosition.y + LOGICAL_BOX_HEIGHT;
  1774. }
  1775. //-----------------------------------------------------------------------------
  1776. // Logical position accessor
  1777. //-----------------------------------------------------------------------------
  1778. const Vector2D& CMapEntity::GetLogicalPosition( )
  1779. {
  1780. return m_vecLogicalPosition;
  1781. }
  1782. void CMapEntity::SetLogicalPosition( const Vector2D &vecPosition )
  1783. {
  1784. m_vecLogicalPosition = vecPosition;
  1785. }
  1786. //-----------------------------------------------------------------------------
  1787. // Returns a logical position
  1788. //-----------------------------------------------------------------------------
  1789. void CMapEntity::GetLogicalConnectionPosition( LogicalConnection_t i, Vector2D &vecPosition )
  1790. {
  1791. Vector2D vecMins, vecMaxs;
  1792. GetRenderLogicalBox( vecMins, vecMaxs );
  1793. vecPosition.y = ( vecMins.y + vecMaxs.y ) * 0.5f;
  1794. if ( i == LOGICAL_CONNECTION_INPUT )
  1795. {
  1796. vecPosition.x = vecMins.x;
  1797. }
  1798. else
  1799. {
  1800. vecPosition.x = vecMaxs.x;
  1801. }
  1802. }
  1803. //-----------------------------------------------------------------------------
  1804. // Renders into the logical view
  1805. //-----------------------------------------------------------------------------
  1806. void CMapEntity::RenderLogical( CRender2D *pRender )
  1807. {
  1808. // Render all our children (helpers & solids)
  1809. BaseClass::RenderLogical(pRender);
  1810. Vector2D vecMins, vecMaxs;
  1811. GetRenderLogicalBox( vecMins, vecMaxs );
  1812. Vector2D vecBoxMins = vecMins;
  1813. Vector2D vecBoxMaxs = vecMaxs;
  1814. vecBoxMins.x += LOGICAL_BOX_CONNECTOR_INPUT_WIDTH;
  1815. vecBoxMaxs.x -= LOGICAL_BOX_CONNECTOR_OUTPUT_WIDTH;
  1816. // Define the entity highlight/lowlight edges
  1817. Vector2D vecInnerMins = vecBoxMins, vecInnerMaxs = vecBoxMaxs;
  1818. vecInnerMins.x += LOGICAL_BOX_INNER_OFFSET;
  1819. vecInnerMins.y += LOGICAL_BOX_INNER_OFFSET;
  1820. vecInnerMaxs.x -= LOGICAL_BOX_INNER_OFFSET;
  1821. vecInnerMaxs.y -= LOGICAL_BOX_INNER_OFFSET;
  1822. // Get the entity render color
  1823. color32 rgbColor = GetRenderColor( pRender );
  1824. color32 rgbHighlight = {7*rgbColor.r/8, 7*rgbColor.g/8, 7*rgbColor.b/8, 255 };
  1825. color32 rgbLowlight = {5*rgbColor.r/8, 5*rgbColor.g/8, 5*rgbColor.b/8, 255 };
  1826. color32 rgbEdgeColor = {3*rgbColor.r/8, 3*rgbColor.g/8, 3*rgbColor.b/8, 255 };
  1827. color32 rgbInterior = {2*rgbColor.r/8, 2*rgbColor.g/8, 2*rgbColor.b/8, 255 };
  1828. // Draw an inside UpperLeft highlight rect (leading edge highlight)
  1829. pRender->SetDrawColor( rgbHighlight.r, rgbHighlight.g, rgbHighlight.b );
  1830. pRender->DrawRectangle( Vector( vecBoxMins.x, vecBoxMins.y, 0.0f ), Vector( vecBoxMaxs.x, vecBoxMaxs.y, 0.0f ), true, 0 );
  1831. // Draw an inside LowerRight lowlight rect (trailing edge lowlight)
  1832. pRender->SetDrawColor( rgbLowlight.r, rgbLowlight.g, rgbLowlight.b );
  1833. pRender->DrawRectangle( Vector( vecInnerMins.x, vecBoxMins.y, 0.0f ), Vector( vecBoxMaxs.x, vecInnerMaxs.y, 0.0f ), true, 0 );
  1834. // Draw an outside border rect in the entities render color
  1835. pRender->SetDrawColor( rgbEdgeColor.r, rgbEdgeColor.g, rgbEdgeColor.b );
  1836. pRender->DrawRectangle( Vector( vecBoxMins.x, vecBoxMins.y, 0.0f ), Vector( vecBoxMaxs.x, vecBoxMaxs.y, 0.0f ), false, 0 );
  1837. // Draw the small diagonals connecting the outer and inner corners
  1838. pRender->DrawLine( Vector( vecBoxMins.x, vecBoxMins.y, 0.0f ), Vector( vecBoxMaxs.x, vecBoxMaxs.y, 0.0f ) );
  1839. pRender->DrawLine( Vector( vecBoxMins.x, vecBoxMaxs.y, 0.0f ), Vector( vecBoxMaxs.x, vecBoxMins.y, 0.0f ) );
  1840. // Draw interior background first
  1841. pRender->SetDrawColor( rgbInterior.r, rgbInterior.g, rgbInterior.b );
  1842. pRender->DrawRectangle( Vector( vecInnerMins.x, vecInnerMins.y, 0.0f ), Vector( vecInnerMaxs.x, vecInnerMaxs.y, 0.0f ), true, 0 );
  1843. // Draws the sprite helper(s) (if it has them)
  1844. bool bFoundSpriteHelper = false;
  1845. FOR_EACH_OBJ( m_Children, pos )
  1846. {
  1847. CMapClass *pMapClass = (CUtlReference< CMapClass >)m_Children[pos];
  1848. CMapSprite *pSprite = dynamic_cast<CMapSprite*>( pMapClass );
  1849. if ( pSprite )
  1850. {
  1851. // Render the sprite on top of the background
  1852. pSprite->RenderLogicalAt( pRender, vecInnerMins, vecInnerMaxs );
  1853. bFoundSpriteHelper = true;
  1854. }
  1855. }
  1856. // Fill in the interior with entity color if no sprite was found
  1857. if ( !bFoundSpriteHelper )
  1858. {
  1859. // Redraw the interior with the entity's render color
  1860. pRender->SetDrawColor( rgbColor.r, rgbColor.g, rgbColor.b );
  1861. pRender->DrawRectangle( Vector( vecInnerMins.x, vecInnerMins.y, 0.0f ), Vector( vecInnerMaxs.x, vecInnerMaxs.y, 0.0f ), true, 0 );
  1862. // Put an inner border around the entity color block
  1863. pRender->SetDrawColor( rgbEdgeColor.r, rgbEdgeColor.g, rgbEdgeColor.b );
  1864. pRender->DrawRectangle( Vector( vecInnerMins.x, vecInnerMins.y, 0.0f ), Vector( vecInnerMaxs.x, vecInnerMaxs.y, 0.0f ), false, 0 );
  1865. }
  1866. // Draw the rest of the entity in the entity color
  1867. pRender->SetDrawColor( rgbColor.r, rgbColor.g, rgbColor.b );
  1868. // Draws the connectors
  1869. float flConnectorY = ( vecMins.y + vecMaxs.y ) * 0.5f;
  1870. pRender->DrawCircle( Vector( vecMins.x + LOGICAL_BOX_CONNECTOR_RADIUS, flConnectorY, 0.0f ), LOGICAL_BOX_CONNECTOR_RADIUS );
  1871. pRender->MoveTo( Vector( vecMins.x + 2 * LOGICAL_BOX_CONNECTOR_RADIUS, flConnectorY, 0.0f ) );
  1872. pRender->DrawLineTo( Vector( vecBoxMins.x, flConnectorY, 0.0f ) );
  1873. pRender->MoveTo( Vector( vecBoxMaxs.x, flConnectorY, 0.0f ) );
  1874. pRender->DrawLineTo( Vector( vecMaxs.x - LOGICAL_BOX_ARROW_LENGTH, flConnectorY, 0.0f ) );
  1875. pRender->DrawLineTo( Vector( vecMaxs.x - LOGICAL_BOX_ARROW_LENGTH, flConnectorY + LOGICAL_BOX_ARROW_HEIGHT, 0.0f ) );
  1876. pRender->DrawLineTo( Vector( vecMaxs.x, flConnectorY, 0.0f ) );
  1877. pRender->DrawLineTo( Vector( vecMaxs.x - LOGICAL_BOX_ARROW_LENGTH, flConnectorY - LOGICAL_BOX_ARROW_HEIGHT, 0.0f ) );
  1878. pRender->DrawLineTo( Vector( vecMaxs.x - LOGICAL_BOX_ARROW_LENGTH, flConnectorY, 0.0f ) );
  1879. // Stop drawing the text once the entity itself gets too small.
  1880. Vector2D pt, pt2;
  1881. pRender->GetView()->WorldToClient( pt, Vector( vecBoxMins.x, vecBoxMins.y, 0.0f ) );
  1882. pRender->GetView()->WorldToClient( pt2, Vector( vecBoxMaxs.x, vecBoxMaxs.y, 0.0f ) );
  1883. if ( fabs( pt.y - pt2.y ) < 32 )
  1884. return;
  1885. // Render the entity's name and class name if enabled.
  1886. pRender->SetTextColor( rgbColor.r, rgbColor.g, rgbColor.b );
  1887. // Draw the inputs and outputs
  1888. const char *pszTargetName = GetKeyValue("targetname");
  1889. if (pszTargetName != NULL)
  1890. {
  1891. pRender->DrawText( pszTargetName, Vector2D( (vecMins.x+vecMaxs.x)/2, vecMaxs.y ), 0, -1, CRender2D::TEXT_JUSTIFY_TOP | CRender2D::TEXT_JUSTIFY_HORZ_CENTER );
  1892. }
  1893. if ( fabs( pt.y - pt2.y ) < 50 )
  1894. return;
  1895. const char *pszClassName = GetClassName();
  1896. if (pszClassName != NULL)
  1897. {
  1898. pRender->DrawText( pszClassName, Vector2D( (vecMins.x+vecMaxs.x)/2, vecMins.y ), 0, 1, CRender2D::TEXT_JUSTIFY_BOTTOM | CRender2D::TEXT_JUSTIFY_HORZ_CENTER );
  1899. }
  1900. }
  1901. //-----------------------------------------------------------------------------
  1902. // Purpose: Returns whether this entity snaps to half grid or not. Some entities,
  1903. // such as hinges, need to snap to a 0.5 grid to center on geometry.
  1904. //-----------------------------------------------------------------------------
  1905. bool CMapEntity::ShouldSnapToHalfGrid()
  1906. {
  1907. return (GetClass() && GetClass()->ShouldSnapToHalfGrid());
  1908. }
  1909. //-----------------------------------------------------------------------------
  1910. // Purpose: Returns the integer value of the nodeid key of this entity.
  1911. //-----------------------------------------------------------------------------
  1912. int CMapEntity::GetNodeID(void)
  1913. {
  1914. int nNodeID = 0;
  1915. const char *pszNodeID = GetKeyValue("nodeid");
  1916. if (pszNodeID)
  1917. {
  1918. nNodeID = atoi(pszNodeID);
  1919. }
  1920. return nNodeID;
  1921. }
  1922. //-----------------------------------------------------------------------------
  1923. // Returns whether this object intersects the given cordon bounds.
  1924. // Return true to keep the object, false to cull it.
  1925. //-----------------------------------------------------------------------------
  1926. bool CMapEntity::IsIntersectingCordon(const Vector &vecMins, const Vector &vecMaxs)
  1927. {
  1928. // Point entities are culled by their origin, not by their bounding box.
  1929. // An exception to that is swept hulls, such as ladders, that are more like solid ents.
  1930. if ( IsPointClass() && !IsSweptHullClass( this ) )
  1931. {
  1932. Vector vecOrigin;
  1933. GetOrigin(vecOrigin);
  1934. return IsPointInBox(vecOrigin, vecMins, vecMaxs);
  1935. }
  1936. return IsIntersectingBox(vecMins, vecMaxs);
  1937. }
  1938. //-----------------------------------------------------------------------------
  1939. // Purpose:
  1940. // Input : pView -
  1941. // vecPoint -
  1942. // nHitData -
  1943. // Output :
  1944. //-----------------------------------------------------------------------------
  1945. bool CMapEntity::HitTest2D(CMapView2D *pView, const Vector2D &point, HitInfo_t &HitData)
  1946. {
  1947. if ( !IsVisible() )
  1948. return false;
  1949. if ( BaseClass::HitTest2D(pView, point, HitData) )
  1950. return true;
  1951. //
  1952. // Only check point entities; brush entities are selected via their brushes.
  1953. //
  1954. if ( !IsPointClass() )
  1955. return false;
  1956. // First check center X.
  1957. Vector vecCenter, vecViewPoint;
  1958. GetBoundsCenter(vecCenter);
  1959. Vector2D vecClientCenter;
  1960. pView->WorldToClient(vecClientCenter, vecCenter);
  1961. pView->GetCamera()->GetViewPoint( vecViewPoint );
  1962. HitData.pObject = this;
  1963. HitData.nDepth = vecViewPoint[pView->axThird]-vecCenter[pView->axThird];
  1964. if ( pView->CheckDistance( point, vecClientCenter, HANDLE_RADIUS) )
  1965. {
  1966. HitData.uData = 0;
  1967. return true;
  1968. }
  1969. else if (!Options.view2d.bSelectbyhandles)
  1970. {
  1971. //
  1972. // See if any edges of the bbox are within a certain distance from the the point.
  1973. //
  1974. int iSelUnits = 2;
  1975. int x1 = point.x - iSelUnits;
  1976. int x2 = point.x + iSelUnits;
  1977. int y1 = point.y - iSelUnits;
  1978. int y2 = point.y + iSelUnits;
  1979. Vector vecMins;
  1980. Vector vecMaxs;
  1981. GetRender2DBox(vecMins, vecMaxs);
  1982. Vector2D vecClientMins;
  1983. Vector2D vecClientMaxs;
  1984. pView->WorldToClient(vecClientMins, vecMins);
  1985. pView->WorldToClient(vecClientMaxs, vecMaxs);
  1986. Vector2D vecEdges[4] =
  1987. {
  1988. Vector2D(vecClientMins.x, vecClientMins.y),
  1989. Vector2D(vecClientMaxs.x, vecClientMins.y),
  1990. Vector2D(vecClientMaxs.x, vecClientMaxs.y),
  1991. Vector2D(vecClientMins.x, vecClientMaxs.y),
  1992. };
  1993. for (int i = 0; i < 4; i++)
  1994. {
  1995. if (IsLineInside(vecEdges[i], vecEdges[(i + 1) % 4], x1, y1, x2, y2))
  1996. {
  1997. HitData.uData = i+1;
  1998. return true;
  1999. }
  2000. }
  2001. }
  2002. return false;
  2003. }
  2004. //-----------------------------------------------------------------------------
  2005. // Hit test for the logical view
  2006. //-----------------------------------------------------------------------------
  2007. bool CMapEntity::HitTestLogical( CMapViewLogical *pView, const Vector2D &vecPoint, HitInfo_t &hitData )
  2008. {
  2009. if ( !IsVisible() || !IsLogical() || !IsVisibleLogical() )
  2010. return false;
  2011. if ( BaseClass::HitTestLogical( pView, vecPoint, hitData ) )
  2012. return true;
  2013. // Is the point inside the box?
  2014. Vector2D vecMins;
  2015. Vector2D vecMaxs;
  2016. GetRenderLogicalBox( vecMins, vecMaxs );
  2017. Vector2D vecClientMins;
  2018. Vector2D vecClientMaxs;
  2019. pView->WorldToClient(vecClientMins, vecMins);
  2020. pView->WorldToClient(vecClientMaxs, vecMaxs);
  2021. NormalizeBox( vecClientMins, vecClientMaxs );
  2022. if ( IsPointInside( vecPoint, vecClientMins, vecClientMaxs ) )
  2023. {
  2024. hitData.pObject = this;
  2025. hitData.uData = 0;
  2026. hitData.nDepth = 0.0f;
  2027. return true;
  2028. }
  2029. return false;
  2030. }
  2031. //-----------------------------------------------------------------------------
  2032. // Is this logical?
  2033. //-----------------------------------------------------------------------------
  2034. bool CMapEntity::IsLogical(void)
  2035. {
  2036. GDclass *pClass = GetClass();
  2037. return pClass && (( pClass->GetInputCount() > 0 ) || ( pClass->GetOutputCount() > 0 )) || (m_Connections.Count() || m_Upstream.Count());
  2038. }
  2039. //-----------------------------------------------------------------------------
  2040. // Is it visible in the logical view?
  2041. //-----------------------------------------------------------------------------
  2042. bool CMapEntity::IsVisibleLogical(void)
  2043. {
  2044. return IsVisible();
  2045. }
  2046. //-----------------------------------------------------------------------------
  2047. // Purpose: Returns true if this entity's name matches the given name, considering
  2048. // wildcards.
  2049. // Input : szName -
  2050. //-----------------------------------------------------------------------------
  2051. bool CMapEntity::NameMatches(const char *szName) const
  2052. {
  2053. const char *pszTargetName = GetKeyValue( "targetname" );
  2054. if (pszTargetName)
  2055. {
  2056. return !CompareEntityNames(pszTargetName, szName);
  2057. }
  2058. return false;
  2059. }
  2060. //-----------------------------------------------------------------------------
  2061. // Purpose: Returns true if this entity's classname matches the given name, considering
  2062. // wildcards.
  2063. // Input : szName -
  2064. //-----------------------------------------------------------------------------
  2065. bool CMapEntity::ClassNameMatches(const char *szName) const
  2066. {
  2067. const char *pszClassName = GetClassName();
  2068. if (pszClassName)
  2069. {
  2070. return !CompareEntityNames(pszClassName, szName);
  2071. }
  2072. return false;
  2073. }