1 /*
2  *	editobj.cc
3  *	Object editing routines.
4  *	BW & RQ sometime in 1993 or 1994.
5  */
6 
7 
8 /*
9 This file is part of Yadex.
10 
11 Yadex incorporates code from DEU 5.21 that was put in the public domain in
12 1994 by Rapha�l Quinet and Brendon Wyber.
13 
14 The rest of Yadex is Copyright � 1997-2003 Andr� Majorel and others.
15 
16 This program is free software; you can redistribute it and/or modify it under
17 the terms of the GNU General Public License as published by the Free Software
18 Foundation; either version 2 of the License, or (at your option) any later
19 version.
20 
21 This program is distributed in the hope that it will be useful, but WITHOUT
22 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
23 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
24 
25 You should have received a copy of the GNU General Public License along with
26 this program; if not, write to the Free Software Foundation, Inc., 59 Temple
27 Place, Suite 330, Boston, MA 02111-1307, USA.
28 */
29 
30 
31 #include "yadex.h"
32 #include "_edit.h"
33 #include "dialog.h"
34 #include "editobj.h"
35 #include "entry.h"
36 #include "flats.h"
37 #include "game.h"
38 #include "gfx.h"
39 #include "levels.h"
40 #include "objects.h"
41 #include "objid.h"
42 #include "oldmenus.h"
43 #include "s_slice.h"
44 #include "s_swapf.h"
45 #include "selectn.h"
46 #include "t_spin.h"
47 #include "x_mirror.h"
48 #include "x_rotate.h"
49 
50 
51 /*
52    ask for an object number and check for maximum valid number
53    (this is just like InputIntegerValue, but with a different prompt)
54 */
55 
InputObjectNumber(int x0,int y0,int objtype,int curobj)56 int InputObjectNumber (int x0, int y0, int objtype, int curobj)
57 {
58 int val, key;
59 char prompt[80];
60 
61 HideMousePointer ();
62 sprintf (prompt, "Enter a %s number between 0 and %d:",
63    GetObjectTypeName (objtype), GetMaxObjectNum (objtype));
64 if (x0 < 0)
65    x0 = (ScrMaxX - 25 - 8 * strlen (prompt)) / 2;
66 if (y0 < 0)
67    y0 = (ScrMaxY - 55) / 2;
68 DrawScreenBox3D (x0, y0, x0 + 25 + 8 * strlen (prompt), y0 + 55);
69 set_colour (WHITE);
70 DrawScreenText (x0 + 10, y0 + 8, prompt);
71 val = curobj;
72 while ((key
73        = InputInteger (x0 + 10, y0 + 28, &val, 0, GetMaxObjectNum (objtype)))
74        != YK_RETURN && key != YK_ESC)
75    Beep ();
76 ShowMousePointer ();
77 return val;
78 }
79 
80 
81 /*
82  *	input_objid - ask for an object number of the specified	type
83  *
84  *	If the user hit [Return], set objid.type to init.type
85  *	and objid.num to whatever number the user entered. If
86  *	the user hit [Esc], call nil() on objid.
87  */
input_objid(Objid & objid,const Objid & init,int x0,int y0)88 void input_objid (Objid& objid, const Objid& init, int x0, int y0)
89 {
90 char prompt[80];
91 
92 HideMousePointer ();
93 sprintf (prompt, "Enter a %s number between 0 and %d:",
94    GetObjectTypeName (init.type), GetMaxObjectNum (init.type));
95 if (x0 < 0)
96    x0 = (ScrMaxX - 25 - 8 * strlen (prompt)) / 2;
97 if (y0 < 0)
98    y0 = (ScrMaxY - 55) / 2;
99 DrawScreenBox3D (x0, y0, x0 + 25 + 8 * strlen (prompt), y0 + 55);
100 set_colour (WHITE);
101 DrawScreenText (x0 + 10, y0 + 8, prompt);
102 int  num = init.num;
103 int  key;
104 while ((key
105        = InputInteger (x0 + 10, y0 + 28, &num, 0, GetMaxObjectNum (init.type)))
106        != YK_RETURN && key != YK_ESC)
107    Beep ();
108 if (key == YK_ESC)
109    objid.nil ();
110 else if (key == YK_RETURN)
111    {
112    objid.type = init.type;
113    objid.num  = num;
114    }
115 else
116    {
117    nf_bug ("input_objid: bad key %d", (int) key);  // Can't happen
118    objid.nil ();
119    }
120 ShowMousePointer ();
121 }
122 
123 
124 /*
125    ask for an object number and display a warning message
126 */
127 
InputObjectXRef(int x0,int y0,int objtype,bool allownone,int curobj)128 int InputObjectXRef (int x0, int y0, int objtype, bool allownone, int curobj)
129 {
130 const char *const msg1 = "Warning: modifying the cross-references";
131 const char *const msg2 = "between some objects may crash the game.";
132 char prompt[80];
133 size_t maxlen = 0;
134 int width;
135 int height;
136 
137 // Dimensions
138 sprintf (prompt, "Enter a %s number between 0 and %d%c",
139   GetObjectTypeName (objtype),
140   GetMaxObjectNum (objtype), allownone ? ',' : ':');
141 maxlen = 40;				// Why 40 ? -- AYM 2002-04-17
142 if (strlen (prompt) > maxlen);
143    maxlen = strlen (prompt);
144 if (strlen (msg1) > maxlen)
145    maxlen = strlen (msg1);
146 if (strlen (msg2) > maxlen)
147    maxlen = strlen (msg2);
148 int ya = 0 + BOX_BORDER + WIDE_VSPACING;
149 int yb = ya;
150 if (allownone)
151   yb += FONTH;
152 int yc = yb + FONTH + WIDE_VSPACING;
153 // FIXME should query InputInteger() instead
154 int yd = yc + 2 * HOLLOW_BORDER + 2 * NARROW_VSPACING + FONTH + WIDE_VSPACING;
155 int ye = yd + FONTH;
156 int yf = ye + FONTH + WIDE_VSPACING + BOX_BORDER;
157 width  = 2 * BOX_BORDER + 2 * WIDE_HSPACING + maxlen * FONTW;
158 height = yf - 0;
159 
160 // Position
161 if (x0 < 0)
162    x0 = (ScrMaxX - width) / 2;
163 if (y0 < 0)
164    y0 = (ScrMaxY - height) / 2;
165 
166 HideMousePointer ();
167 DrawScreenBox3D (x0, y0, x0 + width, y0 + height);
168 set_colour (WHITE);
169 int x = x0 + BOX_BORDER + WIDE_HSPACING;
170 DrawScreenText (x, y0 + ya, prompt);
171 if (allownone)
172    DrawScreenText (x, y0 + yb, "or -1 for none:");
173 set_colour (LIGHTRED);
174 DrawScreenText (x, y0 + yd, msg1);
175 DrawScreenText (x, y0 + ye, msg2);
176 
177 int val = curobj;
178 int key;
179 int min = allownone ? -1 : 0;
180 int max = GetMaxObjectNum (objtype);
181 while (key = InputInteger (x, y0 + yc, &val, min, max),
182        key != YK_RETURN && key != YK_ESC)
183    Beep ();
184 ShowMousePointer ();
185 return val;
186 }
187 
188 
189 
190 /*
191    ask for two vertex numbers and check for maximum valid number
192 */
193 
Input2VertexNumbers(int x0,int y0,const char * prompt1,int * v1,int * v2)194 bool Input2VertexNumbers (int x0, int y0, const char *prompt1, int *v1, int *v2)
195 {
196 int  key;
197 int  maxlen, first;
198 char prompt2[80];
199 int text_x0;
200 int text_y0;
201 int entry1_x0;
202 int entry1_y0;
203 int entry2_x0;
204 int entry2_y0;
205 // FIXME should let InputInteger() tell us
206 const int entry_width  = 2 * HOLLOW_BORDER + 2 * NARROW_HSPACING + 7 * FONTW;
207 const int entry_height = 2 * HOLLOW_BORDER + 2 * NARROW_VSPACING + FONTH;
208 
209 HideMousePointer ();
210 sprintf (prompt2, "Enter two numbers between 0 and %d:", NumVertices - 1);
211 
212 if (strlen (prompt1) > strlen (prompt2))
213    maxlen = strlen (prompt1);
214 else
215    maxlen = strlen (prompt2);
216 if (x0 < 0)
217    x0 = (ScrMaxX - 25 - 8 * maxlen) / 2;
218 if (y0 < 0)
219    y0 = (ScrMaxY - 75) / 2;
220 text_x0 = x0 + BOX_BORDER + WIDE_HSPACING;
221 text_y0 = y0 + BOX_BORDER + WIDE_VSPACING;
222 entry1_x0 = text_x0 + 13 * FONTW;
223 entry1_y0 = text_y0 + 3 * FONTH - HOLLOW_BORDER - NARROW_VSPACING;
224 entry2_x0 = entry1_x0;
225 entry2_y0 = text_y0 + 5 * FONTH - HOLLOW_BORDER - NARROW_VSPACING;
226 
227 DrawScreenBox3D (x0, y0,
228    x0 + 2 * BOX_BORDER + 2 * WIDE_HSPACING
229       + y_max (entry_width + 13 * FONTW, maxlen * FONTW) - 1,
230    y0 + 2 * BOX_BORDER + 2 * WIDE_VSPACING + 6 * FONTH - 1);
231 set_colour (WHITE);
232 DrawScreenText (text_x0, text_y0, prompt1);
233 set_colour (WINFG);
234 DrawScreenText (text_x0, text_y0 + FONTH, prompt2);
235 DrawScreenText (text_x0, text_y0 + 3 * FONTH, "Start vertex");
236 DrawScreenText (text_x0, text_y0 + 5 * FONTH, "End vertex");
237 
238 first = 1;
239 key = 0;
240 for (;;)
241    {
242    DrawScreenBoxHollow (entry1_x0, entry1_y0,
243       entry1_x0 + entry_width - 1, entry1_y0 + entry_height - 1, BLACK);
244    set_colour (first ? WHITE : DARKGREY);
245    DrawScreenText (entry1_x0 + HOLLOW_BORDER + NARROW_HSPACING,
246       entry1_y0 + HOLLOW_BORDER + NARROW_VSPACING, "%d", *v1);
247 
248    DrawScreenBoxHollow (entry2_x0, entry2_y0,
249       entry2_x0 + entry_width - 1, entry2_y0 + entry_height - 1, BLACK);
250    set_colour (! first ? WHITE : DARKGREY);
251    DrawScreenText (entry2_x0 + HOLLOW_BORDER + NARROW_HSPACING,
252       entry2_y0 + HOLLOW_BORDER + NARROW_VSPACING, "%d", *v2);
253 
254    if (first)
255       key = InputInteger (entry1_x0, entry1_y0, v1, 0, NumVertices - 1);
256    else
257       key = InputInteger (entry2_x0, entry2_y0, v2, 0, NumVertices - 1);
258    if (key==YK_LEFT || key==YK_RIGHT || key==YK_TAB || key==YK_BACKTAB)
259       first = !first;
260    else if (key == YK_ESC)
261       break;
262    else if (key == YK_RETURN)
263       {
264       if (first)
265 	 first = 0;
266       else if (*v1 < 0 || *v1 >= NumVertices
267             || *v2 < 0 || *v2 >= NumVertices)
268 	 Beep ();
269       else
270 	 break;
271       }
272    else
273       Beep ();
274    }
275 ShowMousePointer ();
276 return (key == YK_RETURN);
277 }
278 
279 
280 
281 /*
282    edit an object or a group of objects
283 */
284 
EditObjectsInfo(int x0,int y0,int objtype,SelPtr obj)285 void EditObjectsInfo (int x0, int y0, int objtype, SelPtr obj) /* SWAP! */
286 {
287 char  *menustr[3];
288 int    n, val;
289 SelPtr cur;
290 int    subwin_y0;
291 
292 ObjectsNeeded (objtype, 0);
293 if (! obj)
294    return;
295 switch (objtype)
296    {
297    case OBJ_THINGS:
298       ThingProperties (x0, y0, obj);
299       break;
300 
301    case OBJ_VERTICES:
302       for (n = 0; n < 3; n++)
303 	 menustr[n] = (char *) GetMemory (60);
304       sprintf (menustr[2], "Edit Vertex #%d", obj->objnum);
305       sprintf (menustr[0], "Change X position (Current: %d)",
306          Vertices[obj->objnum].x);
307       sprintf (menustr[1], "Change Y position (Current: %d)",
308          Vertices[obj->objnum].y);
309 #ifdef OLDMEN
310       val = DisplayMenuArray (0, y0,
311          menustr[2], 2, NULL, menustr, NULL, NULL, NULL);
312 #else
313       val = vDisplayMenu (0, y0, menustr[2],
314          menustr[0], YK_, 0,
315          menustr[1], YK_, 0,
316 	 NULL);
317 #endif
318       for (n = 0; n < 3; n++)
319 	 FreeMemory (menustr[n]);
320       subwin_y0 = y0 + BOX_BORDER + (2 + val) * FONTH;
321       switch (val)
322 	 {
323 	 case 1:
324 	    val = InputIntegerValue (x0 + 42, subwin_y0,
325                y_min (MapMinX, -10000),
326                y_max (MapMaxX, 10000),
327                Vertices[obj->objnum].x);
328 	    if (val != IIV_CANCEL)
329 	       {
330 	       n = val - Vertices[obj->objnum].x;
331 	       for (cur = obj; cur; cur = cur->next)
332 		  Vertices[cur->objnum].x += n;
333 	       MadeChanges = 1;
334 	       MadeMapChanges = 1;
335 	       }
336 	    break;
337 
338 	 case 2:
339 	    val = InputIntegerValue (x0 + 42, subwin_y0,
340                y_min (MapMinY, -10000),
341                y_max (MapMaxY, 10000),
342                Vertices[obj->objnum].y);
343 	    if (val != IIV_CANCEL)
344 	       {
345 	       n = val - Vertices[obj->objnum].y;
346 	       for (cur = obj; cur; cur = cur->next)
347 		  Vertices[cur->objnum].y += n;
348 	       MadeChanges = 1;
349 	       MadeMapChanges = 1;
350 	       }
351 	    break;
352 	 }
353       break;
354 
355    case OBJ_LINEDEFS:
356       LinedefProperties (x0, y0, obj);
357       break;
358 
359    case OBJ_SECTORS:
360       SectorProperties (x0, y0, obj);
361       break;
362    }
363 }
364 
365 
366 /*
367    Yuck!  Dirty piece of code...
368 */
369 
Input2Numbers(int x0,int y0,const char * name1,const char * name2,int v1max,int v2max,int * v1,int * v2)370 bool Input2Numbers (int x0, int y0, const char *name1, const char *name2,
371    int v1max, int v2max, int *v1, int *v2)
372 {
373 int  key;
374 int  maxlen, first;
375 bool ok;
376 char prompt[80];
377 // FIXME copied from InputInteger()...
378 int  entry_width  = 2 * HOLLOW_BORDER + 2 * NARROW_HSPACING + 7 * FONTW;
379 int  entry_height = 2 * HOLLOW_BORDER + 2 * NARROW_VSPACING + FONTH;
380 
381 y_snprintf (prompt, sizeof prompt, "Give the %s and %s for the object:",
382    name1, name2);
383 maxlen = strlen (prompt);
384 
385 int title_x0       = BOX_BORDER + FONTW;
386 int title_y0       = BOX_BORDER + FONTH / 2;
387 int label1_x0      = title_x0;
388 int label1_y0      = title_y0 + 2 * FONTH;
389 int label2_x0      = title_x0 + (strlen (name1) + 2) * FONTW;
390 {
391    int bound = label1_x0 + entry_width + int (FONTW);
392    if (label2_x0 < bound)
393       label2_x0 = bound;
394 }
395 // FIXME Assuming the range is not longer than the name
396 int label2_y0      = label1_y0;
397 int entry1_out_x0  = label1_x0;
398 int entry1_out_y0  = label1_y0 + 3 * FONTH / 2;
399 int entry1_text_x0 = entry1_out_x0 + HOLLOW_BORDER + NARROW_HSPACING;
400 int entry1_text_y0 = entry1_out_y0 + HOLLOW_BORDER + NARROW_VSPACING;
401 int entry1_out_x1  = entry1_out_x0 + entry_width - 1;
402 int entry1_out_y1  = entry1_out_y0 + entry_height - 1;
403 int entry2_out_x0  = label2_x0;
404 int entry2_out_y0  = label2_y0 + 3 * FONTH / 2;
405 int entry2_text_x0 = entry2_out_x0 + HOLLOW_BORDER + NARROW_HSPACING;
406 int entry2_text_y0 = entry2_out_y0 + HOLLOW_BORDER + NARROW_VSPACING;
407 int entry2_out_x1  = entry2_out_x0 + entry_width - 1;
408 int entry2_out_y1  = entry2_out_y0 + entry_height - 1;
409 int range1_x0      = entry1_out_x0;
410 int range1_y0      = entry1_out_y1 + FONTH / 2;
411 int range2_x0      = entry2_out_x0;
412 int range2_y0      = entry2_out_y1 + FONTH / 2;
413 int window_x1      = entry2_out_x1 + FONTW + BOX_BORDER;
414 int window_y1      = range1_y0 + 3 * FONTH / 2 + BOX_BORDER;
415 {
416    int bound = 2 * BOX_BORDER + (maxlen + 2) * int (FONTW);
417    if (window_x1 < bound)
418       window_x1 = bound;
419 }
420 
421 if (x0 < 0)
422    x0 = (ScrMaxX - window_x1) / 2;
423 if (y0 < 0)
424    y0 = (ScrMaxY - window_y1) / 2;
425 
426 HideMousePointer ();
427 DrawScreenBox3D (x0, y0, x0 + window_x1, y0 + window_y1);
428 set_colour     (WHITE);
429 DrawScreenText (x0 + title_x0,  y0 + title_x0,  prompt);
430 DrawScreenText (x0 + label1_x0, y0 + label1_y0, name1);
431 DrawScreenText (x0 + label2_x0, y0 + label2_y0, name2);
432 DrawScreenText (x0 + range1_x0, y0 + range1_y0, "(0-%d)", v1max);
433 DrawScreenText (x0 + range2_x0, y0 + range2_y0, "(0-%d)", v2max);
434 
435 first = 1;
436 key = 0;
437 for (;;)
438    {
439    ok = true;
440    DrawScreenBoxHollow (x0 + entry1_out_x0, y0 + entry1_out_y0,
441       x0 + entry1_out_x1, y0 + entry1_out_y1, BLACK);
442    if (*v1 < 0 || *v1 > v1max)
443       {
444       set_colour (DARKGREY);
445       ok = false;
446       }
447    else
448       set_colour (LIGHTGREY);
449    DrawScreenText (x0 + entry1_text_x0, y0 + entry1_text_y0, "%d", *v1);
450    DrawScreenBoxHollow (x0 + entry2_out_x0, y0 + entry2_out_y0,
451       x0 + entry2_out_x1, y0 + entry2_out_y1, BLACK);
452    if (*v2 < 0 || *v2 > v2max)
453       {
454       set_colour (DARKGREY);
455       ok = false;
456       }
457    else
458       set_colour (LIGHTGREY);
459    DrawScreenText (x0 + entry2_text_x0, y0 + entry2_text_y0, "%d", *v2);
460    if (first)
461       key = InputInteger (x0 + entry1_out_x0, y0 + entry1_out_y0, v1, 0, v1max);
462    else
463       key = InputInteger (x0 + entry2_out_x0, y0 + entry2_out_y0, v2, 0, v2max);
464    if (key==YK_LEFT || key==YK_RIGHT || key==YK_TAB || key==YK_BACKTAB)
465       first = !first;
466    else if (key == YK_ESC)
467       break;
468    else if (key == YK_RETURN)
469       {
470       if (first)
471 	 first = 0;
472       else if (ok)
473 	 break;
474       else
475 	 Beep ();
476       }
477    else
478       Beep ();
479    }
480 ShowMousePointer ();
481 return (key == YK_RETURN);
482 }
483 
484 
485 
486 /*
487    insert a standard object at given position
488 */
489 
InsertStandardObject(int xpos,int ypos,int choice)490 void InsertStandardObject (int xpos, int ypos, int choice) /* SWAP! */
491 {
492 int sector;
493 int n;
494 int a, b;
495 
496 /* show where the object will be inserted */
497 HideMousePointer ();
498 DrawPointer (1);
499 ShowMousePointer ();
500 /* are we inside a Sector? */
501 Objid o;
502 GetCurObject (o, OBJ_SECTORS, xpos, ypos);
503 sector = o.num;
504 
505 /* !!!! Should also check for overlapping objects (sectors) !!!! */
506 switch (choice)
507    {
508    case 1:
509       a = 256;
510       b = 128;
511       if (Input2Numbers (-1, -1, "Width", "Height", 2000, 2000, &a, &b))
512 	 {
513 	 if (a < 8)
514 	    a = 8;
515 	 if (b < 8)
516 	    b = 8;
517 	 xpos = xpos - a / 2;
518 	 ypos = ypos - b / 2;
519 	 InsertObject (OBJ_VERTICES, -1, xpos, ypos);
520 	 InsertObject (OBJ_VERTICES, -1, xpos + a, ypos);
521 	 InsertObject (OBJ_VERTICES, -1, xpos + a, ypos + b);
522 	 InsertObject (OBJ_VERTICES, -1, xpos, ypos + b);
523 	 if (sector < 0)
524 	    InsertObject (OBJ_SECTORS, -1, 0, 0);
525 	 for (n = 0; n < 4; n++)
526 	    {
527 	    InsertObject (OBJ_LINEDEFS, -1, 0, 0);
528 	    LineDefs[NumLineDefs - 1].sidedef1 = NumSideDefs;
529 	    InsertObject (OBJ_SIDEDEFS, -1, 0, 0);
530 	    if (sector >= 0)
531 	       SideDefs[NumSideDefs - 1].sector = sector;
532 	    }
533 	 ObjectsNeeded (OBJ_LINEDEFS, 0);
534 	 if (sector >= 0)
535 	    {
536 	    LineDefs[NumLineDefs - 4].start = NumVertices - 4;
537 	    LineDefs[NumLineDefs - 4].end = NumVertices - 3;
538 	    LineDefs[NumLineDefs - 3].start = NumVertices - 3;
539 	    LineDefs[NumLineDefs - 3].end = NumVertices - 2;
540 	    LineDefs[NumLineDefs - 2].start = NumVertices - 2;
541 	    LineDefs[NumLineDefs - 2].end = NumVertices - 1;
542 	    LineDefs[NumLineDefs - 1].start = NumVertices - 1;
543 	    LineDefs[NumLineDefs - 1].end = NumVertices - 4;
544 	    }
545 	 else
546 	    {
547 	    LineDefs[NumLineDefs - 4].start = NumVertices - 1;
548 	    LineDefs[NumLineDefs - 4].end = NumVertices - 2;
549 	    LineDefs[NumLineDefs - 3].start = NumVertices - 2;
550 	    LineDefs[NumLineDefs - 3].end = NumVertices - 3;
551 	    LineDefs[NumLineDefs - 2].start = NumVertices - 3;
552 	    LineDefs[NumLineDefs - 2].end = NumVertices - 4;
553 	    LineDefs[NumLineDefs - 1].start = NumVertices - 4;
554 	    LineDefs[NumLineDefs - 1].end = NumVertices - 1;
555 	    }
556 	 }
557       break;
558    case 2:
559       a = 8;
560       b = 128;
561       if (Input2Numbers (-1, -1, "Number of sides", "Radius", 32, 2000, &a, &b))
562 	 {
563 	 if (a < 3)
564 	    a = 3;
565 	 if (b < 8)
566 	    b = 8;
567 	 InsertPolygonVertices (xpos, ypos, a, b);
568 	 if (sector < 0)
569 	    InsertObject (OBJ_SECTORS, -1, 0, 0);
570 	 for (n = 0; n < a; n++)
571 	    {
572 	    InsertObject (OBJ_LINEDEFS, -1, 0, 0);
573 	    LineDefs[NumLineDefs - 1].sidedef1 = NumSideDefs;
574 	    InsertObject (OBJ_SIDEDEFS, -1, 0, 0);
575 	    if (sector >= 0)
576 	       SideDefs[NumSideDefs - 1].sector = sector;
577 	    }
578 	 ObjectsNeeded (OBJ_LINEDEFS, 0);
579 	 if (sector >= 0)
580 	    {
581 	    LineDefs[NumLineDefs - 1].start = NumVertices - 1;
582 	    LineDefs[NumLineDefs - 1].end = NumVertices - a;
583 	    for (n = 2; n <= a; n++)
584 	       {
585 	       LineDefs[NumLineDefs - n].start = NumVertices - n;
586 	       LineDefs[NumLineDefs - n].end = NumVertices - n + 1;
587 	       }
588 	    }
589 	 else
590 	    {
591 	    LineDefs[NumLineDefs - 1].start = NumVertices - a;
592 	    LineDefs[NumLineDefs - 1].end = NumVertices - 1;
593 	    for (n = 2; n <= a; n++)
594 	       {
595 	       LineDefs[NumLineDefs - n].start = NumVertices - n + 1;
596 	       LineDefs[NumLineDefs - n].end = NumVertices - n;
597 	       }
598 	    }
599 	 }
600       break;
601    case 3:
602    /*
603       a = 6;
604       b = 16;
605       if (Input2Numbers (-1, -1, "Number of steps", "Step height", 32, 48, &a, &b))
606 	 {
607 	 if (a < 2)
608 	    a = 2;
609 	 ObjectsNeeded (OBJ_SECTORS, 0);
610 	 n = Sectors[sector].ceilh;
611 	 h = Sectors[sector].floorh;
612 	 if (a * b < n - h)
613 	    {
614 	    Beep ();
615 	    Notify (-1, -1, "The stairs are too high for this Sector", 0);
616 	    return;
617 	    }
618 	 xpos = xpos - 32;
619 	 ypos = ypos - 32 * a;
620 	 for (n = 0; n < a; n++)
621 	    {
622 	    InsertObject (OBJ_VERTICES, -1, xpos, ypos);
623 	    InsertObject (OBJ_VERTICES, -1, xpos + 64, ypos);
624 	    InsertObject (OBJ_VERTICES, -1, xpos + 64, ypos + 64);
625 	    InsertObject (OBJ_VERTICES, -1, xpos, ypos + 64);
626 	    ypos += 64;
627 	    InsertObject (OBJ_SECTORS, sector, 0, 0);
628 	    h += b;
629 	    Sectors[NumSectors - 1].floorh = h;
630 
631 	    InsertObject (OBJ_LINEDEFS, -1, 0, 0);
632 	    LineDefs[NumLineDefs - 1].sidedef1 = NumSideDefs;
633 	    LineDefs[NumLineDefs - 1].sidedef2 = NumSideDefs + 1;
634 	    InsertObject (OBJ_SIDEDEFS, -1, 0, 0);
635 	    SideDefs[NumSideDefs - 1].sector = sector;
636 	    InsertObject (OBJ_SIDEDEFS, -1, 0, 0);
637 
638 	    ObjectsNeeded (OBJ_LINEDEFS, 0);
639 	    LineDefs[NumLineDefs - 4].start = NumVertices - 4;
640 	    LineDefs[NumLineDefs - 4].end = NumVertices - 3;
641 	    LineDefs[NumLineDefs - 3].start = NumVertices - 3;
642 	    LineDefs[NumLineDefs - 3].end = NumVertices - 2;
643 	    LineDefs[NumLineDefs - 2].start = NumVertices - 2;
644 	    LineDefs[NumLineDefs - 2].end = NumVertices - 1;
645 	    LineDefs[NumLineDefs - 1].start = NumVertices - 1;
646 	    LineDefs[NumLineDefs - 1].end = NumVertices - 4;
647 	   }
648 	 }
649       break;
650    */
651    case 4:
652       NotImplemented ();
653       break;
654    }
655 }
656 
657 
658 
659 /*
660    menu of miscellaneous operations
661 */
662 
MiscOperations(int objtype,SelPtr * list,int val)663 void MiscOperations (int objtype, SelPtr *list, int val) /* SWAP! */
664 {
665 char   msg[80];
666 int    angle, scale;
667 
668 if (val > 1 && ! *list)
669    {
670    Beep ();
671    sprintf (msg, "You must select at least one %s", GetObjectTypeName (objtype));
672    Notify (-1, -1, msg, 0);
673    return;
674    }
675 
676 /* I think this switch statement deserves a prize for "worst
677    gratuitous obfuscation" or something. -- AYM 2000-11-07 */
678 switch (val)
679    {
680    case 1:
681       // * -> First free tag number
682       sprintf (msg, "First free tag number: %d", FindFreeTag ());
683       Notify (-1, -1, msg, 0);
684       break;
685 
686    case 2:
687       // * -> Rotate and scale
688       if ((objtype == OBJ_THINGS
689          || objtype == OBJ_VERTICES) && ! (*list)->next)
690 	 {
691 	 Beep ();
692 	 sprintf (msg, "You must select more than one %s",
693             GetObjectTypeName (objtype));
694 	 Notify (-1, -1, msg, 0);
695 	 return;
696 	 }
697       angle = 0;
698       scale = 100;
699       if (Input2Numbers (-1, -1, "rotation angle (�)", "scale (%)",
700          360, 1000, &angle, &scale))
701 	 RotateAndScaleObjects (objtype, *list, (double) angle * 0.0174533,
702             (double) scale * 0.01);
703       break;
704 
705    case 3:
706       // Linedef -> Split
707       if (objtype == OBJ_LINEDEFS)
708 	 {
709 	 SplitLineDefs (*list);
710 	 }
711       // Sector -> Make door from sector
712       else if (objtype == OBJ_SECTORS)
713 	 {
714 	 if ((*list)->next)
715 	    {
716 	    Beep ();
717 	    Notify (-1, -1, "You must select exactly one sector", 0);
718 	    }
719 	 else
720 	    {
721 	    MakeDoorFromSector ((*list)->objnum);
722 	    }
723 	 }
724       // Thing -> Spin 45� clockwise
725       else if (objtype == OBJ_THINGS)
726 	 {
727          spin_things (*list, -45);
728 	 }
729       // Vertex -> Delete and join linedefs
730       else if (objtype == OBJ_VERTICES)
731 	 {
732 	 DeleteVerticesJoinLineDefs (*list);
733 	 ForgetSelection (list);
734 	 }
735       break;
736 
737    case 4:
738       // Linedef -> Split linedefs and sector
739       if (objtype == OBJ_LINEDEFS)
740 	 {
741 	 if (! (*list)->next || (*list)->next->next)
742 	    {
743 	    Beep ();
744 	    Notify (-1, -1, "You must select exactly two linedefs", 0);
745 	    }
746 	 else
747 	    {
748 	    SplitLineDefsAndSector ((*list)->next->objnum, (*list)->objnum);
749 	    ForgetSelection (list);
750 	    }
751 	 }
752       // Sector -> Make lift from sector
753       else if (objtype == OBJ_SECTORS)
754 	 {
755 	 if ((*list)->next)
756 	    {
757 	    Beep ();
758 	    Notify (-1, -1, "You must select exactly one Sector", 0);
759 	    }
760 	 else
761 	    {
762 	    MakeLiftFromSector ((*list)->objnum);
763 	    }
764 	 }
765       // Thing -> Spin 45� counter-clockwise
766       else if (objtype == OBJ_THINGS)
767          spin_things (*list, 45);
768       // Vertex -> Merge
769       else if (objtype == OBJ_VERTICES)
770 	 {
771 	 MergeVertices (list);
772 	 }
773       break;
774 
775    case 5:
776       // Linedef -> Delete linedefs and join sectors
777       if (objtype == OBJ_LINEDEFS)
778 	 {
779 	 DeleteLineDefsJoinSectors (list);
780 	 }
781       // Sector -> Distribute sector floor heights
782       else if (objtype == OBJ_SECTORS)
783 	 {
784 	 if (! (*list)->next || ! (*list)->next->next)
785 	    {
786 	    Beep ();
787 	    Notify (-1, -1, "You must select three or more sectors", 0);
788 	    }
789 	 else
790 	    {
791 	    DistributeSectorFloors (*list);
792 	    }
793 	 }
794       // Thing -> Mirror horizontally
795       else if (objtype == OBJ_THINGS)
796          {
797 	 flip_mirror (*list, OBJ_THINGS, 'm');
798 	 }
799       // Vertex -> Add linedef and split sector
800       else if (objtype == OBJ_VERTICES)
801 	 {
802 	 if (! (*list)->next || (*list)->next->next)
803 	    {
804 	    Beep ();
805 	    Notify (-1, -1, "You must select exactly two vertices", 0);
806 	    }
807 	 else
808 	    {
809 	    SplitSector ((*list)->next->objnum, (*list)->objnum);
810 	    ForgetSelection (list);
811 	    }
812 	 }
813       break;
814 
815    case 6:
816       // Linedef -> Flip
817       if (objtype == OBJ_LINEDEFS)
818 	 {
819 	 FlipLineDefs (*list, 1);
820 	 }
821       // Sector -> Distribute ceiling heights
822       else if (objtype == OBJ_SECTORS)
823 	 {
824 	 if (! (*list)->next || ! (*list)->next->next)
825 	    {
826 	    Beep ();
827 	    Notify (-1, -1, "You must select three or more sectors", 0);
828 	    }
829 	 else
830 	    {
831 	    DistributeSectorCeilings (*list);
832 	    }
833 	 }
834       // Things -> Mirror vertically
835       else if (objtype == OBJ_THINGS)
836 	 {
837 	 flip_mirror (*list, OBJ_THINGS, 'f');
838          }
839       // Vertex -> Mirror horizontally
840       else if (objtype == OBJ_VERTICES)
841 	 {
842 	 flip_mirror (*list, OBJ_VERTICES, 'm');
843 	 }
844       break;
845 
846    case 7:
847       // Linedefs -> Swap sidedefs
848       if (objtype == OBJ_LINEDEFS)
849 	 {
850 	 if (Expert
851             || blindly_swap_sidedefs
852             || Confirm (-1, -1,
853                "Warning: the sector references are also swapped",
854                "You may get strange results if you don't know what you are doing..."))
855 	    FlipLineDefs (*list, 0);
856 	 }
857       // Sectors -> Raise or lower
858       else if (objtype == OBJ_SECTORS)
859 	 {
860 	 RaiseOrLowerSectors (*list);
861 	 }
862       // Vertices -> Mirror vertically
863       else if (objtype == OBJ_VERTICES)
864 	 {
865 	 flip_mirror (*list, OBJ_VERTICES, 'f');
866 	 }
867       break;
868 
869    case 8:
870       // Linedef ->  Align textures vertically
871       if (objtype == OBJ_LINEDEFS)
872 	 {
873 	 SelPtr sdlist, cur;
874 
875 	 /* select all sidedefs */
876 	 ObjectsNeeded (OBJ_LINEDEFS);
877 	 sdlist = 0;
878 	 for (cur = *list; cur; cur = cur->next)
879 	    {
880 	    if (LineDefs[cur->objnum].sidedef1 >= 0)
881 	       SelectObject (&sdlist, LineDefs[cur->objnum].sidedef1);
882 	    if (LineDefs[cur->objnum].sidedef2 >= 0)
883 	       SelectObject (&sdlist, LineDefs[cur->objnum].sidedef2);
884 	    }
885 	 /* align the textures along the Y axis (height) */
886 	 AlignTexturesY (&sdlist);
887 	 }
888       // Sector -> Brighten or darken
889       else if (objtype == OBJ_SECTORS)
890 	 {
891 	 BrightenOrDarkenSectors (*list);
892 	 }
893       break;
894 
895    case 9:
896       // Linedef -> Align texture horizontally
897       if (objtype == OBJ_LINEDEFS)
898 	 {
899 	 SelPtr sdlist, cur;
900 
901 	 /* select all sidedefs */
902 	 ObjectsNeeded (OBJ_LINEDEFS,0);
903 	 sdlist = 0;
904 	 for (cur = *list; cur; cur = cur->next)
905 	    {
906 	    if (LineDefs[cur->objnum].sidedef1 >= 0)
907 	       SelectObject (&sdlist, LineDefs[cur->objnum].sidedef1);
908 	    if (LineDefs[cur->objnum].sidedef2 >= 0)
909 	       SelectObject (&sdlist, LineDefs[cur->objnum].sidedef2);
910 	    }
911 	 /* align the textures along the X axis (width) */
912 	 AlignTexturesX (&sdlist);
913 	 }
914       // Sector -> Unlink room
915       else if (objtype == OBJ_SECTORS)
916 	 {
917 	 NotImplemented ();  // FIXME
918 	 break;
919 	 }
920       break;
921 
922    case 10:
923       // Linedef -> Make linedef single-sided
924       if (objtype == OBJ_LINEDEFS)
925 	 {
926 	 SelPtr cur;
927 	 ObjectsNeeded (OBJ_LINEDEFS, 0);
928 	 for (cur = *list; cur; cur = cur->next)
929 	    {
930 	    struct LineDef *l = LineDefs + cur->objnum;
931 	    l->sidedef2 = -1;  /* remove ref. to 2nd SD */
932 	    l->flags &= ~0x04; /* clear "2S" bit */
933 	    l->flags |= 0x01;  /* set "Im" bit */
934 
935 	    if (is_sidedef (l->sidedef1))
936 	       {
937 	       struct SideDef *s = SideDefs + l->sidedef1;
938 	       strcpy (s->tex1, "-");
939 	       strcpy (s->tex2, "-");
940 	       strcpy (s->tex3, default_middle_texture);
941 	       }
942 	    /* Don't delete the 2nd sidedef, it could be used
943                by another linedef. And if it isn't, the next
944                cross-references check will delete it anyway. */
945 	    }
946 	 }
947       // Sector -> Mirror horizontally
948       else if (objtype == OBJ_SECTORS)
949 	 {
950 	 flip_mirror (*list, OBJ_SECTORS, 'm');
951 	 }
952       break;
953 
954    case 11:
955       // Linedef -> Make rectangular nook
956       if (objtype == OBJ_LINEDEFS)
957 	 MakeRectangularNook (*list, 32, 16, 0);
958       // Sector -> Mirror vertically
959       else if (objtype == OBJ_SECTORS)
960 	 {
961 	 flip_mirror (*list, OBJ_SECTORS, 'f');
962 	 }
963       break;
964 
965    case 12:
966       // Linedef -> Make rectangular boss
967       if (objtype == OBJ_LINEDEFS)
968 	 MakeRectangularNook (*list, 32, 16, 1);
969       // Sector -> Swap flats
970       else if (objtype == OBJ_SECTORS)
971 	 swap_flats (*list);
972       break;
973 
974    case 13:
975       // Linedef -> Set length (1st vertex)
976       if (objtype == OBJ_LINEDEFS)
977 	 {
978 	 static int length = 24;
979 	 length = InputIntegerValue (-1, -1, -10000, 10000, length);
980 	 if (length != IIV_CANCEL)
981 	   SetLinedefLength (*list, length, 0);
982 	 }
983       break;
984 
985    case 14:
986       // Linedef -> Set length (2nd vertex)
987       if (objtype == OBJ_LINEDEFS)
988 	 {
989 	 static int length = 24;
990 	 length = InputIntegerValue (-1, -1, -10000, 10000, length);
991 	 if (length != IIV_CANCEL)
992 	   SetLinedefLength (*list, length, 1);
993 	 }
994       break;
995 
996    case 15:
997       // Linedef -> Unlink 1st sidedef
998       if (objtype == OBJ_LINEDEFS)
999          unlink_sidedef (*list, 1, 0);
1000       break;
1001 
1002    case 16:
1003       // Linedef -> Unlink 2nd sidedef
1004       if (objtype == OBJ_LINEDEFS)
1005          unlink_sidedef (*list, 0, 1);
1006       break;
1007 
1008    case 17:
1009       // Linedef -> Mirror horizontally
1010       flip_mirror (*list, OBJ_LINEDEFS, 'm');
1011       break;
1012 
1013    case 18 :
1014       // Linedef -> Mirror vertically
1015       flip_mirror (*list, OBJ_LINEDEFS, 'f');
1016       break;
1017 
1018    case 19 :
1019       // Linedef -> Cut a slice out of a sector
1020       if (objtype == OBJ_LINEDEFS)
1021 	 {
1022 	 if (! (*list)->next || (*list)->next->next)
1023 	    {
1024 	    Beep ();
1025 	    Notify (-1, -1, "You must select exactly two linedefs", 0);
1026 	    }
1027 	 else
1028 	    {
1029 	    sector_slice ((*list)->next->objnum, (*list)->objnum);
1030 	    ForgetSelection (list);
1031 	    }
1032 	 }
1033       break;
1034    }
1035 }
1036 
1037 
1038 
1039