diff options
| author | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
|---|---|---|
| committer | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
| commit | 3bf9df6b2785fa6d951086978a3e66f49427166a (patch) | |
| tree | 2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /hammer/mapcheckdlg.cpp | |
| download | archived-source-engine-2018-hl2-src-3bf9df6b2785fa6d951086978a3e66f49427166a.tar.xz archived-source-engine-2018-hl2-src-3bf9df6b2785fa6d951086978a3e66f49427166a.zip | |
Diffstat (limited to 'hammer/mapcheckdlg.cpp')
| -rw-r--r-- | hammer/mapcheckdlg.cpp | 1772 |
1 files changed, 1772 insertions, 0 deletions
diff --git a/hammer/mapcheckdlg.cpp b/hammer/mapcheckdlg.cpp new file mode 100644 index 0000000..f9b839f --- /dev/null +++ b/hammer/mapcheckdlg.cpp @@ -0,0 +1,1772 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "stdafx.h" +#include "GameConfig.h" +#include "GlobalFunctions.h" +#include "History.h" +#include "MainFrm.h" +#include "MapCheckDlg.h" +#include "MapDoc.h" +#include "MapEntity.h" +#include "MapSolid.h" +#include "MapWorld.h" +#include "Options.h" +#include "ToolManager.h" +#include "VisGroup.h" +#include "hammer.h" +#include "MapOverlay.h" +#include "Selection.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include <tier0/memdbgon.h> + + +// ******** +// NOTE: Make sure the order matches g_MapErrorStringIDs below! +// ******** +typedef enum +{ + ErrorNoPlayerStart, + ErrorMixedFace, + ErrorDuplicatePlanes, + ErrorMissingTarget, + ErrorInvalidTexture, + ErrorSolidStructure, + ErrorUnusedKeyvalues, + ErrorEmptyEntity, + ErrorDuplicateKeys, + ErrorSolidContents, + ErrorInvalidTextureAxes, + ErrorDuplicateFaceIDs, + ErrorDuplicateNodeIDs, + ErrorBadConnections, + ErrorHiddenGroupHiddenChildren, + ErrorHiddenGroupVisibleChildren, + ErrorHiddenGroupMixedChildren, + ErrorHiddenObjectNoVisGroup, + ErrorHiddenChildOfEntity, + ErrorIllegallyHiddenObject, + ErrorKillInputRaceCondition, + ErrorOverlayFaceList, +} MapErrorType; + +// ******** +// NOTE: Make sure the order matches MapErrorType above! +// ******** +struct +{ + int m_StrResourceID; + int m_DescriptionResourceID; +} g_MapErrorStrings[] = +{ + {IDS_NOPLAYERSTART, IDS_NOPLAYERSTART_DESC}, + {IDS_MIXEDFACES, IDS_MIXEDFACES_DESC}, + {IDS_DUPLICATEPLANES, IDS_DUPLICATEPLANES_DESC}, + {IDS_UNMATCHEDTARGET, IDS_UNMATCHEDTARGET_DESC}, + {IDS_INVALIDTEXTURE, IDS_INVALIDTEXTURE_DESC}, + {IDS_SOLIDSTRUCTURE, IDS_SOLIDSTRUCTURE_DESC}, + {IDS_UNUSEDKEYVALUES, IDS_UNUSEDKEYVALUES_DESC}, + {IDS_EMPTYENTITY, IDS_EMPTYENTITY_DESC}, + {IDS_DUPLICATEKEYS, IDS_DUPLICATEKEYS_DESC}, + {IDS_SOLIDCONTENT, IDS_SOLIDCONTENT_DESC}, + {IDS_INVALIDTEXTUREAXES, IDS_INVALIDTEXTUREAXES_DESC}, + {IDS_DUPLICATEFACEID, IDS_DUPLICATEFACEID_DESC}, + {IDS_DUPLICATE_NODE_ID, IDS_DUPLICATE_NODE_ID_DESC}, + {IDS_BAD_CONNECTIONS, IDS_BAD_CONNECTIONS_DESC}, + {IDS_HIDDEN_GROUP_HIDDEN_CHILDREN, IDS_HIDDEN_GROUP_HIDDEN_CHILDREN_DESC}, + {IDS_HIDDEN_GROUP_VISIBLE_CHILDREN, IDS_HIDDEN_GROUP_VISIBLE_CHILDREN_DESC}, + {IDS_HIDDEN_GROUP_MIXED_CHILDREN, IDS_HIDDEN_GROUP_MIXED_CHILDREN_DESC}, + {IDS_HIDDEN_NO_VISGROUP, IDS_HIDDEN_NO_VISGROUP_DESC}, + {IDS_HIDDEN_CHILD_OF_ENTITY, IDS_HIDDEN_CHILD_OF_ENTITY_DESC}, + {IDS_HIDDEN_ILLEGALLY, IDS_HIDDEN_ILLEGALLY_DESC}, + {IDS_KILL_INPUT_RACE_CONDITION, IDS_KILL_INPUT_RACE_CONDITION_DESC}, + {IDS_BAD_OVERLAY, IDS_DAB_OVERLAY_DESC} +}; + + + +typedef enum +{ + CantFix, + NeedsFix, + Fixed, +} FIXCODE; + + +struct MapError +{ + CMapClass *pObjects[3]; + MapErrorType Type; + DWORD dwExtra; + FIXCODE Fix; +}; + + +// +// Fix functions. +// +static void FixDuplicatePlanes(MapError *pError); +static void FixSolidStructure(MapError *pError); +static void FixInvalidTexture(MapError *pError); +static void FixInvalidTextureAxes(MapError *pError); +static void FixUnusedKeyvalues(MapError *pError); +static void FixEmptyEntity(MapError *pError); +static void FixBadConnections(MapError *pError); +static void FixInvalidContents(MapError *pError); +static void FixDuplicateFaceIDs(MapError *pError); +static void FixDuplicateNodeIDs(MapError *pError); +static void FixMissingTarget(MapError *pError); +void FixHiddenObject(MapError *pError); +static void FixKillInputRaceCondition(MapError *pError); +static void FixOverlayFaceList(MapError *pError); + + +CMapCheckDlg *s_pDlg = NULL; + + +BEGIN_MESSAGE_MAP(CMapCheckDlg, CDialog) + //{{AFX_MSG_MAP(CMapCheckDlg) + ON_BN_CLICKED(IDC_GO, OnGo) + ON_LBN_SELCHANGE(IDC_ERRORS, OnSelchangeErrors) + ON_LBN_DBLCLK(IDC_ERRORS, OnDblClkErrors) + ON_WM_PAINT() + ON_BN_CLICKED(IDC_FIX, OnFix) + ON_BN_CLICKED(IDC_FIXALL, OnFixall) + ON_WM_DESTROY() + ON_WM_CLOSE() + ON_BN_CLICKED(IDC_CHECK_VISIBLE_ONLY, OnCheckVisibleOnly) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + + +//----------------------------------------------------------------------------- +// Visibility check +//----------------------------------------------------------------------------- +inline bool IsCheckVisible( CMapClass *pClass ) +{ + return (Options.general.bCheckVisibleMapErrors == FALSE) || pClass->IsVisible(); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CMapCheckDlg::CheckForProblems(CWnd *pwndParent) +{ + if (!s_pDlg) + { + s_pDlg = new CMapCheckDlg; + s_pDlg->Create(IDD, pwndParent); + } + + if (!s_pDlg->DoCheck()) + { + // Found problems. + s_pDlg->ShowWindow(SW_SHOW); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : pParent - +//----------------------------------------------------------------------------- +CMapCheckDlg::CMapCheckDlg(CWnd *pParent) + : CDialog(CMapCheckDlg::IDD, pParent) +{ + //{{AFX_DATA_INIT(CMapCheckDlg) + m_bCheckVisible = FALSE; + //}}AFX_DATA_INIT + + m_bCheckVisible = Options.general.bCheckVisibleMapErrors; +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : pDX - +//----------------------------------------------------------------------------- +void CMapCheckDlg::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + + //{{AFX_DATA_MAP(CMapCheckDlg) + DDX_Control(pDX, IDC_FIXALL, m_cFixAll); + DDX_Control(pDX, IDC_FIX, m_Fix); + DDX_Control(pDX, IDC_GO, m_Go); + DDX_Control(pDX, IDC_DESCRIPTION, m_Description); + DDX_Control(pDX, IDC_ERRORS, m_Errors); + DDX_Check(pDX, IDC_CHECK_VISIBLE_ONLY, m_bCheckVisible); + //}}AFX_DATA_MAP + + if ( pDX->m_bSaveAndValidate ) + { + Options.general.bCheckVisibleMapErrors = m_bCheckVisible; + } +} + + +//----------------------------------------------------------------------------- +// Checkbox indicating whether we should check visible errors +//----------------------------------------------------------------------------- +void CMapCheckDlg::OnCheckVisibleOnly() +{ + UpdateData( TRUE ); + DoCheck(); +} + + +//----------------------------------------------------------------------------- +// Purpose: Selects the current error objects and centers the views on it. +//----------------------------------------------------------------------------- +void CMapCheckDlg::OnGo() +{ + GotoSelectedErrors(); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CMapCheckDlg::GotoSelectedErrors() +{ + int iSel = m_Errors.GetCurSel(); + if (iSel == LB_ERR) + { + return; + } + + ToolManager()->SetTool(TOOL_POINTER); + + CMapObjectList Objects; + for (int i = 0; i < m_Errors.GetCount(); i++) + { + if (m_Errors.GetSel(i) > 0) + { + MapError *pError = (MapError *)m_Errors.GetItemDataPtr(i); + if (pError) + { + Objects.AddToTail(pError->pObjects[0]); + } + } + } + + CMapDoc *pDoc = CMapDoc::GetActiveMapDoc(); + + pDoc->SelectObjectList(&Objects); + pDoc->CenterViewsOnSelection(); +} + + +//----------------------------------------------------------------------------- +// Purpose: Fixes all the selected errors. +//----------------------------------------------------------------------------- +void CMapCheckDlg::OnFix() +{ + int iSel = m_Errors.GetCurSel(); + if (iSel == LB_ERR) + { + return; + } + + UpdateBox ub; + CMapObjectList Objects; + ub.Objects = &Objects; + + for (int i = 0; i < m_Errors.GetCount(); i++) + { + if (m_Errors.GetSel(i) > 0) + { + MapError *pError = (MapError *)m_Errors.GetItemDataPtr(i); + if (pError) + { + Fix(pError, ub); + } + } + } + + OnSelchangeErrors(); + CMapDoc::GetActiveMapDoc()->UpdateAllViews( MAPVIEW_UPDATE_OBJECTS ); +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : pError - +// ub - +//----------------------------------------------------------------------------- +void CMapCheckDlg::Fix(MapError *pError, UpdateBox &ub) +{ + CMapDoc::GetActiveMapDoc()->SetModifiedFlag(); + + if (pError->Fix != NeedsFix) + { + // should never get here because this button is supposed + // to be disabled if the error cannot be fixed + return; + } + + // + // Expand the bounds of the update region to include the broken objects. + // + for (int i = 0; i < 2; i++) + { + if (!pError->pObjects[i]) + { + continue; + } + + ub.Objects->AddToTail(pError->pObjects[i]); + + Vector mins; + Vector maxs; + pError->pObjects[i]->GetRender2DBox(mins, maxs); + ub.Box.UpdateBounds(mins, maxs); + } + + // + // Perform the fix. + // + switch (pError->Type) + { + case ErrorDuplicatePlanes: + { + FixDuplicatePlanes(pError); + break; + } + case ErrorDuplicateFaceIDs: + { + FixDuplicatePlanes(pError); + break; + } + case ErrorDuplicateNodeIDs: + { + FixDuplicateNodeIDs(pError); + break; + } + case ErrorMissingTarget: + { + FixMissingTarget(pError); + break; + } + case ErrorSolidStructure: + { + FixSolidStructure(pError); + break; + } + case ErrorSolidContents: + { + FixInvalidContents(pError); + break; + } + case ErrorInvalidTexture: + { + FixInvalidTexture(pError); + break; + } + case ErrorInvalidTextureAxes: + { + FixInvalidTextureAxes(pError); + break; + } + case ErrorUnusedKeyvalues: + { + FixUnusedKeyvalues(pError); + break; + } + case ErrorBadConnections: + { + FixBadConnections(pError); + break; + } + case ErrorEmptyEntity: + { + FixEmptyEntity(pError); + break; + } + case ErrorHiddenGroupVisibleChildren: + case ErrorHiddenGroupMixedChildren: + case ErrorHiddenGroupHiddenChildren: + case ErrorHiddenObjectNoVisGroup: + case ErrorHiddenChildOfEntity: + case ErrorIllegallyHiddenObject: + { + FixHiddenObject(pError); + break; + } + case ErrorKillInputRaceCondition: + { + FixKillInputRaceCondition(pError); + break; + } + case ErrorOverlayFaceList: + { + FixOverlayFaceList( pError ); + break; + } + } + + pError->Fix = Fixed; + + // + // Expand the bounds of the update region to include the fixed objects. + // + for (int i = 0; i < 2; i++) + { + if (!pError->pObjects[i]) + { + continue; + } + + Vector mins; + Vector maxs; + pError->pObjects[i]->GetRender2DBox(mins, maxs); + ub.Box.UpdateBounds(mins, maxs); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CMapCheckDlg::OnFixall() +{ + int iSel = m_Errors.GetCurSel(); + if (iSel == LB_ERR) + { + return; + } + + MapError *pError = (MapError *) m_Errors.GetItemDataPtr(iSel); + + if (pError->Fix == CantFix) + { + // should never get here because this button is supposed + // to be disabled if the error cannot be fixed + return; + } + + UpdateBox ub; + CMapObjectList Objects; + ub.Objects = &Objects; + + // For every selected error... + for (int i = 0; i < m_Errors.GetCount(); i++) + { + if (m_Errors.GetSel(i) > 0) + { + pError = (MapError *)m_Errors.GetItemDataPtr(i); + if ((pError) && (pError->Fix == NeedsFix)) + { + // Find and fix every error of the same type. + for (int j = 0; j < m_Errors.GetCount(); j++) + { + MapError *pError2 = (MapError *)m_Errors.GetItemDataPtr(j); + if ((pError2->Type != pError->Type) || (pError2->Fix != NeedsFix)) + { + continue; + } + + Fix(pError2, ub); + } + } + } + } + + OnSelchangeErrors(); + CMapDoc::GetActiveMapDoc()->UpdateAllViews( MAPVIEW_UPDATE_OBJECTS ); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CMapCheckDlg::OnSelchangeErrors() +{ + // change description to match error + int iSel = m_Errors.GetCurSel(); + + if(iSel == LB_ERR) + { + m_Fix.EnableWindow(FALSE); + m_cFixAll.EnableWindow(FALSE); + m_Go.EnableWindow(FALSE); + } + + CString str; + MapError *pError; + pError = (MapError*) m_Errors.GetItemDataPtr(iSel); + + // Figure out which error string we're using. + int iErrorStr = (int)pError->Type; + iErrorStr = clamp( iErrorStr, 0, ARRAYSIZE( g_MapErrorStrings ) - 1 ); + Assert( iErrorStr == (int)pError->Type ); + + str.LoadString(g_MapErrorStrings[iErrorStr].m_DescriptionResourceID); + m_Description.SetWindowText(str); + + m_Go.EnableWindow(pError->pObjects[0] != NULL); + + // set state of fix button + m_Fix.EnableWindow(pError->Fix == NeedsFix); + m_cFixAll.EnableWindow(pError->Fix != CantFix); + + // set text of fix button + switch (pError->Fix) + { + case NeedsFix: + m_Fix.SetWindowText("&Fix"); + break; + case CantFix: + m_Fix.SetWindowText("Can't fix"); + break; + case Fixed: + m_Fix.SetWindowText("(fixed)"); + break; + } + + CMapDoc *pDoc = CMapDoc::GetActiveMapDoc(); + + pDoc->GetSelection()->SetMode(selectObjects); + + if (pError->pObjects[0]) + { + pDoc->SelectObject(pError->pObjects[0], scClear|scSelect|scSaveChanges ); + } + else + { + pDoc->SelectObject(NULL, scClear|scSaveChanges ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CMapCheckDlg::OnDblClkErrors() +{ + GotoSelectedErrors(); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CMapCheckDlg::OnPaint() +{ + CPaintDC dc(this); // device context for painting +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CMapCheckDlg::KillErrorList() +{ + // delete items in list.. their data ptrs are allocated objects + int iSize = m_Errors.GetCount(); + for(int i = 0; i < iSize; i++) + { + MapError *pError = (MapError*) m_Errors.GetItemDataPtr(i); + delete pError; + } + + m_Errors.ResetContent(); +} + + +//----------------------------------------------------------------------------- +// Purpose: Builds the listbox string for the given error and adds it to the list. +//----------------------------------------------------------------------------- +static void AddErrorToListBox(CListBox *pList, MapError *pError) +{ + CString str; + + // Figure out which error string we're using. + int iErrorStr = (int)pError->Type; + iErrorStr = clamp( iErrorStr, 0, ARRAYSIZE( g_MapErrorStrings ) - 1 ); + Assert( iErrorStr == (int)pError->Type ); + + str.LoadString(g_MapErrorStrings[iErrorStr].m_StrResourceID); + + if (str.Find('%') != -1) + { + if (pError->Type == ErrorUnusedKeyvalues) + { + // dwExtra has the name of the string in it + CString str2 = str; + CMapEntity *pEntity = (CMapEntity *)pError->pObjects[0]; + str.Format(str2, pEntity->GetClassName(), pError->dwExtra); + } + else + { + CString str2 = str; + str.Format(str2, pError->dwExtra); + } + } + + int iIndex = pList->AddString(str); + pList->SetItemDataPtr(iIndex, (PVOID)pError); +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : pList - +// Type - +// dwExtra - +// ... - +//----------------------------------------------------------------------------- +static void AddError(CListBox *pList, MapErrorType Type, DWORD dwExtra, ...) +{ + MapError *pError = new MapError; + memset(pError, 0, sizeof(MapError)); + + pError->Type = Type; + pError->dwExtra = dwExtra; + pError->Fix = CantFix; + + va_list vl; + va_start(vl, dwExtra); + + // + // Get the object pointer from the variable argument list. + // + switch (Type) + { + case ErrorNoPlayerStart: + { + // no objects. + break; + } + + case ErrorMixedFace: + case ErrorMissingTarget: + case ErrorDuplicatePlanes: + case ErrorDuplicateFaceIDs: + case ErrorDuplicateNodeIDs: + case ErrorSolidStructure: + case ErrorSolidContents: + case ErrorInvalidTexture: + case ErrorUnusedKeyvalues: + case ErrorBadConnections: + case ErrorEmptyEntity: + case ErrorDuplicateKeys: + case ErrorInvalidTextureAxes: + case ErrorHiddenGroupHiddenChildren: + case ErrorHiddenGroupVisibleChildren: + case ErrorHiddenGroupMixedChildren: + case ErrorHiddenObjectNoVisGroup: + case ErrorHiddenChildOfEntity: + case ErrorIllegallyHiddenObject: + case ErrorOverlayFaceList: + { + pError->pObjects[0] = va_arg(vl, CMapClass *); + break; + } + + case ErrorKillInputRaceCondition: + { + pError->pObjects[0] = va_arg(vl, CMapClass *); + pError->dwExtra = (DWORD)va_arg(vl, CEntityConnection *); + break; + } + } + + // + // Set the can fix flag. + // + switch (Type) + { + case ErrorSolidContents: + case ErrorDuplicatePlanes: + case ErrorDuplicateFaceIDs: + case ErrorDuplicateNodeIDs: + case ErrorSolidStructure: + case ErrorInvalidTexture: + case ErrorUnusedKeyvalues: + case ErrorMissingTarget: + case ErrorBadConnections: + case ErrorEmptyEntity: + case ErrorDuplicateKeys: + case ErrorInvalidTextureAxes: + case ErrorHiddenGroupHiddenChildren: + case ErrorHiddenGroupVisibleChildren: + case ErrorHiddenGroupMixedChildren: + case ErrorHiddenObjectNoVisGroup: + case ErrorHiddenChildOfEntity: + case ErrorIllegallyHiddenObject: + case ErrorKillInputRaceCondition: + case ErrorOverlayFaceList: + { + pError->Fix = NeedsFix; + break; + } + } + + va_end(vl); + + AddErrorToListBox(pList, pError); +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : pObject - +// DWORD - +// Output : +//----------------------------------------------------------------------------- +static BOOL FindPlayer(CMapEntity *pObject, DWORD) +{ + if ( !IsCheckVisible( pObject ) ) + return TRUE; + + if (pObject->IsPlaceholder() && pObject->IsClass("info_player_start")) + { + return(FALSE); + } + return(TRUE); +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : pList - +// pWorld - +//----------------------------------------------------------------------------- +static void CheckRequirements(CListBox *pList, CMapWorld *pWorld) +{ + // ensure there's a player start .. + if (pWorld->EnumChildren((ENUMMAPCHILDRENPROC)FindPlayer, 0, MAPCLASS_TYPE(CMapEntity))) + { + // if rvl is !0, it was not stopped prematurely.. which means there is + // NO player start. + AddError(pList, ErrorNoPlayerStart, 0); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : pSolid - +// pList - +// Output : +//----------------------------------------------------------------------------- +static BOOL _CheckMixedFaces(CMapSolid *pSolid, CListBox *pList) +{ + if ( !IsCheckVisible( pSolid ) ) + return TRUE; + + // run thru faces.. + int iFaces = pSolid->GetFaceCount(); + int iSolid = 2; // start off ambivalent + int i; + for(i = 0; i < iFaces; i++) + { + CMapFace *pFace = pSolid->GetFace(i); + + char ch = pFace->texture.texture[0]; + if((ch == '*' && iSolid == 1) || (ch != '*' && iSolid == 0)) + { + break; + } + else iSolid = (ch == '*') ? 0 : 1; + } + + if(i == iFaces) // all ok + return TRUE; + + // NOT ok + AddError(pList, ErrorMixedFace, 0, pSolid); + + return TRUE; +} + + +static void CheckMixedFaces(CListBox *pList, CMapWorld *pWorld) +{ + pWorld->EnumChildren((ENUMMAPCHILDRENPROC)_CheckMixedFaces, (DWORD)pList, MAPCLASS_TYPE(CMapSolid)); +} + + +//----------------------------------------------------------------------------- +// Purpose: Returns true if there is another node entity in the world with the +// same node ID as the given entity. +// Input : pNode - +// pWorld - +//----------------------------------------------------------------------------- +bool FindDuplicateNodeID(CMapEntity *pNode, CMapWorld *pWorld) +{ + if ( !IsCheckVisible( pNode ) ) + return false; + + EnumChildrenPos_t pos; + CMapClass *pChild = pWorld->GetFirstDescendent(pos); + while (pChild != NULL) + { + CMapEntity *pEntity = dynamic_cast<CMapEntity *>(pChild); + if (pEntity && IsCheckVisible( pEntity ) && (pEntity != pNode) && pEntity->IsNodeClass()) + { + int nNodeID1 = pNode->GetNodeID(); + int nNodeID2 = pEntity->GetNodeID(); + if ((nNodeID1 != 0) && (nNodeID2 != 0) && (nNodeID1 == nNodeID2)) + { + return true; + } + } + + pChild = pWorld->GetNextDescendent(pos); + } + + return false; +} + + +//----------------------------------------------------------------------------- +// Purpose: Checks for node entities with the same node ID. +//----------------------------------------------------------------------------- +static void CheckDuplicateNodeIDs(CListBox *pList, CMapWorld *pWorld) +{ + EnumChildrenPos_t pos; + CMapClass *pChild = pWorld->GetFirstDescendent(pos); + while (pChild != NULL) + { + CMapEntity *pEntity = dynamic_cast<CMapEntity *>(pChild); + if (pEntity && pEntity->IsNodeClass()) + { + if (FindDuplicateNodeID(pEntity, pWorld)) + { + AddError(pList, ErrorDuplicateNodeIDs, (DWORD)pWorld, pEntity); + } + } + + pChild = pWorld->GetNextDescendent(pos); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Checks for faces with identical face normals in this solid object. +// Input : pSolid - Solid to check for duplicate faces. +// Output : Returns TRUE if the face contains at least one duplicate face, +// FALSE if the solid contains no duplicate faces. +//----------------------------------------------------------------------------- +BOOL DoesContainDuplicates(CMapSolid *pSolid) +{ + int iFaces = pSolid->GetFaceCount(); + for (int i = 0; i < iFaces; i++) + { + CMapFace *pFace = pSolid->GetFace(i); + Vector& pts1 = pFace->plane.normal; + + for (int j = 0; j < iFaces; j++) + { + // Don't check self. + if (j == i) + { + continue; + } + + CMapFace *pFace2 = pSolid->GetFace(j); + Vector& pts2 = pFace2->plane.normal; + + if (pts1 == pts2) + { + return(TRUE); + } + } + } + + return(FALSE); +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : pSolid - +// pList - +// Output : +//----------------------------------------------------------------------------- +static BOOL _CheckDuplicatePlanes(CMapSolid *pSolid, CListBox *pList) +{ + if ( !IsCheckVisible( pSolid ) ) + return TRUE; + + if (DoesContainDuplicates(pSolid)) + { + AddError(pList, ErrorDuplicatePlanes, 0, pSolid); + } + + return(TRUE); +} + + +static void CheckDuplicatePlanes(CListBox *pList, CMapWorld *pWorld) +{ + pWorld->EnumChildren((ENUMMAPCHILDRENPROC)_CheckDuplicatePlanes, (DWORD)pList, MAPCLASS_TYPE(CMapSolid)); +} + + +struct FindDuplicateFaceIDs_t +{ + CMapFaceList All; // Collects all the face IDs in this map. + CMapFaceList Duplicates; // Collects the duplicate face IDs in this map. +}; + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : pSolid - +// pData - +// Output : Returns TRUE to continue enumerating. +//----------------------------------------------------------------------------- +static BOOL _CheckDuplicateFaceIDs(CMapSolid *pSolid, FindDuplicateFaceIDs_t *pData) +{ + if ( !IsCheckVisible( pSolid ) ) + return TRUE; + + int nFaceCount = pSolid->GetFaceCount(); + for (int i = 0; i < nFaceCount; i++) + { + CMapFace *pFace = pSolid->GetFace(i); + if (pData->All.FindFaceID(pFace->GetFaceID()) != -1) + { + if (pData->Duplicates.FindFaceID(pFace->GetFaceID()) != -1) + { + pData->Duplicates.AddToTail(pFace); + } + } + else + { + pData->All.AddToTail(pFace); + } + } + + return(TRUE); +} + + +//----------------------------------------------------------------------------- +// Purpose: Reports errors for all faces with duplicate face IDs. +// Input : pList - +// pWorld - +//----------------------------------------------------------------------------- +static void CheckDuplicateFaceIDs(CListBox *pList, CMapWorld *pWorld) +{ + FindDuplicateFaceIDs_t Lists; + Lists.All.SetGrowSize(128); + Lists.Duplicates.SetGrowSize(128); + + pWorld->EnumChildren((ENUMMAPCHILDRENPROC)_CheckDuplicateFaceIDs, (DWORD)&Lists, MAPCLASS_TYPE(CMapSolid)); + + for (int i = 0; i < Lists.Duplicates.Count(); i++) + { + CMapFace *pFace = Lists.Duplicates.Element(i); + AddError(pList, ErrorDuplicateFaceIDs, (DWORD)pFace, (CMapSolid *)pFace->GetParent()); + } +} + + +//----------------------------------------------------------------------------- +// Checks if a particular target is valid. +//----------------------------------------------------------------------------- +static void CheckValidTarget(CMapEntity *pEntity, const char *pFieldName, const char *pTargetName, CListBox *pList, bool bCheckClassNames) +{ + if (!pTargetName) + return; + + // These procedural names are always assumed to exist. + if (!stricmp(pTargetName, "!activator") || !stricmp(pTargetName, "!caller") || !stricmp(pTargetName, "!player") || !stricmp(pTargetName, "!self")) + return; + + CMapDoc *pDoc = CMapDoc::GetActiveMapDoc(); + + // Search by name first. + CMapEntityList Found; + bool bFound = pDoc->FindEntitiesByName(Found, pTargetName, (Options.general.bCheckVisibleMapErrors == TRUE)); + if (!bFound && bCheckClassNames) + { + // Not found, search by classname. + bFound = pDoc->FindEntitiesByClassName(Found, pTargetName, (Options.general.bCheckVisibleMapErrors == TRUE)); + } + + if (!bFound) + { + // No dice, flag it as an error. + AddError(pList, ErrorMissingTarget, (DWORD)pFieldName, pEntity); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Checks the given entity for references by name to nonexistent entities. +// Input : pEntity - +// pList - +// Output : Returns TRUE to keep enumerating. +//----------------------------------------------------------------------------- +static BOOL _CheckMissingTargets(CMapEntity *pEntity, CListBox *pList) +{ + if ( !IsCheckVisible( pEntity ) ) + return TRUE; + + GDclass *pClass = pEntity->GetClass(); + if (!pClass) + { + // Unknown class -- just check for target references. + static char *pszTarget = "target"; + const char *pszValue = pEntity->GetKeyValue(pszTarget); + CheckValidTarget(pEntity, pszTarget, pszValue, pList, false); + } + else + { + // Known class -- check all target_destination and target_name_or_class keyvalues. + for (int i = 0; i < pClass->GetVariableCount(); i++) + { + GDinputvariable *pVar = pClass->GetVariableAt(i); + if ((pVar->GetType() != ivTargetDest) && (pVar->GetType() != ivTargetNameOrClass)) + continue; + + const char *pszValue = pEntity->GetKeyValue(pVar->GetName()); + CheckValidTarget(pEntity, pVar->GetName(), pszValue, pList, (pVar->GetType() == ivTargetNameOrClass)); + } + } + + return TRUE; +} + + +static void CheckMissingTargets(CListBox *pList, CMapWorld *pWorld) +{ + pWorld->EnumChildren((ENUMMAPCHILDRENPROC)_CheckMissingTargets, (DWORD)pList, MAPCLASS_TYPE(CMapEntity)); +} + + +//----------------------------------------------------------------------------- +// Purpose: Determines whether a solid is good or bad. +// Input : pSolid - Solid to check. +// pList - List box into which to place errors. +// Output : Always returns TRUE to continue enumerating. +//----------------------------------------------------------------------------- +static BOOL _CheckSolidIntegrity(CMapSolid *pSolid, CListBox *pList) +{ + if ( !IsCheckVisible( pSolid ) ) + return TRUE; + + CCheckFaceInfo cfi; + int nFaces = pSolid->GetFaceCount(); + for (int i = 0; i < nFaces; i++) + { + CMapFace *pFace = pSolid->GetFace(i); + + // + // Reset the iPoint member so results from previous faces don't carry over. + // + cfi.iPoint = -1; + + // + // Check the face. + // + if (!pFace->CheckFace(&cfi)) + { + AddError(pList, ErrorSolidStructure, 0, pSolid); + break; + } + } + + return(TRUE); +} + + +static void CheckSolidIntegrity(CListBox *pList, CMapWorld *pWorld) +{ + pWorld->EnumChildren((ENUMMAPCHILDRENPROC)_CheckSolidIntegrity, (DWORD)pList, MAPCLASS_TYPE(CMapSolid)); +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : pSolid - +// pList - +// Output : +//----------------------------------------------------------------------------- +static BOOL _CheckSolidContents(CMapSolid *pSolid, CListBox *pList) +{ + if ( !IsCheckVisible( pSolid ) ) + return TRUE; + + CCheckFaceInfo cfi; + int nFaces = pSolid->GetFaceCount(); + CMapFace *pFace = pSolid->GetFace(0); + DWORD dwContents = pFace->texture.q2contents; + + for (int i = 1; i < nFaces; i++) + { + pFace = pSolid->GetFace(i); + if (pFace->texture.q2contents == dwContents) + { + continue; + } + AddError(pList, ErrorSolidContents, 0, pSolid); + break; + } + + return TRUE; +} + + +static void CheckSolidContents(CListBox *pList, CMapWorld *pWorld) +{ + if (CMapDoc::GetActiveMapDoc() && CMapDoc::GetActiveMapDoc()->GetGame() && CMapDoc::GetActiveMapDoc()->GetGame()->mapformat == mfQuake2) + { + pWorld->EnumChildren((ENUMMAPCHILDRENPROC)_CheckSolidContents, (DWORD)pList, MAPCLASS_TYPE(CMapSolid)); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Determines if there are any invalid textures or texture axes on any +// face of this solid. Adds an error message to the list box for each +// error found. +// Input : pSolid - Solid to check. +// pList - Pointer to the error list box. +// Output : Returns TRUE. +//----------------------------------------------------------------------------- +static BOOL _CheckInvalidTextures(CMapSolid *pSolid, CListBox *pList) +{ + if ( !IsCheckVisible( pSolid ) ) + return TRUE; + + int nFaces = pSolid->GetFaceCount(); + for(int i = 0; i < nFaces; i++) + { + const CMapFace *pFace = pSolid->GetFace(i); + + IEditorTexture *pTex = pFace->GetTexture(); + if (pTex->IsDummy()) + { + AddError(pList, ErrorInvalidTexture, (DWORD)pFace->texture.texture, pSolid); + return TRUE; + } + + if (!pFace->IsTextureAxisValid()) + { + AddError(pList, ErrorInvalidTextureAxes, i, pSolid); + return(TRUE); + } + } + + return(TRUE); +} + + +static void CheckInvalidTextures(CListBox *pList, CMapWorld *pWorld) +{ + pWorld->EnumChildren((ENUMMAPCHILDRENPROC)_CheckInvalidTextures, (DWORD)pList, MAPCLASS_TYPE(CMapSolid)); +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : pEntity - +// pList - +// Output : +//----------------------------------------------------------------------------- +static BOOL _CheckUnusedKeyvalues(CMapEntity *pEntity, CListBox *pList) +{ + if ( !IsCheckVisible( pEntity ) ) + return TRUE; + + if (!pEntity->IsClass() || pEntity->IsClass("multi_manager")) + { + return(TRUE); // can't check if no class associated + } + + GDclass *pClass = pEntity->GetClass(); + + for (int i = pEntity->GetFirstKeyValue(); i != pEntity->GetInvalidKeyValue(); i=pEntity->GetNextKeyValue( i ) ) + { + if (pClass->VarForName(pEntity->GetKey(i)) == NULL) + { + AddError(pList, ErrorUnusedKeyvalues, (DWORD)pEntity->GetKey(i), pEntity); + return(TRUE); + } + } + + return(TRUE); +} + + +static void CheckUnusedKeyvalues(CListBox *pList, CMapWorld *pWorld) +{ + pWorld->EnumChildren((ENUMMAPCHILDRENPROC)_CheckUnusedKeyvalues, (DWORD)pList, MAPCLASS_TYPE(CMapEntity)); +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : pEntity - +// pList - +// Output : +//----------------------------------------------------------------------------- +static BOOL _CheckEmptyEntities(CMapEntity *pEntity, CListBox *pList) +{ + if ( !IsCheckVisible( pEntity ) ) + return TRUE; + + if(!pEntity->IsPlaceholder() && !pEntity->GetChildCount()) + { + AddError(pList, ErrorEmptyEntity, (DWORD)pEntity->GetClassName(), pEntity); + } + + return(TRUE); +} + + +static void CheckEmptyEntities(CListBox *pList, CMapWorld *pWorld) +{ + pWorld->EnumChildren((ENUMMAPCHILDRENPROC)_CheckEmptyEntities, (DWORD)pList, MAPCLASS_TYPE(CMapEntity)); +} + + +//----------------------------------------------------------------------------- +// Purpose: Checks the entity for bad I/O connections. +// Input : pEntity - the entity to check +// pList - list box that tracks the errors +// Output : Returns TRUE to keep enumerating. +//----------------------------------------------------------------------------- +static BOOL _CheckBadConnections(CMapEntity *pEntity, CListBox *pList) +{ + if ( !IsCheckVisible( pEntity ) ) + return TRUE; + + if (CEntityConnection::ValidateOutputConnections(pEntity, (Options.general.bCheckVisibleMapErrors == TRUE)) == CONNECTION_BAD) + { + AddError(pList, ErrorBadConnections, (DWORD)pEntity->GetClassName(), pEntity); + } + + // TODO: Check for a "Kill" input with the same output, target, and delay as another input. This + // creates a race condition in the game where the order of arrival is not guaranteed + //int nConnCount = pEntity->Connections_GetCount(); + //for (int i = 0; i < nConnCount; i++) + //{ + // CEntityConnection *pConn = pEntity->Connections_Get(i); + // if (!stricmp(pConn->GetInputName(), "kill")) + // { + // } + //} + + return TRUE; +} + + +static void CheckBadConnections(CListBox *pList, CMapWorld *pWorld) +{ + pWorld->EnumChildren((ENUMMAPCHILDRENPROC)_CheckBadConnections, (DWORD)pList, MAPCLASS_TYPE(CMapEntity)); +} + + +static bool HasVisGroupHiddenChildren(CMapClass *pObject) +{ + const CMapObjectList *pChildren = pObject->GetChildren(); + + FOR_EACH_OBJ( *pChildren, pos ) + { + if (!pChildren->Element(pos)->IsVisGroupShown()) + return true; + } + + return false; +} + + +static bool HasVisGroupShownChildren(CMapClass *pObject) +{ + const CMapObjectList *pChildren = pObject->GetChildren(); + FOR_EACH_OBJ( *pChildren, pos ) + { + if (pChildren->Element(pos)->IsVisGroupShown()) + return true; + } + + return false; +} + + +//----------------------------------------------------------------------------- +// Purpose: Makes sure that the visgroup assignments are valid. +//----------------------------------------------------------------------------- +static BOOL _CheckVisGroups(CMapClass *pObject, CListBox *pList) +{ + CMapDoc *pDoc = CMapDoc::GetActiveMapDoc(); + + // dvs: FIXME: not working yet, revisit + // Entities cannot have hidden children. + //CMapEntity *pEntity = dynamic_cast<CMapEntity *>(pObject); + //if (pEntity && HasVisGroupHiddenChildren(pEntity)) + //{ + // AddError(pList, ErrorHiddenChildOfEntity, 0, pEntity); + // return TRUE; + //} + + // Check the validity of any object that claims to be hidden by visgroups. + if (!pObject->IsVisGroupShown()) + { + // Groups cannot be hidden by visgroups. + if (pObject->IsGroup()) + { + bool bHidden = HasVisGroupHiddenChildren(pObject); + bool bVisible = HasVisGroupShownChildren(pObject); + + if (bHidden && !bVisible) + { + AddError(pList, ErrorHiddenGroupHiddenChildren, 0, pObject); + } + else if (!bHidden && bVisible) + { + AddError(pList, ErrorHiddenGroupVisibleChildren, 0, pObject); + } + else + { + AddError(pList, ErrorHiddenGroupMixedChildren, 0, pObject); + } + + return TRUE; + } + + // Check for unanticipated objects that are hidden but forbidden from visgroup membership. + if (!pDoc->VisGroups_ObjectCanBelongToVisGroup(pObject)) + { + AddError(pList, ErrorIllegallyHiddenObject, 0, pObject); + return TRUE; + } + + // Hidden objects must belong to at least one visgroup. + if (pObject->GetVisGroupCount() == 0) + { + AddError(pList, ErrorHiddenObjectNoVisGroup, 0, pObject); + return TRUE; + } + } + + return TRUE; +} + + +static void CheckVisGroups(CListBox *pList, CMapWorld *pWorld) +{ + pWorld->EnumChildrenRecurseGroupsOnly((ENUMMAPCHILDRENPROC)_CheckVisGroups, (DWORD)pList); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +static BOOL _CheckOverlayFaceList( CMapEntity *pEntity, CListBox *pList ) +{ + if ( !IsCheckVisible( pEntity ) ) + return TRUE; + + const CMapObjectList *pChildren = pEntity->GetChildren(); + + FOR_EACH_OBJ( *pChildren, pos ) + { + CMapOverlay *pOverlay = dynamic_cast<CMapOverlay*>( pChildren->Element(pos) ); + if ( pOverlay ) + { + // Check to see if the overlay has assigned faces. + if ( pOverlay->GetFaceCount() <= 0 ) + { + AddError( pList, ErrorOverlayFaceList, 0, pEntity ); + return TRUE; + } + } + } + + return TRUE; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +static void CheckOverlayFaceList( CListBox *pList, CMapWorld *pWorld ) +{ + pWorld->EnumChildren( ( ENUMMAPCHILDRENPROC )_CheckOverlayFaceList, ( DWORD )pList, MAPCLASS_TYPE( CMapEntity )); +} + +// +// ** FIX FUNCTIONS +// +static void FixDuplicatePlanes(MapError *pError) +{ + // duplicate planes in pObjects[0] + // run thru faces.. + + CMapSolid *pSolid = (CMapSolid*) pError->pObjects[0]; + +ReStart: + int iFaces = pSolid->GetFaceCount(); + for(int i = 0; i < iFaces; i++) + { + CMapFace *pFace = pSolid->GetFace(i); + Vector& pts1 = pFace->plane.normal; + for (int j = 0; j < iFaces; j++) + { + // Don't check self + if (j == i) + { + continue; + } + + CMapFace *pFace2 = pSolid->GetFace(j); + Vector& pts2 = pFace2->plane.normal; + if (pts1 == pts2) + { + pSolid->DeleteFace(j); + goto ReStart; + } + } + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Repairs an invalid solid. +// Input : pError - Contains information about the error. +//----------------------------------------------------------------------------- +static void FixSolidStructure(MapError *pError) +{ + CMapSolid *pSolid = (CMapSolid *)pError->pObjects[0]; + + // + // First make sure all the faces are good. + // + int nFaces = pSolid->GetFaceCount(); + for (int i = nFaces - 1; i >= 0; i--) + { + CMapFace *pFace = pSolid->GetFace(i); + if (!pFace->CheckFace(NULL)) + { + pFace->Fix(); + } + // + // If the face has no points, just remove it from the solid. + // + if (pFace->GetPointCount() == 0) + { + pSolid->DeleteFace(i); + } + } + + // + // Rebuild the solid from the planes. + // + pSolid->CreateFromPlanes(); + pSolid->PostUpdate(Notify_Changed); +} + + +LPCTSTR GetDefaultTextureName(); // dvs: BAD! + + +//----------------------------------------------------------------------------- +// Purpose: Replaces any missing textures with the default texture. +// Input : pError - +//----------------------------------------------------------------------------- +static void FixInvalidTexture(MapError *pError) +{ + CMapSolid *pSolid = (CMapSolid *)pError->pObjects[0]; + + int nFaces = pSolid->GetFaceCount(); + for (int i = 0; i < nFaces; i++) + { + CMapFace *pFace = pSolid->GetFace(i); + if (pFace != NULL) + { + IEditorTexture *pTex = pFace->GetTexture(); + if (pTex != NULL) + { + if (pTex->IsDummy()) + { + pFace->SetTexture(GetDefaultTextureName()); + } + } + } + } +} + + +static void FixInvalidTextureAxes(MapError *pError) +{ + CMapSolid *pSolid = (CMapSolid *)pError->pObjects[0]; + + int nFaces = pSolid->GetFaceCount(); + for (int i = 0; i < nFaces; i++) + { + CMapFace *pFace = pSolid->GetFace(i); + if (!pFace->IsTextureAxisValid()) + { + pFace->InitializeTextureAxes(Options.GetTextureAlignment(), INIT_TEXTURE_FORCE | INIT_TEXTURE_AXES); + } + } +} + + +static void FixInvalidContents(MapError *pError) +{ + CMapSolid *pSolid = (CMapSolid *)pError->pObjects[0]; + + CMapFace *pFace = pSolid->GetFace(0); + DWORD dwContents = pFace->texture.q2contents; + + int nFaces = pSolid->GetFaceCount(); + for (int i = 1; i < nFaces; i++) + { + pFace = pSolid->GetFace(i); + pFace->texture.q2contents = dwContents; + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Fixes duplicate face IDs by assigning the face a unique ID within +// the world. +// Input : pError - Holds the world and the face that is in error. +//----------------------------------------------------------------------------- +static void FixDuplicateFaceIDs(MapError *pError) +{ + CMapWorld *pWorld = (CMapWorld *)pError->pObjects[0]; + CMapFace *pFace = (CMapFace *)pError->dwExtra; + + pFace->SetFaceID(pWorld->FaceID_GetNext()); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : pError - +//----------------------------------------------------------------------------- +static void FixUnusedKeyvalues(MapError *pError) +{ + CMapEntity *pEntity = (CMapEntity*) pError->pObjects[0]; + + GDclass *pClass = pEntity->GetClass(); + if (!pClass) + { + return; + } + + int iNext; + for ( int i=pEntity->GetFirstKeyValue(); i != pEntity->GetInvalidKeyValue(); i = iNext ) + { + iNext = pEntity->GetNextKeyValue( i ); + if (pClass->VarForName(pEntity->GetKey(i)) == NULL) + { + pEntity->DeleteKeyValue(pEntity->GetKey(i)); + } + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Removes any bad connections from the entity associated with the error. +// Input : pError - +//----------------------------------------------------------------------------- +static void FixBadConnections(MapError *pError) +{ + CMapEntity *pEntity = (CMapEntity *)pError->pObjects[0]; + CEntityConnection::FixBadConnections(pEntity, (Options.general.bCheckVisibleMapErrors == TRUE)); +} + + +//----------------------------------------------------------------------------- +// Purpose: Fixes a race condition caused by a Kill input being triggered at the +// same instant as another input. +// Input : pError - +//----------------------------------------------------------------------------- +static void FixKillInputRaceCondition(MapError *pError) +{ + CEntityConnection *pConn = (CEntityConnection *)pError->pObjects[1]; + + // Delay the Kill command so that it arrives after the other command, + // solving the race condition. + pConn->SetDelay(pConn->GetDelay() + 0.01); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : pError - +//----------------------------------------------------------------------------- +static void FixOverlayFaceList( MapError *pError ) +{ + CMapEntity *pEntity = static_cast<CMapEntity*>( pError->pObjects[0] ); + if ( !pEntity ) + return; + + const CMapObjectList *pChildren = pEntity->GetChildren(); + + FOR_EACH_OBJ( *pChildren, pos ) + { + CMapOverlay *pOverlay = dynamic_cast<CMapOverlay*>( pChildren->Element(pos) ); + if ( pOverlay ) + { + // Destroy itself. + CMapDoc *pDoc = CMapDoc::GetActiveMapDoc(); + pDoc->RemoveObjectFromWorld( pEntity, true ); + GetHistory()->KeepForDestruction( pEntity ); + return; + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : pError - +//----------------------------------------------------------------------------- +static void FixEmptyEntity(MapError *pError) +{ + CMapClass *pKillMe = pError->pObjects[0]; + + if (pKillMe->GetParent() != NULL) + { + GetHistory()->KeepForDestruction(pKillMe); + pKillMe->GetParent()->RemoveChild(pKillMe); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Fixes duplicate node IDs by assigning the entity a unique node ID. +// Input : pError - Holds the world and the entity that is in error. +//----------------------------------------------------------------------------- +static void FixDuplicateNodeIDs(MapError *pError) +{ + CMapEntity *pEntity = (CMapEntity *)pError->pObjects[0]; + pEntity->AssignNodeID(); +} + + +//----------------------------------------------------------------------------- +// Purpose: Clears a bad target reference from the given entity. +// Input : pError - +//----------------------------------------------------------------------------- +static void FixMissingTarget(MapError *pError) +{ + CMapEntity *pEntity = (CMapEntity *)pError->pObjects[0]; + const char *pszKey = (const char *)pError->dwExtra; + pEntity->SetKeyValue(pszKey, NULL); +} + + +//----------------------------------------------------------------------------- +// Purpose: Fix a an invalid visgroup state. This is either: +// 1) A group that is hidden +// 2) An object that is hidden but not in any visgroups +//----------------------------------------------------------------------------- +void FixHiddenObject(MapError *pError) +{ + CMapClass *pObject = pError->pObjects[0]; + + // Tweak the object's visgroup state directly to avoid changing the + // hidden/shown state of the object's children. + pObject->m_bVisGroupShown = true; + pObject->m_bVisGroupAutoShown = true; + pObject->m_VisGroups.RemoveAll(); + + // Create a new visgroup to out the objects in (for hiding or inspection/deletion). + CMapObjectList Objects; + Objects.AddToTail(pObject); + CMapDoc *pDoc = CMapDoc::GetActiveMapDoc(); + + if ((pError->Type == ErrorHiddenGroupHiddenChildren) || + (pError->Type == ErrorHiddenObjectNoVisGroup)) + { + // The objects aren't in the compile, so just hide them. + pDoc->VisGroups_CreateNamedVisGroup(Objects, "_hidden by Check for Problems", true, false); + } + else if (pError->Type == ErrorIllegallyHiddenObject) + { + // Do nothing, the object is now shown. + } + else + { + // ErrorHiddenGroupVisibleChildren + // ErrorHiddenGroupMixedChildren + // ErrorHiddenChildOfEntity + + // The objects either ARE in the compile, or they can't be hidden in a visgroup. + // Don't hide them, just stick them in a visgroup for inspection + pDoc->VisGroups_CreateNamedVisGroup(Objects, "found by Check for Problems", false, false); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Checks the map for problems. Returns true if the map is okay, +// false if problems were found. +//----------------------------------------------------------------------------- +bool CMapCheckDlg::DoCheck(void) +{ + CMapWorld *pWorld = GetActiveWorld(); + + // Clear error list + KillErrorList(); + + // Map validation + CheckRequirements(&m_Errors, pWorld); + + // Solid validation + CheckMixedFaces(&m_Errors, pWorld); + //CheckDuplicatePlanes(&m_Errors, pWorld); + CheckDuplicateFaceIDs(&m_Errors, pWorld); + CheckDuplicateNodeIDs(&m_Errors, pWorld); + CheckSolidIntegrity(&m_Errors, pWorld); + CheckSolidContents(&m_Errors, pWorld); + CheckInvalidTextures(&m_Errors, pWorld); + + // Entity validation + CheckUnusedKeyvalues(&m_Errors, pWorld); + CheckEmptyEntities(&m_Errors, pWorld); + CheckMissingTargets(&m_Errors, pWorld); + CheckBadConnections(&m_Errors, pWorld); + + CheckVisGroups(&m_Errors, pWorld); + + CheckOverlayFaceList(&m_Errors, pWorld); + + if (!m_Errors.GetCount()) + { + AfxMessageBox("No errors were found."); + EndDialog(IDOK); + return true; + } + + return false; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CMapCheckDlg::OnOK() +{ + DestroyWindow(); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CMapCheckDlg::OnClose() +{ + DestroyWindow(); +} + + +//----------------------------------------------------------------------------- +// Purpose: Called when our window is being destroyed. +//----------------------------------------------------------------------------- +void CMapCheckDlg::OnDestroy() +{ + delete this; + s_pDlg = NULL; +} |