]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - contrib/bobtoolz/funchandlers.cpp
Merge branch 'master' into divVerent/farplanedist-sky-fix
[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         char buffer[256];
69
70         if ( !el1Loaded ) {
71                 el1Loaded = LoadExclusionList( GetFilename( buffer, "bt\\bt-el1.txt" ), &exclusionList );
72         }
73         if ( !el2Loaded ) {
74                 el2Loaded = LoadExclusionList( GetFilename( buffer, "bt\\bt-el2.txt" ), &exclusionList );
75         }
76 }
77
78 void PolygonBuilder( vec3_t vMin, vec3_t vMax ){
79         // ensure we have something selected
80         if ( g_FuncTable.m_pfnSelectedBrushCount() != 1 ) {
81                 MessageBox( NULL, "Invalid number of brushes selected, chose 1 only", "Error", MB_OK );
82                 return;
83         }
84
85         // tell Radiant we want to access the selected brushes
86         g_FuncTable.m_pfnAllocateSelectedBrushHandles();
87
88         // get handle to size definition brush
89         brush_t *brush = (brush_t*)g_FuncTable.m_pfnGetSelectedBrushHandle( 0 );
90         // cant release until we delete the brush, if we do...
91
92         // ask user for type, size, etc....
93         if ( polygonDlg.DoModal() == IDOK ) {
94                 DShape poly;
95
96                 g_FuncTable.m_pfnDeleteBrushHandle( brush );
97
98                 if ( polygonDlg.m_bInverse ) {
99                         poly.BuildInversePrism( vMin, vMax, polygonDlg.m_nSideCount, polygonDlg.m_bAlignTop );
100                 }
101                 else
102                 {
103                         if ( polygonDlg.m_bBorder ) {
104                                 poly.BuildBorderedPrism( vMin, vMax, polygonDlg.m_nSideCount, polygonDlg.m_nBorderSize, polygonDlg.m_bAlignTop );
105                         }
106                         else{
107                                 poly.BuildRegularPrism( vMin, vMax, polygonDlg.m_nSideCount, polygonDlg.m_bAlignTop );
108                         }
109                 }
110
111                 poly.Commit();
112         }
113
114
115         g_FuncTable.m_pfnReleaseSelectedBrushHandles();
116 }
117
118
119 void IntersectionFinder(){
120         if ( intrDlg.DoModal() == IDCANCEL ) {
121                 return;
122         }
123
124         if ( intrDlg.m_nBrushOptions == BRUSH_OPT_SELECTED ) {
125                 // ensure we have enough brushes selected
126                 if ( g_FuncTable.m_pfnSelectedBrushCount() < 2 ) {
127                         MessageBox( NULL, "Invalid number of brushes selected, choose at least 2", "Error", MB_OK );
128                         return;
129                 }
130         }
131
132         CIntersectInfoDialog*   intrInfoDlg = new CIntersectInfoDialog();
133         intrInfoDlg->Create( IDD_INTERSECT_INFO_DIALOG );
134
135         DEntity world;
136
137         switch ( intrDlg.m_nBrushOptions )
138         {
139         case BRUSH_OPT_SELECTED:
140         {
141                 world.LoadSelectedBrushes( &intrInfoDlg->m_prog1 );
142                 break;
143         }
144         case BRUSH_OPT_WHOLE_MAP:
145         {
146                 world.LoadFromEntity( 0, &intrInfoDlg->m_prog1 );
147                 break;
148         }
149         }
150
151         world.RemoveNonCheckBrushes( &exclusionList, intrDlg.m_bUseDetail );
152         BOOL* pbSelectList;
153         if ( intrDlg.m_bDuplicateOnly ) {
154                 pbSelectList = world.BuildDuplicateList();
155         }
156         else{
157                 pbSelectList = world.BuildIntersectList();
158         }
159
160         world.SelectBrushes( pbSelectList );
161
162         intrInfoDlg->DestroyWindow();
163         delete[] pbSelectList;
164 }
165
166 void StairBuilder( vec3_t vMin, vec3_t vMax ){
167         // ensure we have something selected
168         if ( g_FuncTable.m_pfnSelectedBrushCount() != 1 ) {
169                 MessageBox( NULL, "Invalid number of brushes selected, chose 1 only", "Error", MB_OK );
170                 return;
171         }
172
173         // tell Radiant we want to access the selected brushes
174         g_FuncTable.m_pfnAllocateSelectedBrushHandles();
175
176         // get handle to size definition brush
177         brush_t *brush = (brush_t*)g_FuncTable.m_pfnGetSelectedBrushHandle( 0 );
178         // cant release until we delete the brush, if we do...
179
180
181         // ask user for type, size, etc....
182         if ( stairDlg.DoModal() == IDOK ) {
183
184                 // calc brush size
185                 vec3_t size;
186                 _VectorSubtract( vMax, vMin, size );
187
188
189                 if ( ( (int)size[2] % stairDlg.m_nStairHeight ) != 0 ) {
190                         // stairs must fit evenly into brush
191                         MessageBox( NULL, "Invalid stair height\nHeight of block must be divisable by stair height", "Error", MB_OK );
192                 }
193                 else
194                 {
195
196                         // Remove Size Brush
197                         g_FuncTable.m_pfnDeleteBrushHandle( brush );
198
199
200                         // Get Step Count, Direction of Stairs, Stair Style
201                         int numSteps = (int)size[2] / stairDlg.m_nStairHeight;
202                         int direction = stairDlg.m_StairDir;
203                         int style = stairDlg.m_StairStyle;
204
205                         if ( stairDlg.m_StairStyle == STYLE_CORNER ) {
206                                 BuildCornerStairs( vMin, vMax, numSteps, "textures/common/caulk", (LPCTSTR)stairDlg.m_riserTexture );
207                         }
208                         else
209                         {
210                                 // Get Step Dimensions
211                                 float stairHeight = (float)stairDlg.m_nStairHeight;
212                                 float stairWidth;
213                                 if ( ( direction == MOVE_EAST ) || ( direction == MOVE_WEST ) ) {
214                                         stairWidth = ( size[0] ) / numSteps;
215                                 }
216                                 else{
217                                         stairWidth = ( size[1] ) / numSteps;
218                                 }
219
220
221                                 // Build Base For Stair (bob's style)
222                                 if ( style == STYLE_BOB ) {
223                                         Build_Wedge( direction, vMin, vMax, TRUE );
224                                 }
225
226
227                                 // Set First Step Starting Position
228                                 vMax[2] = vMin[2] + stairHeight;
229                                 SetInitialStairPos( direction, vMin, vMax, stairWidth );
230
231
232                                 // Build The Steps
233                                 for ( int i = 0; i < numSteps; i++ )
234                                 {
235                                         if ( style == STYLE_BOB ) {
236                                                 Build_StairStep_Wedge( direction, vMin, vMax, "textures/common/caulk", (LPCTSTR)stairDlg.m_riserTexture, stairDlg.m_bDetail );
237                                         }
238                                         else if ( style == STYLE_ORIGINAL ) {
239                                                 Build_StairStep( vMin, vMax, "textures/common/caulk", (LPCTSTR)stairDlg.m_riserTexture, direction );
240                                         }
241
242                                         // get step into next position
243                                         MoveBlock( direction, vMin, vMax, stairWidth );
244                                         vMax[2] += stairHeight;
245                                         if ( style == STYLE_BOB ) {
246                                                 vMin[2] += stairHeight; // wedge bottom must be raised
247                                         }
248                                 }
249                         }
250                 }
251         }
252
253         g_FuncTable.m_pfnReleaseSelectedBrushHandles();
254 }
255
256 void DoorBuilder( vec3_t vMin, vec3_t vMax ){
257         // ensure we have something selected
258         if ( g_FuncTable.m_pfnSelectedBrushCount() != 1 ) {
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                 g_FuncTable.m_pfnDeleteBrushHandle( brush );
276
277                 BuildDoorsX2( vMin, vMax,
278                                           doorDlg.m_bSclMainHor, doorDlg.m_bSclMainVert,
279                                           doorDlg.m_bSclTrimHor, doorDlg.m_bSclTrimVert,
280                                           (LPCTSTR)doorDlg.m_fbTextureName,
281                                           (LPCTSTR)doorDlg.m_trimTextureName,
282                                           doorDlg.m_doorDirection );
283         }
284
285         g_FuncTable.m_pfnReleaseSelectedBrushHandles();
286 }
287
288 void FixBrushes(){
289         DEntity world;
290
291         CIntersectInfoDialog*   intrInfoDlg = new CIntersectInfoDialog();
292         intrInfoDlg->Create( IDD_INTERSECT_INFO_DIALOG );
293
294         world.LoadFromEntity( 0, &intrInfoDlg->m_prog1 );
295
296         intrInfoDlg->DestroyWindow();
297
298         CBrushCheckDialog*  chkDlg = new CBrushCheckDialog();
299         chkDlg->Create( IDD_BRUSHCHECKER_DIALOG );
300
301         int count = world.FixBrushes( TRUE, &chkDlg->m_prog1 );
302
303         chkDlg->DestroyWindow();
304
305         Sys_Printf( "%i invalid/duplicate planes removed\n", count );
306 }
307
308 void AutoCaulk(){
309         UndoableCommand undo( "bobToolz.autoCaulk" );
310
311         if ( !el1Loaded ) {
312                 autocaulkDlg.m_Warning1 = "WARNING: Brush exclusion list not found\n, ALL BRUSHES WILL BE USED";
313         }
314
315         if ( autocaulkDlg.DoModal() == IDCANCEL ) {
316                 return;
317         }
318
319         if ( autocaulkDlg.m_nMode == MODE_AC_BUILD_MINI_PRT ) {
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                 char* rad_filename = g_BSPTable.m_pfnGetMapName();
331                 if ( !rad_filename ) {
332                         MessageBox( NULL, "An Error Occurred While Trying To Get The Map Filename", "Error", MB_OK );
333                         acDlg->DestroyWindow();
334                         return;
335                 }
336
337                 strcpy( filename, rad_filename );
338
339                 char* ext = strrchr( filename, '.' ) + 1;
340                 strcpy( ext, "prt" ); // rename the extension
341         }
342         else
343         {
344                 IEpair* pEp = g_EpairTable.m_pfnIEpairForProjectKeys();
345                 char *pn = pEp->ValueForKey( "mapspath" );
346                 pEp->DecRef();
347
348                 strcpy( filename, pn );
349                 strcat( filename, "/ac_prt.prt" );
350         }
351
352         DEntity portals;
353         if ( !portals.LoadFromPrt( filename, &acDlg->m_prog1 ) ) {
354                 MessageBox( NULL, "Failed To Load Portal File", "Error", MB_OK );
355                 acDlg->DestroyWindow();
356                 return;
357         }
358         // load portal file
359
360         CIntersectInfoDialog*   intrInfoDlg = new CIntersectInfoDialog();
361         intrInfoDlg->Create( IDD_INTERSECT_INFO_DIALOG );
362
363         DEntity world;
364
365         world.LoadFromEntity( 0, &intrInfoDlg->m_prog1 );
366         intrInfoDlg->DestroyWindow();
367
368         if ( autocaulkDlg.m_nMode == MODE_AC_NORMAL ) {
369                 world.RemoveNonCheckBrushes( &exclusionList, FALSE );
370         }
371         else{
372                 world.RemoveNonCheckBrushes( &exclusionList, TRUE );
373         }
374
375         world.ResetChecks( &exclusionList_Face );
376
377         int caulkedCount = 0;
378         int killCnt = world.AutoCaulk( &portals, autocaulkDlg.m_bAllowDestruction, &caulkedCount, &acDlg->m_prog2 );
379
380         if ( autocaulkDlg.m_bAllowDestruction ) {
381                 Sys_Printf( "%i unrequired brush(es) killed\n", killCnt );
382         }
383         Sys_Printf( "%i face(s) caulked\n", caulkedCount );
384
385         acDlg->DestroyWindow();
386 }
387
388 void ResetTextures(){
389         texRstDlg.m_TextureName = GetCurrentTexture();
390         texRstDlg.m_NewTextureName = GetCurrentTexture();
391
392         if ( texRstDlg.DoModal() == IDCANCEL ) {
393                 return;
394         }
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         }
410         else{
411                 world.ResetTextures( texRstDlg.m_TextureName, fScale, fShift, texRstDlg.m_nRotation, texRstDlg.m_NewTextureName, texRstDlg.m_bOnlyTexture );
412         }
413 }
414
415 void PathPlotter(){
416         int ret = ppDlg.DoModal();
417         if ( ret == IDCANCEL ) {
418                 return;
419         }
420         if ( ret == IDNO ) {
421                 if ( g_PathView ) {
422                         delete g_PathView;
423                 }
424
425                 return;
426         }
427
428         if ( g_FuncTable.m_pfnSelectedBrushCount() != 1 ) {
429                 MessageBox( NULL, "Invalid number of brushes selected, chose 1 only", "Error", MB_OK );
430                 return;
431         }
432
433         // tell Radiant we want to access the selected brushes
434         g_FuncTable.m_pfnAllocateSelectedBrushHandles();
435
436         // get handle to size definition brush
437         brush_t *brush = (brush_t*)g_FuncTable.m_pfnGetSelectedBrushHandle( 0 );
438         // cant release until we delete the brush, if we do...
439
440         DEntity world;
441         world.LoadEPairList( *g_FuncTable.m_pfnGetEntityKeyValList( brush->owner ) );
442
443         DEPair* trigger_ep = world.FindEPairByKey( "targetname" );
444
445         if ( trigger_ep ) {
446                 if ( !strcmp( world.m_Classname, "trigger_push" ) ) {
447                         DEPair* target_ep = world.FindEPairByKey( "target" );
448                         if ( target_ep ) {
449                                 entity_s* entTarget = FindEntityFromTargetname( target_ep->value );
450                                 if ( entTarget ) {
451                                         if ( g_PathView ) {
452                                                 delete g_PathView;
453                                         }
454                                         g_PathView = new DBobView;
455
456                                         g_PathView->Begin( trigger_ep->value, target_ep->value, ppDlg.m_fMultiplier, ppDlg.m_nPoints, ppDlg.m_fGravity, ppDlg.m_bNoUpdate, ppDlg.m_bShowExtra );
457                                 }
458                                 else{
459                                         MessageBox( NULL, "trigger_push target could not be found.", "Error", MB_OK );
460                                 }
461                         }
462                         else{
463                                 MessageBox( NULL, "trigger_push has no target.", "Error", MB_OK );
464                         }
465                 }
466                 else{
467                         MessageBox( NULL, "You must select a 'trigger_push' entity.", "Error", MB_OK );
468                 }
469         }
470         else{
471                 MessageBox( NULL, "Entity must have a targetname", "Error", MB_OK );
472         }
473
474         g_FuncTable.m_pfnReleaseSelectedBrushHandles();
475 }
476
477 void PitBuilder( vec3_t vMin, vec3_t vMax ){
478         // ensure we have something selected
479         if ( g_FuncTable.m_pfnSelectedBrushCount() != 1 ) {
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                 pit.Commit();
495
496                 g_FuncTable.m_pfnDeleteBrushHandle( brush );
497         }
498         else{
499                 MessageBox( NULL, "Failed To Make Pit\nTry Making The Brush Bigger", "Error", MB_OK );
500         }
501
502         g_FuncTable.m_pfnReleaseSelectedBrushHandles();
503 }