/* BobToolz plugin for GtkRadiant Copyright (C) 2001 Gordon Biggans This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "StdAfx.h" #include "funchandlers.h" #include "IntersectDialog.h" #include "PolygonDialog.h" #include "StairDialog.h" #include "DoorDialog.h" #include "IntersectInfoDialog.h" #include "BrushCheckDialog.h" #include "AutoCaulkDialog.h" #include "AutoCaulkStartDialog.h" #include "TextureResetDialog.h" #include "pathplotterdialog.h" #include "DEntity.h" #include "shapes.h" #include "lists.h" #include "misc.h" #include "DShape.h" // for autocaulk std::list exclusionList; // whole brush exclusion std::list exclusionList_Face; // single face exclusion BOOL el1Loaded; BOOL el2Loaded; DBobView *g_PathView = NULL; // ------------- /************************ Global Variables ************************/ CPolygonDialog polygonDlg; CIntersectDialog intrDlg; CStairDialog stairDlg; CDoorDialog doorDlg; CAutoCaulkStartDialog autocaulkDlg; CTextureResetDialog texRstDlg; CPathPlotterDialog ppDlg; /************************ --Main Functions-- ************************/ void LoadLists() { char buffer[256]; if (!el1Loaded) { el1Loaded = LoadExclusionList(GetFilename(buffer, "bt\\bt-el1.txt"), &exclusionList); } if (!el2Loaded) { el2Loaded = LoadExclusionList(GetFilename(buffer, "bt\\bt-el2.txt"), &exclusionList); } } void PolygonBuilder(vec3_t vMin, vec3_t vMax) { // ensure we have something selected if (g_FuncTable.m_pfnSelectedBrushCount() != 1) { MessageBox(NULL, "Invalid number of brushes selected, chose 1 only", "Error", MB_OK); return; } // tell Radiant we want to access the selected brushes g_FuncTable.m_pfnAllocateSelectedBrushHandles(); // get handle to size definition brush brush_t *brush = (brush_t *) g_FuncTable.m_pfnGetSelectedBrushHandle(0); // cant release until we delete the brush, if we do... // ask user for type, size, etc.... if (polygonDlg.DoModal() == IDOK) { DShape poly; g_FuncTable.m_pfnDeleteBrushHandle(brush); if (polygonDlg.m_bInverse) { poly.BuildInversePrism(vMin, vMax, polygonDlg.m_nSideCount, polygonDlg.m_bAlignTop); } else { if (polygonDlg.m_bBorder) { poly.BuildBorderedPrism(vMin, vMax, polygonDlg.m_nSideCount, polygonDlg.m_nBorderSize, polygonDlg.m_bAlignTop); } else { poly.BuildRegularPrism(vMin, vMax, polygonDlg.m_nSideCount, polygonDlg.m_bAlignTop); } } poly.Commit(); } g_FuncTable.m_pfnReleaseSelectedBrushHandles(); } void IntersectionFinder() { if (intrDlg.DoModal() == IDCANCEL) { return; } if (intrDlg.m_nBrushOptions == BRUSH_OPT_SELECTED) { // ensure we have enough brushes selected if (g_FuncTable.m_pfnSelectedBrushCount() < 2) { MessageBox(NULL, "Invalid number of brushes selected, choose at least 2", "Error", MB_OK); return; } } CIntersectInfoDialog *intrInfoDlg = new CIntersectInfoDialog(); intrInfoDlg->Create(IDD_INTERSECT_INFO_DIALOG); DEntity world; switch (intrDlg.m_nBrushOptions) { case BRUSH_OPT_SELECTED: { world.LoadSelectedBrushes(&intrInfoDlg->m_prog1); break; } case BRUSH_OPT_WHOLE_MAP: { world.LoadFromEntity(0, &intrInfoDlg->m_prog1); break; } } world.RemoveNonCheckBrushes(&exclusionList, intrDlg.m_bUseDetail); BOOL *pbSelectList; if (intrDlg.m_bDuplicateOnly) { pbSelectList = world.BuildDuplicateList(); } else { pbSelectList = world.BuildIntersectList(); } world.SelectBrushes(pbSelectList); intrInfoDlg->DestroyWindow(); delete[] pbSelectList; } void StairBuilder(vec3_t vMin, vec3_t vMax) { // ensure we have something selected if (g_FuncTable.m_pfnSelectedBrushCount() != 1) { MessageBox(NULL, "Invalid number of brushes selected, chose 1 only", "Error", MB_OK); return; } // tell Radiant we want to access the selected brushes g_FuncTable.m_pfnAllocateSelectedBrushHandles(); // get handle to size definition brush brush_t *brush = (brush_t *) g_FuncTable.m_pfnGetSelectedBrushHandle(0); // cant release until we delete the brush, if we do... // ask user for type, size, etc.... if (stairDlg.DoModal() == IDOK) { // calc brush size vec3_t size; _VectorSubtract(vMax, vMin, size); if (((int) size[2] % stairDlg.m_nStairHeight) != 0) { // stairs must fit evenly into brush MessageBox(NULL, "Invalid stair height\nHeight of block must be divisable by stair height", "Error", MB_OK); } else { // Remove Size Brush g_FuncTable.m_pfnDeleteBrushHandle(brush); // Get Step Count, Direction of Stairs, Stair Style int numSteps = (int) size[2] / stairDlg.m_nStairHeight; int direction = stairDlg.m_StairDir; int style = stairDlg.m_StairStyle; if (stairDlg.m_StairStyle == STYLE_CORNER) { BuildCornerStairs(vMin, vMax, numSteps, "textures/common/caulk", (LPCTSTR) stairDlg.m_riserTexture); } else { // Get Step Dimensions float stairHeight = (float) stairDlg.m_nStairHeight; float stairWidth; if ((direction == MOVE_EAST) || (direction == MOVE_WEST)) { stairWidth = (size[0]) / numSteps; } else { stairWidth = (size[1]) / numSteps; } // Build Base For Stair (bob's style) if (style == STYLE_BOB) { Build_Wedge(direction, vMin, vMax, TRUE); } // Set First Step Starting Position vMax[2] = vMin[2] + stairHeight; SetInitialStairPos(direction, vMin, vMax, stairWidth); // Build The Steps for (int i = 0; i < numSteps; i++) { if (style == STYLE_BOB) { Build_StairStep_Wedge(direction, vMin, vMax, "textures/common/caulk", (LPCTSTR) stairDlg.m_riserTexture, stairDlg.m_bDetail); } else if (style == STYLE_ORIGINAL) { Build_StairStep(vMin, vMax, "textures/common/caulk", (LPCTSTR) stairDlg.m_riserTexture, direction); } // get step into next position MoveBlock(direction, vMin, vMax, stairWidth); vMax[2] += stairHeight; if (style == STYLE_BOB) { vMin[2] += stairHeight; // wedge bottom must be raised } } } } } g_FuncTable.m_pfnReleaseSelectedBrushHandles(); } void DoorBuilder(vec3_t vMin, vec3_t vMax) { // ensure we have something selected if (g_FuncTable.m_pfnSelectedBrushCount() != 1) { MessageBox(NULL, "Invalid number of brushes selected, chose 1 only", "Error", MB_OK); return; } // tell Radiant we want to access the selected brushes g_FuncTable.m_pfnAllocateSelectedBrushHandles(); // get handle to size definition brush brush_t *brush = (brush_t *) g_FuncTable.m_pfnGetSelectedBrushHandle(0); // cant release until we delete the brush, if we do... strcpy(doorDlg.m_fbTextureName.GetBuffer(256), g_FuncTable.m_pfnGetCurrentTexture()); if (doorDlg.DoModal() == IDOK) { g_FuncTable.m_pfnDeleteBrushHandle(brush); BuildDoorsX2(vMin, vMax, doorDlg.m_bSclMainHor, doorDlg.m_bSclMainVert, doorDlg.m_bSclTrimHor, doorDlg.m_bSclTrimVert, (LPCTSTR) doorDlg.m_fbTextureName, (LPCTSTR) doorDlg.m_trimTextureName, doorDlg.m_doorDirection); } g_FuncTable.m_pfnReleaseSelectedBrushHandles(); } void FixBrushes() { DEntity world; CIntersectInfoDialog *intrInfoDlg = new CIntersectInfoDialog(); intrInfoDlg->Create(IDD_INTERSECT_INFO_DIALOG); world.LoadFromEntity(0, &intrInfoDlg->m_prog1); intrInfoDlg->DestroyWindow(); CBrushCheckDialog *chkDlg = new CBrushCheckDialog(); chkDlg->Create(IDD_BRUSHCHECKER_DIALOG); int count = world.FixBrushes(TRUE, &chkDlg->m_prog1); chkDlg->DestroyWindow(); Sys_Printf("%i invalid/duplicate planes removed\n", count); } void AutoCaulk() { UndoableCommand undo("bobToolz.autoCaulk"); if (!el1Loaded) { autocaulkDlg.m_Warning1 = "WARNING: Brush exclusion list not found\n, ALL BRUSHES WILL BE USED"; } if (autocaulkDlg.DoModal() == IDCANCEL) { return; } if (autocaulkDlg.m_nMode == MODE_AC_BUILD_MINI_PRT) { BuildMiniPrt(&exclusionList); return; } CAutoCaulkDialog *acDlg = new CAutoCaulkDialog; acDlg->Create(IDD_AUTOCAULK_DIALOG); char filename[1204]; if (autocaulkDlg.m_nMode == MODE_AC_NORMAL) { char *rad_filename = g_BSPTable.m_pfnGetMapName(); if (!rad_filename) { MessageBox(NULL, "An Error Occurred While Trying To Get The Map Filename", "Error", MB_OK); acDlg->DestroyWindow(); return; } strcpy(filename, rad_filename); char *ext = strrchr(filename, '.') + 1; strcpy(ext, "prt"); // rename the extension } else { IEpair *pEp = g_EpairTable.m_pfnIEpairForProjectKeys(); char *pn = pEp->ValueForKey("mapspath"); pEp->DecRef(); strcpy(filename, pn); strcat(filename, "/ac_prt.prt"); } DEntity portals; if (!portals.LoadFromPrt(filename, &acDlg->m_prog1)) { MessageBox(NULL, "Failed To Load Portal File", "Error", MB_OK); acDlg->DestroyWindow(); return; } // load portal file CIntersectInfoDialog *intrInfoDlg = new CIntersectInfoDialog(); intrInfoDlg->Create(IDD_INTERSECT_INFO_DIALOG); DEntity world; world.LoadFromEntity(0, &intrInfoDlg->m_prog1); intrInfoDlg->DestroyWindow(); if (autocaulkDlg.m_nMode == MODE_AC_NORMAL) { world.RemoveNonCheckBrushes(&exclusionList, FALSE); } else { world.RemoveNonCheckBrushes(&exclusionList, TRUE); } world.ResetChecks(&exclusionList_Face); int caulkedCount = 0; int killCnt = world.AutoCaulk(&portals, autocaulkDlg.m_bAllowDestruction, &caulkedCount, &acDlg->m_prog2); if (autocaulkDlg.m_bAllowDestruction) { Sys_Printf("%i unrequired brush(es) killed\n", killCnt); } Sys_Printf("%i face(s) caulked\n", caulkedCount); acDlg->DestroyWindow(); } void ResetTextures() { texRstDlg.m_TextureName = GetCurrentTexture(); texRstDlg.m_NewTextureName = GetCurrentTexture(); if (texRstDlg.DoModal() == IDCANCEL) { return; } float fScale[2]; float fShift[2]; fScale[1] = texRstDlg.m_fScaleVertical; fScale[0] = texRstDlg.m_fScaleHorizontal; fShift[1] = (float) texRstDlg.m_nShiftVertical; fShift[0] = (float) texRstDlg.m_nShiftHorizontal; DEntity world; world.LoadFromEntity(0, NULL); if (texRstDlg.m_bAllTextures) { world.ResetTextures(NULL, fScale, fShift, texRstDlg.m_nRotation, texRstDlg.m_NewTextureName, texRstDlg.m_bOnlyTexture); } else { world.ResetTextures(texRstDlg.m_TextureName, fScale, fShift, texRstDlg.m_nRotation, texRstDlg.m_NewTextureName, texRstDlg.m_bOnlyTexture); } } void PathPlotter() { int ret = ppDlg.DoModal(); if (ret == IDCANCEL) { return; } if (ret == IDNO) { if (g_PathView) { delete g_PathView; } return; } if (g_FuncTable.m_pfnSelectedBrushCount() != 1) { MessageBox(NULL, "Invalid number of brushes selected, chose 1 only", "Error", MB_OK); return; } // tell Radiant we want to access the selected brushes g_FuncTable.m_pfnAllocateSelectedBrushHandles(); // get handle to size definition brush brush_t *brush = (brush_t *) g_FuncTable.m_pfnGetSelectedBrushHandle(0); // cant release until we delete the brush, if we do... DEntity world; world.LoadEPairList(*g_FuncTable.m_pfnGetEntityKeyValList(brush->owner)); DEPair *trigger_ep = world.FindEPairByKey("targetname"); if (trigger_ep) { if (!strcmp(world.m_Classname, "trigger_push")) { DEPair *target_ep = world.FindEPairByKey("target"); if (target_ep) { entity_s *entTarget = FindEntityFromTargetname(target_ep->value); if (entTarget) { if (g_PathView) { delete g_PathView; } g_PathView = new DBobView; g_PathView->Begin(trigger_ep->value, target_ep->value, ppDlg.m_fMultiplier, ppDlg.m_nPoints, ppDlg.m_fGravity, ppDlg.m_bNoUpdate, ppDlg.m_bShowExtra); } else { MessageBox(NULL, "trigger_push target could not be found.", "Error", MB_OK); } } else { MessageBox(NULL, "trigger_push has no target.", "Error", MB_OK); } } else { MessageBox(NULL, "You must select a 'trigger_push' entity.", "Error", MB_OK); } } else { MessageBox(NULL, "Entity must have a targetname", "Error", MB_OK); } g_FuncTable.m_pfnReleaseSelectedBrushHandles(); } void PitBuilder(vec3_t vMin, vec3_t vMax) { // ensure we have something selected if (g_FuncTable.m_pfnSelectedBrushCount() != 1) { MessageBox(NULL, "Invalid number of brushes selected, chose 1 only", "Error", MB_OK); return; } // tell Radiant we want to access the selected brushes g_FuncTable.m_pfnAllocateSelectedBrushHandles(); // get handle to size definition brush brush_t *brush = (brush_t *) g_FuncTable.m_pfnGetSelectedBrushHandle(0); // cant release until we delete the brush, if we do... DShape pit; if (pit.BuildPit(vMin, vMax)) { pit.Commit(); g_FuncTable.m_pfnDeleteBrushHandle(brush); } else { MessageBox(NULL, "Failed To Make Pit\nTry Making The Brush Bigger", "Error", MB_OK); } g_FuncTable.m_pfnReleaseSelectedBrushHandles(); }