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