]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - contrib/bobtoolz/funchandlers.cpp
transfer from internal tree r5311 branches/1.4-gpl
[xonotic/netradiant.git] / contrib / bobtoolz / funchandlers.cpp
1 /*\r
2 BobToolz plugin for GtkRadiant\r
3 Copyright (C) 2001 Gordon Biggans\r
4 \r
5 This library is free software; you can redistribute it and/or\r
6 modify it under the terms of the GNU Lesser General Public\r
7 License as published by the Free Software Foundation; either\r
8 version 2.1 of the License, or (at your option) any later version.\r
9 \r
10 This library is distributed in the hope that it will be useful,\r
11 but WITHOUT ANY WARRANTY; without even the implied warranty of\r
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\r
13 Lesser General Public License for more details.\r
14 \r
15 You should have received a copy of the GNU Lesser General Public\r
16 License along with this library; if not, write to the Free Software\r
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r
18 */\r
19 \r
20 #include "StdAfx.h"\r
21 \r
22 #include "funchandlers.h"\r
23 \r
24 #include "IntersectDialog.h"\r
25 #include "PolygonDialog.h"\r
26 #include "StairDialog.h"\r
27 #include "DoorDialog.h"\r
28 #include "IntersectInfoDialog.h"\r
29 #include "BrushCheckDialog.h"\r
30 #include "AutoCaulkDialog.h"\r
31 #include "AutoCaulkStartDialog.h"\r
32 #include "TextureResetDialog.h"\r
33 #include "pathplotterdialog.h"\r
34 \r
35 #include "DEntity.h"\r
36 #include "shapes.h"\r
37 #include "lists.h"\r
38 #include "misc.h"\r
39 #include "DShape.h"\r
40 \r
41 // for autocaulk\r
42 list<Str> exclusionList;                // whole brush exclusion\r
43 list<Str> exclusionList_Face;   // single face exclusion\r
44 \r
45 BOOL el1Loaded;\r
46 BOOL el2Loaded;\r
47 \r
48 DBobView*       g_PathView = NULL;\r
49 // -------------\r
50 \r
51 /************************\r
52         Global Variables\r
53 ************************/\r
54 \r
55 CPolygonDialog                  polygonDlg;\r
56 CIntersectDialog                intrDlg;\r
57 CStairDialog                    stairDlg;\r
58 CDoorDialog                             doorDlg;\r
59 CAutoCaulkStartDialog   autocaulkDlg;\r
60 CTextureResetDialog             texRstDlg;\r
61 CPathPlotterDialog              ppDlg;\r
62 \r
63 /************************\r
64         --Main Functions--\r
65 ************************/\r
66 \r
67 void LoadLists()\r
68 {\r
69         char buffer[256];\r
70 \r
71         if(!el1Loaded)\r
72                 el1Loaded = LoadExclusionList(GetFilename(buffer, "bt\\bt-el1.txt"), &exclusionList);\r
73         if(!el2Loaded)\r
74                 el2Loaded = LoadExclusionList(GetFilename(buffer, "bt\\bt-el2.txt"), &exclusionList);\r
75 }\r
76 \r
77 void PolygonBuilder(vec3_t vMin, vec3_t vMax)\r
78 {\r
79         // ensure we have something selected\r
80         if( g_FuncTable.m_pfnSelectedBrushCount() != 1 )\r
81         {\r
82                 MessageBox(NULL, "Invalid number of brushes selected, chose 1 only", "Error", MB_OK);\r
83                 return; \r
84         }\r
85 \r
86         // tell Radiant we want to access the selected brushes\r
87         g_FuncTable.m_pfnAllocateSelectedBrushHandles();\r
88                         \r
89         // get handle to size definition brush\r
90         brush_t *brush = (brush_t*)g_FuncTable.m_pfnGetSelectedBrushHandle(0);\r
91         // cant release until we delete the brush, if we do...\r
92 \r
93         // ask user for type, size, etc....\r
94         if(polygonDlg.DoModal() == IDOK)\r
95         {\r
96                 DShape poly;\r
97 \r
98                 g_FuncTable.m_pfnDeleteBrushHandle(brush);\r
99 \r
100                 if(polygonDlg.m_bInverse)\r
101                         poly.BuildInversePrism(vMin, vMax, polygonDlg.m_nSideCount, polygonDlg.m_bAlignTop);\r
102                 else\r
103                 {\r
104                         if(polygonDlg.m_bBorder)\r
105                                 poly.BuildBorderedPrism(vMin, vMax, polygonDlg.m_nSideCount, polygonDlg.m_nBorderSize, polygonDlg.m_bAlignTop);\r
106                         else\r
107                                 poly.BuildRegularPrism(vMin, vMax, polygonDlg.m_nSideCount, polygonDlg.m_bAlignTop);\r
108                 }\r
109 \r
110                 poly.Commit();\r
111         }\r
112 \r
113 \r
114         g_FuncTable.m_pfnReleaseSelectedBrushHandles();\r
115 }\r
116 \r
117 \r
118 void IntersectionFinder()\r
119 {\r
120         if(intrDlg.DoModal() == IDCANCEL)\r
121                 return;\r
122 \r
123         if(intrDlg.m_nBrushOptions == BRUSH_OPT_SELECTED)\r
124         {\r
125                 // ensure we have enough brushes selected\r
126                 if( g_FuncTable.m_pfnSelectedBrushCount() < 2 )\r
127                 {\r
128                         MessageBox(NULL, "Invalid number of brushes selected, choose at least 2", "Error", MB_OK);\r
129                         return; \r
130                 }\r
131         }\r
132 \r
133         CIntersectInfoDialog*   intrInfoDlg = new CIntersectInfoDialog();\r
134         intrInfoDlg->Create(IDD_INTERSECT_INFO_DIALOG);\r
135 \r
136         DEntity world;\r
137 \r
138         switch(intrDlg.m_nBrushOptions)\r
139         {\r
140         case BRUSH_OPT_SELECTED:\r
141                 {\r
142                         world.LoadSelectedBrushes(&intrInfoDlg->m_prog1);\r
143                         break;\r
144                 }\r
145         case BRUSH_OPT_WHOLE_MAP:\r
146                 {\r
147                         world.LoadFromEntity(0, &intrInfoDlg->m_prog1);\r
148                         break;\r
149                 }\r
150         }\r
151 \r
152         world.RemoveNonCheckBrushes(&exclusionList, intrDlg.m_bUseDetail);\r
153         BOOL* pbSelectList;\r
154         if(intrDlg.m_bDuplicateOnly)\r
155                 pbSelectList = world.BuildDuplicateList();\r
156         else\r
157                 pbSelectList = world.BuildIntersectList();\r
158 \r
159         world.SelectBrushes(pbSelectList);\r
160                 \r
161         intrInfoDlg->DestroyWindow();\r
162         delete[] pbSelectList;\r
163 }\r
164 \r
165 void StairBuilder(vec3_t vMin, vec3_t vMax)\r
166 {\r
167         // ensure we have something selected\r
168         if( g_FuncTable.m_pfnSelectedBrushCount() != 1 )\r
169         {\r
170                 MessageBox(NULL, "Invalid number of brushes selected, chose 1 only", "Error", MB_OK);\r
171                 return; \r
172         }\r
173 \r
174         // tell Radiant we want to access the selected brushes\r
175         g_FuncTable.m_pfnAllocateSelectedBrushHandles();\r
176                         \r
177         // get handle to size definition brush\r
178         brush_t *brush = (brush_t*)g_FuncTable.m_pfnGetSelectedBrushHandle(0);\r
179         // cant release until we delete the brush, if we do...\r
180 \r
181 \r
182         // ask user for type, size, etc....\r
183         if(stairDlg.DoModal() == IDOK)\r
184         {\r
185                 \r
186                 // calc brush size\r
187                 vec3_t size;\r
188                 _VectorSubtract(vMax, vMin, size);\r
189 \r
190 \r
191                 if(((int)size[2] % stairDlg.m_nStairHeight) != 0)\r
192                 {\r
193                         // stairs must fit evenly into brush\r
194                         MessageBox(NULL, "Invalid stair height\nHeight of block must be divisable by stair height", "Error", MB_OK);\r
195                 }\r
196                 else\r
197                 {\r
198                         \r
199                         // Remove Size Brush\r
200                         g_FuncTable.m_pfnDeleteBrushHandle(brush);\r
201 \r
202                                                 \r
203                         // Get Step Count, Direction of Stairs, Stair Style\r
204                         int numSteps = (int)size[2] / stairDlg.m_nStairHeight;\r
205                         int direction = stairDlg.m_StairDir;\r
206                         int style = stairDlg.m_StairStyle;\r
207 \r
208                         if(stairDlg.m_StairStyle == STYLE_CORNER)\r
209                         {\r
210                                 BuildCornerStairs(vMin, vMax, numSteps, "textures/common/caulk", (LPCTSTR)stairDlg.m_riserTexture);\r
211                         }\r
212                         else\r
213                         {\r
214                                 // Get Step Dimensions\r
215                                 float stairHeight = (float)stairDlg.m_nStairHeight;\r
216                                 float stairWidth;\r
217                                 if((direction == MOVE_EAST) || (direction == MOVE_WEST))\r
218                                         stairWidth = (size[0])/numSteps;\r
219                                 else\r
220                                         stairWidth = (size[1])/numSteps;\r
221 \r
222 \r
223                                 // Build Base For Stair (bob's style)\r
224                                 if(style == STYLE_BOB)\r
225                                         Build_Wedge(direction, vMin, vMax, TRUE);\r
226 \r
227 \r
228                                 // Set First Step Starting Position\r
229                                 vMax[2] = vMin[2] + stairHeight;\r
230                                 SetInitialStairPos(direction, vMin, vMax, stairWidth);\r
231 \r
232 \r
233                                 // Build The Steps\r
234                                 for(int i = 0; i < numSteps; i++)\r
235                                 {\r
236                                         if(style == STYLE_BOB)\r
237                                                 Build_StairStep_Wedge(direction, vMin, vMax, "textures/common/caulk", (LPCTSTR)stairDlg.m_riserTexture, stairDlg.m_bDetail);\r
238                                         else if(style == STYLE_ORIGINAL)\r
239                                                 Build_StairStep(vMin, vMax, "textures/common/caulk", (LPCTSTR)stairDlg.m_riserTexture, direction);\r
240 \r
241                                         // get step into next position\r
242                                         MoveBlock(direction, vMin, vMax, stairWidth);\r
243                                         vMax[2] += stairHeight;\r
244                                         if(style == STYLE_BOB)\r
245                                                 vMin[2] += stairHeight; // wedge bottom must be raised\r
246                                 }\r
247                         }\r
248                 }\r
249         }\r
250 \r
251         g_FuncTable.m_pfnReleaseSelectedBrushHandles();\r
252 }\r
253 \r
254 void DoorBuilder(vec3_t vMin, vec3_t vMax)\r
255 {\r
256         // ensure we have something selected\r
257         if( g_FuncTable.m_pfnSelectedBrushCount() != 1 )\r
258         {\r
259                 MessageBox(NULL, "Invalid number of brushes selected, chose 1 only", "Error", MB_OK);\r
260                 return; \r
261         }\r
262 \r
263         // tell Radiant we want to access the selected brushes\r
264         g_FuncTable.m_pfnAllocateSelectedBrushHandles();\r
265                         \r
266         // get handle to size definition brush\r
267         brush_t *brush = (brush_t*)g_FuncTable.m_pfnGetSelectedBrushHandle(0);\r
268         // cant release until we delete the brush, if we do...\r
269 \r
270 \r
271 \r
272         strcpy(doorDlg.m_fbTextureName.GetBuffer(256), g_FuncTable.m_pfnGetCurrentTexture());\r
273         \r
274         if(doorDlg.DoModal() == IDOK)\r
275         {\r
276                 g_FuncTable.m_pfnDeleteBrushHandle(brush);\r
277 \r
278                 BuildDoorsX2(vMin, vMax, \r
279                         doorDlg.m_bSclMainHor, doorDlg.m_bSclMainVert,\r
280                         doorDlg.m_bSclTrimHor, doorDlg.m_bSclTrimVert,\r
281                         (LPCTSTR)doorDlg.m_fbTextureName, \r
282                         (LPCTSTR)doorDlg.m_trimTextureName,\r
283                         doorDlg.m_doorDirection);\r
284         }\r
285 \r
286         g_FuncTable.m_pfnReleaseSelectedBrushHandles();\r
287 }\r
288 \r
289 void FixBrushes()\r
290 {\r
291         DEntity world;\r
292 \r
293         CIntersectInfoDialog*   intrInfoDlg = new CIntersectInfoDialog();\r
294         intrInfoDlg->Create(IDD_INTERSECT_INFO_DIALOG);\r
295 \r
296         world.LoadFromEntity(0, &intrInfoDlg->m_prog1);\r
297 \r
298         intrInfoDlg->DestroyWindow();\r
299                         \r
300         CBrushCheckDialog*      chkDlg = new CBrushCheckDialog();\r
301         chkDlg->Create(IDD_BRUSHCHECKER_DIALOG);\r
302 \r
303         int count = world.FixBrushes(TRUE, &chkDlg->m_prog1);\r
304 \r
305         chkDlg->DestroyWindow();\r
306 \r
307         Sys_Printf("%i invalid/duplicate planes removed\n", count);\r
308 }\r
309 \r
310 void AutoCaulk()\r
311 {\r
312         if(!el1Loaded)\r
313                 autocaulkDlg.m_Warning1 = "WARNING: Brush exclusion list not found\n, ALL BRUSHES WILL BE USED";\r
314 \r
315         if(autocaulkDlg.DoModal() == IDCANCEL)\r
316                 return;\r
317 \r
318         if(autocaulkDlg.m_nMode == MODE_AC_BUILD_MINI_PRT)\r
319         {\r
320                 BuildMiniPrt(&exclusionList);\r
321                 return;\r
322         }\r
323 \r
324         CAutoCaulkDialog* acDlg = new CAutoCaulkDialog;\r
325         acDlg->Create(IDD_AUTOCAULK_DIALOG);\r
326 \r
327         char filename[1204];\r
328 \r
329         if(autocaulkDlg.m_nMode == MODE_AC_NORMAL)\r
330         {\r
331                 char* rad_filename = g_BSPTable.m_pfnGetMapName();\r
332                 if(!rad_filename)\r
333                 {\r
334                         MessageBox(NULL, "An Error Occurred While Trying To Get The Map Filename", "Error", MB_OK);\r
335                         acDlg->DestroyWindow();\r
336                         return;\r
337                 }\r
338 \r
339                 strcpy(filename, rad_filename);\r
340         \r
341                 char* ext = strrchr(filename, '.')+1;\r
342                 strcpy(ext, "prt");// rename the extension\r
343         }\r
344         else\r
345   {\r
346     IEpair* pEp = g_EpairTable.m_pfnIEpairForProjectKeys();\r
347     char *pn = pEp->ValueForKey("mapspath");\r
348     pEp->DecRef();\r
349 \r
350           strcpy( filename, pn );\r
351           strcat( filename, "/ac_prt.prt" );\r
352   }\r
353 \r
354         DEntity portals;\r
355         if(!portals.LoadFromPrt(filename, &acDlg->m_prog1))\r
356         {\r
357                 MessageBox(NULL, "Failed To Load Portal File", "Error", MB_OK);\r
358                 acDlg->DestroyWindow();\r
359                 return;\r
360         }\r
361         // load portal file\r
362 \r
363         CIntersectInfoDialog*   intrInfoDlg = new CIntersectInfoDialog();\r
364         intrInfoDlg->Create(IDD_INTERSECT_INFO_DIALOG);\r
365 \r
366         DEntity world;\r
367 \r
368         world.LoadFromEntity(0, &intrInfoDlg->m_prog1);\r
369         intrInfoDlg->DestroyWindow();\r
370 \r
371         if(autocaulkDlg.m_nMode == MODE_AC_NORMAL)\r
372                 world.RemoveNonCheckBrushes(&exclusionList, FALSE);\r
373         else\r
374                 world.RemoveNonCheckBrushes(&exclusionList, TRUE);\r
375 \r
376         world.ResetChecks(&exclusionList_Face);\r
377 \r
378         int caulkedCount = 0;\r
379         int killCnt = world.AutoCaulk(&portals, autocaulkDlg.m_bAllowDestruction, &caulkedCount, &acDlg->m_prog2);\r
380 \r
381         if(autocaulkDlg.m_bAllowDestruction)\r
382                 Sys_Printf("%i unrequired brush(es) killed\n", killCnt);\r
383         Sys_Printf("%i face(s) caulked\n", caulkedCount);\r
384 \r
385         acDlg->DestroyWindow();\r
386 }\r
387 \r
388 void ResetTextures()\r
389 {\r
390         texRstDlg.m_TextureName = GetCurrentTexture();\r
391         texRstDlg.m_NewTextureName = GetCurrentTexture();\r
392 \r
393         if(texRstDlg.DoModal() == IDCANCEL)\r
394                 return;\r
395 \r
396         float fScale[2];\r
397         float fShift[2];\r
398         fScale[1] = texRstDlg.m_fScaleVertical;\r
399         fScale[0] = texRstDlg.m_fScaleHorizontal;\r
400 \r
401         fShift[1] = (float)texRstDlg.m_nShiftVertical;\r
402         fShift[0] = (float)texRstDlg.m_nShiftHorizontal;\r
403 \r
404         DEntity world;\r
405         world.LoadFromEntity(0, NULL);\r
406 \r
407         if(texRstDlg.m_bAllTextures)\r
408                 world.ResetTextures(NULL, fScale, fShift, texRstDlg.m_nRotation, texRstDlg.m_NewTextureName, texRstDlg.m_bOnlyTexture);\r
409         else            \r
410                 world.ResetTextures(texRstDlg.m_TextureName, fScale, fShift, texRstDlg.m_nRotation, texRstDlg.m_NewTextureName, texRstDlg.m_bOnlyTexture);\r
411 }\r
412 \r
413 void PathPlotter()\r
414 {\r
415         int ret = ppDlg.DoModal();\r
416         if(ret == IDCANCEL)\r
417                 return;\r
418         if(ret == IDNO)\r
419         {\r
420                 if(g_PathView)\r
421                         delete g_PathView;\r
422 \r
423                 return;\r
424         }\r
425 \r
426         if( g_FuncTable.m_pfnSelectedBrushCount() != 1)\r
427         {\r
428                 MessageBox(NULL, "Invalid number of brushes selected, chose 1 only", "Error", MB_OK);\r
429                 return;\r
430         }\r
431 \r
432         // tell Radiant we want to access the selected brushes\r
433         g_FuncTable.m_pfnAllocateSelectedBrushHandles();\r
434                         \r
435         // get handle to size definition brush\r
436         brush_t *brush = (brush_t*)g_FuncTable.m_pfnGetSelectedBrushHandle(0);\r
437         // cant release until we delete the brush, if we do...\r
438 \r
439         DEntity world;\r
440         world.LoadEPairList(*g_FuncTable.m_pfnGetEntityKeyValList(brush->owner));\r
441 \r
442         DEPair* trigger_ep = world.FindEPairByKey("targetname");\r
443 \r
444         if(trigger_ep)\r
445         {\r
446                 if(!strcmp(world.m_Classname, "trigger_push"))\r
447                 {\r
448                         DEPair* target_ep = world.FindEPairByKey("target");\r
449                         if(target_ep)\r
450                         {\r
451                                 entity_s* entTarget = FindEntityFromTargetname(target_ep->value);\r
452                                 if(entTarget)\r
453                                 {\r
454                                         if(g_PathView)\r
455                                                 delete g_PathView;\r
456                                         g_PathView = new DBobView;\r
457 \r
458                                         g_PathView->Begin(trigger_ep->value, target_ep->value, ppDlg.m_fMultiplier, ppDlg.m_nPoints, ppDlg.m_fGravity, ppDlg.m_bNoUpdate, ppDlg.m_bShowExtra);\r
459                                 }\r
460                                 else\r
461                                         MessageBox(NULL, "trigger_push target could not be found.", "Error", MB_OK);\r
462                         }\r
463                         else\r
464                                 MessageBox(NULL, "trigger_push has no target.", "Error", MB_OK);\r
465                 }\r
466                 else\r
467                         MessageBox(NULL, "You must select a 'trigger_push' entity.", "Error", MB_OK);\r
468         }       \r
469         else\r
470                 MessageBox(NULL, "Entity must have a targetname", "Error", MB_OK);\r
471 \r
472         g_FuncTable.m_pfnReleaseSelectedBrushHandles();\r
473 }\r
474 \r
475 void PitBuilder(vec3_t vMin, vec3_t vMax)\r
476 {\r
477         // ensure we have something selected\r
478         if( g_FuncTable.m_pfnSelectedBrushCount() != 1 )\r
479         {\r
480                 MessageBox(NULL, "Invalid number of brushes selected, chose 1 only", "Error", MB_OK);\r
481                 return; \r
482         }\r
483 \r
484         // tell Radiant we want to access the selected brushes\r
485         g_FuncTable.m_pfnAllocateSelectedBrushHandles();\r
486                         \r
487         // get handle to size definition brush\r
488         brush_t *brush = (brush_t*)g_FuncTable.m_pfnGetSelectedBrushHandle(0);\r
489         // cant release until we delete the brush, if we do...\r
490 \r
491         DShape pit;\r
492 \r
493         if(pit.BuildPit(vMin, vMax))\r
494         {\r
495                 pit.Commit();\r
496 \r
497                 g_FuncTable.m_pfnDeleteBrushHandle(brush);\r
498         }\r
499         else\r
500                 MessageBox(NULL, "Failed To Make Pit\nTry Making The Brush Bigger", "Error", MB_OK);\r
501 \r
502         g_FuncTable.m_pfnReleaseSelectedBrushHandles();\r
503 }\r