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)§or[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(§or[dst], §or[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], §or[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(§or[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 = §or[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, §orhighlight);
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(§or[numsectors], §or[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(§or[numsectors], §or[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(§or[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(§or[numsectors], §or[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(§or[numsectors], §or[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(§or[numsectors+1], §or[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(§or[i], §or[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(§or[numsectors], §or[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 = §or[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