1 // "Build Engine & Tools" Copyright (c) 1993-1997 Ken Silverman
2 // Ken Silverman's official web site: "http://www.advsys.net/ken"
3 // See the included license file "BUILDLIC.TXT" for license info.
4 //
5 // This file has been modified from Ken Silverman's original release
6 // by Jonathon Fowler (jf@jonof.id.au)
7 // by the EDuke32 team (development@voidpoint.com)
8 
9 #include "build.h"
10 
11 #include "baselayer.h"
12 #include "cache1d.h"
13 #include "colmatch.h"
14 #include "common.h"
15 #include "compat.h"
16 #include "editor.h"
17 #include "m32script.h"
18 #include "osd.h"
19 #include "palette.h"
20 #include "pragmas.h"
21 #include "renderlayer.h"
22 #include "scancodes.h"
23 #include "vfs.h"
24 
25 #ifdef _WIN32
26 #include "winbits.h"
27 #endif
28 
29 char levelname[BMAX_PATH] = {0};
30 
31 #define updatecrc16(crc,dat) (crc = (((crc<<8)&65535)^crctable[((((uint16_t)crc)>>8)&65535)^dat]))
32 static int32_t crctable[256];
33 static char kensig[64];
34 
35 static const char *CallExtGetVer(void);
36 static int32_t CallExtInit(void);
37 static int32_t CallExtPreInit(int32_t argc,char const * const * argv);
38 static int32_t CallExtPostStartupWindow(void);
39 static void CallExtPostInit(void);
40 static void CallExtUnInit(void);
41 static void CallExtPreCheckKeys(void);
42 static void CallExtAnalyzeSprites(int32_t, int32_t, int32_t, int32_t, int32_t);
43 static void CallExtCheckKeys(void);
44 static void CallExtPreLoadMap(void);
45 static void CallExtSetupMapFilename(const char *mapname);
46 static void CallExtLoadMap(const char *mapname);
47 static int32_t CallExtPreSaveMap(void);
48 static void CallExtSaveMap(const char *mapname);
CallExtGetSectorCaption(int16_t sectnum)49 static inline const char *CallExtGetSectorCaption(int16_t sectnum) { return ExtGetSectorCaption(sectnum); }
CallExtGetWallCaption(int16_t wallnum)50 static inline const char *CallExtGetWallCaption(int16_t wallnum) { return ExtGetWallCaption(wallnum); }
CallExtGetSpriteCaption(int16_t spritenum)51 static inline const char *CallExtGetSpriteCaption(int16_t spritenum) { return ExtGetSpriteCaption(spritenum); }
52 static void CallExtShowSectorData(int16_t sectnum);
53 static void CallExtShowWallData(int16_t wallnum);
54 static void CallExtShowSpriteData(int16_t spritenum);
55 static void CallExtEditSectorData(int16_t sectnum);
56 static void CallExtEditWallData(int16_t wallnum);
57 static void CallExtEditSpriteData(int16_t spritenum);
58 // static const char *CallExtGetSectorType(int32_t lotag);
59 
60 int8_t m32_clipping=2;
61 static int32_t m32_rotateang = 0;
62 
63 // 0   1     2     3      4       5      6      7
64 // up, down, left, right, lshift, rctrl, lctrl, space
65 // 8  9  10    11    12   13
66 // a, z, pgdn, pgup, [,], [.]
67 // 14       15     16 17 18   19
68 // kpenter, enter, =, -, tab, `
69 uint8_t buildkeys[NUMBUILDKEYS] =
70 {
71     0xc8,0xd0,0xcb,0xcd,0x2a,0x9d,0x1d,0x39,
72     0x1e,0x2c,0xd1,0xc9,0x33,0x34,
73     0x9c,0x1c,0xd,0xc,0xf,0x29
74 };
75 
76 // Start position
77 vec3_t startpos;
78 int16_t startang, startsectnum;
79 
80 // Current position
81 vec3_t pos;
82 int32_t horiz = 100;
83 int16_t ang, cursectnum;
84 static int32_t hvel, vel, svel, angvel;
85 int32_t g_doHardcodedMovement = 1;
86 
87 static int32_t mousexsurp = 0, mouseysurp = 0;
88 double msens = 1.0;
89 
90 int32_t grponlymode = 0;
91 int32_t graphicsmode = 0;
92 
93 int32_t synctics = 0, lockclock = 0;
94 
95 // those ones save the respective 3d video vars while in 2d mode
96 // so that exiting from mapster32 in 2d mode saves the correct ones
97 float vid_gamma_3d=-1, vid_contrast_3d=-1, vid_brightness_3d=-1;
98 
99 int32_t xdim2d = 640, ydim2d = 480, xdimgame = 640, ydimgame = 480, bppgame = 8;
100 int32_t forcesetup = 1;
101 
102 #ifndef GEKKO
103 int32_t g_maxCacheSize = 128<<20;
104 #else
105 int32_t g_maxCacheSize = 8<<20;
106 #endif
107 
108 static int16_t oldmousebstatus = 0;
109 
110 char game_executable[BMAX_PATH] = {0};
111 
112 int32_t zlock = 0x7fffffff, zmode = 0, kensplayerheight = 32;
113 int16_t defaultspritecstat = 0;
114 
115 int16_t localartfreq[MAXTILES];
116 int16_t localartlookup[MAXTILES], localartlookupnum;
117 
118 char tempbuf[4096];
119 
120 char names[MAXTILES][25];
121 const char *g_namesFileName = "NAMES.H";
122 
123 int16_t asksave = 0;
124 int32_t osearchx, osearchy;                               //old search input
125 
126 int32_t grid = 0, autogrid = 1, gridlock = 1, showtags = 2;
127 int32_t zoom = 768, gettilezoom = 1, ztarget = 768;
128 int32_t lastpm16time = 0;
129 
130 extern int32_t mapversion;
131 
132 int16_t highlight[MAXWALLS+MAXSPRITES];
133 int16_t highlightsector[MAXSECTORS], highlightsectorcnt = -1;
134 extern char textfont[128][8];
135 
136 int32_t tempsectornum = -1;  // for auto ceiling/floor alignment
137 int32_t temppicnum, tempcstat, templotag, temphitag, tempextra;
138 uint32_t temppal, tempvis, tempxrepeat, tempyrepeat, tempxpanning=0, tempypanning=0;
139 int32_t tempshade, tempxvel, tempyvel, tempzvel;
140 int32_t tempstatnum=0, tempblend=0;
141 char somethingintab = 255;
142 
143 // Only valid when highlightsectorcnt>0 and no structural
144 // modifications (deleting/inserting sectors or points, setting new firstwall)
145 // have been made:
146 static int16_t onextwall[MAXWALLS];  // onextwall[i]>=0 implies wall[i].nextwall < 0
mkonwvalid(void)147 static void mkonwvalid(void) { chsecptr_onextwall = onextwall; }
mkonwinvalid(void)148 static void mkonwinvalid(void) { chsecptr_onextwall = NULL; tempsectornum=-1; }
mkonwinvalid_keeptempsect(void)149 static void mkonwinvalid_keeptempsect(void) { chsecptr_onextwall = NULL; }
onwisvalid(void)150 static int32_t onwisvalid(void) { return chsecptr_onextwall != NULL; }
151 
152 int32_t mlook = 0, mskip=0;
153 int32_t revertCTRL=0,scrollamount=3;
154 int32_t unrealedlook=1, quickmapcycling=1; //PK
155 
156 char program_origcwd[BMAX_PATH];
157 const char *mapster32_fullpath;
158 char *testplay_addparam = 0;
159 
160 static char boardfilename[BMAX_PATH], selectedboardfilename[BMAX_PATH];
161 //extern char levelname[BMAX_PATH];  // in astub.c   XXX: clean up this mess!!!
162 
B_SetBoardFileName(const char * fn)163 void B_SetBoardFileName(const char *fn)
164 {
165     Bstrncpyz(boardfilename, fn, BMAX_PATH);
166 }
167 
168 static fnlist_t fnlist;
169 static BUILDVFS_FIND_REC *finddirshigh=NULL, *findfileshigh=NULL;
170 static int32_t currentlist=0;
171 
172 //static int32_t repeatcountx, repeatcounty;
173 
174 static int32_t fillist[640];
175 // used for fillsector, batch point insertion, backup_highlighted_map
176 static int32_t tempxyar[MAXWALLS][2];
177 
178 static int32_t mousx, mousy;
179 int16_t prefixtiles[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
180 uint8_t hlsectorbitmap[(MAXSECTORS+7)>>3];  // show2dsector is already taken...
181 static int32_t minhlsectorfloorz, numhlsecwalls;
182 int32_t searchlock = 0;
183 
184 // used for:
185 //  - hl_all_bunch_sectors_p
186 //  - AlignWalls
187 //  - trace_loop
188 static uint8_t visited[(MAXWALLS+7)>>3];
189 
190 int32_t m32_2d3dmode = 0;
191 int32_t m32_2d3dsize = 4;
192 vec2_t m32_2d3d = { 0xffff, 4 };
193 
194 int32_t m32_3dundo = 1;
195 
196 typedef struct
197 {
198     int16_t numsectors, numwalls, numsprites;
199 #ifdef YAX_ENABLE
200     int16_t numyaxbunches;
201     int16_t *bunchnum;  // [numsectors][2]
202     int16_t *ynextwall;  // [numwalls][2]
203 #endif
204     usectortype *sector;
205     uwalltype *wall;
206     uspritetype *sprite;
207 } mapinfofull_t;
208 
209 int32_t g_doScreenShot;
210 
211 #define eitherALT   (keystatus[sc_LeftAlt]|keystatus[sc_RightAlt])
212 #define eitherCTRL  (keystatus[sc_LeftControl]|keystatus[sc_RightControl])
213 #define eitherSHIFT (keystatus[sc_LeftShift]|keystatus[sc_RightShift])
214 
215 #define DOWN_BK(BuildKey) (keystatus[buildkeys[BK_##BuildKey]])
216 
217 int32_t pk_turnaccel=16;
218 int32_t pk_turndecel=12;
219 int32_t pk_uedaccel=3;
220 
221 int8_t keeptexturestretch = 1;
222 int8_t sideview_reversehrot = 0;
223 
224 int16_t pointhighlightdist = 256;
225 int16_t linehighlightdist = 1024;
226 
227 char lastpm16buf[156];
228 
229 //static int32_t checksectorpointer_warn = 0;
230 static int32_t saveboard_savedtags, saveboard_fixedsprites;
231 static int32_t saveboard_canceled;
232 
233 static int32_t backup_highlighted_map(mapinfofull_t *mapinfo);
234 static int32_t restore_highlighted_map(mapinfofull_t *mapinfo, int32_t forreal);
235 static void SaveBoardAndPrintMessage(const char *fn);
236 
237 static int32_t adjustmark(int32_t *xplc, int32_t *yplc, int16_t danumwalls);
238 static void locktogrid(int32_t *dax, int32_t *day);
239 static int32_t checkautoinsert(int32_t dax, int32_t day, int16_t danumwalls);
240 static void keytimerstuff(void);
241 static void flipwalls(int16_t numwalls, int16_t newnumwalls);
242 static int32_t insertpoint(int16_t linehighlight, int32_t dax, int32_t day, int32_t *mapwallnum);
243 static void deletepoint(int16_t point, int32_t runi);
244 static int32_t deletesector(int16_t sucksect);
245 static int16_t whitelinescan(int16_t sucksect, int16_t dalinehighlight);
246 static void printcoords16(int32_t posxe, int32_t posye, int16_t ange);
247 static void overheadeditor(void);
248 static int32_t getlinehighlight(int32_t xplc, int32_t yplc, int32_t line, int8_t ignore_pointhighlight);
249 static int32_t movewalls(int32_t start, int32_t offs);
250 static void loadnames(const char *namesfile);
251 static void getclosestpointonwall(int32_t x, int32_t y, int32_t dawall, int32_t *nx, int32_t *ny,
252                                   int32_t maybe_screen_coord_p);
253 static void initcrc(void);
254 
255 static int32_t menuselect(void);
256 static int32_t menuselect_auto(int, int); //PK
257 
258 static int32_t insert_sprite_common(int32_t sectnum, int32_t dax, int32_t day);
259 static void correct_ornamented_sprite(int32_t i, int32_t hitw);
260 
261 static int32_t getfilenames(const char *path, const char *kind);
262 
263 // Get basename of BUILD file name (forward slashes as directory separators).
getbasefn(const char * fn)264 static const char *getbasefn(const char *fn)
265 {
266     const char *slash = Bstrrchr(fn, '/');
267     return slash ? slash+1 : fn;
268 }
269 
clearkeys(void)270 void clearkeys(void)
271 {
272     Bmemset(keystatus,0,sizeof(keystatus));
273 }
274 
275 #ifdef USE_OPENGL
osdcmd_restartvid(osdcmdptr_t UNUSED (parm))276 int osdcmd_restartvid(osdcmdptr_t UNUSED(parm))
277 {
278     UNREFERENCED_CONST_PARAMETER(parm);
279 
280     if (!in3dmode()) return OSDCMD_OK;
281 
282     videoResetMode();
283     if (videoSetGameMode(fullscreen,xres,yres,bpp,upscalefactor))
284         OSD_Printf("restartvid: Reset failed...\n");
285 
286     return OSDCMD_OK;
287 }
288 #endif
289 
osdcmd_vidmode(osdcmdptr_t parm)290 static int osdcmd_vidmode(osdcmdptr_t parm)
291 {
292     int32_t newx = xres, newy = yres, newbpp = bpp, newfullscreen = fullscreen;
293 #ifdef USE_OPENGL
294     int32_t tmp;
295 #endif
296 
297     switch (parm->numparms)
298     {
299 #ifdef USE_OPENGL
300     case 1:	// bpp switch
301         tmp = Batol(parm->parms[0]);
302         if (!(tmp==8 || tmp==16 || tmp==32))
303             return OSDCMD_SHOWHELP;
304         newbpp = tmp;
305         break;
306     case 4:	// fs, res, bpp switch
307         newfullscreen = (Batol(parm->parms[3]) != 0);
308         fallthrough__;
309     case 3:	// res & bpp switch
310         tmp = Batol(parm->parms[2]);
311         if (!(tmp==8 || tmp==16 || tmp==32))
312             return OSDCMD_SHOWHELP;
313         newbpp = tmp;
314         fallthrough__;
315 #endif
316     case 2: // res switch
317         newx = Batol(parm->parms[0]);
318         newy = Batol(parm->parms[1]);
319         break;
320     default:
321         return OSDCMD_SHOWHELP;
322     }
323 
324     if (!in3dmode())
325     {
326         videoSet2dMode(newx,newy);
327         xdim2d = xdim;
328         ydim2d = ydim;
329 
330         videoBeginDrawing();	//{{{
331         CLEARLINES2D(0, ydim16, 0);
332         videoEndDrawing();	//}}}
333 
334         ydim16 = ydim-STATUS2DSIZ2;
335 
336         return OSDCMD_OK;
337     }
338 
339     if (videoSetGameMode(newfullscreen,newx,newy,newbpp,upscalefactor))
340         OSD_Printf("vidmode: Mode change failed!\n");
341 
342     xdimgame = newx;
343     ydimgame = newy;
344     bppgame = newbpp;
345     fullscreen = newfullscreen;
346 
347     return OSDCMD_OK;
348 }
349 
350 
351 #ifdef M32_SHOWDEBUG
352 char m32_debugstr[64][128];
353 int32_t m32_numdebuglines=0;
354 
M32_drawdebug(void)355 static void M32_drawdebug(void)
356 {
357     int32_t i;
358     int32_t x=4, y=8;
359 
360 #if 0
361     {
362         static char tstr[128];
363         Bsprintf(tstr, "search... stat=%d, sector=%d, wall=%d (%d), isbottom=%d, asksave=%d",
364                  searchstat, searchsector, searchwall, searchbottomwall, searchisbottom, asksave);
365         printext256(x,y,whitecol,0,tstr,xdimgame>640?0:1);
366     }
367 #endif
368     if (m32_numdebuglines>0)
369     {
370         videoBeginDrawing();
371         for (i=0; i<m32_numdebuglines && y<ydim-8; i++, y+=8)
372             printext256(x,y,whitecol,0,m32_debugstr[i],xdimgame>640?0:1);
373         videoEndDrawing();
374     }
375     m32_numdebuglines=0;
376 }
377 #endif
378 
379 #ifdef YAX_ENABLE
380 // Check whether bunchnum has exactly one corresponding floor and ceiling
381 // and return it in this case. If not 1-to-1, return -1.
yax_is121(int16_t bunchnum,int16_t getfloor)382 int32_t yax_is121(int16_t bunchnum, int16_t getfloor)
383 {
384     int32_t i;
385     i = headsectbunch[0][bunchnum];
386     if (i<0 || nextsectbunch[0][i]>=0)
387         return -1;
388     i = headsectbunch[1][bunchnum];
389     if (i<0 || nextsectbunch[1][i]>=0)
390         return -1;
391 
392     return headsectbunch[getfloor][bunchnum];
393 }
394 
yax_numsectsinbunch(int16_t bunchnum,int16_t cf)395 static int32_t yax_numsectsinbunch(int16_t bunchnum, int16_t cf)
396 {
397     int32_t i, n=0;
398 
399     if (bunchnum<0 || bunchnum>=numyaxbunches)
400         return -1;
401 
402     for (SECTORS_OF_BUNCH(bunchnum, cf, i))
403         n++;
404 
405     return n;
406 }
407 
yax_fixreverselinks(int16_t oldwall,int16_t newwall)408 static void yax_fixreverselinks(int16_t oldwall, int16_t newwall)
409 {
410     int32_t cf, ynw;
411     for (cf=0; cf<2; cf++)
412     {
413         ynw = yax_getnextwall(oldwall, cf);
414         if (ynw >= 0)
415             yax_setnextwall(ynw, !cf, newwall);
416     }
417 }
418 
yax_tweakwalls(int16_t start,int16_t offs)419 static void yax_tweakwalls(int16_t start, int16_t offs)
420 {
421     int32_t i, nw, cf;
422     for (i=0; i<numwalls; i++)
423         for (cf=0; cf<2; cf++)
424         {
425             nw = yax_getnextwall(i, cf);
426             if (nw >= start)
427                 yax_setnextwall(i, cf, nw+offs);
428         }
429 }
430 
yax_resetbunchnums(void)431 static void yax_resetbunchnums(void)
432 {
433     int32_t i;
434 
435     for (i=0; i<MAXSECTORS; i++)
436         yax_setbunches(i, -1, -1);
437     yax_update(1);
438     yax_updategrays(pos.z);
439 }
440 
441 // Whether a wall is constrained by sector extensions.
442 // If false, it's a wall that you can freely move around,
443 // attach points to, etc...
yax_islockedwall(int16_t line)444 static int32_t yax_islockedwall(int16_t line)
445 {
446 #ifdef NEW_MAP_FORMAT
447     return (wall[line].upwall>=0 || wall[line].dnwall>=0);
448 #else
449     return !!(wall[line].cstat&YAX_NEXTWALLBITS);
450 #endif
451 }
452 
453 # define DEFAULT_YAX_HEIGHT (2048<<4)
454 #endif
455 
reset_default_mapstate(void)456 static void reset_default_mapstate(void)
457 {
458     pos.x = 32768;          //new board!
459     pos.y = 32768;
460     pos.z = 0;
461     ang = 1536;
462     cursectnum = -1;
463 
464     numsectors = 0;
465     numwalls = 0;
466 
467     editorzrange[0] = INT32_MIN;
468     editorzrange[1] = INT32_MAX;
469 
470     initspritelists();
471     taglab_init();
472     artClearMapArt();
473 #ifdef YAX_ENABLE
474     yax_resetbunchnums();
475 #endif
476     g_loadedMapVersion = -1;
477 }
478 
m32_keypresscallback(int32_t code,int32_t downp)479 static void m32_keypresscallback(int32_t code, int32_t downp)
480 {
481     UNREFERENCED_PARAMETER(downp);
482 
483     g_iReturnVar = code;
484     VM_OnEvent(EVENT_KEYPRESS, -1);
485 }
486 
M32_ResetFakeRORTiles(void)487 void M32_ResetFakeRORTiles(void)
488 {
489 #ifdef POLYMER
490 # ifdef YAX_ENABLE
491         // END_TWEAK ceiling/floor fake 'TROR' pics, see BEGIN_TWEAK in engine.c
492         if (videoGetRenderMode() == REND_POLYMER && showinvisibility)
493         {
494             int32_t i;
495 
496             for (i=0; i<numyaxbunches; i++)
497             {
498                 yax_tweakpicnums(i, YAX_CEILING, 1);
499                 yax_tweakpicnums(i, YAX_FLOOR, 1);
500             }
501         }
502 # endif
503 #endif
504 }
505 
M32_DrawRoomsAndMasks(void)506 void M32_DrawRoomsAndMasks(void)
507 {
508     static int srchwall = -1;
509     const int32_t tmpyx=yxaspect, tmpvr=viewingrange;
510 
511     if (r_usenewaspect)
512     {
513         newaspect_enable = 1;
514         videoSetCorrectedAspect();
515     }
516 
517     VM_OnEvent(EVENT_PREDRAW3DSCREEN, -1);
518 
519     yax_preparedrawrooms();
520     drawrooms(pos.x,pos.y,pos.z,ang,horiz,cursectnum);
521     yax_drawrooms(CallExtAnalyzeSprites, cursectnum, 0, 0);
522 
523     const int osearchwall=searchwall, osearchstat=searchstat;
524     if (srchwall >= 0)
525     {
526         // a.m32 states 'tduprot' and 'tduplin' need searchstat to check for
527         // whether we've hit a sprite, but these would be only set after the
528         // drawmasks(). Hence this hackish workaround.
529         searchstat = 3;
530         searchwall = srchwall;
531     }
532     CallExtAnalyzeSprites(0,0,0,0,0);
533     searchwall = osearchwall, searchstat=osearchstat;
534 
535     renderDrawMasks();
536     srchwall = (searchstat == 3) ? searchwall : -1;
537     M32_ResetFakeRORTiles();
538 
539 #ifdef POLYMER
540     if (videoGetRenderMode() == REND_POLYMER && searchit == 2)
541     {
542         polymer_editorpick();
543         drawrooms(pos.x,pos.y,pos.z,ang,horiz,cursectnum);
544         CallExtAnalyzeSprites(0,0,0,0,0);
545         renderDrawMasks();
546         M32_ResetFakeRORTiles();
547     }
548 #endif
549 
550     VM_OnEvent(EVENT_DRAW3DSCREEN, -1);
551 
552     if (g_doScreenShot)
553     {
554         videoCaptureScreen("mcapxxxx.tga", 0);
555         g_doScreenShot = 0;
556     }
557 
558     if (r_usenewaspect)
559     {
560         newaspect_enable = 0;
561         renderSetAspect(tmpvr, tmpyx);
562     }
563 }
564 
M32_OnShowOSD(int shown)565 void M32_OnShowOSD(int shown)
566 {
567     mouseLockToWindow((!shown) + 2);
568 }
569 
M32_FatalEngineError(void)570 static void M32_FatalEngineError(void)
571 {
572 #ifdef DEBUGGINGAIDS
573     debug_break();
574 #endif
575     Bsprintf(tempbuf, "There was a problem initializing the engine: %s\n", engineerrstr);
576     ERRprintf("%s", tempbuf);
577     fatal_exit(tempbuf);
578 }
579 
InitCustomColors()580 static void InitCustomColors()
581 {
582     /* blue */
583     vgapal16[9*4+0] = 252;
584     vgapal16[9*4+1] = 124;
585     vgapal16[9*4+2] = 28;
586 
587     /* orange */
588     vgapal16[31*4+0] = 80; // blue
589     vgapal16[31*4+1] = 180; // green
590     vgapal16[31*4+2] = 240; // red
591 
592     // UNUSED?
593     vgapal16[39*4+0] = 144;
594     vgapal16[39*4+1] = 212;
595     vgapal16[39*4+2] = 252;
596 
597 
598     /* light yellow */
599     vgapal16[22*4+0] = 204;
600     vgapal16[22*4+1] = 252;
601     vgapal16[22*4+2] = 252;
602 
603     /* grey */
604     vgapal16[23*4+0] = 180;
605     vgapal16[23*4+1] = 180;
606     vgapal16[23*4+2] = 180;
607 
608     /* blue */
609     vgapal16[24*4+0] = 204;
610     vgapal16[24*4+1] = 164;
611     vgapal16[24*4+2] = 48;
612 
613     vgapal16[32*4+0] = 240;
614     vgapal16[32*4+1] = 200;
615     vgapal16[32*4+2] = 84;
616 
617     // grid color
618     vgapal16[25*4+0] = 64;
619     vgapal16[25*4+1] = 56;
620     vgapal16[25*4+2] = 56;
621 
622     vgapal16[26*4+0] = 96;
623     vgapal16[26*4+1] = 96;
624     vgapal16[26*4+2] = 96;
625 
626     // UNUSED?
627     vgapal16[33*4+0] = 0; //60; // blue
628     vgapal16[33*4+1] = 0; //120; // green
629     vgapal16[33*4+2] = 192; //180; // red
630 
631     // UNUSED?
632     vgapal16[41*4+0] = 0; //96;
633     vgapal16[41*4+1] = 0; //160;
634     vgapal16[41*4+2] = 252; //192;
635 }
636 
app_main(int argc,char const * const * argv)637 int app_main(int argc, char const * const * argv)
638 {
639 #ifdef STARTUP_SETUP_WINDOW
640     char cmdsetup = 0;
641 #endif
642     char quitflag;
643     int32_t i;
644 
645     pathsearchmode = 1;		// unrestrict findfrompath so that full access to the filesystem can be had
646 
647 #ifdef USE_OPENGL
648     OSD_RegisterFunction("restartvid","restartvid: reinitialize the video mode",osdcmd_restartvid);
649     OSD_RegisterFunction("vidmode","vidmode <xdim> <ydim> <bpp> <fullscreen>: immediately change the video mode",osdcmd_vidmode);
650     baselayer_osdcmd_vidmode_func = osdcmd_vidmode;
651 #else
652     OSD_RegisterFunction("vidmode","vidmode <xdim> <ydim>: immediately change the video mode",osdcmd_vidmode);
653 #endif
654 
655     wm_setapptitle(AppProperName);
656 
657     editstatus = 1;
658 
659     if ((i = CallExtPreInit(argc,argv)) < 0) return -1;
660 
661 #ifdef _WIN32
662     win_priorityclass = 1;
663 #endif
664 
665     for (i=1; i<argc; i++)
666     {
667         if (argv[i][0] == '-')
668         {
669 #ifdef STARTUP_SETUP_WINDOW
670             if (!Bstrcmp(argv[i], "-setup")) cmdsetup = 1;
671             else
672 #endif
673             if (!Bstrcmp(argv[i], "-help") || !Bstrcmp(argv[i], "--help") || !Bstrcmp(argv[i], "-?"))
674             {
675 #ifdef WM_MSGBOX_WINDOW
676                 wm_msgbox(AppProperName,
677 #else
678                 Bprintf(
679 #endif
680                     "%s\n"
681                     "Syntax: %s [options] mapname\n"
682                     "Options:\n"
683                     "\t-grp\tUse an extra GRP or ZIP file.\n"
684                     "\t-g\tSame as above.\n"
685 #ifdef STARTUP_SETUP_WINDOW
686                     "\t-setup\tDisplays the configuration dialogue box before entering the editor.\n"
687 #endif
688                     , AppProperName, AppTechnicalName);
689                 return 0;
690             }
691             continue;
692         }
693     }
694 
695     if (boardfilename[0] == 0)
696         Bstrcpy(boardfilename,"newboard.map");
697     else if (Bstrchr(boardfilename,'.') == 0)
698         Bstrcat(boardfilename, ".map");
699     //Bcanonicalisefilename(boardfilename,0);
700 
701     OSD_SetFunctions(
702         NULL, NULL, NULL, NULL, NULL,
703         COMMON_clearbackground,
704         BGetTime,
705         M32_OnShowOSD
706     );
707 
708     if (!buildvfs_getcwd(program_origcwd,BMAX_PATH))
709         program_origcwd[0] = '\0';
710 
711     Bstrncpy(game_executable, DefaultGameLocalExec, sizeof(game_executable));
712 
713     if (enginePreInit())
714         M32_FatalEngineError();
715 
716     if ((i = CallExtInit()) < 0) return -1;
717 
718 #ifdef STARTUP_SETUP_WINDOW
719     if (i || forcesetup || cmdsetup)
720     {
721         if (quitevent || !startwin_run())
722         {
723             engineUnInit();
724             exit(EXIT_SUCCESS);
725         }
726     }
727 #endif
728 
729     if (CallExtPostStartupWindow() < 0) return -1;
730 
731     loadnames(g_namesFileName);
732 
733     if (initinput()) return -1;
734 
735     mouseInit();
736 
737     timerInit(CLOCKTICKSPERSECOND);
738     timerSetCallback(keytimerstuff);
739 
740     if (!bloodhack)
741         artLoadFiles("tiles%03i.art", g_maxCacheSize);
742 
743     Bstrcpy(kensig,"Uses BUILD technology by Ken Silverman");
744     initcrc();
745 
746     InitCustomColors();
747 
748     const char *defsfile = G_DefFile();
749 
750     if (testkopen("editor.def", 0))
751         G_AddDefModule("editor.def");
752 
753     if (!loaddefinitionsfile(defsfile))
754         initprintf("Definitions file \"%s\" loaded.\n",defsfile);
755 
756     for (char * m : g_defModules)
757         Bfree(m);
758     g_defModules.clear();
759 
760     if (enginePostInit())
761         M32_FatalEngineError();
762 
763     CallExtPostInit();
764 
765 #ifdef YAX_ENABLE
766     // init dummy texture for YAX
767     // must be after loadpics(), which inits BUILD's cache
768 
769     i = MAXTILES-1;
770     if (tilesiz[i].x==0 && tilesiz[i].y==0)
771     {
772         static char R[8*16] = { //
773             0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0,
774             0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0,
775             0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0,
776             0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0,
777             0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0,
778             0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0,
779             0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0,
780         };
781 
782         char *newtile;
783         int32_t sx=32, sy=32, col, j;
784 
785         walock[i] = CACHE1D_PERMANENT;
786         picsiz[i] = 5 + (5<<4);
787         tilesiz[i].x = sx; tilesiz[i].y = sy;
788         g_cache.allocateBlock(&waloff[i], sx*sy, &walock[i]);
789         newtile = (char *)waloff[i];
790 
791         col = paletteGetClosestColor(128, 128, 0);
792         for (j=0; j<(signed)sizeof(R); j++)
793             R[j] *= col;
794 
795         Bmemset(newtile, 0, sx*sy);
796         for (j=0; j<8; j++)
797             Bmemcpy(&newtile[32*j], &R[16*j], 16);
798     }
799 #endif
800 
801 #ifdef HAVE_CLIPSHAPE_FEATURE
802     int k = engineLoadClipMaps();
803     if (k>0)
804         initprintf("There was an error loading the sprite clipping map (status %d).\n", k);
805 
806     for (char * f : g_clipMapFiles)
807         Bfree(f);
808     g_clipMapFiles.clear();
809 #endif
810 
811     taglab_init();
812 
813     mkonwinvalid();
814 
815     // executed once per init
816     OSD_Exec("m32_autoexec.cfg");
817 
818     if (LoadBoard(boardfilename, 1))
819         reset_default_mapstate();
820 
821     totalclock = 0;
822 
823     updatesector(pos.x,pos.y,&cursectnum);
824 
825     keySetCallback(&m32_keypresscallback);
826     M32_OnShowOSD(0);  // make sure the desktop's mouse cursor is hidden
827 
828     if (cursectnum == -1)
829     {
830         vid_gamma_3d = g_videoGamma;
831         vid_brightness_3d = g_videoBrightness;
832         vid_contrast_3d = g_videoContrast;
833 
834         g_videoGamma = g_videoContrast = 1.0;
835         g_videoBrightness = 0.0;
836 
837         videoSetPalette(0,0,0);
838         if (videoSetGameMode(fullscreen, xdim2d, ydim2d, 8, upscalefactor) < 0)
839         {
840             CallExtUnInit();
841             engineUnInit();
842             Bprintf("%d * %d not supported in this graphics mode\n",xdim2d,ydim2d);
843             Bexit(EXIT_SUCCESS);
844         }
845 
846         system_getcvars();
847 
848         overheadeditor();
849         keystatus[buildkeys[BK_MODE2D_3D]] = 0;
850 
851         g_videoGamma = vid_gamma_3d;
852         g_videoContrast = vid_contrast_3d;
853         g_videoBrightness = vid_brightness_3d;
854 
855         vid_gamma_3d = vid_contrast_3d = vid_brightness_3d = -1;
856 
857         videoSetPalette(GAMMA_CALC,0,0);
858     }
859     else
860     {
861         if (videoSetGameMode(fullscreen, xdimgame, ydimgame, bppgame, upscalefactor) < 0)
862         {
863             CallExtUnInit();
864             engineUnInit();
865             Bprintf("%d * %d not supported in this graphics mode\n",xdim,ydim);
866             Bexit(EXIT_SUCCESS);
867         }
868 
869         system_getcvars();
870 
871         videoSetPalette(GAMMA_CALC,0,0);
872     }
873 
874 CANCEL:
875     quitflag = 0;
876     while (quitflag == 0)
877     {
878         if (handleevents())
879         {
880             if (quitevent)
881             {
882                 keystatus[sc_Escape] = 1;
883                 quitevent = 0;
884             }
885         }
886 
887         OSD_DispatchQueued();
888 
889         videoNextPage();
890         synctics = (int32_t) totalclock-lockclock;
891         lockclock += synctics;
892 
893         CallExtPreCheckKeys();
894 
895         M32_DrawRoomsAndMasks();
896 
897         inputchecked = 1;
898 
899 #ifdef M32_SHOWDEBUG
900         if (searchstat>=0 && (searchwall<0 || searchsector<0))
901         {
902             if (m32_numdebuglines<64)
903                 Bsprintf(m32_debugstr[m32_numdebuglines++], "inconsistent search variables!");
904             searchstat = -1;
905         }
906 
907         M32_drawdebug();
908 #endif
909         CallExtCheckKeys();
910 
911 
912         if (keystatus[sc_Escape])
913         {
914             keystatus[sc_Escape] = 0;
915 
916             printext256(0,0,whitecol,0,"Are you sure you want to quit?",0);
917 
918             videoShowFrame(1);
919             synctics = (int32_t) totalclock-lockclock;
920             lockclock += synctics;
921 
922             while ((keystatus[sc_Escape]|keystatus[sc_Enter]|keystatus[sc_Space]|keystatus[sc_N]) == 0)
923             {
924                 idle_waitevent();
925                 if (handleevents())
926                 {
927                     if (quitevent)
928                     {
929                         quitflag = 1;
930                         break;
931                     }
932                 }
933 
934                 if (keystatus[sc_Y]||keystatus[sc_Enter]) // Y or ENTER
935                 {
936                     keystatus[sc_Y] = 0;
937                     keystatus[sc_Enter] = 0;
938                     quitflag = 1; break;
939                 }
940             }
941             while (keystatus[sc_Escape])
942             {
943                 keystatus[sc_Escape] = 0;
944                 quitevent = 0;
945                 goto CANCEL;
946             }
947         }
948     }
949 
950     if (asksave)
951     {
952         i = CheckMapCorruption(4, 0);
953 
954         printext256(0,8,whitecol,0,i<4?"Save changes?":"Map is heavily corrupt. Save changes?",0);
955         videoShowFrame(1);
956 
957         while ((keystatus[sc_Escape]|keystatus[sc_Enter]|keystatus[sc_Space]|keystatus[sc_N]|keystatus[sc_C]) == 0)
958         {
959             idle_waitevent();
960             if (handleevents()) { if (quitevent) break;	} // like saying no
961 
962             if (keystatus[sc_Y] || keystatus[sc_Enter]) // Y or ENTER
963             {
964                 keystatus[sc_Y] = keystatus[sc_Enter] = 0;
965 
966                 SaveBoard(NULL, M32_SB_ASKOV);
967 
968                 break;
969             }
970         }
971         while (keystatus[sc_Escape]||keystatus[sc_C])
972         {
973             keystatus[sc_Escape] = keystatus[sc_C] = 0;
974             quitevent = 0;
975             goto CANCEL;
976         }
977     }
978 
979 
980     CallExtUnInit();
981 //    clearfilenames();
982     engineUnInit();
983 
984     return 0;
985 }
986 
987 static int32_t mhk=0;
loadmhk(int32_t domessage)988 static void loadmhk(int32_t domessage)
989 {
990     char levname[BMAX_PATH];
991 
992     if (!mhk)
993         return;
994 
995     Bstrcpy(levname, boardfilename);
996     append_ext_UNSAFE(levname, ".mhk");
997 
998     if (!engineLoadMHK(levname))
999     {
1000         if (domessage)
1001             message("Loaded map hack file \"%s\"",levname);
1002         else
1003             initprintf("Loaded map hack file \"%s\"\n",levname);
1004     }
1005     else
1006     {
1007         mhk=2;
1008         if (domessage)
1009             message("No maphack found for map \"%s\"",boardfilename);
1010     }
1011 }
1012 
1013 // this is spriteon{ceiling,ground}z from astub.c packed into
1014 // one convenient function
spriteoncfz(int32_t i,int32_t * czptr,int32_t * fzptr)1015 void spriteoncfz(int32_t i, int32_t *czptr, int32_t *fzptr)
1016 {
1017     int32_t height, zofs;
1018 
1019     getzsofslope(sprite[i].sectnum, sprite[i].x,sprite[i].y, czptr, fzptr);
1020     if ((sprite[i].cstat&48)==32)
1021         return;
1022     if ((sprite[i].cstat&48)==48)
1023     {
1024         int32_t const heinum = spriteGetSlope(i);
1025         int32_t const ratio = divscale12(heinum, ksqrt(heinum*heinum+16777216));
1026         int32_t const tilenum = sprite[i].picnum;
1027         int32_t const yspan = tilesiz[tilenum].y;
1028         int32_t const yoff = (sprite[i].cstat&CSTAT_SPRITE_YFLIP)
1029                            ? -picanm[tilenum].yofs : picanm[tilenum].yofs;
1030         int32_t const y1 = yspan/2+yoff;
1031         int32_t const y2 = yspan - y1;
1032         int32_t const h1 = mulscale10(ratio, sprite[i].yrepeat*y1);
1033         int32_t const h2 = -mulscale10(ratio, sprite[i].yrepeat*y2);
1034         usectorptr_t sect = (usectorptr_t)&sector[sprite[i].sectnum];
1035         int32_t const wallang = (getangle(wall[wall[sect->wallptr].point2].x-wall[sect->wallptr].x,
1036                                          wall[wall[sect->wallptr].point2].y-wall[sect->wallptr].y)+1536)&2047;
1037         if (heinum == sector[sprite[i].sectnum].ceilingheinum && wallang == (sprite[i].ang&2047))
1038             *czptr += 2;
1039         else
1040             *czptr += max(h1,h2);
1041         if (heinum == sector[sprite[i].sectnum].floorheinum && wallang == (sprite[i].ang&2047))
1042             *fzptr -= 2;
1043         else
1044             *fzptr += min(h1,h2);
1045         return;
1046     }
1047 
1048     zofs = spriteheightofs(i, &height, 0);
1049 
1050     *czptr += height - zofs;
1051     *fzptr -= zofs;
1052 }
1053 
move_and_update(int32_t xvect,int32_t yvect,int32_t addshr)1054 static void move_and_update(int32_t xvect, int32_t yvect, int32_t addshr)
1055 {
1056     if (m32_clipping==0)
1057     {
1058         pos.x += xvect>>(14+addshr);
1059         pos.y += yvect>>(14+addshr);
1060         updatesector(pos.x,pos.y, &cursectnum);
1061     }
1062     else
1063     {
1064         clipmove(&pos,&cursectnum, xvect>>addshr,yvect>>addshr,
1065                  128,4<<8,4<<8, (m32_clipping==1) ? 0 : CLIPMASK0);
1066     }
1067 
1068     if (in3dmode())
1069     {
1070         silentmessage("x:%d y:%d z:%d ang:%d horiz:%d", pos.x, pos.y, pos.z, ang, horiz);
1071         getmessagetimeoff = (int32_t) totalclock+30;
1072     }
1073 }
1074 
mainloop_move(void)1075 static void mainloop_move(void)
1076 {
1077     int32_t xvect, yvect, doubvel;
1078 
1079     if (angvel != 0)  //ang += angvel * constant
1080     {
1081         if (eitherCTRL && m32_2d3dmode)
1082         {
1083             int x = m32_2d3d.x + (angvel / 32);
1084             int xx = m32_2d3d.x + XSIZE_2D3D + (angvel / 32);
1085 
1086             if (x > 4 && xx < xdim2d - 4)
1087             {
1088                 silentmessage("2d3d x:%d y:%d", m32_2d3d.x, m32_2d3d.y);
1089                 m32_2d3d.x += (angvel / 32);
1090             }
1091         }
1092         else
1093         {
1094             //ENGINE calculates angvel for you
1095 
1096             //Lt. shift makes turn velocity 50% faster
1097             doubvel = (synctics + DOWN_BK(RUN)*(synctics>>1));
1098 
1099             ang += ((angvel*doubvel)>>4);
1100             ang &= 2047;
1101 
1102             if (in3dmode())
1103             {
1104                 silentmessage("x:%d y:%d z:%d ang:%d horiz:%d", pos.x, pos.y, pos.z, ang, horiz);
1105                 getmessagetimeoff = (int32_t) totalclock+30;
1106             }
1107         }
1108     }
1109     if ((vel|svel) != 0)
1110     {
1111         if (eitherCTRL && m32_2d3dmode)
1112         {
1113             int y = m32_2d3d.y - (vel / 64);
1114             int yy = m32_2d3d.y + YSIZE_2D3D - (vel / 64);
1115 
1116             if (y > 4 && yy < ydim2d - STATUS2DSIZ2 - 4)
1117             {
1118                 silentmessage("2d3d x:%d y:%d", m32_2d3d.x, m32_2d3d.y);
1119                 m32_2d3d.y -= (vel / 64);
1120             }
1121         }
1122         else
1123 
1124         {
1125             //Lt. shift doubles forward velocity
1126             doubvel = (1+(DOWN_BK(RUN)))*synctics;
1127 
1128             xvect = 0;
1129             yvect = 0;
1130 
1131             if (vel != 0)
1132             {
1133                 xvect += ((vel*doubvel)>>3)*(int32_t) sintable[(ang+2560)&2047];
1134                 yvect += ((vel*doubvel)>>3)*(int32_t) sintable[(ang+2048)&2047];
1135             }
1136             if (svel != 0)
1137             {
1138                 xvect += ((svel*doubvel)>>3)*(int32_t) sintable[(ang+2048)&2047];
1139                 yvect += ((svel*doubvel)>>3)*(int32_t) sintable[(ang+1536)&2047];
1140             }
1141 
1142             move_and_update(xvect, yvect, 0);
1143         }
1144     }
1145 }
1146 
handle_sprite_in_clipboard(int32_t i)1147 static void handle_sprite_in_clipboard(int32_t i)
1148 {
1149     if (somethingintab == 3)
1150     {
1151         int32_t j, k;
1152 
1153         sprite[i].picnum = temppicnum;
1154         if (tilesiz[temppicnum].x <= 0 || tilesiz[temppicnum].y <= 0)
1155         {
1156             j = 0;
1157             for (k=0; k<MAXTILES; k++)
1158                 if (tilesiz[k].x > 0 && tilesiz[k].y > 0)
1159                 {
1160                     j = k;
1161                     break;
1162                 }
1163             sprite[i].picnum = j;
1164         }
1165         sprite[i].shade = tempshade;
1166         sprite[i].blend = tempblend;
1167         sprite[i].pal = temppal;
1168         sprite[i].xrepeat = max(tempxrepeat, 1u);
1169         sprite[i].yrepeat = max(tempyrepeat, 1u);
1170         sprite[i].cstat = tempcstat;
1171     }
1172 }
1173 
1174 
editinput(void)1175 void editinput(void)
1176 {
1177     int32_t mousz, bstatus;
1178     int32_t i, tempint=0;
1179     int32_t goalz, xvect, yvect, hiz, loz, oposz;
1180     int32_t hihit, lohit, omlook=mlook;
1181 
1182 // 3B  3C  3D  3E   3F  40  41  42   43  44  57  58          46
1183 // F1  F2  F3  F4   F5  F6  F7  F8   F9 F10 F11 F12        SCROLL
1184 
1185     mousz = 0;
1186     mouseGetValues(&mousx,&mousy,&bstatus);
1187     mousx = (mousx<<16) + mousexsurp;
1188     mousy = (mousy<<16) + mouseysurp;
1189 
1190     if (unrealedlook && !mskip)
1191     {
1192         if (mlook==0 && (bstatus&(1|2|4))==2)
1193             mlook = 3;
1194         else if ((bstatus&(1|2|4))==1)
1195             mlook = 3;
1196     }
1197 
1198     {
1199         ldiv_t ld;
1200         if (mlook)
1201         {
1202             ld = ldiv(mousx, (int32_t)((1<<16)/(msens*0.5f))); mousx = ld.quot; mousexsurp = ld.rem;
1203             ld = ldiv(mousy, (int32_t)((1<<16)/(msens*0.25f))); mousy = ld.quot; mouseysurp = ld.rem;
1204         }
1205         else
1206         {
1207             ld = ldiv(mousx, (int32_t)((1<<16)/msens)); mousx = ld.quot; mousexsurp = ld.rem;
1208             ld = ldiv(mousy, (int32_t)((1<<16)/msens)); mousy = ld.quot; mouseysurp = ld.rem;
1209         }
1210     }
1211 
1212     if (mlook == 3)
1213         mlook = omlook;
1214 
1215     // UnrealEd:
1216     // rmb: mouselook
1217     // lbm: x:turn y:fwd/back local x
1218     // lmb&rmb: x:strafe y:up/dn (move in local yz plane)
1219     // mmb: fwd/back in viewing vector
1220 
1221     if (unrealedlook && !mskip)    //PK
1222     {
1223         if ((bstatus&(1|2|4))==1)
1224         {
1225             ang += mousx;
1226             xvect = -((mousy*(int32_t)sintable[(ang+2560)&2047])<<(3+pk_uedaccel));
1227             yvect = -((mousy*(int32_t)sintable[(ang+2048)&2047])<<(3+pk_uedaccel));
1228 
1229             move_and_update(xvect, yvect, 0);
1230         }
1231         else if (!mlook && (bstatus&(1|2|4))==2)
1232         {
1233             mlook=2;
1234         }
1235         else if ((bstatus&(1|2|4))==(1|2))
1236         {
1237             zmode = 2;
1238             xvect = -((mousx*(int32_t)sintable[(ang+2048)&2047])<<pk_uedaccel);
1239             yvect = -((mousx*(int32_t)sintable[(ang+1536)&2047])<<pk_uedaccel);
1240             pos.z += mousy<<(4+pk_uedaccel);
1241 
1242             move_and_update(xvect, yvect, 0);
1243         }
1244         else if ((bstatus&(1|2|4))==4)
1245         {
1246             zmode = 2;
1247 
1248             // horiz-100 of 200 is viewing at 326.4 build angle units (=atan(200/128)) upward
1249             tempint = getangle(128, horiz-100);
1250 
1251             xvect = -((mousy*
1252                        ((int32_t)sintable[(ang+2560)&2047]>>6)*
1253                        ((int32_t)sintable[(tempint+512)&2047])>>6)
1254                       <<pk_uedaccel);
1255             yvect = -((mousy*
1256                        ((int32_t)sintable[(ang+2048)&2047]>>6)*
1257                        ((int32_t)sintable[(tempint+512)&2047])>>6)
1258                       <<pk_uedaccel);
1259 
1260             pos.z += mousy*(((int32_t)sintable[(tempint+2048)&2047])>>(10-pk_uedaccel));
1261 
1262             move_and_update(xvect, yvect, 2);
1263         }
1264     }
1265 
1266     if (mskip)
1267     {
1268         // mskip was set in astub.c to not trigger UEd mouse movements.
1269         // Reset now.
1270         mskip = 0;
1271     }
1272     else
1273     {
1274         if (mlook && (unrealedlook==0 || (bstatus&(1|4))==0))
1275         {
1276             ang += mousx;
1277             horiz -= mousy;
1278 
1279             /*
1280             if (mousy && !(mousy/4))
1281                 horiz--;
1282             if (mousx && !(mousx/2))
1283                 ang++;
1284             */
1285 
1286             inpclamp(&horiz, -99, 299);
1287 
1288             if (mlook == 1)
1289             {
1290                 searchx = xdim>>1;
1291                 searchy = ydim>>1;
1292             }
1293             osearchx = searchx-mousx;
1294             osearchy = searchy-mousy;
1295 
1296             if (mousx || mousy)
1297             {
1298                 silentmessage("x:%d y:%d z:%d ang:%d horiz:%d", pos.x, pos.y, pos.z, ang, horiz);
1299                 getmessagetimeoff = (int32_t) totalclock+30;
1300             }
1301         }
1302         else if (unrealedlook==0 || (bstatus&(1|2|4))==0)
1303         {
1304             osearchx = searchx;
1305             osearchy = searchy;
1306             searchx += mousx;
1307             searchy += mousy;
1308 
1309             inpclamp(&searchx, 12, xdim-13);
1310             inpclamp(&searchy, 12, ydim-13);
1311         }
1312     }
1313 
1314 //    showmouse();
1315 
1316     if (keystatus[sc_F9])  // F9
1317     {
1318         if (mhk)
1319         {
1320             Bmemset(spriteext, 0, sizeof(spriteext_t) * MAXSPRITES);
1321             Bmemset(spritesmooth, 0, sizeof(spritesmooth_t) * (MAXSPRITES+MAXUNIQHUDID));
1322             engineClearLightsFromMHK();
1323             mhk = 0;
1324             message("Maphacks disabled");
1325         }
1326         else
1327         {
1328             mhk = 1;
1329             loadmhk(1);
1330         }
1331 
1332         keystatus[sc_F9] = 0;
1333     }
1334 
1335     mainloop_move();
1336 
1337     getzrange(&pos,cursectnum, &hiz,&hihit, &loz,&lohit, 128, (m32_clipping==1)?0:CLIPMASK0);
1338 /*
1339 {
1340     int32_t his = !(hihit&32768), los = !(lohit&32768);
1341     if (m32_numdebuglines<64)
1342         Bsprintf(m32_debugstr[m32_numdebuglines++], "s%d: cf[%s%d, %s%d] z(%d, %d)", cursectnum,
1343                  his?"s":"w",hihit&16383, los?"s":"w",lohit&16383, hiz,loz);
1344 }
1345 */
1346     oposz = pos.z;
1347     if (zmode == 0)
1348     {
1349         goalz = loz-(kensplayerheight<<8);  //playerheight pixels above floor
1350         if (goalz < hiz+(16<<8))  //ceiling&floor too close
1351             goalz = (loz+hiz)>>1;
1352         goalz += mousz;
1353 
1354         if (DOWN_BK(MOVEUP))  //A (stand high)
1355         {
1356             goalz -= (16<<8);
1357             if (DOWN_BK(RUN))
1358                 goalz -= (24<<8);
1359         }
1360         if (DOWN_BK(MOVEDOWN))  //Z (stand low)
1361         {
1362             goalz += (12<<8);
1363             if (DOWN_BK(RUN))
1364                 goalz += (12<<8);
1365         }
1366 
1367         if (goalz != pos.z)
1368         {
1369             if (pos.z < goalz) hvel += 64;
1370             if (pos.z > goalz) hvel = ((goalz-pos.z)>>3);
1371 
1372             pos.z += hvel;
1373             if (pos.z > loz-(4<<8)) pos.z = loz-(4<<8), hvel = 0;
1374             if (pos.z < hiz+(4<<8)) pos.z = hiz+(4<<8), hvel = 0;
1375         }
1376     }
1377     else
1378     {
1379         goalz = pos.z;
1380         if (DOWN_BK(MOVEUP))  //A
1381         {
1382             if (eitherALT)
1383             {
1384                 horiz = max(-100,horiz-((DOWN_BK(RUN)+1)*synctics*2));
1385             }
1386             else
1387             {
1388                 if (zmode != 1)
1389                     goalz -= (8<<8);
1390                 else
1391                 {
1392                     zlock += (4<<8);
1393                     DOWN_BK(MOVEUP) = 0;
1394                 }
1395             }
1396         }
1397         if (DOWN_BK(MOVEDOWN))  //Z (stand low)
1398         {
1399             if (eitherALT)
1400             {
1401                 horiz = min(300,horiz+((DOWN_BK(RUN)+1)*synctics*2));
1402             }
1403             else
1404             {
1405                 if (zmode != 1)
1406                     goalz += (8<<8);
1407                 else if (zlock > 0)
1408                 {
1409                     zlock -= (4<<8);
1410                     DOWN_BK(MOVEDOWN) = 0;
1411                 }
1412             }
1413         }
1414 
1415         if (m32_clipping)
1416             inpclamp(&goalz, hiz+(4<<8), loz-(4<<8));
1417 
1418         if (zmode == 1) goalz = loz-zlock;
1419         if (m32_clipping && (goalz < hiz+(4<<8)))
1420             goalz = ((loz+hiz)>>1);  //ceiling&floor too close
1421         if (zmode == 1) pos.z = goalz;
1422 
1423         if (goalz != pos.z)
1424         {
1425             //if (pos.z < goalz) hvel += (32<<DOWN_BK(RUN));
1426             //if (pos.z > goalz) hvel -= (32<<DOWN_BK(RUN));
1427             if (pos.z < goalz)
1428                 hvel = ((192*synctics)<<DOWN_BK(RUN));
1429             else
1430                 hvel = -((192*synctics)<<DOWN_BK(RUN));
1431 
1432             pos.z += hvel;
1433 
1434             if (m32_clipping)
1435             {
1436                 if (pos.z > loz-(4<<8)) pos.z = loz-(4<<8), hvel = 0;
1437                 if (pos.z < hiz+(4<<8)) pos.z = hiz+(4<<8), hvel = 0;
1438             }
1439         }
1440         else
1441             hvel = 0;
1442     }
1443 
1444     {
1445         int16_t ocursectnum = cursectnum;
1446         updatesectorz(pos.x,pos.y,pos.z, &cursectnum);
1447         if (cursectnum<0)
1448         {
1449             if (zmode != 2)
1450                 pos.z = oposz;  // don't allow to fall into infinity when in void space
1451             cursectnum = ocursectnum;
1452         }
1453     }
1454 
1455     if (pos.z != oposz && in3dmode())
1456     {
1457         silentmessage("x:%d y:%d z:%d ang:%d horiz:%d", pos.x, pos.y, pos.z, ang, horiz);
1458         getmessagetimeoff = (int32_t) totalclock+30;
1459     }
1460 
1461     searchit = 2;
1462     if (searchstat >= 0)
1463     {
1464         if ((bstatus&(1|2|4)) || keystatus[sc_Space])  // SPACE
1465             searchit = 0;
1466 
1467         if (keystatus[sc_S])  //S (insert sprite) (3D)
1468         {
1469             hitdata_t hit;
1470             vec2_t osearch = {searchx, searchy};
1471             vec2_t bdim = {xdim, ydim};
1472             if (m32_is2d3dmode())
1473             {
1474                 xdim = XSIZE_2D3D;
1475                 ydim = YSIZE_2D3D;
1476                 searchx -= m32_2d3d.x;
1477                 searchy -= m32_2d3d.y;
1478             }
1479 
1480             vec2_t da = { 16384, divscale14(searchx-(xdim>>1), xdim>>1) };
1481 
1482             rotatevec(da, ang, &da);
1483 
1484 #ifdef USE_OPENGL
1485             if (videoGetRenderMode() == REND_POLYMOST)
1486                 hit = polymost_hitdata;
1487             else
1488 #endif
1489                 hitscan((const vec3_t *)&pos,cursectnum,              //Start position
1490                     da.x,da.y,(scale(searchy,200,ydim)-horiz)*2000, //vector of 3D ang
1491                     &hit,CLIPMASK1);
1492 
1493             if (hit.sect >= 0)
1494             {
1495                 da.x = hit.pos.x;
1496                 da.y = hit.pos.y;
1497                 if (gridlock && grid > 0)
1498                 {
1499                     if (AIMING_AT_WALL || AIMING_AT_MASKWALL)
1500                         hit.pos.z &= 0xfffffc00;
1501                     else
1502                         locktogrid(&da.x, &da.y);
1503                 }
1504 
1505                 i = insert_sprite_common(hit.sect, da.x, da.y);
1506 
1507                 if (i < 0)
1508                     message("Couldn't insert sprite.");
1509                 else
1510                 {
1511                     int32_t cz, fz;
1512 
1513                     handle_sprite_in_clipboard(i);
1514 
1515                     spriteoncfz(i, &cz, &fz);
1516                     sprite[i].z = clamp2(hit.pos.z, cz, fz);
1517 
1518                     if (AIMING_AT_WALL || AIMING_AT_MASKWALL)
1519                     {
1520                         sprite[i].cstat &= ~48;
1521                         sprite[i].cstat |= (16+64);
1522 
1523                         correct_ornamented_sprite(i, hit.wall);
1524                     }
1525                     else
1526                         sprite[i].cstat |= (tilesiz[sprite[i].picnum].y>=32);
1527 
1528                     correct_sprite_yoffset(i);
1529 
1530                     asksave = 1;
1531 
1532                     VM_OnEvent(EVENT_INSERTSPRITE3D, i);
1533                 }
1534             }
1535 
1536             xdim = bdim.x;
1537             ydim = bdim.y;
1538             searchx = osearch.x;
1539             searchy = osearch.y;
1540             keystatus[sc_S] = 0;
1541         }
1542 
1543         if (keystatus[sc_F5]||keystatus[sc_F6])  //F5,F6
1544         {
1545             switch (searchstat)
1546             {
1547             case SEARCH_CEILING:
1548             case SEARCH_FLOOR:
1549                 CallExtShowSectorData(searchsector); break;
1550             case SEARCH_WALL:
1551             case SEARCH_MASKWALL:
1552                 CallExtShowWallData(searchwall); break;
1553             case SEARCH_SPRITE:
1554                 CallExtShowSpriteData(searchwall); break;
1555             }
1556 
1557             keystatus[sc_F5] = keystatus[sc_F6] = 0;
1558         }
1559         if (keystatus[sc_F7]||keystatus[sc_F8])  //F7,F8
1560         {
1561             switch (searchstat)
1562             {
1563             case SEARCH_CEILING:
1564             case SEARCH_FLOOR:
1565                 CallExtEditSectorData(searchsector); break;
1566             case SEARCH_WALL:
1567             case SEARCH_MASKWALL:
1568                 CallExtEditWallData(searchwall); break;
1569             case SEARCH_SPRITE:
1570                 CallExtEditSpriteData(searchwall); break;
1571             }
1572 
1573             keystatus[sc_F7] = keystatus[sc_F8] = 0;
1574         }
1575 
1576     }
1577 
1578     if (keystatus[buildkeys[BK_MODE2D_3D]] && !m32_is2d3dmode())  // Enter
1579     {
1580 
1581         vid_gamma_3d = g_videoGamma;
1582         vid_contrast_3d = g_videoContrast;
1583         vid_brightness_3d = g_videoBrightness;
1584 
1585         g_videoGamma = g_videoContrast = 1.0;
1586         g_videoBrightness = 0.0;
1587 
1588         videoSetPalette(0,0,0);
1589 
1590         keystatus[buildkeys[BK_MODE2D_3D]] = 0;
1591         overheadeditor();
1592         keystatus[buildkeys[BK_MODE2D_3D]] = 0;
1593 
1594         g_videoGamma = vid_gamma_3d;
1595         g_videoContrast = vid_contrast_3d;
1596         g_videoBrightness = vid_brightness_3d;
1597 
1598         vid_gamma_3d = vid_contrast_3d = vid_brightness_3d = -1;
1599 
1600         videoSetPalette(GAMMA_CALC,0,0);
1601     }
1602 }
1603 
changechar(char dachar,int32_t dadir,char smooshyalign,char boundcheck)1604 char changechar(char dachar, int32_t dadir, char smooshyalign, char boundcheck)
1605 {
1606     if (dadir < 0)
1607     {
1608         if ((dachar > 0) || (boundcheck == 0))
1609         {
1610             dachar--;
1611             if (smooshyalign > 0)
1612                 dachar = (dachar&0xf8);
1613         }
1614     }
1615     else if (dadir > 0)
1616     {
1617         if ((dachar < 255) || (boundcheck == 0))
1618         {
1619             dachar++;
1620             if (smooshyalign > 0)
1621             {
1622                 if (dachar >= 256-8) dachar = 255;
1623                 else dachar = ((dachar+7)&0xf8);
1624             }
1625         }
1626     }
1627     return dachar;
1628 }
1629 
1630 
1631 ////////////////////// OVERHEADEDITOR //////////////////////
1632 
1633 // some 2d mode state
1634 static struct overheadstate
1635 {
1636     // number of backed up drawn walls
1637     int32_t bak_wallsdrawn;
1638 
1639     // state related to line drawing
1640     int16_t suckwall, split;
1641     int16_t splitsect;
1642     int16_t splitstartwall;
1643 } ovh;
1644 
1645 
inside_editor(const vec3_t * pos,int32_t searchx,int32_t searchy,int32_t zoom,int32_t x,int32_t y,int16_t sectnum)1646 static int32_t inside_editor(const vec3_t *pos, int32_t searchx, int32_t searchy, int32_t zoom,
1647                              int32_t x, int32_t y, int16_t sectnum)
1648 {
1649     if (!m32_sideview)
1650         return inside(x, y, sectnum);
1651 
1652     // if in side-view mode, use the screen coords instead
1653     {
1654         int32_t dst = MAXSECTORS+M32_FIXME_SECTORS-1, i, oi;
1655         int32_t srcw=sector[sectnum].wallptr, dstw=MAXWALLS;
1656         int32_t ret;
1657 
1658         if (sector[sectnum].wallnum > M32_FIXME_WALLS)
1659             return -1;
1660 
1661         Bmemcpy(&sector[dst], &sector[sectnum], sizeof(sectortype));
1662         sector[dst].wallptr = dstw;
1663 
1664         Bmemcpy(&wall[dstw], &wall[srcw], sector[dst].wallnum*sizeof(walltype));
1665         for (i=dstw, oi=srcw; i<dstw+sector[dst].wallnum; i++, oi++)
1666         {
1667             wall[i].point2 += dstw-srcw;
1668 
1669             editorGet2dScreenCoordinates(&wall[i].x, &wall[i].y, wall[i].x-pos->x, wall[i].y-pos->y, zoom);
1670             wall[i].y += getscreenvdisp(getflorzofslope(sectnum,wall[oi].x,wall[oi].y)-pos->z, zoom);
1671             wall[i].x += halfxdim16;
1672             wall[i].y += midydim16;
1673         }
1674 
1675         i = numsectors;
1676         numsectors = dst+1;
1677         ret = inside(searchx, searchy, dst);
1678         numsectors = i;
1679         return ret;
1680     }
1681 }
1682 
inside_editor_curpos(int16_t sectnum)1683 int32_t inside_editor_curpos(int16_t sectnum)
1684 {
1685     // TODO: take care: mous[xy]plc global vs overheadeditor auto
1686     return inside_editor(&pos, searchx,searchy, zoom, mousxplc,mousyplc, sectnum);
1687 }
1688 
1689 
drawline16base(int32_t bx,int32_t by,int32_t x1,int32_t y1,int32_t x2,int32_t y2,char col)1690 static inline void drawline16base(int32_t bx, int32_t by, int32_t x1, int32_t y1, int32_t x2, int32_t y2, char col)
1691 {
1692     editorDraw2dLine(bx+x1, by+y1, bx+x2, by+y2, col);
1693 }
1694 
drawsmallabel(const char * text,char col,char backcol,char border,int32_t dax,int32_t day,int32_t daz)1695 void drawsmallabel(const char *text, char col, char backcol, char border, int32_t dax, int32_t day, int32_t daz)
1696 {
1697     editorGet2dScreenCoordinates(&dax,&day, dax-pos.x,day-pos.y, zoom);
1698 
1699     if (m32_sideview)
1700         day += getscreenvdisp(daz-pos.z, zoom);
1701 
1702     int32_t const x1 = halfxdim16+dax-(Bstrlen(text)<<1);
1703     int32_t const y1 = midydim16+day-4;
1704     int32_t const x2 = x1 + (Bstrlen(text)<<2)+2;
1705     int32_t const y2 = y1 + 7;
1706 
1707     int f = mulscale8(x2-x1, zoom);
1708 
1709     if ((x1 <= -f) || (x2 >= xdim + f) || (y1 <= -f) || (y2 >= ydim16 + f))
1710         return;
1711 
1712     printext16(x1,y1, col,backcol, text,1);
1713 
1714     editorDraw2dLine(x1-2, y1-2, x2-2, y1-2, border);
1715     editorDraw2dLine(x1-2, y2+1, x2-2, y2+1, border);
1716 
1717     editorDraw2dLine(x1-3, y1-1, x1-3, y2+0, border);
1718     editorDraw2dLine(x2-1, y1-1, x2-1, y2+0, border);
1719 
1720     editorDraw2dLine(x1-1,y1-1, x2-3,y1-1, backcol);
1721     editorDraw2dLine(x1-1,y2+0, x2-3,y2+0, backcol);
1722 
1723     editorDraw2dLine(x1-2,y1+0, x1-2,y2-1, backcol);
1724     editorDraw2dLine(x2-2,y1+0, x2-2,y2-1, backcol);
1725     editorDraw2dLine(x2-3,y1+0, x2-3,y2+0, backcol);
1726 
1727     videoBeginDrawing(); //{{{
1728 
1729     if ((unsigned)y1-1 < ydim16+0u && (unsigned) (x1-2) < xdim2d+0u && (unsigned) (x2-2) < xdim2d+0u)
1730     {
1731         drawpixel((char *) (frameplace + ((y1-1) * bytesperline) + (x1-2)), border);
1732         drawpixel((char *) (frameplace + ((y1-1) * bytesperline) + (x2-2)), border);
1733     }
1734 
1735     if ((unsigned) y2 < ydim16+0u && (unsigned) (x1-2) < xdim2d+0u && (unsigned) (x2-2) < xdim2d+0u)
1736     {
1737         drawpixel((char *) (frameplace + ((y2) * bytesperline) + (x1-2)), border);
1738         drawpixel((char *) (frameplace + ((y2) * bytesperline) + (x2-2)), border);
1739     }
1740 
1741     videoEndDrawing();
1742 }
1743 
1744 // backup highlighted sectors with sprites as mapinfo for later restoration
1745 // return values:
1746 //  -1: highlightsectorcnt<=0
1747 //   0: ok
backup_highlighted_map(mapinfofull_t * mapinfo)1748 static int32_t backup_highlighted_map(mapinfofull_t *mapinfo)
1749 {
1750     int32_t i, j, k, m, tmpnumwalls=0, tmpnumsprites=0;
1751     int16_t *const otonsect = (int16_t *)tempxyar;  // STRICTALIASING
1752     int16_t *const otonwall = ((int16_t *)tempxyar) + MAXWALLS;
1753 #ifdef YAX_ENABLE
1754     int16_t otonbunch[YAX_MAXBUNCHES];
1755     int16_t numsectsofbunch[YAX_MAXBUNCHES];  // ceilings + floors
1756 #endif
1757 
1758     if (highlightsectorcnt <= 0)
1759         return -1;
1760 
1761 #ifdef YAX_ENABLE
1762     for (i=0; i<numyaxbunches; i++)
1763         numsectsofbunch[i] = 0;
1764 #endif
1765 
1766     // set up old-->new mappings
1767     j = 0;
1768     k = 0;
1769     for (i=0; i<numsectors; i++)
1770     {
1771         int32_t startwall, endwall;
1772 
1773         if (hlsectorbitmap[i>>3]&pow2char[i&7])
1774         {
1775 #ifdef YAX_ENABLE
1776             int16_t bn[2], cf;
1777 
1778             yax_getbunches(i, &bn[0], &bn[1]);
1779             for (cf=0; cf<2; cf++)
1780                 if (bn[cf] >= 0)
1781                     numsectsofbunch[bn[cf]]++;
1782 #endif
1783             otonsect[i] = j++;
1784 
1785             for (WALLS_OF_SECTOR(i, m))
1786                 otonwall[m] = k++;
1787         }
1788         else
1789         {
1790             otonsect[i] = -1;
1791 
1792             for (WALLS_OF_SECTOR(i, m))
1793                 otonwall[m] = -1;
1794         }
1795     }
1796 
1797 #ifdef YAX_ENABLE
1798     j = 0;
1799     for (i=0; i<numyaxbunches; i++)
1800     {
1801         // only back up complete bunches
1802         if (numsectsofbunch[i] == yax_numsectsinbunch(i, 0)+yax_numsectsinbunch(i, 1))
1803             otonbunch[i] = j++;  // kept bunch
1804         else
1805             otonbunch[i] = -1;  // discarded bunch
1806     }
1807     mapinfo->numyaxbunches = j;
1808 #endif
1809 
1810     // count walls & sprites
1811     for (i=0; i<highlightsectorcnt; i++)
1812     {
1813         tmpnumwalls += sector[highlightsector[i]].wallnum;
1814 
1815         m = headspritesect[highlightsector[i]];
1816         while (m != -1)
1817         {
1818             tmpnumsprites++;
1819             m = nextspritesect[m];
1820         }
1821     }
1822 
1823     // allocate temp storage
1824     mapinfo->sector = (usectortype *)Xmalloc(highlightsectorcnt * sizeof(sectortype));
1825     mapinfo->wall = (uwalltype *)Xmalloc(tmpnumwalls * sizeof(walltype));
1826 
1827 #ifdef YAX_ENABLE
1828     if (mapinfo->numyaxbunches > 0)
1829     {
1830         mapinfo->bunchnum = (int16_t *)Xmalloc(highlightsectorcnt*2*sizeof(int16_t));
1831         mapinfo->ynextwall = (int16_t *)Xmalloc(tmpnumwalls*2*sizeof(int16_t));
1832     }
1833     else
1834     {
1835         mapinfo->bunchnum = mapinfo->ynextwall = NULL;
1836     }
1837 #endif
1838 
1839     if (tmpnumsprites>0)
1840     {
1841         mapinfo->sprite = (uspritetype *)Xmalloc(tmpnumsprites * sizeof(spritetype));
1842     }
1843     else
1844     {
1845         // would never be accessed because mapinfo->numsprites is 0, but cleaner
1846         mapinfo->sprite = NULL;
1847     }
1848 
1849 
1850     // copy everything over
1851     tmpnumwalls = 0;
1852     tmpnumsprites = 0;
1853     for (i=0; i<highlightsectorcnt; i++)
1854     {
1855         k = highlightsector[i];
1856         Bmemcpy(&mapinfo->sector[i], &sector[k], sizeof(sectortype));
1857         mapinfo->sector[i].wallptr = tmpnumwalls;
1858 
1859 #ifdef YAX_ENABLE
1860         if (mapinfo->numyaxbunches > 0 || numyaxbunches > 0)
1861         {
1862             int16_t bn[2];
1863 
1864             yax_getbunches(k, &bn[0], &bn[1]);
1865             for (j=0; j<2; j++)
1866             {
1867                 // old bunchnum, new bunchnum
1868                 int32_t obn=bn[j], nbn=(obn>=0) ? otonbunch[obn] : -1;
1869 
1870                 if (mapinfo->numyaxbunches > 0)
1871                     mapinfo->bunchnum[2*i + j] = nbn;
1872 
1873                 if (obn >= 0 && nbn < 0)
1874                 {
1875                     // A bunch was discarded.
1876                     auto const sec = &mapinfo->sector[i];
1877 # if !defined NEW_MAP_FORMAT
1878                     uint16_t *const cs = j==YAX_CEILING ? &sec->ceilingstat : &sec->floorstat;
1879                     uint8_t *const xp = j==YAX_CEILING ? &sec->ceilingxpanning : &sec->floorxpanning;
1880 
1881                     *cs &= ~YAX_BIT;
1882                     *xp = 0;
1883 # else
1884                     if (j == YAX_CEILING)
1885                         sec->ceilingbunch = -1;
1886                     else
1887                         sec->floorbunch = -1;
1888 # endif
1889                 }
1890             }
1891         }
1892 #endif
1893 
1894         for (j=0; j<sector[k].wallnum; j++)
1895         {
1896             m = sector[k].wallptr;
1897             Bmemcpy(&mapinfo->wall[tmpnumwalls+j], &wall[m+j], sizeof(walltype));
1898             mapinfo->wall[tmpnumwalls+j].point2 += (tmpnumwalls-m);
1899 
1900 #ifdef YAX_ENABLE
1901             if (mapinfo->numyaxbunches > 0 || numyaxbunches > 0)
1902             {
1903                 int32_t cf;
1904 
1905                 for (cf=0; cf<2; cf++)
1906                 {
1907                     const int32_t ynw = yax_getnextwall(m+j, cf);
1908                     const int32_t nynw = (ynw >= 0) ? otonwall[ynw] : -1;
1909 
1910                     if (mapinfo->numyaxbunches > 0)
1911                         mapinfo->ynextwall[2*(tmpnumwalls+j) + cf] = nynw;
1912 
1913                     if (ynw >= 0 && nynw < 0)  // CLEAR_YNEXTWALLS
1914                         YAX_PTRNEXTWALL(mapinfo->wall, tmpnumwalls+j, cf) = YAX_NEXTWALLDEFAULT(cf);
1915                 }
1916             }
1917 #endif
1918             m = mapinfo->wall[tmpnumwalls+j].nextsector;
1919             if (m < 0 || otonsect[m] < 0)
1920             {
1921                 mapinfo->wall[tmpnumwalls+j].nextsector = -1;
1922                 mapinfo->wall[tmpnumwalls+j].nextwall = -1;
1923             }
1924             else
1925             {
1926                 mapinfo->wall[tmpnumwalls+j].nextsector = otonsect[m];
1927                 m = mapinfo->wall[tmpnumwalls+j].nextwall;
1928                 mapinfo->wall[tmpnumwalls+j].nextwall = otonwall[m];
1929             }
1930         }
1931         tmpnumwalls += j;
1932 
1933         m = headspritesect[highlightsector[i]];
1934         while (m != -1)
1935         {
1936             Bmemcpy(&mapinfo->sprite[tmpnumsprites], &sprite[m], sizeof(spritetype));
1937             mapinfo->sprite[tmpnumsprites].sectnum = otonsect[highlightsector[i]];
1938             m = nextspritesect[m];
1939             tmpnumsprites++;
1940         }
1941     }
1942 
1943 
1944     mapinfo->numsectors = highlightsectorcnt;
1945     mapinfo->numwalls = tmpnumwalls;
1946     mapinfo->numsprites = tmpnumsprites;
1947 
1948     return 0;
1949 }
1950 
mapinfofull_free(mapinfofull_t * mapinfo)1951 static void mapinfofull_free(mapinfofull_t *mapinfo)
1952 {
1953     Xfree(mapinfo->sector);
1954 #ifdef YAX_ENABLE
1955     if (mapinfo->numyaxbunches > 0)
1956     {
1957         Xfree(mapinfo->bunchnum);
1958         Xfree(mapinfo->ynextwall);
1959     }
1960 #endif
1961     Xfree(mapinfo->wall);
1962     if (mapinfo->numsprites>0)
1963         Xfree(mapinfo->sprite);
1964 }
1965 
1966 // restore map saved with backup_highlighted_map, also
1967 // frees mapinfo's sector, wall, (sprite) in any case.
1968 // return values:
1969 //  -1: limits exceeded
1970 //   0: ok
1971 // forreal: if 0, only test if we have enough space (same return values)
restore_highlighted_map(mapinfofull_t * mapinfo,int32_t forreal)1972 static int32_t restore_highlighted_map(mapinfofull_t *mapinfo, int32_t forreal)
1973 {
1974     int32_t i, j, onumsectors=numsectors, newnumsectors, newnumwalls;
1975 
1976     if (numsectors+mapinfo->numsectors>MAXSECTORS || numwalls+mapinfo->numwalls>MAXWALLS
1977 #ifdef YAX_ENABLE
1978             || numyaxbunches+mapinfo->numyaxbunches > YAX_MAXBUNCHES
1979 #endif
1980             || Numsprites+mapinfo->numsprites>MAXSPRITES)
1981     {
1982         mapinfofull_free(mapinfo);
1983         return -1;
1984     }
1985 
1986     if (!forreal)
1987         return 0;
1988 
1989     newnumsectors = numsectors + mapinfo->numsectors;
1990     newnumwalls = numwalls + mapinfo->numwalls;
1991 
1992     // copy sectors & walls
1993     Bmemcpy(&sector[numsectors], mapinfo->sector, mapinfo->numsectors*sizeof(sectortype));
1994     Bmemcpy(&wall[numwalls], mapinfo->wall, mapinfo->numwalls*sizeof(walltype));
1995 
1996     // tweak index members
1997     for (i=numwalls; i<newnumwalls; i++)
1998     {
1999         wall[i].point2 += numwalls;
2000 
2001         if (wall[i].nextsector >= 0)
2002         {
2003             wall[i].nextsector += numsectors;
2004             wall[i].nextwall += numwalls;
2005         }
2006 #ifdef YAX_ENABLE
2007         for (j=0; j<2; j++)
2008         {
2009             if (mapinfo->numyaxbunches > 0)
2010             {
2011                 yax_setnextwall(i, j, mapinfo->ynextwall[2*(i-numwalls) + j]>=0 ?
2012                                 numwalls+mapinfo->ynextwall[2*(i-numwalls) + j] : -1);
2013             }
2014             else
2015             {
2016 # if !defined NEW_MAP_FORMAT
2017                 // XXX: When copying a TROR portion into a non-TROR map (e.g. a
2018                 // new one), tags denoting ynextwalls are left in place.
2019                 wall[i].cstat &= ~YAX_NEXTWALLBIT(j);  // CLEAR_YNEXTWALLS
2020 # else
2021                 yax_setnextwall(i, j, -1);
2022 # endif
2023             }
2024         }
2025 #endif
2026     }
2027     for (i=numsectors; i<newnumsectors; i++)
2028         sector[i].wallptr += numwalls;
2029 
2030     // highlight copied sectors
2031 
2032     numsectors = newnumsectors;
2033 
2034     Bmemset(hlsectorbitmap, 0, sizeof(hlsectorbitmap));
2035     for (i=onumsectors; i<newnumsectors; i++)
2036     {
2037         hlsectorbitmap[i>>3] |= pow2char[i&7];
2038 
2039 #ifdef YAX_ENABLE
2040         for (j=0; j<2; j++)
2041         {
2042             if (mapinfo->numyaxbunches > 0)
2043             {
2044                 int32_t bn = mapinfo->bunchnum[2*(i-onumsectors)+j];
2045                 yax_setbunch(i, j, bn>=0 ? numyaxbunches+bn : -2);
2046                 // -2 clears forward yax-nextwall links.
2047                 // XXX: still may wrongly reset xpanning.
2048             }
2049             else
2050                 Bassert(yax_getbunch(i, j) < 0);
2051         }
2052 #endif
2053     }
2054 
2055     // insert sprites
2056     for (i=0; i<mapinfo->numsprites; i++)
2057     {
2058         uspriteptr_t srcspr = &mapinfo->sprite[i];
2059         int32_t sect = onumsectors + srcspr->sectnum;
2060 
2061         j = insertsprite(sect, srcspr->statnum);
2062         Bassert(j >= 0);
2063         Bmemcpy(&sprite[j], srcspr, sizeof(spritetype));
2064         sprite[j].sectnum = sect;
2065     }
2066 
2067     mapinfofull_free(mapinfo);
2068 
2069     numwalls = newnumwalls;
2070 
2071     update_highlightsector();
2072 
2073 #ifdef YAX_ENABLE
2074     if (mapinfo->numyaxbunches > 0)
2075         yax_update(0);
2076 #endif
2077     yax_updategrays(pos.z);
2078 
2079     return 0;
2080 }
2081 
2082 
2083 static int16_t newnumwalls=-1;
2084 
ovh_whiteoutgrab(int32_t restoreredwalls)2085 void ovh_whiteoutgrab(int32_t restoreredwalls)
2086 {
2087     int32_t i, j, k, startwall, endwall;
2088 #if 0
2089 //def YAX_ENABLE
2090     int16_t cb, fb;
2091 #endif
2092 
2093     if (restoreredwalls)
2094     {
2095         // restore onextwalls first
2096         for (i=0; i<numsectors; i++)
2097             for (WALLS_OF_SECTOR(i, j))
2098                 checksectorpointer(j, i);
2099     }
2100 
2101     for (i=0; i<MAXWALLS; i++)
2102         onextwall[i] = -1;
2103 
2104     //White out all bordering lines of grab that are
2105     //not highlighted on both sides
2106     for (i=highlightsectorcnt-1; i>=0; i--)
2107         for (WALLS_OF_SECTOR(highlightsector[i], j))
2108         {
2109             if (wall[j].nextwall < 0)
2110                 continue;
2111 
2112             k = wall[j].nextsector;
2113 
2114             if (hlsectorbitmap[k>>3]&pow2char[k&7])
2115                 continue;
2116 #if 0
2117 //def YAX_ENABLE
2118             // internal red walls are kept red
2119             yax_getbunches(highlightsector[i], &cb, &fb);
2120             if (cb>=0 && yax_getbunch(k, YAX_CEILING)>=0)
2121                 continue;
2122             if (fb>=0 && yax_getbunch(k, YAX_FLOOR)>=0)
2123                 continue;
2124 #endif
2125             onextwall[j] = wall[j].nextwall;
2126 
2127             NEXTWALL(j).nextwall = -1;
2128             NEXTWALL(j).nextsector = -1;
2129             wall[j].nextwall = -1;
2130             wall[j].nextsector = -1;
2131         }
2132 
2133     if (highlightsectorcnt > 0)
2134         mkonwvalid();
2135     else
2136         mkonwinvalid_keeptempsect();
2137 }
2138 
duplicate_selected_sectors(void)2139 static void duplicate_selected_sectors(void)
2140 {
2141     mapinfofull_t mapinfo;
2142     int32_t i, j, onumsectors;
2143 #ifdef YAX_ENABLE
2144     int32_t onumyaxbunches;
2145 #endif
2146     int32_t minx=INT32_MAX, maxx=INT32_MIN, miny=INT32_MAX, maxy=INT32_MIN, dx, dy;
2147 
2148     i = backup_highlighted_map(&mapinfo);
2149 
2150     if (i < 0)
2151     {
2152         message("Out of memory!");
2153         return;
2154     }
2155 
2156     i = restore_highlighted_map(&mapinfo, 0);
2157     if (i < 0)
2158     {
2159         // XXX: no, might be another limit too.  Better message needed.
2160         printmessage16("Copying sectors would exceed sector or wall limit.");
2161         return;
2162     }
2163 
2164     // restoring would succeed, tweak things...
2165     Bmemset(hlsectorbitmap, 0, sizeof(hlsectorbitmap));
2166     for (i=0; i<highlightsectorcnt; i++)
2167     {
2168         int32_t startwall, endwall;
2169 
2170         // first, make red lines of old selected sectors, effectively
2171         // restoring the original state
2172         for (WALLS_OF_SECTOR(highlightsector[i], j))
2173         {
2174             if (wall[j].nextwall >= 0)
2175                 checksectorpointer(wall[j].nextwall,wall[j].nextsector);
2176             checksectorpointer(j, highlightsector[i]);
2177 
2178             minx = min(minx, TrackerCast(wall[j].x));
2179             maxx = max(maxx, TrackerCast(wall[j].x));
2180             miny = min(miny, TrackerCast(wall[j].y));
2181             maxy = max(maxy, TrackerCast(wall[j].y));
2182         }
2183     }
2184 
2185     // displace walls & sprites of new sectors by a small amount:
2186     // calculate displacement
2187     if (grid>0 && grid<9)
2188         dx = max(2048>>grid, 128);
2189     else
2190         dx = 512;
2191     dy = -dx;
2192     if (maxx+dx >= editorgridextent) dx*=-1;
2193     if (minx+dx <= -editorgridextent) dx*=-1;
2194     if (maxy+dy >= editorgridextent) dy*=-1;
2195     if (miny+dy <= -editorgridextent) dy*=-1;
2196 
2197     onumsectors = numsectors;
2198 #ifdef YAX_ENABLE
2199     onumyaxbunches = numyaxbunches;
2200 #endif
2201     // restore! this will not fail.
2202     restore_highlighted_map(&mapinfo, 1);
2203 
2204     // displace
2205     for (i=onumsectors; i<numsectors; i++)
2206     {
2207         for (j=sector[i].wallptr; j<sector[i].wallptr+sector[i].wallnum; j++)
2208         {
2209             wall[j].x += dx;
2210             wall[j].y += dy;
2211         }
2212 
2213         for (j=headspritesect[i]; j>=0; j=nextspritesect[j])
2214         {
2215             sprite[j].x += dx;
2216             sprite[j].y += dy;
2217         }
2218     }
2219 
2220 #ifdef YAX_ENABLE
2221     if (numyaxbunches > onumyaxbunches)
2222         printmessage16("Sectors duplicated, creating %d new bunches.", numyaxbunches-onumyaxbunches);
2223     else
2224 #endif
2225         printmessage16("Sectors duplicated.");
2226     asksave = 1;
2227 
2228 #ifdef YAX_ENABLE
2229     if (numyaxbunches > onumyaxbunches)
2230         yax_update(0);
2231 #endif
2232     yax_updategrays(pos.z);
2233 }
2234 
2235 
duplicate_selected_sprites(void)2236 static void duplicate_selected_sprites(void)
2237 {
2238     int32_t i, j, k=0;
2239 
2240     for (i=0; i<highlightcnt; i++)
2241         if ((highlight[i]&0xc000) == 16384)
2242             k++;
2243 
2244     if (Numsprites + k <= MAXSPRITES)
2245     {
2246         for (i=0; i<highlightcnt; i++)
2247             if ((highlight[i]&0xc000) == 16384)
2248             {
2249                 //duplicate sprite
2250                 k = (highlight[i]&16383);
2251                 j = insertsprite(sprite[k].sectnum,sprite[k].statnum);
2252                 Bmemcpy(&sprite[j],&sprite[k],sizeof(spritetype));
2253 //                sprite[j].sectnum = sprite[k].sectnum;   //Don't let memcpy overwrite sector!
2254 //                setsprite(j,(vec3_t *)&sprite[j]);
2255             }
2256 
2257         printmessage16("Sprites duplicated.");
2258         asksave = 1;
2259     }
2260     else
2261     {
2262         printmessage16("Copying sprites would exceed sprite limit.");
2263     }
2264 }
2265 
correct_ornamented_sprite(int32_t i,int32_t hitw)2266 static void correct_ornamented_sprite(int32_t i, int32_t hitw)
2267 {
2268     int32_t j;
2269 
2270     if (hitw >= 0)
2271     {
2272         sprite[i].ang = (getangle(POINT2(hitw).x-wall[hitw].x,
2273                                   POINT2(hitw).y-wall[hitw].y)+512)&2047;
2274 
2275         //Make sure sprite's in right sector
2276         if (inside(sprite[i].x, sprite[i].y, sprite[i].sectnum) != 1)
2277         {
2278             j = wall[hitw].point2;
2279             sprite[i].x -= ksgn(wall[j].y-wall[hitw].y);
2280             sprite[i].y += ksgn(wall[j].x-wall[hitw].x);
2281         }
2282     }
2283 }
2284 
DoSpriteOrnament(int32_t i)2285 void DoSpriteOrnament(int32_t i)
2286 {
2287     hitdata_t hit;
2288 
2289     hitscan((const vec3_t *)&sprite[i],sprite[i].sectnum,
2290             sintable[(sprite[i].ang+1536)&2047],
2291             sintable[(sprite[i].ang+1024)&2047],
2292             0,
2293             &hit,CLIPMASK1);
2294 
2295     if (hit.sect == -1)
2296         return;
2297 
2298     sprite[i].x = hit.pos.x;
2299     sprite[i].y = hit.pos.y;
2300     sprite[i].z = hit.pos.z;
2301     changespritesect(i, hit.sect);
2302 
2303     correct_ornamented_sprite(i, hit.wall);
2304 }
2305 
update_highlight(void)2306 void update_highlight(void)
2307 {
2308     int32_t i;
2309 
2310     highlightcnt = 0;
2311     for (i=0; i<numwalls; i++)
2312         if (show2dwall[i>>3]&pow2char[i&7])
2313             highlight[highlightcnt++] = i;
2314     for (i=0; i<MAXSPRITES; i++)
2315         if (sprite[i].statnum < MAXSTATUS)
2316         {
2317             if (show2dsprite[i>>3]&pow2char[i&7])
2318                 highlight[highlightcnt++] = i+16384;
2319         }
2320         else
2321             show2dsprite[i>>3] &= ~pow2char[i&7];
2322 
2323     if (highlightcnt == 0)
2324         highlightcnt = -1;
2325 }
2326 
update_highlightsector(void)2327 void update_highlightsector(void)
2328 {
2329     int32_t i;
2330 
2331     minhlsectorfloorz = INT32_MAX;
2332     numhlsecwalls = 0;
2333 
2334     highlightsectorcnt = 0;
2335     for (i=0; i<numsectors; i++)
2336         if (hlsectorbitmap[i>>3]&pow2char[i&7])
2337         {
2338             highlightsector[highlightsectorcnt++] = i;
2339             minhlsectorfloorz = min(minhlsectorfloorz, TrackerCast(sector[i].floorz));
2340             numhlsecwalls += sector[i].wallnum;
2341         }
2342 
2343     if (highlightsectorcnt==0)
2344     {
2345         minhlsectorfloorz = 0;
2346         highlightsectorcnt = -1;
2347     }
2348 }
2349 
2350 // Get average point of sectors
get_sectors_center(const int16_t * sectors,int32_t numsecs,int32_t * cx,int32_t * cy)2351 static void get_sectors_center(const int16_t *sectors, int32_t numsecs, int32_t *cx, int32_t *cy)
2352 {
2353     int32_t i, j, k=0, dax = 0, day = 0;
2354     int32_t startwall, endwall;
2355 
2356     for (i=0; i<numsecs; i++)
2357     {
2358         for (WALLS_OF_SECTOR(sectors[i], j))
2359         {
2360             dax += wall[j].x;
2361             day += wall[j].y;
2362             k++;
2363         }
2364     }
2365 
2366     if (k > 0)
2367     {
2368         dax /= k;
2369         day /= k;
2370     }
2371 
2372     *cx = dax;
2373     *cy = day;
2374 }
2375 
insert_sprite_common(int32_t sectnum,int32_t dax,int32_t day)2376 static int32_t insert_sprite_common(int32_t sectnum, int32_t dax, int32_t day)
2377 {
2378     int32_t i, j, k;
2379 
2380     i = insertsprite(sectnum,0);
2381     if (i < 0)
2382         return -1;
2383 
2384     sprite[i].x = dax, sprite[i].y = day;
2385     sprite[i].cstat = defaultspritecstat;
2386     sprite[i].shade = 0;
2387     sprite[i].pal = 0;
2388     sprite[i].xrepeat = 64, sprite[i].yrepeat = 64;
2389     sprite[i].xoffset = 0, sprite[i].yoffset = 0;
2390     sprite[i].ang = 1536;
2391     sprite[i].xvel = 0; sprite[i].yvel = 0; sprite[i].zvel = 0;
2392     sprite[i].owner = -1;
2393     sprite[i].clipdist = 32;
2394     sprite[i].lotag = 0;
2395     sprite[i].hitag = 0;
2396     sprite[i].extra = -1;
2397 
2398     Bmemset(localartfreq, 0, sizeof(localartfreq));
2399     for (k=0; k<MAXSPRITES; k++)
2400         if (sprite[k].statnum < MAXSTATUS && k!=i)
2401             localartfreq[sprite[k].picnum]++;
2402 
2403     j = 0;
2404     for (k=0; k<MAXTILES; k++)
2405         if (localartfreq[k] > localartfreq[j])
2406             j = k;
2407 
2408     if (localartfreq[j] > 0)
2409         sprite[i].picnum = j;
2410     else
2411         sprite[i].picnum = 0;
2412 
2413     return i;
2414 }
2415 
correct_sprite_yoffset(int32_t i)2416 void correct_sprite_yoffset(int32_t i)
2417 {
2418     if ((sprite[i].cstat&48) >= 32)
2419         return;
2420     int32_t tileyofs = picanm[sprite[i].picnum].yofs;
2421     int32_t tileysiz = tilesiz[sprite[i].picnum].y;
2422 
2423     if (klabs(tileyofs) >= tileysiz)
2424     {
2425         tileyofs *= -1;
2426         if (tileyofs == 128)
2427             tileyofs = 127;
2428 
2429         sprite[i].yoffset = tileyofs;
2430     }
2431     else
2432         sprite[i].yoffset = 0;
2433 }
2434 
2435 // keepcol >= 0 && <256: keep that idx-color
2436 // keepcol < 0: keep none
2437 // keepcol >= 256: 0x00ffffff is mask for 3 colors
fade_editor_screen(int32_t keepcol)2438 void fade_editor_screen(int32_t keepcol)
2439 {
2440     char blackcol=0, greycol=whitecol-25, *cp;
2441     int32_t pix, i, threecols = (keepcol >= 256);
2442     char cols[3] = {(char)(keepcol&0xff), (char)((keepcol>>8)&0xff), (char)((keepcol>>16)&0xff)};
2443 
2444     videoBeginDrawing();
2445     cp = (char *)frameplace;
2446     for (i=0; i<bytesperline*(ydim-STATUS2DSIZ2); i++, cp++)
2447     {
2448         pix = (uint8_t)(*cp);
2449 
2450         if (!threecols && pix == keepcol)
2451             continue;
2452         if (threecols)
2453             if (pix==cols[0] || pix==cols[1] || pix==cols[2])
2454                 continue;
2455 
2456         if (*cp==greycol)
2457             *cp = blackcol;
2458         else if (*cp != blackcol)
2459             *cp = greycol;
2460     }
2461     videoEndDrawing();
2462     videoShowFrame(1);
2463 }
2464 
copy_some_wall_members(int16_t dst,int16_t src,int32_t reset_some)2465 static void copy_some_wall_members(int16_t dst, int16_t src, int32_t reset_some)
2466 {
2467     static uwalltype nullwall;
2468     walltype * const dstwal = &wall[dst];
2469     auto const srcwal = src >= 0 ? (uwallptr_t)&wall[src] : &nullwall;
2470 
2471     memset(&nullwall, 0, sizeof(nullwall));
2472     nullwall.yrepeat = 8;
2473     nullwall.extra = -1;
2474 
2475     if (reset_some)
2476     {
2477         dstwal->cstat = srcwal->cstat;
2478     }
2479     else
2480     {
2481         dstwal->cstat &= ~(4+8+256);
2482         dstwal->cstat |= (srcwal->cstat&(4+8+256));
2483     }
2484     dstwal->shade = srcwal->shade;
2485     dstwal->yrepeat = srcwal->yrepeat;
2486     fixrepeats(dst);  // xrepeat
2487     dstwal->picnum = srcwal->picnum;
2488     dstwal->overpicnum = srcwal->overpicnum;
2489 
2490     dstwal->pal = srcwal->pal;
2491     dstwal->xpanning = srcwal->xpanning;
2492     dstwal->ypanning = srcwal->ypanning;
2493 
2494     if (reset_some)
2495     {
2496         dstwal->nextwall = -1;
2497         dstwal->nextsector = -1;
2498 
2499         dstwal->lotag = 0; //srcwal->lotag;
2500         dstwal->hitag = 0; //srcwal->hitag;
2501         dstwal->extra = -1; //srcwal->extra;
2502 #ifdef YAX_ENABLE
2503         yax_setnextwall(dst, YAX_CEILING, -1);
2504         yax_setnextwall(dst, YAX_FLOOR, -1);
2505 #endif
2506     }
2507 }
2508 
init_new_wall1(int16_t * suckwall_ret,int32_t mousxplc,int32_t mousyplc)2509 static void init_new_wall1(int16_t *suckwall_ret, int32_t mousxplc, int32_t mousyplc)
2510 {
2511     int32_t i;
2512 
2513     Bmemset(&wall[newnumwalls], 0, sizeof(walltype));
2514     wall[newnumwalls].extra = -1;
2515 
2516     wall[newnumwalls].x = mousxplc;
2517     wall[newnumwalls].y = mousyplc;
2518     wall[newnumwalls].nextsector = -1;
2519     wall[newnumwalls].nextwall = -1;
2520 
2521     for (i=0; i<numwalls; i++)
2522     {
2523         YAX_SKIPWALL(i);
2524         if (wall[i].nextwall >= 0)
2525             YAX_SKIPWALL(wall[i].nextwall);
2526 
2527         if (wall[i].x == mousxplc && wall[i].y == mousyplc)
2528             *suckwall_ret = i;
2529     }
2530 
2531     wall[newnumwalls].point2 = newnumwalls+1;
2532     newnumwalls++;
2533 }
2534 
2535 // helpers for often needed ops:
do_while_copyloop1(int16_t startwall,int16_t endwall,int16_t * danumwalls,int16_t lastpoint2)2536 static int32_t do_while_copyloop1(int16_t startwall, int16_t endwall,
2537                                   int16_t *danumwalls, int16_t lastpoint2)
2538 {
2539     int32_t m = startwall;
2540 
2541     do
2542     {
2543         if (*danumwalls >= MAXWALLS + M32_FIXME_WALLS)
2544             return 1;
2545 
2546         Bmemcpy(&wall[*danumwalls], &wall[m], sizeof(walltype));
2547         wall[*danumwalls].point2 = *danumwalls+1;
2548         (*danumwalls)++;
2549         m = wall[m].point2;
2550     }
2551     while (m != endwall);
2552 
2553     if (lastpoint2 >= 0)
2554         wall[(*danumwalls)-1].point2 = lastpoint2;
2555 
2556     return 0;
2557 }
2558 
updatesprite1(int16_t i)2559 static void updatesprite1(int16_t i)
2560 {
2561     setsprite(i, &sprite[i].pos);
2562 
2563     if (sprite[i].sectnum>=0)
2564     {
2565         int32_t cz, fz;
2566         spriteoncfz(i, &cz, &fz);
2567         inpclamp(&sprite[i].z, cz, fz);
2568     }
2569 }
2570 
2571 #ifdef YAX_ENABLE
2572 // highlighted OR grayed-out sectors:
2573 static uint8_t hlorgraysectbitmap[(MAXSECTORS+7)>>3];
2574 static int32_t ask_above_or_below(void);
2575 #else
2576 # define hlorgraysectbitmap hlsectorbitmap
2577 #endif
2578 
2579 // returns:
2580 //  0: continue
2581 // >0: newnumwalls
2582 // <0: error
2583 // ignore_ret and refsect_ret are for the 'auto-red-wall' feature
trace_loop(int32_t j,uint8_t * visitedwall,int16_t * ignore_ret,int16_t * refsect_ret,int16_t trace_loop_yaxcf)2584 static int32_t trace_loop(int32_t j, uint8_t *visitedwall, int16_t *ignore_ret, int16_t *refsect_ret,
2585                           int16_t trace_loop_yaxcf)
2586 {
2587     int16_t refsect, ignore;
2588     int32_t k, n, refwall;
2589 #if 0
2590 //def YAX_ENABLE
2591     int32_t yaxp = (ignore_ret==NULL);  // bleh
2592 #else
2593     UNREFERENCED_PARAMETER(trace_loop_yaxcf);
2594 #endif
2595 
2596     if (wall[j].nextwall>=0 || (visitedwall[j>>3]&pow2char[j&7]))
2597         return 0;
2598 
2599     n=2*MAXWALLS;  // simple inf loop check
2600     refwall = j;
2601     k = numwalls;
2602 
2603     ignore = 0;
2604 
2605     if (ignore_ret)
2606     {
2607         refsect = -1;
2608         updatesectorexclude(wall[j].x, wall[j].y, &refsect, hlorgraysectbitmap);
2609         if (refsect<0)
2610             return -1;
2611     }
2612 
2613     do
2614     {
2615         if (j!=refwall && visitedwall[j>>3]&pow2char[j&7])
2616             ignore = 1;
2617         visitedwall[j>>3] |= pow2char[j&7];
2618 
2619         if (ignore_ret)
2620         {
2621             if (inside(wall[j].x, wall[j].y, refsect) != 1)
2622                 ignore = 1;
2623         }
2624 
2625         if (!ignore)
2626         {
2627             if (k>=MAXWALLS)
2628             {
2629                 message("Wall limits exceeded while tracing outer loop.");
2630                 return -2;
2631             }
2632 
2633             if (ignore_ret)  // auto-red wall feature
2634                 onextwall[k] = onextwall[j];
2635 
2636             Bmemcpy(&wall[k], &wall[j], sizeof(walltype));
2637             wall[k].point2 = k+1;
2638 // TODO: protect lotag/extra; see also hl-sector copying stuff
2639             wall[k].nextsector = wall[k].nextwall = wall[k].extra = -1;
2640 #ifdef YAX_ENABLE
2641             if (trace_loop_yaxcf >= 0)
2642                 yax_setnextwall(k, trace_loop_yaxcf, j);
2643 #endif
2644             k++;
2645         }
2646 
2647         j = wall[j].point2;
2648         n--;
2649 
2650         while (wall[j].nextwall>=0 && n>0)
2651         {
2652 #if 0
2653 //def YAX_ENABLE
2654             if (yaxp)
2655             {
2656                 int32_t ns = wall[j].nextsector;
2657                 if ((hlsectorbitmap[ns>>3]&pow2char[ns&7])==0)
2658                     break;
2659             }
2660 #endif
2661             j = wall[wall[j].nextwall].point2;
2662 //            if (j!=refwall && (visitedwall[j>>3]&pow2char[j&7]))
2663 //                ignore = 1;
2664 //            visitedwall[j>>3] |= pow2char[j&7];
2665             n--;
2666         }
2667     }
2668     while (j!=refwall && n>0);
2669 
2670     if (j!=refwall)
2671     {
2672         message("internal error while tracing outer loop: didn't reach refwall");
2673         return -3;
2674     }
2675 
2676     if (ignore_ret)
2677     {
2678         *ignore_ret = ignore;
2679         if (refsect_ret)
2680             *refsect_ret = refsect;
2681     }
2682 
2683     return k;
2684 }
2685 
2686 // Backup drawn walls for carrying out other operations in the middle.
2687 //  0: back up, set newnumwalls to -1
2688 //  1: restore drawn walls and free mem
2689 //  2: only free memory needed for backing up walls but don't restore walls
2690 //     (use this if the map has been mangled too much for a safe restoration)
2691 // Context that needs special treatment: suckwall, splitsect, splitstartwall
backup_drawn_walls(int32_t restore)2692 static int32_t backup_drawn_walls(int32_t restore)
2693 {
2694     static uwalltype *tmpwall;
2695 
2696     // back up
2697     if (restore==0)
2698     {
2699         // ovh.bak_wallsdrawn should be 0 here
2700 
2701         if (newnumwalls != -1)
2702         {
2703             if (newnumwalls <= numwalls)  // shouldn't happen
2704                 return 2;
2705 
2706             Xfree(tmpwall);
2707             tmpwall = (uwalltype *)Xmalloc((newnumwalls-numwalls) * sizeof(walltype));
2708 
2709             ovh.bak_wallsdrawn = newnumwalls-numwalls;
2710 
2711             Bmemcpy(tmpwall, &wall[numwalls], ovh.bak_wallsdrawn*sizeof(walltype));
2712             newnumwalls = -1;
2713         }
2714 
2715         return 0;
2716     }
2717 
2718     // restore/clear
2719     if (tmpwall)
2720     {
2721         if (restore==1)  // really restore
2722         {
2723             const int32_t nnumwalls = numwalls + ovh.bak_wallsdrawn;
2724 
2725             if (nnumwalls < MAXWALLS)  // else, silently discard drawn walls
2726             {
2727                 int32_t i;
2728 
2729                 Bmemcpy(&wall[numwalls], tmpwall, ovh.bak_wallsdrawn*sizeof(walltype));
2730 
2731                 newnumwalls = nnumwalls;
2732                 for (i=numwalls; i<newnumwalls; i++)
2733                     wall[i].point2 = i+1;
2734             }
2735         }
2736 
2737         DO_FREE_AND_NULL(tmpwall);
2738 
2739         ovh.bak_wallsdrawn = 0;
2740     }
2741 
2742     return 0;
2743 }
2744 
2745 // VARIOUS RESETTING FUNCTIONS
2746 #define RESET_EDITOR_VARS() do { \
2747     sectorhighlightstat = -1; \
2748     newnumwalls = -1; \
2749     joinsector[0] = -1; \
2750     circlewall = -1; \
2751     circlepoints = 7; \
2752     } while (0)
2753 
reset_highlightsector(void)2754 void reset_highlightsector(void)
2755 {
2756     Bmemset(hlsectorbitmap, 0, sizeof(hlsectorbitmap));
2757     update_highlightsector();
2758 }
2759 
reset_highlight(void)2760 void reset_highlight(void)  // walls and sprites
2761 {
2762     Bmemset(show2dwall, 0, sizeof(show2dwall));
2763     Bmemset(show2dsprite, 0, sizeof(show2dsprite));
2764     update_highlight();
2765 }
2766 
2767 #ifdef YAX_ENABLE
2768 static int16_t collnumsects[2];
2769 static int16_t collsectlist[2][MAXSECTORS];
2770 static uint8_t collsectbitmap[2][(MAXSECTORS+7)>>3];
2771 
collect_sectors1(int16_t * sectlist,uint8_t * sectbitmap,int16_t * numsectptr,int16_t startsec,int32_t alsoyaxnext,int32_t alsoonw)2772 static void collect_sectors1(int16_t *sectlist, uint8_t *sectbitmap, int16_t *numsectptr,
2773                              int16_t startsec, int32_t alsoyaxnext, int32_t alsoonw)
2774 {
2775     int32_t j, startwall, endwall, sectcnt;
2776 
2777     bfirst_search_init(sectlist, sectbitmap, numsectptr, MAXSECTORS, startsec);
2778 
2779     for (sectcnt=0; sectcnt<*numsectptr; sectcnt++)
2780     {
2781         for (WALLS_OF_SECTOR(sectlist[sectcnt], j))
2782         {
2783             if (wall[j].nextsector >= 0)
2784                 bfirst_search_try(sectlist, sectbitmap, numsectptr, wall[j].nextsector);
2785             else if (alsoonw && onextwall[j]>=0)
2786                 bfirst_search_try(sectlist, sectbitmap, numsectptr, sectorofwall(onextwall[j]));
2787         }
2788 
2789         if (alsoyaxnext)
2790         {
2791             int16_t bn[2], cf;
2792             yax_getbunches(sectlist[sectcnt], &bn[0], &bn[1]);
2793             for (cf=0; cf<2; cf++)
2794                 if (bn[cf]>=0)
2795                 {
2796                     for (SECTORS_OF_BUNCH(bn[cf], !cf, j))
2797                         bfirst_search_try(sectlist, sectbitmap, numsectptr, j);
2798                 }
2799         }
2800     }
2801 }
2802 
2803 
2804 static int32_t sectors_components(int16_t hlsectcnt, const int16_t *hlsectors, int32_t alsoyaxnext, int32_t alsoonw);
highlighted_sectors_components(int32_t alsoyaxnext,int32_t alsoonw)2805 static int32_t highlighted_sectors_components(int32_t alsoyaxnext, int32_t alsoonw)
2806 {
2807     return sectors_components(highlightsectorcnt, highlightsector, alsoyaxnext, alsoonw);
2808 }
2809 
2810 // whether all highlighted sectors are in one (returns 1), two (2)
2811 // or more (>2) connected components wrt the nextsector relation
2812 // -1 means error
2813 //  alsoyaxnext: also consider "yax-nextsector" relation
2814 //  alsoonw: also consider "old-nextwall" relation (must be valid)
sectors_components(int16_t hlsectcnt,const int16_t * hlsector,int32_t alsoyaxnext,int32_t alsoonw)2815 static int32_t sectors_components(int16_t hlsectcnt, const int16_t *hlsector, int32_t alsoyaxnext, int32_t alsoonw)
2816 {
2817     int32_t j, k, tmp;
2818 
2819     if (hlsectcnt<1)
2820         return 0;
2821 
2822     collect_sectors1(collsectlist[0], collsectbitmap[0], &collnumsects[0],
2823                      hlsector[0], alsoyaxnext, alsoonw);
2824 
2825     for (k=1; k<hlsectcnt; k++)
2826     {
2827         j = hlsector[k];
2828         if ((collsectbitmap[0][j>>3]&pow2char[j&7])==0)
2829         {
2830             // sector j not collected --> more than 1 conn. comp.
2831             collect_sectors1(collsectlist[1], collsectbitmap[1], &collnumsects[1],
2832                              j, alsoyaxnext, alsoonw);
2833             break;
2834         }
2835     }
2836 
2837     if (k == hlsectcnt)
2838         return 1;
2839 
2840     for (k=0; k<hlsectcnt; k++)
2841     {
2842         j = hlsector[k];
2843         tmp = (((collsectbitmap[0][j>>3]&pow2char[j&7])!=0) + (((collsectbitmap[1][j>>3]&pow2char[j&7])!=0)<<1));
2844 
2845         if (tmp==3)
2846             return -1;  // components only weakly connected
2847 
2848         if (tmp==0)
2849             return 3;  // sector j not reached
2850     }
2851 
2852     return 2;
2853 }
2854 
cmpgeomwal1(const void * w1,const void * w2)2855 static int cmpgeomwal1(const void *w1, const void *w2)
2856 {
2857     auto const wal1 = (uwallptr_t)&wall[B_UNBUF16(w1)];
2858     auto const wal2 = (uwallptr_t)&wall[B_UNBUF16(w2)];
2859 
2860     if (wal1->x == wal2->x)
2861         return wal1->y - wal2->y;
2862 
2863     return wal1->x - wal2->x;
2864 }
2865 
sort_walls_geometrically(int16_t * wallist,int32_t nwalls)2866 static void sort_walls_geometrically(int16_t *wallist, int32_t nwalls)
2867 {
2868     qsort(wallist, nwalls, sizeof(int16_t), &cmpgeomwal1);
2869 }
2870 #endif
2871 
SetFirstWall(int32_t sectnum,int32_t wallnum,int32_t alsoynw)2872 void SetFirstWall(int32_t sectnum, int32_t wallnum, int32_t alsoynw)
2873 {
2874 #ifdef YAX_ENABLE
2875     int32_t i, j, k=0;
2876 #endif
2877     const sectortype *sec = &sector[sectnum];
2878 
2879     if (sec->wallptr == wallnum)
2880     {
2881         message("Wall %d already first wall of sector %d", wallnum, sectnum);
2882         return;
2883     }
2884 
2885 #ifdef YAX_ENABLE
2886     if (alsoynw)
2887     {
2888         // Also consider upper/lower TROR neighbor walls.
2889         int32_t startwall, endwall;
2890         int16_t cf;
2891 
2892         for (i=0; i<numwalls; i++)
2893             editwall[i>>3] &= ~pow2char[i&7];
2894 
2895         for (cf=0; cf<2; cf++)
2896         {
2897             int16_t bunchnum;
2898             int32_t tempsect=sectnum, tempwall=wallnum;
2899 
2900             while ((bunchnum = yax_getbunch(tempsect, cf)) >= 0 &&
2901                    (tempsect=yax_is121(bunchnum, cf)) >= 0)
2902             {
2903                 tempwall = yax_getnextwall(tempwall, cf);
2904                 if (tempwall < 0)
2905                     break;  // corrupt!
2906                 editwall[tempwall>>3] |= 1<<(tempwall&7);
2907             }
2908         }
2909 
2910         for (i=0; i<numsectors; i++)
2911             for (WALLS_OF_SECTOR(i, j))
2912             {
2913                 if (editwall[j>>3]&pow2char[j&7])
2914                 {
2915                     setfirstwall(i, j);
2916                     k++;
2917                     break;
2918                 }
2919             }
2920     }
2921     else
2922     {
2923         // Only consider aimed at wall <wallnum>.
2924         int16_t cb = yax_getbunch(sectnum, YAX_CEILING);
2925         int16_t fb = yax_getbunch(sectnum, YAX_FLOOR);
2926 
2927         if ((cb>=0 && (sec->ceilingstat&2)) || (fb >= 0 && (sec->floorstat&2)))
2928         {
2929             message("Extended ceilings/floors must not be sloped to set first wall");
2930             return;
2931         }
2932     }
2933 
2934     if (k > 0)
2935         message("Set first walls (sector[].wallptr) for %d sectors", k+1);
2936 
2937     if (k == 0)
2938 #endif
2939         message("This wall now sector %d's first wall (sector[].wallptr)", sectnum);
2940 
2941     setfirstwall(sectnum, wallnum);
2942 
2943     mkonwinvalid_keeptempsect();
2944 
2945     asksave = 1;
2946 }
2947 
handlesecthighlight1(int32_t i,int32_t sub,int32_t nograycheck)2948 void handlesecthighlight1(int32_t i, int32_t sub, int32_t nograycheck)
2949 {
2950     int32_t j;
2951 
2952     if (sub)
2953     {
2954         hlsectorbitmap[i>>3] &= ~pow2char[i&7];
2955         for (j=sector[i].wallptr; j<sector[i].wallptr+sector[i].wallnum; j++)
2956         {
2957             if (wall[j].nextwall >= 0)
2958                 checksectorpointer(wall[j].nextwall,wall[j].nextsector);
2959             checksectorpointer(j, i);
2960         }
2961     }
2962     else
2963     {
2964         if (nograycheck || (graysectbitmap[i>>3]&pow2char[i&7])==0)
2965             hlsectorbitmap[i>>3] |= pow2char[i&7];
2966     }
2967 }
2968 
2969 #ifdef YAX_ENABLE
2970 // 1: good, 0: bad
hl_all_bunch_sectors_p()2971 static int32_t hl_all_bunch_sectors_p()
2972 {
2973     uint8_t *const havebunch = visited;
2974     int16_t cf, cb, fb;
2975     int32_t i, j;
2976 
2977     if (numyaxbunches > 0)
2978     {
2979         Bmemset(havebunch, 0, (numyaxbunches+7)>>3);
2980         for (i=0; i<highlightsectorcnt; i++)
2981         {
2982             yax_getbunches(highlightsector[i], &cb, &fb);
2983             if (cb>=0)
2984                 havebunch[cb>>3] |= pow2char[cb&7];
2985             if (fb>=0)
2986                 havebunch[fb>>3] |= pow2char[fb&7];
2987         }
2988 
2989         for (i=0; i<numyaxbunches; i++)
2990         {
2991             if ((havebunch[i>>3] & pow2char[i&7])==0)
2992                 continue;
2993 
2994             for (cf=0; cf<2; cf++)
2995                 for (SECTORS_OF_BUNCH(i,cf, j))
2996                     if ((hlsectorbitmap[j>>3]&pow2char[j&7])==0)
2997                         return 0;
2998         }
2999     }
3000 
3001     return 1;
3002 }
3003 #endif
3004 
find_nextwall(int32_t sectnum,int32_t sectnum2)3005 static int32_t find_nextwall(int32_t sectnum, int32_t sectnum2)
3006 {
3007     int32_t j, startwall, endwall;
3008 
3009     if (sectnum<0 || sectnum2<0)
3010         return -1;
3011 
3012     for (WALLS_OF_SECTOR(sectnum, j))
3013         if (wall[j].nextsector == sectnum2)
3014             return j;
3015 
3016     return -1;
3017 }
3018 
bakframe_fillandfade(char ** origframeptr,int32_t sectnum,const char * querystr)3019 static int32_t bakframe_fillandfade(char **origframeptr, int32_t sectnum, const char *querystr)
3020 {
3021     if (!*origframeptr)
3022     {
3023         *origframeptr = (char *)Xmalloc(xdim*ydim);
3024 
3025         videoBeginDrawing();
3026         Bmemcpy(*origframeptr, (char *)frameplace, xdim*ydim);
3027         videoEndDrawing();
3028     }
3029     else
3030     {
3031         videoBeginDrawing();
3032         Bmemcpy((char *)frameplace, *origframeptr, xdim*ydim);
3033         videoEndDrawing();
3034     }
3035 
3036     fillsector_notrans(sectnum, editorcolors[9]);
3037     fade_editor_screen(editorcolors[9]);
3038 
3039     return ask_if_sure(querystr, 0);
3040 }
3041 
3042 #ifdef YAX_ENABLE
M32_MarkPointInsertion(int32_t thewall)3043 static void M32_MarkPointInsertion(int32_t thewall)
3044 {
3045     int32_t i, tmpcf;
3046     int32_t nextw = wall[thewall].nextwall;
3047 
3048     // round 1
3049     for (YAX_ITER_WALLS(thewall, i, tmpcf))
3050         editwall[i>>3] |= 1<<(i&7);
3051     if (nextw >= 0)
3052         for (YAX_ITER_WALLS(nextw, i, tmpcf))
3053                 editwall[i>>3] |= 1<<(i&7);
3054     // round 2 (enough?)
3055     for (YAX_ITER_WALLS(thewall, i, tmpcf))
3056         if (wall[i].nextwall >= 0 && (editwall[wall[i].nextwall>>3]&pow2char[wall[i].nextwall&7])==0)
3057             editwall[wall[i].nextwall>>3] |= 1<<(wall[i].nextwall&7);
3058     if (nextw >= 0)
3059         for (YAX_ITER_WALLS(nextw, i, tmpcf))
3060             if (wall[i].nextwall >= 0 && (editwall[wall[i].nextwall>>3]&pow2char[wall[i].nextwall&7])==0)
3061                 editwall[wall[i].nextwall>>3] |= 1<<(wall[i].nextwall&7);
3062 }
3063 #endif
3064 
3065 // High-level insert point, handles TROR constrained walls too
3066 //  onewnumwalls: old numwalls + drawn walls.
3067 // <mapwallnum>: see insertpoint()
3068 // Returns:
3069 //  0 if wall limit would be reached.
3070 //  1 if inserted point on a plain white or 2 points on a plain red wall.
3071 //  N >= 2 if inserted N points on TROR-constrained wall.
3072 //  N|(EXPECTED<<16) if inserted N points but EXPECTED walls were expected.
M32_InsertPoint(int32_t thewall,int32_t dax,int32_t day,int16_t onewnumwalls,int32_t * mapwallnum)3073 static int32_t M32_InsertPoint(int32_t thewall, int32_t dax, int32_t day, int16_t onewnumwalls, int32_t *mapwallnum)
3074 {
3075 #ifdef YAX_ENABLE
3076     int32_t nextw = wall[thewall].nextwall;
3077     int32_t i, j, k, m;
3078 
3079     if (yax_islockedwall(thewall) || (nextw>=0 && yax_islockedwall(nextw)))
3080     {
3081         // yax'ed wall -- first find out which walls are affected
3082         for (i=0; i<numwalls; i++)
3083             editwall[i>>3] &= ~pow2char[i&7];
3084 
3085         M32_MarkPointInsertion(thewall);
3086 
3087         for (i=0; i < numwalls; i++)
3088             if (editwall[i>>3]&pow2char[i&7])
3089                 M32_MarkPointInsertion(i);
3090 
3091         j = 0;
3092         for (i=0; i<numwalls; i++)
3093             j += !!(editwall[i>>3]&pow2char[i&7]);
3094         if (max(numwalls,onewnumwalls)+j > MAXWALLS)
3095         {
3096             return 0;  // no points inserted, would exceed limits
3097         }
3098 
3099         // the actual insertion!
3100         m = 0;
3101         for (i=0; i<numwalls /* rises with ins. */; i++)
3102         {
3103             if (editwall[i>>3]&pow2char[i&7])
3104                 if (wall[i].nextwall<0 || i<wall[i].nextwall) // || !(NEXTWALL(i).cstat&(1<<14)) ??
3105                 {
3106                     m += insertpoint(i, dax,day, mapwallnum);
3107                 }
3108         }
3109 
3110         for (i=0; i<numwalls; i++)
3111         {
3112             if (editwall[i>>3]&pow2char[i&7])
3113             {
3114                 editwall[i>>3] &= ~pow2char[i&7];
3115                 k = yax_getnextwall(i+1, YAX_CEILING);
3116                 if (k >= 0)
3117                     yax_setnextwall(i+1, YAX_CEILING, k+1);
3118                 k = yax_getnextwall(i+1, YAX_FLOOR);
3119                 if (k >= 0)
3120                     yax_setnextwall(i+1, YAX_FLOOR, k+1);
3121             }
3122         }
3123 
3124         if (m==j)
3125             return m;
3126         else
3127             return m|(j<<16);
3128     }
3129     else
3130 #endif
3131     {
3132         insertpoint(thewall, dax,day, mapwallnum);
3133         return 1;
3134     }
3135 }
3136 
3137 
3138 // based on lineintersect in engine.c, but lines are considered as infinitely
3139 // extending
inflineintersect(int32_t x1,int32_t y1,int32_t x2,int32_t y2,int32_t x3,int32_t y3,int32_t x4,int32_t y4,int32_t * intx,int32_t * inty,int32_t * sign12,int32_t * sign34)3140 void inflineintersect(int32_t x1, int32_t y1, int32_t x2, int32_t y2,
3141                       int32_t x3, int32_t y3, int32_t x4, int32_t y4,
3142                       int32_t *intx, int32_t *inty, int32_t *sign12, int32_t *sign34)
3143 {
3144     //p1 to p2 is a line segment
3145 
3146     int64_t const x21 = x2-x1;
3147     int64_t const x34 = x3-x4;
3148     int64_t const y21 = y2-y1;
3149     int64_t const y34 = y3-y4;
3150     int64_t const bot = x21*y34 - y21*x34;
3151 
3152     if (EDUKE32_PREDICT_FALSE(bot == 0))
3153     {
3154         *sign12 = *sign34 = 0;
3155         return;
3156     }
3157 
3158     int64_t const x31 = x3-x1;
3159     int64_t const y31 = y3-y1;
3160 
3161     int64_t const topt = x31*y34 - y31*x34;
3162     int64_t const topu = x21*y31 - y21*x31;
3163 
3164     int64_t const t = tabledivide64_noinline(topt*(1<<24), bot);
3165 
3166     *intx = x1 + ((x21*t)>>24);
3167     *inty = y1 + ((y21*t)>>24);
3168 
3169     *sign12 = topt < 0 ? -1 : 1;
3170     *sign34 = topu < 0 ? -1 : 1;
3171 }
3172 
lineintersect2v(const vec2_t * p1,const vec2_t * p2,const vec2_t * q1,const vec2_t * q2,vec2_t * pint)3173 static int32_t lineintersect2v(const vec2_t *p1, const vec2_t *p2,  // line segment 1
3174                                const vec2_t *q1, const vec2_t *q2,  // line segment 2
3175                                vec2_t *pint)
3176 {
3177     int32_t intz;
3178     return lintersect(p1->x, p1->y, 0, p2->x, p2->y, 0,
3179                          q1->x, q1->y, q2->x, q2->y,
3180                          &pint->x, &pint->y, &intz);
3181 }
3182 
vec2eq(const vec2_t * v1,const vec2_t * v2)3183 static int32_t vec2eq(const vec2_t *v1, const vec2_t *v2)
3184 {
3185     return (v1->x==v2->x && v1->y==v2->y);
3186 }
3187 
3188 #ifdef YAX_ENABLE
3189 // After auto-creating inner sector <ns> in existing sector <os>, we need to
3190 // see if some sprites contained in <os> need to change their sector.
CorrectSpriteSectnums(int32_t os,int32_t ns)3191 static void CorrectSpriteSectnums(int32_t os, int32_t ns)
3192 {
3193     int32_t i, ni;
3194 
3195     for (SPRITES_OF_SECT_SAFE(os, i, ni))
3196     {
3197         if (inside(sprite[i].x, sprite[i].y, ns)==1)
3198             changespritesect(i, ns);
3199     }
3200 }
3201 #endif
3202 
3203 // precondition: [numwalls, newnumwalls-1] form a new loop (may be of wrong orientation)
3204 // ret_ofirstwallofs: if != NULL, *ret_ofirstwallofs will contain the offset of the old
3205 //                    first wall from the new first wall of the sector k, and the automatic
3206 //                    restoring of the old first wll will not be carried out
3207 // returns:
3208 //  -1, -2: errors
3209 //   0,  1: OK, 1 means it was an extended sector and an inner loop has been added automatically
AddLoopToSector(int32_t k,int32_t * ret_ofirstwallofs)3210 static int32_t AddLoopToSector(int32_t k, int32_t *ret_ofirstwallofs)
3211 {
3212     int32_t extendedSector=0, firstwall, i, j;
3213 #ifdef YAX_ENABLE
3214     int16_t cbunch, fbunch;
3215     int32_t newnumwalls2;
3216 
3217     yax_getbunches(k, &cbunch, &fbunch);
3218     extendedSector = (cbunch>=0 || fbunch>=0);
3219 #endif
3220     j = newnumwalls-numwalls;
3221 #ifdef YAX_ENABLE
3222     newnumwalls2 = newnumwalls + j;
3223 
3224     if (extendedSector)
3225     {
3226         if ((cbunch>=0 && (sector[k].ceilingstat&2))
3227             || (fbunch>=0 && (sector[k].floorstat&2)))
3228         {
3229             printmessage16("Sloped extended sectors cannot be subdivided.");
3230             newnumwalls--;
3231             return -1;
3232         }
3233 
3234         if (newnumwalls + j > MAXWALLS || numsectors+1 > MAXSECTORS)
3235         {
3236             message("Automatically adding inner sector to new extended sector would exceed limits!");
3237             newnumwalls--;
3238             return -2;
3239         }
3240     }
3241 #endif
3242     if (clockdir(numwalls) == CLOCKDIR_CW)
3243         flipwalls(numwalls,newnumwalls);
3244 
3245     sector[k].wallnum += j;
3246     for (i=k+1; i<numsectors; i++)
3247         sector[i].wallptr += j;
3248     firstwall = sector[k].wallptr;
3249 
3250     for (i=0; i<numwalls; i++)
3251     {
3252         if (wall[i].nextwall >= firstwall)
3253             wall[i].nextwall += j;
3254         if (wall[i].point2 >= firstwall)
3255             wall[i].point2 += j;
3256     }
3257 #ifdef YAX_ENABLE
3258     yax_tweakwalls(firstwall, j);
3259 #endif
3260 
3261     Bmemmove(&wall[firstwall+j], &wall[firstwall], (newnumwalls-firstwall)*sizeof(walltype));
3262     // add new loop to beginning of sector
3263     Bmemmove(&wall[firstwall], &wall[newnumwalls], j*sizeof(walltype));
3264 
3265     for (i=firstwall; i<firstwall+j; i++)
3266     {
3267         wall[i].point2 += (firstwall-numwalls);
3268 
3269         copy_some_wall_members(i, firstwall+j, 1);
3270         wall[i].cstat &= ~(1+16+32+64);
3271     }
3272 
3273     numwalls = newnumwalls;
3274     newnumwalls = -1;
3275 #ifdef YAX_ENABLE
3276     if (extendedSector)
3277     {
3278         newnumwalls = whitelinescan(k, firstwall);
3279         if (newnumwalls != newnumwalls2)
3280             message("AddLoopToSector: newnumwalls != newnumwalls2!!! WTF?");
3281         for (i=numwalls; i<newnumwalls; i++)
3282         {
3283             NEXTWALL(i).nextwall = i;
3284             NEXTWALL(i).nextsector = numsectors;
3285         }
3286 
3287         yax_setbunches(numsectors, cbunch, fbunch);
3288 
3289         numwalls = newnumwalls;
3290         newnumwalls = -1;
3291         numsectors++;
3292 
3293         CorrectSpriteSectnums(k, numsectors-1);
3294     }
3295 #endif
3296     if (ret_ofirstwallofs)
3297         *ret_ofirstwallofs = j;
3298     else
3299         setfirstwall(k, firstwall+j);  // restore old first wall
3300 
3301     return extendedSector;
3302 }
3303 
select_sprite_tag(int32_t spritenum)3304 int32_t select_sprite_tag(int32_t spritenum)
3305 {
3306     int32_t lt = taglab_linktags(1, spritenum);
3307     spritetype *spr = &sprite[spritenum];
3308 
3309     if (lt==0)
3310         return INT32_MIN;
3311 
3312     if (lt&1)
3313         return spr->lotag;
3314     if (lt&2)
3315         return spr->hitag;
3316     if (lt&4)
3317         return spr->extra;
3318     if (lt&8)
3319         return spr->xvel;
3320     if (lt&16)
3321         return spr->yvel;
3322     if (lt&32)
3323         return spr->zvel;
3324     if (lt&64)
3325         return spr->extra;
3326 
3327     return INT32_MIN;
3328 }
3329 
drawlinebetween(const vec3_t * v1,const vec3_t * v2,int32_t col,uint32_t pat)3330 void drawlinebetween(const vec3_t *v1, const vec3_t *v2, int32_t col, uint32_t pat)
3331 {
3332     // based on m32exec.c/drawline*
3333     const int32_t xofs=halfxdim16, yofs=midydim16;
3334     const uint32_t opat=drawlinepat;
3335 
3336     int32_t x1, x2, y1, y2;
3337 
3338     editorGet2dScreenCoordinates(&x1,&y1, v1->x-pos.x,v1->y-pos.y, zoom);
3339     editorGet2dScreenCoordinates(&x2,&y2, v2->x-pos.x,v2->y-pos.y, zoom);
3340 
3341     if (m32_sideview)
3342     {
3343         y1 += getscreenvdisp(v1->z-pos.z,zoom);
3344         y2 += getscreenvdisp(v2->z-pos.z,zoom);
3345     }
3346 
3347     drawlinepat = pat;
3348     editorDraw2dLine(xofs+x1,yofs+y1, xofs+x2,yofs+y2, col);
3349     drawlinepat = opat;
3350 }
3351 
3352 // world -> screen coords for overhead mode
ovhscrcoords(int32_t x,int32_t y,int32_t * scrx,int32_t * scry)3353 void ovhscrcoords(int32_t x, int32_t y, int32_t *scrx, int32_t *scry)
3354 {
3355     *scrx = halfxdim16 + mulscale14(x-pos.x, zoom);
3356     *scry = midydim16 + mulscale14(y-pos.y, zoom);
3357 }
3358 
draw_cross(int32_t centerx,int32_t centery,int32_t radius,int32_t col)3359 static void draw_cross(int32_t centerx, int32_t centery, int32_t radius, int32_t col)
3360 {
3361     int32_t dax, day;
3362     ovhscrcoords(centerx, centery, &dax, &day);
3363     drawline16base(dax, day, -radius,-radius, +radius,+radius, col);
3364     drawline16base(dax, day, -radius,+radius, +radius,-radius, col);
3365 }
3366 
draw_square(int32_t dax,int32_t day,int32_t ps,int32_t col)3367 static void draw_square(int32_t dax, int32_t day, int32_t ps, int32_t col)
3368 {
3369     ovhscrcoords(dax, day, &dax, &day);
3370     drawline16base(dax, day, -ps,-ps, +ps,-ps, col);
3371     drawline16base(dax, day, +ps,-ps, +ps,+ps, col);
3372     drawline16base(dax, day, +ps,+ps, -ps,+ps, col);
3373     drawline16base(dax, day, -ps,+ps, -ps,-ps, col);
3374 }
3375 
3376 //// Interactive Scaling
3377 static struct {
3378     int8_t active, rotatep;
3379     vec2_t piv;  // pivot point
3380     int32_t dragx, dragy;  // dragged point
3381     int32_t xsc, ysc, ang;
3382 } isc;
3383 
isc_transform(int32_t * x,int32_t * y)3384 static void isc_transform(int32_t *x, int32_t *y)
3385 {
3386     if (!isc.rotatep)
3387     {
3388         *x = isc.piv.x + mulscale16(*x-isc.piv.x, isc.xsc);
3389         *y = isc.piv.y + mulscale16(*y-isc.piv.y, isc.ysc);
3390     }
3391     else
3392     {
3393         vec2_t v = { *x, *y };
3394         rotatepoint(isc.piv, v, isc.ang, &v);
3395         *x = v.x;
3396         *y = v.y;
3397     }
3398 }
3399 
drawspritelabel(int i)3400 static void drawspritelabel(int i)
3401 {
3402     // XXX: oob 'i' may happen, such as passing pointhighlight-16384 when
3403     // pointhighlight == -1.
3404     if ((unsigned)i >= MAXSPRITES)
3405         return;
3406 
3407     const char *dabuffer = CallExtGetSpriteCaption(i);
3408 
3409     if (!dabuffer[0])
3410         return;
3411 
3412     // KEEPINSYNC drawscreen_drawsprite()
3413     uspriteptr_t s = (uspriteptr_t)&sprite[i];
3414     uint8_t const spritecol = spritecol2d[s->picnum][(s->cstat&1)];
3415     int col = spritecol ? editorcolors[spritecol] : editorGet2dSpriteColor(i);
3416     int const blocking = s->cstat & 1;
3417     int bordercol = blocking ? editorcolors[5] : col;
3418 
3419     // group selection
3420     if (show2dsprite[i>>3]&pow2char[i&7])
3421     {
3422         bordercol = editorcolors[14];
3423         col = bordercol - (M32_THROB>>1);
3424     }
3425     else if (i == pointhighlight - 16384)
3426     {
3427         if (spritecol >= 8 && spritecol <= 15)
3428             col -= bloodhack ? M32_THROB>>2 : M32_THROB>>1;
3429         else col += M32_THROB>>2;
3430 
3431         if (bordercol > col && !blocking)
3432             bordercol = col;
3433     }
3434 
3435     else if (s->sectnum < 0)
3436         col = bordercol = editorcolors[4];  // red
3437 
3438     drawsmallabel(dabuffer, editorcolors[0], col, bordercol, s->x, s->y, s->z);
3439 }
3440 
3441 #define EDITING_MAP_P() (newnumwalls>=0 || joinsector[0]>=0 || circlewall>=0 || (bstatus&1) || isc.active)
3442 
3443 #define HLMEMBERX(Hl, Member) (*(((Hl)&16384) ? &sprite[(Hl)&16383].Member : &wall[Hl].Member))
3444 #define HLMEMBER(Hlidx, Member) HLMEMBERX(highlight[Hlidx], Member)
3445 
maybedeletewalls(int32_t dax,int32_t day)3446 static void maybedeletewalls(int32_t dax, int32_t day)
3447 {
3448     int       numdelpoints   = 0;
3449     int const havedrawnwalls = (newnumwalls != -1);
3450     int       restorestat    = 1;
3451 
3452     // attempt to delete some points
3453     for (int runi=0; runi<3; runi++)  // check, tweak, carry out
3454         for (int i=numwalls-1; i>=0; i--)
3455         {
3456             if (runi==0)
3457                 editwall[i>>3] &= ~pow2char[i&7];;
3458 
3459             if (wall[i].x == POINT2(i).x && wall[i].y == POINT2(i).y)
3460             {
3461                 if (havedrawnwalls)
3462                 {
3463                     if (i==ovh.suckwall || (ovh.split && i==ovh.splitstartwall))
3464                     {
3465                         // if we're about to delete a wall that participates
3466                         // in splitting, discard the already drawn walls
3467                         restorestat = 2;
3468                     }
3469                     else if (runi == 1)
3470                     {
3471                         // correct drawn wall anchors
3472                         if (ovh.suckwall > i)
3473                             ovh.suckwall--;
3474                         if (ovh.split && ovh.splitstartwall > i)
3475                             ovh.splitstartwall--;
3476                     }
3477                 }
3478 
3479                 if (runi == 0)
3480                 {
3481                     int32_t sectnum = sectorofwall(i);
3482                     if (sector[sectnum].wallnum <= 3)
3483                     {
3484                         message("Deleting wall %d would leave sector %d with %d walls.",
3485                             i, sectnum, sector[sectnum].wallnum-1);
3486                         goto end_after_dragging;
3487                     }
3488 
3489                     sectnum = wall[i].nextsector;
3490                     if (sectnum >= 0 && sector[sectnum].wallnum <= 3)
3491                     {
3492                         message("Deleting wall %d would leave sector %d with %d walls.",
3493                             i, sectnum, sector[sectnum].wallnum-1);
3494                         goto end_after_dragging;
3495                     }
3496                 }
3497                 else
3498                 {
3499                     deletepoint(i, runi);
3500                     if (runi==2)
3501                         numdelpoints++;
3502                 }
3503             }
3504         }
3505 
3506     if (numdelpoints)
3507     {
3508         if (numdelpoints > 1)
3509             message("Deleted %d points%s", numdelpoints,
3510             (havedrawnwalls && restorestat==2) ? " and cleared drawn walls" : "");
3511         else
3512             printmessage16("Point deleted%s", (havedrawnwalls && restorestat==2) ?
3513                 ", cleared drawn walls" : "");
3514         asksave = 1;
3515     }
3516     else
3517     {
3518         for (int i=0; i<numwalls; i++)     //make new red lines?
3519         {
3520             YAX_SKIPWALL(i);
3521 
3522             if ((wall[i].x == dax && wall[i].y == day)
3523                 || (POINT2(i).x == dax && POINT2(i).y == day))
3524             {
3525                 checksectorpointer(i, sectorofwall(i));
3526                 //                    fixrepeats(i);
3527                 asksave = 1;
3528             }
3529         }
3530     }
3531 #ifdef YAX_ENABLE
3532     yax_update(0);
3533     yax_updategrays(pos.z);
3534 #endif
3535 end_after_dragging:
3536     backup_drawn_walls(restorestat);
3537 }
3538 
deletewall(int w)3539 static void deletewall(int w)
3540 {
3541     if ((w & 0xc000) != 16384)
3542     {
3543         if (sector[sectorofwall(w)].wallnum > 3 && (wall[w].nextwall == -1 || sector[sectorofwall(wall[w].nextwall)].wallnum > 3))
3544         {
3545             dragpoint(w, POINT2(w).x, POINT2(w).y, 0);
3546             maybedeletewalls(wall[w].x, wall[w].y);
3547         }
3548 /*
3549         else
3550         {
3551             deletesector(sectorofwall(w));
3552             mkonwinvalid();
3553             printmessage16("Sector deleted.");
3554         }
3555 */
3556     }
3557 }
3558 
overheadeditor(void)3559 void overheadeditor(void)
3560 {
3561     char buffer[80];
3562     const char *dabuffer;
3563     int32_t i, j, k, m=0, mousxplc, mousyplc, firstx=0, firsty=0, oposz, col;
3564     int32_t numwalls_bak;
3565     int32_t startwall=0, endwall, dax, day, x1, y1, x2, y2, x3, y3; //, x4, y4;
3566     int16_t bad, joinsector[2];
3567     int32_t bstatus, mousewaitmask=0;
3568     int16_t circlepoints;
3569     int32_t sectorhighlightx=0, sectorhighlighty=0;
3570     int16_t cursectorhighlight, sectorhighlightstat;
3571     int32_t prefixarg = 0, tsign;
3572     int32_t resetsynctics = 0, lasttick=timerGetTicks(), waitdelay=(int32_t) totalclock, lastdraw=timerGetTicks();
3573     int32_t olen[2] = {0, 0}, dragwall[2] = {-1, -1};
3574     int16_t linehighlight2 = -1;
3575     vec2_t highlight1 = { 0, 0 }, highlight2 = { 0, 0 };
3576 
3577     ovh.suckwall = -1;
3578     ovh.split = 0;
3579     ovh.splitsect = -1;
3580     ovh.splitstartwall = -1;
3581 
3582     videoSet2dMode(xdim2d,ydim2d);
3583     xdim2d = xdim;
3584     ydim2d = ydim;
3585 
3586     osearchx = searchx;
3587     osearchy = searchy;
3588 
3589     searchx = clamp(scale(searchx,xdim2d,xdimgame), 8, xdim2d-8-1);
3590     searchy = clamp(scale(searchy,ydim2d-STATUS2DSIZ2,ydimgame), 8, ydim2d-STATUS2DSIZ-8-1);
3591     oposz = pos.z;
3592 
3593     yax_updategrays(pos.z);
3594 
3595     videoBeginDrawing(); //{{{
3596     CLEARLINES2D(0, ydim, 0);
3597     videoEndDrawing(); //}}}
3598 
3599     ydim16 = ydim-STATUS2DSIZ2;
3600 
3601     cursectorhighlight = -1;
3602     lastpm16time = -1;
3603 
3604     update_highlightsector();
3605     ovh_whiteoutgrab(0);
3606 
3607     highlightcnt = -1;
3608     Bmemset(show2dwall, 0, sizeof(show2dwall));  //Clear all highlights
3609     Bmemset(show2dsprite, 0, sizeof(show2dsprite));
3610 
3611     RESET_EDITOR_VARS();
3612     bstatus = 0;
3613 
3614     while ((keystatus[buildkeys[BK_MODE2D_3D]]>>1) == 0)
3615     {
3616         int32_t mousx = 0, mousy = 0;
3617 
3618         if (zoom < ztarget)
3619         {
3620             if ((ztarget - zoom) >> 3)
3621                 zoom += synctics * ((ztarget - zoom) >> 3);
3622             else zoom++;
3623             zoom = min(zoom, ztarget);
3624         }
3625         else if (zoom > ztarget)
3626         {
3627             if ((zoom - ztarget) >> 3)
3628                 zoom -= synctics * ((zoom - ztarget) >> 3);
3629             else zoom--;
3630             zoom = max(zoom, ztarget);
3631         }
3632 
3633         if (!((vel|angvel|svel) || m32_is2d3dmode() || ztarget != zoom//DOWN_BK(MOVEFORWARD) || DOWN_BK(MOVEBACKWARD) || DOWN_BK(TURNLEFT) || DOWN_BK(TURNRIGHT)
3634                 || DOWN_BK(MOVEUP) || DOWN_BK(MOVEDOWN) || keystatus[sc_Q] || keystatus[sc_W]
3635                 || keystatus[sc_kpad_8] || keystatus[sc_kpad_4] || keystatus[sc_kpad_6] || keystatus[sc_kpad_2]  // keypad keys
3636                 || bstatus || OSD_IsMoving()))
3637         {
3638             if (totalclock > waitdelay)
3639             {
3640                 uint32_t ms = 50;// (highlightsectorcnt>0) ? 75 : 200;
3641                 // wait for event, timeout after 200 ms - (last loop time)
3642                 idle_waitevent_timeout(ms - min(timerGetTicks()-lasttick, ms));
3643                 // have synctics reset to 0 after we've slept to avoid zooming out to the max instantly
3644                 resetsynctics = 1;
3645             }
3646         }
3647         else waitdelay = (int32_t) totalclock + 6; // should be 50 ms
3648 
3649         lasttick = timerGetTicks();
3650 
3651         if (handleevents())
3652         {
3653             if (quitevent)
3654             {
3655                 keystatus[sc_Escape] = 1;
3656                 quitevent = 0;
3657             }
3658         }
3659 
3660         if (resetsynctics)
3661         {
3662             resetsynctics = 0;
3663             lockclock = (int32_t) totalclock;
3664             synctics = 0;
3665         }
3666 
3667         OSD_DispatchQueued();
3668 
3669         if (totalclock < 120*3)
3670             printmessage16("Uses BUILD technology by Ken Silverman.");
3671         else if (totalclock < 120*6)
3672         {
3673             printmessage16("Press F1 for help.  This is a test release; always keep backups of your maps.");
3674             //        printext16(8L,ydim-STATUS2DSIZ+32L,editorcolors[9],-1,kensig,0);
3675         }
3676 
3677         if (!m32_is2d3dmode())
3678         {
3679             oldmousebstatus = bstatus;
3680             mouseGetValues(&mousx, &mousy, &bstatus);
3681 
3682             {
3683                 int32_t bs = bstatus;
3684                 bstatus &= ~mousewaitmask;
3685                 mousewaitmask &= bs;
3686             }
3687 
3688             mousx = (mousx<<16)+mousexsurp;
3689             mousy = (mousy<<16)+mouseysurp;
3690             {
3691                 ldiv_t ld;
3692                 ld = ldiv(mousx, 1<<16); mousx = ld.quot; mousexsurp = ld.rem;
3693                 ld = ldiv(mousy, 1<<16); mousy = ld.quot; mouseysurp = ld.rem;
3694             }
3695             searchx += mousx;
3696             searchy += mousy;
3697 
3698             inpclamp(&searchx, 8, xdim-8-1);
3699             inpclamp(&searchy, 8, ydim-8-1);
3700 
3701             mainloop_move();
3702 
3703             getpoint(searchx, searchy, &mousxplc, &mousyplc);
3704             linehighlight = getlinehighlight(mousxplc, mousyplc, linehighlight, 0);
3705             linehighlight2 = getlinehighlight(mousxplc, mousyplc, linehighlight, 1);
3706 
3707             if (!m32_sideview)
3708                 updatesector(mousxplc, mousyplc, &sectorhighlight);
3709             else
3710                 sectorhighlight = -1;
3711         }
3712 
3713         if ((unsigned)newnumwalls < MAXWALLS && newnumwalls >= numwalls)
3714         {
3715             // if we're in the process of drawing a wall, set the end point's coordinates
3716             dax = mousxplc;
3717             day = mousyplc;
3718             adjustmark(&dax,&day,numwalls+!ovh.split);
3719             wall[newnumwalls].x = dax;
3720             wall[newnumwalls].y = day;
3721         }
3722 
3723         ydim16 = ydim;// - STATUS2DSIZ2;
3724         midydim16 = ydim>>1;
3725 
3726         numwalls_bak = numwalls;
3727         numwalls = newnumwalls;
3728         if (numwalls < 0)
3729             numwalls = numwalls_bak;
3730 
3731         if ((timerGetTicks() - lastdraw) >= 5 || (vel|angvel|svel) || DOWN_BK(MOVEUP) || DOWN_BK(MOVEDOWN)
3732                 || mousx || mousy || bstatus || keystatus[sc_Q] || keystatus[sc_W]
3733                 || newnumwalls>=0 || OSD_IsMoving())
3734         {
3735             lastdraw = timerGetTicks();
3736 
3737             clear2dscreen();
3738 
3739             editorSetup2dSideView();
3740 
3741             VM_OnEvent(EVENT_PREDRAW2DSCREEN, -1);
3742 
3743             if (graphicsmode && (!m32_sideview || m32_sideelev == 512))
3744             {
3745                 Bmemset(show2dsector, 0, sizeof(show2dsector));
3746                 for (i=0; i<numsectors; i++)
3747                 {
3748                     YAX_SKIPSECTOR(i);
3749                     show2dsector[i>>3] |= pow2char[i&7];
3750                 }
3751 
3752                 videoSetViewableArea(0, 0, xdim-1, ydim16-1);
3753 
3754                 if (graphicsmode == 2)
3755                     totalclocklock = totalclock;
3756 
3757                 renderDrawMapView(pos.x, pos.y, zoom, m32_sideview ? (3584 - m32_sideang) & 2047: 1536);
3758             }
3759 
3760             editorDraw2dGrid(pos.x,pos.y,pos.z,cursectnum,ang,zoom,grid);
3761             CallExtPreCheckKeys();
3762             editorDraw2dScreen(&pos,cursectnum,ang,zoom,grid);
3763 
3764             // Draw brown arrow (start)
3765             editorGet2dScreenCoordinates(&x2, &y2, startpos.x-pos.x,startpos.y-pos.y, zoom);
3766             if (m32_sideview)
3767                 y2 += getscreenvdisp(startpos.z-pos.z, zoom);
3768 
3769             int32_t cx = halfxdim16+x2;
3770             int32_t cy = midydim16+y2;
3771 
3772             videoBeginDrawing();	//{{{  LOCK_FRAME_1
3773 
3774             if ((cx >= 2 && cx <= xdim-3) && (cy >= 2 && cy <= ydim16-3))
3775             {
3776                 int16_t angofs = m32_sideview ? m32_sideang : 0;
3777                 x1 = mulscale11(sintable[(startang+angofs+2560)&2047],zoom) / 768;
3778                 y1 = mulscale11(sintable[(startang+angofs+2048)&2047],zoom) / 768;
3779                 i = scalescreeny(x1);
3780                 j = scalescreeny(y1);
3781                 drawline16base(cx,cy, x1,j, -x1,-j, editorcolors[6]);
3782                 drawline16base(cx,cy, x1,j, +y1,-i, editorcolors[6]);
3783                 drawline16base(cx,cy, x1,j, -y1,+i, editorcolors[6]);
3784             }
3785 
3786             if (keystatus[sc_LeftShift] && (pointhighlight&16384) && highlightcnt<=0 && !bloodhack)  // LShift
3787             {
3788                 // draw lines to linking sprites
3789                 const int32_t refspritenum = pointhighlight&16383;
3790                 const int32_t reftag = select_sprite_tag(refspritenum);
3791 
3792                 if (reftag != INT32_MIN)
3793                 {
3794                     for (i=0; i<numsectors; i++)
3795                         for (SPRITES_OF_SECT(i, j))
3796                             if (reftag==select_sprite_tag(j))
3797                                 drawlinebetween(&sprite[refspritenum].pos, &sprite[j].pos, editorcolors[12], 0x33333333);
3798                 }
3799             }
3800 
3801             if (showtags)
3802             {
3803                 if (zoom >= 768)
3804                 {
3805                     for (i=0; i<numsectors; i++)
3806                     {
3807                         int16_t secshort = i;
3808 
3809                         YAX_SKIPSECTOR(i);
3810 
3811                         dabuffer = CallExtGetSectorCaption(i);
3812                         if (dabuffer[0] == 0)
3813                             continue;
3814 
3815                         get_sectors_center(&secshort, 1, &dax, &day);
3816 
3817                         drawsmallabel(dabuffer, editorcolors[0], editorcolors[7], editorcolors[7] - 3, dax, day, getflorzofslope(i,dax,day));
3818                     }
3819                 }
3820 
3821                 x3 = pos.x + divscale14(-halfxdim16,zoom);
3822                 y3 = pos.y + divscale14(-(midydim16-4),zoom);
3823 //                x4 = pos.x + divscale14(halfxdim16,zoom);
3824 //                y4 = pos.y + divscale14(ydim16-(midydim16-4),zoom);
3825 
3826                 if (newnumwalls >= 0)
3827                 {
3828                     for (i=newnumwalls; i>=numwalls_bak; i--)
3829                         editwall[i>>3] |= 1<<(i&7);;
3830                 }
3831 
3832                 i = numwalls-1;
3833                 j = numsectors-1;  // might be -1 if empty map!
3834                 if (newnumwalls >= 0)
3835                     i = newnumwalls-1;
3836                 for (; i>=0; i--)
3837                 {
3838                     walltype const * const wal = &wall[i];
3839 
3840                     if (j>=0 && sector[j].wallptr > i)
3841                         j--;
3842 
3843                     if (zoom < 768 && !(editwall[i>>3]&pow2char[i&7]))
3844                         continue;
3845 
3846                     YAX_SKIPWALL(i);
3847 
3848                     //Get average point of wall
3849 //                    if ((dax > x3) && (dax < x4) && (day > y3) && (day < y4))
3850                     {
3851                         dabuffer = CallExtGetWallCaption(i);
3852                         if (dabuffer[0] == 0)
3853                             continue;
3854 
3855                         dax = (wal->x+wall[wal->point2].x)>>1;
3856                         day = (wal->y+wall[wal->point2].y)>>1;
3857                         drawsmallabel(dabuffer, editorcolors[0], editorcolors[31], editorcolors[31] - 3, dax, day, (i >= numwalls || j<0) ? 0 : getflorzofslope(j, dax,day));
3858                     }
3859                 }
3860 
3861                 if (zoom >= 768)
3862                 {
3863                     int32_t alwaysshowgray = get_alwaysshowgray();
3864 
3865                     for (i=0, k=0; (m32_sideview && k<m32_swcnt) || (!m32_sideview && i<MAXSPRITES); i++, k++)
3866                     {
3867                         if (m32_sideview)
3868                         {
3869                             i = m32_wallsprite[k];
3870                             if (i<MAXWALLS)
3871                                 continue;
3872                             i = i-MAXWALLS;
3873                         }
3874                         else
3875                             if (sprite[i].statnum == MAXSTATUS)
3876                                 continue;
3877 
3878                         if ((!m32_sideview || !alwaysshowgray) && sprite[i].sectnum >= 0)
3879                             YAX_SKIPSECTOR(sprite[i].sectnum);
3880 
3881                         drawspritelabel(i);
3882                     }
3883 
3884                     if (pointhighlight & 16384)
3885                         drawspritelabel(pointhighlight - 16384);
3886                 }
3887             }
3888 
3889             // stick this event right between begin- end enddrawing()...
3890             // also after the above label stuff so users can redefine them
3891             VM_OnEvent(EVENT_DRAW2DSCREEN, -1);
3892 
3893             printcoords16(pos.x,pos.y,ang);
3894 
3895             numwalls = numwalls_bak;
3896 
3897             if (highlightsectorcnt >= 0)
3898             {
3899                 for (i=0; i<numsectors; i++)
3900                     if (hlsectorbitmap[i>>3]&pow2char[i&7])
3901                         fillsector(i, -1);
3902             }
3903 
3904             if (keystatus[sc_LeftShift])  // LShift
3905             {
3906                 if (!m32_is2d3dmode() && (m32_sideview || highlightcnt <= 0))
3907                 {
3908                     drawlinepat = 0x00ff00ff;
3909                     editorDraw2dLine(searchx,0, searchx,ydim2d-1, editorcolors[15]);
3910                     editorDraw2dLine(0,searchy, xdim2d-1,searchy, editorcolors[15]);
3911                     drawlinepat = 0xffffffff;
3912 
3913                     _printmessage16("(%d,%d)",mousxplc,mousyplc);
3914                 }
3915                 else
3916                 {
3917                     // do interactive scaling
3918                     if (!isc.active)
3919                     {
3920                         if (pointhighlight >= 0 && (bstatus&3))
3921                         {
3922                             // initialize by finding pivot point
3923                             int32_t minx=INT32_MAX, miny=INT32_MAX;
3924                             int32_t maxx=INT32_MIN, maxy=INT32_MIN;
3925 
3926                             isc.rotatep = ((bstatus&3)==2);
3927                             bstatus &= ~3;
3928 
3929                             for (i=0; i<highlightcnt; i++)
3930                             {
3931                                 minx = min(minx, HLMEMBER(i, x));
3932                                 miny = min(miny, HLMEMBER(i, y));
3933                                 maxx = max(maxx, HLMEMBER(i, x));
3934                                 maxy = max(maxy, HLMEMBER(i, y));
3935                             }
3936 
3937                             isc.piv.x = (minx+maxx)/2;
3938                             isc.piv.y = (miny+maxy)/2;
3939 
3940                             isc.dragx = HLMEMBERX(pointhighlight, x);
3941                             isc.dragy = HLMEMBERX(pointhighlight, y);
3942 
3943                             isc.xsc = isc.ysc = 1<<16;
3944                             isc.ang = 0;
3945 
3946                             isc.active = 1;
3947                         }
3948                     }
3949                     else
3950                     {
3951                         if (bstatus&3)
3952                         {
3953                             // drag/rotate the reference point
3954                             const int32_t pivx=isc.piv.x, pivy=isc.piv.y;
3955                             const int32_t dragx=isc.dragx, dragy=isc.dragy;
3956                             int32_t mxplc=mousxplc, myplc=mousyplc, xsc=1<<16, ysc=1<<16;
3957 
3958                             const int32_t dx=dragx-pivx, dy=dragy-pivy;
3959                             int32_t mdx, mdy;
3960 
3961                             bstatus &= ~3;
3962 
3963                             draw_cross(pivx, pivy, 3, editorcolors[14]);
3964 
3965                             adjustmark(&mxplc, &myplc, numwalls);
3966                             mdx = mxplc-pivx;
3967                             mdy = myplc-pivy;
3968 
3969                             if (!isc.rotatep)
3970                             {
3971                                 if (mdx != 0 && dx != 0 && klabs(dx) >= 8)
3972                                     xsc = min(klabs(divscale16(mdx, dx)), 1<<18);
3973                                 if (mdy != 0 && dy != 0 && klabs(dy) >= 8)
3974                                     ysc = min(klabs(divscale16(mdy, dy)), 1<<18);
3975 
3976                                 if (eitherCTRL)
3977                                     xsc = ysc = max(xsc, ysc);
3978 
3979                                 isc.xsc = xsc;
3980                                 isc.ysc = ysc;
3981 
3982                                 printmessage16("scale x=%.3f y=%.3f", (double)xsc/65536, (double)ysc/65536);
3983                             }
3984                             else
3985                             {
3986                                 isc.ang = getangle(mdx, mdy) - getangle(dx, dy);
3987                                 printmessage16("rotate ang %d", isc.ang);
3988                             }
3989 
3990                             for (i=0; i<highlightcnt; i++)
3991                             {
3992                                 int32_t x=HLMEMBER(i, x), y=HLMEMBER(i, y);
3993 
3994                                 isc_transform(&x, &y);
3995 
3996                                 draw_square(x, y, 2, editorcolors[15]);
3997 
3998                                 if ((highlight[i]&16384)==0)
3999                                 {
4000                                     walltype const * const wal = &wall[highlight[i]];
4001                                     const int32_t p2=wal->point2, hlp=(show2dwall[p2>>3]&pow2char[p2&7]);
4002                                     vec3_t v1 = { x, y, 0 }, v2 = { wall[p2].x, wall[p2].y, 0 };
4003 
4004                                     isc_transform(&v2.x, &v2.y);
4005                                     if (!hlp)
4006                                     {
4007                                         v2.x = wall[p2].x;
4008                                         v2.y = wall[p2].y;
4009                                     }
4010 
4011                                     drawlinebetween(&v1, &v2, !hlp ? 8 :
4012                                                     editorcolors[wal->nextwall >= 0 ? 12 : 7],
4013                                                     0x11111111);
4014                                 }
4015                             }
4016                         }
4017                         else
4018                         {
4019                             // finish interactive scaling
4020                             isc.active = 0;
4021 
4022                             if ((!isc.rotatep && (isc.xsc!=1<<16 || isc.ysc!=1<<16)) ||
4023                                 (isc.rotatep && (isc.ang!=0)))
4024                             {
4025                                 for (i=0; i<highlightcnt; i++)
4026                                 {
4027                                     int32_t *x=&HLMEMBER(i, x), *y=&HLMEMBER(i, y);
4028 
4029                                     isc_transform(x, y);
4030 
4031                                     if (isc.rotatep && (highlight[i]&16384))
4032                                     {
4033                                         spritetype *spr = &sprite[highlight[i]&16383];
4034                                         spr->ang = (spr->ang + isc.ang)&2047;
4035                                     }
4036                                 }
4037 
4038                                 if (!isc.rotatep)
4039                                     message("Highlights scaled by x=%.3f y=%.3f",
4040                                             (double)isc.xsc/65536, (double)isc.ysc/65536);
4041                                 else
4042                                     message("Highlights rotated by %d BUILD degrees", isc.ang);
4043 
4044                                 asksave = 1;
4045                             }
4046                             else
4047                                 printmessage16(" ");
4048                         }
4049                     }
4050                 }
4051             }
4052             else
4053             {
4054                 if (isc.active)
4055                 {
4056                     isc.active = 0;
4057 
4058                     printmessage16("Aborted interactive %s.", isc.rotatep ? "rotation" : "scaling");
4059                     bstatus &= ~3;
4060                     mousewaitmask = 3;
4061                     pointhighlight = -1;
4062                 }
4063             }
4064 
4065             editorDraw2dLine(searchx,0, searchx,8, editorcolors[15]);
4066             editorDraw2dLine(0,searchy, 8,searchy, editorcolors[15]);
4067 
4068             // 2d3d mode
4069             if (m32_2d3dmode && m32_2d3d_resolutions_match())
4070             {
4071 #ifdef USE_OPENGL
4072                 int bakrendmode = rendmode;
4073 #endif
4074                 vec2_t bdim = { xdim, ydim };
4075 
4076                 xdim = xdim2d;
4077                 ydim = ydim2d;
4078 #ifdef USE_OPENGL
4079                 rendmode = REND_CLASSIC;
4080 #endif
4081 
4082                 if (m32_2d3d.x + XSIZE_2D3D > xdim2d - 4)
4083                     m32_2d3d.x = xdim2d - 4 - XSIZE_2D3D;
4084 
4085                 if (m32_2d3d.y + YSIZE_2D3D > ydim2d - 4 - STATUS2DSIZ2)
4086                     m32_2d3d.y = ydim2d - 4 - YSIZE_2D3D - STATUS2DSIZ2;
4087 
4088                 updatesectorz(pos.x, pos.y, pos.z, &cursectnum);
4089 
4090                 if (cursectnum == -1)
4091                     updatesector(pos.x, pos.y, &cursectnum);
4092 
4093                 if (cursectnum != -1)
4094                 {
4095                     int32_t cz, fz;
4096 
4097                     getzsofslope(cursectnum, pos.x, pos.y, &cz, &fz);
4098 
4099                     inpclamp(&pos.z, cz+(4<<8), fz-(4<<8));
4100 
4101                     videoEndDrawing();
4102                     videoSetViewableArea(m32_2d3d.x, m32_2d3d.y, m32_2d3d.x + XSIZE_2D3D, m32_2d3d.y + YSIZE_2D3D);
4103                     videoClearViewableArea(-1);
4104 
4105                     vec2_t osearch = { searchx, searchy };
4106 
4107                     searchx -= m32_2d3d.x;
4108                     searchy -= m32_2d3d.y;
4109 
4110                     M32_DrawRoomsAndMasks();
4111                     videoSetViewableArea(0, 0, xdim2d-1, ydim2d-1);
4112 
4113 #ifdef USE_OPENGL
4114                     rendmode = bakrendmode;
4115 #endif
4116                     xdim = bdim.x;
4117                     ydim = bdim.y;
4118                     searchx = osearch.x;
4119                     searchy = osearch.y;
4120 
4121                     videoBeginDrawing();
4122                     editorDraw2dLine(m32_2d3d.x, m32_2d3d.y, m32_2d3d.x + XSIZE_2D3D, m32_2d3d.y, editorcolors[15]);
4123                     editorDraw2dLine(m32_2d3d.x + XSIZE_2D3D, m32_2d3d.y, m32_2d3d.x + XSIZE_2D3D, m32_2d3d.y  + YSIZE_2D3D, editorcolors[15]);
4124                     editorDraw2dLine(m32_2d3d.x, m32_2d3d.y, m32_2d3d.x, m32_2d3d.y + YSIZE_2D3D, editorcolors[15]);
4125                     editorDraw2dLine(m32_2d3d.x, m32_2d3d.y + YSIZE_2D3D, m32_2d3d.x + XSIZE_2D3D, m32_2d3d.y + YSIZE_2D3D, editorcolors[15]);
4126                 }
4127             }
4128 
4129             if (!m32_is2d3dmode())
4130             {
4131                 ////// draw mouse pointer
4132 
4133                 col = editorcolors[0];
4134 
4135                 drawline16base(searchx+1, searchy+1, +0, -8, +0, -1, col);
4136                 drawline16base(searchx+1, searchy+1, +0, 1, +0, 8, col);
4137 
4138                 drawline16base(searchx+1, searchy+1, -8, 0, -1, 0, col);
4139                 drawline16base(searchx+1, searchy+1, 1, 0, 8, 0, col);
4140 
4141                 col = searchlock ? editorcolors[13] : editorcolors[15 - 3*gridlock];
4142 
4143                 if (joinsector[0] >= 0)
4144                     col = editorcolors[11];
4145 
4146                 if (numcorruptthings>0)
4147                 {
4148                     static char cbuf[64];
4149 
4150                     if ((pointhighlight&16384)==0)
4151                     {
4152                         // If aiming at wall, check whether it is corrupt, and print a
4153                         // warning message near the mouse pointer if that is the case.
4154                         for (i=0; i<numcorruptthings; i++)
4155                             if ((corruptthings[i]&CORRUPT_MASK)==CORRUPT_WALL &&
4156                                 (corruptthings[i]&(MAXWALLS-1))==pointhighlight)
4157                             {
4158                                 col = editorcolors[13];
4159                                 printext16(searchx+6, searchy-6-8, editorcolors[13], editorcolors[0], "corrupt wall", 0);
4160                                 break;
4161                             }
4162                     }
4163 
4164                     Bsprintf(cbuf, "Map corrupt (level %d): %s%d errors", corruptlevel,
4165                         numcorruptthings>=MAXCORRUPTTHINGS ? ">=" : "", numcorruptthings);
4166                     printext16(8, 8, editorcolors[13]+(M32_THROB>>2), editorcolors[0], cbuf, 0);
4167                 }
4168 
4169                 if (highlightsectorcnt==0 || highlightcnt==0)
4170                 {
4171                     if (keystatus[sc_SemiColon] || keystatus[sc_Quote])  // ' and ;
4172                     {
4173                         col = editorcolors[14];
4174 
4175                         drawline16base(searchx+16, searchy-16, -4, 0, +4, 0, col);
4176                         if (keystatus[sc_Quote])
4177                             drawline16base(searchx+16, searchy-16, 0, -4, 0, +4, col);
4178                     }
4179 
4180                     if (highlightsectorcnt == 0)
4181                         if (keystatus[sc_RightShift])
4182                             printext16(searchx+6, searchy-2+8, editorcolors[12], -1, "ALL", 0);
4183 
4184                     if (highlightcnt == 0)
4185                     {
4186                         if (eitherCTRL && (highlight1.x!=highlight2.x || highlight1.y!=highlight2.y))
4187                             printext16(searchx+6, searchy-6-8, editorcolors[12], -1, "SPR ONLY", 0);
4188 #ifdef YAX_ENABLE
4189                         if (keystatus[sc_End])  // End
4190                             printext16(searchx+6, searchy-2+8, editorcolors[12], -1, "ALL", 0);
4191 #endif
4192                     }
4193                 }
4194 
4195                 drawline16base(searchx, searchy, +0, -8, +0, -1, col);
4196                 drawline16base(searchx, searchy, +0, 1, +0, 8, col);
4197 
4198                 drawline16base(searchx, searchy, -8, 0, -1, 0, col);
4199                 drawline16base(searchx, searchy, 1, 0, 8, 0, col);
4200 
4201                 ////// Draw the white pixel closest to mouse cursor on linehighlight
4202                 if (linehighlight>=0)
4203                 {
4204                     char col = wall[linehighlight].nextsector >= 0 ? editorcolors[15] : editorcolors[5];
4205 
4206                     if (m32_sideview)
4207                     {
4208                         getclosestpointonwall(searchx, searchy, linehighlight, &dax, &day, 1);
4209                         drawline16base(dax, day, 0, 0, 0, 0, col);
4210                     }
4211                     else
4212                     {
4213                         getclosestpointonwall(mousxplc, mousyplc, linehighlight, &dax, &day, 0);
4214                         ovhscrcoords(dax, day, &x2, &y2);
4215                         drawline16base(x2, y2, 0, 0, 0, 0, col);
4216                     }
4217                 }
4218             }
4219 
4220             videoEndDrawing();	//}}} LOCK_FRAME_1
4221 
4222             OSD_Draw();
4223         }
4224 
4225         inputchecked = 1;
4226 
4227 
4228         VM_OnEvent(EVENT_PREKEYS2D, -1);
4229         CallExtCheckKeys(); // TX 20050101, it makes more sense to have this here so keys can be overwritten with new functions in bstub.c
4230 
4231         // 2d3d mode
4232         if (m32_is2d3dmode())
4233             goto nokeys;
4234 
4235         // Flip/mirror sector Ed Coolidge
4236         if (keystatus[sc_X] || keystatus[sc_Y])  // X or Y (2D)
4237         {
4238             int32_t about_x=keystatus[sc_X];
4239             int32_t doMirror = eitherALT;  // mirror walls and wall/floor sprites
4240 
4241 #ifdef YAX_ENABLE
4242             if (highlightsectorcnt > 0 && !hl_all_bunch_sectors_p())
4243             {
4244                 printmessage16("To flip extended sectors, all sectors of a bunch must be selected");
4245                 keystatus[sc_X] = keystatus[sc_Y] = 0;
4246             }
4247             else
4248 #endif
4249             if (highlightsectorcnt > 0)
4250             {
4251                 int16_t *const otonwall = onextwall;  // OK, since we make old-nextwalls invalid
4252 
4253                 mkonwinvalid();
4254 
4255                 keystatus[sc_X] = keystatus[sc_Y] = 0;
4256 
4257                 for (j=0; j<numwalls; j++)
4258                     otonwall[j] = j;
4259 
4260                 get_sectors_center(highlightsector, highlightsectorcnt, &dax, &day);
4261 
4262                 if (gridlock && grid > 0)
4263                     locktogrid(&dax, &day);
4264 
4265                 for (i=0; i<highlightsectorcnt; i++)
4266                 {
4267                     int32_t startofloop, endofloop;
4268                     int32_t numtoswap = -1;
4269                     int32_t w=0;
4270                     uwalltype tempwall;
4271 
4272                     startofloop = startwall = sector[highlightsector[i]].wallptr;
4273                     endofloop = endwall = startwall+sector[highlightsector[i]].wallnum-1;
4274 #if 0
4275                     if (doMirror)
4276                     {
4277                         //mirror sector textures
4278                         sector[highlightsector[i]].ceilingstat ^= 0x10;
4279                         sector[highlightsector[i]].floorstat ^= 0x10;
4280                     }
4281 #endif
4282                     //save position of wall at start of loop
4283                     x3 = wall[startofloop].x;
4284                     y3 = wall[startofloop].y;
4285 
4286                     for (j=startwall; j<=endwall; j++)
4287                     {
4288                         //fix position of walls
4289                         if (about_x)
4290                         {
4291                             wall[j].x = dax-POINT2(j).x+dax; //flip wall.x about dax
4292                             wall[j].y = POINT2(j).y;
4293                         }
4294                         else
4295                         {
4296                             wall[j].x = POINT2(j).x;
4297                             wall[j].y = day-POINT2(j).y+day; //flip wall.y about day
4298                         }
4299 
4300                         if (doMirror)
4301                             wall[j].cstat ^= 8;  //mirror walls about dax/day
4302 
4303                         if (wall[j].point2==startofloop) //check if j is end of loop
4304                         {
4305                             endofloop = j;
4306                             if (about_x)
4307                             {
4308                                 wall[endofloop].x = dax-x3+dax; //flip wall.x about dax
4309                                 wall[endofloop].y = y3;
4310                             }
4311                             else
4312                             {
4313                                 wall[endofloop].x = x3;
4314                                 wall[endofloop].y = day-y3+day; //flip wall.y about dax
4315                             }
4316 
4317                             //correct order of walls in loop to maintain player space (right-hand rule)
4318                             numtoswap = (endofloop-startofloop)>>1;
4319                             for (w=1; w<=numtoswap; w++)
4320                             {
4321                                 Bmemcpy(&tempwall, &wall[startofloop+w], sizeof(walltype));
4322                                 Bmemcpy(&wall[startofloop+w], &wall[endofloop-w+1], sizeof(walltype));
4323                                 Bmemcpy(&wall[endofloop-w+1], &tempwall, sizeof(walltype));
4324 
4325                                 otonwall[startofloop+w] = endofloop-w+1;
4326                                 otonwall[endofloop-w+1] = startofloop+w;
4327                             }
4328 
4329                             //make point2 point to next wall in loop
4330                             for (w=startofloop; w<endofloop; w++)
4331                                 wall[w].point2 = w+1;
4332                             wall[endofloop].point2 = startofloop;
4333 
4334                             startofloop = endofloop+1; //set first wall of next loop
4335                             //save position of wall at start of loop
4336                             x3 = wall[startofloop].x;
4337                             y3 = wall[startofloop].y;
4338                         }
4339                     }
4340 
4341                     j = headspritesect[highlightsector[i]];
4342                     while (j != -1)
4343                     {
4344                         if (about_x)
4345                         {
4346                             x3 = sprite[j].x;
4347                             sprite[j].x = dax-x3+dax; //flip sprite.x about dax
4348                             sprite[j].ang = (1024+2048-sprite[j].ang)&2047; //flip ang about 512
4349                         }
4350                         else
4351                         {
4352                             y3 = sprite[j].y;
4353                             sprite[j].y = day-y3+day; //flip sprite.y about day
4354                             sprite[j].ang = (2048-sprite[j].ang)&2047; //flip ang about 512
4355                         }
4356 
4357                         if (doMirror && (sprite[j].cstat & 0x30))
4358                             sprite[j].cstat ^= 4;  // mirror sprites about dax/day (don't mirror monsters)
4359 
4360                         j = nextspritesect[j];
4361                     }
4362                 }
4363 
4364                 // finally, construct the nextwalls and yax-nextwalls
4365                 // for the new arrangement!
4366                 for (i=0; i<highlightsectorcnt; i++)
4367                 {
4368                     for (WALLS_OF_SECTOR(highlightsector[i], j))
4369                     {
4370                         if (wall[j].nextwall >= 0)
4371                             wall[j].nextwall = otonwall[wall[j].nextwall];
4372 #ifdef YAX_ENABLE
4373                         {
4374                             int32_t cf, ynw;
4375                             for (cf=0; cf<2; cf++)
4376                                 if ((ynw = yax_getnextwall(j, cf)) >= 0)
4377                                     yax_setnextwall(j, cf, otonwall[ynw]);
4378                         }
4379 #endif
4380                     }
4381                 }
4382 
4383                 printmessage16("Selected sector(s) flipped");
4384                 asksave = 1;
4385             }
4386         }
4387         // end edit for sector flip
4388 
4389         if (keystatus[88])   //F12
4390         {
4391             keystatus[88] = 0;
4392 //__clearscreen_beforecapture__
4393             videoCaptureScreen("captxxxx.tga", eitherSHIFT);
4394 
4395             videoShowFrame(1);
4396         }
4397         if (keystatus[sc_B])  // B (clip Blocking xor) (2D)
4398         {
4399             pointhighlight = getpointhighlight(mousxplc, mousyplc, pointhighlight);
4400             linehighlight = getlinehighlight(mousxplc, mousyplc, linehighlight, 0);
4401 
4402             if ((pointhighlight&0xc000) == 16384)
4403             {
4404                 sprite[pointhighlight&16383].cstat ^= 1;
4405                 sprite[pointhighlight&16383].cstat &= ~256;
4406                 sprite[pointhighlight&16383].cstat |= ((sprite[pointhighlight&16383].cstat&1)<<8);
4407                 asksave = 1;
4408             }
4409             else if (linehighlight >= 0)
4410             {
4411                 wall[linehighlight].cstat ^= 1;
4412                 wall[linehighlight].cstat &= ~64;
4413                 if ((wall[linehighlight].nextwall >= 0) && !eitherSHIFT)
4414                 {
4415                     NEXTWALL(linehighlight).cstat &= ~(1+64);
4416                     NEXTWALL(linehighlight).cstat |= (wall[linehighlight].cstat&1);
4417                 }
4418                 asksave = 1;
4419             }
4420             keystatus[sc_B] = 0;
4421         }
4422         if (keystatus[sc_F])  //F (F alone does nothing in 2D right now)
4423         {
4424             keystatus[sc_F] = 0;
4425             if (eitherALT)  //ALT-F (relative alignmment flip)
4426             {
4427                 linehighlight = getlinehighlight(mousxplc, mousyplc, linehighlight, 0);
4428                 if (linehighlight >= 0)
4429                     SetFirstWall(sectorofwall(linehighlight), linehighlight, 1);
4430             }
4431         }
4432 
4433         if (keystatus[sc_O])  // O (ornament onto wall) (2D)
4434         {
4435             keystatus[sc_O] = 0;
4436             if ((pointhighlight&0xc000) == 16384)
4437             {
4438                 asksave = 1;
4439                 DoSpriteOrnament(pointhighlight&16383);
4440             }
4441         }
4442 
4443 
4444         tsign = 0;
4445         if (keystatus[sc_Comma] || (bstatus&33)==33)  // , (2D)
4446             tsign = +1;
4447         if (keystatus[sc_Period] || (bstatus&17)==17)  // . (2D)
4448             tsign = -1;
4449 
4450         if (tsign)
4451         {
4452 #ifdef YAX_ENABLE
4453             if (highlightsectorcnt > 0 && !hl_all_bunch_sectors_p())
4454             {
4455                 printmessage16("To rotate ext. sectors, all sectors of a bunch must be selected");
4456             }
4457             else
4458 #endif
4459             if (highlightsectorcnt > 0)
4460             {
4461                 int32_t smoothRotation = eitherSHIFT, manualAngle = eitherALT;
4462                 vec2_t da = { dax, day };
4463 
4464                 if (manualAngle)
4465                 {
4466                     tsign = getnumber16("Rotation BUILD angle: ", 0, 2047, 1);
4467                     if (tsign==0)
4468                     {
4469                         printmessage16(" ");
4470                         goto rotate_hlsect_out;
4471                     }
4472 
4473                     printmessage16("Rotated highlighted sectors by %d BUILD degrees", tsign);
4474                     tsign &= 2047;
4475                     smoothRotation = 1;
4476                 }
4477 
4478                 get_sectors_center(highlightsector, highlightsectorcnt, &da.x, &da.y);
4479 
4480                 if (!smoothRotation)
4481                 {
4482                     if (gridlock && grid > 0)
4483                         locktogrid(&da.x, &da.y);
4484 
4485                     tsign *= 512;
4486                 }
4487 
4488 
4489                 for (i=0; i<highlightsectorcnt; i++)
4490                 {
4491                     for (WALLS_OF_SECTOR(highlightsector[i], j))
4492                         rotatepoint(da, wall[j].pos, tsign&2047, &wall[j].pos);
4493 
4494                     for (j=headspritesect[highlightsector[i]]; j != -1; j=nextspritesect[j])
4495                     {
4496                         rotatepoint(da, sprite[j].pos.vec2, tsign&2047, &sprite[j].pos.vec2);
4497                         sprite[j].ang = (sprite[j].ang+tsign)&2047;
4498                     }
4499                 }
4500 
4501                 m32_rotateang += tsign;
4502                 m32_rotateang &= 2047;
4503                 asksave = 1;
4504 rotate_hlsect_out:
4505                 if (!smoothRotation || manualAngle)
4506                     keystatus[sc_Comma] = keystatus[sc_Period] = 0;
4507 
4508                 g_mouseBits &= ~(16|32);
4509                 bstatus &= ~(16|32);
4510             }
4511             else
4512             {
4513                 if (pointhighlight >= 16384)
4514                 {
4515                     i = pointhighlight-16384;
4516                     if (eitherSHIFT)
4517                         sprite[i].ang = (sprite[i].ang-tsign)&2047;
4518                     else
4519                     {
4520                         sprite[i].ang = (sprite[i].ang-128*tsign)&2047;
4521                         keystatus[sc_Comma] = keystatus[sc_Period] = 0;
4522                     }
4523 
4524                     g_mouseBits &= ~(16|32);
4525                     bstatus &= ~(16|32);
4526                 }
4527             }
4528         }
4529 
4530         if (keystatus[sc_ScrollLock])  //Scroll lock (set starting position)
4531         {
4532             startpos = pos;
4533             startang = ang;
4534             startsectnum = cursectnum;
4535             keystatus[sc_ScrollLock] = 0;
4536             asksave = 1;
4537 
4538             printmessage16("Set starting position");
4539         }
4540 #if 1
4541         if (keystatus[sc_F5])  //F5
4542         {
4543             int found = 0;
4544             if (bloodhack)
4545             {
4546                 for (i=0; i<numsectors; i++)
4547                     if (inside_editor_curpos(i) == 1)
4548                     {
4549                         YAX_SKIPSECTOR(i);
4550 
4551                         CallExtShowSectorData(i);
4552                         found = 1;
4553                         break;
4554                     }
4555             }
4556             if (!found)
4557                 CallExtShowSectorData(-1);
4558         }
4559         if (keystatus[sc_F6])  //F6
4560         {
4561             if (pointhighlight >= 16384)
4562                 CallExtShowSpriteData(pointhighlight-16384);
4563             else if (linehighlight >= 0)
4564                 CallExtShowWallData(linehighlight);
4565             else
4566                 CallExtShowWallData(-1);
4567         }
4568         if (keystatus[sc_F7])  //F7
4569         {
4570             keystatus[sc_F7] = 0;
4571 
4572             for (i=0; i<numsectors; i++)
4573                 if (inside_editor_curpos(i) == 1)
4574                 {
4575                     YAX_SKIPSECTOR(i);
4576 
4577                     CallExtEditSectorData(i);
4578                     break;
4579                 }
4580         }
4581         if (keystatus[sc_F8])  //F8
4582         {
4583             keystatus[sc_F8] = 0;
4584 
4585             if (pointhighlight >= 16384)
4586                 CallExtEditSpriteData(pointhighlight-16384);
4587             else if (linehighlight >= 0)
4588                 CallExtEditWallData(linehighlight);
4589         }
4590 #endif
4591 
4592         if (keystatus[sc_H])  //H (Hi 16 bits of tag)
4593         {
4594             keystatus[sc_H] = 0;
4595             if (eitherCTRL)  //Ctrl-H
4596             {
4597                 pointhighlight = getpointhighlight(mousxplc, mousyplc, pointhighlight);
4598                 linehighlight = getlinehighlight(mousxplc, mousyplc, linehighlight, 0);
4599 
4600                 if ((pointhighlight&0xc000) == 16384)
4601                 {
4602                     sprite[pointhighlight&16383].cstat ^= 256;
4603                     asksave = 1;
4604                 }
4605                 else if (linehighlight >= 0)
4606                 {
4607                     wall[linehighlight].cstat ^= 64;
4608                     if ((wall[linehighlight].nextwall >= 0) && !eitherSHIFT)
4609                     {
4610                         NEXTWALL(linehighlight).cstat &= ~64;
4611                         NEXTWALL(linehighlight).cstat |= (wall[linehighlight].cstat&64);
4612                     }
4613                     asksave = 1;
4614                 }
4615             }
4616             else if (eitherALT)  //ALT
4617             {
4618                 if (pointhighlight >= 16384)
4619                 {
4620                     i = pointhighlight-16384;
4621                     j = taglab_linktags(1, i);
4622                     j = 2*(j&2);
4623                     Bsprintf(buffer, "Sprite (%d) Hi-tag: ", i);
4624                     sprite[i].hitag = getnumber16(buffer, sprite[i].hitag, BTAG_MAX, 0+j);
4625                 }
4626                 else if (linehighlight >= 0)
4627                 {
4628                     i = linehighlight;
4629                     j = taglab_linktags(1, i);
4630                     j = 2*(j&2);
4631                     Bsprintf(buffer, "Wall (%d) Hi-tag: ", i);
4632                     wall[i].hitag = getnumber16(buffer, wall[i].hitag, BTAG_MAX, 0+j);
4633                 }
4634             }
4635             else
4636             {
4637                 for (i=0; i<numsectors; i++)
4638                     if (inside_editor_curpos(i) == 1)
4639                     {
4640                         YAX_SKIPSECTOR(i);
4641 
4642                         Bsprintf(buffer, "Sector (%d) Hi-tag: ", i);
4643                         sector[i].hitag = getnumber16(buffer, sector[i].hitag, BTAG_MAX, 0);
4644                         break;
4645                     }
4646             }
4647             // printmessage16("");
4648         }
4649         if (keystatus[sc_P])  // P (palookup #)
4650         {
4651             keystatus[sc_P] = 0;
4652 
4653             for (i=0; i<numsectors; i++)
4654                 if (inside_editor_curpos(i) == 1)
4655                 {
4656                     YAX_SKIPSECTOR(i);
4657 
4658                     Bsprintf(buffer, "Sector (%d) Ceilingpal: ", i);
4659                     sector[i].ceilingpal = getnumber16(buffer, sector[i].ceilingpal, M32_MAXPALOOKUPS, 0);
4660 
4661                     Bsprintf(buffer, "Sector (%d) Floorpal: ", i);
4662                     sector[i].floorpal = getnumber16(buffer, sector[i].floorpal, M32_MAXPALOOKUPS, 0);
4663                     break;
4664                 }
4665         }
4666         if (keystatus[sc_E])  // E (status list)
4667         {
4668             keystatus[sc_E] = 0;
4669 
4670             if (!eitherCTRL)
4671             {
4672                 if (pointhighlight >= 16384)
4673                 {
4674                     i = pointhighlight-16384;
4675                     Bsprintf(buffer, "Sprite (%d) Status list: ", i);
4676                     changespritestat(i, getnumber16(buffer, sprite[i].statnum, MAXSTATUS-1, 0));
4677                 }
4678             }
4679 #ifdef YAX_ENABLE
4680             else if (highlightsectorcnt > 0 && newnumwalls < 0)
4681             {
4682                 ////////// YAX //////////
4683                 static const char *cfs[2] = {"ceiling", "floor"};
4684 
4685                 int32_t cf, thez, ulz[2] = {0,0};
4686                 int16_t bn, sandwichbunch=-1;
4687 
4688                 if (numyaxbunches==YAX_MAXBUNCHES)
4689                 {
4690                     message("Bunch limit of %d reached, cannot extend", YAX_MAXBUNCHES);
4691                     goto end_yax;
4692                 }
4693 
4694                 if (highlighted_sectors_components(0,0) != 1)
4695                 {
4696                     message("Sectors to extend must be in one connected component");
4697                     goto end_yax;
4698                 }
4699 
4700                 cf = ask_above_or_below();
4701                 if (cf==-1)
4702                     goto end_yax;
4703 
4704                 thez = SECTORFLD(highlightsector[0],z, cf);
4705                 for (i=0; i<highlightsectorcnt; i++)
4706                 {
4707                     bn = yax_getbunch(highlightsector[i], cf);
4708 
4709                     if (sandwichbunch >= 0 && bn!=sandwichbunch)
4710                     {
4711                         message("When sandwiching extension, must select only sectors of one bunch");
4712                         goto end_yax;
4713                     }
4714 
4715                     if (bn >= 0)
4716                     {
4717                         if (cf==YAX_FLOOR)
4718                         {
4719                             if (sandwichbunch < 0 && i!=0)
4720                             {
4721                                 message("When sandwiching extension, must select only sectors of the bunch");
4722                                 goto end_yax;
4723                             }
4724                             sandwichbunch = bn;
4725                         }
4726                         else
4727                         {
4728                             message("Sector %d's %s is already extended", highlightsector[i], cfs[cf]);
4729                             goto end_yax;
4730                         }
4731                     }
4732 
4733                     if (SECTORFLD(highlightsector[i],z, cf) != thez)
4734                     {
4735                         message("Sector %d's %s height doesn't match sector %d's",
4736                                 highlightsector[i], cfs[cf], highlightsector[0]);
4737                         goto end_yax;
4738                     }
4739 
4740                     if ((sandwichbunch>=0 || highlightsectorcnt>1) && SECTORFLD(highlightsector[i],stat, cf)&2)
4741                     {
4742                         message("Sector %ss must not be sloped%s", cfs[cf],
4743                                 sandwichbunch>=0 ? "" : "if extending more than one");
4744                         goto end_yax;
4745                     }
4746                 }
4747 
4748                 if (sandwichbunch >= 0)
4749                 {
4750                     // cf==YAX_FLOOR here
4751 
4752                     int32_t tempz, oldfz, swsecheight = DEFAULT_YAX_HEIGHT/4;
4753                     // highest floor z of lower sectors, lowest ceiling z of these sectors
4754                     int32_t minfloorz = INT32_MAX, maxceilz = INT32_MIN;
4755 
4756                     // some preparation for making the sandwich
4757                     if (highlightsectorcnt != yax_numsectsinbunch(sandwichbunch, YAX_FLOOR))
4758                     {
4759                         message("When sandwiching extension, must select all sectors of the bunch");
4760                         goto end_yax;
4761                     }
4762 
4763                     // "for i in sectors of sandwichbunch(floor)" is now the same as
4764                     // "for i in highlighted sectors"
4765 
4766                     oldfz = sector[highlightsector[0]].floorz;
4767 
4768                     // check if enough room in z
4769                     for (SECTORS_OF_BUNCH(sandwichbunch, YAX_CEILING, i))
4770                         for (WALLS_OF_SECTOR(i, j))
4771                         {
4772                             tempz = getflorzofslope(i, wall[j].x, wall[j].y);
4773                             minfloorz = min(minfloorz, tempz);
4774                         }
4775                     for (SECTORS_OF_BUNCH(sandwichbunch, YAX_FLOOR, i))
4776                         for (WALLS_OF_SECTOR(i, j))
4777                         {
4778                             tempz = getceilzofslope(i, wall[j].x, wall[j].y);
4779                             maxceilz = max(maxceilz, tempz);
4780                         }
4781 
4782                     if (minfloorz - maxceilz < 2*swsecheight)
4783                     {
4784                         message("Too little z headroom for sandwiching, need at least %d",
4785                                 2*swsecheight);
4786                         goto end_yax;
4787                     }
4788 
4789                     if (maxceilz >= oldfz || oldfz >= minfloorz)
4790                     {
4791                         message("Internal error while sandwiching: oldfz out of bounds");
4792                         goto end_yax;
4793                     }
4794 
4795                     // maxceilz   ---|
4796                     //   ^           |
4797                     // ulz[0]        ^
4798                     //   ^          oldfz
4799                     // ulz[1]        ^
4800                     //   ^           |
4801                     // minfloorz  ---|
4802 
4803                     ulz[0] = (int32_t)(oldfz - swsecheight*((double)(oldfz-maxceilz)/(minfloorz-maxceilz)));
4804                     ulz[0] &= ~255;
4805                     ulz[1] = ulz[0] + swsecheight;
4806 
4807                     if (maxceilz >= ulz[0] || ulz[1] >= minfloorz)
4808                     {
4809                         message("Too little z headroom for sandwiching");
4810                         goto end_yax;
4811                     }
4812                 }
4813 
4814                 m = numwalls;
4815                 Bmemset(visited, 0, sizeof(visited));
4816                 // construct!
4817                 for (i=0; i<highlightsectorcnt; i++)
4818                     for (WALLS_OF_SECTOR(highlightsector[i], j))
4819                     {
4820                         k = trace_loop(j, visited, NULL, NULL, !cf);
4821                         if (k == 0)
4822                             continue;
4823                         else if (k < 0)
4824                         {
4825                             numwalls = m;
4826                             goto end_yax;
4827                         }
4828 //message("loop");
4829                         wall[k-1].point2 = numwalls;
4830                         numwalls = k;
4831                     }
4832 
4833                 for (i=m; i<numwalls; i++)  // try
4834                 {
4835                     j = YAX_NEXTWALL(i, !cf);
4836                     if (j < 0)
4837                     {
4838                         message("Internal error while constructing sector: "
4839                                 "YAX_NEXTWALL(%d, %d)<0!", i, !cf);
4840                         numwalls = m;
4841                         goto end_yax;
4842                     }
4843                     if (sandwichbunch >= 0)
4844                     {
4845                         if (YAX_NEXTWALL(j, cf) < 0)
4846                         {
4847                             message("Internal error while sandwiching (2): "
4848                                     "YAX_NEXTWALL(%d, %d)<0!", j, cf);
4849                             numwalls = m;
4850                             goto end_yax;
4851                         }
4852                     }
4853                 }
4854                 for (i=m; i<numwalls; i++)  // do!
4855                 {
4856                     j = YAX_NEXTWALL(i, !cf);
4857 
4858                     if (sandwichbunch >= 0)
4859                     {
4860                         int16_t oynw = YAX_NEXTWALL(j, cf);
4861                         yax_setnextwall(j, cf, i);
4862                         yax_setnextwall(i, cf, oynw);
4863                         yax_setnextwall(oynw, !cf, i);
4864                     }
4865                     else
4866                     {
4867                         yax_setnextwall(j, cf, i);
4868                     }
4869                 }
4870 
4871                 // create new sector based on first highlighted one
4872                 i = highlightsector[0];
4873                 Bmemcpy(&sector[numsectors], &sector[i], sizeof(sectortype));
4874                 sector[numsectors].wallptr = m;
4875                 sector[numsectors].wallnum = numwalls-m;
4876 
4877                 if (sandwichbunch < 0)
4878                 {
4879                     if (SECTORFLD(i,stat, cf)&2)
4880                         setslope(numsectors, !cf, SECTORFLD(i,heinum, cf));
4881                     else
4882                         setslope(numsectors, !cf, 0);
4883                     setslope(numsectors, cf, 0);
4884 
4885                     SECTORFLD(numsectors,z, !cf) = SECTORFLD(i,z, cf);
4886                     SECTORFLD(numsectors,z, cf) = SECTORFLD(i,z, cf) - (1-2*cf)*DEFAULT_YAX_HEIGHT;
4887                 }
4888                 else
4889                 {
4890                     for (SECTORS_OF_BUNCH(sandwichbunch, cf, i))
4891                         sector[i].floorz = ulz[0];
4892                     sector[numsectors].ceilingz = ulz[0];
4893                     sector[numsectors].floorz = ulz[1];
4894                     for (SECTORS_OF_BUNCH(sandwichbunch, !cf, i))
4895                         sector[i].ceilingz = ulz[1];
4896                 }
4897 
4898                 newnumwalls = numwalls;
4899                 numwalls = m;
4900 
4901                 SECTORFLD(numsectors,stat, !cf) &= ~1;  // no plax
4902 
4903                 // restore red walls of the selected sectors
4904                 for (i=0; i<highlightsectorcnt; i++)
4905                 {
4906                     SECTORFLD(highlightsector[i],stat, cf) &= ~1;  // no plax
4907 
4908                     for (WALLS_OF_SECTOR(highlightsector[i], j))
4909                         if (wall[j].nextwall < 0)
4910                             checksectorpointer(j, highlightsector[i]);
4911                 }
4912 
4913                 // link
4914                 if (sandwichbunch < 0)
4915                 {
4916                     yax_setbunch(numsectors, !cf, numyaxbunches);
4917                     for (i=0; i<highlightsectorcnt; i++)
4918                         yax_setbunch(highlightsector[i], cf, numyaxbunches);
4919                 }
4920                 else
4921                 {
4922                     yax_setbunch(numsectors, !cf, sandwichbunch);
4923                     // also relink
4924                     yax_setbunch(numsectors, cf, numyaxbunches);
4925                     for (SECTORS_OF_BUNCH(sandwichbunch, !cf, i))
4926                         yax_setbunch(i, !cf, numyaxbunches);
4927                 }
4928 
4929                 numwalls = newnumwalls;
4930                 newnumwalls = -1;
4931 
4932                 numsectors++;
4933                 yax_update(0);
4934                 yax_updategrays(pos.z);
4935 
4936                 reset_highlightsector();
4937 
4938                 if (sandwichbunch < 0)
4939                     message("Extended %ss of highlighted sectors, creating bunch %d",
4940                             cfs[cf], numyaxbunches-1);
4941                 else
4942                     message("Sandwiched bunch %d, creating bunch %d",
4943                             sandwichbunch, numyaxbunches-1);
4944                 asksave = 1;
4945             }
4946             else if (highlightcnt > 0)
4947             {
4948                 /// 'punch' wall loop through extension
4949 
4950                 int32_t loopstartwall = -1, numloopwalls, cf;
4951                 int32_t srcsect, dstsect, ofirstwallofs;
4952                 int16_t cb, fb, bunchnum;
4953 
4954                 if (EDITING_MAP_P())
4955                 {
4956                     printmessage16("Must not be editing map to punch loop");
4957                     goto end_yax;
4958                 }
4959 
4960                 if (numyaxbunches >= YAX_MAXBUNCHES)
4961                 {
4962                     message("TROR bunch limit reached, cannot punch loop");
4963                     goto end_yax;
4964                 }
4965 
4966                 // determine start wall
4967                 for (i=0; i<highlightcnt; i++)
4968                 {
4969                     j = highlight[i];
4970 
4971                     if (j&16384)
4972                         continue;
4973 
4974                     // we only want loop-starting walls
4975                     if (j>0 && wall[j-1].point2==j)
4976                         continue;
4977 
4978                     if (clockdir(j)==CLOCKDIR_CCW)
4979                     {
4980                         YAX_SKIPWALL(j);
4981 
4982                         if (loopstartwall >= 0)
4983                         {
4984                             message("Must have a unique highlighted CCW loop to punch");
4985                             goto end_yax;
4986                         }
4987 
4988                         loopstartwall = j;
4989                     }
4990                 }
4991 
4992                 if (loopstartwall == -1)
4993                 {
4994                     message("Didn't find any non-grayed out CCW loop start walls");
4995                     goto end_yax;
4996                 }
4997 
4998                 // determine sector
4999                 srcsect = sectorofwall(loopstartwall);
5000                 yax_getbunches(srcsect, &cb, &fb);
5001                 if (cb < 0 && fb < 0)
5002                 {
5003                     message("Ceiling or floor must be extended to punch loop");
5004                     goto end_yax;
5005                 }
5006 
5007                 /// determine c/f
5008                 cf = -1;
5009                 if (fb < 0)
5010                     cf = YAX_CEILING;
5011                 else if (cb < 0)
5012                     cf = YAX_FLOOR;
5013 
5014                 fade_editor_screen(-1);
5015 
5016                 // query top/bottom
5017                 if (cf == -1)
5018                 {
5019                     char dachars[2] = {'a', 'z'};
5020                     cf = editor_ask_function("Punch loop above (a) or below (z)?", dachars, 2);
5021                     if (cf == -1)
5022                         goto end_yax;
5023                 }
5024                 else
5025                 {
5026                     // ask even if only one choice -- I find it more
5027                     // consistent with 'extend sector' this way
5028                     if (-1 == editor_ask_function(cf==YAX_CEILING ? "Punch loop above (a)?" :
5029                                                   "Punch loop below (z)?", cf==YAX_CEILING?"a":"z", 1))
5030                         goto end_yax;
5031                 }
5032 
5033                 bunchnum = (cf==YAX_CEILING) ? cb : fb;
5034 
5035                 // check 1
5036                 j = loopstartwall;  // will be real start wall of loop
5037                 numloopwalls = 1;  // will be number of walls in loop
5038                 for (i=wall[loopstartwall].point2; i!=loopstartwall; i=wall[i].point2)
5039                 {
5040                     numloopwalls++;
5041                     if (i < j)
5042                         j = i;
5043 
5044                     if ((show2dwall[i>>3]&pow2char[i&7])==0)
5045                     {
5046                         message("All loop points must be highlighted to punch");
5047                         goto end_yax;
5048                     }
5049 
5050                     if (yax_getnextwall(loopstartwall, cf) >= 0 || yax_getnextwall(i, cf) >= 0)
5051                     {
5052                         // somewhat redundant, since it would also be caught by check 2
5053                         message("Loop walls must not already have TROR neighbors");
5054                         goto end_yax;
5055                     }
5056 
5057                     if (wall[loopstartwall].nextwall < 0 || wall[i].nextwall < 0)
5058                     {
5059                         message("INTERNAL ERROR: All loop walls are expected to be red");
5060                         goto end_yax;
5061                     }
5062                 }
5063                 loopstartwall = j;
5064 
5065                 if (numwalls + 2*numloopwalls > MAXWALLS || numsectors+1 > MAXSECTORS)
5066                 {
5067                     message("Punching loop through extension would exceed limits");
5068                     goto end_yax;
5069                 }
5070 
5071                 // get other-side sector, j==loopstartwall
5072                 dstsect = yax_getneighborsect(wall[j].x, wall[j].y, srcsect, cf);
5073                 if (dstsect < 0)
5074                 {
5075                     message("Punch loop INTERNAL ERROR: dstsect < 0. Map corrupt?");
5076                     goto end_yax;
5077                 }
5078 
5079                 // check 2
5080                 i = loopstartwall;
5081                 do
5082                 {
5083                     j = wall[i].point2;
5084 
5085                     for (WALLS_OF_SECTOR(dstsect, k))
5086                     {
5087                         vec2_t pint;
5088                         if (lineintersect2v(&wall[i].pos, &wall[j].pos, &wall[k].pos, &POINT2(k).pos, &pint))
5089                         {
5090                             message("Loop lines must not intersect any destination sector's walls");
5091                             goto end_yax;
5092                         }
5093                     }
5094                 }
5095                 while ((i = j) != loopstartwall);
5096 
5097                 // construct new loop and (dummy yet) sector
5098                 Bmemcpy(&wall[numwalls], &wall[loopstartwall], numloopwalls*sizeof(walltype));
5099                 newnumwalls = numwalls+numloopwalls;
5100 
5101                 for (i=numwalls; i<newnumwalls; i++)
5102                 {
5103                     wall[i].point2 += (numwalls - loopstartwall);
5104                     wall[i].nextsector = wall[i].nextwall = -1;
5105                 }
5106 
5107                 sector[numsectors].wallptr = numwalls;
5108                 sector[numsectors].wallnum = numloopwalls;
5109                 numsectors++; // temp
5110 
5111                 // check 3
5112                 for (SECTORS_OF_BUNCH(bunchnum, !cf, i))
5113                     for (WALLS_OF_SECTOR(i, j))
5114                     {
5115                         if (inside(wall[j].x, wall[j].y, numsectors-1)==1)
5116                         {
5117                             numsectors--;
5118                             newnumwalls = -1;
5119                             message("A point of bunch %d's sectors lies inside the loop to punch",
5120                                     bunchnum);
5121                             goto end_yax;
5122                         }
5123                     }
5124 
5125                 numsectors--;
5126 
5127                 // clear wall & sprite highlights
5128                 //  TODO: see about consistency with update_highlight() after other ops
5129                 reset_highlight();
5130 
5131                 // construct the loop!
5132                 i = AddLoopToSector(dstsect, &ofirstwallofs);
5133 
5134                 if (i <= 0)
5135                 {
5136                     message("Punch loop INTERNAL ERROR with AddLoopToSector!");
5137                 }
5138                 else
5139                 {
5140                     int32_t oneinnersect = -1, innerdstsect = numsectors-1;
5141 
5142                     if (dstsect < srcsect)
5143                         loopstartwall += numloopwalls;
5144 
5145                     /// handle bunchnums! (specifically, create a new one)
5146 
5147                     // collect sectors inside source loop; for that, first break the
5148                     // inner->outer nextwall links
5149                     for (i=loopstartwall; i<loopstartwall+numloopwalls; i++)
5150                     {
5151                         // all src loop walls are red!
5152                         NEXTWALL(i).nextwall = NEXTWALL(i).nextsector = -1;
5153                         oneinnersect = wall[i].nextsector;
5154                     }
5155 
5156                     // vvv
5157                     // expect oneinnersect >= 0 here!  Assumption: we collect exactly
5158                     // one connected component of sectors
5159                     collect_sectors1(collsectlist[0], collsectbitmap[0],
5160                                      &collnumsects[0], oneinnersect, 0, 0);
5161 
5162                     // set new bunchnums
5163                     for (i=0; i<collnumsects[0]; i++)
5164                         yax_setbunch(collsectlist[0][i], cf, numyaxbunches);
5165                     yax_setbunch(innerdstsect, !cf, numyaxbunches);
5166                     // ^^^
5167 
5168                     // restore inner->outer nextwall links
5169                     for (i=loopstartwall; i<loopstartwall+numloopwalls; i++)
5170                     {
5171                         NEXTWALL(i).nextwall = i;
5172                         NEXTWALL(i).nextsector = srcsect;
5173 
5174                         // set yax-nextwalls!
5175                         j = (i-loopstartwall) + sector[dstsect].wallptr;
5176                         yax_setnextwall(i, cf, j);
5177                         yax_setnextwall(j, !cf, i);
5178 
5179                         yax_setnextwall(wall[i].nextwall, cf, wall[j].nextwall);
5180                         yax_setnextwall(wall[j].nextwall, !cf, wall[i].nextwall);
5181                     }
5182 
5183                     setfirstwall(dstsect, sector[dstsect].wallptr+ofirstwallofs);
5184 
5185                     message("Punched loop starting w/ wall %d into %s sector %d%s",
5186                             loopstartwall, cf==YAX_CEILING?"upper":"lower", dstsect,
5187                             (oneinnersect>=0) ? "" : " (ERRORS)");
5188                 }
5189 
5190                 mkonwinvalid();
5191                 asksave = 1;
5192 
5193                 yax_update(0);
5194                 yax_updategrays(pos.z);
5195             }
5196 end_yax: ;
5197 #endif
5198         }
5199 
5200         if (highlightsectorcnt < 0)
5201         {
5202             if (((bstatus & 5) == 1 && highlightcnt <= 0) || ((bstatus & 5) == 1 && pointhighlight == -1) || keystatus[sc_RightShift])  //Right shift (point highlighting)
5203             {
5204                 if (highlightcnt == 0)
5205                 {
5206                     int32_t xx[] = { highlight1.x, highlight1.x, searchx, searchx, highlight1.x };
5207                     int32_t yy[] = { highlight1.y, searchy, searchy, highlight1.y, highlight1.y };
5208 
5209                     highlight2.x = searchx;
5210                     highlight2.y = searchy;
5211                     ydim16 = ydim-STATUS2DSIZ2;
5212 
5213                     plotlines2d(xx, yy, 5, -editorcolors[14]);
5214                 }
5215                 else if (pointhighlight == -1 || keystatus[sc_RightShift])
5216                 {
5217                     highlightcnt = 0;
5218 
5219                     highlight1.x = searchx;
5220                     highlight1.y = searchy;
5221                     highlight2.x = searchx;
5222                     highlight2.y = searchy;
5223                 }
5224             }
5225             else
5226             {
5227                 if (highlightcnt == 0)
5228                 {
5229                     int32_t add=keystatus[sc_Quote], sub=(!add && keystatus[sc_SemiColon]), setop=(add||sub);
5230 
5231                     if (!m32_sideview)
5232                     {
5233                         getpoint(highlight1.x,highlight1.y, &highlight1.x,&highlight1.y);
5234                         getpoint(highlight2.x,highlight2.y, &highlight2.x,&highlight2.y);
5235                     }
5236 
5237                     if (highlight1.x > highlight2.x)
5238                         swaplong(&highlight1.x, &highlight2.x);
5239                     if (highlight1.y > highlight2.y)
5240                         swaplong(&highlight1.y, &highlight2.y);
5241 
5242                     // Ctrl+RShift: select all wall-points of highlighted wall's loop:
5243                     if (eitherCTRL && highlight1.x==highlight2.x && highlight1.y==highlight2.y)
5244                     {
5245                         if (!setop)
5246                         {
5247                             Bmemset(show2dwall, 0, sizeof(show2dwall));
5248                             Bmemset(show2dsprite, 0, sizeof(show2dsprite));
5249                         }
5250 
5251                         if (linehighlight >= 0 && linehighlight < MAXWALLS)
5252                         {
5253                             i = linehighlight;
5254                             do
5255                             {
5256                                 if (!sub)
5257                                     show2dwall[i>>3] |= pow2char[i&7];
5258                                 else
5259                                     show2dwall[i>>3] &= ~pow2char[i&7];
5260 
5261                                 // XXX: this selects too many walls, need something more like
5262                                 //      those of dragpoint() -- could be still too many for
5263                                 //      loop punching though
5264                                 for (j=0; j<numwalls; j++)
5265                                     if (j!=i && wall[j].x==wall[i].x && wall[j].y==wall[i].y)
5266                                     {
5267                                         if (!sub)
5268                                             show2dwall[j>>3] |= pow2char[j&7];
5269                                         else
5270                                             show2dwall[j>>3] &= ~pow2char[j&7];
5271                                     }
5272 
5273                                 i = wall[i].point2;
5274                             }
5275                             while (i != linehighlight);
5276                         }
5277 
5278                         update_highlight();
5279                     }
5280                     else
5281                     {
5282                         int32_t tx, ty, onlySprites=eitherCTRL;
5283                         int32_t accum_dragged_verts = 0;
5284 
5285                         if (!setop)
5286                         {
5287                             Bmemset(show2dwall, 0, sizeof(show2dwall));
5288                             Bmemset(show2dsprite, 0, sizeof(show2dsprite));
5289                         }
5290 
5291                         for (i=0; i<numwalls; i++)
5292                             editwall[i>>3] &= ~pow2char[i&7];;
5293 
5294                         for (i=0; i<numwalls; i++)
5295                         {
5296                             if (onlySprites)
5297                                 break;
5298 
5299                             YAX_SKIPWALL(i);
5300 
5301                             if (!m32_sideview)
5302                             {
5303                                 tx = wall[i].x;
5304                                 ty = wall[i].y;
5305 //                                wall[i].cstat &= ~(1<<14);
5306                             }
5307                             else
5308                             {
5309                                 editorGet2dScreenCoordinates(&tx,&ty, wall[i].x-pos.x,wall[i].y-pos.y, zoom);
5310                                 ty += getscreenvdisp(
5311                                     getflorzofslope(sectorofwall(i), wall[i].x,wall[i].y)-pos.z, zoom);
5312                                 tx += halfxdim16;
5313                                 ty += midydim16;
5314                             }
5315 
5316                             if (tx >= highlight1.x && tx <= highlight2.x &&
5317                                     ty >= highlight1.y && ty <= highlight2.y)
5318                             {
5319                                 if (!sub)
5320                                 {
5321                                     if (numgraysects > 0 || m32_sideview)
5322                                     {
5323                                         // Only called to find out which walls would get dragged:
5324                                         dragpoint(i, wall[i].x, wall[i].y, accum_dragged_verts);
5325                                         accum_dragged_verts = 1;
5326                                     }
5327                                     else
5328                                         show2dwall[i>>3] |= pow2char[i&7];
5329                                 }
5330                                 else
5331                                     show2dwall[i>>3] &= ~pow2char[i&7];
5332                             }
5333                         }
5334 
5335                         if (!sub && (numgraysects > 0 || m32_sideview))
5336                         {
5337                             for (i=0; i<numwalls; i++)
5338                                 if (editwall[i>>3]&pow2char[i&7])
5339                                     show2dwall[i>>3] |= pow2char[i&7];
5340                         }
5341 
5342                         for (i=0; i<MAXSPRITES; i++)
5343                         {
5344                             if (sprite[i].statnum == MAXSTATUS)
5345                                 continue;
5346 
5347                             //  v v v: if END pressed, also permit sprites from grayed out sectors
5348                             if (!keystatus[sc_End] && (unsigned)sprite[i].sectnum < MAXSECTORS)
5349                                 YAX_SKIPSECTOR(sprite[i].sectnum);
5350 
5351                             if (!m32_sideview)
5352                             {
5353                                 tx = sprite[i].x;
5354                                 ty = sprite[i].y;
5355                             }
5356                             else
5357                             {
5358                                 editorGet2dScreenCoordinates(&tx,&ty, sprite[i].x-pos.x,sprite[i].y-pos.y, zoom);
5359                                 ty += getscreenvdisp(sprite[i].z-pos.z, zoom);
5360                                 tx += halfxdim16;
5361                                 ty += midydim16;
5362                             }
5363 
5364                             if (tx >= highlight1.x && tx <= highlight2.x &&
5365                                     ty >= highlight1.y && ty <= highlight2.y)
5366                             {
5367                                 if (!sub)
5368                                 {
5369                                     if (sprite[i].sectnum >= 0)  // don't allow to select sprites in null space
5370                                         show2dsprite[i>>3] |= pow2char[i&7];
5371                                 }
5372                                 else
5373                                     show2dsprite[i>>3] &= ~pow2char[i&7];
5374                             }
5375                         }
5376 
5377                         update_highlight();
5378 
5379                         for (i=0; i<numwalls; i++)
5380                             editwall[i>>3] &= ~pow2char[i&7];
5381                     }
5382                 }
5383             }
5384         }
5385 
5386         if (highlightcnt < 0)
5387         {
5388             if (((bstatus & 4) && highlightsectorcnt <= 0) || ((bstatus & 4) && linehighlight == -1)
5389                 || keystatus[sc_RightAlt])  //Right alt (sector highlighting)
5390             {
5391                 if (highlightsectorcnt == 0)
5392                 {
5393                     if (!eitherCTRL)
5394                     {
5395                         int32_t xx[] = { highlight1.x, highlight1.x, searchx, searchx, highlight1.x };
5396                         int32_t yy[] = { highlight1.y, searchy, searchy, highlight1.y, highlight1.y };
5397 
5398                         highlight2.x = searchx;
5399                         highlight2.y = searchy;
5400                         ydim16 = ydim-STATUS2DSIZ2;
5401 
5402                         plotlines2d(xx, yy, 5, -editorcolors[10]);
5403                     }
5404                 }
5405                 else
5406                 {
5407                     // didmakered: 'bad'!
5408                     int32_t didmakered = (highlightsectorcnt<0) || eitherCTRL, hadouterpoint=0;
5409 #ifdef YAX_ENABLE
5410                     for (i=0; i<(MAXSECTORS+7)>>3; i++)
5411                         hlorgraysectbitmap[i] = hlsectorbitmap[i]|graysectbitmap[i];
5412 #endif
5413                     for (i=0; i<highlightsectorcnt; i++)
5414                     {
5415                         int16_t tmpsect = -1;
5416 
5417                         for (WALLS_OF_SECTOR(highlightsector[i], j))
5418                         {
5419 //                            if (wall[j].nextwall >= 0)
5420 //                                checksectorpointer(wall[j].nextwall,wall[j].nextsector);
5421                             if (wall[j].nextwall < 0)
5422                                 didmakered |= !!checksectorpointer(j, highlightsector[i]);
5423 
5424                             if (!didmakered)
5425                             {
5426                                 updatesectorexclude(wall[j].x, wall[j].y, &tmpsect, hlorgraysectbitmap);
5427                                 if (tmpsect<0)
5428                                     hadouterpoint = 1;
5429                             }
5430                         }
5431 #ifdef YAX_ENABLE
5432                         int16_t cb, fb;
5433 
5434                         yax_getbunches(highlightsector[i], &cb, &fb);
5435                         if (cb>=0 || fb>=0)
5436                         {
5437                             // TROR stuff in the pasted sectors would really
5438                             // complicate things, so don't allow this
5439                             didmakered=1;
5440                         }
5441 #endif
5442                     }
5443 
5444                     if (!didmakered && !hadouterpoint && newnumwalls<0)
5445                     {
5446                         // fade the screen to have the user's attention
5447                         fade_editor_screen(-1);
5448 
5449                         didmakered |= !ask_if_sure("Insert outer loop and make red walls? (Y/N)", 0);
5450                         clearkeys();
5451                     }
5452 
5453                     if (!didmakered && !hadouterpoint && newnumwalls<0)
5454                     {
5455                         int16_t ignore, refsect;
5456                         int32_t n;
5457 #ifdef YAX_ENABLE
5458                         int16_t refsectbn[2] = {-1,-1};
5459                         int32_t refextcf=-1;
5460 #endif
5461                         Bmemset(visited, 0, sizeof(visited));
5462 
5463                         for (i=0; i<highlightsectorcnt; i++)
5464                         {
5465                             for (WALLS_OF_SECTOR(highlightsector[i], j))
5466                             {
5467                                 k = trace_loop(j, visited, &ignore, &refsect, -1);
5468                                 if (k == 0)
5469                                     continue;
5470                                 else if (k < 0)
5471                                     goto end_autoredwall;
5472 
5473                                 if (ignore)
5474                                     continue;
5475 
5476                                 // done tracing one outer loop
5477 #ifdef YAX_ENABLE
5478                                 yax_getbunches(refsect, &refsectbn[0], &refsectbn[1]);
5479                                 if (refsectbn[0]>=0 || refsectbn[1]>=0)
5480                                 {
5481                                     if (refsectbn[0]>=0 && refsectbn[1]>=0)
5482                                     {
5483                                         // at least one of ceiling/floor must be non-extended
5484                                         didmakered = 1;
5485                                     }
5486                                     else
5487                                     {
5488                                         // ... and the other must be non-sloped
5489                                         refextcf = (refsectbn[1]>=0);
5490                                         if (SECTORFLD(refsect,stat, !refextcf)&2)
5491                                             didmakered = 1;
5492                                     }
5493                                 }
5494 
5495                                 if (didmakered)
5496                                     goto end_autoredwall;
5497 
5498                                 if (refextcf >= 0)
5499                                 {
5500                                     int32_t refz = SECTORFLD(refsect,z, refextcf), tmpsect;
5501                                     int32_t neededzofs=0;
5502 
5503                                     // the reference sector is extended on one side
5504                                     // (given by refextcf) and non-sloped on the other
5505                                     if (highlighted_sectors_components(0,0) != 1)
5506                                     {
5507                                         message("Highlighted sectors must be in one connected component");
5508                                         goto end_autoredwall;
5509                                     }
5510 
5511                                     for (m=0; m<highlightsectorcnt; m++)
5512                                     {
5513                                         tmpsect = highlightsector[m];
5514                                         yax_setbunch(tmpsect, refextcf, refsectbn[refextcf]);
5515                                         // walls: not needed, since they're all inner to the bunch
5516 
5517                                         SECTORFLD(tmpsect,z, refextcf) = refz;
5518                                         setslope(tmpsect, refextcf, 0);
5519                                         if (refextcf==0)
5520                                             neededzofs = max(neededzofs, refz-sector[tmpsect].floorz);
5521                                         else
5522                                             neededzofs = max(neededzofs, sector[tmpsect].ceilingz-refz);
5523                                     }
5524 
5525                                     if (neededzofs > 0)
5526                                     {
5527                                         neededzofs += ksgn(neededzofs)*(512<<4);
5528                                         neededzofs &= ~((256<<4)-1);
5529                                         if (refextcf==1)
5530                                             neededzofs *= -1;
5531                                         for (m=0; m<highlightsectorcnt; m++)
5532                                             SECTORFLD(highlightsector[m],z, !refextcf) += neededzofs;
5533                                     }
5534                                 }
5535 #endif
5536                                 wall[k-1].point2 = numwalls;  // close the loop
5537                                 newnumwalls = k;
5538                                 n = (newnumwalls-numwalls);  // number of walls in just constructed loop
5539 
5540                                 if (clockdir(numwalls)==CLOCKDIR_CW)
5541                                 {
5542                                     int16_t begwalltomove = sector[refsect].wallptr+sector[refsect].wallnum;
5543                                     int32_t onwwasvalid = onwisvalid();
5544 
5545                                     flipwalls(numwalls, newnumwalls);
5546 
5547                                     sector[refsect].wallnum += n;
5548                                     if (refsect != numsectors-1)
5549                                     {
5550                                         uwalltype *tmpwall = (uwalltype *)Xmalloc(n * sizeof(walltype));
5551                                         int16_t *tmponw = (int16_t *)Xmalloc(n * sizeof(int16_t));
5552 
5553                                         for (m=0; m<numwalls; m++)
5554                                         {
5555                                             if (wall[m].nextwall >= begwalltomove)
5556                                                 wall[m].nextwall += n;
5557                                         }
5558 #ifdef YAX_ENABLE
5559                                         yax_tweakwalls(begwalltomove, n);
5560 #endif
5561                                         for (m=refsect+1; m<numsectors; m++)
5562                                             sector[m].wallptr += n;
5563                                         for (m=begwalltomove; m<numwalls; m++)
5564                                             wall[m].point2 += n;
5565                                         for (m=numwalls; m<newnumwalls; m++)
5566                                             wall[m].point2 += (begwalltomove-numwalls);
5567 
5568                                         Bmemcpy(tmponw, &onextwall[numwalls], n*sizeof(int16_t));
5569                                         Bmemmove(&onextwall[begwalltomove+n], &onextwall[begwalltomove],
5570                                                  (numwalls-begwalltomove)*sizeof(int16_t));
5571                                         Bmemcpy(&onextwall[begwalltomove], tmponw, n*sizeof(int16_t));
5572 
5573                                         Bmemcpy(tmpwall, &wall[numwalls], n*sizeof(walltype));
5574                                         Bmemmove(&wall[begwalltomove+n], &wall[begwalltomove],
5575                                                  (numwalls-begwalltomove)*sizeof(walltype));
5576                                         Bmemcpy(&wall[begwalltomove], tmpwall, n*sizeof(walltype));
5577 
5578                                         Xfree(tmpwall);
5579                                         Xfree(tmponw);
5580                                     }
5581                                     numwalls = newnumwalls;
5582                                     newnumwalls = -1;
5583 
5584                                     mkonwinvalid();
5585 
5586                                     for (m=begwalltomove; m<begwalltomove+n; m++)
5587                                         if (checksectorpointer(m, refsect) > 0)
5588                                             if (onwwasvalid && onextwall[wall[m].nextwall]>=0)
5589                                             {
5590 //initprintf("%d %d\n", m, onextwall[wall[m].nextwall]);
5591                                                 copy_some_wall_members(m, onextwall[wall[m].nextwall], 0);
5592                                             }
5593 
5594 #ifndef YAX_ENABLE
5595                                     message("Attached new inner loop to sector %d", refsect);
5596 #else
5597                                     {
5598                                         const char *cfstr[2] = {"ceiling","floor"};
5599                                         message("Attached new inner loop to %s%ssector %d",
5600                                                 refextcf>=0 ? cfstr[refextcf] : "",
5601                                                 refextcf>=0 ? "-extended " : "", refsect);
5602                                     }
5603 
5604                                     asksave = 1;
5605 
5606                                     if (refextcf >= 0)
5607                                     {
5608                                         yax_update(0);
5609                                         goto end_autoredwall;
5610                                     }
5611 #endif
5612                                 }
5613                             }
5614                         }
5615 end_autoredwall:
5616                         newnumwalls = -1;
5617 #ifdef YAX_ENABLE
5618                         yax_updategrays(pos.z);
5619 #endif
5620                     }
5621 
5622                     highlight1.x = searchx;
5623                     highlight1.y = searchy;
5624                     highlight2.x = searchx;
5625                     highlight2.y = searchy;
5626                     highlightsectorcnt = 0;
5627                 }
5628             }
5629             else
5630             {
5631                 if (highlightsectorcnt == 0)
5632                 {
5633                     int32_t const add=keystatus[sc_Quote], sub=(!add && keystatus[sc_SemiColon]), setop=(add||sub);
5634                     int32_t const pointsel = eitherCTRL;
5635                     int32_t tx,ty;
5636 #ifdef YAX_ENABLE
5637                     // home: ceilings, end: floors
5638                     int32_t fb, bunchsel = keystatus[sc_End] ? 1 : (keystatus[sc_Home] ? 0 : -1);
5639                     uint8_t bunchbitmap[(YAX_MAXBUNCHES+7)>>3];
5640                     Bmemset(bunchbitmap, 0, sizeof(bunchbitmap));
5641 #endif
5642                     if (!m32_sideview)
5643                     {
5644                         getpoint(highlight1.x,highlight1.y, &highlight1.x,&highlight1.y);
5645                         getpoint(highlight2.x,highlight2.y, &highlight2.x,&highlight2.y);
5646                     }
5647 
5648                     if (!pointsel)
5649                     {
5650                         if (highlight1.x > highlight2.x)
5651                             swaplong(&highlight1.x, &highlight2.x);
5652                         if (highlight1.y > highlight2.y)
5653                             swaplong(&highlight1.y, &highlight2.y);
5654                     }
5655 
5656                     if (!setop)
5657                         Bmemset(hlsectorbitmap, 0, sizeof(hlsectorbitmap));
5658 
5659                     for (i=0; i<numsectors; i++)
5660                     {
5661                         if (pointsel)
5662                         {
5663                             bad = (inside_editor(&pos, searchx,searchy, zoom, highlight2.x, highlight2.y, i)!=1);
5664                         }
5665                         else
5666                         {
5667                             bad = 0;
5668                             for (WALLS_OF_SECTOR(i, j))
5669                             {
5670                                 if (!m32_sideview)
5671                                 {
5672                                     tx = wall[j].x;
5673                                     ty = wall[j].y;
5674                                 }
5675                                 else
5676                                 {
5677                                     editorGet2dScreenCoordinates(&tx,&ty, wall[j].x-pos.x,wall[j].y-pos.y, zoom);
5678                                     ty += getscreenvdisp(getflorzofslope(i, wall[j].x,wall[j].y)-pos.z, zoom);
5679                                     tx += halfxdim16;
5680                                     ty += midydim16;
5681                                 }
5682 
5683                                 if (tx < highlight1.x || tx > highlight2.x) bad = 1;
5684                                 if (ty < highlight1.y || ty > highlight2.y) bad = 1;
5685                                 if (bad == 1) break;
5686                             }
5687                         }
5688 
5689                         if (bad == 0)
5690                         {
5691 #ifdef YAX_ENABLE
5692                             if (bunchsel!=-1 && (fb = yax_getbunch(i, YAX_FLOOR))>=0)
5693                             {
5694                                 if ((sub || (graysectbitmap[i>>3]&pow2char[i&7])==0) &&
5695                                         (bunchbitmap[fb>>3]&pow2char[fb&7])==0)
5696                                 {
5697                                     bunchbitmap[fb>>3] |= pow2char[fb&7];
5698                                     for (SECTORS_OF_BUNCH(fb, bunchsel, j))
5699                                         handlesecthighlight1(j, sub, 1);
5700                                 }
5701                             }
5702                             else
5703 #endif
5704                                 handlesecthighlight1(i, sub, eitherSHIFT);
5705                         }
5706                     }
5707 
5708                     update_highlightsector();
5709                     ovh_whiteoutgrab(0);
5710                 }
5711             }
5712         }
5713 
5714         if (((bstatus&1) < (oldmousebstatus&1)) && highlightsectorcnt < 0)  //after dragging
5715         {
5716 
5717             // restorestat is set to 2 whenever the drawn walls should NOT be
5718             // restored afterwards
5719 
5720             int32_t err = backup_drawn_walls(0);
5721 
5722             if (err)
5723             {
5724                 message("Error backing up drawn walls (code %d)!", err);
5725                 backup_drawn_walls(1);
5726                 goto end_after_dragging;
5727             }
5728 
5729             j = 1;
5730             for (i=0; i<highlightcnt; i++)
5731                 if (pointhighlight == highlight[i])
5732                 {
5733                     j = 0;
5734                     break;
5735                 }
5736 
5737             if (j == 0)
5738             {
5739                 for (i=0; i<highlightcnt; i++)
5740                     if ((highlight[i]&0xc000) == 16384)
5741                         updatesprite1(highlight[i]&16383);
5742             }
5743             else if ((pointhighlight&0xc000) == 16384)
5744             {
5745                 updatesprite1(pointhighlight&16383);
5746             }
5747 
5748             if ((pointhighlight&0xc000) == 0)
5749             {
5750                 dax = wall[pointhighlight].x;
5751                 day = wall[pointhighlight].y;
5752 
5753                 for (i=0; i<2; i++)
5754                 {
5755                     if (dragwall[i] < 0)
5756                         break;
5757 
5758                     if (keeptexturestretch && olen[i] != 0)
5759                     {
5760 #ifndef YAX_ENABLE
5761                         j = dragwall[i];
5762 #else
5763                         int32_t cf;
5764                         for (YAX_ITER_WALLS(dragwall[i], j, cf))
5765 #endif
5766                         {
5767                             int32_t nw = wall[j].nextwall;
5768 
5769                             k = getlenbyrep(olen[i], wall[j].xrepeat);
5770                             fixxrepeat(j, k);
5771                             if (nw >= 0)
5772                             {
5773                                 k = getlenbyrep(olen[i], wall[nw].xrepeat);
5774                                 fixxrepeat(nw, k);
5775                             }
5776                         }
5777                     }
5778                 }
5779             }
5780             else if ((pointhighlight&0xc000) == 16384)
5781             {
5782                 dax = sprite[pointhighlight&16383].x;
5783                 day = sprite[pointhighlight&16383].y;
5784             }
5785 
5786             dragwall[0] = dragwall[1] = -1;
5787 
5788             maybedeletewalls(dax, day);
5789         }
5790 end_after_dragging:
5791         if ((bstatus&1) > 0)                //drag points
5792         {
5793             if (highlightsectorcnt > 0)
5794             {
5795                 if ((bstatus&1) > (oldmousebstatus&1))
5796                 {
5797                     newnumwalls = -1;
5798                     sectorhighlightstat = -1;
5799 
5800 //                    updatesector(mousxplc,mousyplc,&cursectorhighlight);
5801                     cursectorhighlight = -1;
5802                     for (i=0; i<highlightsectorcnt; i++)
5803                         if (inside_editor_curpos(highlightsector[i])==1)
5804                         {
5805                             cursectorhighlight = highlightsector[i];
5806                             break;
5807                         }
5808 
5809                     if (cursectorhighlight >= 0 && cursectorhighlight < numsectors)
5810                     {
5811                         //You clicked inside one of the flashing sectors!
5812                         sectorhighlightstat = 1;
5813 
5814                         dax = mousxplc;
5815                         day = mousyplc;
5816                         if (gridlock && grid > 0)
5817                             locktogrid(&dax, &day);
5818 
5819                         sectorhighlightx = dax;
5820                         sectorhighlighty = day;
5821                     }
5822                 }
5823                 else if (sectorhighlightstat == 1)
5824                 {
5825                     dax = mousxplc;
5826                     day = mousyplc;
5827                     if (gridlock && grid > 0)
5828                         locktogrid(&dax, &day);
5829 
5830                     dax -= sectorhighlightx;
5831                     day -= sectorhighlighty;
5832                     sectorhighlightx += dax;
5833                     sectorhighlighty += day;
5834 #ifdef YAX_ENABLE
5835                     if (!hl_all_bunch_sectors_p())
5836                         printmessage16("To drag extended sectors, all sectors of a bunch must be selected");
5837                     else
5838 #endif
5839                     for (i=0; i<highlightsectorcnt; i++)
5840                     {
5841                         for (WALLS_OF_SECTOR(highlightsector[i], j))
5842                             { wall[j].x += dax; wall[j].y += day; }
5843 
5844                         for (j=headspritesect[highlightsector[i]]; j>=0; j=nextspritesect[j])
5845                             { sprite[j].x += dax; sprite[j].y += day; }
5846                     }
5847 
5848                     //for(i=0;i<highlightsectorcnt;i++)
5849                     //{
5850                     //   startwall = sector[highlightsector[i]].wallptr;
5851                     //   endwall = startwall+sector[highlightsector[i]].wallnum-1;
5852                     //   for(j=startwall;j<=endwall;j++)
5853                     //   {
5854                     //      if (wall[j].nextwall >= 0)
5855                     //         checksectorpointer(wall[j].nextwall,wall[j].nextsector);
5856                     //     checksectorpointer((short)j,highlightsector[i]);
5857                     //   }
5858                     //}
5859                     asksave = 1;
5860                 }
5861 
5862             }
5863             else  //if (highlightsectorcnt <= 0)
5864             {
5865                 if ((bstatus&1) > (oldmousebstatus&1))
5866                 {
5867                     pointhighlight = getpointhighlight(mousxplc, mousyplc, pointhighlight);
5868 
5869                     if (pointhighlight >= 0 && (pointhighlight&0xc000)==0)
5870                     {
5871                         dragwall[0] = lastwall(pointhighlight);
5872                         dragwall[1] = pointhighlight;
5873                         olen[0] = wallength(dragwall[0]);
5874                         olen[1] = wallength(dragwall[1]);
5875                     }
5876                 }
5877 
5878                 if (pointhighlight >= 0 && (!m32_sideview || m32_sideelev>=32))
5879                 {
5880                     if (m32_sideview)
5881                     {
5882                         int32_t dz;
5883                         if (pointhighlight>=16384)
5884                             dz = sprite[pointhighlight&16383].z - pos.z;
5885                         else
5886                             dz = getflorzofslope(sectorofwall(pointhighlight),
5887                                                  wall[pointhighlight].x, wall[pointhighlight].y) - pos.z;
5888                         getinvdisplacement(&dax,&day, -dz);
5889                         dax += mousxplc;
5890                         day += mousyplc;
5891                     }
5892                     else
5893                     {
5894                         dax = mousxplc;
5895                         day = mousyplc;
5896                     }
5897 
5898                     if (gridlock && grid > 0)
5899                         locktogrid(&dax, &day);
5900 
5901                     j = 1;
5902                     if (highlightcnt > 0)
5903                         for (i=0; i<highlightcnt; i++)
5904                             if (pointhighlight == highlight[i])
5905                                 { j = 0; break; }
5906 
5907                     if (j == 0)
5908                     {
5909                         if ((pointhighlight&0xc000) == 0)
5910                         {
5911                             dax -= wall[pointhighlight].x;
5912                             day -= wall[pointhighlight].y;
5913                         }
5914                         else
5915                         {
5916                             dax -= sprite[pointhighlight&16383].x;
5917                             day -= sprite[pointhighlight&16383].y;
5918                         }
5919 
5920                         for (i=0; i<highlightcnt; i++)
5921                         {
5922                             if ((highlight[i]&0xc000) == 0)
5923                             {
5924                                 wall[highlight[i]].x += dax;
5925                                 wall[highlight[i]].y += day;
5926                             }
5927                             else
5928                             {
5929                                 spritetype *daspr = &sprite[highlight[i]&16383];
5930 
5931                                 daspr->x += dax;
5932                                 daspr->y += day;
5933                                 setspritez(daspr-sprite, (const vec3_t *)daspr);
5934                             }
5935                         }
5936                     }
5937                     else
5938                     {
5939                         if ((pointhighlight&0xc000) == 0)
5940                         {
5941                             if (newnumwalls >= numwalls &&
5942                                     wall[pointhighlight].x==firstx && wall[pointhighlight].y==firsty)
5943                             {
5944                                 printmessage16("Can't drag point where drawing started.");
5945                                 goto end_point_dragging;
5946                             }
5947 
5948                             dragpoint(pointhighlight,dax,day,2);
5949                             int wn = lastwall(pointhighlight);
5950                             editwall[wn>>3] |= pow2char[wn&7];
5951                         }
5952                         else if ((pointhighlight&0xc000) == 16384)
5953                         {
5954                             int32_t daspr=pointhighlight&16383;
5955                             int16_t osec=sprite[daspr].sectnum, nsec=osec;
5956                             vec3_t vec, ovec;
5957 
5958                             Bmemcpy(&ovec, &sprite[daspr].pos, sizeof(vec3_t));
5959                             vec.x = dax;
5960                             vec.y = day;
5961                             vec.z = sprite[daspr].z;
5962                             if (setspritez(daspr, &vec) == -1 && osec>=0)
5963                             {
5964                                 updatesector(dax, day, &nsec);
5965 
5966                                 if (nsec >= 0)
5967                                 {
5968                                     sprite[daspr].x = dax;
5969                                     sprite[daspr].y = day;
5970                                     // z updating is after we released the mouse button
5971                                     if (sprite[daspr].sectnum != nsec)
5972                                         changespritesect(daspr, nsec);
5973                                 }
5974                                 else
5975                                     Bmemcpy(&sprite[daspr], &ovec, sizeof(vec3_t));
5976                             }
5977                         }
5978                     }
5979 
5980                     asksave = 1;
5981                 }
5982             }
5983         }
5984         else //if ((bstatus&1) == 0)
5985         {
5986             pointhighlight = getpointhighlight(mousxplc, mousyplc, pointhighlight);
5987             sectorhighlightstat = -1;
5988         }
5989 end_point_dragging:
5990 
5991         if (bstatus&(2) && (!(bstatus&5) || pointhighlight > 0 || highlightcnt > 0 || highlightsectorcnt > 0))  // change arrow position
5992         {
5993             if (eitherCTRL)
5994             {
5995                 int16_t cursectornum;
5996 
5997                 for (cursectornum=0; cursectornum<numsectors; cursectornum++)
5998                     if (inside_editor_curpos(cursectornum) == 1)
5999                         break;
6000 
6001                 if (cursectornum < numsectors)
6002                 {
6003                     if (pointhighlight >= 16384)
6004                         CallExtEditSpriteData(pointhighlight-16384);
6005                     else if ((linehighlight >= 0) && ((bstatus&1) || sectorofwall(linehighlight) == cursectornum))
6006                         CallExtEditWallData(linehighlight);
6007                     else if (cursectornum >= 0)
6008                         CallExtEditSectorData(cursectornum);
6009                 }
6010 
6011                 bstatus &= ~2;
6012             }
6013             else
6014             {
6015                 if (m32_sideview && (bstatus&4))
6016                 {
6017                     pos.z += divscale18(searchy-midydim16,zoom);
6018                     getpoint(searchx,midydim16, &pos.x, &pos.y);
6019 #ifdef YAX_ENABLE
6020                     yax_updategrays(pos.z);
6021 #endif
6022                 }
6023                 else
6024                 {
6025                     pos.x = mousxplc;
6026                     pos.y = mousyplc;
6027                 }
6028 
6029                 if (m32_sideview)
6030                 {
6031                     int32_t opat=drawlinepat;
6032 
6033                     y1 = INT32_MAX;
6034 
6035                     for (i=0; i<numsectors; i++)
6036                     {
6037                         if (inside(pos.x, pos.y, i)==1)
6038                         {
6039                             day = getscreenvdisp(getflorzofslope(i, pos.x, pos.y)-pos.z, zoom);
6040 
6041                             x2 = max(4, mulscale14(64,zoom));
6042                             y2 = scalescreeny(x2);
6043 
6044                             if (klabs(day) < y1)
6045                                 y1 = day;
6046 
6047                             drawline16base(halfxdim16, midydim16+day, -x2,-y2, x2,y2, editorcolors[14]);
6048                             drawline16base(halfxdim16, midydim16+day, -x2,y2, x2,-y2, editorcolors[14]);
6049                         }
6050                     }
6051 
6052                     drawlinepat = 0x11111111;
6053                     if (y1 != INT32_MAX)
6054                         drawline16base(halfxdim16,midydim16, 0,0, 0,y1, editorcolors[14]);
6055 //                    else
6056 //                        drawline16base(halfxdim16,midydim16, 0,0, 0,getscreenvdisp(-pos.z, zoom), editorcolors[14]);
6057                     drawlinepat = opat;
6058                 }
6059 
6060                 searchx = halfxdim16;
6061                 searchy = midydim16;
6062             }
6063         }
6064 //        else if ((oldmousebstatus&6) > 0)
6065             updatesectorz(pos.x,pos.y,pos.z,&cursectnum);
6066 
6067         if (circlewall != -1 && (keystatus[sc_kpad_Minus] || ((bstatus&32) && !eitherCTRL)))  // -, mousewheel down
6068         {
6069             if (circlepoints > 1)
6070                 circlepoints--;
6071             keystatus[sc_kpad_Minus] = 0;
6072             g_mouseBits &= ~32;
6073             bstatus &= ~32;
6074         }
6075         if (circlewall != -1 && (keystatus[sc_kpad_Plus] || ((bstatus&16) && !eitherCTRL)))  // +, mousewheel up
6076         {
6077             if (circlepoints < 63)
6078                 circlepoints++;
6079             keystatus[sc_kpad_Plus] = 0;
6080             g_mouseBits &= ~16;
6081             bstatus &= ~16;
6082         }
6083 
6084         if (keystatus[sc_F3])  // F3
6085         {
6086             keystatus[sc_F3]=0;
6087             if (!m32_sideview && EDITING_MAP_P())
6088                 message("Must not be editing map while switching to side view mode.");
6089             else
6090             {
6091                 m32_sideview = !m32_sideview;
6092                 printmessage16("Side view %s", m32_sideview?"enabled":"disabled");
6093             }
6094         }
6095 
6096         if (m32_sideview && (keystatus[sc_Q] || keystatus[sc_W]))
6097         {
6098             if (eitherCTRL)
6099             {
6100                 if (m32_sideang&63)
6101                 {
6102                     m32_sideang += (1-2*keystatus[sc_Q])*(1-2*sideview_reversehrot)*32;
6103                     m32_sideang &= (2047&~63);
6104                 }
6105                 else
6106                 {
6107                     m32_sideang += (1-2*keystatus[sc_Q])*(1-2*sideview_reversehrot)*64;
6108                     m32_sideang &= 2047;
6109                 }
6110 
6111                 keystatus[sc_Q] = keystatus[sc_W] = 0;
6112             }
6113             else
6114             {
6115                 m32_sideang += (1-2*keystatus[sc_Q])*(1-2*sideview_reversehrot)*synctics<<(eitherSHIFT*2);
6116                 m32_sideang &= 2047;
6117             }
6118             _printmessage16("Sideview angle: %d", (int32_t)m32_sideang);
6119         }
6120 
6121         if (m32_sideview && (eitherSHIFT || (bstatus&(16|32))))
6122         {
6123             if ((DOWN_BK(MOVEUP) || (bstatus&16)) && m32_sideelev < 512)
6124             {
6125                 if (DOWN_BK(MOVEUP))
6126                     m32_sideelev += synctics<<1;
6127                 if (bstatus&16)
6128                     m32_sideelev += 4<<1;
6129 
6130                 if (m32_sideelev > 512)
6131                     m32_sideelev = 512;
6132                 _printmessage16("Sideview elevation: %d", m32_sideelev);
6133             }
6134             if ((DOWN_BK(MOVEDOWN) || (bstatus&32)) && m32_sideelev > 0)
6135             {
6136                 if (DOWN_BK(MOVEDOWN))
6137                     m32_sideelev -= synctics<<1;
6138                 if (bstatus&32)
6139                     m32_sideelev -= 4<<1;
6140 
6141                 if (m32_sideelev < 0)
6142                     m32_sideelev = 0;
6143                 _printmessage16("Sideview elevation: %d", m32_sideelev);
6144             }
6145         }
6146         else
6147         {
6148             int32_t didzoom=0;
6149 
6150             if ((DOWN_BK(MOVEUP) || (bstatus&16)) && zoom < 39936)
6151             {
6152                 if (DOWN_BK(MOVEUP))
6153                 {
6154                     ztarget += (synctics*(ztarget>>4))>>(eitherSHIFT<<1);
6155 
6156                     if (zoom < 64)
6157                         ztarget += (synctics*(ztarget>>4)) * (eitherSHIFT);
6158                 }
6159                 if (bstatus&16)
6160                     ztarget += 4*(ztarget>>4);
6161 
6162                 if (zoom < 24) zoom += 2;
6163                 didzoom = 1;
6164             }
6165             if ((DOWN_BK(MOVEDOWN) || (bstatus&32)) && zoom > 16)
6166             {
6167                 if (DOWN_BK(MOVEDOWN))
6168                 {
6169                     ztarget -= (synctics*(ztarget>>4))>>(eitherSHIFT<<1);
6170 
6171                     if (zoom < 64)
6172                         ztarget -= (synctics * (ztarget >> 4)) * (eitherSHIFT);
6173                 }
6174                 if (bstatus&32)
6175                     ztarget -= 4*(ztarget>>4);
6176 
6177                 didzoom = 1;
6178             }
6179 
6180             if (didzoom)
6181             {
6182                 if (eitherALT)
6183                 {
6184                     searchx = halfxdim16;
6185                     searchy = midydim16;
6186                     pos.x = mousxplc;
6187                     pos.y = mousyplc;
6188                 }
6189                 ztarget = clamp(ztarget, 16, 39936);
6190 
6191                 _printmessage16("Zoom: %d",ztarget);
6192             }
6193         }
6194 
6195 
6196         if (keystatus[sc_G])  // G (grid on/off)
6197         {
6198             keystatus[sc_G] = 0;
6199             grid++;
6200             if (grid == 7) grid = 0;
6201         }
6202         if (keystatus[sc_L])  // L (grid lock)
6203         {
6204             keystatus[sc_L] = 0;
6205 
6206             if (eitherSHIFT)
6207             {
6208                 searchlock = 1-searchlock;
6209                 silentmessage("Selection lock %s", searchlock ? "on" : "off");
6210             }
6211             else
6212             {
6213                 gridlock = !gridlock;
6214                 silentmessage("Grid locking %s", gridlock ? "on" : "off");
6215             }
6216         }
6217 
6218         if (keystatus[sc_J])  // J (join sectors)
6219         {
6220             keystatus[sc_J] = 0;
6221 
6222             if (newnumwalls >= 0)
6223             {
6224                 printmessage16("Can't join sectors while editing.");
6225                 goto end_join_sectors;
6226             }
6227 
6228 #ifdef YAX_ENABLE
6229             if (highlightsectorcnt > 0 && eitherCTRL)
6230             {
6231                 // [component][ceiling(0) or floor(1)]
6232                 // compstat: &1: "has extension", &2: "differ in z", &4: "sloped", -1: "uninited"
6233                 int32_t cf, comp, compstat[2][2] = {{-1,-1},{-1,-1}}, compcfz[2][2];
6234 
6235                 // joinstat: join what to what?
6236                 //  &1: ceil(comp 0) <-> flor(comp 1),  &2: flor(comp 0) <-> ceil(comp 1)
6237                 //  (doesn't yet say which is stationary)
6238                 // movestat: which component can be displaced?
6239                 //  &1: first,  &2: second
6240                 int32_t askres, joinstat, needsdisp, moveonwp;
6241                 int32_t movestat, dx=0,dy=0,dz, delayerr=0;
6242 
6243                 int32_t numouterwalls[2] = {0,0}, numowals;
6244                 static int16_t outerwall[2][MAXWALLS];
6245                 uwallptr_t wal0, wal1, wal0p2, wal1p2;
6246 
6247                 // join sector ceilings/floors to a new bunch
6248                 if (numyaxbunches==YAX_MAXBUNCHES)
6249                 {
6250                     message("Bunch limit of %d reached, cannot join", YAX_MAXBUNCHES);
6251                     goto end_join_sectors;
6252                 }
6253 
6254                 // first, see whether we have exactly two connected components
6255                 // wrt wall[].nextsector
6256                 if (highlighted_sectors_components(0,0) != 2)
6257                 {
6258                     message("Sectors must be partitioned in two components to join");
6259                     goto end_join_sectors;
6260                 }
6261 
6262                 for (k=0; k<highlightsectorcnt; k++)
6263                 {
6264                     j = highlightsector[k];
6265                     comp = !!(collsectbitmap[1][j>>3]&pow2char[j&7]);
6266 
6267                     for (cf=0; cf<2; cf++)
6268                     {
6269                         if (compstat[comp][cf]==-1)
6270                         {
6271                             compstat[comp][cf] = 0;
6272                             compcfz[comp][cf] = SECTORFLD(j,z, cf);
6273                         }
6274 
6275                         if (yax_getbunch(j, cf)>=0)
6276                             compstat[comp][cf] |= 1;
6277                         if (SECTORFLD(j,z, cf) != compcfz[comp][cf])
6278                             compstat[comp][cf] |= 2;
6279                         if (SECTORFLD(j,stat, cf)&2)
6280                             compstat[comp][cf] |= 4;
6281 
6282                         compcfz[comp][cf] = SECTORFLD(j,z, cf);
6283                     }
6284                 }
6285 
6286                 // check for consistency
6287                 joinstat = 0;
6288                 if (!compstat[0][YAX_CEILING] && !compstat[1][YAX_FLOOR])
6289                     joinstat |= 1;
6290                 if (!compstat[0][YAX_FLOOR] && !compstat[1][YAX_CEILING])
6291                     joinstat |= 2;
6292 
6293                 if (joinstat==0)
6294                 {
6295                     message("No consistent joining combination found");
6296                     OSD_Printf("comp0: c=%d,f=%d;  comp1: c=%d,f=%d  (1:extended, 2:z mismatch, 4:sloped)\n",
6297                                compstat[0][YAX_CEILING], compstat[0][YAX_FLOOR],
6298                                compstat[1][YAX_CEILING], compstat[1][YAX_FLOOR]);
6299                     //for (i=0; i<2; i++) for (j=0; j<2; j++) message("%d", compstat[i][j]);
6300                     goto end_join_sectors;
6301                 }
6302                 if (joinstat==3)
6303                 {
6304                     if (compcfz[0][YAX_CEILING] != compstat[1][YAX_FLOOR])
6305                         joinstat &= 1;
6306                     if (compcfz[0][YAX_CEILING] != compstat[1][YAX_FLOOR])
6307                         joinstat &= 2;
6308 
6309                     if (joinstat == 0)
6310                         joinstat = 3;  // we couldn't disambiguate
6311                 }
6312 
6313                 for (comp=0; comp<2; comp++)
6314                     for (k=0; k<collnumsects[comp]; k++)
6315                     {
6316                         i = collsectlist[comp][k];
6317                         for (WALLS_OF_SECTOR(i, j))
6318                             if (wall[j].nextwall < 0)
6319                                 outerwall[comp][numouterwalls[comp]++] = j;
6320                     }
6321 
6322                 if (numouterwalls[0] != numouterwalls[1])
6323                 {
6324                     message("Number of outer walls must be equal for both components"
6325                             " (have %d and %d)", numouterwalls[0], numouterwalls[1]);
6326                     if (numouterwalls[0]>0 && numouterwalls[1]>0)
6327                         delayerr = 1;
6328                     else
6329                         goto end_join_sectors;
6330                 }
6331                 numowals = min(numouterwalls[0], numouterwalls[1]);
6332 
6333                 // now sort outer walls 'geometrically'
6334                 for (comp=0; comp<2; comp++)
6335                     sort_walls_geometrically(outerwall[comp], numouterwalls[comp]);
6336 
6337                 for (k=0; k<numowals; k++)
6338                 {
6339                     wal0 = (uwallptr_t)&wall[outerwall[0][k]];
6340                     wal1 = (uwallptr_t)&wall[outerwall[1][k]];
6341 
6342                     wal0p2 = (uwallptr_t)&wall[wal0->point2];
6343                     wal1p2 = (uwallptr_t)&wall[wal1->point2];
6344 
6345                     if (k==0)
6346                     {
6347                         dx = wal1->x - wal0->x;
6348                         dy = wal1->y - wal0->y;
6349                     }
6350 
6351                     if (wal1->x - wal0->x != dx || wal1->y - wal0->y != dy ||
6352                             wal1p2->x - wal0p2->x != dx || wal1p2->y - wal0p2->y != dy)
6353                     {
6354                         pos.x = wal0->x + (wal0p2->x - wal0->x)/4;
6355                         pos.y = wal0->y + (wal0p2->y - wal0->y)/4;
6356                         pos.z = getflorzofslope(sectorofwall(wal0-(uwalltype *)wall), pos.x, pos.y);
6357 
6358                         if (!delayerr)
6359                             message("Outer wall coordinates must coincide for both components");
6360                         OSD_Printf("wal0:%d (%d,%d)--(%d,%d)\n",(int)(wal0-(uwalltype *)wall),
6361                                    wal0->x,wal0->y, wal0p2->x,wal0p2->y);
6362                         OSD_Printf("wal1:%d (%d,%d)--(%d,%d)\n",(int)(wal1-(uwalltype *)wall),
6363                                    wal1->x,wal1->y, wal1p2->x,wal1p2->y);
6364 
6365                         goto end_join_sectors;
6366                     }
6367                 }
6368 
6369                 if (delayerr)
6370                     goto end_join_sectors;
6371 
6372                 if (joinstat == 3)
6373                 {
6374                     char askchars[2] = {'1', 'v'};
6375 
6376                     // now is a good time to ask...
6377                     for (comp=0; comp<2; comp++)
6378                         for (k=0; k<collnumsects[comp]; k++)
6379                             fillsector_notrans(collsectlist[comp][k], comp==0 ? 159 : editorcolors[11]);
6380 
6381                     fade_editor_screen(editorcolors[11] | (159<<8));
6382 
6383                     char buffer[128];
6384                     Bsnprintf(buffer, sizeof(buffer), "Z differences | yellow ceiling w/ blue floor: %d, vice versa: %d",
6385                               compcfz[YAX_CEILING][0]-compcfz[YAX_FLOOR][1],
6386                               compcfz[YAX_CEILING][1]-compcfz[YAX_FLOOR][0]);
6387                     printext16(8, ydim-STATUS2DSIZ2-12, editorcolors[15], -1, buffer, 0);
6388 
6389                     askres = editor_ask_function("Connect yellow ceiling w/ blue floor (1) or (v)ice versa?", askchars, 2);
6390                     if (askres==-1)
6391                         goto end_join_sectors;
6392                     joinstat &= (1<<askres);
6393                 }
6394 
6395                 joinstat--;  // 0:ceil(0)<->flor(1), 1:ceil(1)<->flor(0)
6396 
6397                 dz = compcfz[1][!joinstat] - compcfz[0][joinstat];
6398                 needsdisp = (dx || dy || dz);
6399 
6400                 if (needsdisp)
6401                 {
6402                     // a component is more likely to be displaced if it's not
6403                     // extended on the non-joining side
6404                     movestat = (!(compstat[0][!joinstat]&1)) | ((!(compstat[1][joinstat]&1))<<1);
6405                     if (!movestat)
6406                     {
6407                         movestat = 3;
6408 //                        message("Internal error while TROR-joining: movestat inconsistent!");
6409 //                        goto end_join_sectors;
6410                     }
6411 
6412                     if (movestat==3)
6413                     {
6414                         char askchars[2] = {'y', 'b'};
6415 
6416                         for (comp=0; comp<2; comp++)
6417                             for (k=0; k<collnumsects[comp]; k++)
6418                                 fillsector_notrans(collsectlist[comp][k], comp==0 ? 159 : editorcolors[11]);
6419 
6420                         fade_editor_screen(editorcolors[11] | (159<<8));
6421                         askres = editor_ask_function("Move (y)ellow or (b)lue component?", askchars, 2);
6422                         if (askres==-1)
6423                             goto end_join_sectors;
6424                         movestat &= (1<<askres);
6425                     }
6426 
6427                     movestat--;  // 0:move 1st, 1:move 2nd component
6428                     if (movestat==1)
6429                         dx*=-1, dy*=-1, dz*=-1;
6430 
6431                     moveonwp = 0;
6432                     if (onwisvalid())
6433                     {
6434                         static int16_t ocollsectlist[MAXSECTORS];
6435                         static uint8_t tcollbitmap[(MAXSECTORS+7)>>3];
6436                         int16_t ocollnumsects=collnumsects[movestat], tmpsect;
6437 
6438                         Bmemcpy(ocollsectlist, collsectlist[movestat], ocollnumsects*sizeof(int16_t));
6439                         Bmemset(tcollbitmap, 0, sizeof(tcollbitmap));
6440 
6441                         for (k=0; k<ocollnumsects; k++)
6442                             for (WALLS_OF_SECTOR(ocollsectlist[k], j))
6443                             {
6444                                 if (onextwall[j] < 0)
6445                                     continue;
6446 
6447                                 tmpsect = sectorofwall(onextwall[j]);
6448                                 sectors_components(1, &tmpsect, 1,0);
6449 
6450                                 for (m=0; m<(numsectors+7)>>3; m++)
6451                                     tcollbitmap[m] |= collsectbitmap[0][m];
6452                                 moveonwp = 1;
6453                             }
6454 
6455                         if (moveonwp)
6456                         {
6457                             int32_t movecol = movestat==0 ? 159 : editorcolors[11];
6458                             for (i=0; i<numsectors; i++)
6459                                 if (tcollbitmap[i>>3]&pow2char[i&7])
6460                                     fillsector_notrans(i, editorcolors[12]);
6461 
6462                             fade_editor_screen(editorcolors[12] | (movecol<<8));
6463                             moveonwp = ask_if_sure("Also move formerly wall-connected sectors?",0);
6464                             if (moveonwp==-1)
6465                                 goto end_join_sectors;
6466                         }
6467                     }
6468 
6469                     // now need to collect them wrt. the nextsector but also
6470                     // the yax-nextsector relation
6471                     if (highlighted_sectors_components(1,moveonwp) != 2)
6472                     {
6473                         message("Must not have TROR connections between the two components");
6474                         goto end_join_sectors;
6475                     }
6476 
6477                     // displace!
6478                     for (k=0; k<collnumsects[movestat]; k++)
6479                     {
6480                         i = collsectlist[movestat][k];
6481 
6482                         sector[i].floorz += dz;
6483                         sector[i].ceilingz += dz;
6484 
6485                         for (WALLS_OF_SECTOR(i, j))
6486                         {
6487                             wall[j].x += dx;
6488                             wall[j].y += dy;
6489                         }
6490 
6491                         for (j=headspritesect[i]; j>=0; j=nextspritesect[j])
6492                         {
6493                             sprite[j].x += dx;
6494                             sprite[j].y += dy;
6495                             sprite[j].z += dz;
6496                         }
6497                     }
6498 
6499                     // restore old components, i.e. only the bunch sectors
6500                     highlighted_sectors_components(0,0);
6501 
6502                 }  // end if (needsdisp)
6503 
6504                 /*** construct the YAX connection! ***/
6505                 for (comp=0; comp<2; comp++)
6506                 {
6507                     // walls
6508                     for (j=0; j<numowals; j++)
6509                         yax_setnextwall(outerwall[comp][j], comp^joinstat, outerwall[!comp][j]);
6510 
6511                     // sectors
6512                     for (k=0; k<collnumsects[comp]; k++)
6513                     {
6514                         i = collsectlist[comp][k];
6515                         yax_setbunch(i, comp^joinstat, numyaxbunches);
6516                         SECTORFLD(i,stat, comp^joinstat) &= ~1;  // no plax
6517 
6518                         // restore red walls AFTER setting nextwalls
6519                         // (see checksectorpointer() for why)
6520                         for (WALLS_OF_SECTOR(i, j))
6521                             if (wall[j].nextwall < 0)
6522                                 checksectorpointer(j, i);
6523                     }
6524                 }
6525 
6526                 reset_highlightsector();
6527 
6528                 yax_update(0);
6529                 yax_updategrays(pos.z);
6530 
6531                 message("Joined highlighted sectors to new bunch %d", numyaxbunches);
6532                 asksave = 1;
6533             }
6534             else
6535 #endif  // defined YAX_ENABLE
6536             if (joinsector[0] < 0)
6537             {
6538                 int32_t numjoincandidates = 0;
6539                 char *origframe=NULL;
6540 
6541                 for (i=0; i<numsectors; i++)
6542                 {
6543                     YAX_SKIPSECTOR(i);
6544                     numjoincandidates += (inside_editor_curpos(i) == 1);
6545                 }
6546 
6547                 for (i=0; i<numsectors; i++)
6548                 {
6549                     YAX_SKIPSECTOR(i);
6550                     if (inside_editor_curpos(i) == 1)
6551                     {
6552                         if (numjoincandidates > 1)
6553                         {
6554                             if (!bakframe_fillandfade(&origframe, i, "Use this as first joining sector? (Y/N)"))
6555                                 continue;
6556                         }
6557 
6558                         joinsector[0] = i;
6559                         printmessage16("Join sector - press J again on sector to join with.");
6560                         break;
6561                     }
6562                 }
6563 
6564                 Xfree(origframe);
6565             }
6566             else
6567             {
6568                 char *origframe = NULL;
6569                 int32_t numjoincandidates = 0;
6570 
6571                 joinsector[1] = -1;
6572 
6573                 for (i=0; i<numsectors; i++)
6574                 {
6575                     YAX_SKIPSECTOR(i);
6576                     numjoincandidates += (inside_editor_curpos(i) == 1);
6577                 }
6578 
6579                 for (i=0; i<numsectors; i++)
6580                 {
6581                     YAX_SKIPSECTOR(i);
6582 
6583                     if (inside_editor_curpos(i) == 1)
6584                     {
6585                         if (numjoincandidates > 1)
6586                         {
6587                             if (!bakframe_fillandfade(&origframe, i, "Use this as second joining sector? (Y/N)"))
6588                                 continue;
6589 
6590                             DO_FREE_AND_NULL(origframe);
6591                         }
6592 
6593                         joinsector[1] = i;
6594 
6595                         const int s1to0wall = find_nextwall(i, joinsector[0]);
6596                         const int s0to1wall = s1to0wall == -1 ? -1 : wall[s1to0wall].nextwall;
6597 #ifdef YAX_ENABLE
6598                         int16_t jbn[2][2];  // [join index][c/f]
6599 
6600                         for (k=0; k<2; k++)
6601                             yax_getbunches(joinsector[k], &jbn[k][YAX_CEILING], &jbn[k][YAX_FLOOR]);
6602 #endif
6603                         // pressing J into the same sector is the same as saying 'no'
6604                         //                     v----------------v
6605                         if (s1to0wall == -1 && i != joinsector[0])
6606                         {
6607                             int32_t good = 1;
6608 #ifdef YAX_ENABLE
6609                             if (jbn[0][0]>=0 || jbn[0][1]>=0 || jbn[1][0]>=0 || jbn[1][1]>=0)
6610                             {
6611                                 message("Joining non-adjacent extended sectors not allowed!");
6612                                 good = 0;
6613                             }
6614 #endif
6615                             if (!m32_script_expertmode)
6616                             {
6617                                 message("Joining non-adjacent disabled in non-expert mode");
6618                                 good = 0;
6619                             }
6620 
6621                             if (!good)
6622                             {
6623                                 joinsector[0] = joinsector[1] = -1;
6624                                 goto end_join_sectors;
6625                             }
6626 
6627                             {
6628                                 fillsector_notrans(i, editorcolors[9]);
6629                                 fillsector_notrans(joinsector[0], editorcolors[9]);
6630                                 fade_editor_screen(editorcolors[9]);
6631 
6632                                 if (!ask_if_sure("Really join non-adjacent sectors? (Y/N)", 0))
6633                                     joinsector[1] = joinsector[0];
6634                             }
6635                         }
6636 #ifdef YAX_ENABLE
6637                         // unequal bunchnums (bitmap): 1:above, 2:below
6638                         int uneqbn =
6639                             (jbn[0][YAX_CEILING] != jbn[1][YAX_CEILING]) |
6640                             ((jbn[0][YAX_FLOOR] != jbn[1][YAX_FLOOR])<<1);
6641 
6642                         if (uneqbn)
6643                         {
6644                             const int32_t cf=YAX_FLOOR;
6645                             int32_t whybad=0;
6646 
6647                             if (uneqbn == 1)
6648                             {
6649                                 OSD_Printf("Can't join two sectors with different ceiling bunchnums."
6650                                            " To make them equal, join their upper neighbor's floors.\n");
6651                                 printmessage16("Can't join two sectors with different ceiling bunchnums. See OSD");
6652                                 joinsector[0] = joinsector[1] = -1;
6653                                 goto end_join_sectors;
6654                             }
6655                             if (s0to1wall < 0)
6656                             {
6657                                 printmessage16("INTERNAL ERROR: nextwalls inconsistent!");
6658                                 joinsector[0] = joinsector[1] = -1;
6659                                 goto end_join_sectors;
6660                             }
6661 
6662                             // both must be extended
6663                             if (jbn[0][cf]<0 || jbn[1][cf]<0)
6664                                 uneqbn &= ~(1<<cf), whybad|=1;
6665                             // if any sloped, can't join
6666                             if ((SECTORFLD(joinsector[0],stat, cf)&2) || (SECTORFLD(joinsector[1],stat, cf)&2))
6667                                 uneqbn &= ~(1<<cf), whybad|=2;
6668                             // if on unequal heights, can't join either
6669                             if (SECTORFLD(joinsector[0],z, cf) != SECTORFLD(joinsector[1],z, cf))
6670                                 uneqbn &= ~(1<<cf), whybad|=4;
6671 
6672                             // check whether the lower neighbors have a red-wall link to each other
6673                             const int jsynw[2] = {
6674                                 yax_getnextwall(s0to1wall, cf),
6675                                 yax_getnextwall(s1to0wall, cf)
6676                             };
6677 
6678                             if (jsynw[0]<0 || jsynw[1]<0)  // this shouldn't happen
6679                             {
6680                                 uneqbn &= ~(1<<cf), whybad|=8;
6681                             }
6682                             else if (wall[jsynw[1]].nextwall != jsynw[0])
6683                             {
6684 //                                if (find_nextwall(sectorofwall(jsynw[1]), sectorofwall(jsynw[0])) < 0)
6685                                     uneqbn &= ~(1<<cf), whybad|=16;
6686                             }
6687 
6688                             if ((uneqbn&2)==0)
6689                             {
6690 #if 0
6691                                 if (whybad==1+8 && jbn[0][cf]>=0 && jbn[1][cf]<0)
6692                                 {
6693                                     // 1st join sector extended, 2nd not... let's see
6694                                     // if the latter is inner to the former one
6695 
6696                                     int32_t lowerstartsec = yax_vnextsec(s0to1wall, cf);
6697 
6698                                     m = (lowerstartsec < 0)<<1;
6699                                     for (WALLS_OF_SECTOR(joinsector[1], k))
6700                                     {
6701                                         if (m) break;
6702 
6703                                         m |= (wall[k].nextsector>=0 && wall[k].nextsector != joinsector[0]);
6704                                         m |= (wall[k].nextwall>=0 && yax_vnextsec(wall[k].nextwall, cf)!=lowerstartsec)<<1;
6705                                     }
6706 
6707                                     if (m==0)
6708                                     {
6709                                         yax_setbunch(joinsector[1], YAX_FLOOR, jbn[0][cf]);
6710                                         yax_update(0);
6711                                         yax_updategrays(pos.z);
6712                                         asksave = 1;
6713 
6714                                         printmessage16("Added sector %d's floor to bunch %d",
6715                                                        joinsector[1], jbn[0][cf]);
6716                                     }
6717                                     else
6718                                     {
6719                                         if (m&1)
6720                                         {
6721                                             message("Can't add sector %d's floor to bunch %d: not inner to sector %d",
6722                                                     joinsector[1], jbn[0][cf], joinsector[0]);
6723                                         }
6724                                         else // if (m&2)
6725                                         {
6726                                             message("Can't add sector %d's floor to bunch %d: must have lower neighbor",
6727                                                     joinsector[1], jbn[0][cf]);
6728                                         }
6729                                     }
6730                                 }
6731                                 else
6732 #endif
6733                                 {
6734                                     if (whybad&1)
6735                                         message("Can't make floor bunchnums equal: both floors must be extended");
6736                                     else if (whybad&2)
6737                                         message("Can't make floor bunchnums equal: both floors must be non-sloped");
6738                                     else if (whybad&4)
6739                                         message("Can't make floor bunchnums equal: both floors must have equal height");
6740                                     else if (whybad&8)
6741                                         message("Can't make floor bunchnums equal: INTERNAL ERROR");
6742                                     else if (whybad&16)
6743                                         message("Can't make floor bunchnums equal: lower neighbors must be linked");
6744                                 }
6745                             }
6746                             else
6747                             {
6748                                 int32_t vcf, newbn, ynw;
6749 
6750                                 // we're good to go for making floor bunchnums equal
6751                                 for (SECTORS_OF_BUNCH(jbn[1][cf], YAX_FLOOR, k))
6752                                     yax_setbunch(k, YAX_FLOOR, jbn[0][cf]);
6753                                 for (SECTORS_OF_BUNCH(jbn[1][cf], YAX_CEILING, k))
6754                                     yax_setbunch(k, YAX_CEILING, jbn[0][cf]);
6755 
6756                                 yax_update(0);
6757                                 // now we can iterate the sectors with the new bunchnums
6758                                 newbn = yax_getbunch(joinsector[0], cf);
6759 
6760                                 // clear all yax-nextwall links on walls that are inside the bunch
6761                                 for (vcf=0; vcf<2; vcf++)
6762                                     for (SECTORS_OF_BUNCH(newbn, vcf, k))
6763                                         for (WALLS_OF_SECTOR(k, m))
6764                                         {
6765                                             ynw = yax_getnextwall(m, vcf);
6766                                             if (ynw < 0 || wall[m].nextsector < 0)
6767                                                 continue;
6768 
6769                                             if (yax_getbunch(wall[m].nextsector, vcf) == newbn)
6770                                             {
6771                                                 yax_setnextwall(ynw, !vcf, -1);
6772                                                 yax_setnextwall(m, vcf, -1);
6773                                             }
6774                                         }
6775 
6776                                 // shouldn't be needed again for the editor, but can't harm either:
6777                                 yax_update(0);
6778                                 yax_updategrays(pos.z);
6779 
6780                                 printmessage16("Made sector %d and %d floor bunchnums equal",
6781                                                joinsector[0], joinsector[1]);
6782                                 asksave = 1;
6783                             }
6784 
6785                             joinsector[0] = joinsector[1] = -1;
6786                             goto end_join_sectors;
6787                         }
6788 #endif
6789                         break;
6790                     }
6791                 }
6792 
6793                 if (joinsector[1] < 0 || joinsector[0] == joinsector[1])
6794                 {
6795                     printmessage16("No sectors joined.");
6796                     joinsector[0] = -1;
6797                     goto end_join_sectors;
6798                 }
6799 
6800 
6801                 for (i=0; i<numwalls; i++)
6802                     editwall[i>>3] &= ~pow2char[i&7];
6803 
6804                 newnumwalls = numwalls;
6805 
6806                 for (k=0; k<2; k++)
6807                 {
6808                     for (WALLS_OF_SECTOR(joinsector[k], j))
6809                     {
6810                         int32_t loopnum=MAXWALLS*2;
6811 
6812                         if (editwall[j>>3]&pow2char[j&7])
6813                             continue;
6814 
6815                         if (wall[j].nextsector == joinsector[1-k])
6816                         {
6817                             editwall[j>>3] |= 1<<(j&7);
6818                             continue;
6819                         }
6820 
6821                         i = j;
6822                         m = newnumwalls;
6823                         int joink = k;
6824                         do
6825                         {
6826                             if (newnumwalls >= MAXWALLS + M32_FIXME_WALLS)
6827                             {
6828                                 message("Joining sectors failed: not enough space beyond wall[]");
6829                                 joinsector[0] = -1;
6830                                 newnumwalls = -1;
6831 
6832                                 for (i=0; i<numwalls; i++)
6833                                     editwall[i>>3] &= ~pow2char[i&7];
6834 
6835                                 goto end_join_sectors;
6836                             }
6837 
6838                             Bmemcpy(&wall[newnumwalls], &wall[i], sizeof(walltype));
6839 #ifdef YAX_ENABLE
6840                             yax_fixreverselinks(newnumwalls, newnumwalls);
6841 #endif
6842                             wall[newnumwalls].point2 = newnumwalls+1;
6843                             newnumwalls++;
6844 
6845                             editwall[i>>3] |= 1<<(i&7);
6846 
6847                             i = wall[i].point2;
6848                             if (wall[i].nextsector == joinsector[1-joink])
6849                             {
6850                                 i = NEXTWALL(i).point2;
6851                                 joink = 1 - joink;
6852                             }
6853 
6854                             loopnum--;
6855                         }
6856                         while (loopnum>0 && ((editwall[i>>3]&pow2char[i&7])==0)
6857                                    && (wall[i].nextsector != joinsector[1-joink]));
6858 
6859                         wall[newnumwalls-1].point2 = m;
6860 
6861                         if (loopnum==0)
6862                         {
6863                             message("internal error while joining sectors: infloop!");
6864                             newnumwalls = -1;
6865                         }
6866                     }
6867                 }
6868 
6869                 if (newnumwalls > numwalls)
6870                 {
6871                     Bmemcpy(&sector[numsectors], &sector[joinsector[0]], sizeof(sectortype));
6872                     sector[numsectors].wallptr = numwalls;
6873                     sector[numsectors].wallnum = newnumwalls-numwalls;
6874 
6875                     //fix sprites
6876                     for (i=0; i<2; i++)
6877                     {
6878                         j = headspritesect[joinsector[i]];
6879                         while (j != -1)
6880                         {
6881                             k = nextspritesect[j];
6882                             changespritesect(j, numsectors);
6883                             j = k;
6884                         }
6885                     }
6886 
6887                     numsectors++;
6888 
6889                     for (i=numwalls; i<newnumwalls; i++)
6890                     {
6891                         if (wall[i].nextwall >= 0)
6892                         {
6893                             NEXTWALL(i).nextwall = i;
6894                             NEXTWALL(i).nextsector = numsectors-1;
6895                         }
6896                     }
6897 
6898                     numwalls = newnumwalls;
6899                     newnumwalls = -1;
6900 
6901                     // clean out nextwall links for deletesector
6902                     for (k=0; k<2; k++)
6903                         for (WALLS_OF_SECTOR(joinsector[k], j))
6904                         {
6905                             wall[j].nextwall = wall[j].nextsector = -1;
6906 #ifdef YAX_ENABLE
6907                             yax_setnextwall(j, YAX_CEILING, -1);
6908                             yax_setnextwall(j, YAX_FLOOR, -1);
6909 #endif
6910                         }
6911 
6912                     deletesector(joinsector[0]);
6913                     if (joinsector[0] < joinsector[1])
6914                         joinsector[1]--;
6915                     deletesector(joinsector[1]);
6916 
6917                     printmessage16("Sectors joined.");
6918                     mkonwinvalid();
6919                     asksave = 1;
6920 #ifdef YAX_ENABLE
6921                     yax_update(0);
6922                     yax_updategrays(pos.z);
6923 #endif
6924                 }
6925 
6926                 joinsector[0] = -1;
6927             }
6928         }
6929 end_join_sectors:
6930 
6931 // PK
6932         for (i=0x02; i<=0x0b; i++)  // keys '1' to '0' on the upper row
6933             if (keystatus[i])
6934             {
6935                 prefixarg = prefixtiles[i-2];
6936                 break;
6937             }
6938 
6939         if (eitherALT && keystatus[sc_S]) //ALT-S
6940         {
6941             keystatus[sc_S] = 0;
6942 
6943             if (linehighlight >= 0 && wall[linehighlight].nextwall == -1)
6944             {
6945                 newnumwalls = whitelinescan(sectorofwall(linehighlight), linehighlight);
6946                 if (newnumwalls < numwalls)
6947                 {
6948                     printmessage16("Can't make a sector out there.");
6949                     newnumwalls = -1;
6950                 }
6951                 else if (newnumwalls > MAXWALLS)
6952                 {
6953                     printmessage16("Making new sector from inner loop would exceed wall limits.");
6954                     newnumwalls = -1;
6955                 }
6956                 else
6957                 {
6958                     for (i=numwalls; i<newnumwalls; i++)
6959                     {
6960                         NEXTWALL(i).nextwall = i;
6961                         NEXTWALL(i).nextsector = numsectors;
6962 #ifdef YAX_ENABLE
6963                         yax_setnextwall(i, YAX_CEILING, -1);
6964                         yax_setnextwall(i, YAX_FLOOR, -1);
6965 #endif
6966                     }
6967 #ifdef YAX_ENABLE
6968                     yax_setbunches(numsectors, -1, -1);
6969                     yax_update(0);
6970                     yax_updategrays(pos.z);
6971 #endif
6972                     numwalls = newnumwalls;
6973                     newnumwalls = -1;
6974                     numsectors++;
6975 
6976                     asksave = 1;
6977                     printmessage16("Inner loop made into new sector.");
6978                 }
6979             }
6980         }
6981         else if (keystatus[sc_S])  //S
6982         {
6983             int16_t sucksect = -1;
6984 
6985             keystatus[sc_S] = 0;
6986 
6987             for (i=0; i<numsectors; i++)
6988             {
6989                 YAX_SKIPSECTOR(i);
6990                 if (inside_editor_curpos(i) == 1)
6991                 {
6992                     sucksect = i;
6993                     break;
6994                 }
6995             }
6996 
6997             if (sucksect >= 0)
6998             {
6999                 dax = mousxplc;
7000                 day = mousyplc;
7001                 if (gridlock && grid > 0)
7002                     locktogrid(&dax, &day);
7003 
7004                 i = insert_sprite_common(sucksect, dax, day);
7005 
7006                 if (i < 0)
7007                     printmessage16("Couldn't insert sprite.");
7008                 else
7009                 {
7010                     sprite[i].z = getflorzofslope(sucksect,dax,day);
7011 // PK
7012                     if (prefixarg)
7013                     {
7014                         sprite[i].picnum = prefixarg;
7015                         sprite[i].xrepeat = sprite[i].yrepeat = 48;
7016                         prefixarg = 0;
7017                     }
7018                     else handle_sprite_in_clipboard(i);
7019 
7020                     if (tilesiz[sprite[i].picnum].y >= 32)
7021                         sprite[i].cstat |= 1;
7022 
7023                     correct_sprite_yoffset(i);
7024 
7025                     printmessage16("Sprite inserted.");
7026 
7027                     asksave = 1;
7028 
7029                     VM_OnEvent(EVENT_INSERTSPRITE2D, i);
7030                 }
7031             }
7032         }
7033 
7034         if (keystatus[sc_C])  // C (make circle of points)
7035         {
7036             if (highlightsectorcnt > 0)
7037                 duplicate_selected_sectors();
7038             else if (highlightcnt > 0)
7039                 duplicate_selected_sprites();
7040             else if (circlewall >= 0)
7041             {
7042                 circlewall = -1;
7043             }
7044             else if (!m32_sideview)
7045             {
7046                 if (linehighlight >= 0)
7047                 {
7048 #if 0 //def YAX_ENABLE
7049                     j = linehighlight;
7050 
7051                     if (yax_islockedwall(j) ||
7052                             (wall[j].nextwall >= 0 && yax_islockedwall(wall[j].nextwall)))
7053                         printmessage16("Can't make circle in wall constrained by sector extension.");
7054                     else
7055 #endif
7056                     circlewall = linehighlight;
7057                 }
7058             }
7059 
7060             keystatus[sc_C] = 0;
7061         }
7062 
7063         bad = keystatus[sc_Space] && (!m32_sideview || m32_sideelev == 512);  //Gotta do this to save lots of 3 spaces!
7064 
7065         if (keystatus[sc_Space] && !bad)
7066             message("Unable to create sectors in angled sideview mode.");
7067 
7068         if (circlewall >= 0)
7069         {
7070             int32_t tempint1, tempint2;
7071 
7072             x1 = wall[circlewall].x;
7073             y1 = wall[circlewall].y;
7074             x2 = POINT2(circlewall).x;
7075             y2 = POINT2(circlewall).y;
7076             x3 = mousxplc;
7077             y3 = mousyplc;
7078             adjustmark(&x3,&y3,newnumwalls);
7079             tempint1 = dmulscale4(x3-x2,x1-x3, y1-y3,y3-y2);
7080             tempint2 = dmulscale4(y1-y2,x1-x3, y1-y3,x2-x1);
7081 
7082             if (tempint2 != 0)
7083             {
7084                 int32_t goodtogo, err=0;
7085 
7086                 const int32_t centerx = ((x1+x2) + scale(y1-y2,tempint1,tempint2))>>1;
7087                 const int32_t centery = ((y1+y2) + scale(x2-x1,tempint1,tempint2))>>1;
7088                 const int32_t circlerad = ksqrt(dmulscale4(centerx-x1,centerx-x1, centery-y1,centery-y1))<<2;
7089 
7090                 const int32_t circleang1 = getangle(x1-centerx,y1-centery);
7091                 const int32_t circleang2 = getangle(x2-centerx,y2-centery);
7092 
7093                 const int32_t redw = (int32_t)(wall[circlewall].nextwall >= 0);
7094                 int32_t insdpoints = 0;
7095 
7096                 draw_cross(centerx, centery, 2, editorcolors[14]);
7097 
7098                 k = ((circleang2-circleang1)&2047);
7099                 if (mulscale4(x3-x1,y2-y1) < mulscale4(x2-x1,y3-y1))
7100                 {
7101                     k = -((circleang1-circleang2)&2047);
7102                 }
7103 
7104                 // XXX: Still too permissive for TROR insertion
7105                 goodtogo = (numwalls+(1+redw)*circlepoints <= MAXWALLS);
7106 
7107                 if (bad > 0 && goodtogo)
7108                 {
7109                     err = backup_drawn_walls(0);
7110 
7111                     if (err)
7112                     {
7113                         message("Error backing up drawn walls (code %d)!", err);
7114                         goodtogo = 0;
7115                     }
7116                 }
7117 
7118                 for (i=circlepoints; i>0; i--)
7119                 {
7120                     const int32_t ps = 2;
7121 
7122                     j = (circleang1 + scale(i,k,circlepoints+1))&2047;
7123                     dax = centerx + mulscale14(sintable[(j+512)&2047],circlerad);
7124                     day = centery + mulscale14(sintable[j],circlerad);
7125 
7126                     inpclamp(&dax, -editorgridextent, editorgridextent);
7127                     inpclamp(&day, -editorgridextent, editorgridextent);
7128 
7129                     if (bad > 0 && goodtogo)
7130                     {
7131                         int32_t inspts = M32_InsertPoint(circlewall, dax,day, -1, &circlewall);
7132 
7133                         if (inspts==0)
7134                         {
7135                             message("Wall limit exceeded while inserting points.");
7136                             goto end_circle_insertion;
7137                         }
7138                         else if (inspts >= 65536)
7139                         {
7140                             message("ERR: Inserted %d points for constr. wall (exp. %d; %d already ins'd)",
7141                                     inspts&65535, inspts>>16, insdpoints);
7142                             goto end_circle_insertion;
7143                         }
7144 
7145                         insdpoints += inspts;
7146                     }
7147 
7148                     draw_square(dax, day, ps, editorcolors[14]);
7149                 }
7150 
7151                 if (bad > 0 && goodtogo)
7152                     backup_drawn_walls(1);
7153 
7154                 if (bad > 0)
7155                 {
7156                     if (goodtogo)
7157                     {
7158                         asksave = 1;
7159                         printmessage16("Circle points inserted.");
7160 end_circle_insertion:
7161                         circlewall = -1;
7162                         mkonwinvalid();
7163                     }
7164                     else
7165                         printmessage16("Inserting circle points would exceed wall limit.");
7166                 }
7167             }
7168 
7169             bad = 0;
7170             keystatus[sc_Space] = 0;
7171         }
7172 
7173         if (bad > 0)   //Space bar test
7174         {
7175             keystatus[sc_Space] = 0;
7176             adjustmark(&mousxplc,&mousyplc,newnumwalls);
7177 
7178             if (checkautoinsert(mousxplc,mousyplc,newnumwalls) == 1)
7179             {
7180                 printmessage16("You must insert a point there first.");
7181                 bad = 0;
7182             }
7183         }
7184 
7185         if (bad > 0)  //Space
7186         {
7187             if (newnumwalls < numwalls)  // starting wall drawing
7188             {
7189                 if (numwalls >= MAXWALLS-1)
7190                 {
7191                     // whatever we do, we will need at least two new walls
7192                     printmessage16("Can't start sector drawing: wall limit reached.");
7193                     goto end_space_handling;
7194                 }
7195 
7196                 if (numsectors >= MAXSECTORS)
7197                 {
7198                     printmessage16("Can't start sector drawing: sector limit reached.");
7199                     goto end_space_handling;
7200                 }
7201 
7202                 firstx = mousxplc;
7203                 firsty = mousyplc;  //Make first point
7204                 newnumwalls = numwalls;
7205                 ovh.suckwall = -1;
7206                 ovh.split = 0;
7207 
7208                 init_new_wall1(&ovh.suckwall, mousxplc, mousyplc);
7209 
7210                 printmessage16("Sector drawing started.");
7211             }
7212             else  // 2nd point and up...
7213             {
7214                 //if not back to first point
7215                 if (firstx != mousxplc || firsty != mousyplc)  //nextpoint
7216                 {
7217                     if (newnumwalls>=MAXWALLS)
7218                     {
7219                         printmessage16("Inserting another point would exceed wall limit.");
7220                         goto end_space_handling;
7221                     }
7222 
7223                     j = 0;
7224                     for (i=numwalls; i<newnumwalls; i++)
7225                         if (mousxplc == wall[i].x && mousyplc == wall[i].y)
7226                             j = 1;
7227 
7228                     if (j == 0)  // if new point is not on a position of already drawn points
7229                     {
7230                         // on the second point insertion, check if starting to split a sector
7231                         if (newnumwalls == numwalls+1)
7232                         {
7233                             dax = (wall[numwalls].x+mousxplc)>>1;
7234                             day = (wall[numwalls].y+mousyplc)>>1;
7235 
7236                             for (i=0; i<numsectors; i++)
7237                             {
7238                                 YAX_SKIPSECTOR(i);
7239                                 if (inside(dax,day,i) != 1)
7240                                     continue;
7241 
7242                                 //check if first point at point of sector
7243                                 m = -1;
7244                                 for (WALLS_OF_SECTOR(i, k))
7245                                     if (wall[k].x==wall[numwalls].x && wall[k].y==wall[numwalls].y)
7246                                     {
7247                                         YAX_SKIPWALL(k);
7248 
7249                                         m = k;
7250                                         break;
7251                                     }
7252 
7253                                 // if the second insertion is not on a neighboring point of the first one...
7254                                 if (m>=0 && (POINT2(k).x != mousxplc || POINT2(k).y != mousyplc))
7255                                     if (wall[lastwall(k)].x != mousxplc || wall[lastwall(k)].y != mousyplc)
7256                                     {
7257                                         ovh.split = 1;
7258                                         ovh.splitsect = i;
7259                                         ovh.splitstartwall = m;
7260                                         break;
7261                                     }
7262                             }
7263                         }
7264 
7265                         //make new point
7266 
7267                         //make sure not drawing over old red line
7268                         bad = 0;
7269                         for (i=0; i<numwalls; i++)
7270                         {
7271                             YAX_SKIPWALL(i);
7272 
7273                             if (wall[i].nextwall >= 0)
7274                             {
7275                                 int32_t lastwalx = wall[newnumwalls-1].x;
7276                                 int32_t lastwaly = wall[newnumwalls-1].y;
7277 
7278                                 YAX_SKIPWALL(wall[i].nextwall);
7279 
7280                                 if (wall[i].x == mousxplc && wall[i].y == mousyplc)
7281                                     if (POINT2(i).x == lastwalx && POINT2(i).y == lastwaly)
7282                                         bad = 1;
7283                                 if (wall[i].x == lastwalx && wall[i].y == lastwaly)
7284                                     if (POINT2(i).x == mousxplc && POINT2(i).y == mousyplc)
7285                                         bad = 1;
7286                             }
7287                         }
7288 
7289                         if (bad == 0)
7290                         {
7291                             init_new_wall1(&ovh.suckwall, mousxplc, mousyplc);
7292                         }
7293                         else
7294                         {
7295                             printmessage16("You can't draw new lines over red lines.");
7296                             goto end_space_handling;
7297                         }
7298                     }
7299                 }
7300 
7301                 ////////// newnumwalls is at most MAXWALLS here //////////
7302 
7303                 //if not split and back to first point
7304                 if (!ovh.split && newnumwalls >= numwalls+3
7305                         && firstx==mousxplc && firsty==mousyplc)
7306                 {
7307                     wall[newnumwalls-1].point2 = numwalls;
7308 
7309                     if (ovh.suckwall == -1)  //if no connections to other sectors
7310                     {
7311                         k = -1;
7312                         for (i=0; i<numsectors; i++)
7313                         {
7314                             YAX_SKIPSECTOR(i);
7315 
7316                             if (inside(firstx,firsty,i) == 1)
7317                             {
7318                                 // if all points inside that one sector i,
7319                                 // will add an inner loop
7320                                 for (j=numwalls+1; j<newnumwalls; j++)
7321                                 {
7322                                     if (inside(wall[j].x, wall[j].y, i) != 1)
7323                                         goto check_next_sector;
7324                                 }
7325 
7326                                 k = i;
7327                                 break;
7328                             }
7329 check_next_sector: ;
7330                         }
7331 
7332                         if (k == -1)   //if not inside another sector either
7333                         {
7334                             //add island sector
7335                             if (clockdir(numwalls) == CLOCKDIR_CCW)
7336                                 flipwalls(numwalls,newnumwalls);
7337 
7338                             Bmemset(&sector[numsectors], 0, sizeof(sectortype));
7339                             sector[numsectors].extra = -1;
7340 
7341                             sector[numsectors].wallptr = numwalls;
7342                             sector[numsectors].wallnum = newnumwalls-numwalls;
7343                             sector[numsectors].ceilingz = -(32<<8);
7344                             sector[numsectors].floorz = (32<<8);
7345 
7346                             for (i=numwalls; i<newnumwalls; i++)
7347                                 copy_some_wall_members(i, -1, 1);
7348 #ifdef YAX_ENABLE
7349                             yax_setbunches(numsectors, -1, -1);
7350 #endif
7351                             headspritesect[numsectors] = -1;
7352                             numsectors++;
7353 
7354                             numwalls = newnumwalls;
7355                             newnumwalls = -1;
7356 
7357                             printmessage16("Created new sector %d", numsectors-1);
7358                         }
7359                         else       //else add loop to sector
7360                         {
7361                             int32_t ret = AddLoopToSector(k, NULL);
7362 
7363                             if (ret < 0)
7364                                 goto end_space_handling;
7365 #ifdef YAX_ENABLE
7366                             else if (ret > 0)
7367                                 printmessage16("Added inner loop to sector %d and made new inner sector", k);
7368                             else
7369 #endif
7370                                 printmessage16("Added inner loop to sector %d", k);
7371                             mkonwinvalid();
7372                             asksave = 1;
7373                         }
7374                     }
7375                     else  // if connected to at least one other sector
7376                     {
7377                         int16_t sucksect;
7378 
7379                         //add new sector with connections
7380 
7381                         if (clockdir(numwalls) == CLOCKDIR_CCW)
7382                             flipwalls(numwalls,newnumwalls);
7383 
7384                         for (i=numwalls; i<newnumwalls; i++)
7385                         {
7386                             copy_some_wall_members(i, ovh.suckwall, 1);
7387                             wall[i].cstat &= ~(1+16+32+64);
7388 
7389                             if (checksectorpointer(i, numsectors) > 0)
7390                             {
7391                                 // if new red line, prefer the other-side wall as base
7392                                 ovh.suckwall = wall[i].nextwall;
7393                             }
7394                         }
7395                         sucksect = sectorofwall(ovh.suckwall);
7396 
7397                         if (numsectors != sucksect)
7398                             Bmemcpy(&sector[numsectors], &sector[sucksect], sizeof(sectortype));
7399 
7400                         sector[numsectors].wallptr = numwalls;
7401                         sector[numsectors].wallnum = newnumwalls-numwalls;
7402 
7403                         sector[numsectors].extra = -1;
7404                         sector[numsectors].lotag = sector[numsectors].hitag = 0;
7405 
7406                         setslope(numsectors, YAX_CEILING, 0);
7407                         setslope(numsectors, YAX_FLOOR, 0);
7408 
7409                         sector[numsectors].ceilingpal = sector[numsectors].floorpal = 0;
7410 #ifdef YAX_ENABLE
7411                         yax_setbunches(numsectors, -1, -1);
7412 #endif
7413                         headspritesect[numsectors] = -1;
7414                         numsectors++;
7415 
7416                         numwalls = newnumwalls;
7417                         newnumwalls = -1;
7418 
7419                         message("Created new sector %d based on sector %d", numsectors-1, sucksect);
7420                     }
7421 
7422                     asksave = 1;
7423 #ifdef YAX_ENABLE
7424                     yax_update(0);
7425                     yax_updategrays(pos.z);
7426 #endif
7427 
7428                     goto end_space_handling;
7429                 }
7430                 ////////// split sector //////////
7431                 else if (ovh.split == 1)
7432                 {
7433                     int16_t danumwalls, splitendwall, doSectorSplit;
7434                     int16_t secondstartwall=-1;  // used only with splitting
7435                     int32_t expectedNumwalls = numwalls+2*(newnumwalls-numwalls-1), loopnum;
7436                     int32_t firstwallflag;
7437 #ifdef YAX_ENABLE
7438                     int16_t cb, fb;
7439 #endif
7440                     startwall = sector[ovh.splitsect].wallptr;
7441                     endwall = startwall + sector[ovh.splitsect].wallnum - 1;
7442 
7443                     firstwallflag = (startwall==ovh.splitstartwall || startwall==lastwall(ovh.splitstartwall));
7444 
7445 //                    OSD_Printf("numwalls: %d, newnumwalls: %d\n", numwalls, newnumwalls);
7446                     i = -1;
7447                     for (k=startwall; k<=endwall; k++)
7448                         if (wall[k].x == wall[newnumwalls-1].x && wall[k].y == wall[newnumwalls-1].y)
7449                         {
7450                             i = k;
7451                             break;
7452                         }
7453                     //           vvvv shouldn't happen, but you never know...
7454                     if (i==-1 || k==ovh.splitstartwall)
7455                         goto end_space_handling;
7456 
7457                     splitendwall = k;
7458                     doSectorSplit = (loopnumofsector(ovh.splitsect,ovh.splitstartwall)
7459                                      == loopnumofsector(ovh.splitsect,splitendwall));
7460 
7461                     if (expectedNumwalls > MAXWALLS)
7462                     {
7463                         printmessage16("%s would exceed wall limit.", bad==0 ?
7464                                        "Splitting sector" : "Joining sector loops");
7465                         newnumwalls--;
7466                         goto end_space_handling;
7467                     }
7468 #ifdef YAX_ENABLE
7469                     yax_getbunches(ovh.splitsect, &cb, &fb);
7470 
7471                     if ((cb>=0 && (sector[ovh.splitsect].ceilingstat&2))
7472                         || (fb>=0 && (sector[ovh.splitsect].floorstat&2)))
7473                     {
7474                         printmessage16("Sloped extended sectors cannot be split.");
7475                         newnumwalls--;
7476                         goto end_space_handling;
7477                     }
7478 #endif
7479                     ////////// common code for splitting/loop joining //////////
7480 
7481                     newnumwalls--;  //first fix up the new walls
7482                     for (i=numwalls; i<newnumwalls; i++)
7483                     {
7484                         copy_some_wall_members(i, startwall, 1);
7485                         wall[i].point2 = i+1;
7486                     }
7487 
7488                     danumwalls = newnumwalls;  //where to add more walls
7489 
7490                     if (doSectorSplit)
7491                     {
7492                         // Copy outer loop of first sector
7493                         if (do_while_copyloop1(splitendwall, ovh.splitstartwall, &danumwalls, numwalls))
7494                             goto split_not_enough_walls;
7495 
7496                         //Add other loops for 1st sector
7497                         i = loopnum = loopnumofsector(ovh.splitsect,ovh.splitstartwall);
7498 
7499                         for (j=startwall; j<=endwall; j++)
7500                         {
7501                             k = loopnumofsector(ovh.splitsect,j);
7502                             if (k == i)
7503                                 continue;
7504 
7505                             if (k == loopnum)
7506                                 continue;
7507 
7508                             i = k;
7509 
7510                             if (loopinside(wall[j].x,wall[j].y, numwalls) != 1)
7511                                 continue;
7512 
7513                             if (do_while_copyloop1(j, j, &danumwalls, danumwalls))
7514                                 goto split_not_enough_walls;
7515                         }
7516 
7517                         secondstartwall = danumwalls;
7518                     }
7519                     else
7520                     {
7521                         if (do_while_copyloop1(splitendwall, splitendwall, &danumwalls, -1))
7522                             goto split_not_enough_walls;
7523                     }
7524 
7525                     //copy split points for other sector backwards
7526                     for (j=newnumwalls; j>numwalls; j--)
7527                     {
7528                         Bmemcpy(&wall[danumwalls], &wall[j], sizeof(walltype));
7529                         wall[danumwalls].nextwall = -1;
7530                         wall[danumwalls].nextsector = -1;
7531                         wall[danumwalls].point2 = danumwalls+1;
7532 #ifdef YAX_ENABLE
7533                         yax_setnextwall(danumwalls,YAX_CEILING, -1);
7534                         yax_setnextwall(danumwalls,YAX_FLOOR, -1);
7535 #endif
7536                         danumwalls++;
7537                     }
7538 
7539                     //copy rest of loop next
7540                     if (doSectorSplit)
7541                     {
7542                         if (do_while_copyloop1(ovh.splitstartwall, splitendwall, &danumwalls, secondstartwall))
7543                             goto split_not_enough_walls;
7544                     }
7545                     else
7546                     {
7547                         if (do_while_copyloop1(ovh.splitstartwall, ovh.splitstartwall, &danumwalls, numwalls))
7548                             goto split_not_enough_walls;
7549                     }
7550 
7551                     //Add other loops for 2nd sector
7552                     i = loopnum = loopnumofsector(ovh.splitsect,ovh.splitstartwall);
7553 
7554                     for (j=startwall; j<=endwall; j++)
7555                     {
7556                         k = loopnumofsector(ovh.splitsect, j);
7557                         if (k==i)
7558                             continue;
7559 
7560                         if (doSectorSplit && k==loopnum)
7561                             continue;
7562                         if (!doSectorSplit && (k == loopnumofsector(ovh.splitsect,ovh.splitstartwall) ||
7563                                                k == loopnumofsector(ovh.splitsect,splitendwall)))
7564                             continue;
7565 
7566                         i = k;
7567 
7568                         // was loopinside(... , secondstartwall) != 1, but this way there are
7569                         // no duplicate or left-out loops (can happen with convoluted geometry)
7570                         if (doSectorSplit && (loopinside(wall[j].x,wall[j].y, numwalls) != 0))
7571                             continue;
7572 
7573                         if (do_while_copyloop1(j, j, &danumwalls, danumwalls))
7574                             goto split_not_enough_walls;
7575                     }
7576 
7577                     //fix all next pointers on old sector line
7578                     for (j=numwalls; j<danumwalls; j++)
7579                     {
7580 #ifdef YAX_ENABLE
7581 //                        if (doSectorSplit || (j!=numwalls && j!=danumwalls-1))
7582                             yax_fixreverselinks(j, j);
7583 #endif
7584                         if (wall[j].nextwall >= 0)
7585                         {
7586                             NEXTWALL(j).nextwall = j;
7587 
7588                             if (!doSectorSplit || j < secondstartwall)
7589                                 NEXTWALL(j).nextsector = numsectors;
7590                             else
7591                                 NEXTWALL(j).nextsector = numsectors+1;
7592                         }
7593                     }
7594 
7595                     //copy sector attributes & fix wall pointers
7596                     Bmemcpy(&sector[numsectors], &sector[ovh.splitsect], sizeof(sectortype));
7597                     sector[numsectors].wallptr = numwalls;
7598                     sector[numsectors].wallnum = (doSectorSplit?secondstartwall:danumwalls) - numwalls;
7599 
7600                     if (doSectorSplit)
7601                     {
7602                         //set all next pointers on split
7603                         for (j=numwalls; j<newnumwalls; j++)
7604                         {
7605                             m = secondstartwall+(newnumwalls-1-j);
7606                             wall[j].nextwall = m;
7607                             wall[j].nextsector = numsectors+1;
7608                             wall[m].nextwall = j;
7609                             wall[m].nextsector = numsectors;
7610                         }
7611 
7612                         Bmemcpy(&sector[numsectors+1], &sector[ovh.splitsect], sizeof(sectortype));
7613                         sector[numsectors+1].wallptr = secondstartwall;
7614                         sector[numsectors+1].wallnum = danumwalls-secondstartwall;
7615                     }
7616 
7617                     //fix sprites
7618                     j = headspritesect[ovh.splitsect];
7619                     while (j != -1)
7620                     {
7621                         k = nextspritesect[j];
7622                         if (!doSectorSplit || loopinside(sprite[j].x,sprite[j].y,numwalls) == 1)
7623                             changespritesect(j,numsectors);
7624                         //else if (loopinside(sprite[j].x,sprite[j].y,secondstartwall) == 1)
7625                         else  //Make sure no sprites get left out & deleted!
7626                             changespritesect(j,numsectors+1);
7627                         j = k;
7628                     }
7629 
7630                     numsectors += 1 + doSectorSplit;
7631 
7632                     k = danumwalls-numwalls;  //Back up number of walls of new sector for later
7633                     numwalls = danumwalls;
7634 
7635                     //clear out old sector's next pointers for clean deletesector
7636                     for (j=startwall; j<=endwall; j++)
7637                     {
7638 #ifdef YAX_ENABLE
7639                         // same thing for yax-nextwalls (only forward links!)
7640                         yax_setnextwall(j, YAX_CEILING, -1);
7641                         yax_setnextwall(j, YAX_FLOOR, -1);
7642 #endif
7643                         wall[j].nextwall = wall[j].nextsector = -1;
7644                     }
7645                     deletesector(ovh.splitsect);
7646 
7647                     //Check pointers
7648                     for (j=numwalls-k; j<numwalls; j++)
7649                     {
7650                         if (wall[j].nextwall >= 0)
7651                             checksectorpointer(wall[j].nextwall, wall[j].nextsector);
7652                         checksectorpointer(j, sectorofwall(j));
7653                     }
7654 
7655                     //k now safe to use as temp
7656 
7657                     if (numwalls==expectedNumwalls)
7658                     {
7659                         if (doSectorSplit && cb<0 && fb<0)
7660                         {
7661                             if (firstwallflag)
7662                             {
7663                                 int32_t rhsnew1stwall = sector[numsectors-2].wallptr;
7664                                 int32_t lhsotherwall = wall[rhsnew1stwall].nextwall;
7665 
7666                                 Bassert(lhsotherwall >= 0);
7667 
7668                                 setfirstwall(numsectors-2, lastwall(rhsnew1stwall));
7669                                 setfirstwall(numsectors-1, wall[lhsotherwall].point2);
7670                             }
7671                         }
7672 
7673                         message("%s", doSectorSplit ? "Sector split." : "Loops joined.");
7674                     }
7675                     else
7676                     {
7677                         message("%s WARNING: CREATED %d MORE WALLS THAN EXPECTED!",
7678                                 doSectorSplit ? "Sector split." : "Loops joined.",
7679                                 numwalls-expectedNumwalls);
7680                         // this would display 'num* out of bounds' but this corruption
7681                         // is almost as bad... (shouldn't happen anymore)
7682                         if (numcorruptthings < MAXCORRUPTTHINGS)
7683                             corruptthings[numcorruptthings++] = 0;
7684                         corruptlevel = 5;
7685                     }
7686 
7687                     if (0)
7688                     {
7689 split_not_enough_walls:
7690                         message("%s failed: not enough space beyond wall[]",
7691                                 doSectorSplit ? "Splitting sectors" : "Joining loops");
7692                     }
7693 
7694                     newnumwalls = -1;
7695                     asksave = 1;
7696 
7697                     mkonwinvalid();
7698 #ifdef YAX_ENABLE
7699                     yax_update(0);
7700                     yax_updategrays(pos.z);
7701 #endif
7702                 }
7703             }
7704         }
7705 end_space_handling:
7706 
7707         if (keystatus[sc_Enter]) //Left Enter
7708         {
7709             keystatus[sc_Enter] = 0;
7710             if (keystatus[sc_LeftShift] && keystatus[sc_LeftControl])  // LCtrl+LShift
7711             {
7712 #ifdef YAX_ENABLE
7713                 if (numyaxbunches == 0 ||
7714                     (fade_editor_screen(-1), ask_if_sure("Really check all wall pointers in TROR map?", 0)))
7715 #endif
7716                 {
7717                     printmessage16("CHECKING ALL POINTERS!");
7718                     for (i=0; i<numsectors; i++)
7719                     {
7720                         startwall = sector[i].wallptr;
7721                         for (j=startwall; j<numwalls; j++)
7722                             if (startwall > wall[j].point2)
7723                                 startwall = wall[j].point2;
7724                         sector[i].wallptr = startwall;
7725                     }
7726                     for (i=numsectors-2; i>=0; i--)
7727                         sector[i].wallnum = sector[i+1].wallptr-sector[i].wallptr;
7728                     sector[numsectors-1].wallnum = numwalls-sector[numsectors-1].wallptr;
7729 
7730                     for (i=0; i<numwalls; i++)
7731                     {
7732                         wall[i].nextsector = -1;
7733                         wall[i].nextwall = -1;
7734                     }
7735                     for (i=0; i<numsectors; i++)
7736                     {
7737                         for (WALLS_OF_SECTOR(i, j))
7738                             checksectorpointer(j, i);
7739                     }
7740                     printmessage16("ALL POINTERS CHECKED!");
7741                     asksave = 1;
7742                 }
7743             }
7744             else  // NOT (LCtrl + LShift)
7745             {
7746                 if (newnumwalls > numwalls)  // batch insert points
7747                 {
7748                     const int32_t numdrawnwalls = newnumwalls-numwalls;
7749                     vec2_t *point = (vec2_t *)tempxyar;  // [MAXWALLS][2]
7750                     int32_t insdpoints = 0;
7751 
7752                     // back up the points of the line strip
7753                     for (i=0; i<numdrawnwalls+1; i++)
7754                     {
7755                         point[i].x = wall[numwalls+i].x;
7756                         point[i].y = wall[numwalls+i].y;
7757                     }
7758 
7759                     newnumwalls = -1;
7760 
7761                     for (i=0; i<numdrawnwalls; i++)
7762                     {
7763                         char touchedwall[(MAXWALLS+7)>>3];
7764                         Bmemset(touchedwall, 0, sizeof(touchedwall));
7765 
7766                         for (j=numwalls-1; j>=0; j--)  /* j may be modified in loop */
7767                         {
7768                             YAX_SKIPWALL(j);
7769 
7770                             if ((touchedwall[j >> 3] & pow2char[j & 7])
7771                                 || (wall[j].nextwall >= 0 && (touchedwall[wall[j].nextwall >> 3] & pow2char[wall[j].nextwall & 7])))
7772                                 continue;
7773 
7774                             vec2_t pint;
7775 
7776                             if (!lineintersect2v(&wall[j].pos, &POINT2(j).pos, &point[i], &point[i + 1], &pint))
7777                                 continue;
7778 
7779                             if (vec2eq(&pint, &wall[j].pos) || vec2eq(&pint, &POINT2(j).pos))
7780                                 continue;
7781 
7782                             touchedwall[j>>3] |= pow2char[j&7];
7783 
7784                             if (wall[j].nextwall != -1)
7785                                 touchedwall[wall[j].nextwall>>3] |= pow2char[wall[j].nextwall&7];
7786 
7787                             int32_t inspts = M32_InsertPoint(j, pint.x, pint.y, -1, &j);  /* maybe modify j */
7788 
7789                             if (inspts==0)
7790                             {
7791                                 printmessage16("Wall limit exceeded while inserting points.");
7792                                 goto end_batch_insert_points;
7793                             }
7794                             else if (inspts >= 65536)
7795                             {
7796                                 message("ERR: Inserted %d points for constr. wall (exp. %d; %d already ins'd)",
7797                                         inspts&65535, inspts>>16, insdpoints);
7798                                 goto end_batch_insert_points;
7799                             }
7800 
7801                             insdpoints += inspts;
7802                         }
7803                     }
7804 
7805                     message("Batch-inserted %d points in total", insdpoints);
7806 end_batch_insert_points:
7807 
7808                     if (insdpoints != 0)
7809                     {
7810 #ifdef YAX_ENABLE
7811                         yax_updategrays(pos.z);
7812 #endif
7813                         mkonwinvalid_keeptempsect();
7814                         asksave = 1;
7815                     }
7816                 }
7817                 else if (linehighlight >= 0)
7818                 {
7819                     checksectorpointer(linehighlight,sectorofwall(linehighlight));
7820                     printmessage16("Checked pointers of highlighted line.");
7821                     asksave = 1;
7822                 }
7823             }
7824         }
7825 
7826         static int32_t backspace_last = 0;
7827 
7828         if (keystatus[sc_BackSpace]) //Backspace
7829         {
7830             keystatus[sc_BackSpace] = 0;
7831 
7832             if (newnumwalls >= numwalls)
7833             {
7834                 backspace_last = 1;
7835 
7836                 if (newnumwalls == numwalls+1 || keystatus[sc_LeftControl])  // LCtrl: delete all newly drawn walls
7837                     newnumwalls = -1;
7838                 else
7839                     newnumwalls--;
7840             }
7841             else if (backspace_last==0)
7842             {
7843                 graphicsmode += (1-2*(DOWN_BK(RUN) || keystatus[sc_RightShift]))+3;
7844                 graphicsmode %= 3;
7845                 printmessage16("2D mode textures %s",
7846                                 (graphicsmode == 2)?"enabled w/ animation":graphicsmode?"enabled":"disabled");
7847             }
7848         }
7849         else
7850             backspace_last = 0;
7851 
7852         if ((keystatus[sc_Delete]|keystatus[sc_D]) && eitherCTRL && numwalls > 0)  //sector delete
7853         {
7854             int32_t numdelsectors = 0;
7855             char *origframe=NULL;
7856 
7857 #ifdef YAX_ENABLE
7858             int16_t cb, fb;
7859             uint8_t bunchbitmap[(YAX_MAXBUNCHES+7)>>3];
7860             Bmemset(bunchbitmap, 0, sizeof(bunchbitmap));
7861 #endif
7862             keystatus[sc_Delete] = 0;
7863             keystatus[sc_D] = 0;
7864 
7865             for (i=0; i<numsectors; i++)
7866             {
7867                 YAX_SKIPSECTOR(i);
7868                 numdelsectors += (inside_editor_curpos(i) == 1);
7869             }
7870 
7871             for (i=0; i<numsectors; i++)
7872             {
7873                 if (highlightsectorcnt <= 0 || !keystatus[sc_LeftShift])  // LShift
7874                 {
7875                     YAX_SKIPSECTOR(i);
7876 
7877                     if (inside_editor_curpos(i) != 1)
7878                         continue;
7879                 }
7880 
7881                 k = 0;
7882                 if (highlightsectorcnt > 0)
7883                 {
7884                     // LShift: force highlighted sector deleting
7885                     if (keystatus[sc_LeftShift] || (hlsectorbitmap[i>>3]&pow2char[i&7]))
7886                     {
7887                         for (j=highlightsectorcnt-1; j>=0; j--)
7888                         {
7889 #ifdef YAX_ENABLE
7890                             yax_getbunches(highlightsector[j], &cb, &fb);
7891                             if (cb>=0) bunchbitmap[cb>>3] |= pow2char[cb&7];
7892                             if (fb>=0) bunchbitmap[fb>>3] |= pow2char[fb&7];
7893 #endif
7894                             deletesector(highlightsector[j]);
7895                             for (k=j-1; k>=0; k--)
7896                                 if (highlightsector[k] >= highlightsector[j])
7897                                     highlightsector[k]--;
7898                         }
7899 
7900                         printmessage16("Highlighted sectors deleted.");
7901                         mkonwinvalid();
7902                         k = 1;
7903                     }
7904                 }
7905 
7906                 if (k == 0)
7907                 {
7908                     if (numdelsectors > 1)
7909                     {
7910                         if (!bakframe_fillandfade(&origframe, i, "Delete this sector? (Y/N)"))
7911                             continue;
7912                     }
7913 
7914 #ifdef YAX_ENABLE
7915                     yax_getbunches(i, &cb, &fb);
7916                     if (cb>=0) bunchbitmap[cb>>3] |= pow2char[cb&7];
7917                     if (fb>=0) bunchbitmap[fb>>3] |= pow2char[fb&7];
7918 #endif
7919                     deletesector(i);
7920                     mkonwinvalid();
7921                     printmessage16("Sector deleted.");
7922                 }
7923 
7924                 Xfree(origframe);
7925 
7926 #ifdef YAX_ENABLE
7927                 for (j=0; j<numsectors; j++)
7928                 {
7929                     yax_getbunches(j, &cb, &fb);
7930                     if (cb>=0 && (bunchbitmap[cb>>3] & pow2char[cb&7]))
7931                         yax_setbunch(j, YAX_CEILING, -1);
7932                     if (fb>=0 && (bunchbitmap[fb>>3] & pow2char[fb&7]))
7933                         yax_setbunch(j, YAX_FLOOR, -1);
7934                 }
7935 #endif
7936                 reset_highlightsector();
7937                 reset_highlight();
7938 
7939                 newnumwalls = -1;
7940                 asksave = 1;
7941 #ifdef YAX_ENABLE
7942                 yax_update(0);
7943                 yax_updategrays(pos.z);
7944 #endif
7945                 break;
7946             }
7947         }
7948 
7949         if ((keystatus[sc_Delete]|keystatus[sc_D]) && (pointhighlight >= 0))
7950         {
7951             if ((pointhighlight&0xc000) == 16384)   //Sprite Delete
7952             {
7953                 deletesprite(pointhighlight&16383);
7954                 printmessage16("Sprite deleted.");
7955 
7956                 update_highlight();
7957                 asksave = 1;
7958             }
7959             else if (eitherSHIFT|keystatus[sc_D])
7960             {
7961                 deletewall(pointhighlight);
7962                 asksave = 1;
7963             }
7964             keystatus[sc_Delete] = 0;
7965             keystatus[sc_D] = 0;
7966         }
7967 
7968         if (keystatus[sc_Insert] || keystatus[sc_I])  //InsertPoint
7969         {
7970             if (highlightsectorcnt > 0)
7971                 duplicate_selected_sectors();
7972             else if (highlightcnt > 0)
7973                 duplicate_selected_sprites();
7974             else if (linehighlight2 >= 0)
7975             {
7976                 int16_t onewnumwalls = newnumwalls;
7977                 int32_t wallis2sided = (wall[linehighlight2].nextwall>=0);
7978 
7979                 int32_t err = backup_drawn_walls(0);
7980 
7981                 if (err)
7982                 {
7983                     message("Error backing up drawn walls (code %d)!", err);
7984                 }
7985                 else if (max(numwalls, onewnumwalls) >= MAXWALLS-wallis2sided)
7986                 {
7987                     printmessage16("Inserting point would exceed wall limit.");
7988                 }
7989                 else
7990                 {
7991                     getclosestpointonwall(m32_sideview?searchx:mousxplc, m32_sideview?searchy:mousyplc,
7992                                           linehighlight2, &dax,&day, 1);
7993                     i = linehighlight2;
7994                     if (m32_sideview)
7995                     {
7996                         int32_t y_p, d, dx, dy, frac;
7997 
7998                         dx = dax - m32_wallscreenxy[i][0];
7999                         dy = day - m32_wallscreenxy[i][1];
8000                         d = max(dx, dy);
8001                         y_p = (dy>dx);
8002 
8003                         if (d==0)
8004                             goto point_not_inserted;
8005 
8006                         frac = divscale24(d, m32_wallscreenxy[wall[i].point2][y_p]-m32_wallscreenxy[i][y_p]);
8007                         dax = POINT2(i).x - wall[i].x;
8008                         day = POINT2(i).y - wall[i].y;
8009                         dax = wall[i].x + mulscale24(dax,frac);
8010                         day = wall[i].y + mulscale24(day,frac);
8011                     }
8012 
8013                     adjustmark(&dax,&day, newnumwalls);
8014                     if ((wall[i].x == dax && wall[i].y == day) || (POINT2(i).x == dax && POINT2(i).y == day))
8015                     {
8016 point_not_inserted:
8017                         printmessage16("Point not inserted.");
8018                     }
8019                     else
8020                     {
8021                         int32_t insdpoints = M32_InsertPoint(linehighlight2, dax, day, onewnumwalls, NULL);
8022 
8023                         if (insdpoints == 0)
8024                         {
8025                             printmessage16("Inserting points would exceed wall limit.");
8026                             goto end_insert_points;
8027                         }
8028                         else if (insdpoints == 1)
8029                         {
8030                             printmessage16("Point inserted.");
8031                         }
8032                         else if (insdpoints > 1 && insdpoints < 65536)
8033                         {
8034                             message("Inserted %d points for constrained wall.", insdpoints);
8035                         }
8036                         else  // insdpoints >= 65536
8037                         {
8038                             message("Inserted %d points for constrained wall (expected %d, WTF?).",
8039                                     insdpoints&65535, insdpoints>>16);
8040                         }
8041 
8042 #ifdef YAX_ENABLE
8043                         yax_updategrays(pos.z);
8044 #endif
8045                         mkonwinvalid_keeptempsect();
8046                     }
8047                 }
8048 
8049 end_insert_points:
8050                 backup_drawn_walls(1);
8051 
8052                 asksave = 1;
8053             }
8054             keystatus[sc_Insert] = keystatus[sc_I] = 0;
8055         }
8056 
8057         /*j = 0;
8058         for(i=22-1;i>=0;i--) updatecrc16(j,kensig[i]);
8059         if ((j&0xffff) != 0xebf)
8060         {
8061         	printf("Don't screw with my name.\n");
8062         	Bexit(EXIT_SUCCESS);
8063         }*/
8064         //printext16(9L,336+9L,4,-1,kensig,0);
8065         //printext16(8L,336+8L,12,-1,kensig,0);
8066 
8067     nokeys:
8068 
8069         videoShowFrame(1);
8070         synctics = (int32_t) totalclock-lockclock;
8071         lockclock += synctics;
8072 
8073         if (keystatus[buildkeys[BK_MODE2D_3D]])
8074         {
8075             updatesector(pos.x,pos.y,&cursectnum);
8076             if (cursectnum >= 0)
8077                 keystatus[buildkeys[BK_MODE2D_3D]] = 2;
8078             else
8079                 printmessage16("Arrow must be inside a sector before entering 3D mode.");
8080         }
8081 
8082 // vvv PK ------------------------------------ (LShift) Ctrl-X: (prev) next map
8083 // this is copied from 'L' (load map), but without copying the highlighted sectors
8084 
8085         if (quickmapcycling && keystatus[sc_X])   //X
8086         {
8087 
8088             if (eitherCTRL)  //Ctrl
8089             {
8090                 if (asksave)
8091                     message("You have unsaved changes.");
8092                 else
8093                 {
8094                     int skip = 0;
8095                 nextmap:
8096                     //				bad = 0;
8097                     i = menuselect_auto(keystatus[sc_LeftShift] ? 0 : 1, skip); // LShift: prev map
8098                     if (i < 0)
8099                     {
8100                         if (i == -1)
8101                             message("No more map files.");
8102                         else if (i == -2)
8103                             message("No .MAP files found.");
8104                     }
8105                     else
8106                     {
8107                         if (LoadBoard(NULL, 4))
8108                         {
8109                             skip = 2;
8110                             goto nextmap;
8111                         }
8112 
8113                         RESET_EDITOR_VARS();
8114                         oposz = pos.z;
8115                     }
8116                     videoShowFrame(1);
8117                     keystatus[sc_Enter] = 0;
8118                     keystatus[sc_X]     = 0;
8119                     keystatus[sc_R]     = 0;
8120                 }
8121             }
8122         }
8123 // ^^^ PK ------------------------------------
8124 
8125         if (keystatus[sc_Escape] && joinsector[0] >= 0)
8126         {
8127             keystatus[sc_Escape]=0;
8128             joinsector[0]=-1;
8129             printmessage16("No sectors joined.");
8130         }
8131 
8132 CANCEL:
8133         if (keystatus[sc_Escape])
8134         {
8135             keystatus[sc_Escape] = 0;
8136 #if M32_UNDO
8137             _printmessage16("(N)ew, (L)oad, (S)ave, save (A)s, (T)est map, (U)ndo, (R)edo, (Q)uit");
8138 #else
8139             _printmessage16("(N)ew, (L)oad, (S)ave, save (A)s, (T)est map, (Q)uit");
8140 #endif
8141             printext16(16*8, ydim-STATUS2DSIZ2-12, editorcolors[15], -1, GetSaveBoardFilename(NULL), 0);
8142 
8143             videoShowFrame(1);
8144             keyFlushChars();
8145             bad = 1;
8146             while (bad == 1)
8147             {
8148                 char ch;
8149 
8150                 if (handleevents())
8151                 {
8152                     if (quitevent)
8153                         quitevent = 0;
8154                 }
8155                 idle();
8156 
8157                 ch = keyGetChar();
8158 
8159                 if (keystatus[sc_Escape])
8160                 {
8161                     keystatus[sc_Escape] = 0;
8162                     bad = 0;
8163                     // printmessage16("");
8164                 }
8165                 else if (ch == 'n' || ch == 'N')  //N
8166                 {
8167                     bad = 0;
8168 
8169                     if (ask_if_sure("Are you sure you want to start a new board? (Y/N)", 0))
8170                     {
8171                         int32_t bakstat=-1;
8172                         mapinfofull_t bakmap;
8173 
8174                         if (highlightsectorcnt > 0)
8175                             bakstat = backup_highlighted_map(&bakmap);
8176 
8177 //                        Bmemset(hlsectorbitmap, 0, sizeof(hlsectorbitmap));
8178 //                        highlightsectorcnt = -1;
8179 
8180                         highlightcnt = -1;
8181                         //Clear all highlights
8182                         Bmemset(show2dwall, 0, sizeof(show2dwall));
8183                         Bmemset(show2dsprite, 0, sizeof(show2dsprite));
8184 
8185                         for (i=0; i<MAXSECTORS; i++) sector[i].extra = -1;
8186                         for (i=0; i<MAXWALLS; i++) wall[i].extra = -1;
8187                         for (i=0; i<MAXSPRITES; i++) sprite[i].extra = -1;
8188 
8189                         RESET_EDITOR_VARS();
8190                         mkonwinvalid();
8191 
8192                         reset_default_mapstate();
8193 
8194                         Bstrcpy(boardfilename,"newboard.map");
8195                         CallExtLoadMap(boardfilename);
8196 #if M32_UNDO
8197                         map_undoredo_free();
8198 #endif
8199                         if (bakstat==0)
8200                         {
8201                             bakstat = restore_highlighted_map(&bakmap, 1);
8202                             if (bakstat == -1)
8203                             {
8204                                 message("Can't copy highlighted portion of old map: limits exceeded.");
8205                                 reset_highlightsector();
8206                             }
8207                         }
8208 
8209                         CheckMapCorruption(4, 0);
8210 
8211                     }
8212 
8213                     break;
8214                 }
8215                 else if (ch == 'l' || ch == 'L')  //L
8216                 {
8217                     bad = 0;
8218                     i = menuselect();
8219                     if (i < 0)
8220                     {
8221                         if (i == -2)
8222                             printmessage16("No .MAP files found.");
8223                     }
8224                     else
8225                     {
8226                         int32_t bakstat=-1, ret;
8227                         mapinfofull_t bakmap;
8228 
8229                         if (highlightsectorcnt > 0)
8230                             bakstat = backup_highlighted_map(&bakmap);
8231 
8232                         ret = LoadBoard(NULL, 4);
8233                         if (ret)
8234                         {
8235                             message("^13Invalid map format, nothing loaded (code %d).", ret);
8236                             if (bakstat==0)
8237                                 mapinfofull_free(&bakmap);
8238                         }
8239                         else
8240                         {
8241                             RESET_EDITOR_VARS();
8242                             oposz = pos.z;
8243 
8244                             if (bakstat==0)
8245                             {
8246                                 bakstat = restore_highlighted_map(&bakmap, 1);
8247                                 if (bakstat == -1)
8248                                 {
8249                                     message("Can't copy highlighted portion of old map: limits exceeded.");
8250                                     reset_highlightsector();
8251                                 }
8252                             }
8253                         }
8254                     }
8255                     videoShowFrame(1);
8256                     keystatus[sc_Enter] = 0;
8257                 }
8258                 else if (ch == 'a' || ch == 'A')  //A
8259                 {
8260                     int32_t corrupt = CheckMapCorruption(4, 0);
8261 
8262                     bad = 0;
8263 
8264                     // Back up full name.
8265                     Bstrcpy(selectedboardfilename, boardfilename);
8266 
8267                     // Get base name.
8268                     {
8269                         const char *basefn = getbasefn(boardfilename);
8270 
8271                         if (basefn != boardfilename)
8272                             Bmemmove(boardfilename, basefn, Bstrlen(basefn)+1);
8273                     }
8274 
8275                     i = 0;
8276                     while (boardfilename[i] != 0 && i < 64)
8277                         i++;
8278                     if (i >= 4 && boardfilename[i-4] == '.')
8279                         i -= 4;
8280                     boardfilename[i] = 0;
8281 
8282                     keyFlushChars();
8283                     while (bad == 0)
8284                     {
8285                         _printmessage16("%sSave as: ^011%s%s", corrupt>=4?"(map corrupt) ":"",
8286                                         boardfilename, ((int32_t) totalclock&32)?"_":"");
8287                         videoShowFrame(1);
8288 
8289                         if (handleevents())
8290                             quitevent = 0;
8291 
8292                         idle();
8293 
8294                         ch = keyGetChar();
8295 
8296                         if (keystatus[sc_Escape]) bad = 1;
8297                         else if (ch == 13) bad = 2;
8298                         else if (ch > 0)
8299                         {
8300                             if (i > 0 && (ch == 8 || ch == 127))
8301                             {
8302                                 i--;
8303                                 boardfilename[i] = 0;
8304                             }
8305                             else if (i < 40 && ch > 32 && ch < 128)
8306                             {
8307                                 boardfilename[i++] = ch;
8308                                 boardfilename[i] = 0;
8309                             }
8310                         }
8311                     }
8312 
8313                     if (bad == 1)
8314                     {
8315                         Bstrcpy(boardfilename, selectedboardfilename);
8316                         keystatus[sc_Escape] = 0;
8317                         printmessage16("Operation cancelled");
8318                         videoShowFrame(1);
8319                     }
8320                     else if (bad == 2)
8321                     {
8322                         char *slash;
8323 
8324                         keystatus[sc_Enter] = 0;
8325 
8326                         Bstrcpy(&boardfilename[i], ".map");
8327 
8328                         // Update full name with new basename.
8329                         slash = Bstrrchr(selectedboardfilename,'/');
8330                         Bstrcpy(slash ? slash+1 : selectedboardfilename, boardfilename);
8331 
8332                         SaveBoardAndPrintMessage(selectedboardfilename);
8333 
8334                         Bstrcpy(boardfilename, selectedboardfilename);
8335                         CallExtSetupMapFilename(boardfilename);
8336                     }
8337                     bad = 0;
8338                 }
8339                 else if (ch == 's' || ch == 'S')  //S
8340                 {
8341                     bad = 0;
8342 
8343                     if (CheckMapCorruption(4, 0)>=4)
8344                     {
8345                         fade_editor_screen(-1);
8346                         if (!ask_if_sure("Map is corrupt. Are you sure you want to save? (Y/N)", 0))
8347                             break;
8348                     }
8349 
8350                     SaveBoardAndPrintMessage(NULL);
8351 
8352                     videoShowFrame(1);
8353                 }
8354                 else if (ch == 't' || ch == 'T')
8355                 {
8356                     bad = 0;
8357                     test_map(0);
8358                 }
8359 #if M32_UNDO
8360                 else if (ch == 'u' || ch == 'U')
8361                 {
8362                     bad = 0;
8363                     if (map_undoredo(0)) printmessage16("Nothing to undo!");
8364                     else printmessage16("Revision %d undone",map_revision);
8365                 }
8366                 else if (ch == 'r' || ch == 'R')
8367                 {
8368                     bad = 0;
8369                     if (map_undoredo(1)) printmessage16("Nothing to redo!");
8370                     else printmessage16("Restored revision %d",map_revision-1);
8371                 }
8372 #endif
8373                 else if (ch == 'q' || ch == 'Q')  //Q
8374                 {
8375                     bad = 0;
8376 
8377                     if (ask_if_sure("Are you sure you want to quit?", 0))
8378                     {
8379                         //QUIT!
8380 
8381                         int32_t corrupt = CheckMapCorruption(4, 0);
8382 
8383                         if (ask_if_sure(corrupt<4?"Save changes?":"Map corrupt. Save changes?", 2+(corrupt>=4)))
8384                             SaveBoard(NULL, M32_SB_ASKOV);
8385 
8386                         while (keystatus[sc_Escape] || keystatus[sc_C])
8387                         {
8388                             keystatus[sc_Escape] = 0;
8389                             keystatus[sc_C] = 0;
8390                             quitevent = 0;
8391                             printmessage16("Operation cancelled");
8392                             videoShowFrame(1);
8393                             goto CANCEL;
8394                         }
8395 
8396                         CallExtUnInit();
8397 //                        clearfilenames();
8398                         engineUnInit();
8399 
8400                         exit(EXIT_SUCCESS);
8401                     }
8402 
8403                     // printmessage16("");
8404                     videoShowFrame(1);
8405                 }
8406             }
8407 
8408             clearkeys();
8409         }
8410 
8411         VM_OnEvent(EVENT_KEYS2D, -1);
8412 
8413         //nextpage();
8414     }
8415 
8416     for (i=0; i<highlightsectorcnt; i++)
8417     {
8418         for (WALLS_OF_SECTOR(highlightsector[i], j))
8419         {
8420             if (wall[j].nextwall >= 0)
8421                 checksectorpointer(wall[j].nextwall, wall[j].nextsector);
8422             checksectorpointer(j, highlightsector[i]);
8423         }
8424     }
8425     mkonwinvalid_keeptempsect();
8426 
8427     fixspritesectors();
8428 
8429     if (videoSetGameMode(fullscreen,xdimgame,ydimgame,bppgame,upscalefactor) < 0)
8430     {
8431         initprintf("%d * %d not supported in this graphics mode\n",xdim,ydim);
8432         CallExtUnInit();
8433 //        clearfilenames();
8434         engineUnInit();
8435         Bexit(EXIT_FAILURE);
8436     }
8437 
8438     videoSetPalette(GAMMA_CALC,0,0);
8439 
8440     pos.z = oposz;
8441 
8442     searchx = clamp(scale(searchx,xdimgame,xdim2d), 8, xdimgame-8-1);
8443     searchy = clamp(scale(searchy,ydimgame,ydim2d-STATUS2DSIZ), 8, ydimgame-8-1);
8444 
8445     VM_OnEvent(EVENT_ENTER3DMODE, -1);
8446 }
8447 
8448 // flags:
8449 // 1: quit_is_yes
8450 // 2: don't clear keys on return
ask_if_sure(const char * query,uint32_t flags)8451 int32_t ask_if_sure(const char *query, uint32_t flags)
8452 {
8453     char ch;
8454     int32_t ret=-1;
8455 
8456     if (!query)
8457         _printmessage16("Are you sure?");
8458     else
8459         _printmessage16("%s", query);
8460     videoShowFrame(1);
8461     keyFlushChars();
8462 
8463     while ((keystatus[sc_Escape]|keystatus[sc_C]) == 0 && ret==-1)
8464     {
8465         if (handleevents())
8466         {
8467             if (quitevent)
8468             {
8469                 if (flags&1)
8470                     return 1;
8471                 else
8472                     quitevent = 0;
8473             }
8474         }
8475         idle();
8476 
8477         ch = keyGetChar();
8478 
8479         if (ch == 'y' || ch == 'Y')
8480             ret = 1;
8481         else if (ch == 'n' || ch == 'N' || ch == 13 || ch == ' ')
8482             ret = 0;
8483     }
8484 
8485     if ((flags&2)==0)
8486         clearkeys();
8487 
8488     if (ret >= 0)
8489         return ret;
8490 
8491     return 0;
8492 }
8493 
editor_ask_function(const char * question,const char * dachars,int32_t numchars)8494 int32_t editor_ask_function(const char *question, const char *dachars, int32_t numchars)
8495 {
8496     char ch;
8497     int32_t i, ret=-1;
8498 
8499     _printmessage16("%s", question);
8500 
8501     videoShowFrame(1);
8502     keyFlushChars();
8503 
8504     // 'c' is cancel too, but can be overridden
8505     while ((keystatus[sc_Escape]|keystatus[sc_C]) == 0 && ret==-1)
8506     {
8507         if (handleevents())
8508             quitevent = 0;
8509 
8510         idle();
8511         ch = keyGetChar();
8512 
8513         for (i=0; i<numchars; i++)
8514             if (ch==Btolower(dachars[i]) || ch==Btoupper(dachars[i]))
8515                 ret = i;
8516     }
8517 
8518     clearkeys();
8519 
8520     return ret;
8521 }
8522 
8523 #ifdef YAX_ENABLE
ask_above_or_below(void)8524 static int32_t ask_above_or_below(void)
8525 {
8526     char dachars[2] = {'a', 'z'};
8527     return editor_ask_function("Extend above (a) or below (z)?", dachars, 2);
8528 }
8529 #endif
8530 
SaveBoardAndPrintMessage(const char * fn)8531 static void SaveBoardAndPrintMessage(const char *fn)
8532 {
8533     const char *f;
8534 
8535     _printmessage16("Saving board...");
8536     videoShowFrame(1);
8537 
8538     f = SaveBoard(fn, M32_SB_ASKOV);
8539 
8540     if (f)
8541     {
8542         if (saveboard_fixedsprites)
8543             message("Saved board %sto %s (changed sectnums of %d sprites).",
8544                     saveboard_savedtags?"and tags ":"", f, saveboard_fixedsprites);
8545         else
8546             message("Saved board %sto %s.", saveboard_savedtags?"and tags ":"", f);
8547     }
8548     else
8549     {
8550         if (!saveboard_canceled)
8551         {
8552             if (saveboard_fixedsprites)
8553                 message("^13SAVING BOARD FAILED (changed sectnums of %d sprites).",
8554                         saveboard_fixedsprites);
8555             else
8556                 message("^13SAVING BOARD FAILED.");
8557         }
8558     }
8559 }
8560 
8561 // get the file name of the file that would be written if SaveBoard(fn, 0) was called
GetSaveBoardFilename(const char * fn)8562 const char *GetSaveBoardFilename(const char *fn)
8563 {
8564     if (!fn)
8565         fn = boardfilename;
8566 
8567     if (pathsearchmode)
8568         return fn;
8569 
8570     // virtual filesystem mode can't save to directories so drop the file into
8571     // the current directory
8572     return getbasefn(fn);
8573 }
8574 
8575 // flags: see enum SaveBoardFlags.
8576 // returns: NULL on failure, file name on success.
SaveBoard(const char * fn,uint32_t flags)8577 const char *SaveBoard(const char *fn, uint32_t flags)
8578 {
8579     int32_t ret;
8580     const char *f = GetSaveBoardFilename(fn);
8581 
8582     saveboard_canceled = 0;
8583 #ifdef NEW_MAP_FORMAT
8584     if ((flags&M32_SB_ASKOV) && mapversion>=10 &&
8585             g_loadedMapVersion != -1 && g_loadedMapVersion < mapversion)
8586     {
8587         char question[128];
8588         // XXX: This message is potentially confusing if the user is "Saving
8589         // As" to a new file name.
8590         Bsnprintf(question, sizeof(question), "Are you sure to overwrite a version "
8591                   "V%d map with a V%d map-text one?", g_loadedMapVersion, mapversion);
8592 
8593         if (AskIfSure(question))
8594         {
8595             message("Cancelled saving board");
8596             saveboard_canceled = 1;
8597             return NULL;
8598         }
8599     }
8600 #endif
8601 
8602     saveboard_savedtags = 0;
8603     saveboard_fixedsprites = CallExtPreSaveMap();
8604 
8605     ret = saveboard(f, &startpos, startang, startsectnum);
8606     if ((flags&M32_SB_NOEXT)==0)
8607     {
8608         CallExtSaveMap(f);
8609         saveboard_savedtags = !taglab_save(f);
8610     }
8611 
8612     return (ret==0) ? f : NULL;
8613 }
8614 
8615 // flags:  1: for running on Mapster32 init
8616 //         4: passed to loadboard flags (no polymer_loadboard); implies no maphack loading
8617 // returns:
8618 //     0 on success,
8619 //    <0 on failure.
LoadBoard(const char * filename,uint32_t flags)8620 int32_t LoadBoard(const char *filename, uint32_t flags)
8621 {
8622     int32_t i, tagstat;
8623     const int32_t loadingflags = (!pathsearchmode && grponlymode) ? 2 : 0;
8624 
8625     if (!filename)
8626         filename = selectedboardfilename;
8627 
8628     editorzrange[0] = INT32_MIN;
8629     editorzrange[1] = INT32_MAX;
8630 
8631     CallExtPreLoadMap();
8632     i = engineLoadBoard(filename, (flags&4)|loadingflags, &pos, &ang, &cursectnum);
8633     if (i == -2)
8634         i = engineLoadBoardV5V6(filename,loadingflags, &pos, &ang, &cursectnum);
8635 
8636     if (i < 0)
8637     {
8638 //        printmessage16("Invalid map format.");
8639         return i;
8640     }
8641 
8642     // Success, so copy the file name.
8643     if (filename != boardfilename)
8644         Bstrcpy(boardfilename, filename);
8645 
8646     mkonwinvalid();
8647 
8648     highlightcnt = -1;
8649     Bmemset(show2dwall, 0, sizeof(show2dwall));  //Clear all highlights
8650     Bmemset(show2dsprite, 0, sizeof(show2dsprite));
8651 
8652     if ((flags&4)==0)
8653         loadmhk(0);
8654 
8655     tagstat = taglab_load(boardfilename, loadingflags);
8656     CallExtLoadMap(boardfilename);
8657 
8658     {
8659         char msgtail[64];
8660         const int32_t ci = CheckMapCorruption(4, 0);
8661 
8662         if (ci == 5)
8663             Bstrcpy(msgtail, "^12(EXTREME corruption)");
8664         else if (ci == 4)
8665             Bstrcpy(msgtail, "^12(HEAVY corruption)");
8666         else if (i > 0)
8667             Bsprintf(msgtail, "^14(removed %d sprites)", i);
8668         else if (ci >= 1 && ci < 4)
8669             Bstrcpy(msgtail, "^14(moderate corruption)");
8670         else
8671             Bstrcpy(msgtail, "successfully");
8672 
8673         message("Loaded V%d map %s%s %s", g_loadedMapVersion,
8674                 boardfilename, tagstat==0?" w/tags":"", msgtail);
8675     }
8676 
8677     startpos = pos;      //this is same
8678     startang = ang;
8679     startsectnum = cursectnum;
8680     asksave = 0;
8681 
8682     return 0;
8683 }
8684 
getpoint(int32_t searchxe,int32_t searchye,int32_t * x,int32_t * y)8685 void getpoint(int32_t searchxe, int32_t searchye, int32_t *x, int32_t *y)
8686 {
8687     inpclamp(&pos.x, -editorgridextent, editorgridextent);
8688     inpclamp(&pos.y, -editorgridextent, editorgridextent);
8689 
8690     searchxe -= halfxdim16;
8691     searchye -= midydim16;
8692 
8693     vec2_t svec = { searchxe ,searchye };
8694 
8695     if (m32_sideview)
8696     {
8697         if (m32_sidesin!=0)
8698             svec.y = divscale14(svec.y, m32_sidesin);
8699         rotatevec(svec, -m32_sideang, &svec);
8700     }
8701 
8702     *x = pos.x + divscale14(svec.x,zoom);
8703     *y = pos.y + divscale14(svec.y,zoom);
8704 
8705     inpclamp(x, -editorgridextent, editorgridextent);
8706     inpclamp(y, -editorgridextent, editorgridextent);
8707 }
8708 
getlinehighlight(int32_t xplc,int32_t yplc,int32_t line,int8_t ignore_pointhighlight)8709 static int32_t getlinehighlight(int32_t xplc, int32_t yplc, int32_t line, int8_t ignore_pointhighlight)
8710 {
8711     int32_t i, j, dst, dist, closest, x1, y1, x2, y2, nx, ny;
8712     int32_t daxplc, dayplc;
8713 
8714     if (numwalls == 0)
8715         return -1;
8716 
8717     if (g_mouseBits & 1 || searchlock)
8718         return line;
8719 
8720     if (!ignore_pointhighlight && (pointhighlight&0xc000) == 16384)
8721         return -1;
8722 
8723     dist = linehighlightdist;
8724     if (m32_sideview)
8725     {
8726         daxplc = searchx;
8727         dayplc = searchy;
8728         dist = mulscale14(dist, zoom);
8729     }
8730     else
8731     {
8732         daxplc = xplc;
8733         dayplc = yplc;
8734     }
8735 
8736     closest = -1;
8737     for (i=0; i<numwalls; i++)
8738     {
8739         if (!m32_sideview)
8740             YAX_SKIPWALL(i);
8741 
8742         getclosestpointonwall(daxplc,dayplc, i, &nx,&ny, 1);
8743         dst = klabs(daxplc-nx) + klabs(dayplc-ny);
8744         if (dst <= dist)
8745         {
8746             dist = dst;
8747             closest = i;
8748         }
8749     }
8750 
8751     if (closest>=0 && (j = wall[closest].nextwall) >= 0)
8752 #ifdef YAX_ENABLE
8753     if (m32_sideview || ((graywallbitmap[j>>3]&pow2char[j&7])==0))
8754 #endif
8755     {
8756         //if red line, allow highlighting of both sides
8757         if (m32_sideview)
8758         {
8759             x1 = m32_wallscreenxy[closest][0];
8760             y1 = m32_wallscreenxy[closest][1];
8761             x2 = m32_wallscreenxy[wall[closest].point2][0];
8762             y2 = m32_wallscreenxy[wall[closest].point2][1];
8763         }
8764         else
8765         {
8766             x1 = wall[closest].x;
8767             y1 = wall[closest].y;
8768             x2 = POINT2(closest).x;
8769             y2 = POINT2(closest).y;
8770         }
8771 
8772         i = wall[closest].nextwall;
8773         if (!m32_sideview ||
8774             ((B_UNBUF64(m32_wallscreenxy[closest]) == B_UNBUF64(m32_wallscreenxy[wall[j].point2])) &&
8775              (B_UNBUF64(m32_wallscreenxy[wall[closest].point2]) == B_UNBUF64(m32_wallscreenxy[j]))))
8776             if (dmulscale32(daxplc-x1,y2-y1,-(x2-x1),dayplc-y1) >= 0)
8777                 closest = j;
8778     }
8779 
8780     return closest;
8781 }
8782 
getpointhighlight(int32_t xplc,int32_t yplc,int32_t point)8783 int32_t getpointhighlight(int32_t xplc, int32_t yplc, int32_t point)
8784 {
8785     int32_t i, j, dst, dist = pointhighlightdist, closest = -1;
8786     int32_t dax,day;
8787     int32_t alwaysshowgray = get_alwaysshowgray();
8788 
8789     if (numwalls == 0)
8790         return -1;
8791 
8792     if (g_mouseBits & 1 || searchlock)
8793         return point;
8794 
8795     if (grid < 1)
8796         dist = 0;
8797 
8798     for (i=0; i<numsectors; i++)
8799     {
8800         if (!m32_sideview || !alwaysshowgray)
8801             YAX_SKIPSECTOR(i);
8802 
8803         for (j=sector[i].wallptr; j<sector[i].wallptr+sector[i].wallnum; j++)
8804         {
8805             if (!m32_sideview)
8806                 dst = klabs(xplc-wall[j].x) + klabs(yplc-wall[j].y);
8807             else
8808             {
8809                 editorGet2dScreenCoordinates(&dax,&day, wall[j].x-pos.x,wall[j].y-pos.y, zoom);
8810                 day += getscreenvdisp(getflorzofslope(i, wall[j].x,wall[j].y)-pos.z, zoom);
8811 
8812                 if (halfxdim16+dax < 0 || halfxdim16+dax >= xdim || midydim16+day < 0 || midydim16+day >= ydim)
8813                     continue;
8814 
8815                 dst = klabs(halfxdim16+dax-searchx) + klabs(midydim16+day-searchy);
8816             }
8817 
8818             if (dst <= dist)
8819             {
8820                 // prefer white walls
8821                 if (dst<dist || closest==-1 || (wall[j].nextwall>=0)-(wall[closest].nextwall>=0) <= 0)
8822                     dist = dst, closest = j;
8823             }
8824         }
8825     }
8826 
8827     if (zoom >= 256)
8828         for (i=0; i<MAXSPRITES; i++)
8829             if (sprite[i].statnum < MAXSTATUS)
8830             {
8831                 if ((!m32_sideview || !alwaysshowgray) && sprite[i].sectnum >= 0)
8832                     YAX_SKIPSECTOR(sprite[i].sectnum);
8833 
8834                 if (!m32_sideview)
8835                 {
8836                     dst = klabs(xplc-sprite[i].x) + klabs(yplc-sprite[i].y);
8837                 }
8838                 else
8839                 {
8840                     editorGet2dScreenCoordinates(&dax,&day, sprite[i].x-pos.x,sprite[i].y-pos.y, zoom);
8841                     day += getscreenvdisp(sprite[i].z-pos.z, zoom);
8842 
8843                     if (halfxdim16+dax < 0 || halfxdim16+dax >= xdim || midydim16+day < 0 || midydim16+day >= ydim)
8844                         continue;
8845 
8846                     dst = klabs(halfxdim16+dax-searchx) + klabs(midydim16+day-searchy);
8847                 }
8848 
8849                 // was (dst <= dist), but this way, when duplicating sprites,
8850                 // the selected ones are dragged first
8851                 if (dst < dist || (dst == dist && (show2dsprite[i>>3]&pow2char[i&7])))
8852                     dist = dst, closest = i+16384;
8853             }
8854 
8855     return closest;
8856 }
8857 
locktogrid(int32_t * dax,int32_t * day)8858 static void locktogrid(int32_t *dax, int32_t *day)
8859 {
8860     *dax = ((*dax+(1024>>grid))&(0xffffffff<<(11-grid)));
8861     *day = ((*day+(1024>>grid))&(0xffffffff<<(11-grid)));
8862 }
8863 
adjustmark(int32_t * xplc,int32_t * yplc,int16_t danumwalls)8864 static int32_t adjustmark(int32_t *xplc, int32_t *yplc, int16_t danumwalls)
8865 {
8866     int32_t i, dst, dist, dax, day, pointlockdist;
8867 
8868     if (danumwalls < 0)
8869         danumwalls = numwalls;
8870 
8871     pointlockdist = 0;
8872     if (grid > 0 && gridlock)
8873         pointlockdist = (256>>grid);
8874 
8875     dist = pointlockdist;
8876     dax = *xplc;
8877     day = *yplc;
8878 
8879     for (i=0; i<danumwalls; i++)
8880     {
8881         YAX_SKIPWALL(i);
8882 
8883         dst = klabs((*xplc)-wall[i].x) + klabs((*yplc)-wall[i].y);
8884         if (dst < dist)
8885         {
8886             dist = dst;
8887             dax = wall[i].x;
8888             day = wall[i].y;
8889         }
8890     }
8891     if (dist == pointlockdist)
8892         if (gridlock && grid > 0)
8893             locktogrid(&dax, &day);
8894 
8895     *xplc = dax;
8896     *yplc = day;
8897 
8898     return 0;
8899 }
8900 
checkautoinsert(int32_t dax,int32_t day,int16_t danumwalls)8901 static int32_t checkautoinsert(int32_t dax, int32_t day, int16_t danumwalls)
8902 {
8903     int32_t i, x1, y1, x2, y2;
8904 
8905     if (danumwalls < 0)
8906         danumwalls = numwalls;
8907 
8908     for (i=0; i<danumwalls; i++)    // Check if a point should be inserted
8909     {
8910         YAX_SKIPWALL(i);
8911 
8912         x1 = wall[i].x;
8913         y1 = wall[i].y;
8914         x2 = POINT2(i).x;
8915         y2 = POINT2(i).y;
8916 
8917         if ((x1 != dax || y1 != day) && (x2 != dax || y2 != day))
8918             if ((x1 <= dax && dax <= x2) || (x2 <= dax && dax <= x1))
8919                 if ((y1 <= day && day <= y2) || (y2 <= day && day <= y1))
8920                     if ((dax-x1)*(y2-y1) == (day-y1)*(x2-x1))
8921                         return 1;          //insertpoint((short)i,dax,day,NULL);
8922     }
8923 
8924     return 0;
8925 }
8926 
8927 // <wallstart> has to be the starting (i.e. least index) wall of a loop!
8928 // Returns: CLOCKDIR_CW or CLOCKDIR_CCW.
clockdir(int32_t wallstart)8929 int32_t clockdir(int32_t wallstart)
8930 {
8931     int32_t tempint, x0, x1, x2, y0, y1, y2;
8932 
8933     int32_t minx = 0x7fffffff;
8934     int32_t themin = -1;
8935     int32_t i = wallstart-1;
8936 
8937     do
8938     {
8939         i++;
8940         if (POINT2(i).x < minx)
8941         {
8942             minx = POINT2(i).x;
8943             themin = i;
8944         }
8945     }
8946     while (wall[i].point2 != wallstart && i < MAXWALLS-1);
8947     // NOTE: the i < MAXWALLS-1 check is really only safety against either
8948     //  - a really corrupt map, or
8949     //  - misuse of clockdir() where <wallstart> is not the starting wall of
8950     //    the very last loop
8951 
8952     x0 = wall[themin].x;
8953     y0 = wall[themin].y;
8954     x1 = POINT2(themin).x;
8955     y1 = POINT2(themin).y;
8956     x2 = POINT2(wall[themin].point2).x;
8957     y2 = POINT2(wall[themin].point2).y;
8958 
8959     if (y2 <= y1 && y1 <= y0)
8960         return CLOCKDIR_CW;
8961     if (y0 <= y1 && y1 <= y2)
8962         return CLOCKDIR_CCW;
8963 
8964     tempint = (x0-x1)*(y2-y1) - (x2-x1)*(y0-y1);
8965     if (tempint < 0)
8966         return CLOCKDIR_CW;
8967     else
8968         return CLOCKDIR_CCW;
8969 }
8970 
flipwalls(int16_t numwalls,int16_t newnumwalls)8971 static void flipwalls(int16_t numwalls, int16_t newnumwalls)
8972 {
8973     int32_t i, j, nume;
8974 
8975     nume = newnumwalls-numwalls;
8976 
8977     for (i=numwalls; i<numwalls+(nume>>1); i++)
8978     {
8979         j = numwalls+newnumwalls-i-1;
8980 
8981         swapshort(&onextwall[i], &onextwall[j]);
8982 
8983         swaplong(&wall[i].x, &wall[j].x);
8984         swaplong(&wall[i].y, &wall[j].y);
8985     }
8986 }
8987 
do_insertpoint(int32_t w,int32_t dax,int32_t day,int32_t * mapwallnum)8988 static void do_insertpoint(int32_t w, int32_t dax, int32_t day, int32_t *mapwallnum)
8989 {
8990     int32_t i;
8991     const int32_t sucksect = sectorofwall(w);
8992     const uint32_t lenbyrep = getlenbyrep(wallength(w), wall[w].xrepeat);
8993 
8994     sector[sucksect].wallnum++;
8995     for (i=sucksect+1; i<numsectors; i++)
8996         sector[i].wallptr++;
8997 
8998     if (mapwallnum && *mapwallnum >= w+1)
8999         (*mapwallnum)++;
9000 
9001     movewalls(w+1, +1);
9002     Bmemcpy(&wall[w+1], &wall[w], sizeof(walltype));
9003 #ifdef YAX_ENABLE
9004     editwall[(w+1)>>3] &= ~pow2char[(w+1)&7];
9005 #endif
9006     wall[w].point2 = w+1;
9007     wall[w+1].x = dax;
9008     wall[w+1].y = day;
9009 
9010     fixxrepeat(w, lenbyrep);
9011     AlignWallPoint2(w);
9012     fixxrepeat(w+1, lenbyrep);
9013 }
9014 
9015 // Returns number of points inserted (1; or 2 if wall had a nextwall).
9016 //  *mapwallnum is set to the new wallnum of the former (pre-insertpoint) *mapwallnum
9017 //  (the new one can only be >= than the old one; ptr may be NULL if we don't care)
insertpoint(int16_t linehighlight,int32_t dax,int32_t day,int32_t * mapwallnum)9018 static int32_t insertpoint(int16_t linehighlight, int32_t dax, int32_t day, int32_t *mapwallnum)
9019 {
9020     int32_t j = linehighlight;
9021 
9022     do_insertpoint(j, dax, day, mapwallnum);
9023 
9024     if (wall[j].nextwall >= 0)
9025     {
9026         int32_t k = wall[j].nextwall;
9027 
9028         do_insertpoint(k, dax, day, mapwallnum);
9029 
9030         j = wall[k].nextwall;
9031         wall[j].nextwall = k+1;
9032         wall[j+1].nextwall = k;
9033         wall[k].nextwall = j+1;
9034         wall[k+1].nextwall = j;
9035 
9036         return 2;
9037     }
9038 
9039     return 1;
9040 }
9041 
9042 // runi: 0=check (forbidden), 1=prepare, 2=do!
deletepoint(int16_t point,int32_t runi)9043 static void deletepoint(int16_t point, int32_t runi)
9044 {
9045     int32_t i, j, sucksect;
9046 
9047     Bassert(runi != 0);
9048 
9049     if (runi==1)
9050     {
9051         i = wall[point].nextwall;
9052         if (i >= 0)
9053         {
9054             NEXTWALL(i).nextwall = NEXTWALL(i).nextsector = -1;
9055             wall[i].nextwall = wall[i].nextsector = -1;
9056         }
9057 
9058         return;
9059     }
9060 
9061     sucksect = sectorofwall(point);
9062 
9063     sector[sucksect].wallnum--;
9064     for (i=sucksect+1; i<numsectors; i++)
9065         sector[i].wallptr--;
9066 
9067     j = lastwall(point);
9068     wall[j].point2 = wall[point].point2;
9069 
9070 #if 0
9071     if (wall[j].nextwall >= 0)
9072     {
9073         NEXTWALL(j).nextwall = -1;
9074         NEXTWALL(j).nextsector = -1;
9075     }
9076     if (wall[point].nextwall >= 0)
9077     {
9078         NEXTWALL(point).nextwall = -1;
9079         NEXTWALL(point).nextsector = -1;
9080     }
9081 #endif
9082     movewalls(point, -1);
9083 }
9084 
deletesector(int16_t sucksect)9085 static int32_t deletesector(int16_t sucksect)
9086 {
9087     int32_t i, j, k, nextk, startwall, endwall;
9088 
9089     while (headspritesect[sucksect] >= 0)
9090         deletesprite(headspritesect[sucksect]);
9091 
9092     startwall = sector[sucksect].wallptr;
9093     endwall = startwall + sector[sucksect].wallnum - 1;
9094     j = sector[sucksect].wallnum;
9095 
9096 #ifdef YAX_ENABLE
9097     yax_setbunches(sucksect, -1, -1);
9098 #endif
9099 
9100     for (i=sucksect; i<numsectors-1; i++)
9101     {
9102         k = headspritesect[i+1];
9103         while (k != -1)
9104         {
9105             nextk = nextspritesect[k];
9106             changespritesect(k, i);
9107             k = nextk;
9108         }
9109 
9110         Bmemcpy(&sector[i], &sector[i+1], sizeof(sectortype));
9111         sector[i].wallptr -= j;
9112     }
9113     numsectors--;
9114 
9115     for (i=startwall; i<=endwall; i++)
9116     {
9117         if (wall[i].nextwall >= 0)
9118         {
9119             NEXTWALL(i).nextwall = -1;
9120             NEXTWALL(i).nextsector = -1;
9121         }
9122     }
9123 
9124     movewalls(startwall, -j);
9125     for (i=0; i<numwalls; i++)
9126         if (wall[i].nextwall >= startwall)
9127             wall[i].nextsector--;
9128 
9129     // cursectnum will exceed numsectors when the arrow position is inside the last sector in the map and that sector is removed
9130     if (cursectnum > numsectors)
9131         cursectnum = -1;
9132 
9133     return 0;
9134 }
9135 
fixspritesectors(void)9136 int32_t fixspritesectors(void)
9137 {
9138     int32_t i;
9139     int32_t numfixedsprites = 0, printfirsttime = 0;
9140 
9141     for (i=numsectors-1; i>=0; i--)
9142         if (sector[i].wallnum <= 0 || sector[i].wallptr >= numwalls)
9143         {
9144             // XXX: This is not the best course of action for
9145             //  such great corruption.
9146             deletesector(i);
9147             mkonwinvalid();
9148             initprintf("NOTE: Deleted sector %d which had corrupt .wallnum or .wallptr\n", i);
9149         }
9150 
9151     if (m32_script_expertmode)
9152         return 0;
9153 
9154     for (i=0; i<MAXSPRITES; i++)
9155         if (sprite[i].statnum < MAXSTATUS)
9156         {
9157             const int32_t dax=sprite[i].x, day=sprite[i].y;
9158 
9159             if (inside(dax,day,sprite[i].sectnum) != 1)
9160             {
9161                 int32_t j, cz, fz;
9162 
9163                 spriteoncfz(i, &cz, &fz);
9164 
9165                 for (j=0; j<numsectors; j++)
9166                 {
9167                     if (cz <= sprite[i].z && sprite[i].z <= fz && inside(dax,day, j) == 1)
9168                     {
9169                         if (fixmaponsave_sprites || (unsigned)sprite[i].sectnum >= numsectors+0u)
9170                         {
9171                             if (printfirsttime == 0)
9172                             {
9173                                 initprintf("--------------------\n");
9174                                 printfirsttime = 1;
9175                             }
9176                             initprintf("Changed sectnum of sprite #%d from %d to %d\n",
9177                                               i, TrackerCast(sprite[i].sectnum), j);
9178 
9179                             changespritesect(i, j);
9180                             numfixedsprites++;
9181                         }
9182                         break;
9183                     }
9184                 }
9185             }
9186         }
9187 
9188     return numfixedsprites;
9189 }
9190 
movewalls(int32_t start,int32_t offs)9191 static int32_t movewalls(int32_t start, int32_t offs)
9192 {
9193     int32_t i;
9194 
9195     if (offs < 0)  //Delete
9196     {
9197         for (i=start; i<numwalls+offs; i++)
9198         {
9199             Bmemcpy(&wall[i], &wall[i-offs], sizeof(walltype));
9200             int const editw = !!(editwall[(i-offs)>>3]&pow2char[(i-offs)&7]);
9201             editwall[i>>3] &= ~pow2char[i&7];
9202             editwall[i>>3] |= editw<<(i&7);
9203         }
9204     }
9205     else if (offs > 0)  //Insert
9206     {
9207         for (i=numwalls+offs-1; i>=start+offs; i--)
9208         {
9209             Bmemcpy(&wall[i], &wall[i-offs], sizeof(walltype));
9210             int const editw = !!(editwall[(i-offs)>>3]&pow2char[(i-offs)&7]);
9211             editwall[i>>3] &= ~pow2char[i&7];
9212             editwall[i>>3] |= editw<<(i&7);
9213         }
9214 
9215         if (ovh.bak_wallsdrawn > 0)
9216         {
9217             if (ovh.suckwall >= start)
9218                 ovh.suckwall += offs;
9219             if (ovh.splitstartwall >= start)
9220                 ovh.splitstartwall += offs;
9221         }
9222     }
9223 
9224     numwalls += offs;
9225     for (i=0; i<numwalls; i++)
9226     {
9227         if (wall[i].nextwall >= start) wall[i].nextwall += offs;
9228         if (wall[i].point2 >= start) wall[i].point2 += offs;
9229     }
9230 #ifdef YAX_ENABLE
9231     yax_tweakwalls(start, offs);
9232 #endif
9233 
9234     return 0;
9235 }
9236 
wallength(int16_t i)9237 int32_t wallength(int16_t i)
9238 {
9239     int64_t dax = POINT2(i).x - wall[i].x;
9240     int64_t day = POINT2(i).y - wall[i].y;
9241 #if 1 //def POLYMOST
9242     int64_t hypsq = dax*dax + day*day;
9243     if (hypsq > (int64_t)INT32_MAX)
9244         return (int32_t)sqrt((double)hypsq);
9245     else
9246         return ksqrt((uint32_t)hypsq);
9247 #else
9248     return ksqrt(dax*dax + day*day);
9249 #endif
9250 }
9251 
fixrepeats(int16_t i)9252 void fixrepeats(int16_t i)
9253 {
9254     int32_t dist = wallength(i);
9255     int32_t day = wall[i].yrepeat;
9256 
9257     wall[i].xrepeat = clamp(mulscale10(dist,day), 1, 255);
9258 
9259     asksave = 1;
9260 }
9261 
getlenbyrep(int32_t len,int32_t repeat)9262 uint32_t getlenbyrep(int32_t len, int32_t repeat)
9263 {
9264     if (repeat <= 0)
9265         return ((uint32_t)len)<<12;
9266 
9267     return divscale12(len, repeat);
9268 }
9269 
fixxrepeat(int16_t wallnum,uint32_t lenrepquot)9270 void fixxrepeat(int16_t wallnum, uint32_t lenrepquot)  // lenrepquot: divscale12(wallength,xrepeat)
9271 {
9272     if (lenrepquot != 0)
9273     {
9274         uint32_t res = (((wallength(wallnum)<<12)+(1<<11))/lenrepquot);
9275         wall[wallnum].xrepeat = clamp(res, 1, 255);
9276     }
9277 }
9278 
9279 
9280 int32_t overridepm16y = -1;
9281 
clearmidstatbar16(void)9282 void clearmidstatbar16(void)
9283 {
9284     int32_t y = overridepm16y<0 ? STATUS2DSIZ : overridepm16y;
9285 
9286     videoBeginDrawing();
9287     CLEARLINES2D(ydim-y+25, STATUS2DSIZ+2-(25<<1), 0);
9288     videoEndDrawing();
9289 }
9290 
clearministatbar16(void)9291 static void clearministatbar16(void)
9292 {
9293     int32_t i, col = editorcolors[25];
9294 
9295     videoBeginDrawing();
9296 
9297     for (i=ydim-STATUS2DSIZ2; i<ydim; i+=2)
9298     {
9299 //        drawline256(0, i<<12, xdim<<12, i<<12, col);
9300         CLEARLINES2D(i, 1, (col<<24)|(col<<16)|(col<<8)|col);
9301         CLEARLINES2D(i+1, 1, (col<<24)|(col<<16)|(col<<8)|col);
9302         col--;
9303         if (col <= 0) break;
9304     }
9305 
9306     CLEARLINES2D(i, ydim-i, 0);
9307 
9308     if (xdim >= 800)
9309     {
9310         Bsnprintf(tempbuf, sizeof(tempbuf), "%s %s", AppProperName, CallExtGetVer());
9311         printext16(xdim2d-(Bstrlen(tempbuf)<<3)-3, ydim2d-STATUS2DSIZ2+10, editorcolors[4],-1, tempbuf, 0);
9312         printext16(xdim2d-(Bstrlen(tempbuf)<<3)-2, ydim2d-STATUS2DSIZ2+9, editorcolors[12],-1, tempbuf, 0);
9313     }
9314 
9315     videoEndDrawing();
9316 }
9317 
9318 // <startwall> has to be the starting wall of a loop!
9319 //
9320 // Assuming that <startwall> indicates a CW (outer) loop, the return value can
9321 // be seen as a boolean of whether (x,y) is inside it.
9322 //
9323 // XXX: this function suffers from asymmetry issues in degenerate cases,
9324 // similar to how inside() did before r3898.
loopinside(int32_t x,int32_t y,int16_t startwall)9325 int32_t loopinside(int32_t x, int32_t y, int16_t startwall)
9326 {
9327     bssize_t cnt = clockdir(startwall);
9328     int32_t i = startwall;
9329 
9330     do
9331     {
9332         int32_t x1 = wall[i].x;
9333         int32_t x2 = POINT2(i).x;
9334 
9335         if (x <= x1 || x <= x2)
9336         {
9337             int32_t y1 = wall[i].y;
9338             int32_t y2 = POINT2(i).y;
9339 
9340             if (y1 > y2)
9341             {
9342                 swaplong(&x1, &x2);
9343                 swaplong(&y1, &y2);
9344             }
9345 
9346             if (y1 <= y && y < y2)
9347                 if ((uint64_t)x1*(y-y2) + (uint64_t)x2*(y1-y) <= (uint64_t)x*(y1-y2))
9348                     cnt ^= 1;
9349         }
9350 
9351         i = wall[i].point2;
9352     }
9353     while (i != startwall);
9354 
9355     return cnt;
9356 }
9357 
9358 #if 0
9359 static int32_t numloopsofsector(int16_t sectnum)
9360 {
9361     int32_t i, numloops, startwall, endwall;
9362 
9363     numloops = 0;
9364     startwall = sector[sectnum].wallptr;
9365     endwall = startwall + sector[sectnum].wallnum;
9366     for (i=startwall; i<endwall; i++)
9367         if (wall[i].point2 < i) numloops++;
9368     return numloops;
9369 }
9370 #endif
9371 
getnumber_internal1(char ch,int32_t * danumptr,int32_t maxnumber,char sign)9372 int32_t getnumber_internal1(char ch, int32_t *danumptr, int32_t maxnumber, char sign)
9373 {
9374     int32_t danum = *danumptr;
9375 
9376     if (ch >= '0' && ch <= '9')
9377     {
9378         int64_t nbig;
9379         if (danum >= 0)
9380         {
9381             nbig = ((int64_t)danum*10)+(ch-'0');
9382             if (nbig <= (int64_t)maxnumber) danum = nbig;
9383         }
9384         else if (sign) // this extra check isn't hurting anything
9385         {
9386             nbig = ((int64_t)danum*10)-(ch-'0');
9387             if (nbig >= (int64_t)(-maxnumber)) danum = nbig;
9388         }
9389     }
9390     else if (ch == 8 || ch == 127)  	// backspace
9391     {
9392         danum /= 10;
9393     }
9394     else if (ch == 13)
9395     {
9396         return 1;
9397     }
9398     else if (ch == '-' && sign)  	// negate
9399     {
9400         danum = -danum;
9401     }
9402 
9403     *danumptr = danum;
9404     return 0;
9405 }
9406 
getnumber_autocomplete(const char * namestart,char ch,int32_t * danum,int32_t flags)9407 int32_t getnumber_autocomplete(const char *namestart, char ch, int32_t *danum, int32_t flags)
9408 {
9409     if (flags!=1 && flags!=2)
9410         return 0;
9411 
9412     if (flags==2 && *danum<0)
9413         return 0;
9414 
9415     if (isalpha(ch))
9416     {
9417         char b[2];
9418         const char *gotstr;
9419         int32_t i, diddel;
9420 
9421         b[0] = ch;
9422         b[1] = 0;
9423         gotstr = getstring_simple(namestart, b, (flags==1)?sizeof(names[0])-1:TAGLAB_MAX-1, flags);
9424         if (!gotstr || !gotstr[0])
9425             return 0;
9426 
9427         if (flags==1)
9428         {
9429             for (i=0; i<MAXTILES; i++)
9430                 if (!Bstrcasecmp(names[i], gotstr))
9431                 {
9432                     *danum = i;
9433                     return 1;
9434                 }
9435         }
9436         else
9437         {
9438             i = taglab_gettag(gotstr);
9439 //initprintf("taglab: s=%s, i=%d\n",gotstr,i);
9440             if (i > 0)
9441             {
9442                 *danum = i;
9443                 return 1;
9444             }
9445             else
9446             {
9447                 // insert new tag
9448                 if (*danum > 0 && *danum<32768)
9449                 {
9450                     diddel = taglab_add(gotstr, *danum);
9451                     message("Added label \"%s\" for tag %d%s%s", gotstr, *danum,
9452                             diddel?", deleting old ":"",
9453                             (!diddel)?"":(diddel==1?"label":"tag"));
9454                     return 1;
9455                 }
9456                 else if (*danum==0)
9457                 {
9458                     i = taglab_getnextfreetag(NULL);
9459                     if (i >= 1)
9460                     {
9461                         *danum = i;
9462                         diddel = taglab_add(gotstr, *danum);
9463                         message("%sadded label \"%s\" for tag %d%s%s",
9464                                 diddel?"Auto-":"Automatically ", gotstr, *danum,
9465                                 diddel?", deleting old ":"",
9466                                 (!diddel)?"":(diddel==1?"label":"tag"));
9467                         return 1;
9468                     }
9469                 }
9470             }
9471         }
9472     }
9473 
9474     return 0;
9475 }
9476 
9477 // sign is now used for more than one flag (also _getnumber256):
9478 //  1: sign
9479 //  2: autocomplete names
9480 //  4: autocomplete taglabels
9481 //  8: return -1 if cancelled
_getnumber16(const char * namestart,int32_t num,int32_t maxnumber,char sign,const char * (func)(int32_t))9482 int32_t _getnumber16(const char *namestart, int32_t num, int32_t maxnumber, char sign, const char *(func)(int32_t))
9483 {
9484     char buffer[80], ournamestart[80-17], ch;
9485     int32_t n, danum, oldnum;
9486     uint8_t flags = (sign&(2|4|8))>>1;
9487     sign &= 1;
9488 
9489     danum = num;
9490     oldnum = danum;
9491 
9492     // need to have 4+11+2==17 chars room at the end
9493     // ("^011", max. string length of an int32, "_ ")
9494     Bstrncpyz(ournamestart, namestart, sizeof(ournamestart));
9495 
9496     keyFlushChars();
9497     while (keystatus[sc_Escape] == 0)
9498     {
9499         if (handleevents())
9500             quitevent = 0;
9501 
9502         idle();
9503         ch = keyGetChar();
9504 
9505         Bsprintf(buffer, "%s^011%d", ournamestart, danum);
9506         n = Bstrlen(buffer);  // maximum is 62+4+11 == 77
9507         if ((int32_t) totalclock & 32)
9508             Bstrcat(buffer,"_ ");
9509         // max strlen now 79
9510         _printmessage16("%s", buffer);
9511 
9512         if (func != NULL)
9513         {
9514             Bsnprintf(buffer, sizeof(buffer), "%s", func(danum));
9515             // printext16(200L-24, ydim-STATUS2DSIZ+20L, editorcolors[9], editorcolors[0], buffer, 0);
9516             printext16(n<<3, ydim-STATUS2DSIZ+128, editorcolors[11], -1, buffer,0);
9517         }
9518 
9519         g_iReturnVar = danum;
9520         VM_OnEvent(EVENT_GETNUMBER, -1);
9521 
9522         videoShowFrame(1);
9523 
9524         n = 0;
9525         if (getnumber_internal1(ch, &danum, maxnumber, sign) ||
9526             (n=getnumber_autocomplete(ournamestart, ch, &danum, flags&(1+2))))
9527         {
9528             if (flags==1 || n==0)
9529                 printmessage16("%s", buffer);
9530             if (danum != oldnum)
9531                 asksave = 1;
9532             oldnum = danum;
9533             break;
9534         }
9535     }
9536 
9537     if (keystatus[sc_Escape] && (flags&4))
9538         oldnum = INT32_MIN;
9539 
9540     clearkeys();
9541 
9542     return oldnum;
9543 }
9544 
getnumber_clearline(void)9545 static void getnumber_clearline(void)
9546 {
9547     char cbuf[128];
9548     int32_t i;
9549     for (i=0; i<min(xdim>>3, (signed)sizeof(cbuf)-1); i++)
9550         cbuf[i] = ' ';
9551     cbuf[i] = 0;
9552     printext256(0, 0, whitecol, 0, cbuf, 0);
9553 }
9554 
9555 // sign: |16: don't draw scene
_getnumber256(const char * namestart,int32_t num,int32_t maxnumber,char sign,const char * (func)(int32_t))9556 int32_t _getnumber256(const char *namestart, int32_t num, int32_t maxnumber, char sign, const char *(func)(int32_t))
9557 {
9558     char buffer[80], ournamestart[80-13], ch;
9559     int32_t danum, oldnum;
9560     uint8_t flags = (sign&(2|4|8|16))>>1;
9561     sign &= 1;
9562 
9563     danum = num;
9564     oldnum = danum;
9565 
9566     // need to have 11+2==13 chars room at the end
9567     // (max. string length of an int32, "_ ")
9568     Bstrncpyz(ournamestart, namestart, sizeof(ournamestart));
9569 
9570     keyFlushChars();
9571     while (keystatus[sc_Escape] == 0)
9572     {
9573         if (handleevents())
9574             quitevent = 0;
9575 
9576         if ((flags&8)==0)
9577             M32_DrawRoomsAndMasks();
9578 
9579         ch = keyGetChar();
9580         if (keystatus[sc_Escape])
9581             break;
9582         clearkeys();
9583 
9584         g_mouseBits = 0;
9585         searchx = osearchx;
9586         searchy = osearchy;
9587 
9588         inputchecked = 1;
9589 
9590         if ((flags&8)==0)
9591             CallExtCheckKeys();
9592 
9593         getnumber_clearline();
9594 
9595         Bsprintf(buffer,"%s%d",ournamestart,danum);
9596         // max strlen now 66+11==77
9597         if ((int32_t) totalclock & 32)
9598             Bstrcat(buffer,"_ ");
9599         // max strlen now 79
9600         printmessage256(0, 0, buffer);
9601         if (func != NULL)
9602         {
9603             Bsnprintf(buffer, sizeof(buffer), "%s", func(danum));
9604             printmessage256(0, 9, buffer);
9605         }
9606 
9607         g_iReturnVar = danum;
9608         VM_OnEvent(EVENT_GETNUMBER, -1);
9609 
9610         videoShowFrame(1);
9611 
9612         if (getnumber_internal1(ch, &danum, maxnumber, sign) ||
9613             getnumber_autocomplete(ournamestart, ch, &danum, flags&(1+2)))
9614         {
9615             if (danum != oldnum)
9616                 asksave = 1;
9617             oldnum = danum;
9618             break;
9619         }
9620     }
9621 
9622     if (keystatus[sc_Escape] && (flags&4))
9623         oldnum = INT32_MIN;
9624 
9625     clearkeys();
9626 
9627     lockclock = (int32_t) totalclock;  //Reset timing
9628 
9629     return oldnum;
9630 }
9631 
9632 // querystr: e.g. "Name: ", must be !=NULL
9633 // defaultstr: can be NULL
9634 //  NO overflow checks are done when copying them!
9635 // maxlen: maximum length of entry string, if ==1, enter single char
9636 // completion: 0=none, 1=names[][], 2=taglabels
getstring_simple(const char * querystr,const char * defaultstr,int32_t maxlen,int32_t completion)9637 const char *getstring_simple(const char *querystr, const char *defaultstr, int32_t maxlen, int32_t completion)
9638 {
9639     static char buf[128];
9640     int32_t ei=0, qrylen=0, maxidx, havecompl=0;
9641     char ch;
9642 
9643     keyFlushChars();
9644     clearkeys();
9645 
9646     Bmemset(buf, 0, sizeof(buf));
9647 
9648     qrylen = Bstrlen(querystr);
9649     Bmemcpy(buf, querystr, qrylen);
9650 
9651     if (maxlen==0)
9652         maxlen = 64;
9653 
9654     maxidx = min((signed)sizeof(buf), xdim>>3);
9655 
9656     ei = qrylen;
9657 
9658     if (defaultstr)
9659     {
9660         int32_t deflen = Bstrlen(defaultstr);
9661         Bmemcpy(&buf[ei], defaultstr, deflen);
9662         ei += deflen;
9663     }
9664 
9665     buf[ei] = '_';
9666     buf[ei+1] = 0;
9667 
9668     while (1)
9669     {
9670         if (in3dmode())
9671             getnumber_clearline();
9672 
9673         if (in3dmode())
9674             printext256(0, 0, whitecol, 0, buf, 0);
9675         else
9676             _printmessage16("%s", buf);
9677 
9678         videoShowFrame(1);
9679 
9680         if (handleevents())
9681             quitevent = 0;
9682 
9683         idle();
9684         ch = keyGetChar();
9685 
9686         if (ch==13)
9687         {
9688             if (maxlen != 1)
9689                 buf[ei] = 0;
9690             break;
9691         }
9692         else if (keystatus[sc_Escape])
9693         {
9694             clearkeys();
9695             return completion ? NULL : defaultstr;
9696         }
9697 
9698         if (maxlen!=1)
9699         {
9700             // blink...
9701             if ((int32_t) totalclock&32)
9702             {
9703                 buf[ei] = '_';
9704                 buf[ei+1] = 0;
9705             }
9706             else
9707             {
9708                 buf[ei] = 0;
9709             }
9710 
9711             if (ei>qrylen && (ch==8 || ch==127))
9712             {
9713                 buf[ei--] = 0;
9714                 buf[ei] = '_';
9715                 havecompl = 0;
9716             }
9717             else if (ei<maxidx-2 && ei-qrylen<maxlen && isprint(ch))
9718             {
9719                 if (completion==2 && ch==' ')
9720                     ch = '_';
9721                 buf[ei++] = ch;
9722                 buf[ei] = '_';
9723                 buf[ei+1] = 0;
9724             }
9725 
9726             if (completion && ((ei>qrylen && ch==9) || havecompl))  // tab: maybe do auto-completion
9727             {
9728                 char cmpbuf[128];
9729                 char completions[3][16];
9730                 const char *cmpstr;
9731                 int32_t len=ei-qrylen, i, j, k=len, first=1, numcompl=0;
9732 
9733                 Bmemcpy(cmpbuf, &buf[qrylen], len);
9734                 cmpbuf[len] = 0;
9735 
9736                 for (i=(completion!=1); i<((completion==1)?MAXTILES:32768); i++)
9737                 {
9738                     cmpstr = (completion==1) ? names[i] : taglab_getlabel(i);
9739                     if (!cmpstr)
9740                         continue;
9741 
9742                     if (Bstrncasecmp(cmpbuf, cmpstr, len) || Bstrlen(cmpstr)==(unsigned)len)  // compare the prefix
9743                         continue;
9744 
9745                     if (ch==9)
9746                     {
9747                         if (first)
9748                         {
9749                             Bstrncpy(cmpbuf+len, cmpstr+len, sizeof(cmpbuf)-len);
9750                             cmpbuf[sizeof(cmpbuf)-1] = 0;
9751                             first = 0;
9752                         }
9753                         else
9754                         {
9755                             for (k=len; cmpstr[k] && cmpbuf[k] && Btolower(cmpstr[k])==Btolower(cmpbuf[k]); k++)
9756                                 /* nop */;
9757                             cmpbuf[k] = 0;
9758                         }
9759                     }
9760 
9761                     if (numcompl<3)
9762                     {
9763                         Bstrncpyz(completions[numcompl], cmpstr+len, sizeof(completions[0]));
9764 
9765                         for (k=0; completions[numcompl][k]; k++)
9766                             completions[numcompl][k] = Btolower(completions[numcompl][k]);
9767                         numcompl++;
9768                     }
9769 
9770                     for (k=len; cmpbuf[k]; k++)
9771                         cmpbuf[k] = Btolower(cmpbuf[k]);
9772                 }
9773 
9774                 ei = qrylen;
9775                 for (i=0; i<k && i<maxlen && ei<maxidx-2; i++)
9776                     buf[ei++] = cmpbuf[i];
9777 
9778                 if (k==len && numcompl>0)  // no chars autocompleted/completion request
9779                 {
9780                     buf[ei] = '{';
9781                     buf[ei+1] = 0;
9782 
9783                     i = ei+1;
9784                     for (k=0; k<numcompl; k++)
9785                     {
9786                         j = 0;
9787                         while (i<maxidx-1 && completions[k][j])
9788                             buf[i++] = completions[k][j++];
9789                         if (i<maxidx-1)
9790                             buf[i++] = (k==numcompl-1) ? '}' : ',';
9791                     }
9792                     buf[i] = 0;
9793 
9794                     havecompl = 1;
9795                 }
9796                 else
9797                 {
9798                     buf[ei] = '_';
9799                     buf[ei+1] = 0;
9800                 }
9801             }
9802         }
9803         else
9804         {
9805             if (isalnum(ch) || ch==' ')
9806                 buf[ei] = Btoupper(ch);
9807         }
9808     }
9809 
9810     clearkeys();
9811 
9812     return buf+qrylen;
9813 }
9814 
getfilenames(const char * path,const char * kind)9815 static int32_t getfilenames(const char *path, const char *kind)
9816 {
9817     const int32_t addflags = (!pathsearchmode && grponlymode ? BUILDVFS_OPT_NOSTACK : 0);
9818 
9819     fnlist_getnames(&fnlist, path, kind, addflags|BUILDVFS_FIND_DRIVE, addflags);
9820 
9821     finddirshigh = fnlist.finddirs;
9822     findfileshigh = fnlist.findfiles;
9823     currentlist = (findfileshigh != NULL);
9824 
9825     return 0;
9826 }
9827 
tweak_sboardfilename(void)9828 static void tweak_sboardfilename(void)
9829 {
9830     if (pathsearchmode)
9831         Bcanonicalisefilename(selectedboardfilename, 1);  // clips off the last token and compresses relative path
9832     else
9833         Bcorrectfilename(selectedboardfilename, 1);
9834 }
9835 
9836 ////////// FILE SELECTION MENU //////////
9837 
9838 static char g_oldpath[BMAX_PATH];
9839 
menuselect_try_findlast(void)9840 static void menuselect_try_findlast(void)
9841 {
9842     // PK 20080103: start with last selected map
9843     const char *boardbasename = getbasefn(boardfilename);
9844 
9845     for (; findfileshigh; findfileshigh=findfileshigh->next)
9846         if (!Bstrcmp(findfileshigh->name, boardbasename))
9847             break;
9848 
9849     if (!findfileshigh)
9850         findfileshigh = fnlist.findfiles;
9851 }
9852 
9853 // vvv PK ------------------------------------
9854 // copied off menuselect
9855 
menuselect_auto(int direction,int skip)9856 static int32_t menuselect_auto(int direction, int skip) // 20080104: jump to next (direction!=0) or prev (direction==0) file
9857 {
9858     Bstrcpy(selectedboardfilename, g_oldpath);
9859     tweak_sboardfilename();
9860 
9861     getfilenames(selectedboardfilename, "*.map");
9862     if (fnlist.numfiles==0)
9863         return -2;
9864 
9865     menuselect_try_findlast();
9866 
9867     do
9868     {
9869         if (direction)
9870         {
9871             if (findfileshigh->next)
9872                 findfileshigh=findfileshigh->next;
9873             else
9874                 return -1;
9875         }
9876         else
9877         {
9878             if (findfileshigh->prev)
9879                 findfileshigh=findfileshigh->prev;
9880             else
9881                 return -1;
9882         }
9883     } while (skip--);
9884 
9885     Bstrcat(selectedboardfilename, findfileshigh->name);
9886 
9887     return 0;
9888 }
9889 // ^^^ PK ------------------------------------
9890 
menuselect(void)9891 static int32_t menuselect(void)
9892 {
9893     int32_t listsize;
9894     int32_t i;
9895     char ch, buffer[96];
9896     const int32_t bakpathsearchmode = pathsearchmode;
9897 
9898     Bstrcpy(selectedboardfilename, g_oldpath);
9899     tweak_sboardfilename();
9900 
9901     getfilenames(selectedboardfilename, "*.map");
9902 
9903     menuselect_try_findlast();
9904 
9905     _printmessage16("Select map file with arrow keys and enter.");
9906 
9907     ydim16 = ydim-STATUS2DSIZ2;
9908     listsize = (ydim16-32)/9;
9909 
9910     do
9911     {
9912         videoBeginDrawing(); //{{{
9913 
9914         CLEARLINES2D(0, ydim16, 0);
9915 
9916         if (pathsearchmode)
9917             Bstrcpy(buffer,"Local filesystem mode.  Ctrl-F: game filesystem");
9918         else
9919             Bsnprintf(buffer, sizeof(buffer),
9920                       "Game filesystem %smode.  Ctrl-F: local filesystem, Ctrl-G: %s",
9921                       grponlymode?"GRP-only ":"", grponlymode?"all files":"GRP contents only");
9922 
9923         printext16(halfxdim16-(8*Bstrlen(buffer)/2), 4, editorcolors[12],editorcolors[0],buffer,0);
9924 
9925         Bsnprintf(buffer, sizeof(buffer), "(%d dirs, %d files) %s",
9926                   fnlist.numdirs, fnlist.numfiles, selectedboardfilename);
9927 
9928         printext16(8,ydim16-8-1,editorcolors[8],editorcolors[0],buffer,0);
9929 
9930         if (finddirshigh)
9931         {
9932             const BUILDVFS_FIND_REC *dir = finddirshigh;
9933 
9934             for (i=(listsize/2)-1; i>=0; i--)
9935             {
9936                 if (!dir->prev) break;
9937                 else dir=dir->prev;
9938             }
9939             for (i=0; ((i<listsize) && dir); i++, dir=dir->next)
9940             {
9941                 int32_t c = (dir->type == BUILDVFS_FIND_DIR ? 2 : 3); //PK
9942                 Bmemset(buffer,0,sizeof(buffer));
9943                 Bstrncpy(buffer,dir->name,25);
9944                 if (Bstrlen(buffer) == 25)
9945                     buffer[21] = buffer[22] = buffer[23] = '.', buffer[24] = 0;
9946                 if (dir == finddirshigh)
9947                 {
9948                     if (currentlist == 0) printext16(8,16+9*i,editorcolors[c|8],editorcolors[0],"->",0);
9949                     printext16(32,16+9*i,editorcolors[c|8],editorcolors[0],buffer,0);
9950                 }
9951                 else
9952                 {
9953                     printext16(32,16+9*i,editorcolors[c],editorcolors[0],buffer,0);
9954                 }
9955             }
9956         }
9957 
9958         if (findfileshigh)
9959         {
9960             const BUILDVFS_FIND_REC *dir = findfileshigh;
9961 
9962             for (i=(listsize/2)-1; i>=0; i--)
9963             {
9964                 if (!dir->prev) break;
9965                 else dir=dir->prev;
9966             }
9967             for (i=0; (i<listsize && dir); i++, dir=dir->next)
9968             {
9969                 if (dir == findfileshigh)
9970                 {
9971                     if (currentlist == 1) printext16(240,16+9*i,editorcolors[7|8],editorcolors[0],"->",0);
9972                     printext16(240+24,16+9*i,editorcolors[7|8],editorcolors[0],dir->name,0);
9973                 }
9974                 else
9975                 {
9976                     printext16(240+24,16+9*i,editorcolors[7],editorcolors[0],dir->name,0);
9977                 }
9978             }
9979         }
9980 
9981         videoEndDrawing(); //}}}
9982         videoShowFrame(1);
9983 
9984         keystatus[sc_LeftArrow]  = 0;
9985         keystatus[sc_RightArrow] = 0;
9986         keystatus[sc_UpArrow]    = 0;
9987         keystatus[sc_DownArrow]  = 0;
9988         keystatus[sc_Enter]      = 0;
9989         keystatus[sc_BackSpace]  = 0;
9990         keystatus[sc_Tab]        = 0;
9991         keystatus[sc_Escape]     = 0;
9992 
9993         ch = 0;                      //Interesting fakery of ch = getch()
9994 
9995         while (ch == 0)
9996         {
9997             if (handleevents())
9998                 if (quitevent)
9999                 {
10000                     keystatus[sc_Escape] = 1;
10001                     quitevent = 0;
10002                 }
10003 
10004             idle();
10005 
10006             ch = keyGetChar();
10007 
10008             {
10009                 // JBF 20040208: seek to first name matching pressed character
10010                 BUILDVFS_FIND_REC *seeker = currentlist ? fnlist.findfiles : fnlist.finddirs;
10011                 if (keystatus[sc_Home]||keystatus[sc_End]) // home/end
10012                 {
10013                     while (keystatus[sc_End] ? seeker->next : seeker->prev)
10014                         seeker = keystatus[sc_End] ? seeker->next : seeker->prev;
10015 
10016                     if (seeker)
10017                     {
10018                         if (currentlist) findfileshigh = seeker;
10019                         else finddirshigh = seeker;
10020                     }
10021                     ch = keystatus[sc_End] ? 80 : 72;
10022                     keystatus[sc_Home] = keystatus[sc_End] = 0;
10023                 }
10024                 else if (keystatus[sc_PgUp]|keystatus[sc_PgDn]) // page up/down
10025                 {
10026                     seeker = currentlist?findfileshigh:finddirshigh;
10027                     i = (ydim2d-STATUS2DSIZ2-48)>>5/*3*/;  //PK
10028 
10029                     while (i>0)
10030                     {
10031                         if (keystatus[sc_PgDn] ? seeker->next : seeker->prev)
10032                             seeker = keystatus[sc_PgDn] ? seeker->next : seeker->prev;
10033                         i--;
10034                     }
10035                     if (seeker)
10036                     {
10037                         if (currentlist) findfileshigh = seeker;
10038                         else finddirshigh = seeker;
10039                     }
10040                     ch = keystatus[sc_PgDn]?80:72;
10041                     keystatus[sc_PgUp] = keystatus[sc_PgDn] = 0;
10042                 }
10043                 else
10044                 {
10045                     char ch2;
10046                     if (ch > 0 && ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') ||
10047                                    (ch >= '0' && ch <= '9') || (ch=='_')))
10048                     {
10049 #ifdef _WIN32
10050                         if (ch >= 'a') ch -= ('a'-'A');
10051 #endif
10052                         while (seeker)
10053                         {
10054                             ch2 = seeker->name[0];
10055 #ifdef _WIN32
10056                             if (ch2 >= 'a' && ch2 <= 'z') ch2 -= ('a'-'A');
10057 #endif
10058                             if (ch2 == ch) break;
10059                             seeker = seeker->next;
10060                         }
10061                         if (seeker)
10062                         {
10063                             if (currentlist) findfileshigh = seeker;
10064                             else finddirshigh = seeker;
10065                             continue;
10066                         }
10067                     }
10068                 }
10069             }
10070             if (keystatus[sc_LeftArrow]) ch = 9;		// left arr
10071             if (keystatus[sc_RightArrow]) ch = 9;		// right arr
10072             if (keystatus[sc_UpArrow]) ch = 72;   	// up arr
10073             if (keystatus[sc_DownArrow]) ch = 80;   	// down arr
10074         }
10075 
10076         if (ch==6)  // Ctrl-F
10077         {
10078             currentlist = 0;
10079             pathsearchmode = 1-pathsearchmode;
10080             if (pathsearchmode)
10081             {
10082                 Bstrcpy(selectedboardfilename, "");
10083                 Bcanonicalisefilename(selectedboardfilename, 0);
10084             }
10085             else Bstrcpy(selectedboardfilename, "/");
10086 
10087             getfilenames(selectedboardfilename, "*.map");
10088             Bstrcpy(g_oldpath, selectedboardfilename);
10089         }
10090         else if (ch==7)  // Ctrl-G
10091         {
10092             if (!pathsearchmode)
10093             {
10094                 grponlymode = 1-grponlymode;
10095                 getfilenames(selectedboardfilename, "*.map");
10096                 Bstrcpy(g_oldpath, selectedboardfilename);
10097             }
10098         }
10099         else if (ch == 9)
10100         {
10101             if ((currentlist == 0 && fnlist.findfiles) || (currentlist == 1 && fnlist.finddirs))
10102                 currentlist = 1-currentlist;
10103         }
10104         else if (keystatus[sc_UpArrow] /*(ch == 75) || (ch == 72)*/)
10105         {
10106             if (currentlist == 0)
10107             {
10108                 if (finddirshigh && finddirshigh->prev)
10109                     finddirshigh = finddirshigh->prev;
10110             }
10111             else
10112             {
10113                 if (findfileshigh && findfileshigh->prev)
10114                     findfileshigh = findfileshigh->prev;
10115             }
10116         }
10117         else if (keystatus[sc_DownArrow] /*(ch == 77) || (ch == 80)*/)
10118         {
10119             if (currentlist == 0)
10120             {
10121                 if (finddirshigh && finddirshigh->next)
10122                     finddirshigh = finddirshigh->next;
10123             }
10124             else
10125             {
10126                 if (findfileshigh && findfileshigh->next)
10127                     findfileshigh = findfileshigh->next;
10128             }
10129         }
10130         else if (keystatus[sc_BackSpace]) // backspace
10131         {
10132             Bstrcat(selectedboardfilename, "../");
10133             tweak_sboardfilename();
10134             Bstrcpy(g_oldpath, selectedboardfilename);
10135             getfilenames(selectedboardfilename, "*.map");
10136             keystatus[sc_BackSpace] = 0;
10137         }
10138         else if (ch == 13 && currentlist == 0)
10139         {
10140             if (finddirshigh->type == BUILDVFS_FIND_DRIVE)
10141                 Bstrcpy(selectedboardfilename, finddirshigh->name);
10142             else
10143                 Bstrcat(selectedboardfilename, finddirshigh->name);
10144 
10145             Bstrcat(selectedboardfilename, "/");
10146             tweak_sboardfilename();
10147 
10148             Bstrcpy(g_oldpath, selectedboardfilename);
10149             //printf("Changing directories to: %s\n", selectedboardfilename);
10150 
10151             if (finddirshigh == fnlist.finddirs)
10152             {
10153                 getfilenames(selectedboardfilename, "*.map");
10154                 currentlist = 0;
10155             }
10156             else getfilenames(selectedboardfilename, "*.map");;
10157 
10158 
10159             ch = 0;
10160 
10161             videoBeginDrawing();
10162             CLEARLINES2D(0, ydim16, 0);
10163             videoEndDrawing();
10164             videoShowFrame(1);
10165         }
10166 
10167         if (ch == 13 && !findfileshigh) ch = 0;
10168     }
10169     while (ch != 13 && ch != 27);
10170 
10171     if (ch == 13)
10172     {
10173         Bstrcat(selectedboardfilename, findfileshigh->name);
10174         //printf("Selected file: %s\n", selectedboardfilename);
10175 
10176         return 0;
10177     }
10178 
10179     pathsearchmode = bakpathsearchmode;
10180 
10181     return -1;
10182 }
10183 
10184 #if 0
10185 static inline int32_t imod(int32_t a, int32_t b)
10186 {
10187     if (a >= 0)
10188         return a%b;
10189 
10190     return ((a+1)%b)+b-1;
10191 }
10192 #endif
10193 
fillsector_maybetrans(int16_t sectnum,int32_t fillcolor,uint8_t dotrans)10194 int32_t fillsector_maybetrans(int16_t sectnum, int32_t fillcolor, uint8_t dotrans)
10195 {
10196     if (sectnum == -1)
10197         return 0;
10198 
10199     int32_t x1, x2, y1, y2, sy, y, daminy;
10200     int32_t lborder, rborder, uborder, dborder, miny, maxy, dax;
10201     int16_t z, zz, startwall, endwall, fillcnt;
10202 
10203     char col;
10204 
10205     if (fillcolor == -1)
10206         col = editorcolors[14]-(M32_THROB>>1);
10207     else
10208         col = fillcolor;
10209 
10210     lborder = 0; rborder = xdim;
10211     y = OSD_GetRowsCur();
10212     uborder = (y>=0)?(y+1)*8:0; dborder = ydim16-STATUS2DSIZ2;
10213 
10214 
10215     miny = dborder-1;
10216     maxy = uborder;
10217 
10218     startwall = sector[sectnum].wallptr;
10219     endwall = startwall + sector[sectnum].wallnum - 1;
10220     for (z=startwall; z<=endwall; z++)
10221     {
10222         editorGet2dScreenCoordinates(&x1,&y1, wall[z].x-pos.x,wall[z].y-pos.y, zoom);
10223         if (m32_sideview)
10224             y1 += getscreenvdisp(getflorzofslope(sectnum,wall[z].x,wall[z].y)-pos.z, zoom);
10225 
10226         x1 += halfxdim16;
10227         y1 += midydim16;
10228 
10229         if (m32_sideview)
10230         {
10231             tempxyar[z][0] = x1;
10232             tempxyar[z][1] = y1;
10233         }
10234 
10235         miny = min(miny, y1);
10236         maxy = max(maxy, y1);
10237     }
10238 
10239     if (miny < uborder) miny = uborder;
10240     if (maxy >= dborder) maxy = dborder-1;
10241 
10242     daminy = miny;// +2 - imod(miny+2, 3);
10243     if (sector[sectnum].floorz > minhlsectorfloorz)
10244         daminy++;
10245 
10246 //+((totalclock>>2)&3)
10247     for (sy=daminy; sy<=maxy; sy++)	// JBF 20040116: numframes%3 -> (totalclock>>2)&3
10248     {
10249         y = pos.y + ((sy-midydim16)<<14)/zoom;
10250 
10251         fillist[0] = lborder; fillcnt = 1;
10252         for (z=startwall; z<=endwall; z++)
10253         {
10254             if (m32_sideview)
10255             {
10256                 x1 = tempxyar[z][0];
10257                 y1 = tempxyar[z][1];
10258                 x2 = tempxyar[wall[z].point2][0];
10259                 y2 = tempxyar[wall[z].point2][1];
10260 
10261                 if (y1 > y2)
10262                 {
10263                     swaplong(&x1, &x2);
10264                     swaplong(&y1, &y2);
10265                 }
10266 
10267                 if (y1 <= sy && sy < y2)
10268                 {
10269                     if (fillcnt == ARRAY_SSIZE(fillist))
10270                         break;
10271 
10272                     x1 += scale(sy-y1, x2-x1, y2-y1);
10273                     fillist[fillcnt++] = x1;
10274                 }
10275             }
10276             else
10277             {
10278                 x1 = wall[z].x; x2 = POINT2(z).x;
10279                 y1 = wall[z].y; y2 = POINT2(z).y;
10280 
10281                 if (y1 > y2)
10282                 {
10283                     swaplong(&x1, &x2);
10284                     swaplong(&y1, &y2);
10285                 }
10286 
10287                 if (y1 <= y && y < y2)
10288                     //if (x1*(y-y2) + x2*(y1-y) <= 0)
10289                 {
10290                     dax = x1 + scale(y-y1, x2-x1, y2-y1);
10291                     dax = halfxdim16 + (((dax-pos.x)*zoom)>>14);
10292                     if (dax >= lborder)
10293                     {
10294                         if (fillcnt == ARRAY_SSIZE(fillist))
10295                             break;
10296 
10297                         fillist[fillcnt++] = dax;
10298                     }
10299                 }
10300             }
10301         }
10302 
10303         if (fillcnt > 0)
10304         {
10305             for (z=1; z<fillcnt; z++)
10306                 for (zz=0; zz<z; zz++)
10307                     if (fillist[z] < fillist[zz])
10308                         swaplong(&fillist[z], &fillist[zz]);
10309 
10310             for (z=(fillcnt&1); z<fillcnt-1; z+=2)
10311             {
10312                 if (fillist[z] > rborder)
10313                     break;
10314                 if (fillist[z+1] > rborder)
10315                     fillist[z+1] = rborder;
10316 
10317                 editorDraw2dLine(fillist[z]+1,sy, fillist[z+1]-1,sy, dotrans ? -col : col);  //editorcolors[fillcolor]
10318             }
10319         }
10320     }
10321 
10322     return 0;
10323 }
10324 
whitelinescan(int16_t sucksect,int16_t dalinehighlight)10325 static int16_t whitelinescan(int16_t sucksect, int16_t dalinehighlight)
10326 {
10327     int32_t i, j, k;
10328     int16_t tnewnumwalls;
10329 
10330     if (numsectors >= MAXSECTORS)
10331         return MAXWALLS+1;
10332 
10333     Bmemcpy(&sector[numsectors], &sector[sucksect], sizeof(sectortype));
10334     sector[numsectors].wallptr = numwalls;
10335     sector[numsectors].wallnum = 0;
10336 
10337     i = dalinehighlight;
10338     tnewnumwalls = numwalls;
10339     do
10340     {
10341         if (tnewnumwalls >= MAXWALLS)
10342             return MAXWALLS+1;
10343 
10344         j = lastwall(i);
10345         if (wall[j].nextwall >= 0)
10346         {
10347             j = wall[j].point2;
10348             for (k=0; k<numwalls; k++)
10349             {
10350                 if (POINT2(k).x == wall[j].x && POINT2(k).y == wall[j].y)
10351                     if (wall[k].nextwall == -1)
10352                     {
10353                         j = k;
10354                         break;
10355                     }
10356             }
10357         }
10358 
10359         Bmemcpy(&wall[tnewnumwalls], &wall[i], sizeof(walltype));
10360 
10361         wall[tnewnumwalls].nextwall = j;
10362         wall[tnewnumwalls].nextsector = sectorofwall(j);
10363 
10364         tnewnumwalls++;
10365         sector[numsectors].wallnum++;
10366 
10367         i = j;
10368     }
10369     while (i != dalinehighlight);
10370 
10371     for (i=numwalls; i<tnewnumwalls-1; i++)
10372         wall[i].point2 = i+1;
10373     wall[tnewnumwalls-1].point2 = numwalls;
10374 
10375     return (clockdir(numwalls) == CLOCKDIR_CCW) ? -1 : tnewnumwalls;
10376 }
10377 
10378 //
10379 // names.h loading
10380 //
10381 
10382 enum {
10383     T_INCLUDE = 0,
10384     T_DEFINE = 1,
10385 
10386     T_DUMMY,
10387 };
10388 
parsenamesfile(scriptfile * script)10389 static int32_t parsenamesfile(scriptfile *script)
10390 {
10391     int32_t syms = 0;
10392 
10393     tokenlist const tokens[] =
10394     {
10395         { "include",         T_INCLUDE          },
10396         { "#include",        T_INCLUDE          },
10397         { "define",          T_DEFINE           },
10398         { "#define",         T_DEFINE           },
10399 
10400         { "dynamicremap",    T_DUMMY            },
10401     };
10402 
10403     while (1)
10404     {
10405         int32_t tokn = getatoken(script,tokens,ARRAY_SIZE(tokens));
10406         char *cmdtokptr = script->ltextptr;
10407         switch (tokn)
10408         {
10409         case T_INCLUDE:
10410         {
10411             char *fn;
10412             if (scriptfile_getstring(script,&fn))
10413             {
10414                 initprintf("Error: Malformed include on line %s:%d\n",
10415                            script->filename,scriptfile_getlinum(script,cmdtokptr));
10416                 break;
10417             }
10418 
10419             scriptfile *included;
10420 
10421             included = scriptfile_fromfile(fn);
10422             if (!included)
10423             {
10424                 initprintf("Error: Failed including %s on line %s:%d\n",
10425                            fn, script->filename,scriptfile_getlinum(script,cmdtokptr));
10426                 break;
10427             }
10428 
10429             initprintf("Including: %s\n", fn);
10430 
10431             syms += parsenamesfile(included);
10432             scriptfile_close(included);
10433 
10434             break;
10435         }
10436         case T_DEFINE:
10437         {
10438             char *name;
10439             int32_t number;
10440 
10441             if (scriptfile_getstring(script,&name))
10442             {
10443                 initprintf("Error: Malformed define on line %s:%d\n",
10444                            script->filename, scriptfile_getlinum(script,cmdtokptr));
10445                 break;
10446             }
10447 
10448             if (scriptfile_getsymbol(script,&number))
10449             {
10450                 initprintf("Error: No number given for name \"%s\" on line %s:%d\n",
10451                            name, script->filename, scriptfile_getlinum(script,cmdtokptr));
10452                 break;
10453             }
10454 
10455             if ((unsigned)number >= MAXTILES)
10456             {
10457                 initprintf("Error: Constant %d for name \"%s\" out of range on line %s:%d\n",
10458                            number, name, script->filename, scriptfile_getlinum(script,cmdtokptr));
10459                 break;
10460             }
10461 
10462             if (Bstrlen(name) > 24)
10463                 initprintf("Warning: Truncating name \"%s\" to 24 characters on line %s:%d\n",
10464                            name, script->filename, scriptfile_getlinum(script,cmdtokptr));
10465 
10466             Bstrncpyz(names[number], name, 25);
10467             name = names[number];
10468 
10469             ++syms;
10470 
10471             if (scriptfile_addsymbolvalue(name,number) < 0)
10472                 initprintf("Warning: Symbol %s was NOT redefined to %d on line %s:%d\n",
10473                            name, number, script->filename, scriptfile_getlinum(script,cmdtokptr));
10474             break;
10475         }
10476         case T_EOF:
10477             return syms;
10478         default:
10479             break;
10480         }
10481     }
10482 
10483     return syms;
10484 }
10485 
loadnames(const char * namesfile)10486 static void loadnames(const char *namesfile)
10487 {
10488     scriptfile *script = scriptfile_fromfile(namesfile);
10489 
10490     if (!script)
10491         return;
10492 
10493     initprintf("Loading names file: %s\n", namesfile);
10494 
10495     int32_t const syms = parsenamesfile(script);
10496     initprintf("Loaded %d names.\n", syms);
10497 
10498     scriptfile_close(script);
10499 
10500     scriptfile_clearsymbols();
10501 }
10502 
10503 
printcoords16(int32_t posxe,int32_t posye,int16_t ange)10504 void printcoords16(int32_t posxe, int32_t posye, int16_t ange)
10505 {
10506     char snotbuf[128];
10507     int32_t i, m;
10508     int32_t v8 = (numsectors > MAXSECTORSV7 || numwalls > MAXWALLSV7 ||
10509                   Numsprites > MAXSPRITESV7 || numyaxbunches > 0);
10510 #if M32_UNDO
10511     Bsprintf(snotbuf,"x:%d y:%d ang:%d r%d",posxe,posye,ange,map_revision-1);
10512 #else
10513     Bsprintf(snotbuf,"x:%d y:%d ang:%d",posxe,posye,ange);
10514 #endif
10515     i = 0;
10516     while ((snotbuf[i] != 0) && (i < 33))
10517         i++;
10518     while (i < 33)
10519     {
10520         snotbuf[i] = 32;
10521         i++;
10522     }
10523     snotbuf[33] = 0;
10524 
10525     clearministatbar16();
10526 
10527     printext16(8, ydim-STATUS2DSIZ+128, whitecol, -1, snotbuf,0);
10528 
10529     if (highlightcnt<=0 && highlightsectorcnt<=0)
10530     {
10531         m = Bsprintf(snotbuf,"%d/%d %s. %d",
10532                      numsectors, v8?MAXSECTORSV8:MAXSECTORSV7,
10533                      numyaxbunches>0 ? "SEC":"sec", numwalls);
10534         if (numyaxbunches > 0)
10535         {
10536             if (xdim >= 800)
10537                 Bsprintf(&snotbuf[m], "/%d wal. %d/16k spr. %d/256 bn.",
10538                          MAXWALLSV8, Numsprites, numyaxbunches);
10539             else
10540                 Bsprintf(&snotbuf[m], " wal. %d spr. %d/256 bn.",
10541                          Numsprites, numyaxbunches);
10542         }
10543         else
10544         {
10545             if (xdim >= 800)
10546                 Bsprintf(&snotbuf[m], "/%d wal. %d/%d spr.",
10547                          v8?MAXWALLSV8:MAXWALLSV7, Numsprites,
10548                          v8?MAXSPRITESV8:MAXSPRITESV7);
10549             else
10550                 Bsprintf(&snotbuf[m], "/%dk wal. %d/%dk spr.",
10551                          (v8?MAXWALLSV8:MAXWALLSV7)/1000, Numsprites,
10552                          (v8?MAXSPRITESV8:MAXSPRITESV7)/1000);
10553         }
10554     }
10555     else
10556     {
10557         if (highlightcnt>0)
10558         {
10559             m = 0;
10560             for (i=0; i<highlightcnt; i++)
10561                 m += !!(highlight[i]&16384);
10562             Bsprintf(snotbuf, "%d sprites, %d walls selected", m, highlightcnt-m);
10563         }
10564         else if (highlightsectorcnt>0)
10565         {
10566             int32_t ii=Bsprintf(snotbuf, "%d sectors with a total of %d walls selected",
10567                                 highlightsectorcnt, numhlsecwalls);
10568             if (m32_rotateang)
10569                 Bsprintf(&snotbuf[ii], " (ang=%d)", m32_rotateang);
10570         }
10571         else
10572         {
10573             snotbuf[0] = 0;
10574         }
10575 
10576         v8 = 1;  // yellow color
10577     }
10578 
10579     m = xdim/8 - 264/8;
10580     m = clamp(m, 1, (signed)sizeof(snotbuf)-1);
10581 
10582     i = 0;
10583     while (snotbuf[i] && i < m)
10584         i++;
10585     while (i < m)
10586     {
10587         snotbuf[i] = 32;
10588         i++;
10589     }
10590     snotbuf[m] = 0;
10591 
10592     printext16(264, ydim-STATUS2DSIZ+128, v8?editorcolors[10]:whitecol, -1, snotbuf,0);
10593 }
10594 
10595 #define DOPRINT(Yofs, fmt, ...) do { \
10596     Bsprintf(snotbuf, fmt, ## __VA_ARGS__); \
10597     printext16(8+col*200, ydim/*-(row*96)*/-STATUS2DSIZ+Yofs, color, -1, snotbuf, 0); \
10598     } while (0)
10599 
showsectordata(int16_t sectnum,int16_t small)10600 void showsectordata(int16_t sectnum, int16_t small)
10601 {
10602     char snotbuf[80];
10603     int32_t col=0;  //,row = 0;
10604     int32_t color = small ? whitecol : editorcolors[11];
10605 
10606     const sectortype *const sec = &sector[sectnum];
10607 
10608     if (small)
10609     {
10610         _printmessage16("^10Sector %d %s ^O(F7 to edit)", sectnum, CallExtGetSectorCaption(sectnum));
10611         return;
10612     }
10613 
10614     DOPRINT(32, "^10Sector %d", sectnum);
10615     DOPRINT(48, "Firstwall: %d", TrackerCast(sec->wallptr));
10616     DOPRINT(56, "Numberofwalls: %d", TrackerCast(sec->wallnum));
10617     DOPRINT(64, "Firstsprite: %d", headspritesect[sectnum]);
10618     DOPRINT(72, "Tags: %d, %d", TrackerCast(sec->hitag), TrackerCast(sec->lotag));
10619     DOPRINT(80, "     (0x%x), (0x%x)", TrackerCast(sec->hitag), TrackerCast(sec->lotag));
10620     DOPRINT(88, "Extra: %d", TrackerCast(sec->extra));
10621     DOPRINT(96, "Visibility: %d", TrackerCast(sec->visibility));
10622     DOPRINT(104, "Pixel height: %d", (sec->floorz-sec->ceilingz)>>8);
10623 
10624     col++;
10625 
10626     DOPRINT(32, "^10CEILING:^O");
10627     DOPRINT(48, "Flags (hex): %x", TrackerCast(sec->ceilingstat));
10628     {
10629         int32_t xp=sec->ceilingxpanning, yp=sec->ceilingypanning;
10630 #ifdef YAX_ENABLE__COMPAT
10631         if (yax_getbunch(sectnum, YAX_CEILING) >= 0)
10632             xp = yp = 0;
10633 #endif
10634         DOPRINT(56, "(X,Y)pan: %d, %d", xp, yp);
10635     }
10636     DOPRINT(64, "Shade byte: %d", TrackerCast(sec->ceilingshade));
10637     DOPRINT(72, "Z-coordinate: %d", TrackerCast(sec->ceilingz));
10638     DOPRINT(80, "Tile number: %d", TrackerCast(sec->ceilingpicnum));
10639     DOPRINT(88, "Ceiling heinum: %d", TrackerCast(sec->ceilingheinum));
10640     DOPRINT(96, "Palookup number: %d", TrackerCast(sec->ceilingpal));
10641 #ifdef YAX_ENABLE
10642     DOPRINT(104, "Bunch number: %d", yax_getbunch(sectnum, YAX_CEILING));
10643 #endif
10644 
10645     col++;
10646 
10647     DOPRINT(32, "^10FLOOR:^O");
10648     DOPRINT(48, "Flags (hex): %x", TrackerCast(sec->floorstat));
10649     {
10650         int32_t xp=sec->floorxpanning, yp=sec->floorypanning;
10651 #ifdef YAX_ENABLE__COMPAT
10652         if (yax_getbunch(sectnum, YAX_FLOOR) >= 0)
10653             xp = yp = 0;
10654 #endif
10655         DOPRINT(56, "(X,Y)pan: %d, %d", xp, yp);
10656     }
10657     DOPRINT(64, "Shade byte: %d", TrackerCast(sec->floorshade));
10658     DOPRINT(72, "Z-coordinate: %d", TrackerCast(sec->floorz));
10659     DOPRINT(80, "Tile number: %d", TrackerCast(sec->floorpicnum));
10660     DOPRINT(88, "Floor heinum: %d", TrackerCast(sec->floorheinum));
10661     DOPRINT(96, "Palookup number: %d", TrackerCast(sec->floorpal));
10662 #ifdef YAX_ENABLE
10663     DOPRINT(104, "Bunch number: %d", yax_getbunch(sectnum, YAX_FLOOR));
10664 #endif
10665 }
10666 
showwalldata(int16_t wallnum,int16_t small)10667 void showwalldata(int16_t wallnum, int16_t small)
10668 {
10669     walltype const * const wal = &wall[wallnum];
10670     int32_t sec;
10671     char snotbuf[80];
10672     int32_t col=0; //, row = 0;
10673     int32_t color = small ? whitecol : editorcolors[11];
10674 
10675     if (small)
10676     {
10677         _printmessage16("^10Wall %d %s ^O(F8 to edit)", wallnum,
10678                         CallExtGetWallCaption(wallnum));
10679         return;
10680     }
10681 
10682     DOPRINT(32, "^10Wall %d", wallnum);
10683     DOPRINT(48, "X-coordinate: %d", TrackerCast(wal->x));
10684     DOPRINT(56, "Y-coordinate: %d", TrackerCast(wal->y));
10685     DOPRINT(64, "Point2: %d", TrackerCast(wal->point2));
10686     DOPRINT(72, "Sector: ^010%d", sectorofwall(wallnum));
10687 
10688     DOPRINT(88, "Tags: %d,  %d", TrackerCast(wal->hitag), TrackerCast(wal->lotag));
10689     DOPRINT(96, "     (0x%x),  (0x%x)", TrackerCast(wal->hitag), TrackerCast(wal->lotag));
10690 
10691     col++;
10692 
10693     DOPRINT(32, "^10%s^O", (wal->picnum>=0 && wal->picnum<MAXTILES) ? names[wal->picnum] : "!INVALID!");
10694     DOPRINT(48, "Flags (hex): %x", TrackerCast(wal->cstat));
10695     DOPRINT(56, "Shade: %d", TrackerCast(wal->shade));
10696     DOPRINT(64, "Pal: %d", TrackerCast(wal->pal));
10697     DOPRINT(72, "(X,Y)repeat: %d, %d", TrackerCast(wal->xrepeat), TrackerCast(wal->yrepeat));
10698     DOPRINT(80, "(X,Y)pan: %d, %d", TrackerCast(wal->xpanning), TrackerCast(wal->ypanning));
10699     DOPRINT(88, "Tile number: %d", TrackerCast(wal->picnum));
10700     DOPRINT(96, "OverTile number: %d", TrackerCast(wal->overpicnum));
10701 
10702     col++;
10703 
10704     DOPRINT(48, "nextsector: %d", TrackerCast(wal->nextsector));
10705     DOPRINT(56, "nextwall: %d", TrackerCast(wal->nextwall));
10706 
10707     DOPRINT(72, "Extra: %d", TrackerCast(wal->extra));
10708 
10709     // TX 20050102 I'm not sure what unit dist<<4 is supposed to be, but dist itself is correct in terms of game coordinates as one would expect
10710     DOPRINT(96,  "Wall length: %d",  wallength(wallnum));
10711 
10712     sec = sectorofwall(wallnum);
10713     DOPRINT(104, "Pixel height: %d", (sector[sec].floorz-sector[sec].ceilingz)>>8);
10714 }
10715 
showspritedata(int16_t spritenum,int16_t small)10716 void showspritedata(int16_t spritenum, int16_t small)
10717 {
10718     spritetype *spr;
10719     char snotbuf[80];
10720     int32_t col=0; //, row = 0;
10721     int32_t color = small ? whitecol : editorcolors[11];
10722 
10723     spr = &sprite[spritenum];
10724 
10725     if (small)
10726     {
10727         _printmessage16("^10Sprite %d %s ^O(F8 to edit)",spritenum, CallExtGetSpriteCaption(spritenum));
10728         return;
10729     }
10730 
10731     DOPRINT(32, "^10Sprite %d", spritenum);
10732     DOPRINT(48, "X-coordinate: %d", TrackerCast(spr->x));
10733     DOPRINT(56, "Y-coordinate: %d", TrackerCast(spr->y));
10734     DOPRINT(64, "Z-coordinate: %d", TrackerCast(spr->z));
10735 
10736     DOPRINT(72, "Sectnum: ^010%d", TrackerCast(spr->sectnum));
10737     DOPRINT(80, "Statnum: %d", TrackerCast(spr->statnum));
10738 
10739     DOPRINT(96, "Tags: %d,  %d", TrackerCast(spr->hitag), TrackerCast(spr->lotag));
10740     DOPRINT(104, "     (0x%x),  (0x%x)", TrackerCast(spr->hitag), TrackerCast(spr->lotag));
10741 
10742     col++;
10743 
10744     DOPRINT(32, "^10,0                        ^O");  // 24 blanks
10745     DOPRINT(32, "^10%s^O", (spr->picnum>=0 && spr->picnum<MAXTILES) ? names[spr->picnum] : "!INVALID!");
10746     DOPRINT(48, "Flags (hex): %x", TrackerCast(spr->cstat));
10747     DOPRINT(56, "Shade: %d", TrackerCast(spr->shade));
10748     DOPRINT(64, "Pal: %d", TrackerCast(spr->pal));
10749     DOPRINT(72, "Blend: %d", TrackerCast(spr->blend));
10750     DOPRINT(80, "(X,Y)repeat: %d, %d", TrackerCast(spr->xrepeat), TrackerCast(spr->yrepeat));
10751     DOPRINT(88, "(X,Y)offset: %d, %d", TrackerCast(spr->xoffset), TrackerCast(spr->yoffset));
10752     DOPRINT(96, "Tile number: %d", TrackerCast(spr->picnum));
10753 
10754     col++;
10755 
10756     DOPRINT(48, "Angle (2048 degrees): %d", TrackerCast(spr->ang));
10757     DOPRINT(56, "X-Velocity: %d", TrackerCast(spr->xvel));
10758     DOPRINT(64, "Y-Velocity: %d", TrackerCast(spr->yvel));
10759     DOPRINT(72, "Z-Velocity: %d", TrackerCast(spr->zvel));
10760     DOPRINT(80, "Owner: %d", TrackerCast(spr->owner));
10761     DOPRINT(88, "Clipdist: %d", TrackerCast(spr->clipdist));
10762     DOPRINT(96, "Extra: %d", TrackerCast(spr->extra));
10763 }
10764 
10765 #undef DOPRINT
10766 
10767 // gets called once per totalclock increment since last call
keytimerstuff(void)10768 static void keytimerstuff(void)
10769 {
10770     if (!g_doHardcodedMovement)
10771         return;
10772 
10773     if (DOWN_BK(STRAFE) == 0)
10774     {
10775         if (DOWN_BK(TURNLEFT)) angvel = max(angvel-pk_turnaccel, -127);
10776         if (DOWN_BK(TURNRIGHT)) angvel = min(angvel+pk_turnaccel, 127);
10777     }
10778     else
10779     {
10780         if (DOWN_BK(TURNLEFT)) svel = min(svel+16, 255); // svel and vel aren't even chars...
10781         if (DOWN_BK(TURNRIGHT)) svel = max(svel-16, -255);
10782     }
10783     if (DOWN_BK(MOVEFORWARD))  vel = min(vel+16, 255);
10784     if (DOWN_BK(MOVEBACKWARD)) vel = max(vel-16, -255);
10785     /*  if (DOWN_BK(STRAFELEFT))  svel = min(svel+8, 127);
10786     	if (DOWN_BK(STRAFERIGHT)) svel = max(svel-8, -128); */
10787 
10788     if (angvel < 0) angvel = min(angvel+pk_turndecel, 0);
10789     if (angvel > 0) angvel = max(angvel-pk_turndecel, 0);
10790     if (svel < 0) svel = min(svel+6, 0);
10791     if (svel > 0) svel = max(svel-6, 0);
10792     if (vel < 0) vel = min(vel+6, 0);
10793     if (vel > 0) vel = max(vel-6, 0);
10794     /*    if(mlook) pos.z -= (horiz-101)*(vel/40); */
10795 }
10796 
10797 #if 0
10798 int32_t snfillprintf(char *outbuf, size_t bufsiz, int32_t fill, const char *fmt, ...)
10799 {
10800     char tmpstr[256];
10801     int32_t nwritten, ofs;
10802     va_list va;
10803 
10804     va_start(va, fmt);
10805     nwritten = Bvsnprintf(tmpstr, bufsiz, fmt, va);
10806     va_end(va);
10807 
10808     ofs = min(nwritten, (signed)bufsiz-1);
10809     Bmemset(outbuf, fill, bufsiz-ofs);
10810 
10811     return ofs;
10812 }
10813 #endif
10814 
_printmessage16(const char * fmt,...)10815 void _printmessage16(const char *fmt, ...)
10816 {
10817     int32_t i, ybase;
10818     char snotbuf[156];
10819     char tmpstr[160];
10820     va_list va;
10821 
10822     va_start(va, fmt);
10823     Bvsnprintf(tmpstr, sizeof(tmpstr), fmt, va);
10824     va_end(va);
10825 
10826     i = 0;
10827     while (tmpstr[i] && i < 146)
10828     {
10829         snotbuf[i] = tmpstr[i];
10830         i++;
10831     }
10832     snotbuf[i] = 0;
10833     if (lastpm16time == totalclock)
10834         Bstrcpy(lastpm16buf, snotbuf);
10835 
10836     clearministatbar16();
10837 
10838     ybase = ydim-STATUS2DSIZ+128-8;
10839 
10840     printext16(8, ybase+8, whitecol, -1, snotbuf, 0);
10841 }
10842 
printmessage256(int32_t x,int32_t y,const char * name)10843 void printmessage256(int32_t x, int32_t y, const char *name)
10844 {
10845     char snotbuf[80];
10846     int32_t i;
10847 
10848     i = 0;
10849     while (name[i] && i < 62)
10850     {
10851         snotbuf[i] = name[i];
10852         i++;
10853     }
10854     while (i < 62)
10855     {
10856         snotbuf[i] = 32;
10857         i++;
10858     }
10859     snotbuf[62] = 0;
10860     printext256(x+2,y+2,0,-1,snotbuf,0);
10861     printext256(x,y,whitecol,-1,snotbuf,0);
10862 }
10863 
10864 //Find closest point (*dax, *day) on wall (dawall) to (x, y)
getclosestpointonwall(int32_t x,int32_t y,int32_t dawall,int32_t * nx,int32_t * ny,int32_t maybe_screen_coord_p)10865 static void getclosestpointonwall(int32_t x, int32_t y, int32_t dawall, int32_t *nx, int32_t *ny,
10866                                   int32_t maybe_screen_coord_p)
10867 {
10868     int64_t i, j, wx,wy, wx2,wy2, dx, dy;
10869 
10870     if (m32_sideview && maybe_screen_coord_p)
10871     {
10872         wx = m32_wallscreenxy[dawall][0];
10873         wy = m32_wallscreenxy[dawall][1];
10874         wx2 = m32_wallscreenxy[wall[dawall].point2][0];
10875         wy2 = m32_wallscreenxy[wall[dawall].point2][1];
10876     }
10877     else
10878     {
10879         wx = wall[dawall].x;
10880         wy = wall[dawall].y;
10881         wx2 = POINT2(dawall).x;
10882         wy2 = POINT2(dawall).y;
10883     }
10884 
10885     dx = wx2 - wx;
10886     dy = wy2 - wy;
10887     i = dx*(x-wx) + dy*(y-wy);
10888     if (i <= 0) { *nx = wx; *ny = wy; return; }
10889     j = dx*dx + dy*dy;
10890     if (i >= j) { *nx = wx2; *ny = wy2; return; }
10891     i=((i<<15)/j)<<15;
10892     *nx = wx + ((dx*i)>>30);
10893     *ny = wy + ((dy*i)>>30);
10894 }
10895 
initcrc(void)10896 static void initcrc(void)
10897 {
10898     int32_t i, j, k, a;
10899 
10900     for (j=0; j<256; j++)   //Calculate CRC table
10901     {
10902         k = (j<<8); a = 0;
10903         for (i=7; i>=0; i--)
10904         {
10905             if (((k^a)&0x8000) > 0)
10906                 a = ((a<<1)&65535) ^ 0x1021;   //0x1021 = genpoly
10907             else
10908                 a = ((a<<1)&65535);
10909             k = ((k<<1)&65535);
10910         }
10911         crctable[j] = (a&65535);
10912     }
10913 }
10914 
GetWallBaseZ(int32_t wallnum)10915 static int32_t GetWallBaseZ(int32_t wallnum)
10916 {
10917     int32_t z=0;
10918 
10919     const int32_t sectnum = sectorofwall(wallnum);
10920     const int32_t nextsec = wall[wallnum].nextsector;
10921 
10922     if (nextsec == -1)  //1-sided wall
10923     {
10924         if (wall[wallnum].cstat&4)  // floor-aligned
10925             z = sector[sectnum].floorz;
10926         else
10927             z = sector[sectnum].ceilingz;
10928     }
10929     else  //2-sided wall
10930     {
10931         if (wall[wallnum].cstat&4)
10932             z = sector[sectnum].ceilingz;
10933         else
10934         {
10935             if (sector[nextsec].ceilingz > sector[sectnum].ceilingz)
10936                 z = sector[nextsec].ceilingz;   //top step
10937             if (sector[nextsec].floorz < sector[sectnum].floorz)
10938                 z = sector[nextsec].floorz;   //bottom step
10939         }
10940     }
10941 
10942     return z;
10943 }
10944 
10945 
10946 ////////// AUTOMATIC WALL ALIGNMENT //////////
10947 
AlignWalls_(int32_t tilenum,int32_t z0,int32_t z1,int32_t doxpanning,int32_t w0_pan,int32_t w0_rep,int32_t w1_pan,int32_t w1_rep)10948 static void AlignWalls_(int32_t tilenum, int32_t z0, int32_t z1, int32_t doxpanning,
10949                         int32_t w0_pan, int32_t w0_rep, int32_t w1_pan, int32_t w1_rep)
10950 {
10951     if (tilesiz[tilenum].x==0 || tilesiz[tilenum].y==0)
10952         return;
10953 
10954     //do the x alignment
10955     if (doxpanning)
10956         wall[w1_pan].xpanning = (uint8_t)((wall[w0_pan].xpanning + (wall[w0_rep].xrepeat<<3))%tilesiz[tilenum].x);
10957 
10958     int32_t zDiff = z1 - z0;                        // Z difference between current wall floor or ceiling, and the next.
10959 
10960     uint8_t first_yPan = wall[w0_pan].ypanning;     // Y panning of first wall
10961     uint8_t first_yRepeat = wall[w0_rep].yrepeat;   // Y repeat of first wall
10962 
10963     int32_t yPan_offset = ((zDiff * first_yRepeat) / (tilesiz[tilenum].y * 8)); // y-panning offset.
10964     uint8_t second_yPan = (uint8_t)(yPan_offset + first_yPan); // The final y-panning for the second wall
10965 
10966 #if 0
10967     OSD_Printf("----------------\n"
10968                "Auto-Align Debug\n"
10969                "w0_pan: %d\n"
10970                "w1_pan: %d\n"
10971                "z0: %d\n"
10972                "z1: %d\n"
10973                "zDiff: %d\n"
10974                "first_yPan: %d\n"
10975                "first_yRepeat: %d\n"
10976                "ypan_offset: %d\n"
10977                "second_yPan: %d\n"
10978         , w0_pan, w1_pan, z0, z1, zDiff, first_yPan, first_yRepeat, yPan_offset, second_yPan);
10979 #endif
10980 
10981     wall[w1_rep].yrepeat = first_yRepeat;
10982     wall[w1_pan].ypanning = second_yPan;
10983 }
10984 
AlignWalls(int32_t w0,int32_t z0,int32_t w1,int32_t z1,int32_t doxpanning)10985 static void AlignWalls(int32_t w0, int32_t z0, int32_t w1, int32_t z1, int32_t doxpanning)
10986 {
10987     AlignWalls_(wall[w0].picnum, z0, z1, doxpanning, w0, w0, w1, w1);
10988 }
10989 
AlignWallPoint2(int32_t w0)10990 void AlignWallPoint2(int32_t w0)
10991 {
10992     int32_t w1 = wall[w0].point2;
10993     AlignWalls(w0,GetWallBaseZ(w0), w1,GetWallBaseZ(w1), 1);
10994 }
10995 
10996 #define ALIGN_WALLS_CSTAT_MASK (4+8+256)
10997 
AlignGetWall(int32_t botswap,int32_t w)10998 static int32_t AlignGetWall(int32_t botswap, int32_t w)
10999 {
11000     return botswap && (wall[w].cstat&2) && wall[w].nextwall >= 0 ? wall[w].nextwall : w;
11001 }
11002 
11003 // flags:
11004 //  1: more than once
11005 //  2: (unused)
11006 //  4: carry pixel width from first wall over to the rest
11007 //  8: align TROR nextwalls
11008 // 16: iterate lastwall()s (point2 in reverse)
11009 // 32: use special logic for 'bottom-swapped' walls
AutoAlignWalls(int32_t w0,uint32_t flags,int32_t nrecurs)11010 int32_t AutoAlignWalls(int32_t w0, uint32_t flags, int32_t nrecurs)
11011 {
11012     static int numaligned, wall0;
11013     static int32_t cstat0;
11014     static uint32_t lenrepquot;
11015 
11016     int const totheleft = flags & 16;
11017     int const botswap   = flags & 32;
11018 
11019     int32_t z0  = GetWallBaseZ(w0);
11020     int32_t w1  = totheleft ? lastwall(w0) : wall[w0].point2;
11021     int32_t w0b = AlignGetWall(botswap, w0);
11022 
11023     if (nrecurs == 0)
11024     {
11025         //clear visited bits
11026         Bmemset(visited, 0, sizeof(visited));
11027         visited[w0>>3] |= pow2char[w0&7];
11028         numaligned = 0;
11029         lenrepquot = getlenbyrep(wallength(w0), wall[w0].xrepeat);
11030         wall0 = w0;
11031         cstat0 = wall[w0b].cstat & ALIGN_WALLS_CSTAT_MASK;  // top/bottom orientation; x/y-flip
11032     }
11033 
11034     int const tilenum = wall[w0b].picnum;
11035     int const rotated = wall[w0b].cstat & CSTAT_WALL_ROTATE_90;
11036 
11037     //loop through walls at this vertex in point2 order
11038     do
11039     {
11040         int const w1b = AlignGetWall(botswap, w1);
11041 
11042         //break if this wall would connect us in a loop
11043         if (visited[w1>>3]&pow2char[w1&7])
11044             break;
11045 
11046         visited[w1>>3] |= pow2char[w1&7];
11047 
11048 #ifdef YAX_ENABLE
11049         if (flags&8)
11050         {
11051             for (int cf=0; cf<2; cf++)
11052             {
11053                 int const ynw = yax_getnextwall(w0, cf);
11054 
11055                 if (ynw >= 0 && wall[ynw].picnum == tilenum && ((wall[ynw].cstat & CSTAT_WALL_ROTATE_90) == rotated)
11056                     && (visited[ynw>>3] & pow2char[ynw&7]) == 0)
11057                 {
11058                     wall[ynw].xrepeat  = wall[w0].xrepeat;
11059                     wall[ynw].xpanning = wall[w0].xpanning;
11060                     AlignWalls(w0,z0, ynw,GetWallBaseZ(ynw), 0);  // initial vertical alignment
11061                 }
11062             }
11063         }
11064 #endif
11065 
11066         //break if reached back of left wall
11067         if (wall[w1].nextwall == w0)
11068             break;
11069 
11070         if (wall[w1b].picnum == tilenum && ((wall[w1b].cstat & CSTAT_WALL_ROTATE_90) == rotated))
11071         {
11072             bool visible = true;
11073             int const nextsec = wall[w1].nextsector;
11074 
11075             if (nextsec >= 0)
11076             {
11077                 int const sectnum = NEXTWALL(w1).nextsector;
11078 
11079                 //ignore two sided walls that have no visible face
11080                 // TODO: can be more precise (i.e. taking into account the wall face)
11081                 //  ... needs to be factored out from some engine code maybe...
11082                 // as is the whole "z base" determination.
11083 
11084                 int32_t cz,fz, czn,fzn;
11085                 getzsofslope(sectnum, wall[w1].x,wall[w1].y, &cz, &fz);
11086                 getzsofslope(nextsec, wall[w1].x,wall[w1].y, &czn, &fzn);
11087 
11088                 if (czn <= cz && fzn >= fz)
11089                     visible = false;
11090             }
11091 
11092             if (visible)
11093             {
11094                 const int32_t z1 = GetWallBaseZ(w1);
11095 
11096                 if ((flags&4) && w1!=wall0)
11097                     fixxrepeat(w1, lenrepquot);
11098 
11099                 AlignWalls_(tilenum,z0, z1, 1, w0b, w0, w1b, w1);
11100                 wall[w1b].cstat &= ~ALIGN_WALLS_CSTAT_MASK;
11101                 wall[w1b].cstat |= cstat0;
11102                 numaligned++;
11103 
11104                 if ((flags&1)==0)
11105                     return 1;
11106 
11107                 //if wall was 1-sided, no need to recurse
11108                 if (wall[w1].nextwall < 0)
11109                 {
11110                     w0 = w1;
11111                     w0b = AlignGetWall(botswap, w0);
11112                     z0 = GetWallBaseZ(w0);
11113                     w1 = totheleft ? lastwall(w0) : wall[w0].point2;
11114 
11115                     continue;
11116                 }
11117 
11118                 AutoAlignWalls(w1, flags, nrecurs+1);
11119             }
11120         }
11121 
11122         if (wall[w1].nextwall < 0)
11123             break;
11124         w1 = totheleft ? lastwall(wall[w1].nextwall) : NEXTWALL(w1).point2;
11125     } while (1);
11126 
11127     return numaligned;
11128 }
11129 
11130 #define PLAYTEST_MAPNAME "autosave_playtest.map"
11131 
test_map(int32_t mode)11132 void test_map(int32_t mode)
11133 {
11134     if (!mode)
11135         updatesector(pos.x, pos.y, &cursectnum);
11136     else
11137         updatesector(startpos.x, startpos.y, &startsectnum);
11138 
11139 #ifdef _WIN32
11140     if (fullscreen)
11141     {
11142         printmessage16("Must be in windowed mode to test map!");
11143         return;
11144     }
11145 #endif
11146 
11147     if ((!mode && cursectnum >= 0) || (mode && startsectnum >= 0))
11148     {
11149         char const *param = " -map " PLAYTEST_MAPNAME " -noinstancechecking";
11150         char current_cwd[BMAX_PATH];
11151         int32_t slen = 0;
11152         buildvfs_FILE fp;
11153 
11154         if ((program_origcwd[0] == '\0') || !buildvfs_getcwd(current_cwd, BMAX_PATH))
11155             current_cwd[0] = '\0';
11156         else // Before we check if file exists, for the case there's no absolute path.
11157             buildvfs_chdir(program_origcwd);
11158 
11159         fp = buildvfs_fopen_read(game_executable); // File exists?
11160         if (fp != NULL)
11161             buildvfs_fclose(fp);
11162         else
11163         {
11164             char const * lastslash = (char const *)Bstrrchr(mapster32_fullpath, '/');
11165 #ifdef _WIN32
11166             char const * lastbackslash = (char const *)Bstrrchr(mapster32_fullpath, '\\');
11167             lastslash = max(lastslash, lastbackslash);
11168 #endif
11169             if (lastslash)
11170             {
11171                 slen = lastslash-mapster32_fullpath+1;
11172                 Bstrncpy(game_executable, mapster32_fullpath, slen);
11173                 Bstrncpy(game_executable+slen, DefaultGameExec, sizeof(game_executable)-slen);
11174             }
11175             else
11176                 Bstrncpy(game_executable, DefaultGameLocalExec, sizeof(game_executable));
11177         }
11178 
11179         if (current_cwd[0] != '\0') // Temporarily changing back,
11180             buildvfs_chdir(current_cwd);     // after checking if file exists.
11181 
11182         if (testplay_addparam)
11183             slen = Bstrlen(testplay_addparam);
11184 
11185         // Considering the NULL character, quatation marks
11186         // and a possible extra space not in testplay_addparam,
11187         // the length should be Bstrlen(game_executable)+Bstrlen(param)+(slen+1)+2+1.
11188 
11189         char *fullparam = (char *)Xmalloc(Bstrlen(game_executable)+Bstrlen(param)+slen+4);
11190         Bsprintf(fullparam,"\"%s\"",game_executable);
11191 
11192         if (testplay_addparam)
11193         {
11194             Bstrcat(fullparam, " ");
11195             Bstrcat(fullparam, testplay_addparam);
11196         }
11197         Bstrcat(fullparam, param);
11198 
11199         CallExtPreSaveMap();
11200         if (mode)
11201             saveboard(PLAYTEST_MAPNAME, &startpos, startang, startsectnum);
11202         else
11203             saveboard(PLAYTEST_MAPNAME, &pos, ang, cursectnum);
11204 
11205         message("Board saved to " PLAYTEST_MAPNAME ". Starting the game...");
11206         OSD_Printf("...as `%s'\n", fullparam);
11207 
11208         videoShowFrame(1);
11209         mouseUninit();
11210 #ifdef _WIN32
11211         {
11212             STARTUPINFO si;
11213             PROCESS_INFORMATION pi;
11214 
11215             ZeroMemory(&si,sizeof(si));
11216             ZeroMemory(&pi,sizeof(pi));
11217             si.cb = sizeof(si);
11218 
11219             if (!CreateProcess(NULL,fullparam,NULL,NULL,0,0,NULL,NULL,&si,&pi))
11220                 message("Error launching the game!");
11221             else WaitForSingleObject(pi.hProcess,INFINITE);
11222         }
11223 #else
11224         if (current_cwd[0] != '\0')
11225         {
11226             buildvfs_chdir(program_origcwd);
11227             if (system(fullparam))
11228                 message("Error launching the game!");
11229             buildvfs_chdir(current_cwd);
11230         }
11231         else system(fullparam);
11232 #endif
11233         printmessage16("Game process exited");
11234         mouseInit();
11235         clearkeys();
11236 
11237         Xfree(fullparam);
11238     }
11239     else
11240         printmessage16("Position must be in valid player space to test map!");
11241 }
11242 
app_crashhandler(void)11243 void app_crashhandler(void)
11244 {
11245     if (levelname[0])
11246     {
11247         append_ext_UNSAFE(levelname, "_crash.map");
11248         SaveBoard(levelname, M32_SB_NOEXT);
11249     }
11250 }
11251 
11252 // These will be more useful in the future...
CallExtGetVer(void)11253 static const char *CallExtGetVer(void)
11254 {
11255     return ExtGetVer();
11256 }
CallExtInit(void)11257 static int32_t CallExtInit(void)
11258 {
11259     return ExtInit();
11260 }
CallExtPreInit(int32_t argc,char const * const * argv)11261 static int32_t CallExtPreInit(int32_t argc,char const * const * argv)
11262 {
11263     return ExtPreInit(argc, argv);
11264 }
CallExtPostStartupWindow(void)11265 static int32_t CallExtPostStartupWindow(void)
11266 {
11267     return ExtPostStartupWindow();
11268 }
CallExtPostInit(void)11269 static void CallExtPostInit(void)
11270 {
11271     return ExtPostInit();
11272 }
CallExtUnInit(void)11273 static void CallExtUnInit(void)
11274 {
11275     ExtUnInit();
11276 }
CallExtPreCheckKeys(void)11277 static void CallExtPreCheckKeys(void)
11278 {
11279     ExtPreCheckKeys();
11280 }
CallExtAnalyzeSprites(int32_t ourx,int32_t oury,int32_t ourz,int32_t oura,int32_t smoothr)11281 static void CallExtAnalyzeSprites(int32_t ourx, int32_t oury, int32_t ourz, int32_t oura, int32_t smoothr)
11282 {
11283     ExtAnalyzeSprites(ourx, oury, ourz, oura, smoothr);
11284     VM_OnEvent(EVENT_ANALYZESPRITES, -1);
11285 }
CallExtCheckKeys(void)11286 static void CallExtCheckKeys(void)
11287 {
11288     ExtCheckKeys();
11289 }
CallExtPreLoadMap(void)11290 static void CallExtPreLoadMap(void)
11291 {
11292     VM_OnEvent(EVENT_PRELOADMAP, -1);
11293     ExtPreLoadMap();
11294 }
CallExtSetupMapFilename(const char * mapname)11295 static void CallExtSetupMapFilename(const char *mapname)
11296 {
11297     Bstrncpy(levelname, mapname, sizeof(levelname));
11298 
11299     Bsnprintf(tempbuf, sizeof(tempbuf), "%s - %s", AppProperName, mapname);
11300     wm_setapptitle(tempbuf);
11301 
11302     ExtSetupMapFilename(mapname);
11303 }
CallExtLoadMap(const char * mapname)11304 static void CallExtLoadMap(const char *mapname)
11305 {
11306     CallExtSetupMapFilename(mapname);
11307     ExtLoadMap(mapname);
11308     VM_OnEvent(EVENT_LOADMAP, -1);
11309 }
CallExtPreSaveMap(void)11310 static int32_t CallExtPreSaveMap(void)
11311 {
11312     VM_OnEvent(EVENT_PRESAVEMAP, -1);
11313     return ExtPreSaveMap();
11314 }
CallExtSaveMap(const char * mapname)11315 static void CallExtSaveMap(const char *mapname)
11316 {
11317     ExtSaveMap(mapname);
11318     saveboard("backup.map", &pos, ang, cursectnum);
11319     VM_OnEvent(EVENT_SAVEMAP, -1);
11320 }
CallExtShowSectorData(int16_t sectnum)11321 static void CallExtShowSectorData(int16_t sectnum)
11322 {
11323     ExtShowSectorData(sectnum);
11324 }
CallExtShowWallData(int16_t wallnum)11325 static void CallExtShowWallData(int16_t wallnum)
11326 {
11327     ExtShowWallData(wallnum);
11328 }
CallExtShowSpriteData(int16_t spritenum)11329 static void CallExtShowSpriteData(int16_t spritenum)
11330 {
11331     ExtShowSpriteData(spritenum);
11332 }
CallExtEditSectorData(int16_t sectnum)11333 static void CallExtEditSectorData(int16_t sectnum)
11334 {
11335     ExtEditSectorData(sectnum);
11336 }
CallExtEditWallData(int16_t wallnum)11337 static void CallExtEditWallData(int16_t wallnum)
11338 {
11339     ExtEditWallData(wallnum);
11340 }
CallExtEditSpriteData(int16_t spritenum)11341 static void CallExtEditSpriteData(int16_t spritenum)
11342 {
11343     ExtEditSpriteData(spritenum);
11344 }
11345 #if 0
11346 static const char *CallExtGetSectorType(int32_t lotag)
11347 {
11348     return ExtGetSectorType(lotag);
11349 }
11350 #endif
11351