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.

1772 lines
46 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================//
  6. #include "stdafx.h"
  7. #include "GameConfig.h"
  8. #include "GlobalFunctions.h"
  9. #include "History.h"
  10. #include "MainFrm.h"
  11. #include "MapCheckDlg.h"
  12. #include "MapDoc.h"
  13. #include "MapEntity.h"
  14. #include "MapSolid.h"
  15. #include "MapWorld.h"
  16. #include "Options.h"
  17. #include "ToolManager.h"
  18. #include "VisGroup.h"
  19. #include "hammer.h"
  20. #include "MapOverlay.h"
  21. #include "Selection.h"
  22. // memdbgon must be the last include file in a .cpp file!!!
  23. #include <tier0/memdbgon.h>
  24. // ********
  25. // NOTE: Make sure the order matches g_MapErrorStringIDs below!
  26. // ********
  27. typedef enum
  28. {
  29. ErrorNoPlayerStart,
  30. ErrorMixedFace,
  31. ErrorDuplicatePlanes,
  32. ErrorMissingTarget,
  33. ErrorInvalidTexture,
  34. ErrorSolidStructure,
  35. ErrorUnusedKeyvalues,
  36. ErrorEmptyEntity,
  37. ErrorDuplicateKeys,
  38. ErrorSolidContents,
  39. ErrorInvalidTextureAxes,
  40. ErrorDuplicateFaceIDs,
  41. ErrorDuplicateNodeIDs,
  42. ErrorBadConnections,
  43. ErrorHiddenGroupHiddenChildren,
  44. ErrorHiddenGroupVisibleChildren,
  45. ErrorHiddenGroupMixedChildren,
  46. ErrorHiddenObjectNoVisGroup,
  47. ErrorHiddenChildOfEntity,
  48. ErrorIllegallyHiddenObject,
  49. ErrorKillInputRaceCondition,
  50. ErrorOverlayFaceList,
  51. } MapErrorType;
  52. // ********
  53. // NOTE: Make sure the order matches MapErrorType above!
  54. // ********
  55. struct
  56. {
  57. int m_StrResourceID;
  58. int m_DescriptionResourceID;
  59. } g_MapErrorStrings[] =
  60. {
  61. {IDS_NOPLAYERSTART, IDS_NOPLAYERSTART_DESC},
  62. {IDS_MIXEDFACES, IDS_MIXEDFACES_DESC},
  63. {IDS_DUPLICATEPLANES, IDS_DUPLICATEPLANES_DESC},
  64. {IDS_UNMATCHEDTARGET, IDS_UNMATCHEDTARGET_DESC},
  65. {IDS_INVALIDTEXTURE, IDS_INVALIDTEXTURE_DESC},
  66. {IDS_SOLIDSTRUCTURE, IDS_SOLIDSTRUCTURE_DESC},
  67. {IDS_UNUSEDKEYVALUES, IDS_UNUSEDKEYVALUES_DESC},
  68. {IDS_EMPTYENTITY, IDS_EMPTYENTITY_DESC},
  69. {IDS_DUPLICATEKEYS, IDS_DUPLICATEKEYS_DESC},
  70. {IDS_SOLIDCONTENT, IDS_SOLIDCONTENT_DESC},
  71. {IDS_INVALIDTEXTUREAXES, IDS_INVALIDTEXTUREAXES_DESC},
  72. {IDS_DUPLICATEFACEID, IDS_DUPLICATEFACEID_DESC},
  73. {IDS_DUPLICATE_NODE_ID, IDS_DUPLICATE_NODE_ID_DESC},
  74. {IDS_BAD_CONNECTIONS, IDS_BAD_CONNECTIONS_DESC},
  75. {IDS_HIDDEN_GROUP_HIDDEN_CHILDREN, IDS_HIDDEN_GROUP_HIDDEN_CHILDREN_DESC},
  76. {IDS_HIDDEN_GROUP_VISIBLE_CHILDREN, IDS_HIDDEN_GROUP_VISIBLE_CHILDREN_DESC},
  77. {IDS_HIDDEN_GROUP_MIXED_CHILDREN, IDS_HIDDEN_GROUP_MIXED_CHILDREN_DESC},
  78. {IDS_HIDDEN_NO_VISGROUP, IDS_HIDDEN_NO_VISGROUP_DESC},
  79. {IDS_HIDDEN_CHILD_OF_ENTITY, IDS_HIDDEN_CHILD_OF_ENTITY_DESC},
  80. {IDS_HIDDEN_ILLEGALLY, IDS_HIDDEN_ILLEGALLY_DESC},
  81. {IDS_KILL_INPUT_RACE_CONDITION, IDS_KILL_INPUT_RACE_CONDITION_DESC},
  82. {IDS_BAD_OVERLAY, IDS_DAB_OVERLAY_DESC}
  83. };
  84. typedef enum
  85. {
  86. CantFix,
  87. NeedsFix,
  88. Fixed,
  89. } FIXCODE;
  90. struct MapError
  91. {
  92. CMapClass *pObjects[3];
  93. MapErrorType Type;
  94. DWORD dwExtra;
  95. FIXCODE Fix;
  96. };
  97. //
  98. // Fix functions.
  99. //
  100. static void FixDuplicatePlanes(MapError *pError);
  101. static void FixSolidStructure(MapError *pError);
  102. static void FixInvalidTexture(MapError *pError);
  103. static void FixInvalidTextureAxes(MapError *pError);
  104. static void FixUnusedKeyvalues(MapError *pError);
  105. static void FixEmptyEntity(MapError *pError);
  106. static void FixBadConnections(MapError *pError);
  107. static void FixInvalidContents(MapError *pError);
  108. static void FixDuplicateFaceIDs(MapError *pError);
  109. static void FixDuplicateNodeIDs(MapError *pError);
  110. static void FixMissingTarget(MapError *pError);
  111. void FixHiddenObject(MapError *pError);
  112. static void FixKillInputRaceCondition(MapError *pError);
  113. static void FixOverlayFaceList(MapError *pError);
  114. CMapCheckDlg *s_pDlg = NULL;
  115. BEGIN_MESSAGE_MAP(CMapCheckDlg, CDialog)
  116. //{{AFX_MSG_MAP(CMapCheckDlg)
  117. ON_BN_CLICKED(IDC_GO, OnGo)
  118. ON_LBN_SELCHANGE(IDC_ERRORS, OnSelchangeErrors)
  119. ON_LBN_DBLCLK(IDC_ERRORS, OnDblClkErrors)
  120. ON_WM_PAINT()
  121. ON_BN_CLICKED(IDC_FIX, OnFix)
  122. ON_BN_CLICKED(IDC_FIXALL, OnFixall)
  123. ON_WM_DESTROY()
  124. ON_WM_CLOSE()
  125. ON_BN_CLICKED(IDC_CHECK_VISIBLE_ONLY, OnCheckVisibleOnly)
  126. //}}AFX_MSG_MAP
  127. END_MESSAGE_MAP()
  128. //-----------------------------------------------------------------------------
  129. // Visibility check
  130. //-----------------------------------------------------------------------------
  131. inline bool IsCheckVisible( CMapClass *pClass )
  132. {
  133. return (Options.general.bCheckVisibleMapErrors == FALSE) || pClass->IsVisible();
  134. }
  135. //-----------------------------------------------------------------------------
  136. // Purpose:
  137. //-----------------------------------------------------------------------------
  138. void CMapCheckDlg::CheckForProblems(CWnd *pwndParent)
  139. {
  140. if (!s_pDlg)
  141. {
  142. s_pDlg = new CMapCheckDlg;
  143. s_pDlg->Create(IDD, pwndParent);
  144. }
  145. if (!s_pDlg->DoCheck())
  146. {
  147. // Found problems.
  148. s_pDlg->ShowWindow(SW_SHOW);
  149. }
  150. }
  151. //-----------------------------------------------------------------------------
  152. // Purpose:
  153. // Input : pParent -
  154. //-----------------------------------------------------------------------------
  155. CMapCheckDlg::CMapCheckDlg(CWnd *pParent)
  156. : CDialog(CMapCheckDlg::IDD, pParent)
  157. {
  158. //{{AFX_DATA_INIT(CMapCheckDlg)
  159. m_bCheckVisible = FALSE;
  160. //}}AFX_DATA_INIT
  161. m_bCheckVisible = Options.general.bCheckVisibleMapErrors;
  162. }
  163. //-----------------------------------------------------------------------------
  164. // Purpose:
  165. // Input : pDX -
  166. //-----------------------------------------------------------------------------
  167. void CMapCheckDlg::DoDataExchange(CDataExchange* pDX)
  168. {
  169. CDialog::DoDataExchange(pDX);
  170. //{{AFX_DATA_MAP(CMapCheckDlg)
  171. DDX_Control(pDX, IDC_FIXALL, m_cFixAll);
  172. DDX_Control(pDX, IDC_FIX, m_Fix);
  173. DDX_Control(pDX, IDC_GO, m_Go);
  174. DDX_Control(pDX, IDC_DESCRIPTION, m_Description);
  175. DDX_Control(pDX, IDC_ERRORS, m_Errors);
  176. DDX_Check(pDX, IDC_CHECK_VISIBLE_ONLY, m_bCheckVisible);
  177. //}}AFX_DATA_MAP
  178. if ( pDX->m_bSaveAndValidate )
  179. {
  180. Options.general.bCheckVisibleMapErrors = m_bCheckVisible;
  181. }
  182. }
  183. //-----------------------------------------------------------------------------
  184. // Checkbox indicating whether we should check visible errors
  185. //-----------------------------------------------------------------------------
  186. void CMapCheckDlg::OnCheckVisibleOnly()
  187. {
  188. UpdateData( TRUE );
  189. DoCheck();
  190. }
  191. //-----------------------------------------------------------------------------
  192. // Purpose: Selects the current error objects and centers the views on it.
  193. //-----------------------------------------------------------------------------
  194. void CMapCheckDlg::OnGo()
  195. {
  196. GotoSelectedErrors();
  197. }
  198. //-----------------------------------------------------------------------------
  199. // Purpose:
  200. //-----------------------------------------------------------------------------
  201. void CMapCheckDlg::GotoSelectedErrors()
  202. {
  203. int iSel = m_Errors.GetCurSel();
  204. if (iSel == LB_ERR)
  205. {
  206. return;
  207. }
  208. ToolManager()->SetTool(TOOL_POINTER);
  209. CMapObjectList Objects;
  210. for (int i = 0; i < m_Errors.GetCount(); i++)
  211. {
  212. if (m_Errors.GetSel(i) > 0)
  213. {
  214. MapError *pError = (MapError *)m_Errors.GetItemDataPtr(i);
  215. if (pError)
  216. {
  217. Objects.AddToTail(pError->pObjects[0]);
  218. }
  219. }
  220. }
  221. CMapDoc *pDoc = CMapDoc::GetActiveMapDoc();
  222. pDoc->SelectObjectList(&Objects);
  223. pDoc->CenterViewsOnSelection();
  224. }
  225. //-----------------------------------------------------------------------------
  226. // Purpose: Fixes all the selected errors.
  227. //-----------------------------------------------------------------------------
  228. void CMapCheckDlg::OnFix()
  229. {
  230. int iSel = m_Errors.GetCurSel();
  231. if (iSel == LB_ERR)
  232. {
  233. return;
  234. }
  235. UpdateBox ub;
  236. CMapObjectList Objects;
  237. ub.Objects = &Objects;
  238. for (int i = 0; i < m_Errors.GetCount(); i++)
  239. {
  240. if (m_Errors.GetSel(i) > 0)
  241. {
  242. MapError *pError = (MapError *)m_Errors.GetItemDataPtr(i);
  243. if (pError)
  244. {
  245. Fix(pError, ub);
  246. }
  247. }
  248. }
  249. OnSelchangeErrors();
  250. CMapDoc::GetActiveMapDoc()->UpdateAllViews( MAPVIEW_UPDATE_OBJECTS );
  251. }
  252. //-----------------------------------------------------------------------------
  253. // Purpose:
  254. // Input : pError -
  255. // ub -
  256. //-----------------------------------------------------------------------------
  257. void CMapCheckDlg::Fix(MapError *pError, UpdateBox &ub)
  258. {
  259. CMapDoc::GetActiveMapDoc()->SetModifiedFlag();
  260. if (pError->Fix != NeedsFix)
  261. {
  262. // should never get here because this button is supposed
  263. // to be disabled if the error cannot be fixed
  264. return;
  265. }
  266. //
  267. // Expand the bounds of the update region to include the broken objects.
  268. //
  269. for (int i = 0; i < 2; i++)
  270. {
  271. if (!pError->pObjects[i])
  272. {
  273. continue;
  274. }
  275. ub.Objects->AddToTail(pError->pObjects[i]);
  276. Vector mins;
  277. Vector maxs;
  278. pError->pObjects[i]->GetRender2DBox(mins, maxs);
  279. ub.Box.UpdateBounds(mins, maxs);
  280. }
  281. //
  282. // Perform the fix.
  283. //
  284. switch (pError->Type)
  285. {
  286. case ErrorDuplicatePlanes:
  287. {
  288. FixDuplicatePlanes(pError);
  289. break;
  290. }
  291. case ErrorDuplicateFaceIDs:
  292. {
  293. FixDuplicatePlanes(pError);
  294. break;
  295. }
  296. case ErrorDuplicateNodeIDs:
  297. {
  298. FixDuplicateNodeIDs(pError);
  299. break;
  300. }
  301. case ErrorMissingTarget:
  302. {
  303. FixMissingTarget(pError);
  304. break;
  305. }
  306. case ErrorSolidStructure:
  307. {
  308. FixSolidStructure(pError);
  309. break;
  310. }
  311. case ErrorSolidContents:
  312. {
  313. FixInvalidContents(pError);
  314. break;
  315. }
  316. case ErrorInvalidTexture:
  317. {
  318. FixInvalidTexture(pError);
  319. break;
  320. }
  321. case ErrorInvalidTextureAxes:
  322. {
  323. FixInvalidTextureAxes(pError);
  324. break;
  325. }
  326. case ErrorUnusedKeyvalues:
  327. {
  328. FixUnusedKeyvalues(pError);
  329. break;
  330. }
  331. case ErrorBadConnections:
  332. {
  333. FixBadConnections(pError);
  334. break;
  335. }
  336. case ErrorEmptyEntity:
  337. {
  338. FixEmptyEntity(pError);
  339. break;
  340. }
  341. case ErrorHiddenGroupVisibleChildren:
  342. case ErrorHiddenGroupMixedChildren:
  343. case ErrorHiddenGroupHiddenChildren:
  344. case ErrorHiddenObjectNoVisGroup:
  345. case ErrorHiddenChildOfEntity:
  346. case ErrorIllegallyHiddenObject:
  347. {
  348. FixHiddenObject(pError);
  349. break;
  350. }
  351. case ErrorKillInputRaceCondition:
  352. {
  353. FixKillInputRaceCondition(pError);
  354. break;
  355. }
  356. case ErrorOverlayFaceList:
  357. {
  358. FixOverlayFaceList( pError );
  359. break;
  360. }
  361. }
  362. pError->Fix = Fixed;
  363. //
  364. // Expand the bounds of the update region to include the fixed objects.
  365. //
  366. for (int i = 0; i < 2; i++)
  367. {
  368. if (!pError->pObjects[i])
  369. {
  370. continue;
  371. }
  372. Vector mins;
  373. Vector maxs;
  374. pError->pObjects[i]->GetRender2DBox(mins, maxs);
  375. ub.Box.UpdateBounds(mins, maxs);
  376. }
  377. }
  378. //-----------------------------------------------------------------------------
  379. // Purpose:
  380. //-----------------------------------------------------------------------------
  381. void CMapCheckDlg::OnFixall()
  382. {
  383. int iSel = m_Errors.GetCurSel();
  384. if (iSel == LB_ERR)
  385. {
  386. return;
  387. }
  388. MapError *pError = (MapError *) m_Errors.GetItemDataPtr(iSel);
  389. if (pError->Fix == CantFix)
  390. {
  391. // should never get here because this button is supposed
  392. // to be disabled if the error cannot be fixed
  393. return;
  394. }
  395. UpdateBox ub;
  396. CMapObjectList Objects;
  397. ub.Objects = &Objects;
  398. // For every selected error...
  399. for (int i = 0; i < m_Errors.GetCount(); i++)
  400. {
  401. if (m_Errors.GetSel(i) > 0)
  402. {
  403. pError = (MapError *)m_Errors.GetItemDataPtr(i);
  404. if ((pError) && (pError->Fix == NeedsFix))
  405. {
  406. // Find and fix every error of the same type.
  407. for (int j = 0; j < m_Errors.GetCount(); j++)
  408. {
  409. MapError *pError2 = (MapError *)m_Errors.GetItemDataPtr(j);
  410. if ((pError2->Type != pError->Type) || (pError2->Fix != NeedsFix))
  411. {
  412. continue;
  413. }
  414. Fix(pError2, ub);
  415. }
  416. }
  417. }
  418. }
  419. OnSelchangeErrors();
  420. CMapDoc::GetActiveMapDoc()->UpdateAllViews( MAPVIEW_UPDATE_OBJECTS );
  421. }
  422. //-----------------------------------------------------------------------------
  423. // Purpose:
  424. //-----------------------------------------------------------------------------
  425. void CMapCheckDlg::OnSelchangeErrors()
  426. {
  427. // change description to match error
  428. int iSel = m_Errors.GetCurSel();
  429. if(iSel == LB_ERR)
  430. {
  431. m_Fix.EnableWindow(FALSE);
  432. m_cFixAll.EnableWindow(FALSE);
  433. m_Go.EnableWindow(FALSE);
  434. }
  435. CString str;
  436. MapError *pError;
  437. pError = (MapError*) m_Errors.GetItemDataPtr(iSel);
  438. // Figure out which error string we're using.
  439. int iErrorStr = (int)pError->Type;
  440. iErrorStr = clamp( iErrorStr, 0, ARRAYSIZE( g_MapErrorStrings ) - 1 );
  441. Assert( iErrorStr == (int)pError->Type );
  442. str.LoadString(g_MapErrorStrings[iErrorStr].m_DescriptionResourceID);
  443. m_Description.SetWindowText(str);
  444. m_Go.EnableWindow(pError->pObjects[0] != NULL);
  445. // set state of fix button
  446. m_Fix.EnableWindow(pError->Fix == NeedsFix);
  447. m_cFixAll.EnableWindow(pError->Fix != CantFix);
  448. // set text of fix button
  449. switch (pError->Fix)
  450. {
  451. case NeedsFix:
  452. m_Fix.SetWindowText("&Fix");
  453. break;
  454. case CantFix:
  455. m_Fix.SetWindowText("Can't fix");
  456. break;
  457. case Fixed:
  458. m_Fix.SetWindowText("(fixed)");
  459. break;
  460. }
  461. CMapDoc *pDoc = CMapDoc::GetActiveMapDoc();
  462. pDoc->GetSelection()->SetMode(selectObjects);
  463. if (pError->pObjects[0])
  464. {
  465. pDoc->SelectObject(pError->pObjects[0], scClear|scSelect|scSaveChanges );
  466. }
  467. else
  468. {
  469. pDoc->SelectObject(NULL, scClear|scSaveChanges );
  470. }
  471. }
  472. //-----------------------------------------------------------------------------
  473. // Purpose:
  474. //-----------------------------------------------------------------------------
  475. void CMapCheckDlg::OnDblClkErrors()
  476. {
  477. GotoSelectedErrors();
  478. }
  479. //-----------------------------------------------------------------------------
  480. // Purpose:
  481. //-----------------------------------------------------------------------------
  482. void CMapCheckDlg::OnPaint()
  483. {
  484. CPaintDC dc(this); // device context for painting
  485. }
  486. //-----------------------------------------------------------------------------
  487. // Purpose:
  488. //-----------------------------------------------------------------------------
  489. void CMapCheckDlg::KillErrorList()
  490. {
  491. // delete items in list.. their data ptrs are allocated objects
  492. int iSize = m_Errors.GetCount();
  493. for(int i = 0; i < iSize; i++)
  494. {
  495. MapError *pError = (MapError*) m_Errors.GetItemDataPtr(i);
  496. delete pError;
  497. }
  498. m_Errors.ResetContent();
  499. }
  500. //-----------------------------------------------------------------------------
  501. // Purpose: Builds the listbox string for the given error and adds it to the list.
  502. //-----------------------------------------------------------------------------
  503. static void AddErrorToListBox(CListBox *pList, MapError *pError)
  504. {
  505. CString str;
  506. // Figure out which error string we're using.
  507. int iErrorStr = (int)pError->Type;
  508. iErrorStr = clamp( iErrorStr, 0, ARRAYSIZE( g_MapErrorStrings ) - 1 );
  509. Assert( iErrorStr == (int)pError->Type );
  510. str.LoadString(g_MapErrorStrings[iErrorStr].m_StrResourceID);
  511. if (str.Find('%') != -1)
  512. {
  513. if (pError->Type == ErrorUnusedKeyvalues)
  514. {
  515. // dwExtra has the name of the string in it
  516. CString str2 = str;
  517. CMapEntity *pEntity = (CMapEntity *)pError->pObjects[0];
  518. str.Format(str2, pEntity->GetClassName(), pError->dwExtra);
  519. }
  520. else
  521. {
  522. CString str2 = str;
  523. str.Format(str2, pError->dwExtra);
  524. }
  525. }
  526. int iIndex = pList->AddString(str);
  527. pList->SetItemDataPtr(iIndex, (PVOID)pError);
  528. }
  529. //-----------------------------------------------------------------------------
  530. // Purpose:
  531. // Input : pList -
  532. // Type -
  533. // dwExtra -
  534. // ... -
  535. //-----------------------------------------------------------------------------
  536. static void AddError(CListBox *pList, MapErrorType Type, DWORD dwExtra, ...)
  537. {
  538. MapError *pError = new MapError;
  539. memset(pError, 0, sizeof(MapError));
  540. pError->Type = Type;
  541. pError->dwExtra = dwExtra;
  542. pError->Fix = CantFix;
  543. va_list vl;
  544. va_start(vl, dwExtra);
  545. //
  546. // Get the object pointer from the variable argument list.
  547. //
  548. switch (Type)
  549. {
  550. case ErrorNoPlayerStart:
  551. {
  552. // no objects.
  553. break;
  554. }
  555. case ErrorMixedFace:
  556. case ErrorMissingTarget:
  557. case ErrorDuplicatePlanes:
  558. case ErrorDuplicateFaceIDs:
  559. case ErrorDuplicateNodeIDs:
  560. case ErrorSolidStructure:
  561. case ErrorSolidContents:
  562. case ErrorInvalidTexture:
  563. case ErrorUnusedKeyvalues:
  564. case ErrorBadConnections:
  565. case ErrorEmptyEntity:
  566. case ErrorDuplicateKeys:
  567. case ErrorInvalidTextureAxes:
  568. case ErrorHiddenGroupHiddenChildren:
  569. case ErrorHiddenGroupVisibleChildren:
  570. case ErrorHiddenGroupMixedChildren:
  571. case ErrorHiddenObjectNoVisGroup:
  572. case ErrorHiddenChildOfEntity:
  573. case ErrorIllegallyHiddenObject:
  574. case ErrorOverlayFaceList:
  575. {
  576. pError->pObjects[0] = va_arg(vl, CMapClass *);
  577. break;
  578. }
  579. case ErrorKillInputRaceCondition:
  580. {
  581. pError->pObjects[0] = va_arg(vl, CMapClass *);
  582. pError->dwExtra = (DWORD)va_arg(vl, CEntityConnection *);
  583. break;
  584. }
  585. }
  586. //
  587. // Set the can fix flag.
  588. //
  589. switch (Type)
  590. {
  591. case ErrorSolidContents:
  592. case ErrorDuplicatePlanes:
  593. case ErrorDuplicateFaceIDs:
  594. case ErrorDuplicateNodeIDs:
  595. case ErrorSolidStructure:
  596. case ErrorInvalidTexture:
  597. case ErrorUnusedKeyvalues:
  598. case ErrorMissingTarget:
  599. case ErrorBadConnections:
  600. case ErrorEmptyEntity:
  601. case ErrorDuplicateKeys:
  602. case ErrorInvalidTextureAxes:
  603. case ErrorHiddenGroupHiddenChildren:
  604. case ErrorHiddenGroupVisibleChildren:
  605. case ErrorHiddenGroupMixedChildren:
  606. case ErrorHiddenObjectNoVisGroup:
  607. case ErrorHiddenChildOfEntity:
  608. case ErrorIllegallyHiddenObject:
  609. case ErrorKillInputRaceCondition:
  610. case ErrorOverlayFaceList:
  611. {
  612. pError->Fix = NeedsFix;
  613. break;
  614. }
  615. }
  616. va_end(vl);
  617. AddErrorToListBox(pList, pError);
  618. }
  619. //-----------------------------------------------------------------------------
  620. // Purpose:
  621. // Input : pObject -
  622. // DWORD -
  623. // Output :
  624. //-----------------------------------------------------------------------------
  625. static BOOL FindPlayer(CMapEntity *pObject, DWORD)
  626. {
  627. if ( !IsCheckVisible( pObject ) )
  628. return TRUE;
  629. if (pObject->IsPlaceholder() && pObject->IsClass("info_player_start"))
  630. {
  631. return(FALSE);
  632. }
  633. return(TRUE);
  634. }
  635. //-----------------------------------------------------------------------------
  636. // Purpose:
  637. // Input : pList -
  638. // pWorld -
  639. //-----------------------------------------------------------------------------
  640. static void CheckRequirements(CListBox *pList, CMapWorld *pWorld)
  641. {
  642. // ensure there's a player start ..
  643. if (pWorld->EnumChildren((ENUMMAPCHILDRENPROC)FindPlayer, 0, MAPCLASS_TYPE(CMapEntity)))
  644. {
  645. // if rvl is !0, it was not stopped prematurely.. which means there is
  646. // NO player start.
  647. AddError(pList, ErrorNoPlayerStart, 0);
  648. }
  649. }
  650. //-----------------------------------------------------------------------------
  651. // Purpose:
  652. // Input : pSolid -
  653. // pList -
  654. // Output :
  655. //-----------------------------------------------------------------------------
  656. static BOOL _CheckMixedFaces(CMapSolid *pSolid, CListBox *pList)
  657. {
  658. if ( !IsCheckVisible( pSolid ) )
  659. return TRUE;
  660. // run thru faces..
  661. int iFaces = pSolid->GetFaceCount();
  662. int iSolid = 2; // start off ambivalent
  663. int i;
  664. for(i = 0; i < iFaces; i++)
  665. {
  666. CMapFace *pFace = pSolid->GetFace(i);
  667. char ch = pFace->texture.texture[0];
  668. if((ch == '*' && iSolid == 1) || (ch != '*' && iSolid == 0))
  669. {
  670. break;
  671. }
  672. else iSolid = (ch == '*') ? 0 : 1;
  673. }
  674. if(i == iFaces) // all ok
  675. return TRUE;
  676. // NOT ok
  677. AddError(pList, ErrorMixedFace, 0, pSolid);
  678. return TRUE;
  679. }
  680. static void CheckMixedFaces(CListBox *pList, CMapWorld *pWorld)
  681. {
  682. pWorld->EnumChildren((ENUMMAPCHILDRENPROC)_CheckMixedFaces, (DWORD)pList, MAPCLASS_TYPE(CMapSolid));
  683. }
  684. //-----------------------------------------------------------------------------
  685. // Purpose: Returns true if there is another node entity in the world with the
  686. // same node ID as the given entity.
  687. // Input : pNode -
  688. // pWorld -
  689. //-----------------------------------------------------------------------------
  690. bool FindDuplicateNodeID(CMapEntity *pNode, CMapWorld *pWorld)
  691. {
  692. if ( !IsCheckVisible( pNode ) )
  693. return false;
  694. EnumChildrenPos_t pos;
  695. CMapClass *pChild = pWorld->GetFirstDescendent(pos);
  696. while (pChild != NULL)
  697. {
  698. CMapEntity *pEntity = dynamic_cast<CMapEntity *>(pChild);
  699. if (pEntity && IsCheckVisible( pEntity ) && (pEntity != pNode) && pEntity->IsNodeClass())
  700. {
  701. int nNodeID1 = pNode->GetNodeID();
  702. int nNodeID2 = pEntity->GetNodeID();
  703. if ((nNodeID1 != 0) && (nNodeID2 != 0) && (nNodeID1 == nNodeID2))
  704. {
  705. return true;
  706. }
  707. }
  708. pChild = pWorld->GetNextDescendent(pos);
  709. }
  710. return false;
  711. }
  712. //-----------------------------------------------------------------------------
  713. // Purpose: Checks for node entities with the same node ID.
  714. //-----------------------------------------------------------------------------
  715. static void CheckDuplicateNodeIDs(CListBox *pList, CMapWorld *pWorld)
  716. {
  717. EnumChildrenPos_t pos;
  718. CMapClass *pChild = pWorld->GetFirstDescendent(pos);
  719. while (pChild != NULL)
  720. {
  721. CMapEntity *pEntity = dynamic_cast<CMapEntity *>(pChild);
  722. if (pEntity && pEntity->IsNodeClass())
  723. {
  724. if (FindDuplicateNodeID(pEntity, pWorld))
  725. {
  726. AddError(pList, ErrorDuplicateNodeIDs, (DWORD)pWorld, pEntity);
  727. }
  728. }
  729. pChild = pWorld->GetNextDescendent(pos);
  730. }
  731. }
  732. //-----------------------------------------------------------------------------
  733. // Purpose: Checks for faces with identical face normals in this solid object.
  734. // Input : pSolid - Solid to check for duplicate faces.
  735. // Output : Returns TRUE if the face contains at least one duplicate face,
  736. // FALSE if the solid contains no duplicate faces.
  737. //-----------------------------------------------------------------------------
  738. BOOL DoesContainDuplicates(CMapSolid *pSolid)
  739. {
  740. int iFaces = pSolid->GetFaceCount();
  741. for (int i = 0; i < iFaces; i++)
  742. {
  743. CMapFace *pFace = pSolid->GetFace(i);
  744. Vector& pts1 = pFace->plane.normal;
  745. for (int j = 0; j < iFaces; j++)
  746. {
  747. // Don't check self.
  748. if (j == i)
  749. {
  750. continue;
  751. }
  752. CMapFace *pFace2 = pSolid->GetFace(j);
  753. Vector& pts2 = pFace2->plane.normal;
  754. if (pts1 == pts2)
  755. {
  756. return(TRUE);
  757. }
  758. }
  759. }
  760. return(FALSE);
  761. }
  762. //-----------------------------------------------------------------------------
  763. // Purpose:
  764. // Input : pSolid -
  765. // pList -
  766. // Output :
  767. //-----------------------------------------------------------------------------
  768. static BOOL _CheckDuplicatePlanes(CMapSolid *pSolid, CListBox *pList)
  769. {
  770. if ( !IsCheckVisible( pSolid ) )
  771. return TRUE;
  772. if (DoesContainDuplicates(pSolid))
  773. {
  774. AddError(pList, ErrorDuplicatePlanes, 0, pSolid);
  775. }
  776. return(TRUE);
  777. }
  778. static void CheckDuplicatePlanes(CListBox *pList, CMapWorld *pWorld)
  779. {
  780. pWorld->EnumChildren((ENUMMAPCHILDRENPROC)_CheckDuplicatePlanes, (DWORD)pList, MAPCLASS_TYPE(CMapSolid));
  781. }
  782. struct FindDuplicateFaceIDs_t
  783. {
  784. CMapFaceList All; // Collects all the face IDs in this map.
  785. CMapFaceList Duplicates; // Collects the duplicate face IDs in this map.
  786. };
  787. //-----------------------------------------------------------------------------
  788. // Purpose:
  789. // Input : pSolid -
  790. // pData -
  791. // Output : Returns TRUE to continue enumerating.
  792. //-----------------------------------------------------------------------------
  793. static BOOL _CheckDuplicateFaceIDs(CMapSolid *pSolid, FindDuplicateFaceIDs_t *pData)
  794. {
  795. if ( !IsCheckVisible( pSolid ) )
  796. return TRUE;
  797. int nFaceCount = pSolid->GetFaceCount();
  798. for (int i = 0; i < nFaceCount; i++)
  799. {
  800. CMapFace *pFace = pSolid->GetFace(i);
  801. if (pData->All.FindFaceID(pFace->GetFaceID()) != -1)
  802. {
  803. if (pData->Duplicates.FindFaceID(pFace->GetFaceID()) != -1)
  804. {
  805. pData->Duplicates.AddToTail(pFace);
  806. }
  807. }
  808. else
  809. {
  810. pData->All.AddToTail(pFace);
  811. }
  812. }
  813. return(TRUE);
  814. }
  815. //-----------------------------------------------------------------------------
  816. // Purpose: Reports errors for all faces with duplicate face IDs.
  817. // Input : pList -
  818. // pWorld -
  819. //-----------------------------------------------------------------------------
  820. static void CheckDuplicateFaceIDs(CListBox *pList, CMapWorld *pWorld)
  821. {
  822. FindDuplicateFaceIDs_t Lists;
  823. Lists.All.SetGrowSize(128);
  824. Lists.Duplicates.SetGrowSize(128);
  825. pWorld->EnumChildren((ENUMMAPCHILDRENPROC)_CheckDuplicateFaceIDs, (DWORD)&Lists, MAPCLASS_TYPE(CMapSolid));
  826. for (int i = 0; i < Lists.Duplicates.Count(); i++)
  827. {
  828. CMapFace *pFace = Lists.Duplicates.Element(i);
  829. AddError(pList, ErrorDuplicateFaceIDs, (DWORD)pFace, (CMapSolid *)pFace->GetParent());
  830. }
  831. }
  832. //-----------------------------------------------------------------------------
  833. // Checks if a particular target is valid.
  834. //-----------------------------------------------------------------------------
  835. static void CheckValidTarget(CMapEntity *pEntity, const char *pFieldName, const char *pTargetName, CListBox *pList, bool bCheckClassNames)
  836. {
  837. if (!pTargetName)
  838. return;
  839. // These procedural names are always assumed to exist.
  840. if (!stricmp(pTargetName, "!activator") || !stricmp(pTargetName, "!caller") || !stricmp(pTargetName, "!player") || !stricmp(pTargetName, "!self"))
  841. return;
  842. CMapDoc *pDoc = CMapDoc::GetActiveMapDoc();
  843. // Search by name first.
  844. CMapEntityList Found;
  845. bool bFound = pDoc->FindEntitiesByName(Found, pTargetName, (Options.general.bCheckVisibleMapErrors == TRUE));
  846. if (!bFound && bCheckClassNames)
  847. {
  848. // Not found, search by classname.
  849. bFound = pDoc->FindEntitiesByClassName(Found, pTargetName, (Options.general.bCheckVisibleMapErrors == TRUE));
  850. }
  851. if (!bFound)
  852. {
  853. // No dice, flag it as an error.
  854. AddError(pList, ErrorMissingTarget, (DWORD)pFieldName, pEntity);
  855. }
  856. }
  857. //-----------------------------------------------------------------------------
  858. // Purpose: Checks the given entity for references by name to nonexistent entities.
  859. // Input : pEntity -
  860. // pList -
  861. // Output : Returns TRUE to keep enumerating.
  862. //-----------------------------------------------------------------------------
  863. static BOOL _CheckMissingTargets(CMapEntity *pEntity, CListBox *pList)
  864. {
  865. if ( !IsCheckVisible( pEntity ) )
  866. return TRUE;
  867. GDclass *pClass = pEntity->GetClass();
  868. if (!pClass)
  869. {
  870. // Unknown class -- just check for target references.
  871. static char *pszTarget = "target";
  872. const char *pszValue = pEntity->GetKeyValue(pszTarget);
  873. CheckValidTarget(pEntity, pszTarget, pszValue, pList, false);
  874. }
  875. else
  876. {
  877. // Known class -- check all target_destination and target_name_or_class keyvalues.
  878. for (int i = 0; i < pClass->GetVariableCount(); i++)
  879. {
  880. GDinputvariable *pVar = pClass->GetVariableAt(i);
  881. if ((pVar->GetType() != ivTargetDest) && (pVar->GetType() != ivTargetNameOrClass))
  882. continue;
  883. const char *pszValue = pEntity->GetKeyValue(pVar->GetName());
  884. CheckValidTarget(pEntity, pVar->GetName(), pszValue, pList, (pVar->GetType() == ivTargetNameOrClass));
  885. }
  886. }
  887. return TRUE;
  888. }
  889. static void CheckMissingTargets(CListBox *pList, CMapWorld *pWorld)
  890. {
  891. pWorld->EnumChildren((ENUMMAPCHILDRENPROC)_CheckMissingTargets, (DWORD)pList, MAPCLASS_TYPE(CMapEntity));
  892. }
  893. //-----------------------------------------------------------------------------
  894. // Purpose: Determines whether a solid is good or bad.
  895. // Input : pSolid - Solid to check.
  896. // pList - List box into which to place errors.
  897. // Output : Always returns TRUE to continue enumerating.
  898. //-----------------------------------------------------------------------------
  899. static BOOL _CheckSolidIntegrity(CMapSolid *pSolid, CListBox *pList)
  900. {
  901. if ( !IsCheckVisible( pSolid ) )
  902. return TRUE;
  903. CCheckFaceInfo cfi;
  904. int nFaces = pSolid->GetFaceCount();
  905. for (int i = 0; i < nFaces; i++)
  906. {
  907. CMapFace *pFace = pSolid->GetFace(i);
  908. //
  909. // Reset the iPoint member so results from previous faces don't carry over.
  910. //
  911. cfi.iPoint = -1;
  912. //
  913. // Check the face.
  914. //
  915. if (!pFace->CheckFace(&cfi))
  916. {
  917. AddError(pList, ErrorSolidStructure, 0, pSolid);
  918. break;
  919. }
  920. }
  921. return(TRUE);
  922. }
  923. static void CheckSolidIntegrity(CListBox *pList, CMapWorld *pWorld)
  924. {
  925. pWorld->EnumChildren((ENUMMAPCHILDRENPROC)_CheckSolidIntegrity, (DWORD)pList, MAPCLASS_TYPE(CMapSolid));
  926. }
  927. //-----------------------------------------------------------------------------
  928. // Purpose:
  929. // Input : pSolid -
  930. // pList -
  931. // Output :
  932. //-----------------------------------------------------------------------------
  933. static BOOL _CheckSolidContents(CMapSolid *pSolid, CListBox *pList)
  934. {
  935. if ( !IsCheckVisible( pSolid ) )
  936. return TRUE;
  937. CCheckFaceInfo cfi;
  938. int nFaces = pSolid->GetFaceCount();
  939. CMapFace *pFace = pSolid->GetFace(0);
  940. DWORD dwContents = pFace->texture.q2contents;
  941. for (int i = 1; i < nFaces; i++)
  942. {
  943. pFace = pSolid->GetFace(i);
  944. if (pFace->texture.q2contents == dwContents)
  945. {
  946. continue;
  947. }
  948. AddError(pList, ErrorSolidContents, 0, pSolid);
  949. break;
  950. }
  951. return TRUE;
  952. }
  953. static void CheckSolidContents(CListBox *pList, CMapWorld *pWorld)
  954. {
  955. if (CMapDoc::GetActiveMapDoc() && CMapDoc::GetActiveMapDoc()->GetGame() && CMapDoc::GetActiveMapDoc()->GetGame()->mapformat == mfQuake2)
  956. {
  957. pWorld->EnumChildren((ENUMMAPCHILDRENPROC)_CheckSolidContents, (DWORD)pList, MAPCLASS_TYPE(CMapSolid));
  958. }
  959. }
  960. //-----------------------------------------------------------------------------
  961. // Purpose: Determines if there are any invalid textures or texture axes on any
  962. // face of this solid. Adds an error message to the list box for each
  963. // error found.
  964. // Input : pSolid - Solid to check.
  965. // pList - Pointer to the error list box.
  966. // Output : Returns TRUE.
  967. //-----------------------------------------------------------------------------
  968. static BOOL _CheckInvalidTextures(CMapSolid *pSolid, CListBox *pList)
  969. {
  970. if ( !IsCheckVisible( pSolid ) )
  971. return TRUE;
  972. int nFaces = pSolid->GetFaceCount();
  973. for(int i = 0; i < nFaces; i++)
  974. {
  975. const CMapFace *pFace = pSolid->GetFace(i);
  976. IEditorTexture *pTex = pFace->GetTexture();
  977. if (pTex->IsDummy())
  978. {
  979. AddError(pList, ErrorInvalidTexture, (DWORD)pFace->texture.texture, pSolid);
  980. return TRUE;
  981. }
  982. if (!pFace->IsTextureAxisValid())
  983. {
  984. AddError(pList, ErrorInvalidTextureAxes, i, pSolid);
  985. return(TRUE);
  986. }
  987. }
  988. return(TRUE);
  989. }
  990. static void CheckInvalidTextures(CListBox *pList, CMapWorld *pWorld)
  991. {
  992. pWorld->EnumChildren((ENUMMAPCHILDRENPROC)_CheckInvalidTextures, (DWORD)pList, MAPCLASS_TYPE(CMapSolid));
  993. }
  994. //-----------------------------------------------------------------------------
  995. // Purpose:
  996. // Input : pEntity -
  997. // pList -
  998. // Output :
  999. //-----------------------------------------------------------------------------
  1000. static BOOL _CheckUnusedKeyvalues(CMapEntity *pEntity, CListBox *pList)
  1001. {
  1002. if ( !IsCheckVisible( pEntity ) )
  1003. return TRUE;
  1004. if (!pEntity->IsClass() || pEntity->IsClass("multi_manager"))
  1005. {
  1006. return(TRUE); // can't check if no class associated
  1007. }
  1008. GDclass *pClass = pEntity->GetClass();
  1009. for (int i = pEntity->GetFirstKeyValue(); i != pEntity->GetInvalidKeyValue(); i=pEntity->GetNextKeyValue( i ) )
  1010. {
  1011. if (pClass->VarForName(pEntity->GetKey(i)) == NULL)
  1012. {
  1013. AddError(pList, ErrorUnusedKeyvalues, (DWORD)pEntity->GetKey(i), pEntity);
  1014. return(TRUE);
  1015. }
  1016. }
  1017. return(TRUE);
  1018. }
  1019. static void CheckUnusedKeyvalues(CListBox *pList, CMapWorld *pWorld)
  1020. {
  1021. pWorld->EnumChildren((ENUMMAPCHILDRENPROC)_CheckUnusedKeyvalues, (DWORD)pList, MAPCLASS_TYPE(CMapEntity));
  1022. }
  1023. //-----------------------------------------------------------------------------
  1024. // Purpose:
  1025. // Input : pEntity -
  1026. // pList -
  1027. // Output :
  1028. //-----------------------------------------------------------------------------
  1029. static BOOL _CheckEmptyEntities(CMapEntity *pEntity, CListBox *pList)
  1030. {
  1031. if ( !IsCheckVisible( pEntity ) )
  1032. return TRUE;
  1033. if(!pEntity->IsPlaceholder() && !pEntity->GetChildCount())
  1034. {
  1035. AddError(pList, ErrorEmptyEntity, (DWORD)pEntity->GetClassName(), pEntity);
  1036. }
  1037. return(TRUE);
  1038. }
  1039. static void CheckEmptyEntities(CListBox *pList, CMapWorld *pWorld)
  1040. {
  1041. pWorld->EnumChildren((ENUMMAPCHILDRENPROC)_CheckEmptyEntities, (DWORD)pList, MAPCLASS_TYPE(CMapEntity));
  1042. }
  1043. //-----------------------------------------------------------------------------
  1044. // Purpose: Checks the entity for bad I/O connections.
  1045. // Input : pEntity - the entity to check
  1046. // pList - list box that tracks the errors
  1047. // Output : Returns TRUE to keep enumerating.
  1048. //-----------------------------------------------------------------------------
  1049. static BOOL _CheckBadConnections(CMapEntity *pEntity, CListBox *pList)
  1050. {
  1051. if ( !IsCheckVisible( pEntity ) )
  1052. return TRUE;
  1053. if (CEntityConnection::ValidateOutputConnections(pEntity, (Options.general.bCheckVisibleMapErrors == TRUE)) == CONNECTION_BAD)
  1054. {
  1055. AddError(pList, ErrorBadConnections, (DWORD)pEntity->GetClassName(), pEntity);
  1056. }
  1057. // TODO: Check for a "Kill" input with the same output, target, and delay as another input. This
  1058. // creates a race condition in the game where the order of arrival is not guaranteed
  1059. //int nConnCount = pEntity->Connections_GetCount();
  1060. //for (int i = 0; i < nConnCount; i++)
  1061. //{
  1062. // CEntityConnection *pConn = pEntity->Connections_Get(i);
  1063. // if (!stricmp(pConn->GetInputName(), "kill"))
  1064. // {
  1065. // }
  1066. //}
  1067. return TRUE;
  1068. }
  1069. static void CheckBadConnections(CListBox *pList, CMapWorld *pWorld)
  1070. {
  1071. pWorld->EnumChildren((ENUMMAPCHILDRENPROC)_CheckBadConnections, (DWORD)pList, MAPCLASS_TYPE(CMapEntity));
  1072. }
  1073. static bool HasVisGroupHiddenChildren(CMapClass *pObject)
  1074. {
  1075. const CMapObjectList *pChildren = pObject->GetChildren();
  1076. FOR_EACH_OBJ( *pChildren, pos )
  1077. {
  1078. if (!pChildren->Element(pos)->IsVisGroupShown())
  1079. return true;
  1080. }
  1081. return false;
  1082. }
  1083. static bool HasVisGroupShownChildren(CMapClass *pObject)
  1084. {
  1085. const CMapObjectList *pChildren = pObject->GetChildren();
  1086. FOR_EACH_OBJ( *pChildren, pos )
  1087. {
  1088. if (pChildren->Element(pos)->IsVisGroupShown())
  1089. return true;
  1090. }
  1091. return false;
  1092. }
  1093. //-----------------------------------------------------------------------------
  1094. // Purpose: Makes sure that the visgroup assignments are valid.
  1095. //-----------------------------------------------------------------------------
  1096. static BOOL _CheckVisGroups(CMapClass *pObject, CListBox *pList)
  1097. {
  1098. CMapDoc *pDoc = CMapDoc::GetActiveMapDoc();
  1099. // dvs: FIXME: not working yet, revisit
  1100. // Entities cannot have hidden children.
  1101. //CMapEntity *pEntity = dynamic_cast<CMapEntity *>(pObject);
  1102. //if (pEntity && HasVisGroupHiddenChildren(pEntity))
  1103. //{
  1104. // AddError(pList, ErrorHiddenChildOfEntity, 0, pEntity);
  1105. // return TRUE;
  1106. //}
  1107. // Check the validity of any object that claims to be hidden by visgroups.
  1108. if (!pObject->IsVisGroupShown())
  1109. {
  1110. // Groups cannot be hidden by visgroups.
  1111. if (pObject->IsGroup())
  1112. {
  1113. bool bHidden = HasVisGroupHiddenChildren(pObject);
  1114. bool bVisible = HasVisGroupShownChildren(pObject);
  1115. if (bHidden && !bVisible)
  1116. {
  1117. AddError(pList, ErrorHiddenGroupHiddenChildren, 0, pObject);
  1118. }
  1119. else if (!bHidden && bVisible)
  1120. {
  1121. AddError(pList, ErrorHiddenGroupVisibleChildren, 0, pObject);
  1122. }
  1123. else
  1124. {
  1125. AddError(pList, ErrorHiddenGroupMixedChildren, 0, pObject);
  1126. }
  1127. return TRUE;
  1128. }
  1129. // Check for unanticipated objects that are hidden but forbidden from visgroup membership.
  1130. if (!pDoc->VisGroups_ObjectCanBelongToVisGroup(pObject))
  1131. {
  1132. AddError(pList, ErrorIllegallyHiddenObject, 0, pObject);
  1133. return TRUE;
  1134. }
  1135. // Hidden objects must belong to at least one visgroup.
  1136. if (pObject->GetVisGroupCount() == 0)
  1137. {
  1138. AddError(pList, ErrorHiddenObjectNoVisGroup, 0, pObject);
  1139. return TRUE;
  1140. }
  1141. }
  1142. return TRUE;
  1143. }
  1144. static void CheckVisGroups(CListBox *pList, CMapWorld *pWorld)
  1145. {
  1146. pWorld->EnumChildrenRecurseGroupsOnly((ENUMMAPCHILDRENPROC)_CheckVisGroups, (DWORD)pList);
  1147. }
  1148. //-----------------------------------------------------------------------------
  1149. // Purpose:
  1150. //-----------------------------------------------------------------------------
  1151. static BOOL _CheckOverlayFaceList( CMapEntity *pEntity, CListBox *pList )
  1152. {
  1153. if ( !IsCheckVisible( pEntity ) )
  1154. return TRUE;
  1155. const CMapObjectList *pChildren = pEntity->GetChildren();
  1156. FOR_EACH_OBJ( *pChildren, pos )
  1157. {
  1158. CMapOverlay *pOverlay = dynamic_cast<CMapOverlay*>( pChildren->Element(pos) );
  1159. if ( pOverlay )
  1160. {
  1161. // Check to see if the overlay has assigned faces.
  1162. if ( pOverlay->GetFaceCount() <= 0 )
  1163. {
  1164. AddError( pList, ErrorOverlayFaceList, 0, pEntity );
  1165. return TRUE;
  1166. }
  1167. }
  1168. }
  1169. return TRUE;
  1170. }
  1171. //-----------------------------------------------------------------------------
  1172. // Purpose:
  1173. //-----------------------------------------------------------------------------
  1174. static void CheckOverlayFaceList( CListBox *pList, CMapWorld *pWorld )
  1175. {
  1176. pWorld->EnumChildren( ( ENUMMAPCHILDRENPROC )_CheckOverlayFaceList, ( DWORD )pList, MAPCLASS_TYPE( CMapEntity ));
  1177. }
  1178. //
  1179. // ** FIX FUNCTIONS
  1180. //
  1181. static void FixDuplicatePlanes(MapError *pError)
  1182. {
  1183. // duplicate planes in pObjects[0]
  1184. // run thru faces..
  1185. CMapSolid *pSolid = (CMapSolid*) pError->pObjects[0];
  1186. ReStart:
  1187. int iFaces = pSolid->GetFaceCount();
  1188. for(int i = 0; i < iFaces; i++)
  1189. {
  1190. CMapFace *pFace = pSolid->GetFace(i);
  1191. Vector& pts1 = pFace->plane.normal;
  1192. for (int j = 0; j < iFaces; j++)
  1193. {
  1194. // Don't check self
  1195. if (j == i)
  1196. {
  1197. continue;
  1198. }
  1199. CMapFace *pFace2 = pSolid->GetFace(j);
  1200. Vector& pts2 = pFace2->plane.normal;
  1201. if (pts1 == pts2)
  1202. {
  1203. pSolid->DeleteFace(j);
  1204. goto ReStart;
  1205. }
  1206. }
  1207. }
  1208. }
  1209. //-----------------------------------------------------------------------------
  1210. // Purpose: Repairs an invalid solid.
  1211. // Input : pError - Contains information about the error.
  1212. //-----------------------------------------------------------------------------
  1213. static void FixSolidStructure(MapError *pError)
  1214. {
  1215. CMapSolid *pSolid = (CMapSolid *)pError->pObjects[0];
  1216. //
  1217. // First make sure all the faces are good.
  1218. //
  1219. int nFaces = pSolid->GetFaceCount();
  1220. for (int i = nFaces - 1; i >= 0; i--)
  1221. {
  1222. CMapFace *pFace = pSolid->GetFace(i);
  1223. if (!pFace->CheckFace(NULL))
  1224. {
  1225. pFace->Fix();
  1226. }
  1227. //
  1228. // If the face has no points, just remove it from the solid.
  1229. //
  1230. if (pFace->GetPointCount() == 0)
  1231. {
  1232. pSolid->DeleteFace(i);
  1233. }
  1234. }
  1235. //
  1236. // Rebuild the solid from the planes.
  1237. //
  1238. pSolid->CreateFromPlanes();
  1239. pSolid->PostUpdate(Notify_Changed);
  1240. }
  1241. LPCTSTR GetDefaultTextureName(); // dvs: BAD!
  1242. //-----------------------------------------------------------------------------
  1243. // Purpose: Replaces any missing textures with the default texture.
  1244. // Input : pError -
  1245. //-----------------------------------------------------------------------------
  1246. static void FixInvalidTexture(MapError *pError)
  1247. {
  1248. CMapSolid *pSolid = (CMapSolid *)pError->pObjects[0];
  1249. int nFaces = pSolid->GetFaceCount();
  1250. for (int i = 0; i < nFaces; i++)
  1251. {
  1252. CMapFace *pFace = pSolid->GetFace(i);
  1253. if (pFace != NULL)
  1254. {
  1255. IEditorTexture *pTex = pFace->GetTexture();
  1256. if (pTex != NULL)
  1257. {
  1258. if (pTex->IsDummy())
  1259. {
  1260. pFace->SetTexture(GetDefaultTextureName());
  1261. }
  1262. }
  1263. }
  1264. }
  1265. }
  1266. static void FixInvalidTextureAxes(MapError *pError)
  1267. {
  1268. CMapSolid *pSolid = (CMapSolid *)pError->pObjects[0];
  1269. int nFaces = pSolid->GetFaceCount();
  1270. for (int i = 0; i < nFaces; i++)
  1271. {
  1272. CMapFace *pFace = pSolid->GetFace(i);
  1273. if (!pFace->IsTextureAxisValid())
  1274. {
  1275. pFace->InitializeTextureAxes(Options.GetTextureAlignment(), INIT_TEXTURE_FORCE | INIT_TEXTURE_AXES);
  1276. }
  1277. }
  1278. }
  1279. static void FixInvalidContents(MapError *pError)
  1280. {
  1281. CMapSolid *pSolid = (CMapSolid *)pError->pObjects[0];
  1282. CMapFace *pFace = pSolid->GetFace(0);
  1283. DWORD dwContents = pFace->texture.q2contents;
  1284. int nFaces = pSolid->GetFaceCount();
  1285. for (int i = 1; i < nFaces; i++)
  1286. {
  1287. pFace = pSolid->GetFace(i);
  1288. pFace->texture.q2contents = dwContents;
  1289. }
  1290. }
  1291. //-----------------------------------------------------------------------------
  1292. // Purpose: Fixes duplicate face IDs by assigning the face a unique ID within
  1293. // the world.
  1294. // Input : pError - Holds the world and the face that is in error.
  1295. //-----------------------------------------------------------------------------
  1296. static void FixDuplicateFaceIDs(MapError *pError)
  1297. {
  1298. CMapWorld *pWorld = (CMapWorld *)pError->pObjects[0];
  1299. CMapFace *pFace = (CMapFace *)pError->dwExtra;
  1300. pFace->SetFaceID(pWorld->FaceID_GetNext());
  1301. }
  1302. //-----------------------------------------------------------------------------
  1303. // Purpose:
  1304. // Input : pError -
  1305. //-----------------------------------------------------------------------------
  1306. static void FixUnusedKeyvalues(MapError *pError)
  1307. {
  1308. CMapEntity *pEntity = (CMapEntity*) pError->pObjects[0];
  1309. GDclass *pClass = pEntity->GetClass();
  1310. if (!pClass)
  1311. {
  1312. return;
  1313. }
  1314. int iNext;
  1315. for ( int i=pEntity->GetFirstKeyValue(); i != pEntity->GetInvalidKeyValue(); i = iNext )
  1316. {
  1317. iNext = pEntity->GetNextKeyValue( i );
  1318. if (pClass->VarForName(pEntity->GetKey(i)) == NULL)
  1319. {
  1320. pEntity->DeleteKeyValue(pEntity->GetKey(i));
  1321. }
  1322. }
  1323. }
  1324. //-----------------------------------------------------------------------------
  1325. // Purpose: Removes any bad connections from the entity associated with the error.
  1326. // Input : pError -
  1327. //-----------------------------------------------------------------------------
  1328. static void FixBadConnections(MapError *pError)
  1329. {
  1330. CMapEntity *pEntity = (CMapEntity *)pError->pObjects[0];
  1331. CEntityConnection::FixBadConnections(pEntity, (Options.general.bCheckVisibleMapErrors == TRUE));
  1332. }
  1333. //-----------------------------------------------------------------------------
  1334. // Purpose: Fixes a race condition caused by a Kill input being triggered at the
  1335. // same instant as another input.
  1336. // Input : pError -
  1337. //-----------------------------------------------------------------------------
  1338. static void FixKillInputRaceCondition(MapError *pError)
  1339. {
  1340. CEntityConnection *pConn = (CEntityConnection *)pError->pObjects[1];
  1341. // Delay the Kill command so that it arrives after the other command,
  1342. // solving the race condition.
  1343. pConn->SetDelay(pConn->GetDelay() + 0.01);
  1344. }
  1345. //-----------------------------------------------------------------------------
  1346. // Purpose:
  1347. // Input : pError -
  1348. //-----------------------------------------------------------------------------
  1349. static void FixOverlayFaceList( MapError *pError )
  1350. {
  1351. CMapEntity *pEntity = static_cast<CMapEntity*>( pError->pObjects[0] );
  1352. if ( !pEntity )
  1353. return;
  1354. const CMapObjectList *pChildren = pEntity->GetChildren();
  1355. FOR_EACH_OBJ( *pChildren, pos )
  1356. {
  1357. CMapOverlay *pOverlay = dynamic_cast<CMapOverlay*>( pChildren->Element(pos) );
  1358. if ( pOverlay )
  1359. {
  1360. // Destroy itself.
  1361. CMapDoc *pDoc = CMapDoc::GetActiveMapDoc();
  1362. pDoc->RemoveObjectFromWorld( pEntity, true );
  1363. GetHistory()->KeepForDestruction( pEntity );
  1364. return;
  1365. }
  1366. }
  1367. }
  1368. //-----------------------------------------------------------------------------
  1369. // Purpose:
  1370. // Input : pError -
  1371. //-----------------------------------------------------------------------------
  1372. static void FixEmptyEntity(MapError *pError)
  1373. {
  1374. CMapClass *pKillMe = pError->pObjects[0];
  1375. if (pKillMe->GetParent() != NULL)
  1376. {
  1377. GetHistory()->KeepForDestruction(pKillMe);
  1378. pKillMe->GetParent()->RemoveChild(pKillMe);
  1379. }
  1380. }
  1381. //-----------------------------------------------------------------------------
  1382. // Purpose: Fixes duplicate node IDs by assigning the entity a unique node ID.
  1383. // Input : pError - Holds the world and the entity that is in error.
  1384. //-----------------------------------------------------------------------------
  1385. static void FixDuplicateNodeIDs(MapError *pError)
  1386. {
  1387. CMapEntity *pEntity = (CMapEntity *)pError->pObjects[0];
  1388. pEntity->AssignNodeID();
  1389. }
  1390. //-----------------------------------------------------------------------------
  1391. // Purpose: Clears a bad target reference from the given entity.
  1392. // Input : pError -
  1393. //-----------------------------------------------------------------------------
  1394. static void FixMissingTarget(MapError *pError)
  1395. {
  1396. CMapEntity *pEntity = (CMapEntity *)pError->pObjects[0];
  1397. const char *pszKey = (const char *)pError->dwExtra;
  1398. pEntity->SetKeyValue(pszKey, NULL);
  1399. }
  1400. //-----------------------------------------------------------------------------
  1401. // Purpose: Fix a an invalid visgroup state. This is either:
  1402. // 1) A group that is hidden
  1403. // 2) An object that is hidden but not in any visgroups
  1404. //-----------------------------------------------------------------------------
  1405. void FixHiddenObject(MapError *pError)
  1406. {
  1407. CMapClass *pObject = pError->pObjects[0];
  1408. // Tweak the object's visgroup state directly to avoid changing the
  1409. // hidden/shown state of the object's children.
  1410. pObject->m_bVisGroupShown = true;
  1411. pObject->m_bVisGroupAutoShown = true;
  1412. pObject->m_VisGroups.RemoveAll();
  1413. // Create a new visgroup to out the objects in (for hiding or inspection/deletion).
  1414. CMapObjectList Objects;
  1415. Objects.AddToTail(pObject);
  1416. CMapDoc *pDoc = CMapDoc::GetActiveMapDoc();
  1417. if ((pError->Type == ErrorHiddenGroupHiddenChildren) ||
  1418. (pError->Type == ErrorHiddenObjectNoVisGroup))
  1419. {
  1420. // The objects aren't in the compile, so just hide them.
  1421. pDoc->VisGroups_CreateNamedVisGroup(Objects, "_hidden by Check for Problems", true, false);
  1422. }
  1423. else if (pError->Type == ErrorIllegallyHiddenObject)
  1424. {
  1425. // Do nothing, the object is now shown.
  1426. }
  1427. else
  1428. {
  1429. // ErrorHiddenGroupVisibleChildren
  1430. // ErrorHiddenGroupMixedChildren
  1431. // ErrorHiddenChildOfEntity
  1432. // The objects either ARE in the compile, or they can't be hidden in a visgroup.
  1433. // Don't hide them, just stick them in a visgroup for inspection
  1434. pDoc->VisGroups_CreateNamedVisGroup(Objects, "found by Check for Problems", false, false);
  1435. }
  1436. }
  1437. //-----------------------------------------------------------------------------
  1438. // Purpose: Checks the map for problems. Returns true if the map is okay,
  1439. // false if problems were found.
  1440. //-----------------------------------------------------------------------------
  1441. bool CMapCheckDlg::DoCheck(void)
  1442. {
  1443. CMapWorld *pWorld = GetActiveWorld();
  1444. // Clear error list
  1445. KillErrorList();
  1446. // Map validation
  1447. CheckRequirements(&m_Errors, pWorld);
  1448. // Solid validation
  1449. CheckMixedFaces(&m_Errors, pWorld);
  1450. //CheckDuplicatePlanes(&m_Errors, pWorld);
  1451. CheckDuplicateFaceIDs(&m_Errors, pWorld);
  1452. CheckDuplicateNodeIDs(&m_Errors, pWorld);
  1453. CheckSolidIntegrity(&m_Errors, pWorld);
  1454. CheckSolidContents(&m_Errors, pWorld);
  1455. CheckInvalidTextures(&m_Errors, pWorld);
  1456. // Entity validation
  1457. CheckUnusedKeyvalues(&m_Errors, pWorld);
  1458. CheckEmptyEntities(&m_Errors, pWorld);
  1459. CheckMissingTargets(&m_Errors, pWorld);
  1460. CheckBadConnections(&m_Errors, pWorld);
  1461. CheckVisGroups(&m_Errors, pWorld);
  1462. CheckOverlayFaceList(&m_Errors, pWorld);
  1463. if (!m_Errors.GetCount())
  1464. {
  1465. AfxMessageBox("No errors were found.");
  1466. EndDialog(IDOK);
  1467. return true;
  1468. }
  1469. return false;
  1470. }
  1471. //-----------------------------------------------------------------------------
  1472. // Purpose:
  1473. //-----------------------------------------------------------------------------
  1474. void CMapCheckDlg::OnOK()
  1475. {
  1476. DestroyWindow();
  1477. }
  1478. //-----------------------------------------------------------------------------
  1479. // Purpose:
  1480. //-----------------------------------------------------------------------------
  1481. void CMapCheckDlg::OnClose()
  1482. {
  1483. DestroyWindow();
  1484. }
  1485. //-----------------------------------------------------------------------------
  1486. // Purpose: Called when our window is being destroyed.
  1487. //-----------------------------------------------------------------------------
  1488. void CMapCheckDlg::OnDestroy()
  1489. {
  1490. delete this;
  1491. s_pDlg = NULL;
  1492. }