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.

2418 lines
68 KiB

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