ok
[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 list<Str> exclusionList;                // whole brush exclusion
43 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         if(!el1Loaded)
313                 autocaulkDlg.m_Warning1 = "WARNING: Brush exclusion list not found\n, ALL BRUSHES WILL BE USED";
314
315         if(autocaulkDlg.DoModal() == IDCANCEL)
316                 return;
317
318         if(autocaulkDlg.m_nMode == MODE_AC_BUILD_MINI_PRT)
319         {
320                 BuildMiniPrt(&exclusionList);
321                 return;
322         }
323
324         CAutoCaulkDialog* acDlg = new CAutoCaulkDialog;
325         acDlg->Create(IDD_AUTOCAULK_DIALOG);
326
327         char filename[1204];
328
329         if(autocaulkDlg.m_nMode == MODE_AC_NORMAL)
330         {
331                 char* rad_filename = g_BSPTable.m_pfnGetMapName();
332                 if(!rad_filename)
333                 {
334                         MessageBox(NULL, "An Error Occurred While Trying To Get The Map Filename", "Error", MB_OK);
335                         acDlg->DestroyWindow();
336                         return;
337                 }
338
339                 strcpy(filename, rad_filename);
340         
341                 char* ext = strrchr(filename, '.')+1;
342                 strcpy(ext, "prt");// rename the extension
343         }
344         else
345   {
346     IEpair* pEp = g_EpairTable.m_pfnIEpairForProjectKeys();
347     char *pn = pEp->ValueForKey("mapspath");
348     pEp->DecRef();
349
350           strcpy( filename, pn );
351           strcat( filename, "/ac_prt.prt" );
352   }
353
354         DEntity portals;
355         if(!portals.LoadFromPrt(filename, &acDlg->m_prog1))
356         {
357                 MessageBox(NULL, "Failed To Load Portal File", "Error", MB_OK);
358                 acDlg->DestroyWindow();
359                 return;
360         }
361         // load portal file
362
363         CIntersectInfoDialog*   intrInfoDlg = new CIntersectInfoDialog();
364         intrInfoDlg->Create(IDD_INTERSECT_INFO_DIALOG);
365
366         DEntity world;
367
368         world.LoadFromEntity(0, &intrInfoDlg->m_prog1);
369         intrInfoDlg->DestroyWindow();
370
371         if(autocaulkDlg.m_nMode == MODE_AC_NORMAL)
372                 world.RemoveNonCheckBrushes(&exclusionList, FALSE);
373         else
374                 world.RemoveNonCheckBrushes(&exclusionList, TRUE);
375
376         world.ResetChecks(&exclusionList_Face);
377
378         int caulkedCount = 0;
379         int killCnt = world.AutoCaulk(&portals, autocaulkDlg.m_bAllowDestruction, &caulkedCount, &acDlg->m_prog2);
380
381         if(autocaulkDlg.m_bAllowDestruction)
382                 Sys_Printf("%i unrequired brush(es) killed\n", killCnt);
383         Sys_Printf("%i face(s) caulked\n", caulkedCount);
384
385         acDlg->DestroyWindow();
386 }
387
388 void ResetTextures()
389 {
390         texRstDlg.m_TextureName = GetCurrentTexture();
391         texRstDlg.m_NewTextureName = GetCurrentTexture();
392
393         if(texRstDlg.DoModal() == IDCANCEL)
394                 return;
395
396         float fScale[2];
397         float fShift[2];
398         fScale[1] = texRstDlg.m_fScaleVertical;
399         fScale[0] = texRstDlg.m_fScaleHorizontal;
400
401         fShift[1] = (float)texRstDlg.m_nShiftVertical;
402         fShift[0] = (float)texRstDlg.m_nShiftHorizontal;
403
404         DEntity world;
405         world.LoadFromEntity(0, NULL);
406
407         if(texRstDlg.m_bAllTextures)
408                 world.ResetTextures(NULL, fScale, fShift, texRstDlg.m_nRotation, texRstDlg.m_NewTextureName, texRstDlg.m_bOnlyTexture);
409         else            
410                 world.ResetTextures(texRstDlg.m_TextureName, fScale, fShift, texRstDlg.m_nRotation, texRstDlg.m_NewTextureName, texRstDlg.m_bOnlyTexture);
411 }
412
413 void PathPlotter()
414 {
415         int ret = ppDlg.DoModal();
416         if(ret == IDCANCEL)
417                 return;
418         if(ret == IDNO)
419         {
420                 if(g_PathView)
421                         delete g_PathView;
422
423                 return;
424         }
425
426         if( g_FuncTable.m_pfnSelectedBrushCount() != 1)
427         {
428                 MessageBox(NULL, "Invalid number of brushes selected, chose 1 only", "Error", MB_OK);
429                 return;
430         }
431
432         // tell Radiant we want to access the selected brushes
433         g_FuncTable.m_pfnAllocateSelectedBrushHandles();
434                         
435         // get handle to size definition brush
436         brush_t *brush = (brush_t*)g_FuncTable.m_pfnGetSelectedBrushHandle(0);
437         // cant release until we delete the brush, if we do...
438
439         DEntity world;
440         world.LoadEPairList(*g_FuncTable.m_pfnGetEntityKeyValList(brush->owner));
441
442         DEPair* trigger_ep = world.FindEPairByKey("targetname");
443
444         if(trigger_ep)
445         {
446                 if(!strcmp(world.m_Classname, "trigger_push"))
447                 {
448                         DEPair* target_ep = world.FindEPairByKey("target");
449                         if(target_ep)
450                         {
451                                 entity_s* entTarget = FindEntityFromTargetname(target_ep->value);
452                                 if(entTarget)
453                                 {
454                                         if(g_PathView)
455                                                 delete g_PathView;
456                                         g_PathView = new DBobView;
457
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);
459                                 }
460                                 else
461                                         MessageBox(NULL, "trigger_push target could not be found.", "Error", MB_OK);
462                         }
463                         else
464                                 MessageBox(NULL, "trigger_push has no target.", "Error", MB_OK);
465                 }
466                 else
467                         MessageBox(NULL, "You must select a 'trigger_push' entity.", "Error", MB_OK);
468         }       
469         else
470                 MessageBox(NULL, "Entity must have a targetname", "Error", MB_OK);
471
472         g_FuncTable.m_pfnReleaseSelectedBrushHandles();
473 }
474
475 void PitBuilder(vec3_t vMin, vec3_t vMax)
476 {
477         // ensure we have something selected
478         if( g_FuncTable.m_pfnSelectedBrushCount() != 1 )
479         {
480                 MessageBox(NULL, "Invalid number of brushes selected, chose 1 only", "Error", MB_OK);
481                 return; 
482         }
483
484         // tell Radiant we want to access the selected brushes
485         g_FuncTable.m_pfnAllocateSelectedBrushHandles();
486                         
487         // get handle to size definition brush
488         brush_t *brush = (brush_t*)g_FuncTable.m_pfnGetSelectedBrushHandle(0);
489         // cant release until we delete the brush, if we do...
490
491         DShape pit;
492
493         if(pit.BuildPit(vMin, vMax))
494         {
495                 pit.Commit();
496
497                 g_FuncTable.m_pfnDeleteBrushHandle(brush);
498         }
499         else
500                 MessageBox(NULL, "Failed To Make Pit\nTry Making The Brush Bigger", "Error", MB_OK);
501
502         g_FuncTable.m_pfnReleaseSelectedBrushHandles();
503 }