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