summaryrefslogtreecommitdiff
path: root/hammer/mapcheckdlg.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'hammer/mapcheckdlg.cpp')
-rw-r--r--hammer/mapcheckdlg.cpp1772
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;
+}