raze-gles/source/build/src/build.cpp
pogokeen 1df7127609 Support software renderer upscaling engine-side in sdlayer and sdlayer12 with various scaling factors (beyond just pixel doubling).
When glsurface is available, use GL to upscale the render buffer.
Additionally, fix build issues with sdlayer12 introduced by GLAD changes (r6656).

git-svn-id: https://svn.eduke32.com/eduke32@6939 1a8010ca-5511-0410-912e-c29ae57300e0
2018-07-14 21:36:44 +00:00

11153 lines
360 KiB
C++

// "Build Engine & Tools" Copyright (c) 1993-1997 Ken Silverman
// Ken Silverman's official web site: "http://www.advsys.net/ken"
// See the included license file "BUILDLIC.TXT" for license info.
//
// This file has been modified from Ken Silverman's original release
// by Jonathon Fowler (jf@jonof.id.au)
#include "build.h"
#include "compat.h"
#include "pragmas.h"
#include "osd.h"
#include "cache1d.h"
#include "editor.h"
#include "common.h"
#include "colmatch.h"
#include "palette.h"
#include "scancodes.h"
#include "baselayer.h"
#include "renderlayer.h"
#ifdef _WIN32
# include "winbits.h"
#endif
#include "m32script.h"
char levelname[BMAX_PATH] = {0};
#define TIMERINTSPERSECOND 120
#define updatecrc16(crc,dat) (crc = (((crc<<8)&65535)^crctable[((((uint16_t)crc)>>8)&65535)^dat]))
static int32_t crctable[256];
static char kensig[64];
static const char *CallExtGetVer(void);
static int32_t CallExtInit(void);
static int32_t CallExtPreInit(int32_t argc,char const * const * argv);
static int32_t CallExtPostStartupWindow(void);
static void CallExtPostInit(void);
static void CallExtUnInit(void);
static void CallExtPreCheckKeys(void);
static void CallExtAnalyzeSprites(int32_t, int32_t, int32_t, int32_t);
static void CallExtCheckKeys(void);
static void CallExtPreLoadMap(void);
static void CallExtSetupMapFilename(const char *mapname);
static void CallExtLoadMap(const char *mapname);
static int32_t CallExtPreSaveMap(void);
static void CallExtSaveMap(const char *mapname);
static inline const char *CallExtGetSectorCaption(int16_t sectnum) { return ExtGetSectorCaption(sectnum); }
static inline const char *CallExtGetWallCaption(int16_t wallnum) { return ExtGetWallCaption(wallnum); }
static inline const char *CallExtGetSpriteCaption(int16_t spritenum) { return ExtGetSpriteCaption(spritenum); }
static void CallExtShowSectorData(int16_t sectnum);
static void CallExtShowWallData(int16_t wallnum);
static void CallExtShowSpriteData(int16_t spritenum);
static void CallExtEditSectorData(int16_t sectnum);
static void CallExtEditWallData(int16_t wallnum);
static void CallExtEditSpriteData(int16_t spritenum);
// static const char *CallExtGetSectorType(int32_t lotag);
int8_t m32_clipping=2;
static int32_t m32_rotateang = 0;
// 0 1 2 3 4 5 6 7
// up, down, left, right, lshift, rctrl, lctrl, space
// 8 9 10 11 12 13
// a, z, pgdn, pgup, [,], [.]
// 14 15 16 17 18 19
// kpenter, enter, =, -, tab, `
uint8_t buildkeys[NUMBUILDKEYS] =
{
0xc8,0xd0,0xcb,0xcd,0x2a,0x9d,0x1d,0x39,
0x1e,0x2c,0xd1,0xc9,0x33,0x34,
0x9c,0x1c,0xd,0xc,0xf,0x29
};
// Start position
vec3_t startpos;
int16_t startang, startsectnum;
// Current position
vec3_t pos;
int32_t horiz = 100;
int16_t ang, cursectnum;
static int32_t hvel, vel, svel, angvel;
int32_t g_doHardcodedMovement = 1;
static int32_t mousexsurp = 0, mouseysurp = 0;
double msens = 1.0;
int32_t grponlymode = 0;
int32_t graphicsmode = 0;
int32_t synctics = 0, lockclock = 0;
// those ones save the respective 3d video vars while in 2d mode
// so that exiting from mapster32 in 2d mode saves the correct ones
float vid_gamma_3d=-1, vid_contrast_3d=-1, vid_brightness_3d=-1;
int32_t xdim2d = 640, ydim2d = 480, xdimgame = 640, ydimgame = 480, bppgame = 8;
int32_t forcesetup = 1;
#ifndef GEKKO
int32_t g_maxCacheSize = 24<<20;
#else
int32_t g_maxCacheSize = 8<<20;
#endif
static int16_t oldmousebstatus = 0;
char game_executable[BMAX_PATH] = {0};
int32_t zlock = 0x7fffffff, zmode = 0, kensplayerheight = 32;
//int16_t defaultspritecstat = 0;
int16_t localartfreq[MAXTILES];
int16_t localartlookup[MAXTILES], localartlookupnum;
char tempbuf[4096];
char names[MAXTILES][25];
const char *g_namesFileName = "NAMES.H";
int16_t asksave = 0;
int32_t osearchx, osearchy; //old search input
int32_t grid = 0, autogrid = 1, gridlock = 1, showtags = 2;
int32_t zoom = 768, gettilezoom = 1, ztarget = 768;
int32_t lastpm16time = 0;
extern int32_t mapversion;
int16_t highlight[MAXWALLS+MAXSPRITES];
int16_t highlightsector[MAXSECTORS], highlightsectorcnt = -1;
extern char textfont[128][8];
int32_t tempsectornum = -1; // for auto ceiling/floor alignment
int32_t temppicnum, tempcstat, templotag, temphitag, tempextra;
uint32_t temppal, tempvis, tempxrepeat, tempyrepeat, tempxpanning=0, tempypanning=0;
int32_t tempshade, tempxvel, tempyvel, tempzvel;
int32_t tempstatnum=0, tempblend=0;
char somethingintab = 255;
// Only valid when highlightsectorcnt>0 and no structural
// modifications (deleting/inserting sectors or points, setting new firstwall)
// have been made:
static int16_t onextwall[MAXWALLS]; // onextwall[i]>=0 implies wall[i].nextwall < 0
static void mkonwvalid(void) { chsecptr_onextwall = onextwall; }
static void mkonwinvalid(void) { chsecptr_onextwall = NULL; tempsectornum=-1; }
static void mkonwinvalid_keeptempsect(void) { chsecptr_onextwall = NULL; }
static int32_t onwisvalid(void) { return chsecptr_onextwall != NULL; }
int32_t mlook = 0, mskip=0;
int32_t revertCTRL=0,scrollamount=3;
int32_t unrealedlook=1, quickmapcycling=1; //PK
char program_origcwd[BMAX_PATH];
const char *mapster32_fullpath;
char *testplay_addparam = 0;
static char boardfilename[BMAX_PATH], selectedboardfilename[BMAX_PATH];
//extern char levelname[BMAX_PATH]; // in astub.c XXX: clean up this mess!!!
void B_SetBoardFileName(const char *fn)
{
Bstrncpyz(boardfilename, fn, BMAX_PATH);
}
static fnlist_t fnlist;
static CACHE1D_FIND_REC *finddirshigh=NULL, *findfileshigh=NULL;
static int32_t currentlist=0;
//static int32_t repeatcountx, repeatcounty;
static int32_t fillist[640];
// used for fillsector, batch point insertion, backup_highlighted_map
static int32_t tempxyar[MAXWALLS][2];
static int32_t mousx, mousy;
int16_t prefixtiles[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
uint8_t hlsectorbitmap[MAXSECTORS>>3]; // show2dsector is already taken...
static int32_t minhlsectorfloorz, numhlsecwalls;
int32_t searchlock = 0;
// used for:
// - hl_all_bunch_sectors_p
// - AlignWalls
// - trace_loop
static uint8_t visited[MAXWALLS>>3];
int32_t m32_2d3dmode = 0;
int32_t m32_2d3dsize = 4;
vec2_t m32_2d3d = { 0xffff, 4 };
typedef struct
{
int16_t numsectors, numwalls, numsprites;
#ifdef YAX_ENABLE
int16_t numyaxbunches;
int16_t *bunchnum; // [numsectors][2]
int16_t *ynextwall; // [numwalls][2]
#endif
usectortype *sector;
uwalltype *wall;
uspritetype *sprite;
} mapinfofull_t;
int32_t g_doScreenShot;
#define eitherALT (keystatus[sc_LeftAlt]|keystatus[sc_RightAlt])
#define eitherCTRL (keystatus[sc_LeftControl]|keystatus[sc_RightControl])
#define eitherSHIFT (keystatus[sc_LeftShift]|keystatus[sc_RightShift])
#define DOWN_BK(BuildKey) (keystatus[buildkeys[BK_##BuildKey]])
int32_t pk_turnaccel=16;
int32_t pk_turndecel=12;
int32_t pk_uedaccel=3;
int8_t keeptexturestretch = 1;
int8_t sideview_reversehrot = 0;
int16_t pointhighlightdist = 256;
int16_t linehighlightdist = 1024;
char lastpm16buf[156];
//static int32_t checksectorpointer_warn = 0;
static int32_t saveboard_savedtags, saveboard_fixedsprites;
static int32_t saveboard_canceled;
static int32_t backup_highlighted_map(mapinfofull_t *mapinfo);
static int32_t restore_highlighted_map(mapinfofull_t *mapinfo, int32_t forreal);
static void SaveBoardAndPrintMessage(const char *fn);
static int32_t adjustmark(int32_t *xplc, int32_t *yplc, int16_t danumwalls);
static void locktogrid(int32_t *dax, int32_t *day);
static int32_t checkautoinsert(int32_t dax, int32_t day, int16_t danumwalls);
static void keytimerstuff(void);
static void flipwalls(int16_t numwalls, int16_t newnumwalls);
static int32_t insertpoint(int16_t linehighlight, int32_t dax, int32_t day, int32_t *mapwallnum);
static void deletepoint(int16_t point, int32_t runi);
static int32_t deletesector(int16_t sucksect);
static int16_t whitelinescan(int16_t sucksect, int16_t dalinehighlight);
static void printcoords16(int32_t posxe, int32_t posye, int16_t ange);
static void overheadeditor(void);
static int32_t getlinehighlight(int32_t xplc, int32_t yplc, int32_t line, int8_t ignore_pointhighlight);
static int32_t movewalls(int32_t start, int32_t offs);
static void loadnames(const char *namesfile);
static void getclosestpointonwall(int32_t x, int32_t y, int32_t dawall, int32_t *nx, int32_t *ny,
int32_t maybe_screen_coord_p);
static void initcrc(void);
static int32_t menuselect(void);
static int32_t menuselect_auto(int, int); //PK
static int32_t insert_sprite_common(int32_t sucksect, int32_t dax, int32_t day);
static void correct_ornamented_sprite(int32_t i, int32_t hitw);
static int32_t getfilenames(const char *path, const char *kind);
// Get basename of BUILD file name (forward slashes as directory separators).
static const char *getbasefn(const char *fn)
{
const char *slash = Bstrrchr(fn, '/');
return slash ? slash+1 : fn;
}
void clearkeys(void)
{
Bmemset(keystatus,0,sizeof(keystatus));
}
#ifdef USE_OPENGL
int32_t osdcmd_restartvid(osdfuncparm_t const * const UNUSED(parm))
{
UNREFERENCED_CONST_PARAMETER(parm);
if (!in3dmode()) return OSDCMD_OK;
videoResetMode();
if (videoSetGameMode(fullscreen,xres,yres,bpp,upscalefactor))
OSD_Printf("restartvid: Reset failed...\n");
return OSDCMD_OK;
}
#endif
static int32_t osdcmd_vidmode(osdfuncparm_t const * const parm)
{
int32_t newx = xres, newy = yres, newbpp = bpp, newfullscreen = fullscreen;
#ifdef USE_OPENGL
int32_t tmp;
#endif
switch (parm->numparms)
{
#ifdef USE_OPENGL
case 1: // bpp switch
tmp = Batol(parm->parms[0]);
if (!(tmp==8 || tmp==16 || tmp==32))
return OSDCMD_SHOWHELP;
newbpp = tmp;
break;
case 4: // fs, res, bpp switch
newfullscreen = (Batol(parm->parms[3]) != 0);
fallthrough__;
case 3: // res & bpp switch
tmp = Batol(parm->parms[2]);
if (!(tmp==8 || tmp==16 || tmp==32))
return OSDCMD_SHOWHELP;
newbpp = tmp;
fallthrough__;
#endif
case 2: // res switch
newx = Batol(parm->parms[0]);
newy = Batol(parm->parms[1]);
break;
default:
return OSDCMD_SHOWHELP;
}
if (!in3dmode())
{
videoSet2dMode(newx,newy);
xdim2d = xdim;
ydim2d = ydim;
videoBeginDrawing(); //{{{
CLEARLINES2D(0, ydim16, 0);
videoEndDrawing(); //}}}
ydim16 = ydim-STATUS2DSIZ2;
return OSDCMD_OK;
}
if (videoSetGameMode(newfullscreen,newx,newy,newbpp,upscalefactor))
OSD_Printf("vidmode: Mode change failed!\n");
xdimgame = newx;
ydimgame = newy;
bppgame = newbpp;
fullscreen = newfullscreen;
return OSDCMD_OK;
}
#ifdef M32_SHOWDEBUG
char m32_debugstr[64][128];
int32_t m32_numdebuglines=0;
static void M32_drawdebug(void)
{
int32_t i;
int32_t x=4, y=8;
#if 0
{
static char tstr[128];
Bsprintf(tstr, "search... stat=%d, sector=%d, wall=%d (%d), isbottom=%d, asksave=%d",
searchstat, searchsector, searchwall, searchbottomwall, searchisbottom, asksave);
printext256(x,y,whitecol,0,tstr,xdimgame>640?0:1);
}
#endif
if (m32_numdebuglines>0)
{
videoBeginDrawing();
for (i=0; i<m32_numdebuglines && y<ydim-8; i++, y+=8)
printext256(x,y,whitecol,0,m32_debugstr[i],xdimgame>640?0:1);
videoEndDrawing();
}
m32_numdebuglines=0;
}
#endif
#ifdef YAX_ENABLE
// Check whether bunchnum has exactly one corresponding floor and ceiling
// and return it in this case. If not 1-to-1, return -1.
int32_t yax_is121(int16_t bunchnum, int16_t getfloor)
{
int32_t i;
i = headsectbunch[0][bunchnum];
if (i<0 || nextsectbunch[0][i]>=0)
return -1;
i = headsectbunch[1][bunchnum];
if (i<0 || nextsectbunch[1][i]>=0)
return -1;
return headsectbunch[getfloor][bunchnum];
}
static int32_t yax_numsectsinbunch(int16_t bunchnum, int16_t cf)
{
int32_t i, n=0;
if (bunchnum<0 || bunchnum>=numyaxbunches)
return -1;
for (SECTORS_OF_BUNCH(bunchnum, cf, i))
n++;
return n;
}
static void yax_fixreverselinks(int16_t oldwall, int16_t newwall)
{
int32_t cf, ynw;
for (cf=0; cf<2; cf++)
{
ynw = yax_getnextwall(oldwall, cf);
if (ynw >= 0)
yax_setnextwall(ynw, !cf, newwall);
}
}
static void yax_tweakwalls(int16_t start, int16_t offs)
{
int32_t i, nw, cf;
for (i=0; i<numwalls; i++)
for (cf=0; cf<2; cf++)
{
nw = yax_getnextwall(i, cf);
if (nw >= start)
yax_setnextwall(i, cf, nw+offs);
}
}
static void yax_resetbunchnums(void)
{
int32_t i;
for (i=0; i<MAXSECTORS; i++)
yax_setbunches(i, -1, -1);
yax_update(1);
yax_updategrays(pos.z);
}
// Whether a wall is constrained by sector extensions.
// If false, it's a wall that you can freely move around,
// attach points to, etc...
static int32_t yax_islockedwall(int16_t line)
{
#ifdef NEW_MAP_FORMAT
return (wall[line].upwall>=0 || wall[line].dnwall>=0);
#else
return !!(wall[line].cstat&YAX_NEXTWALLBITS);
#endif
}
# define DEFAULT_YAX_HEIGHT (2048<<4)
#endif
static void reset_default_mapstate(void)
{
pos.x = 32768; //new board!
pos.y = 32768;
pos.z = 0;
ang = 1536;
cursectnum = -1;
numsectors = 0;
numwalls = 0;
editorzrange[0] = INT32_MIN;
editorzrange[1] = INT32_MAX;
initspritelists();
taglab_init();
artClearMapArt();
#ifdef YAX_ENABLE
yax_resetbunchnums();
#endif
g_loadedMapVersion = -1;
}
static void m32_keypresscallback(int32_t code, int32_t downp)
{
UNREFERENCED_PARAMETER(downp);
g_iReturnVar = code;
VM_OnEvent(EVENT_KEYPRESS, -1);
}
void M32_ResetFakeRORTiles(void)
{
#ifdef POLYMER
# ifdef YAX_ENABLE
// END_TWEAK ceiling/floor fake 'TROR' pics, see BEGIN_TWEAK in engine.c
if (videoGetRenderMode() == REND_POLYMER && showinvisibility)
{
int32_t i;
for (i=0; i<numyaxbunches; i++)
{
yax_tweakpicnums(i, YAX_CEILING, 1);
yax_tweakpicnums(i, YAX_FLOOR, 1);
}
}
# endif
#endif
}
void M32_DrawRoomsAndMasks(void)
{
static int srchwall = -1;
const int32_t tmpyx=yxaspect, tmpvr=viewingrange;
if (r_usenewaspect)
{
newaspect_enable = 1;
videoSetCorrectedAspect();
}
VM_OnEvent(EVENT_PREDRAW3DSCREEN, -1);
yax_preparedrawrooms();
drawrooms(pos.x,pos.y,pos.z,ang,horiz,cursectnum);
yax_drawrooms(CallExtAnalyzeSprites, cursectnum, 0, 0);
const int osearchwall=searchwall, osearchstat=searchstat;
if (srchwall >= 0)
{
// a.m32 states 'tduprot' and 'tduplin' need searchstat to check for
// whether we've hit a sprite, but these would be only set after the
// drawmasks(). Hence this hackish workaround.
searchstat = 3;
searchwall = srchwall;
}
CallExtAnalyzeSprites(0,0,0,0);
searchwall = osearchwall, searchstat=osearchstat;
renderDrawMasks();
srchwall = (searchstat == 3) ? searchwall : -1;
M32_ResetFakeRORTiles();
#ifdef POLYMER
if (videoGetRenderMode() == REND_POLYMER && searchit == 2)
{
polymer_editorpick();
drawrooms(pos.x,pos.y,pos.z,ang,horiz,cursectnum);
CallExtAnalyzeSprites(0,0,0,0);
renderDrawMasks();
M32_ResetFakeRORTiles();
}
#endif
VM_OnEvent(EVENT_DRAW3DSCREEN, -1);
if (g_doScreenShot)
{
videoCaptureScreen("mcapxxxx.tga", 0);
g_doScreenShot = 0;
}
if (r_usenewaspect)
{
newaspect_enable = 0;
renderSetAspect(tmpvr, tmpyx);
}
}
void M32_OnShowOSD(int32_t shown)
{
mouseLockToWindow((!shown) + 2);
}
static void M32_FatalEngineError(void)
{
wm_msgbox("Build Engine Initialization Error",
"There was a problem initializing the Build engine: %s", engineerrstr);
ERRprintf("app_main: There was a problem initializing the Build engine: %s\n", engineerrstr);
exit(2);
}
int app_main(int argc, char const * const * argv)
{
#ifdef STARTUP_SETUP_WINDOW
char cmdsetup = 0;
#endif
char quitflag;
int32_t i;
pathsearchmode = 1; // unrestrict findfrompath so that full access to the filesystem can be had
#ifdef USE_OPENGL
OSD_RegisterFunction("restartvid","restartvid: reinitialize the video mode",osdcmd_restartvid);
OSD_RegisterFunction("vidmode","vidmode <xdim> <ydim> <bpp> <fullscreen>: immediately change the video mode",osdcmd_vidmode);
baselayer_osdcmd_vidmode_func = osdcmd_vidmode;
#else
OSD_RegisterFunction("vidmode","vidmode <xdim> <ydim>: immediately change the video mode",osdcmd_vidmode);
#endif
wm_setapptitle(AppProperName);
editstatus = 1;
if ((i = CallExtPreInit(argc,argv)) < 0) return -1;
#ifdef _WIN32
backgroundidle = 1;
#endif
for (i=1; i<argc; i++)
{
if (argv[i][0] == '-')
{
#ifdef STARTUP_SETUP_WINDOW
if (!Bstrcmp(argv[i], "-setup")) cmdsetup = 1;
else
#endif
if (!Bstrcmp(argv[i], "-help") || !Bstrcmp(argv[i], "--help") || !Bstrcmp(argv[i], "-?"))
{
#ifdef WM_MSGBOX_WINDOW
wm_msgbox(AppProperName,
#else
Bprintf(
#endif
"%s\n"
"Syntax: %s [options] mapname\n"
"Options:\n"
"\t-grp\tUse an extra GRP or ZIP file.\n"
"\t-g\tSame as above.\n"
#ifdef STARTUP_SETUP_WINDOW
"\t-setup\tDisplays the configuration dialogue box before entering the editor.\n"
#endif
, AppProperName, AppTechnicalName);
return 0;
}
continue;
}
}
if (boardfilename[0] == 0)
Bstrcpy(boardfilename,"newboard.map");
else if (Bstrchr(boardfilename,'.') == 0)
Bstrcat(boardfilename, ".map");
//Bcanonicalisefilename(boardfilename,0);
OSD_SetFunctions(
NULL, NULL, NULL, NULL, NULL,
COMMON_clearbackground,
BGetTime,
M32_OnShowOSD
);
if (!getcwd(program_origcwd,BMAX_PATH))
program_origcwd[0] = '\0';
Bstrncpy(game_executable, DefaultGameLocalExec, sizeof(game_executable));
if (enginePreInit())
M32_FatalEngineError();
if ((i = CallExtInit()) < 0) return -1;
#ifdef STARTUP_SETUP_WINDOW
if (i || forcesetup || cmdsetup)
{
if (quitevent || !startwin_run())
{
engineUnInit();
Bexit(0);
}
}
#endif
if (CallExtPostStartupWindow() < 0) return -1;
loadnames(g_namesFileName);
if (initinput()) return -1;
mouseInit();
timerInit(TIMERINTSPERSECOND);
timerSetCallback(keytimerstuff);
artLoadFiles("tiles000.art", g_maxCacheSize);
Bstrcpy(kensig,"Uses BUILD technology by Ken Silverman");
initcrc();
const char *defsfile = G_DefFile();
if (!loaddefinitionsfile(defsfile))
initprintf("Definitions file \"%s\" loaded.\n",defsfile);
for (char * m : g_defModules)
free(m);
g_defModules.clear();
if (enginePostInit())
M32_FatalEngineError();
CallExtPostInit();
#ifdef YAX_ENABLE
// init dummy texture for YAX
// must be after loadpics(), which inits BUILD's cache
i = MAXTILES-1;
if (tilesiz[i].x==0 && tilesiz[i].y==0)
{
static char R[8*16] = { //
0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0,
0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0,
0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0,
0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0,
0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0,
0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0,
0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0,
};
char *newtile;
int32_t sx=32, sy=32, col, j;
walock[i] = 255; // permanent tile
picsiz[i] = 5 + (5<<4);
tilesiz[i].x = sx; tilesiz[i].y = sy;
cacheAllocateBlock(&waloff[i], sx*sy, &walock[i]);
newtile = (char *)waloff[i];
col = paletteGetClosestColor(128, 128, 0);
for (j=0; j<(signed)sizeof(R); j++)
R[j] *= col;
Bmemset(newtile, 0, sx*sy);
for (j=0; j<8; j++)
Bmemcpy(&newtile[32*j], &R[16*j], 16);
}
#endif
#ifdef HAVE_CLIPSHAPE_FEATURE
int k = engineLoadClipMaps();
if (k>0)
initprintf("There was an error loading the sprite clipping map (status %d).\n", k);
for (char * f : g_clipMapFiles)
free(f);
g_clipMapFiles.clear();
#endif
taglab_init();
mkonwinvalid();
// executed once per init
OSD_Exec("m32_autoexec.cfg");
if (LoadBoard(boardfilename, 1))
reset_default_mapstate();
totalclock = 0;
updatesector(pos.x,pos.y,&cursectnum);
keySetCallback(&m32_keypresscallback);
M32_OnShowOSD(0); // make sure the desktop's mouse cursor is hidden
if (cursectnum == -1)
{
vid_gamma_3d = g_videoGamma;
vid_brightness_3d = g_videoBrightness;
vid_contrast_3d = g_videoContrast;
g_videoGamma = g_videoContrast = 1.0;
g_videoBrightness = 0.0;
videoSetPalette(0,0,0);
if (videoSetGameMode(fullscreen, xdim2d, ydim2d, 8, upscalefactor) < 0)
{
CallExtUnInit();
engineUnInit();
Bprintf("%d * %d not supported in this graphics mode\n",xdim2d,ydim2d);
Bexit(0);
}
system_getcvars();
overheadeditor();
keystatus[buildkeys[BK_MODE2D_3D]] = 0;
g_videoGamma = vid_gamma_3d;
g_videoContrast = vid_contrast_3d;
g_videoBrightness = vid_brightness_3d;
vid_gamma_3d = vid_contrast_3d = vid_brightness_3d = -1;
videoSetPalette(GAMMA_CALC,0,0);
}
else
{
if (videoSetGameMode(fullscreen, xdimgame, ydimgame, bppgame, upscalefactor) < 0)
{
CallExtUnInit();
engineUnInit();
Bprintf("%d * %d not supported in this graphics mode\n",xdim,ydim);
Bexit(0);
}
system_getcvars();
videoSetPalette(GAMMA_CALC,0,0);
}
CANCEL:
quitflag = 0;
while (quitflag == 0)
{
if (handleevents())
{
if (quitevent)
{
keystatus[sc_Escape] = 1;
quitevent = 0;
}
}
OSD_DispatchQueued();
videoNextPage();
synctics = totalclock-lockclock;
lockclock += synctics;
CallExtPreCheckKeys();
M32_DrawRoomsAndMasks();
inputchecked = 1;
#ifdef M32_SHOWDEBUG
if (searchstat>=0 && (searchwall<0 || searchsector<0))
{
if (m32_numdebuglines<64)
Bsprintf(m32_debugstr[m32_numdebuglines++], "inconsistent search variables!");
searchstat = -1;
}
M32_drawdebug();
#endif
CallExtCheckKeys();
if (keystatus[sc_Escape])
{
keystatus[sc_Escape] = 0;
printext256(0,0,whitecol,0,"Are you sure you want to quit?",0);
videoShowFrame(1);
synctics = totalclock-lockclock;
lockclock += synctics;
while ((keystatus[sc_Escape]|keystatus[sc_Enter]|keystatus[sc_Space]|keystatus[sc_N]) == 0)
{
idle_waitevent();
if (handleevents())
{
if (quitevent)
{
quitflag = 1;
break;
}
}
if (keystatus[sc_Y]||keystatus[sc_Enter]) // Y or ENTER
{
keystatus[sc_Y] = 0;
keystatus[sc_Enter] = 0;
quitflag = 1; break;
}
}
while (keystatus[sc_Escape])
{
keystatus[sc_Escape] = 0;
quitevent = 0;
goto CANCEL;
}
}
}
if (asksave)
{
i = CheckMapCorruption(4, 0);
printext256(0,8,whitecol,0,i<4?"Save changes?":"Map is heavily corrupt. Save changes?",0);
videoShowFrame(1);
while ((keystatus[sc_Escape]|keystatus[sc_Enter]|keystatus[sc_Space]|keystatus[sc_N]|keystatus[sc_C]) == 0)
{
idle_waitevent();
if (handleevents()) { if (quitevent) break; } // like saying no
if (keystatus[sc_Y] || keystatus[sc_Enter]) // Y or ENTER
{
keystatus[sc_Y] = keystatus[sc_Enter] = 0;
SaveBoard(NULL, M32_SB_ASKOV);
break;
}
}
while (keystatus[sc_Escape]||keystatus[sc_C])
{
keystatus[sc_Escape] = keystatus[sc_C] = 0;
quitevent = 0;
goto CANCEL;
}
}
CallExtUnInit();
// clearfilenames();
engineUnInit();
return 0;
}
static int32_t mhk=0;
static void loadmhk(int32_t domessage)
{
char levname[BMAX_PATH];
if (!mhk)
return;
Bstrcpy(levname, boardfilename);
append_ext_UNSAFE(levname, ".mhk");
if (!engineLoadMHK(levname))
{
if (domessage)
message("Loaded map hack file \"%s\"",levname);
else
initprintf("Loaded map hack file \"%s\"\n",levname);
}
else
{
mhk=2;
if (domessage)
message("No maphack found for map \"%s\"",boardfilename);
}
}
// this is spriteon{ceiling,ground}z from astub.c packed into
// one convenient function
void spriteoncfz(int32_t i, int32_t *czptr, int32_t *fzptr)
{
int32_t height, zofs;
getzsofslope(sprite[i].sectnum, sprite[i].x,sprite[i].y, czptr, fzptr);
if ((sprite[i].cstat&48)==32)
return;
zofs = spriteheightofs(i, &height, 0);
*czptr += height - zofs;
*fzptr -= zofs;
}
static void move_and_update(int32_t xvect, int32_t yvect, int32_t addshr)
{
if (m32_clipping==0)
{
pos.x += xvect>>(14+addshr);
pos.y += yvect>>(14+addshr);
updatesector(pos.x,pos.y, &cursectnum);
}
else
{
clipmove(&pos,&cursectnum, xvect>>addshr,yvect>>addshr,
128,4<<8,4<<8, (m32_clipping==1) ? 0 : CLIPMASK0);
}
if (in3dmode())
{
silentmessage("x:%d y:%d z:%d ang:%d horiz:%d", pos.x, pos.y, pos.z, ang, horiz);
getmessagetimeoff = totalclock+30;
}
}
static void mainloop_move(void)
{
int32_t xvect, yvect, doubvel;
if (angvel != 0) //ang += angvel * constant
{
if (eitherCTRL && m32_2d3dmode)
{
int x = m32_2d3d.x + (angvel / 32);
int xx = m32_2d3d.x + XSIZE_2D3D + (angvel / 32);
if (x > 4 && xx < xdim2d - 4)
{
silentmessage("2d3d x:%d y:%d", m32_2d3d.x, m32_2d3d.y);
m32_2d3d.x += (angvel / 32);
}
}
else
{
//ENGINE calculates angvel for you
//Lt. shift makes turn velocity 50% faster
doubvel = (synctics + DOWN_BK(RUN)*(synctics>>1));
ang += ((angvel*doubvel)>>4);
ang &= 2047;
if (in3dmode())
{
silentmessage("x:%d y:%d z:%d ang:%d horiz:%d", pos.x, pos.y, pos.z, ang, horiz);
getmessagetimeoff = totalclock+30;
}
}
}
if ((vel|svel) != 0)
{
if (eitherCTRL && m32_2d3dmode)
{
int y = m32_2d3d.y - (vel / 64);
int yy = m32_2d3d.y + YSIZE_2D3D - (vel / 64);
if (y > 4 && yy < ydim2d - STATUS2DSIZ2 - 4)
{
silentmessage("2d3d x:%d y:%d", m32_2d3d.x, m32_2d3d.y);
m32_2d3d.y -= (vel / 64);
}
}
else
{
//Lt. shift doubles forward velocity
doubvel = (1+(DOWN_BK(RUN)))*synctics;
xvect = 0;
yvect = 0;
if (vel != 0)
{
xvect += ((vel*doubvel)>>3)*(int32_t) sintable[(ang+2560)&2047];
yvect += ((vel*doubvel)>>3)*(int32_t) sintable[(ang+2048)&2047];
}
if (svel != 0)
{
xvect += ((svel*doubvel)>>3)*(int32_t) sintable[(ang+2048)&2047];
yvect += ((svel*doubvel)>>3)*(int32_t) sintable[(ang+1536)&2047];
}
move_and_update(xvect, yvect, 0);
}
}
}
static void handle_sprite_in_clipboard(int32_t i)
{
if (somethingintab == 3)
{
int32_t j, k;
sprite[i].picnum = temppicnum;
if (tilesiz[temppicnum].x <= 0 || tilesiz[temppicnum].y <= 0)
{
j = 0;
for (k=0; k<MAXTILES; k++)
if (tilesiz[k].x > 0 && tilesiz[k].y > 0)
{
j = k;
break;
}
sprite[i].picnum = j;
}
sprite[i].shade = tempshade;
sprite[i].blend = tempblend;
sprite[i].pal = temppal;
sprite[i].xrepeat = max(tempxrepeat, 1);
sprite[i].yrepeat = max(tempyrepeat, 1);
sprite[i].cstat = tempcstat;
}
}
void editinput(void)
{
int32_t mousz, bstatus;
int32_t i, tempint=0;
int32_t goalz, xvect, yvect, hiz, loz, oposz;
int32_t hihit, lohit, omlook=mlook;
// 3B 3C 3D 3E 3F 40 41 42 43 44 57 58 46
// F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 SCROLL
mousz = 0;
mouseGetValues(&mousx,&mousy,&bstatus);
mousx = (mousx<<16) + mousexsurp;
mousy = (mousy<<16) + mouseysurp;
if (unrealedlook && !mskip)
{
if (mlook==0 && (bstatus&(1|2|4))==2)
mlook = 3;
else if ((bstatus&(1|2|4))==1)
mlook = 3;
}
{
ldiv_t ld;
if (mlook)
{
ld = ldiv(mousx, (int32_t)((1<<16)/(msens*0.5f))); mousx = ld.quot; mousexsurp = ld.rem;
ld = ldiv(mousy, (int32_t)((1<<16)/(msens*0.25f))); mousy = ld.quot; mouseysurp = ld.rem;
}
else
{
ld = ldiv(mousx, (int32_t)((1<<16)/msens)); mousx = ld.quot; mousexsurp = ld.rem;
ld = ldiv(mousy, (int32_t)((1<<16)/msens)); mousy = ld.quot; mouseysurp = ld.rem;
}
}
if (mlook == 3)
mlook = omlook;
// UnrealEd:
// rmb: mouselook
// lbm: x:turn y:fwd/back local x
// lmb&rmb: x:strafe y:up/dn (move in local yz plane)
// mmb: fwd/back in viewing vector
if (unrealedlook && !mskip) //PK
{
if ((bstatus&(1|2|4))==1)
{
ang += mousx;
xvect = -((mousy*(int32_t)sintable[(ang+2560)&2047])<<(3+pk_uedaccel));
yvect = -((mousy*(int32_t)sintable[(ang+2048)&2047])<<(3+pk_uedaccel));
move_and_update(xvect, yvect, 0);
}
else if (!mlook && (bstatus&(1|2|4))==2)
{
mlook=2;
}
else if ((bstatus&(1|2|4))==(1|2))
{
zmode = 2;
xvect = -((mousx*(int32_t)sintable[(ang+2048)&2047])<<pk_uedaccel);
yvect = -((mousx*(int32_t)sintable[(ang+1536)&2047])<<pk_uedaccel);
pos.z += mousy<<(4+pk_uedaccel);
move_and_update(xvect, yvect, 0);
}
else if ((bstatus&(1|2|4))==4)
{
zmode = 2;
// horiz-100 of 200 is viewing at 326.4 build angle units (=atan(200/128)) upward
tempint = getangle(128, horiz-100);
xvect = -((mousy*
((int32_t)sintable[(ang+2560)&2047]>>6)*
((int32_t)sintable[(tempint+512)&2047])>>6)
<<pk_uedaccel);
yvect = -((mousy*
((int32_t)sintable[(ang+2048)&2047]>>6)*
((int32_t)sintable[(tempint+512)&2047])>>6)
<<pk_uedaccel);
pos.z += mousy*(((int32_t)sintable[(tempint+2048)&2047])>>(10-pk_uedaccel));
move_and_update(xvect, yvect, 2);
}
}
if (mskip)
{
// mskip was set in astub.c to not trigger UEd mouse movements.
// Reset now.
mskip = 0;
}
else
{
if (mlook && (unrealedlook==0 || (bstatus&(1|4))==0))
{
ang += mousx;
horiz -= mousy;
/*
if (mousy && !(mousy/4))
horiz--;
if (mousx && !(mousx/2))
ang++;
*/
inpclamp(&horiz, -99, 299);
if (mlook == 1)
{
searchx = xdim>>1;
searchy = ydim>>1;
}
osearchx = searchx-mousx;
osearchy = searchy-mousy;
if (mousx || mousy)
{
silentmessage("x:%d y:%d z:%d ang:%d horiz:%d", pos.x, pos.y, pos.z, ang, horiz);
getmessagetimeoff = totalclock+30;
}
}
else if (unrealedlook==0 || (bstatus&(1|2|4))==0)
{
osearchx = searchx;
osearchy = searchy;
searchx += mousx;
searchy += mousy;
inpclamp(&searchx, 12, xdim-13);
inpclamp(&searchy, 12, ydim-13);
}
}
// showmouse();
if (keystatus[sc_F9]) // F9
{
if (mhk)
{
Bmemset(spriteext, 0, sizeof(spriteext_t) * MAXSPRITES);
Bmemset(spritesmooth, 0, sizeof(spritesmooth_t) * (MAXSPRITES+MAXUNIQHUDID));
engineClearLightsFromMHK();
mhk = 0;
message("Maphacks disabled");
}
else
{
mhk = 1;
loadmhk(1);
}
keystatus[sc_F9] = 0;
}
mainloop_move();
getzrange(&pos,cursectnum, &hiz,&hihit, &loz,&lohit, 128, (m32_clipping==1)?0:CLIPMASK0);
/*
{
int32_t his = !(hihit&32768), los = !(lohit&32768);
if (m32_numdebuglines<64)
Bsprintf(m32_debugstr[m32_numdebuglines++], "s%d: cf[%s%d, %s%d] z(%d, %d)", cursectnum,
his?"s":"w",hihit&16383, los?"s":"w",lohit&16383, hiz,loz);
}
*/
oposz = pos.z;
if (zmode == 0)
{
goalz = loz-(kensplayerheight<<8); //playerheight pixels above floor
if (goalz < hiz+(16<<8)) //ceiling&floor too close
goalz = (loz+hiz)>>1;
goalz += mousz;
if (DOWN_BK(MOVEUP)) //A (stand high)
{
goalz -= (16<<8);
if (DOWN_BK(RUN))
goalz -= (24<<8);
}
if (DOWN_BK(MOVEDOWN)) //Z (stand low)
{
goalz += (12<<8);
if (DOWN_BK(RUN))
goalz += (12<<8);
}
if (goalz != pos.z)
{
if (pos.z < goalz) hvel += 64;
if (pos.z > goalz) hvel = ((goalz-pos.z)>>3);
pos.z += hvel;
if (pos.z > loz-(4<<8)) pos.z = loz-(4<<8), hvel = 0;
if (pos.z < hiz+(4<<8)) pos.z = hiz+(4<<8), hvel = 0;
}
}
else
{
goalz = pos.z;
if (DOWN_BK(MOVEUP)) //A
{
if (eitherCTRL)
{
horiz = max(-100,horiz-((DOWN_BK(RUN)+1)*synctics*2));
}
else
{
if (zmode != 1)
goalz -= (8<<8);
else
{
zlock += (4<<8);
DOWN_BK(MOVEUP) = 0;
}
}
}
if (DOWN_BK(MOVEDOWN)) //Z (stand low)
{
if (eitherCTRL)
{
horiz = min(300,horiz+((DOWN_BK(RUN)+1)*synctics*2));
}
else
{
if (zmode != 1)
goalz += (8<<8);
else if (zlock > 0)
{
zlock -= (4<<8);
DOWN_BK(MOVEDOWN) = 0;
}
}
}
if (m32_clipping)
inpclamp(&goalz, hiz+(4<<8), loz-(4<<8));
if (zmode == 1) goalz = loz-zlock;
if (m32_clipping && (goalz < hiz+(4<<8)))
goalz = ((loz+hiz)>>1); //ceiling&floor too close
if (zmode == 1) pos.z = goalz;
if (goalz != pos.z)
{
//if (pos.z < goalz) hvel += (32<<DOWN_BK(RUN));
//if (pos.z > goalz) hvel -= (32<<DOWN_BK(RUN));
if (pos.z < goalz)
hvel = ((192*synctics)<<DOWN_BK(RUN));
else
hvel = -((192*synctics)<<DOWN_BK(RUN));
pos.z += hvel;
if (m32_clipping)
{
if (pos.z > loz-(4<<8)) pos.z = loz-(4<<8), hvel = 0;
if (pos.z < hiz+(4<<8)) pos.z = hiz+(4<<8), hvel = 0;
}
}
else
hvel = 0;
}
{
int16_t ocursectnum = cursectnum;
updatesectorz(pos.x,pos.y,pos.z, &cursectnum);
if (cursectnum<0)
{
if (zmode != 2)
pos.z = oposz; // don't allow to fall into infinity when in void space
cursectnum = ocursectnum;
}
}
if (pos.z != oposz && in3dmode())
{
silentmessage("x:%d y:%d z:%d ang:%d horiz:%d", pos.x, pos.y, pos.z, ang, horiz);
getmessagetimeoff = totalclock+30;
}
searchit = 2;
if (searchstat >= 0)
{
if ((bstatus&(1|2|4)) || keystatus[sc_Space]) // SPACE
searchit = 0;
if (keystatus[sc_S]) //S (insert sprite) (3D)
{
hitdata_t hit;
vec2_t da = { 16384, divscale14(searchx-(xdim>>1), xdim>>1) };
rotatepoint(zerovec, da, ang, &da);
#ifdef USE_OPENGL
if (videoGetRenderMode() == REND_POLYMOST)
hit = polymost_hitdata;
else
#endif
hitscan((const vec3_t *)&pos,cursectnum, //Start position
da.x,da.y,(scale(searchy,200,ydim)-horiz)*2000, //vector of 3D ang
&hit,CLIPMASK1);
if (hit.sect >= 0)
{
da.x = hit.pos.x;
da.y = hit.pos.y;
if (gridlock && grid > 0)
{
if (AIMING_AT_WALL || AIMING_AT_MASKWALL)
hit.pos.z &= 0xfffffc00;
else
locktogrid(&da.x, &da.y);
}
i = insert_sprite_common(hit.sect, da.x, da.y);
if (i < 0)
message("Couldn't insert sprite.");
else
{
int32_t cz, fz;
handle_sprite_in_clipboard(i);
spriteoncfz(i, &cz, &fz);
sprite[i].z = clamp2(hit.pos.z, cz, fz);
if (AIMING_AT_WALL || AIMING_AT_MASKWALL)
{
sprite[i].cstat &= ~48;
sprite[i].cstat |= (16+64);
correct_ornamented_sprite(i, hit.wall);
}
else
sprite[i].cstat |= (tilesiz[sprite[i].picnum].y>=32);
correct_sprite_yoffset(i);
asksave = 1;
VM_OnEvent(EVENT_INSERTSPRITE3D, i);
}
}
keystatus[sc_S] = 0;
}
if (keystatus[sc_F5]||keystatus[sc_F6]) //F5,F6
{
switch (searchstat)
{
case SEARCH_CEILING:
case SEARCH_FLOOR:
CallExtShowSectorData(searchsector); break;
case SEARCH_WALL:
case SEARCH_MASKWALL:
CallExtShowWallData(searchwall); break;
case SEARCH_SPRITE:
CallExtShowSpriteData(searchwall); break;
}
keystatus[sc_F5] = keystatus[sc_F6] = 0;
}
if (keystatus[sc_F7]||keystatus[sc_F8]) //F7,F8
{
switch (searchstat)
{
case SEARCH_CEILING:
case SEARCH_FLOOR:
CallExtEditSectorData(searchsector); break;
case SEARCH_WALL:
case SEARCH_MASKWALL:
CallExtEditWallData(searchwall); break;
case SEARCH_SPRITE:
CallExtEditSpriteData(searchwall); break;
}
keystatus[sc_F7] = keystatus[sc_F8] = 0;
}
}
if (keystatus[buildkeys[BK_MODE2D_3D]] && !m32_is2d3dmode()) // Enter
{
vid_gamma_3d = g_videoGamma;
vid_contrast_3d = g_videoContrast;
vid_brightness_3d = g_videoBrightness;
g_videoGamma = g_videoContrast = 1.0;
g_videoBrightness = 0.0;
videoSetPalette(0,0,0);
keystatus[buildkeys[BK_MODE2D_3D]] = 0;
overheadeditor();
keystatus[buildkeys[BK_MODE2D_3D]] = 0;
g_videoGamma = vid_gamma_3d;
g_videoContrast = vid_contrast_3d;
g_videoBrightness = vid_brightness_3d;
vid_gamma_3d = vid_contrast_3d = vid_brightness_3d = -1;
videoSetPalette(GAMMA_CALC,0,0);
}
}
char changechar(char dachar, int32_t dadir, char smooshyalign, char boundcheck)
{
if (dadir < 0)
{
if ((dachar > 0) || (boundcheck == 0))
{
dachar--;
if (smooshyalign > 0)
dachar = (dachar&0xf8);
}
}
else if (dadir > 0)
{
if ((dachar < 255) || (boundcheck == 0))
{
dachar++;
if (smooshyalign > 0)
{
if (dachar >= 256-8) dachar = 255;
else dachar = ((dachar+7)&0xf8);
}
}
}
return dachar;
}
////////////////////// OVERHEADEDITOR //////////////////////
// some 2d mode state
static struct overheadstate
{
// number of backed up drawn walls
int32_t bak_wallsdrawn;
// state related to line drawing
int16_t suckwall, split;
int16_t splitsect;
int16_t splitstartwall;
} ovh;
static int32_t inside_editor(const vec3_t *pos, int32_t searchx, int32_t searchy, int32_t zoom,
int32_t x, int32_t y, int16_t sectnum)
{
if (!m32_sideview)
return inside(x, y, sectnum);
// if in side-view mode, use the screen coords instead
{
int32_t dst = MAXSECTORS+M32_FIXME_SECTORS-1, i, oi;
int32_t srcw=sector[sectnum].wallptr, dstw=MAXWALLS;
int32_t ret;
if (sector[sectnum].wallnum > M32_FIXME_WALLS)
return -1;
Bmemcpy(&sector[dst], &sector[sectnum], sizeof(sectortype));
sector[dst].wallptr = dstw;
Bmemcpy(&wall[dstw], &wall[srcw], sector[dst].wallnum*sizeof(walltype));
for (i=dstw, oi=srcw; i<dstw+sector[dst].wallnum; i++, oi++)
{
wall[i].point2 += dstw-srcw;
editorGet2dScreenCoordinates(&wall[i].x, &wall[i].y, wall[i].x-pos->x, wall[i].y-pos->y, zoom);
wall[i].y += getscreenvdisp(getflorzofslope(sectnum,wall[oi].x,wall[oi].y)-pos->z, zoom);
wall[i].x += halfxdim16;
wall[i].y += midydim16;
}
i = numsectors;
numsectors = dst+1;
ret = inside(searchx, searchy, dst);
numsectors = i;
return ret;
}
}
int32_t inside_editor_curpos(int16_t sectnum)
{
// TODO: take care: mous[xy]plc global vs overheadeditor auto
return inside_editor(&pos, searchx,searchy, zoom, mousxplc,mousyplc, sectnum);
}
static inline void drawline16base(int32_t bx, int32_t by, int32_t x1, int32_t y1, int32_t x2, int32_t y2, char col)
{
editorDraw2dLine(bx+x1, by+y1, bx+x2, by+y2, col);
}
void drawsmallabel(const char *text, char col, char backcol, char border, int32_t dax, int32_t day, int32_t daz)
{
editorGet2dScreenCoordinates(&dax,&day, dax-pos.x,day-pos.y, zoom);
if (m32_sideview)
day += getscreenvdisp(daz-pos.z, zoom);
int32_t const x1 = halfxdim16+dax-(Bstrlen(text)<<1);
int32_t const y1 = midydim16+day-4;
int32_t const x2 = x1 + (Bstrlen(text)<<2)+2;
int32_t const y2 = y1 + 7;
int f = mulscale8(x2-x1, zoom);
if ((x1 <= -f) || (x2 >= xdim + f) || (y1 <= -f) || (y2 >= ydim16 + f))
return;
printext16(x1,y1, col,backcol, text,1);
editorDraw2dLine(x1-2, y1-2, x2-2, y1-2, border);
editorDraw2dLine(x1-2, y2+1, x2-2, y2+1, border);
editorDraw2dLine(x1-3, y1-1, x1-3, y2+0, border);
editorDraw2dLine(x2-1, y1-1, x2-1, y2+0, border);
editorDraw2dLine(x1-1,y1-1, x2-3,y1-1, backcol);
editorDraw2dLine(x1-1,y2+0, x2-3,y2+0, backcol);
editorDraw2dLine(x1-2,y1+0, x1-2,y2-1, backcol);
editorDraw2dLine(x2-2,y1+0, x2-2,y2-1, backcol);
editorDraw2dLine(x2-3,y1+0, x2-3,y2+0, backcol);
videoBeginDrawing(); //{{{
if ((unsigned)y1-1 < ydim16+0u && (unsigned) (x1-2) < xdim2d+0u && (unsigned) (x2-2) < xdim2d+0u)
{
drawpixel((char *) (frameplace + ((y1-1) * bytesperline) + (x1-2)), border);
drawpixel((char *) (frameplace + ((y1-1) * bytesperline) + (x2-2)), border);
}
if ((unsigned) y2 < ydim16+0u && (unsigned) (x1-2) < xdim2d+0u && (unsigned) (x2-2) < xdim2d+0u)
{
drawpixel((char *) (frameplace + ((y2) * bytesperline) + (x1-2)), border);
drawpixel((char *) (frameplace + ((y2) * bytesperline) + (x2-2)), border);
}
videoEndDrawing();
}
// backup highlighted sectors with sprites as mapinfo for later restoration
// return values:
// -1: highlightsectorcnt<=0
// 0: ok
static int32_t backup_highlighted_map(mapinfofull_t *mapinfo)
{
int32_t i, j, k, m, tmpnumwalls=0, tmpnumsprites=0;
int16_t *const otonsect = (int16_t *)tempxyar; // STRICTALIASING
int16_t *const otonwall = ((int16_t *)tempxyar) + MAXWALLS;
#ifdef YAX_ENABLE
int16_t otonbunch[YAX_MAXBUNCHES];
int16_t numsectsofbunch[YAX_MAXBUNCHES]; // ceilings + floors
#endif
if (highlightsectorcnt <= 0)
return -1;
#ifdef YAX_ENABLE
for (i=0; i<numyaxbunches; i++)
numsectsofbunch[i] = 0;
#endif
// set up old-->new mappings
j = 0;
k = 0;
for (i=0; i<numsectors; i++)
{
int32_t startwall, endwall;
if (hlsectorbitmap[i>>3]&(1<<(i&7)))
{
#ifdef YAX_ENABLE
int16_t bn[2], cf;
yax_getbunches(i, &bn[0], &bn[1]);
for (cf=0; cf<2; cf++)
if (bn[cf] >= 0)
numsectsofbunch[bn[cf]]++;
#endif
otonsect[i] = j++;
for (WALLS_OF_SECTOR(i, m))
otonwall[m] = k++;
}
else
{
otonsect[i] = -1;
for (WALLS_OF_SECTOR(i, m))
otonwall[m] = -1;
}
}
#ifdef YAX_ENABLE
j = 0;
for (i=0; i<numyaxbunches; i++)
{
// only back up complete bunches
if (numsectsofbunch[i] == yax_numsectsinbunch(i, 0)+yax_numsectsinbunch(i, 1))
otonbunch[i] = j++; // kept bunch
else
otonbunch[i] = -1; // discarded bunch
}
mapinfo->numyaxbunches = j;
#endif
// count walls & sprites
for (i=0; i<highlightsectorcnt; i++)
{
tmpnumwalls += sector[highlightsector[i]].wallnum;
m = headspritesect[highlightsector[i]];
while (m != -1)
{
tmpnumsprites++;
m = nextspritesect[m];
}
}
// allocate temp storage
mapinfo->sector = (usectortype *)Xmalloc(highlightsectorcnt * sizeof(sectortype));
mapinfo->wall = (uwalltype *)Xmalloc(tmpnumwalls * sizeof(walltype));
#ifdef YAX_ENABLE
if (mapinfo->numyaxbunches > 0)
{
mapinfo->bunchnum = (int16_t *)Xmalloc(highlightsectorcnt*2*sizeof(int16_t));
mapinfo->ynextwall = (int16_t *)Xmalloc(tmpnumwalls*2*sizeof(int16_t));
}
else
{
mapinfo->bunchnum = mapinfo->ynextwall = NULL;
}
#endif
if (tmpnumsprites>0)
{
mapinfo->sprite = (uspritetype *)Xmalloc(tmpnumsprites * sizeof(spritetype));
}
else
{
// would never be accessed because mapinfo->numsprites is 0, but cleaner
mapinfo->sprite = NULL;
}
// copy everything over
tmpnumwalls = 0;
tmpnumsprites = 0;
for (i=0; i<highlightsectorcnt; i++)
{
k = highlightsector[i];
Bmemcpy(&mapinfo->sector[i], &sector[k], sizeof(sectortype));
mapinfo->sector[i].wallptr = tmpnumwalls;
#ifdef YAX_ENABLE
if (mapinfo->numyaxbunches > 0 || numyaxbunches > 0)
{
int16_t bn[2];
yax_getbunches(k, &bn[0], &bn[1]);
for (j=0; j<2; j++)
{
// old bunchnum, new bunchnum
int32_t obn=bn[j], nbn=(obn>=0) ? otonbunch[obn] : -1;
if (mapinfo->numyaxbunches > 0)
mapinfo->bunchnum[2*i + j] = nbn;
if (obn >= 0 && nbn < 0)
{
// A bunch was discarded.
usectortype *const sec = &mapinfo->sector[i];
# if !defined NEW_MAP_FORMAT
uint16_t *const cs = j==YAX_CEILING ? &sec->ceilingstat : &sec->floorstat;
uint8_t *const xp = j==YAX_CEILING ? &sec->ceilingxpanning : &sec->floorxpanning;
*cs &= ~YAX_BIT;
*xp = 0;
# else
if (j == YAX_CEILING)
sec->ceilingbunch = -1;
else
sec->floorbunch = -1;
# endif
}
}
}
#endif
for (j=0; j<sector[k].wallnum; j++)
{
m = sector[k].wallptr;
Bmemcpy(&mapinfo->wall[tmpnumwalls+j], &wall[m+j], sizeof(walltype));
mapinfo->wall[tmpnumwalls+j].point2 += (tmpnumwalls-m);
#ifdef YAX_ENABLE
if (mapinfo->numyaxbunches > 0 || numyaxbunches > 0)
{
int32_t cf;
for (cf=0; cf<2; cf++)
{
const int32_t ynw = yax_getnextwall(m+j, cf);
const int32_t nynw = (ynw >= 0) ? otonwall[ynw] : -1;
if (mapinfo->numyaxbunches > 0)
mapinfo->ynextwall[2*(tmpnumwalls+j) + cf] = nynw;
if (ynw >= 0 && nynw < 0) // CLEAR_YNEXTWALLS
YAX_PTRNEXTWALL(mapinfo->wall, tmpnumwalls+j, cf) = YAX_NEXTWALLDEFAULT(cf);
}
}
#endif
m = mapinfo->wall[tmpnumwalls+j].nextsector;
if (m < 0 || otonsect[m] < 0)
{
mapinfo->wall[tmpnumwalls+j].nextsector = -1;
mapinfo->wall[tmpnumwalls+j].nextwall = -1;
}
else
{
mapinfo->wall[tmpnumwalls+j].nextsector = otonsect[m];
m = mapinfo->wall[tmpnumwalls+j].nextwall;
mapinfo->wall[tmpnumwalls+j].nextwall = otonwall[m];
}
}
tmpnumwalls += j;
m = headspritesect[highlightsector[i]];
while (m != -1)
{
Bmemcpy(&mapinfo->sprite[tmpnumsprites], &sprite[m], sizeof(spritetype));
mapinfo->sprite[tmpnumsprites].sectnum = otonsect[highlightsector[i]];
m = nextspritesect[m];
tmpnumsprites++;
}
}
mapinfo->numsectors = highlightsectorcnt;
mapinfo->numwalls = tmpnumwalls;
mapinfo->numsprites = tmpnumsprites;
return 0;
}
static void mapinfofull_free(mapinfofull_t *mapinfo)
{
Bfree(mapinfo->sector);
#ifdef YAX_ENABLE
if (mapinfo->numyaxbunches > 0)
{
Bfree(mapinfo->bunchnum);
Bfree(mapinfo->ynextwall);
}
#endif
Bfree(mapinfo->wall);
if (mapinfo->numsprites>0)
Bfree(mapinfo->sprite);
}
// restore map saved with backup_highlighted_map, also
// frees mapinfo's sector, wall, (sprite) in any case.
// return values:
// -1: limits exceeded
// 0: ok
// forreal: if 0, only test if we have enough space (same return values)
static int32_t restore_highlighted_map(mapinfofull_t *mapinfo, int32_t forreal)
{
int32_t i, j, onumsectors=numsectors, newnumsectors, newnumwalls;
if (numsectors+mapinfo->numsectors>MAXSECTORS || numwalls+mapinfo->numwalls>MAXWALLS
#ifdef YAX_ENABLE
|| numyaxbunches+mapinfo->numyaxbunches > YAX_MAXBUNCHES
#endif
|| Numsprites+mapinfo->numsprites>MAXSPRITES)
{
mapinfofull_free(mapinfo);
return -1;
}
if (!forreal)
return 0;
newnumsectors = numsectors + mapinfo->numsectors;
newnumwalls = numwalls + mapinfo->numwalls;
// copy sectors & walls
Bmemcpy(&sector[numsectors], mapinfo->sector, mapinfo->numsectors*sizeof(sectortype));
Bmemcpy(&wall[numwalls], mapinfo->wall, mapinfo->numwalls*sizeof(walltype));
// tweak index members
for (i=numwalls; i<newnumwalls; i++)
{
wall[i].point2 += numwalls;
if (wall[i].nextsector >= 0)
{
wall[i].nextsector += numsectors;
wall[i].nextwall += numwalls;
}
#ifdef YAX_ENABLE
for (j=0; j<2; j++)
{
if (mapinfo->numyaxbunches > 0)
{
yax_setnextwall(i, j, mapinfo->ynextwall[2*(i-numwalls) + j]>=0 ?
numwalls+mapinfo->ynextwall[2*(i-numwalls) + j] : -1);
}
else
{
# if !defined NEW_MAP_FORMAT
// XXX: When copying a TROR portion into a non-TROR map (e.g. a
// new one), tags denoting ynextwalls are left in place.
wall[i].cstat &= ~YAX_NEXTWALLBIT(j); // CLEAR_YNEXTWALLS
# else
yax_setnextwall(i, j, -1);
# endif
}
}
#endif
}
for (i=numsectors; i<newnumsectors; i++)
sector[i].wallptr += numwalls;
// highlight copied sectors
numsectors = newnumsectors;
Bmemset(hlsectorbitmap, 0, sizeof(hlsectorbitmap));
for (i=onumsectors; i<newnumsectors; i++)
{
hlsectorbitmap[i>>3] |= (1<<(i&7));
#ifdef YAX_ENABLE
for (j=0; j<2; j++)
{
if (mapinfo->numyaxbunches > 0)
{
int32_t bn = mapinfo->bunchnum[2*(i-onumsectors)+j];
yax_setbunch(i, j, bn>=0 ? numyaxbunches+bn : -2);
// -2 clears forward yax-nextwall links.
// XXX: still may wrongly reset xpanning.
}
else
Bassert(yax_getbunch(i, j) < 0);
}
#endif
}
// insert sprites
for (i=0; i<mapinfo->numsprites; i++)
{
const uspritetype *srcspr = &mapinfo->sprite[i];
int32_t sect = onumsectors + srcspr->sectnum;
j = insertsprite(sect, srcspr->statnum);
Bassert(j >= 0);
Bmemcpy(&sprite[j], srcspr, sizeof(spritetype));
sprite[j].sectnum = sect;
}
mapinfofull_free(mapinfo);
numwalls = newnumwalls;
update_highlightsector();
#ifdef YAX_ENABLE
if (mapinfo->numyaxbunches > 0)
yax_update(0);
#endif
yax_updategrays(pos.z);
return 0;
}
static int32_t newnumwalls=-1;
void ovh_whiteoutgrab(int32_t restoreredwalls)
{
int32_t i, j, k, startwall, endwall;
#if 0
//def YAX_ENABLE
int16_t cb, fb;
#endif
if (restoreredwalls)
{
// restore onextwalls first
for (i=0; i<numsectors; i++)
for (WALLS_OF_SECTOR(i, j))
checksectorpointer(j, i);
}
for (i=0; i<MAXWALLS; i++)
onextwall[i] = -1;
//White out all bordering lines of grab that are
//not highlighted on both sides
for (i=highlightsectorcnt-1; i>=0; i--)
for (WALLS_OF_SECTOR(highlightsector[i], j))
{
if (wall[j].nextwall < 0)
continue;
k = wall[j].nextsector;
if (hlsectorbitmap[k>>3]&(1<<(k&7)))
continue;
#if 0
//def YAX_ENABLE
// internal red walls are kept red
yax_getbunches(highlightsector[i], &cb, &fb);
if (cb>=0 && yax_getbunch(k, YAX_CEILING)>=0)
continue;
if (fb>=0 && yax_getbunch(k, YAX_FLOOR)>=0)
continue;
#endif
onextwall[j] = wall[j].nextwall;
NEXTWALL(j).nextwall = -1;
NEXTWALL(j).nextsector = -1;
wall[j].nextwall = -1;
wall[j].nextsector = -1;
}
if (highlightsectorcnt > 0)
mkonwvalid();
else
mkonwinvalid_keeptempsect();
}
static void duplicate_selected_sectors(void)
{
mapinfofull_t mapinfo;
int32_t i, j, onumsectors;
#ifdef YAX_ENABLE
int32_t onumyaxbunches;
#endif
int32_t minx=INT32_MAX, maxx=INT32_MIN, miny=INT32_MAX, maxy=INT32_MIN, dx, dy;
i = backup_highlighted_map(&mapinfo);
if (i < 0)
{
message("Out of memory!");
return;
}
i = restore_highlighted_map(&mapinfo, 0);
if (i < 0)
{
// XXX: no, might be another limit too. Better message needed.
printmessage16("Copying sectors would exceed sector or wall limit.");
return;
}
// restoring would succeed, tweak things...
Bmemset(hlsectorbitmap, 0, sizeof(hlsectorbitmap));
for (i=0; i<highlightsectorcnt; i++)
{
int32_t startwall, endwall;
// first, make red lines of old selected sectors, effectively
// restoring the original state
for (WALLS_OF_SECTOR(highlightsector[i], j))
{
if (wall[j].nextwall >= 0)
checksectorpointer(wall[j].nextwall,wall[j].nextsector);
checksectorpointer(j, highlightsector[i]);
minx = min(minx, wall[j].x);
maxx = max(maxx, wall[j].x);
miny = min(miny, wall[j].y);
maxy = max(maxy, wall[j].y);
}
}
// displace walls & sprites of new sectors by a small amount:
// calculate displacement
if (grid>0 && grid<9)
dx = max(2048>>grid, 128);
else
dx = 512;
dy = -dx;
if (maxx+dx >= editorgridextent) dx*=-1;
if (minx+dx <= -editorgridextent) dx*=-1;
if (maxy+dy >= editorgridextent) dy*=-1;
if (miny+dy <= -editorgridextent) dy*=-1;
onumsectors = numsectors;
#ifdef YAX_ENABLE
onumyaxbunches = numyaxbunches;
#endif
// restore! this will not fail.
restore_highlighted_map(&mapinfo, 1);
// displace
for (i=onumsectors; i<numsectors; i++)
{
for (j=sector[i].wallptr; j<sector[i].wallptr+sector[i].wallnum; j++)
{
wall[j].x += dx;
wall[j].y += dy;
}
for (j=headspritesect[i]; j>=0; j=nextspritesect[j])
{
sprite[j].x += dx;
sprite[j].y += dy;
}
}
#ifdef YAX_ENABLE
if (numyaxbunches > onumyaxbunches)
printmessage16("Sectors duplicated, creating %d new bunches.", numyaxbunches-onumyaxbunches);
else
#endif
printmessage16("Sectors duplicated.");
asksave = 1;
#ifdef YAX_ENABLE
if (numyaxbunches > onumyaxbunches)
yax_update(0);
#endif
yax_updategrays(pos.z);
}
static void duplicate_selected_sprites(void)
{
int32_t i, j, k=0;
for (i=0; i<highlightcnt; i++)
if ((highlight[i]&0xc000) == 16384)
k++;
if (Numsprites + k <= MAXSPRITES)
{
for (i=0; i<highlightcnt; i++)
if ((highlight[i]&0xc000) == 16384)
{
//duplicate sprite
k = (highlight[i]&16383);
j = insertsprite(sprite[k].sectnum,sprite[k].statnum);
Bmemcpy(&sprite[j],&sprite[k],sizeof(spritetype));
// sprite[j].sectnum = sprite[k].sectnum; //Don't let memcpy overwrite sector!
// setsprite(j,(vec3_t *)&sprite[j]);
}
printmessage16("Sprites duplicated.");
asksave = 1;
}
else
{
printmessage16("Copying sprites would exceed sprite limit.");
}
}
static void correct_ornamented_sprite(int32_t i, int32_t hitw)
{
int32_t j;
if (hitw >= 0)
{
sprite[i].ang = (getangle(POINT2(hitw).x-wall[hitw].x,
POINT2(hitw).y-wall[hitw].y)+512)&2047;
//Make sure sprite's in right sector
if (inside(sprite[i].x, sprite[i].y, sprite[i].sectnum) != 1)
{
j = wall[hitw].point2;
sprite[i].x -= ksgn(wall[j].y-wall[hitw].y);
sprite[i].y += ksgn(wall[j].x-wall[hitw].x);
}
}
}
void DoSpriteOrnament(int32_t i)
{
hitdata_t hit;
hitscan((const vec3_t *)&sprite[i],sprite[i].sectnum,
sintable[(sprite[i].ang+1536)&2047],
sintable[(sprite[i].ang+1024)&2047],
0,
&hit,CLIPMASK1);
if (hit.sect == -1)
return;
sprite[i].x = hit.pos.x;
sprite[i].y = hit.pos.y;
sprite[i].z = hit.pos.z;
changespritesect(i, hit.sect);
correct_ornamented_sprite(i, hit.wall);
}
void update_highlight(void)
{
int32_t i;
highlightcnt = 0;
for (i=0; i<numwalls; i++)
if (show2dwall[i>>3]&(1<<(i&7)))
highlight[highlightcnt++] = i;
for (i=0; i<MAXSPRITES; i++)
if (sprite[i].statnum < MAXSTATUS)
{
if (show2dsprite[i>>3]&(1<<(i&7)))
highlight[highlightcnt++] = i+16384;
}
else
show2dsprite[i>>3] &= ~(1<<(i&7));
if (highlightcnt == 0)
highlightcnt = -1;
}
void update_highlightsector(void)
{
int32_t i;
minhlsectorfloorz = INT32_MAX;
numhlsecwalls = 0;
highlightsectorcnt = 0;
for (i=0; i<numsectors; i++)
if (hlsectorbitmap[i>>3]&(1<<(i&7)))
{
highlightsector[highlightsectorcnt++] = i;
minhlsectorfloorz = min(minhlsectorfloorz, sector[i].floorz);
numhlsecwalls += sector[i].wallnum;
}
if (highlightsectorcnt==0)
{
minhlsectorfloorz = 0;
highlightsectorcnt = -1;
}
}
// Get average point of sectors
static void get_sectors_center(const int16_t *sectors, int32_t numsecs, int32_t *cx, int32_t *cy)
{
int32_t i, j, k=0, dax = 0, day = 0;
int32_t startwall, endwall;
for (i=0; i<numsecs; i++)
{
for (WALLS_OF_SECTOR(sectors[i], j))
{
dax += wall[j].x;
day += wall[j].y;
k++;
}
}
if (k > 0)
{
dax /= k;
day /= k;
}
*cx = dax;
*cy = day;
}
static int32_t insert_sprite_common(int32_t sectnum, int32_t dax, int32_t day)
{
int32_t i, j, k;
i = insertsprite(sectnum,0);
if (i < 0)
return -1;
sprite[i].x = dax, sprite[i].y = day;
sprite[i].cstat = DEFAULT_SPRITE_CSTAT;
sprite[i].shade = 0;
sprite[i].pal = 0;
sprite[i].xrepeat = 64, sprite[i].yrepeat = 64;
sprite[i].xoffset = 0, sprite[i].yoffset = 0;
sprite[i].ang = 1536;
sprite[i].xvel = 0; sprite[i].yvel = 0; sprite[i].zvel = 0;
sprite[i].owner = -1;
sprite[i].clipdist = 32;
sprite[i].lotag = 0;
sprite[i].hitag = 0;
sprite[i].extra = -1;
Bmemset(localartfreq, 0, sizeof(localartfreq));
for (k=0; k<MAXSPRITES; k++)
if (sprite[k].statnum < MAXSTATUS && k!=i)
localartfreq[sprite[k].picnum]++;
j = 0;
for (k=0; k<MAXTILES; k++)
if (localartfreq[k] > localartfreq[j])
j = k;
if (localartfreq[j] > 0)
sprite[i].picnum = j;
else
sprite[i].picnum = 0;
return i;
}
void correct_sprite_yoffset(int32_t i)
{
int32_t tileyofs = picanm[sprite[i].picnum].yofs;
int32_t tileysiz = tilesiz[sprite[i].picnum].y;
if (klabs(tileyofs) >= tileysiz)
{
tileyofs *= -1;
if (tileyofs == 128)
tileyofs = 127;
sprite[i].yoffset = tileyofs;
}
else
sprite[i].yoffset = 0;
}
// keepcol >= 0 && <256: keep that idx-color
// keepcol < 0: keep none
// keepcol >= 256: 0x00ffffff is mask for 3 colors
void fade_editor_screen(int32_t keepcol)
{
char blackcol=0, greycol=whitecol-25, *cp;
int32_t pix, i, threecols = (keepcol >= 256);
char cols[3] = {(char)(keepcol&0xff), (char)((keepcol>>8)&0xff), (char)((keepcol>>16)&0xff)};
videoBeginDrawing();
cp = (char *)frameplace;
for (i=0; i<bytesperline*(ydim-STATUS2DSIZ2); i++, cp++)
{
pix = (uint8_t)(*cp);
if (!threecols && pix == keepcol)
continue;
if (threecols)
if (pix==cols[0] || pix==cols[1] || pix==cols[2])
continue;
if (*cp==greycol)
*cp = blackcol;
else if (*cp != blackcol)
*cp = greycol;
}
videoEndDrawing();
videoShowFrame(1);
}
static void copy_some_wall_members(int16_t dst, int16_t src, int32_t reset_some)
{
static uwalltype nullwall;
walltype * const dstwal = &wall[dst];
const uwalltype *srcwal = src >= 0 ? (uwalltype *)&wall[src] : &nullwall;
memset(&nullwall, 0, sizeof(nullwall));
nullwall.yrepeat = 8;
nullwall.extra = -1;
if (reset_some)
{
dstwal->cstat = srcwal->cstat;
}
else
{
dstwal->cstat &= ~(4+8+256);
dstwal->cstat |= (srcwal->cstat&(4+8+256));
}
dstwal->shade = srcwal->shade;
dstwal->yrepeat = srcwal->yrepeat;
fixrepeats(dst); // xrepeat
dstwal->picnum = srcwal->picnum;
dstwal->overpicnum = srcwal->overpicnum;
dstwal->pal = srcwal->pal;
dstwal->xpanning = srcwal->xpanning;
dstwal->ypanning = srcwal->ypanning;
if (reset_some)
{
dstwal->nextwall = -1;
dstwal->nextsector = -1;
dstwal->lotag = 0; //srcwal->lotag;
dstwal->hitag = 0; //srcwal->hitag;
dstwal->extra = -1; //srcwal->extra;
#ifdef YAX_ENABLE
yax_setnextwall(dst, YAX_CEILING, -1);
yax_setnextwall(dst, YAX_FLOOR, -1);
#endif
}
}
static void init_new_wall1(int16_t *suckwall_ret, int32_t mousxplc, int32_t mousyplc)
{
int32_t i;
Bmemset(&wall[newnumwalls], 0, sizeof(walltype));
wall[newnumwalls].extra = -1;
wall[newnumwalls].x = mousxplc;
wall[newnumwalls].y = mousyplc;
wall[newnumwalls].nextsector = -1;
wall[newnumwalls].nextwall = -1;
for (i=0; i<numwalls; i++)
{
YAX_SKIPWALL(i);
if (wall[i].nextwall >= 0)
YAX_SKIPWALL(wall[i].nextwall);
if (wall[i].x == mousxplc && wall[i].y == mousyplc)
*suckwall_ret = i;
}
wall[newnumwalls].point2 = newnumwalls+1;
newnumwalls++;
}
// helpers for often needed ops:
static int32_t do_while_copyloop1(int16_t startwall, int16_t endwall,
int16_t *danumwalls, int16_t lastpoint2)
{
int32_t m = startwall;
do
{
if (*danumwalls >= MAXWALLS + M32_FIXME_WALLS)
return 1;
Bmemcpy(&wall[*danumwalls], &wall[m], sizeof(walltype));
wall[*danumwalls].point2 = *danumwalls+1;
(*danumwalls)++;
m = wall[m].point2;
}
while (m != endwall);
if (lastpoint2 >= 0)
wall[(*danumwalls)-1].point2 = lastpoint2;
return 0;
}
static void updatesprite1(int16_t i)
{
setsprite(i, (vec3_t *)&sprite[i]);
if (sprite[i].sectnum>=0)
{
int32_t cz, fz;
spriteoncfz(i, &cz, &fz);
inpclamp(&sprite[i].z, cz, fz);
}
}
#ifdef YAX_ENABLE
// highlighted OR grayed-out sectors:
static uint8_t hlorgraysectbitmap[MAXSECTORS>>3];
static int32_t ask_above_or_below(void);
#else
# define hlorgraysectbitmap hlsectorbitmap
#endif
// returns:
// 0: continue
// >0: newnumwalls
// <0: error
// ignore_ret and refsect_ret are for the 'auto-red-wall' feature
static int32_t trace_loop(int32_t j, uint8_t *visitedwall, int16_t *ignore_ret, int16_t *refsect_ret,
int16_t trace_loop_yaxcf)
{
int16_t refsect, ignore;
int32_t k, n, refwall;
#if 0
//def YAX_ENABLE
int32_t yaxp = (ignore_ret==NULL); // bleh
#else
UNREFERENCED_PARAMETER(trace_loop_yaxcf);
#endif
if (wall[j].nextwall>=0 || (visitedwall[j>>3]&(1<<(j&7))))
return 0;
n=2*MAXWALLS; // simple inf loop check
refwall = j;
k = numwalls;
ignore = 0;
if (ignore_ret)
{
refsect = -1;
updatesectorexclude(wall[j].x, wall[j].y, &refsect, hlorgraysectbitmap);
if (refsect<0)
return -1;
}
do
{
if (j!=refwall && visitedwall[j>>3]&(1<<(j&7)))
ignore = 1;
visitedwall[j>>3] |= (1<<(j&7));
if (ignore_ret)
{
if (inside(wall[j].x, wall[j].y, refsect) != 1)
ignore = 1;
}
if (!ignore)
{
if (k>=MAXWALLS)
{
message("Wall limits exceeded while tracing outer loop.");
return -2;
}
if (ignore_ret) // auto-red wall feature
onextwall[k] = onextwall[j];
Bmemcpy(&wall[k], &wall[j], sizeof(walltype));
wall[k].point2 = k+1;
// TODO: protect lotag/extra; see also hl-sector copying stuff
wall[k].nextsector = wall[k].nextwall = wall[k].extra = -1;
#ifdef YAX_ENABLE
if (trace_loop_yaxcf >= 0)
yax_setnextwall(k, trace_loop_yaxcf, j);
#endif
k++;
}
j = wall[j].point2;
n--;
while (wall[j].nextwall>=0 && n>0)
{
#if 0
//def YAX_ENABLE
if (yaxp)
{
int32_t ns = wall[j].nextsector;
if ((hlsectorbitmap[ns>>3]&(1<<(ns&7)))==0)
break;
}
#endif
j = wall[wall[j].nextwall].point2;
// if (j!=refwall && (visitedwall[j>>3]&(1<<(j&7))))
// ignore = 1;
// visitedwall[j>>3] |= (1<<(j&7));
n--;
}
}
while (j!=refwall && n>0);
if (j!=refwall)
{
message("internal error while tracing outer loop: didn't reach refwall");
return -3;
}
if (ignore_ret)
{
*ignore_ret = ignore;
if (refsect_ret)
*refsect_ret = refsect;
}
return k;
}
// Backup drawn walls for carrying out other operations in the middle.
// 0: back up, set newnumwalls to -1
// 1: restore drawn walls and free mem
// 2: only free memory needed for backing up walls but don't restore walls
// (use this if the map has been mangled too much for a safe restoration)
// Context that needs special treatment: suckwall, splitsect, splitstartwall
static int32_t backup_drawn_walls(int32_t restore)
{
static uwalltype *tmpwall;
// back up
if (restore==0)
{
// ovh.bak_wallsdrawn should be 0 here
if (newnumwalls != -1)
{
if (newnumwalls <= numwalls) // shouldn't happen
return 2;
Bfree(tmpwall);
tmpwall = (uwalltype *)Xmalloc((newnumwalls-numwalls) * sizeof(walltype));
ovh.bak_wallsdrawn = newnumwalls-numwalls;
Bmemcpy(tmpwall, &wall[numwalls], ovh.bak_wallsdrawn*sizeof(walltype));
newnumwalls = -1;
}
return 0;
}
// restore/clear
if (tmpwall)
{
if (restore==1) // really restore
{
const int32_t nnumwalls = numwalls + ovh.bak_wallsdrawn;
if (nnumwalls < MAXWALLS) // else, silently discard drawn walls
{
int32_t i;
Bmemcpy(&wall[numwalls], tmpwall, ovh.bak_wallsdrawn*sizeof(walltype));
newnumwalls = nnumwalls;
for (i=numwalls; i<newnumwalls; i++)
wall[i].point2 = i+1;
}
}
DO_FREE_AND_NULL(tmpwall);
ovh.bak_wallsdrawn = 0;
}
return 0;
}
// VARIOUS RESETTING FUNCTIONS
#define RESET_EDITOR_VARS() do { \
sectorhighlightstat = -1; \
newnumwalls = -1; \
joinsector[0] = -1; \
circlewall = -1; \
circlepoints = 7; \
} while (0)
void reset_highlightsector(void)
{
Bmemset(hlsectorbitmap, 0, sizeof(hlsectorbitmap));
update_highlightsector();
}
void reset_highlight(void) // walls and sprites
{
Bmemset(show2dwall, 0, sizeof(show2dwall));
Bmemset(show2dsprite, 0, sizeof(show2dsprite));
update_highlight();
}
#ifdef YAX_ENABLE
static int32_t collnumsects[2];
static int16_t collsectlist[2][MAXSECTORS];
static uint8_t collsectbitmap[2][MAXSECTORS>>3];
static void collect_sectors1(int16_t *sectlist, uint8_t *sectbitmap, int32_t *numsectptr,
int16_t startsec, int32_t alsoyaxnext, int32_t alsoonw)
{
int32_t j, startwall, endwall, sectcnt;
bfirst_search_init(sectlist, sectbitmap, numsectptr, MAXSECTORS, startsec);
for (sectcnt=0; sectcnt<*numsectptr; sectcnt++)
{
for (WALLS_OF_SECTOR(sectlist[sectcnt], j))
{
if (wall[j].nextsector >= 0)
bfirst_search_try(sectlist, sectbitmap, numsectptr, wall[j].nextsector);
else if (alsoonw && onextwall[j]>=0)
bfirst_search_try(sectlist, sectbitmap, numsectptr, sectorofwall(onextwall[j]));
}
if (alsoyaxnext)
{
int16_t bn[2], cf;
yax_getbunches(sectlist[sectcnt], &bn[0], &bn[1]);
for (cf=0; cf<2; cf++)
if (bn[cf]>=0)
{
for (SECTORS_OF_BUNCH(bn[cf], !cf, j))
bfirst_search_try(sectlist, sectbitmap, numsectptr, j);
}
}
}
}
static int32_t sectors_components(int16_t hlsectcnt, const int16_t *hlsectors, int32_t alsoyaxnext, int32_t alsoonw);
static int32_t highlighted_sectors_components(int32_t alsoyaxnext, int32_t alsoonw)
{
return sectors_components(highlightsectorcnt, highlightsector, alsoyaxnext, alsoonw);
}
// whether all highlighted sectors are in one (returns 1), two (2)
// or more (>2) connected components wrt the nextsector relation
// -1 means error
// alsoyaxnext: also consider "yax-nextsector" relation
// alsoonw: also consider "old-nextwall" relation (must be valid)
static int32_t sectors_components(int16_t hlsectcnt, const int16_t *hlsector, int32_t alsoyaxnext, int32_t alsoonw)
{
int32_t j, k, tmp;
if (hlsectcnt<1)
return 0;
collect_sectors1(collsectlist[0], collsectbitmap[0], &collnumsects[0],
hlsector[0], alsoyaxnext, alsoonw);
for (k=1; k<hlsectcnt; k++)
{
j = hlsector[k];
if ((collsectbitmap[0][j>>3]&(1<<(j&7)))==0)
{
// sector j not collected --> more than 1 conn. comp.
collect_sectors1(collsectlist[1], collsectbitmap[1], &collnumsects[1],
j, alsoyaxnext, alsoonw);
break;
}
}
if (k == hlsectcnt)
return 1;
for (k=0; k<hlsectcnt; k++)
{
j = hlsector[k];
tmp = (((collsectbitmap[0][j>>3]&(1<<(j&7)))!=0) + (((collsectbitmap[1][j>>3]&(1<<(j&7)))!=0)<<1));
if (tmp==3)
return -1; // components only weakly connected
if (tmp==0)
return 3; // sector j not reached
}
return 2;
}
static int cmpgeomwal1(const void *w1, const void *w2)
{
uwalltype const * const wal1 = (uwalltype *)&wall[B_UNBUF16(w1)];
uwalltype const * const wal2 = (uwalltype *)&wall[B_UNBUF16(w2)];
if (wal1->x == wal2->x)
return wal1->y - wal2->y;
return wal1->x - wal2->x;
}
static void sort_walls_geometrically(int16_t *wallist, int32_t nwalls)
{
qsort(wallist, nwalls, sizeof(int16_t), &cmpgeomwal1);
}
#endif
void SetFirstWall(int32_t sectnum, int32_t wallnum, int32_t alsoynw)
{
#ifdef YAX_ENABLE
int32_t i, j, k=0;
#endif
const sectortype *sec = &sector[sectnum];
if (sec->wallptr == wallnum)
{
message("Wall %d already first wall of sector %d", wallnum, sectnum);
return;
}
#ifdef YAX_ENABLE
if (alsoynw)
{
// Also consider upper/lower TROR neighbor walls.
int32_t startwall, endwall;
int16_t cf;
for (i=0; i<numwalls; i++)
wall[i].cstat &= ~(1<<14);
for (cf=0; cf<2; cf++)
{
int16_t bunchnum;
int32_t tempsect=sectnum, tempwall=wallnum;
while ((bunchnum = yax_getbunch(tempsect, cf)) >= 0 &&
(tempsect=yax_is121(bunchnum, cf)) >= 0)
{
tempwall = yax_getnextwall(tempwall, cf);
if (tempwall < 0)
break; // corrupt!
wall[tempwall].cstat |= (1<<14);
}
}
for (i=0; i<numsectors; i++)
for (WALLS_OF_SECTOR(i, j))
{
if (wall[j].cstat & (1<<14))
{
setfirstwall(i, j);
k++;
break;
}
}
}
else
{
// Only consider aimed at wall <wallnum>.
int16_t cb = yax_getbunch(sectnum, YAX_CEILING);
int16_t fb = yax_getbunch(sectnum, YAX_FLOOR);
if ((cb>=0 && (sec->ceilingstat&2)) || (fb >= 0 && (sec->floorstat&2)))
{
message("Extended ceilings/floors must not be sloped to set first wall");
return;
}
}
if (k > 0)
message("Set first walls (sector[].wallptr) for %d sectors", k+1);
if (k == 0)
#endif
message("This wall now sector %d's first wall (sector[].wallptr)", sectnum);
setfirstwall(sectnum, wallnum);
mkonwinvalid_keeptempsect();
asksave = 1;
}
void handlesecthighlight1(int32_t i, int32_t sub, int32_t nograycheck)
{
int32_t j;
if (sub)
{
hlsectorbitmap[i>>3] &= ~(1<<(i&7));
for (j=sector[i].wallptr; j<sector[i].wallptr+sector[i].wallnum; j++)
{
if (wall[j].nextwall >= 0)
checksectorpointer(wall[j].nextwall,wall[j].nextsector);
checksectorpointer(j, i);
}
}
else
{
if (nograycheck || (graysectbitmap[i>>3]&(1<<(i&7)))==0)
hlsectorbitmap[i>>3] |= (1<<(i&7));
}
}
#ifdef YAX_ENABLE
// 1: good, 0: bad
static int32_t hl_all_bunch_sectors_p()
{
uint8_t *const havebunch = visited;
int16_t cf, cb, fb;
int32_t i, j;
if (numyaxbunches > 0)
{
Bmemset(havebunch, 0, (numyaxbunches+7)>>3);
for (i=0; i<highlightsectorcnt; i++)
{
yax_getbunches(highlightsector[i], &cb, &fb);
if (cb>=0)
havebunch[cb>>3] |= (1<<(cb&7));
if (fb>=0)
havebunch[fb>>3] |= (1<<(fb&7));
}
for (i=0; i<numyaxbunches; i++)
{
if ((havebunch[i>>3] & (1<<(i&7)))==0)
continue;
for (cf=0; cf<2; cf++)
for (SECTORS_OF_BUNCH(i,cf, j))
if ((hlsectorbitmap[j>>3]&(1<<(j&7)))==0)
return 0;
}
}
return 1;
}
#endif
static int32_t find_nextwall(int32_t sectnum, int32_t sectnum2)
{
int32_t j, startwall, endwall;
if (sectnum<0 || sectnum2<0)
return -1;
for (WALLS_OF_SECTOR(sectnum, j))
if (wall[j].nextsector == sectnum2)
return j;
return -1;
}
static int32_t bakframe_fillandfade(char **origframeptr, int32_t sectnum, const char *querystr)
{
if (!*origframeptr)
{
*origframeptr = (char *)Xmalloc(xdim*ydim);
videoBeginDrawing();
Bmemcpy(*origframeptr, (char *)frameplace, xdim*ydim);
videoEndDrawing();
}
else
{
videoBeginDrawing();
Bmemcpy((char *)frameplace, *origframeptr, xdim*ydim);
videoEndDrawing();
}
fillsector_notrans(sectnum, editorcolors[9]);
fade_editor_screen(editorcolors[9]);
return ask_if_sure(querystr, 0);
}
#ifdef YAX_ENABLE
static void M32_MarkPointInsertion(int32_t thewall)
{
int32_t i, tmpcf;
int32_t nextw = wall[thewall].nextwall;
// round 1
for (YAX_ITER_WALLS(thewall, i, tmpcf))
wall[i].cstat |= (1<<14);
if (nextw >= 0)
for (YAX_ITER_WALLS(nextw, i, tmpcf))
wall[i].cstat |= (1<<14);
// round 2 (enough?)
for (YAX_ITER_WALLS(thewall, i, tmpcf))
if (wall[i].nextwall >= 0 && (wall[wall[i].nextwall].cstat&(1<<14))==0)
wall[wall[i].nextwall].cstat |= (1<<14);
if (nextw >= 0)
for (YAX_ITER_WALLS(nextw, i, tmpcf))
if (wall[i].nextwall >= 0 && (wall[wall[i].nextwall].cstat&(1<<14))==0)
wall[wall[i].nextwall].cstat |= (1<<14);
}
#endif
// High-level insert point, handles TROR constrained walls too
// onewnumwalls: old numwalls + drawn walls.
// <mapwallnum>: see insertpoint()
// Returns:
// 0 if wall limit would be reached.
// 1 if inserted point on a plain white or 2 points on a plain red wall.
// N >= 2 if inserted N points on TROR-constrained wall.
// N|(EXPECTED<<16) if inserted N points but EXPECTED walls were expected.
static int32_t M32_InsertPoint(int32_t thewall, int32_t dax, int32_t day, int32_t onewnumwalls, int32_t *mapwallnum)
{
#ifdef YAX_ENABLE
int32_t nextw = wall[thewall].nextwall;
int32_t i, j, k, m;
if (yax_islockedwall(thewall) || (nextw>=0 && yax_islockedwall(nextw)))
{
// yax'ed wall -- first find out which walls are affected
for (i=0; i<numwalls; i++)
wall[i].cstat &= ~(1<<14);
M32_MarkPointInsertion(thewall);
for (i=0; i < numwalls; i++)
if (wall[i].cstat&(1<<14))
M32_MarkPointInsertion(i);
j = 0;
for (i=0; i<numwalls; i++)
j += !!(wall[i].cstat&(1<<14));
if (max(numwalls,onewnumwalls)+j > MAXWALLS)
{
return 0; // no points inserted, would exceed limits
}
// the actual insertion!
m = 0;
for (i=0; i<numwalls /* rises with ins. */; i++)
{
if (wall[i].cstat&(1<<14))
if (wall[i].nextwall<0 || i<wall[i].nextwall) // || !(NEXTWALL(i).cstat&(1<<14)) ??
{
m += insertpoint(i, dax,day, mapwallnum);
}
}
for (i=0; i<numwalls; i++)
{
if (wall[i].cstat&(1<<14))
{
wall[i].cstat &= ~(1<<14);
k = yax_getnextwall(i+1, YAX_CEILING);
if (k >= 0)
yax_setnextwall(i+1, YAX_CEILING, k+1);
k = yax_getnextwall(i+1, YAX_FLOOR);
if (k >= 0)
yax_setnextwall(i+1, YAX_FLOOR, k+1);
}
}
if (m==j)
return m;
else
return m|(j<<16);
}
else
#endif
{
insertpoint(thewall, dax,day, mapwallnum);
return 1;
}
}
// based on lineintersect in engine.c, but lines are considered as infinitely
// extending
void 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)
{
//p1 to p2 is a line segment
int64_t x21, y21, x34, y34, x31, y31, bot, topt, topu, t;
x21 = x2-x1; x34 = x3-x4;
y21 = y2-y1; y34 = y3-y4;
bot = x21*y34 - y21*x34;
if (bot >= 0)
{
if (bot == 0) { *sign12 = *sign34 = 0; return; };
x31 = x3-x1; y31 = y3-y1;
topt = x31*y34 - y31*x34;
topu = x21*y31 - y21*x31;
}
else
{
x31 = x3-x1; y31 = y3-y1;
topt = x31*y34 - y31*x34;
topu = x21*y31 - y21*x31;
}
t = (topt*(1<<24))/bot;
*intx = x1 + ((x21*t)>>24);
*inty = y1 + ((y21*t)>>24);
*sign12 = topt < 0 ? -1 : 1;
*sign34 = topu < 0 ? -1 : 1;
return;
}
static int32_t lineintersect2v(const vec2_t *p1, const vec2_t *p2, // line segment 1
const vec2_t *q1, const vec2_t *q2, // line segment 2
vec2_t *pint)
{
int32_t intz;
return lintersect(p1->x, p1->y, 0, p2->x, p2->y, 0,
q1->x, q1->y, q2->x, q2->y,
&pint->x, &pint->y, &intz);
}
static int32_t vec2eq(const vec2_t *v1, const vec2_t *v2)
{
return (v1->x==v2->x && v1->y==v2->y);
}
#ifdef YAX_ENABLE
// After auto-creating inner sector <ns> in existing sector <os>, we need to
// see if some sprites contained in <os> need to change their sector.
static void CorrectSpriteSectnums(int32_t os, int32_t ns)
{
int32_t i, ni;
for (SPRITES_OF_SECT_SAFE(os, i, ni))
{
if (inside(sprite[i].x, sprite[i].y, ns)==1)
changespritesect(i, ns);
}
}
#endif
// precondition: [numwalls, newnumwalls-1] form a new loop (may be of wrong orientation)
// ret_ofirstwallofs: if != NULL, *ret_ofirstwallofs will contain the offset of the old
// first wall from the new first wall of the sector k, and the automatic
// restoring of the old first wll will not be carried out
// returns:
// -1, -2: errors
// 0, 1: OK, 1 means it was an extended sector and an inner loop has been added automatically
static int32_t AddLoopToSector(int32_t k, int32_t *ret_ofirstwallofs)
{
int32_t extendedSector=0, firstwall, i, j;
#ifdef YAX_ENABLE
int16_t cbunch, fbunch;
int32_t newnumwalls2;
yax_getbunches(k, &cbunch, &fbunch);
extendedSector = (cbunch>=0 || fbunch>=0);
#endif
j = newnumwalls-numwalls;
#ifdef YAX_ENABLE
newnumwalls2 = newnumwalls + j;
if (extendedSector)
{
if ((cbunch>=0 && (sector[k].ceilingstat&2))
|| (fbunch>=0 && (sector[k].floorstat&2)))
{
printmessage16("Sloped extended sectors cannot be subdivided.");
newnumwalls--;
return -1;
}
if (newnumwalls + j > MAXWALLS || numsectors+1 > MAXSECTORS)
{
message("Automatically adding inner sector to new extended sector would exceed limits!");
newnumwalls--;
return -2;
}
}
#endif
if (clockdir(numwalls) == CLOCKDIR_CW)
flipwalls(numwalls,newnumwalls);
sector[k].wallnum += j;
for (i=k+1; i<numsectors; i++)
sector[i].wallptr += j;
firstwall = sector[k].wallptr;
for (i=0; i<numwalls; i++)
{
if (wall[i].nextwall >= firstwall)
wall[i].nextwall += j;
if (wall[i].point2 >= firstwall)
wall[i].point2 += j;
}
#ifdef YAX_ENABLE
yax_tweakwalls(firstwall, j);
#endif
Bmemmove(&wall[firstwall+j], &wall[firstwall], (newnumwalls-firstwall)*sizeof(walltype));
// add new loop to beginning of sector
Bmemmove(&wall[firstwall], &wall[newnumwalls], j*sizeof(walltype));
for (i=firstwall; i<firstwall+j; i++)
{
wall[i].point2 += (firstwall-numwalls);
copy_some_wall_members(i, firstwall+j, 1);
wall[i].cstat &= ~(1+16+32+64);
}
numwalls = newnumwalls;
newnumwalls = -1;
#ifdef YAX_ENABLE
if (extendedSector)
{
newnumwalls = whitelinescan(k, firstwall);
if (newnumwalls != newnumwalls2)
message("AddLoopToSector: newnumwalls != newnumwalls2!!! WTF?");
for (i=numwalls; i<newnumwalls; i++)
{
NEXTWALL(i).nextwall = i;
NEXTWALL(i).nextsector = numsectors;
}
yax_setbunches(numsectors, cbunch, fbunch);
numwalls = newnumwalls;
newnumwalls = -1;
numsectors++;
CorrectSpriteSectnums(k, numsectors-1);
}
#endif
if (ret_ofirstwallofs)
*ret_ofirstwallofs = j;
else
setfirstwall(k, firstwall+j); // restore old first wall
return extendedSector;
}
int32_t select_sprite_tag(int32_t spritenum)
{
int32_t lt = taglab_linktags(1, spritenum);
spritetype *spr = &sprite[spritenum];
if (lt==0)
return INT32_MIN;
if (lt&1)
return spr->lotag;
if (lt&2)
return spr->hitag;
if (lt&4)
return spr->extra;
if (lt&8)
return spr->xvel;
if (lt&16)
return spr->yvel;
if (lt&32)
return spr->zvel;
if (lt&64)
return spr->extra;
return INT32_MIN;
}
static void drawlinebetween(const vec3_t *v1, const vec3_t *v2, int32_t col, uint32_t pat)
{
// based on m32exec.c/drawline*
const int32_t xofs=halfxdim16, yofs=midydim16;
const uint32_t opat=drawlinepat;
int32_t x1, x2, y1, y2;
editorGet2dScreenCoordinates(&x1,&y1, v1->x-pos.x,v1->y-pos.y, zoom);
editorGet2dScreenCoordinates(&x2,&y2, v2->x-pos.x,v2->y-pos.y, zoom);
if (m32_sideview)
{
y1 += getscreenvdisp(v1->z-pos.z,zoom);
y2 += getscreenvdisp(v2->z-pos.z,zoom);
}
drawlinepat = pat;
editorDraw2dLine(xofs+x1,yofs+y1, xofs+x2,yofs+y2, col);
drawlinepat = opat;
}
// world -> screen coords for overhead mode
void ovhscrcoords(int32_t x, int32_t y, int32_t *scrx, int32_t *scry)
{
*scrx = halfxdim16 + mulscale14(x-pos.x, zoom);
*scry = midydim16 + mulscale14(y-pos.y, zoom);
}
static void draw_cross(int32_t centerx, int32_t centery, int32_t radius, int32_t col)
{
int32_t dax, day;
ovhscrcoords(centerx, centery, &dax, &day);
drawline16base(dax, day, -radius,-radius, +radius,+radius, col);
drawline16base(dax, day, -radius,+radius, +radius,-radius, col);
}
static void draw_square(int32_t dax, int32_t day, int32_t ps, int32_t col)
{
ovhscrcoords(dax, day, &dax, &day);
drawline16base(dax, day, -ps,-ps, +ps,-ps, col);
drawline16base(dax, day, +ps,-ps, +ps,+ps, col);
drawline16base(dax, day, +ps,+ps, -ps,+ps, col);
drawline16base(dax, day, -ps,+ps, -ps,-ps, col);
}
//// Interactive Scaling
static struct {
int8_t active, rotatep;
vec2_t piv; // pivot point
int32_t dragx, dragy; // dragged point
int32_t xsc, ysc, ang;
} isc;
static void isc_transform(int32_t *x, int32_t *y)
{
if (!isc.rotatep)
{
*x = isc.piv.x + mulscale16(*x-isc.piv.x, isc.xsc);
*y = isc.piv.y + mulscale16(*y-isc.piv.y, isc.ysc);
}
else
{
vec2_t v = { *x, *y };
rotatepoint(isc.piv, v, isc.ang, &v);
*x = v.x;
*y = v.y;
}
}
static void drawspritelabel(int i)
{
// XXX: oob 'i' may happen, such as passing pointhighlight-16384 when
// pointhighlight == -1.
if ((unsigned)i >= MAXSPRITES)
return;
const char *dabuffer = CallExtGetSpriteCaption(i);
if (!dabuffer[0])
return;
// KEEPINSYNC drawscreen_drawsprite()
uspritetype const * s = (uspritetype *)&sprite[i];
uint8_t const spritecol = spritecol2d[s->picnum][(s->cstat&1)];
int col = spritecol ? editorcolors[spritecol] : editorGet2dSpriteColor(i);
int const blocking = s->cstat & 1;
int bordercol = blocking ? editorcolors[5] : col;
// group selection
if (show2dsprite[i>>3]&pow2char[i&7])
{
bordercol = editorcolors[14];
col = bordercol - (M32_THROB>>1);
}
else if (i == pointhighlight - 16384)
{
if (spritecol >= 8 && spritecol <= 15)
col -= M32_THROB>>1;
else col += M32_THROB>>2;
if (bordercol > col && !blocking)
bordercol = col;
}
else if (s->sectnum < 0)
col = bordercol = editorcolors[4]; // red
drawsmallabel(dabuffer, editorcolors[0], col, bordercol, s->x, s->y, s->z);
}
#define EDITING_MAP_P() (newnumwalls>=0 || joinsector[0]>=0 || circlewall>=0 || (bstatus&1) || isc.active)
#define HLMEMBERX(Hl, Member) (*(((Hl)&16384) ? &sprite[(Hl)&16383].Member : &wall[Hl].Member))
#define HLMEMBER(Hlidx, Member) HLMEMBERX(highlight[Hlidx], Member)
void overheadeditor(void)
{
char buffer[80];
const char *dabuffer;
int32_t i, j, k, m=0, mousxplc, mousyplc, firstx=0, firsty=0, oposz, col;
int32_t numwalls_bak;
int32_t startwall=0, endwall, dax, day, x1, y1, x2, y2, x3, y3; //, x4, y4;
int32_t highlightx1, highlighty1, highlightx2, highlighty2;
int16_t bad, joinsector[2];
int32_t bstatus, mousewaitmask=0;
int16_t circlepoints;
int32_t sectorhighlightx=0, sectorhighlighty=0;
int16_t cursectorhighlight, sectorhighlightstat;
int32_t prefixarg = 0, tsign;
int32_t resetsynctics = 0, lasttick=timerGetTicks(), waitdelay=totalclock, lastdraw=timerGetTicks();
int32_t olen[2] = {0, 0}, dragwall[2] = {-1, -1};
int16_t linehighlight2 = -1;
ovh.suckwall = -1;
ovh.split = 0;
ovh.splitsect = -1;
ovh.splitstartwall = -1;
videoSet2dMode(xdim2d,ydim2d);
xdim2d = xdim;
ydim2d = ydim;
osearchx = searchx;
osearchy = searchy;
searchx = clamp(scale(searchx,xdim2d,xdimgame), 8, xdim2d-8-1);
searchy = clamp(scale(searchy,ydim2d-STATUS2DSIZ2,ydimgame), 8, ydim2d-STATUS2DSIZ-8-1);
oposz = pos.z;
yax_updategrays(pos.z);
videoBeginDrawing(); //{{{
CLEARLINES2D(0, ydim, 0);
videoEndDrawing(); //}}}
ydim16 = ydim-STATUS2DSIZ2;
cursectorhighlight = -1;
lastpm16time = -1;
update_highlightsector();
ovh_whiteoutgrab(0);
highlightcnt = -1;
Bmemset(show2dwall, 0, sizeof(show2dwall)); //Clear all highlights
Bmemset(show2dsprite, 0, sizeof(show2dsprite));
RESET_EDITOR_VARS();
bstatus = 0;
while ((keystatus[buildkeys[BK_MODE2D_3D]]>>1) == 0)
{
int32_t mousx, mousy;
if (zoom < ztarget)
{
if ((ztarget - zoom) >> 3)
zoom += synctics * ((ztarget - zoom) >> 3);
else zoom++;
zoom = min(zoom, ztarget);
}
else if (zoom > ztarget)
{
if ((zoom - ztarget) >> 3)
zoom -= synctics * ((zoom - ztarget) >> 3);
else zoom--;
zoom = max(zoom, ztarget);
}
if (!((vel|angvel|svel) || m32_is2d3dmode() || ztarget != zoom//DOWN_BK(MOVEFORWARD) || DOWN_BK(MOVEBACKWARD) || DOWN_BK(TURNLEFT) || DOWN_BK(TURNRIGHT)
|| DOWN_BK(MOVEUP) || DOWN_BK(MOVEDOWN) || keystatus[sc_Q] || keystatus[sc_W]
|| keystatus[sc_kpad_8] || keystatus[sc_kpad_4] || keystatus[sc_kpad_6] || keystatus[sc_kpad_2] // keypad keys
|| bstatus || OSD_IsMoving()))
{
if (totalclock > waitdelay)
{
uint32_t ms = 50;// (highlightsectorcnt>0) ? 75 : 200;
// wait for event, timeout after 200 ms - (last loop time)
idle_waitevent_timeout(ms - min(timerGetTicks()-lasttick, ms));
// have synctics reset to 0 after we've slept to avoid zooming out to the max instantly
resetsynctics = 1;
}
}
else waitdelay = totalclock + 6; // should be 50 ms
lasttick = timerGetTicks();
if (handleevents())
{
if (quitevent)
{
keystatus[sc_Escape] = 1;
quitevent = 0;
}
}
if (resetsynctics)
{
resetsynctics = 0;
lockclock = totalclock;
synctics = 0;
}
OSD_DispatchQueued();
if (totalclock < 120*3)
printmessage16("Uses BUILD technology by Ken Silverman.");
else if (totalclock < 120*6)
{
printmessage16("Press F1 for help. This is a test release; always keep backups of your maps.");
// printext16(8L,ydim-STATUS2DSIZ+32L,editorcolors[9],-1,kensig,0);
}
if (!m32_is2d3dmode())
{
oldmousebstatus = bstatus;
mouseGetValues(&mousx, &mousy, &bstatus);
{
int32_t bs = bstatus;
bstatus &= ~mousewaitmask;
mousewaitmask &= bs;
}
mousx = (mousx<<16)+mousexsurp;
mousy = (mousy<<16)+mouseysurp;
{
ldiv_t ld;
ld = ldiv(mousx, 1<<16); mousx = ld.quot; mousexsurp = ld.rem;
ld = ldiv(mousy, 1<<16); mousy = ld.quot; mouseysurp = ld.rem;
}
searchx += mousx;
searchy += mousy;
inpclamp(&searchx, 8, xdim-8-1);
inpclamp(&searchy, 8, ydim-8-1);
mainloop_move();
getpoint(searchx, searchy, &mousxplc, &mousyplc);
linehighlight = getlinehighlight(mousxplc, mousyplc, linehighlight, 0);
linehighlight2 = getlinehighlight(mousxplc, mousyplc, linehighlight, 1);
}
if (newnumwalls >= numwalls)
{
// if we're in the process of drawing a wall, set the end point's coordinates
dax = mousxplc;
day = mousyplc;
adjustmark(&dax,&day,numwalls+!ovh.split);
wall[newnumwalls].x = dax;
wall[newnumwalls].y = day;
}
ydim16 = ydim;// - STATUS2DSIZ2;
midydim16 = ydim>>1;
numwalls_bak = numwalls;
numwalls = newnumwalls;
if (numwalls < 0)
numwalls = numwalls_bak;
if ((timerGetTicks() - lastdraw) >= 5 || (vel|angvel|svel) || DOWN_BK(MOVEUP) || DOWN_BK(MOVEDOWN)
|| mousx || mousy || bstatus || keystatus[sc_Q] || keystatus[sc_W]
|| newnumwalls>=0 || OSD_IsMoving())
{
lastdraw = timerGetTicks();
clear2dscreen();
editorSetup2dSideView();
VM_OnEvent(EVENT_PREDRAW2DSCREEN, -1);
if (graphicsmode && (!m32_sideview || m32_sideelev == 512))
{
Bmemset(show2dsector, 0, sizeof(show2dsector));
for (i=0; i<numsectors; i++)
{
YAX_SKIPSECTOR(i);
show2dsector[i>>3] |= (1<<(i&7));
}
videoSetViewableArea(0, 0, xdim-1, ydim16-1);
if (graphicsmode == 2)
totalclocklock = totalclock;
renderDrawMapView(pos.x, pos.y, zoom, m32_sideview ? (3584 - m32_sideang) & 2047: 1536);
}
editorDraw2dGrid(pos.x,pos.y,pos.z,cursectnum,ang,zoom,grid);
CallExtPreCheckKeys();
editorDraw2dScreen(&pos,cursectnum,ang,zoom,grid);
// Draw brown arrow (start)
editorGet2dScreenCoordinates(&x2, &y2, startpos.x-pos.x,startpos.y-pos.y, zoom);
if (m32_sideview)
y2 += getscreenvdisp(startpos.z-pos.z, zoom);
int32_t cx = halfxdim16+x2;
int32_t cy = midydim16+y2;
videoBeginDrawing(); //{{{ LOCK_FRAME_1
if ((cx >= 2 && cx <= xdim-3) && (cy >= 2 && cy <= ydim16-3))
{
int16_t angofs = m32_sideview ? m32_sideang : 0;
x1 = mulscale11(sintable[(startang+angofs+2560)&2047],zoom) / 768;
y1 = mulscale11(sintable[(startang+angofs+2048)&2047],zoom) / 768;
i = scalescreeny(x1);
j = scalescreeny(y1);
drawline16base(cx,cy, x1,j, -x1,-j, editorcolors[6]);
drawline16base(cx,cy, x1,j, +y1,-i, editorcolors[6]);
drawline16base(cx,cy, x1,j, -y1,+i, editorcolors[6]);
}
if (keystatus[sc_LeftShift] && (pointhighlight&16384) && highlightcnt<=0) // LShift
{
// draw lines to linking sprites
const int32_t refspritenum = pointhighlight&16383;
const int32_t reftag = select_sprite_tag(refspritenum);
if (reftag != INT32_MIN)
{
for (i=0; i<numsectors; i++)
for (SPRITES_OF_SECT(i, j))
if (reftag==select_sprite_tag(j))
drawlinebetween((vec3_t *)&sprite[refspritenum], (vec3_t *)&sprite[j],
editorcolors[12], 0x33333333);
}
}
if (showtags)
{
if (zoom >= 768)
{
for (i=0; i<numsectors; i++)
{
int16_t secshort = i;
YAX_SKIPSECTOR(i);
dabuffer = CallExtGetSectorCaption(i);
if (dabuffer[0] == 0)
continue;
get_sectors_center(&secshort, 1, &dax, &day);
drawsmallabel(dabuffer, editorcolors[0], editorcolors[7], editorcolors[7] - 3, dax, day, getflorzofslope(i,dax,day));
}
}
x3 = pos.x + divscale14(-halfxdim16,zoom);
y3 = pos.y + divscale14(-(midydim16-4),zoom);
// x4 = pos.x + divscale14(halfxdim16,zoom);
// y4 = pos.y + divscale14(ydim16-(midydim16-4),zoom);
if (newnumwalls >= 0)
{
for (i=newnumwalls; i>=numwalls_bak; i--)
wall[i].cstat |= (1<<14);
}
i = numwalls-1;
j = numsectors-1; // might be -1 if empty map!
if (newnumwalls >= 0)
i = newnumwalls-1;
for (; i>=0; i--)
{
walltype const * const wal = &wall[i];
if (j>=0 && sector[j].wallptr > i)
j--;
if (zoom < 768 && !(wal->cstat & (1<<14)))
continue;
YAX_SKIPWALL(i);
//Get average point of wall
// if ((dax > x3) && (dax < x4) && (day > y3) && (day < y4))
{
dabuffer = CallExtGetWallCaption(i);
if (dabuffer[0] == 0)
continue;
dax = (wal->x+wall[wal->point2].x)>>1;
day = (wal->y+wall[wal->point2].y)>>1;
drawsmallabel(dabuffer, editorcolors[0], editorcolors[31], editorcolors[31] - 3, dax, day, (i >= numwalls || j<0) ? 0 : getflorzofslope(j, dax,day));
}
}
if (zoom >= 768)
{
int32_t alwaysshowgray = get_alwaysshowgray();
for (i=0, k=0; (m32_sideview && k<m32_swcnt) || (!m32_sideview && i<MAXSPRITES); i++, k++)
{
if (m32_sideview)
{
i = m32_wallsprite[k];
if (i<MAXWALLS)
continue;
i = i-MAXWALLS;
}
else
if (sprite[i].statnum == MAXSTATUS)
continue;
if ((!m32_sideview || !alwaysshowgray) && sprite[i].sectnum >= 0)
YAX_SKIPSECTOR(sprite[i].sectnum);
drawspritelabel(i);
}
if (pointhighlight & 16384)
drawspritelabel(pointhighlight - 16384);
}
}
// stick this event right between begin- end enddrawing()...
// also after the above label stuff so users can redefine them
VM_OnEvent(EVENT_DRAW2DSCREEN, -1);
printcoords16(pos.x,pos.y,ang);
numwalls = numwalls_bak;
if (highlightsectorcnt >= 0)
{
for (i=0; i<numsectors; i++)
if (hlsectorbitmap[i>>3]&(1<<(i&7)))
fillsector(i, -1);
}
if (keystatus[sc_LeftShift]) // LShift
{
if (!m32_is2d3dmode() && (m32_sideview || highlightcnt <= 0))
{
drawlinepat = 0x00ff00ff;
editorDraw2dLine(searchx,0, searchx,ydim2d-1, editorcolors[15]);
editorDraw2dLine(0,searchy, xdim2d-1,searchy, editorcolors[15]);
drawlinepat = 0xffffffff;
_printmessage16("(%d,%d)",mousxplc,mousyplc);
}
else
{
// do interactive scaling
if (!isc.active)
{
if (pointhighlight >= 0 && (bstatus&3))
{
// initialize by finding pivot point
int32_t minx=INT32_MAX, miny=INT32_MAX;
int32_t maxx=INT32_MIN, maxy=INT32_MIN;
isc.rotatep = ((bstatus&3)==2);
bstatus &= ~3;
for (i=0; i<highlightcnt; i++)
{
minx = min(minx, HLMEMBER(i, x));
miny = min(miny, HLMEMBER(i, y));
maxx = max(maxx, HLMEMBER(i, x));
maxy = max(maxy, HLMEMBER(i, y));
}
isc.piv.x = (minx+maxx)/2;
isc.piv.y = (miny+maxy)/2;
isc.dragx = HLMEMBERX(pointhighlight, x);
isc.dragy = HLMEMBERX(pointhighlight, y);
isc.xsc = isc.ysc = 1<<16;
isc.ang = 0;
isc.active = 1;
}
}
else
{
if (bstatus&3)
{
// drag/rotate the reference point
const int32_t pivx=isc.piv.x, pivy=isc.piv.y;
const int32_t dragx=isc.dragx, dragy=isc.dragy;
int32_t mxplc=mousxplc, myplc=mousyplc, xsc=1<<16, ysc=1<<16;
const int32_t dx=dragx-pivx, dy=dragy-pivy;
int32_t mdx, mdy;
bstatus &= ~3;
draw_cross(pivx, pivy, 3, editorcolors[14]);
adjustmark(&mxplc, &myplc, numwalls);
mdx = mxplc-pivx;
mdy = myplc-pivy;
if (!isc.rotatep)
{
if (mdx != 0 && dx != 0 && klabs(dx) >= 8)
xsc = min(klabs(divscale16(mdx, dx)), 1<<18);
if (mdy != 0 && dy != 0 && klabs(dy) >= 8)
ysc = min(klabs(divscale16(mdy, dy)), 1<<18);
if (eitherCTRL)
xsc = ysc = max(xsc, ysc);
isc.xsc = xsc;
isc.ysc = ysc;
printmessage16("scale x=%.3f y=%.3f", (double)xsc/65536, (double)ysc/65536);
}
else
{
isc.ang = getangle(mdx, mdy) - getangle(dx, dy);
printmessage16("rotate ang %d", isc.ang);
}
for (i=0; i<highlightcnt; i++)
{
int32_t x=HLMEMBER(i, x), y=HLMEMBER(i, y);
isc_transform(&x, &y);
draw_square(x, y, 2, editorcolors[15]);
if ((highlight[i]&16384)==0)
{
walltype const * const wal = &wall[highlight[i]];
const int32_t p2=wal->point2, hlp=(show2dwall[p2>>3]&(1<<(p2&7)));
vec3_t v1 = { x, y, 0 }, v2 = { wall[p2].x, wall[p2].y, 0 };
isc_transform(&v2.x, &v2.y);
if (!hlp)
{
v2.x = wall[p2].x;
v2.y = wall[p2].y;
}
drawlinebetween(&v1, &v2, !hlp ? 8 :
editorcolors[wal->nextwall >= 0 ? 12 : 7],
0x11111111);
}
}
}
else
{
// finish interactive scaling
isc.active = 0;
if ((!isc.rotatep && (isc.xsc!=1<<16 || isc.ysc!=1<<16)) ||
(isc.rotatep && (isc.ang!=0)))
{
for (i=0; i<highlightcnt; i++)
{
int32_t *x=&HLMEMBER(i, x), *y=&HLMEMBER(i, y);
isc_transform(x, y);
if (isc.rotatep && (highlight[i]&16384))
{
spritetype *spr = &sprite[highlight[i]&16383];
spr->ang = (spr->ang + isc.ang)&2047;
}
}
if (!isc.rotatep)
message("Highlights scaled by x=%.3f y=%.3f",
(double)isc.xsc/65536, (double)isc.ysc/65536);
else
message("Highlights rotated by %d BUILD degrees", isc.ang);
asksave = 1;
}
else
printmessage16(" ");
}
}
}
}
else
{
if (isc.active)
{
isc.active = 0;
printmessage16("Aborted interactive %s.", isc.rotatep ? "rotation" : "scaling");
bstatus &= ~3;
mousewaitmask = 3;
pointhighlight = -1;
}
}
editorDraw2dLine(searchx,0, searchx,8, editorcolors[15]);
editorDraw2dLine(0,searchy, 8,searchy, editorcolors[15]);
// 2d3d mode
if (m32_2d3dmode && m32_2d3d_resolutions_match())
{
#ifdef USE_OPENGL
int bakrendmode = rendmode;
#endif
vec2_t bdim = { xdim, ydim };
xdim = xdim2d;
ydim = ydim2d;
#ifdef USE_OPENGL
rendmode = REND_CLASSIC;
#endif
if (m32_2d3d.x + XSIZE_2D3D > xdim2d - 4)
m32_2d3d.x = xdim2d - 4 - XSIZE_2D3D;
if (m32_2d3d.y + YSIZE_2D3D > ydim2d - 4 - STATUS2DSIZ2)
m32_2d3d.y = ydim2d - 4 - YSIZE_2D3D - STATUS2DSIZ2;
updatesectorz(pos.x, pos.y, pos.z, &cursectnum);
if (cursectnum == -1)
updatesector(pos.x, pos.y, &cursectnum);
if (cursectnum != -1)
{
int32_t cz, fz;
getzsofslope(cursectnum, pos.x, pos.y, &cz, &fz);
inpclamp(&pos.z, cz+(4<<8), fz-(4<<8));
videoEndDrawing();
videoSetViewableArea(m32_2d3d.x, m32_2d3d.y, m32_2d3d.x + XSIZE_2D3D, m32_2d3d.y + YSIZE_2D3D);
videoClearViewableArea(-1);
vec2_t osearch = { searchx, searchy };
searchx -= m32_2d3d.x;
searchy -= m32_2d3d.y;
M32_DrawRoomsAndMasks();
videoSetViewableArea(0, 0, xdim2d-1, ydim2d-1);
#ifdef USE_OPENGL
rendmode = bakrendmode;
#endif
xdim = bdim.x;
ydim = bdim.y;
searchx = osearch.x;
searchy = osearch.y;
videoBeginDrawing();
editorDraw2dLine(m32_2d3d.x, m32_2d3d.y, m32_2d3d.x + XSIZE_2D3D, m32_2d3d.y, editorcolors[15]);
editorDraw2dLine(m32_2d3d.x + XSIZE_2D3D, m32_2d3d.y, m32_2d3d.x + XSIZE_2D3D, m32_2d3d.y + YSIZE_2D3D, editorcolors[15]);
editorDraw2dLine(m32_2d3d.x, m32_2d3d.y, m32_2d3d.x, m32_2d3d.y + YSIZE_2D3D, editorcolors[15]);
editorDraw2dLine(m32_2d3d.x, m32_2d3d.y + YSIZE_2D3D, m32_2d3d.x + XSIZE_2D3D, m32_2d3d.y + YSIZE_2D3D, editorcolors[15]);
}
}
if (!m32_is2d3dmode())
{
////// draw mouse pointer
col = editorcolors[0];
drawline16base(searchx+1, searchy+1, +0, -8, +0, -1, col);
drawline16base(searchx+1, searchy+1, +0, 1, +0, 8, col);
drawline16base(searchx+1, searchy+1, -8, 0, -1, 0, col);
drawline16base(searchx+1, searchy+1, 1, 0, 8, 0, col);
col = searchlock ? editorcolors[13] : editorcolors[15 - 3*gridlock];
if (joinsector[0] >= 0)
col = editorcolors[11];
if (numcorruptthings>0)
{
static char cbuf[64];
if ((pointhighlight&16384)==0)
{
// If aiming at wall, check whether it is corrupt, and print a
// warning message near the mouse pointer if that is the case.
for (i=0; i<numcorruptthings; i++)
if ((corruptthings[i]&CORRUPT_MASK)==CORRUPT_WALL &&
(corruptthings[i]&(MAXWALLS-1))==pointhighlight)
{
col = editorcolors[13];
printext16(searchx+6, searchy-6-8, editorcolors[13], editorcolors[0], "corrupt wall", 0);
break;
}
}
Bsprintf(cbuf, "Map corrupt (level %d): %s%d errors", corruptlevel,
numcorruptthings>=MAXCORRUPTTHINGS ? ">=" : "", numcorruptthings);
printext16(8, 8, editorcolors[13]+(M32_THROB>>2), editorcolors[0], cbuf, 0);
}
if (highlightsectorcnt==0 || highlightcnt==0)
{
if (keystatus[sc_SemiColon] || keystatus[sc_Quote]) // ' and ;
{
col = editorcolors[14];
drawline16base(searchx+16, searchy-16, -4, 0, +4, 0, col);
if (keystatus[sc_Quote])
drawline16base(searchx+16, searchy-16, 0, -4, 0, +4, col);
}
if (highlightsectorcnt == 0)
if (keystatus[sc_RightShift])
printext16(searchx+6, searchy-2+8, editorcolors[12], -1, "ALL", 0);
if (highlightcnt == 0)
{
if (eitherCTRL && (highlightx1!=highlightx2 || highlighty1!=highlighty2))
printext16(searchx+6, searchy-6-8, editorcolors[12], -1, "SPR ONLY", 0);
#ifdef YAX_ENABLE
if (keystatus[sc_End]) // End
printext16(searchx+6, searchy-2+8, editorcolors[12], -1, "ALL", 0);
#endif
}
}
drawline16base(searchx, searchy, +0, -8, +0, -1, col);
drawline16base(searchx, searchy, +0, 1, +0, 8, col);
drawline16base(searchx, searchy, -8, 0, -1, 0, col);
drawline16base(searchx, searchy, 1, 0, 8, 0, col);
////// Draw the white pixel closest to mouse cursor on linehighlight
if (linehighlight>=0)
{
char col = wall[linehighlight].nextsector >= 0 ? editorcolors[15] : editorcolors[5];
if (m32_sideview)
{
getclosestpointonwall(searchx, searchy, linehighlight, &dax, &day, 1);
drawline16base(dax, day, 0, 0, 0, 0, col);
}
else
{
getclosestpointonwall(mousxplc, mousyplc, linehighlight, &dax, &day, 0);
ovhscrcoords(dax, day, &x2, &y2);
drawline16base(x2, y2, 0, 0, 0, 0, col);
}
}
}
videoEndDrawing(); //}}} LOCK_FRAME_1
OSD_Draw();
}
inputchecked = 1;
VM_OnEvent(EVENT_PREKEYS2D, -1);
CallExtCheckKeys(); // TX 20050101, it makes more sense to have this here so keys can be overwritten with new functions in bstub.c
// 2d3d mode
if (m32_is2d3dmode())
goto nokeys;
// Flip/mirror sector Ed Coolidge
if (keystatus[sc_X] || keystatus[sc_Y]) // X or Y (2D)
{
int32_t about_x=keystatus[sc_X];
int32_t doMirror = eitherALT; // mirror walls and wall/floor sprites
#ifdef YAX_ENABLE
if (highlightsectorcnt > 0 && !hl_all_bunch_sectors_p())
{
printmessage16("To flip extended sectors, all sectors of a bunch must be selected");
keystatus[sc_X] = keystatus[sc_Y] = 0;
}
else
#endif
if (highlightsectorcnt > 0)
{
int16_t *const otonwall = onextwall; // OK, since we make old-nextwalls invalid
mkonwinvalid();
keystatus[sc_X] = keystatus[sc_Y] = 0;
for (j=0; j<numwalls; j++)
otonwall[j] = j;
get_sectors_center(highlightsector, highlightsectorcnt, &dax, &day);
if (gridlock && grid > 0)
locktogrid(&dax, &day);
for (i=0; i<highlightsectorcnt; i++)
{
int32_t startofloop, endofloop;
int32_t numtoswap = -1;
int32_t w=0;
uwalltype tempwall;
startofloop = startwall = sector[highlightsector[i]].wallptr;
endofloop = endwall = startwall+sector[highlightsector[i]].wallnum-1;
#if 0
if (doMirror)
{
//mirror sector textures
sector[highlightsector[i]].ceilingstat ^= 0x10;
sector[highlightsector[i]].floorstat ^= 0x10;
}
#endif
//save position of wall at start of loop
x3 = wall[startofloop].x;
y3 = wall[startofloop].y;
for (j=startwall; j<=endwall; j++)
{
//fix position of walls
if (about_x)
{
wall[j].x = dax-POINT2(j).x+dax; //flip wall.x about dax
wall[j].y = POINT2(j).y;
}
else
{
wall[j].x = POINT2(j).x;
wall[j].y = day-POINT2(j).y+day; //flip wall.y about day
}
if (doMirror)
wall[j].cstat ^= 8; //mirror walls about dax/day
if (wall[j].point2==startofloop) //check if j is end of loop
{
endofloop = j;
if (about_x)
{
wall[endofloop].x = dax-x3+dax; //flip wall.x about dax
wall[endofloop].y = y3;
}
else
{
wall[endofloop].x = x3;
wall[endofloop].y = day-y3+day; //flip wall.y about dax
}
//correct order of walls in loop to maintain player space (right-hand rule)
numtoswap = (endofloop-startofloop)>>1;
for (w=1; w<=numtoswap; w++)
{
Bmemcpy(&tempwall, &wall[startofloop+w], sizeof(walltype));
Bmemcpy(&wall[startofloop+w], &wall[endofloop-w+1], sizeof(walltype));
Bmemcpy(&wall[endofloop-w+1], &tempwall, sizeof(walltype));
otonwall[startofloop+w] = endofloop-w+1;
otonwall[endofloop-w+1] = startofloop+w;
}
//make point2 point to next wall in loop
for (w=startofloop; w<endofloop; w++)
wall[w].point2 = w+1;
wall[endofloop].point2 = startofloop;
startofloop = endofloop+1; //set first wall of next loop
//save position of wall at start of loop
x3 = wall[startofloop].x;
y3 = wall[startofloop].y;
}
}
j = headspritesect[highlightsector[i]];
while (j != -1)
{
if (about_x)
{
x3 = sprite[j].x;
sprite[j].x = dax-x3+dax; //flip sprite.x about dax
sprite[j].ang = (1024+2048-sprite[j].ang)&2047; //flip ang about 512
}
else
{
y3 = sprite[j].y;
sprite[j].y = day-y3+day; //flip sprite.y about day
sprite[j].ang = (2048-sprite[j].ang)&2047; //flip ang about 512
}
if (doMirror && (sprite[j].cstat & 0x30))
sprite[j].cstat ^= 4; // mirror sprites about dax/day (don't mirror monsters)
j = nextspritesect[j];
}
}
// finally, construct the nextwalls and yax-nextwalls
// for the new arrangement!
for (i=0; i<highlightsectorcnt; i++)
{
for (WALLS_OF_SECTOR(highlightsector[i], j))
{
if (wall[j].nextwall >= 0)
wall[j].nextwall = otonwall[wall[j].nextwall];
#ifdef YAX_ENABLE
{
int32_t cf, ynw;
for (cf=0; cf<2; cf++)
if ((ynw = yax_getnextwall(j, cf)) >= 0)
yax_setnextwall(j, cf, otonwall[ynw]);
}
#endif
}
}
printmessage16("Selected sector(s) flipped");
asksave = 1;
}
}
// end edit for sector flip
if (keystatus[88]) //F12
{
keystatus[88] = 0;
//__clearscreen_beforecapture__
videoCaptureScreen("captxxxx.tga", eitherSHIFT);
videoShowFrame(1);
}
if (keystatus[sc_B]) // B (clip Blocking xor) (2D)
{
pointhighlight = getpointhighlight(mousxplc, mousyplc, pointhighlight);
linehighlight = getlinehighlight(mousxplc, mousyplc, linehighlight, 0);
if ((pointhighlight&0xc000) == 16384)
{
sprite[pointhighlight&16383].cstat ^= 1;
sprite[pointhighlight&16383].cstat &= ~256;
sprite[pointhighlight&16383].cstat |= ((sprite[pointhighlight&16383].cstat&1)<<8);
asksave = 1;
}
else if (linehighlight >= 0)
{
wall[linehighlight].cstat ^= 1;
wall[linehighlight].cstat &= ~64;
if ((wall[linehighlight].nextwall >= 0) && !eitherSHIFT)
{
NEXTWALL(linehighlight).cstat &= ~(1+64);
NEXTWALL(linehighlight).cstat |= (wall[linehighlight].cstat&1);
}
asksave = 1;
}
keystatus[sc_B] = 0;
}
if (keystatus[sc_F]) //F (F alone does nothing in 2D right now)
{
keystatus[sc_F] = 0;
if (eitherALT) //ALT-F (relative alignmment flip)
{
linehighlight = getlinehighlight(mousxplc, mousyplc, linehighlight, 0);
if (linehighlight >= 0)
SetFirstWall(sectorofwall(linehighlight), linehighlight, 1);
}
}
if (keystatus[sc_O]) // O (ornament onto wall) (2D)
{
keystatus[sc_O] = 0;
if ((pointhighlight&0xc000) == 16384)
{
asksave = 1;
DoSpriteOrnament(pointhighlight&16383);
}
}
tsign = 0;
if (keystatus[sc_Comma] || (bstatus&33)==33) // , (2D)
tsign = +1;
if (keystatus[sc_Period] || (bstatus&17)==17) // . (2D)
tsign = -1;
if (tsign)
{
#ifdef YAX_ENABLE
if (highlightsectorcnt > 0 && !hl_all_bunch_sectors_p())
{
printmessage16("To rotate ext. sectors, all sectors of a bunch must be selected");
}
else
#endif
if (highlightsectorcnt > 0)
{
int32_t smoothRotation = eitherSHIFT, manualAngle = eitherALT;
vec2_t da = { dax, day };
if (manualAngle)
{
tsign = getnumber16("Rotation BUILD angle: ", 0, 2047, 1);
if (tsign==0)
{
printmessage16(" ");
goto rotate_hlsect_out;
}
printmessage16("Rotated highlighted sectors by %d BUILD degrees", tsign);
tsign &= 2047;
smoothRotation = 1;
}
get_sectors_center(highlightsector, highlightsectorcnt, &da.x, &da.y);
if (!smoothRotation)
{
if (gridlock && grid > 0)
locktogrid(&da.x, &da.y);
tsign *= 512;
}
for (i=0; i<highlightsectorcnt; i++)
{
for (WALLS_OF_SECTOR(highlightsector[i], j))
rotatepoint(da, *(vec2_t *)&wall[j], tsign&2047, (vec2_t *)&wall[j].x);
for (j=headspritesect[highlightsector[i]]; j != -1; j=nextspritesect[j])
{
rotatepoint(da, *(vec2_t *)&sprite[j], tsign&2047, (vec2_t *)&sprite[j].x);
sprite[j].ang = (sprite[j].ang+tsign)&2047;
}
}
m32_rotateang += tsign;
m32_rotateang &= 2047;
asksave = 1;
rotate_hlsect_out:
if (!smoothRotation || manualAngle)
keystatus[sc_Comma] = keystatus[sc_Period] = 0;
g_mouseBits &= ~(16|32);
bstatus &= ~(16|32);
}
else
{
if (pointhighlight >= 16384)
{
i = pointhighlight-16384;
if (eitherSHIFT)
sprite[i].ang = (sprite[i].ang-tsign)&2047;
else
{
sprite[i].ang = (sprite[i].ang-128*tsign)&2047;
keystatus[sc_Comma] = keystatus[sc_Period] = 0;
}
g_mouseBits &= ~(16|32);
bstatus &= ~(16|32);
}
}
}
if (keystatus[sc_ScrollLock]) //Scroll lock (set starting position)
{
startpos = pos;
startang = ang;
startsectnum = cursectnum;
keystatus[sc_ScrollLock] = 0;
asksave = 1;
printmessage16("Set starting position");
}
#if 1
if (keystatus[sc_F5]) //F5
{
CallExtShowSectorData(0);
}
if (keystatus[sc_F6]) //F6
{
if (pointhighlight >= 16384)
CallExtShowSpriteData(pointhighlight-16384);
else if (linehighlight >= 0)
CallExtShowWallData(linehighlight);
else
CallExtShowWallData(0);
}
if (keystatus[sc_F7]) //F7
{
keystatus[sc_F7] = 0;
for (i=0; i<numsectors; i++)
if (inside_editor_curpos(i) == 1)
{
YAX_SKIPSECTOR(i);
CallExtEditSectorData(i);
break;
}
}
if (keystatus[sc_F8]) //F8
{
keystatus[sc_F8] = 0;
if (pointhighlight >= 16384)
CallExtEditSpriteData(pointhighlight-16384);
else if (linehighlight >= 0)
CallExtEditWallData(linehighlight);
}
#endif
if (keystatus[sc_H]) //H (Hi 16 bits of tag)
{
keystatus[sc_H] = 0;
if (eitherCTRL) //Ctrl-H
{
pointhighlight = getpointhighlight(mousxplc, mousyplc, pointhighlight);
linehighlight = getlinehighlight(mousxplc, mousyplc, linehighlight, 0);
if ((pointhighlight&0xc000) == 16384)
{
sprite[pointhighlight&16383].cstat ^= 256;
asksave = 1;
}
else if (linehighlight >= 0)
{
wall[linehighlight].cstat ^= 64;
if ((wall[linehighlight].nextwall >= 0) && !eitherSHIFT)
{
NEXTWALL(linehighlight).cstat &= ~64;
NEXTWALL(linehighlight).cstat |= (wall[linehighlight].cstat&64);
}
asksave = 1;
}
}
else if (eitherALT) //ALT
{
if (pointhighlight >= 16384)
{
i = pointhighlight-16384;
j = taglab_linktags(1, i);
j = 2*(j&2);
Bsprintf(buffer, "Sprite (%d) Hi-tag: ", i);
sprite[i].hitag = getnumber16(buffer, sprite[i].hitag, BTAG_MAX, 0+j);
}
else if (linehighlight >= 0)
{
i = linehighlight;
j = taglab_linktags(1, i);
j = 2*(j&2);
Bsprintf(buffer, "Wall (%d) Hi-tag: ", i);
wall[i].hitag = getnumber16(buffer, wall[i].hitag, BTAG_MAX, 0+j);
}
}
else
{
for (i=0; i<numsectors; i++)
if (inside_editor_curpos(i) == 1)
{
YAX_SKIPSECTOR(i);
Bsprintf(buffer, "Sector (%d) Hi-tag: ", i);
sector[i].hitag = getnumber16(buffer, sector[i].hitag, BTAG_MAX, 0);
break;
}
}
// printmessage16("");
}
if (keystatus[sc_P]) // P (palookup #)
{
keystatus[sc_P] = 0;
for (i=0; i<numsectors; i++)
if (inside_editor_curpos(i) == 1)
{
YAX_SKIPSECTOR(i);
Bsprintf(buffer, "Sector (%d) Ceilingpal: ", i);
sector[i].ceilingpal = getnumber16(buffer, sector[i].ceilingpal, M32_MAXPALOOKUPS, 0);
Bsprintf(buffer, "Sector (%d) Floorpal: ", i);
sector[i].floorpal = getnumber16(buffer, sector[i].floorpal, M32_MAXPALOOKUPS, 0);
break;
}
}
if (keystatus[sc_E]) // E (status list)
{
keystatus[sc_E] = 0;
if (!eitherCTRL)
{
if (pointhighlight >= 16384)
{
i = pointhighlight-16384;
Bsprintf(buffer, "Sprite (%d) Status list: ", i);
changespritestat(i, getnumber16(buffer, sprite[i].statnum, MAXSTATUS-1, 0));
}
}
#ifdef YAX_ENABLE
else if (highlightsectorcnt > 0 && newnumwalls < 0)
{
////////// YAX //////////
static const char *cfs[2] = {"ceiling", "floor"};
int32_t cf, thez, ulz[2]={0,0};
int16_t bn, sandwichbunch=-1;
if (numyaxbunches==YAX_MAXBUNCHES)
{
message("Bunch limit of %d reached, cannot extend", YAX_MAXBUNCHES);
goto end_yax;
}
if (highlighted_sectors_components(0,0) != 1)
{
message("Sectors to extend must be in one connected component");
goto end_yax;
}
cf = ask_above_or_below();
if (cf==-1)
goto end_yax;
thez = SECTORFLD(highlightsector[0],z, cf);
for (i=0; i<highlightsectorcnt; i++)
{
bn = yax_getbunch(highlightsector[i], cf);
if (sandwichbunch >= 0 && bn!=sandwichbunch)
{
message("When sandwiching extension, must select only sectors of one bunch");
goto end_yax;
}
if (bn >= 0)
{
if (cf==YAX_FLOOR)
{
if (sandwichbunch < 0 && i!=0)
{
message("When sandwiching extension, must select only sectors of the bunch");
goto end_yax;
}
sandwichbunch = bn;
}
else
{
message("Sector %d's %s is already extended", highlightsector[i], cfs[cf]);
goto end_yax;
}
}
if (SECTORFLD(highlightsector[i],z, cf) != thez)
{
message("Sector %d's %s height doesn't match sector %d's",
highlightsector[i], cfs[cf], highlightsector[0]);
goto end_yax;
}
if ((sandwichbunch>=0 || highlightsectorcnt>1) && SECTORFLD(highlightsector[i],stat, cf)&2)
{
message("Sector %ss must not be sloped%s", cfs[cf],
sandwichbunch>=0 ? "" : "if extending more than one");
goto end_yax;
}
}
if (sandwichbunch >= 0)
{
// cf==YAX_FLOOR here
int32_t tempz, oldfz, swsecheight = DEFAULT_YAX_HEIGHT/4;
// highest floor z of lower sectors, lowest ceiling z of these sectors
int32_t minfloorz = INT32_MAX, maxceilz = INT32_MIN;
// some preparation for making the sandwich
if (highlightsectorcnt != yax_numsectsinbunch(sandwichbunch, YAX_FLOOR))
{
message("When sandwiching extension, must select all sectors of the bunch");
goto end_yax;
}
// "for i in sectors of sandwichbunch(floor)" is now the same as
// "for i in highlighted sectors"
oldfz = sector[highlightsector[0]].floorz;
// check if enough room in z
for (SECTORS_OF_BUNCH(sandwichbunch, YAX_CEILING, i))
for (WALLS_OF_SECTOR(i, j))
{
tempz = getflorzofslope(i, wall[j].x, wall[j].y);
minfloorz = min(minfloorz, tempz);
}
for (SECTORS_OF_BUNCH(sandwichbunch, YAX_FLOOR, i))
for (WALLS_OF_SECTOR(i, j))
{
tempz = getceilzofslope(i, wall[j].x, wall[j].y);
maxceilz = max(maxceilz, tempz);
}
if (minfloorz - maxceilz < 2*swsecheight)
{
message("Too little z headroom for sandwiching, need at least %d",
2*swsecheight);
goto end_yax;
}
if (maxceilz >= oldfz || oldfz >= minfloorz)
{
message("Internal error while sandwiching: oldfz out of bounds");
goto end_yax;
}
// maxceilz ---|
// ^ |
// ulz[0] ^
// ^ oldfz
// ulz[1] ^
// ^ |
// minfloorz ---|
ulz[0] = (int32_t)(oldfz - swsecheight*((double)(oldfz-maxceilz)/(minfloorz-maxceilz)));
ulz[0] &= ~255;
ulz[1] = ulz[0] + swsecheight;
if (maxceilz >= ulz[0] || ulz[1] >= minfloorz)
{
message("Too little z headroom for sandwiching");
goto end_yax;
}
}
m = numwalls;
Bmemset(visited, 0, sizeof(visited));
// construct!
for (i=0; i<highlightsectorcnt; i++)
for (WALLS_OF_SECTOR(highlightsector[i], j))
{
k = trace_loop(j, visited, NULL, NULL, !cf);
if (k == 0)
continue;
else if (k < 0)
{
numwalls = m;
goto end_yax;
}
//message("loop");
wall[k-1].point2 = numwalls;
numwalls = k;
}
for (i=m; i<numwalls; i++) // try
{
j = YAX_NEXTWALL(i, !cf);
if (j < 0)
{
message("Internal error while constructing sector: "
"YAX_NEXTWALL(%d, %d)<0!", i, !cf);
numwalls = m;
goto end_yax;
}
if (sandwichbunch >= 0)
{
if (YAX_NEXTWALL(j, cf) < 0)
{
message("Internal error while sandwiching (2): "
"YAX_NEXTWALL(%d, %d)<0!", j, cf);
numwalls = m;
goto end_yax;
}
}
}
for (i=m; i<numwalls; i++) // do!
{
j = YAX_NEXTWALL(i, !cf);
if (sandwichbunch >= 0)
{
int16_t oynw = YAX_NEXTWALL(j, cf);
yax_setnextwall(j, cf, i);
yax_setnextwall(i, cf, oynw);
yax_setnextwall(oynw, !cf, i);
}
else
{
yax_setnextwall(j, cf, i);
}
}
// create new sector based on first highlighted one
i = highlightsector[0];
Bmemcpy(&sector[numsectors], &sector[i], sizeof(sectortype));
sector[numsectors].wallptr = m;
sector[numsectors].wallnum = numwalls-m;
if (sandwichbunch < 0)
{
if (SECTORFLD(i,stat, cf)&2)
setslope(numsectors, !cf, SECTORFLD(i,heinum, cf));
else
setslope(numsectors, !cf, 0);
setslope(numsectors, cf, 0);
SECTORFLD(numsectors,z, !cf) = SECTORFLD(i,z, cf);
SECTORFLD(numsectors,z, cf) = SECTORFLD(i,z, cf) - (1-2*cf)*DEFAULT_YAX_HEIGHT;
}
else
{
for (SECTORS_OF_BUNCH(sandwichbunch, cf, i))
sector[i].floorz = ulz[0];
sector[numsectors].ceilingz = ulz[0];
sector[numsectors].floorz = ulz[1];
for (SECTORS_OF_BUNCH(sandwichbunch, !cf, i))
sector[i].ceilingz = ulz[1];
}
newnumwalls = numwalls;
numwalls = m;
SECTORFLD(numsectors,stat, !cf) &= ~1; // no plax
// restore red walls of the selected sectors
for (i=0; i<highlightsectorcnt; i++)
{
SECTORFLD(highlightsector[i],stat, cf) &= ~1; // no plax
for (WALLS_OF_SECTOR(highlightsector[i], j))
if (wall[j].nextwall < 0)
checksectorpointer(j, highlightsector[i]);
}
// link
if (sandwichbunch < 0)
{
yax_setbunch(numsectors, !cf, numyaxbunches);
for (i=0; i<highlightsectorcnt; i++)
yax_setbunch(highlightsector[i], cf, numyaxbunches);
}
else
{
yax_setbunch(numsectors, !cf, sandwichbunch);
// also relink
yax_setbunch(numsectors, cf, numyaxbunches);
for (SECTORS_OF_BUNCH(sandwichbunch, !cf, i))
yax_setbunch(i, !cf, numyaxbunches);
}
numwalls = newnumwalls;
newnumwalls = -1;
numsectors++;
yax_update(0);
yax_updategrays(pos.z);
reset_highlightsector();
if (sandwichbunch < 0)
message("Extended %ss of highlighted sectors, creating bunch %d",
cfs[cf], numyaxbunches-1);
else
message("Sandwiched bunch %d, creating bunch %d",
sandwichbunch, numyaxbunches-1);
asksave = 1;
}
else if (highlightcnt > 0)
{
/// 'punch' wall loop through extension
int32_t loopstartwall = -1, numloopwalls, cf;
int32_t srcsect, dstsect, ofirstwallofs;
int16_t cb, fb, bunchnum;
if (EDITING_MAP_P())
{
printmessage16("Must not be editing map to punch loop");
goto end_yax;
}
if (numyaxbunches >= YAX_MAXBUNCHES)
{
message("TROR bunch limit reached, cannot punch loop");
goto end_yax;
}
// determine start wall
for (i=0; i<highlightcnt; i++)
{
j = highlight[i];
if (j&16384)
continue;
// we only want loop-starting walls
if (j>0 && wall[j-1].point2==j)
continue;
if (clockdir(j)==CLOCKDIR_CCW)
{
YAX_SKIPWALL(j);
if (loopstartwall >= 0)
{
message("Must have a unique highlighted CCW loop to punch");
goto end_yax;
}
loopstartwall = j;
}
}
if (loopstartwall == -1)
{
message("Didn't find any non-grayed out CCW loop start walls");
goto end_yax;
}
// determine sector
srcsect = sectorofwall(loopstartwall);
yax_getbunches(srcsect, &cb, &fb);
if (cb < 0 && fb < 0)
{
message("Ceiling or floor must be extended to punch loop");
goto end_yax;
}
/// determine c/f
cf = -1;
if (fb < 0)
cf = YAX_CEILING;
else if (cb < 0)
cf = YAX_FLOOR;
fade_editor_screen(-1);
// query top/bottom
if (cf == -1)
{
char dachars[2] = {'a', 'z'};
cf = editor_ask_function("Punch loop above (a) or below (z)?", dachars, 2);
if (cf == -1)
goto end_yax;
}
else
{
// ask even if only one choice -- I find it more
// consistent with 'extend sector' this way
if (-1 == editor_ask_function(cf==YAX_CEILING ? "Punch loop above (a)?" :
"Punch loop below (z)?", cf==YAX_CEILING?"a":"z", 1))
goto end_yax;
}
bunchnum = (cf==YAX_CEILING) ? cb : fb;
// check 1
j = loopstartwall; // will be real start wall of loop
numloopwalls = 1; // will be number of walls in loop
for (i=wall[loopstartwall].point2; i!=loopstartwall; i=wall[i].point2)
{
numloopwalls++;
if (i < j)
j = i;
if ((show2dwall[i>>3]&(1<<(i&7)))==0)
{
message("All loop points must be highlighted to punch");
goto end_yax;
}
if (yax_getnextwall(loopstartwall, cf) >= 0 || yax_getnextwall(i, cf) >= 0)
{
// somewhat redundant, since it would also be caught by check 2
message("Loop walls must not already have TROR neighbors");
goto end_yax;
}
if (wall[loopstartwall].nextwall < 0 || wall[i].nextwall < 0)
{
message("INTERNAL ERROR: All loop walls are expected to be red");
goto end_yax;
}
}
loopstartwall = j;
if (numwalls + 2*numloopwalls > MAXWALLS || numsectors+1 > MAXSECTORS)
{
message("Punching loop through extension would exceed limits");
goto end_yax;
}
// get other-side sector, j==loopstartwall
dstsect = yax_getneighborsect(wall[j].x, wall[j].y, srcsect, cf);
if (dstsect < 0)
{
message("Punch loop INTERNAL ERROR: dstsect < 0. Map corrupt?");
goto end_yax;
}
// check 2
i = loopstartwall;
do
{
j = wall[i].point2;
for (WALLS_OF_SECTOR(dstsect, k))
{
vec2_t pint;
if (lineintersect2v((vec2_t *)&wall[i], (vec2_t *)&wall[j],
(vec2_t *)&wall[k], (vec2_t *)&POINT2(k), &pint))
{
message("Loop lines must not intersect any destination sector's walls");
goto end_yax;
}
}
}
while ((i = j) != loopstartwall);
// construct new loop and (dummy yet) sector
Bmemcpy(&wall[numwalls], &wall[loopstartwall], numloopwalls*sizeof(walltype));
newnumwalls = numwalls+numloopwalls;
for (i=numwalls; i<newnumwalls; i++)
{
wall[i].point2 += (numwalls - loopstartwall);
wall[i].nextsector = wall[i].nextwall = -1;
}
sector[numsectors].wallptr = numwalls;
sector[numsectors].wallnum = numloopwalls;
numsectors++; // temp
// check 3
for (SECTORS_OF_BUNCH(bunchnum, !cf, i))
for (WALLS_OF_SECTOR(i, j))
{
if (inside(wall[j].x, wall[j].y, numsectors-1)==1)
{
numsectors--;
newnumwalls = -1;
message("A point of bunch %d's sectors lies inside the loop to punch",
bunchnum);
goto end_yax;
}
}
numsectors--;
// clear wall & sprite highlights
// TODO: see about consistency with update_highlight() after other ops
reset_highlight();
// construct the loop!
i = AddLoopToSector(dstsect, &ofirstwallofs);
if (i <= 0)
{
message("Punch loop INTERNAL ERROR with AddLoopToSector!");
}
else
{
int32_t oneinnersect = -1, innerdstsect = numsectors-1;
if (dstsect < srcsect)
loopstartwall += numloopwalls;
/// handle bunchnums! (specifically, create a new one)
// collect sectors inside source loop; for that, first break the
// inner->outer nextwall links
for (i=loopstartwall; i<loopstartwall+numloopwalls; i++)
{
// all src loop walls are red!
NEXTWALL(i).nextwall = NEXTWALL(i).nextsector = -1;
oneinnersect = wall[i].nextsector;
}
// vvv
// expect oneinnersect >= 0 here! Assumption: we collect exactly
// one connected component of sectors
collect_sectors1(collsectlist[0], collsectbitmap[0],
&collnumsects[0], oneinnersect, 0, 0);
// set new bunchnums
for (i=0; i<collnumsects[0]; i++)
yax_setbunch(collsectlist[0][i], cf, numyaxbunches);
yax_setbunch(innerdstsect, !cf, numyaxbunches);
// ^^^
// restore inner->outer nextwall links
for (i=loopstartwall; i<loopstartwall+numloopwalls; i++)
{
NEXTWALL(i).nextwall = i;
NEXTWALL(i).nextsector = srcsect;
// set yax-nextwalls!
j = (i-loopstartwall) + sector[dstsect].wallptr;
yax_setnextwall(i, cf, j);
yax_setnextwall(j, !cf, i);
yax_setnextwall(wall[i].nextwall, cf, wall[j].nextwall);
yax_setnextwall(wall[j].nextwall, !cf, wall[i].nextwall);
}
setfirstwall(dstsect, sector[dstsect].wallptr+ofirstwallofs);
message("Punched loop starting w/ wall %d into %s sector %d%s",
loopstartwall, cf==YAX_CEILING?"upper":"lower", dstsect,
(oneinnersect>=0) ? "" : " (ERRORS)");
}
mkonwinvalid();
asksave = 1;
yax_update(0);
yax_updategrays(pos.z);
}
end_yax: ;
#endif
}
if (highlightsectorcnt < 0)
{
if (((bstatus & 5) == 1 && highlightcnt <= 0) || ((bstatus & 5) == 1 && pointhighlight == -1) || keystatus[sc_RightShift]) //Right shift (point highlighting)
{
if (highlightcnt == 0)
{
int32_t xx[] = { highlightx1, highlightx1, searchx, searchx, highlightx1 };
int32_t yy[] = { highlighty1, searchy, searchy, highlighty1, highlighty1 };
highlightx2 = searchx;
highlighty2 = searchy;
ydim16 = ydim-STATUS2DSIZ2;
plotlines2d(xx, yy, 5, -editorcolors[14]);
}
else if (pointhighlight == -1 || keystatus[sc_RightShift])
{
highlightcnt = 0;
highlightx1 = searchx;
highlighty1 = searchy;
highlightx2 = searchx;
highlighty2 = searchy;
}
}
else
{
if (highlightcnt == 0)
{
int32_t add=keystatus[sc_Quote], sub=(!add && keystatus[sc_SemiColon]), setop=(add||sub);
if (!m32_sideview)
{
getpoint(highlightx1,highlighty1, &highlightx1,&highlighty1);
getpoint(highlightx2,highlighty2, &highlightx2,&highlighty2);
}
if (highlightx1 > highlightx2)
swaplong(&highlightx1, &highlightx2);
if (highlighty1 > highlighty2)
swaplong(&highlighty1, &highlighty2);
// Ctrl+RShift: select all wall-points of highlighted wall's loop:
if (eitherCTRL && highlightx1==highlightx2 && highlighty1==highlighty2)
{
if (!setop)
{
Bmemset(show2dwall, 0, sizeof(show2dwall));
Bmemset(show2dsprite, 0, sizeof(show2dsprite));
}
if (linehighlight >= 0 && linehighlight < MAXWALLS)
{
i = linehighlight;
do
{
if (!sub)
show2dwall[i>>3] |= (1<<(i&7));
else
show2dwall[i>>3] &= ~(1<<(i&7));
// XXX: this selects too many walls, need something more like
// those of dragpoint() -- could be still too many for
// loop punching though
for (j=0; j<numwalls; j++)
if (j!=i && wall[j].x==wall[i].x && wall[j].y==wall[i].y)
{
if (!sub)
show2dwall[j>>3] |= (1<<(j&7));
else
show2dwall[j>>3] &= ~(1<<(j&7));
}
i = wall[i].point2;
}
while (i != linehighlight);
}
update_highlight();
}
else
{
int32_t tx, ty, onlySprites=eitherCTRL;
int32_t accum_dragged_verts = 0;
if (!setop)
{
Bmemset(show2dwall, 0, sizeof(show2dwall));
Bmemset(show2dsprite, 0, sizeof(show2dsprite));
}
for (i=0; i<numwalls; i++)
wall[i].cstat &= ~(1<<14);
for (i=0; i<numwalls; i++)
{
if (onlySprites)
break;
YAX_SKIPWALL(i);
if (!m32_sideview)
{
tx = wall[i].x;
ty = wall[i].y;
// wall[i].cstat &= ~(1<<14);
}
else
{
editorGet2dScreenCoordinates(&tx,&ty, wall[i].x-pos.x,wall[i].y-pos.y, zoom);
ty += getscreenvdisp(
getflorzofslope(sectorofwall(i), wall[i].x,wall[i].y)-pos.z, zoom);
tx += halfxdim16;
ty += midydim16;
}
if (tx >= highlightx1 && tx <= highlightx2 &&
ty >= highlighty1 && ty <= highlighty2)
{
if (!sub)
{
if (numgraysects > 0 || m32_sideview)
{
// Only called to find out which walls would get dragged:
dragpoint(i, wall[i].x, wall[i].y, accum_dragged_verts);
accum_dragged_verts = 1;
}
else
show2dwall[i>>3] |= (1<<(i&7));
}
else
show2dwall[i>>3] &= ~(1<<(i&7));
}
}
if (!sub && (numgraysects > 0 || m32_sideview))
{
for (i=0; i<numwalls; i++)
if (wall[i].cstat&(1<<14))
show2dwall[i>>3] |= (1<<(i&7));
}
for (i=0; i<MAXSPRITES; i++)
{
if (sprite[i].statnum == MAXSTATUS)
continue;
// v v v: if END pressed, also permit sprites from grayed out sectors
if (!keystatus[sc_End] && (unsigned)sprite[i].sectnum < MAXSECTORS)
YAX_SKIPSECTOR(sprite[i].sectnum);
if (!m32_sideview)
{
tx = sprite[i].x;
ty = sprite[i].y;
}
else
{
editorGet2dScreenCoordinates(&tx,&ty, sprite[i].x-pos.x,sprite[i].y-pos.y, zoom);
ty += getscreenvdisp(sprite[i].z-pos.z, zoom);
tx += halfxdim16;
ty += midydim16;
}
if (tx >= highlightx1 && tx <= highlightx2 &&
ty >= highlighty1 && ty <= highlighty2)
{
if (!sub)
{
if (sprite[i].sectnum >= 0) // don't allow to select sprites in null space
show2dsprite[i>>3] |= (1<<(i&7));
}
else
show2dsprite[i>>3] &= ~(1<<(i&7));
}
}
update_highlight();
for (i=0; i<numwalls; i++)
wall[i].cstat &= ~(1<<14);
}
}
}
}
if (highlightcnt < 0)
{
if (((bstatus & 4) && highlightsectorcnt <= 0) || ((bstatus & 4) && linehighlight == -1)
|| keystatus[sc_RightAlt]) //Right alt (sector highlighting)
{
if (highlightsectorcnt == 0)
{
if (!eitherCTRL)
{
int32_t xx[] = { highlightx1, highlightx1, searchx, searchx, highlightx1 };
int32_t yy[] = { highlighty1, searchy, searchy, highlighty1, highlighty1 };
highlightx2 = searchx;
highlighty2 = searchy;
ydim16 = ydim-STATUS2DSIZ2;
plotlines2d(xx, yy, 5, -editorcolors[10]);
}
}
else
{
// didmakered: 'bad'!
int32_t didmakered = (highlightsectorcnt<0) || eitherCTRL, hadouterpoint=0;
#ifdef YAX_ENABLE
for (i=0; i<MAXSECTORS>>3; i++)
hlorgraysectbitmap[i] = hlsectorbitmap[i]|graysectbitmap[i];
#endif
for (i=0; i<highlightsectorcnt; i++)
{
int16_t tmpsect = -1;
for (WALLS_OF_SECTOR(highlightsector[i], j))
{
// if (wall[j].nextwall >= 0)
// checksectorpointer(wall[j].nextwall,wall[j].nextsector);
if (wall[j].nextwall < 0)
didmakered |= !!checksectorpointer(j, highlightsector[i]);
if (!didmakered)
{
updatesectorexclude(wall[j].x, wall[j].y, &tmpsect, hlorgraysectbitmap);
if (tmpsect<0)
hadouterpoint = 1;
}
}
#ifdef YAX_ENABLE
int16_t cb, fb;
yax_getbunches(highlightsector[i], &cb, &fb);
if (cb>=0 || fb>=0)
{
// TROR stuff in the pasted sectors would really
// complicate things, so don't allow this
didmakered=1;
}
#endif
}
if (!didmakered && !hadouterpoint && newnumwalls<0)
{
// fade the screen to have the user's attention
fade_editor_screen(-1);
didmakered |= !ask_if_sure("Insert outer loop and make red walls? (Y/N)", 0);
clearkeys();
}
if (!didmakered && !hadouterpoint && newnumwalls<0)
{
int16_t ignore, refsect;
int32_t n;
#ifdef YAX_ENABLE
int16_t refsectbn[2]={-1,-1};
int32_t refextcf=-1;
#endif
Bmemset(visited, 0, sizeof(visited));
for (i=0; i<highlightsectorcnt; i++)
{
for (WALLS_OF_SECTOR(highlightsector[i], j))
{
k = trace_loop(j, visited, &ignore, &refsect, -1);
if (k == 0)
continue;
else if (k < 0)
goto end_autoredwall;
if (ignore)
continue;
// done tracing one outer loop
#ifdef YAX_ENABLE
yax_getbunches(refsect, &refsectbn[0], &refsectbn[1]);
if (refsectbn[0]>=0 || refsectbn[1]>=0)
{
if (refsectbn[0]>=0 && refsectbn[1]>=0)
{
// at least one of ceiling/floor must be non-extended
didmakered = 1;
}
else
{
// ... and the other must be non-sloped
refextcf = (refsectbn[1]>=0);
if (SECTORFLD(refsect,stat, !refextcf)&2)
didmakered = 1;
}
}
if (didmakered)
goto end_autoredwall;
if (refextcf >= 0)
{
int32_t refz = SECTORFLD(refsect,z, refextcf), tmpsect;
int32_t neededzofs=0;
// the reference sector is extended on one side
// (given by refextcf) and non-sloped on the other
if (highlighted_sectors_components(0,0) != 1)
{
message("Highlighted sectors must be in one connected component");
goto end_autoredwall;
}
for (m=0; m<highlightsectorcnt; m++)
{
tmpsect = highlightsector[m];
yax_setbunch(tmpsect, refextcf, refsectbn[refextcf]);
// walls: not needed, since they're all inner to the bunch
SECTORFLD(tmpsect,z, refextcf) = refz;
setslope(tmpsect, refextcf, 0);
if (refextcf==0)
neededzofs = max(neededzofs, refz-sector[tmpsect].floorz);
else
neededzofs = max(neededzofs, sector[tmpsect].ceilingz-refz);
}
if (neededzofs > 0)
{
neededzofs += ksgn(neededzofs)*(512<<4);
neededzofs &= ~((256<<4)-1);
if (refextcf==1)
neededzofs *= -1;
for (m=0; m<highlightsectorcnt; m++)
SECTORFLD(highlightsector[m],z, !refextcf) += neededzofs;
}
}
#endif
wall[k-1].point2 = numwalls; // close the loop
newnumwalls = k;
n = (newnumwalls-numwalls); // number of walls in just constructed loop
if (clockdir(numwalls)==CLOCKDIR_CW)
{
int16_t begwalltomove = sector[refsect].wallptr+sector[refsect].wallnum;
int32_t onwwasvalid = onwisvalid();
flipwalls(numwalls, newnumwalls);
sector[refsect].wallnum += n;
if (refsect != numsectors-1)
{
uwalltype *tmpwall = (uwalltype *)Xmalloc(n * sizeof(walltype));
int16_t *tmponw = (int16_t *)Xmalloc(n * sizeof(int16_t));
for (m=0; m<numwalls; m++)
{
if (wall[m].nextwall >= begwalltomove)
wall[m].nextwall += n;
}
#ifdef YAX_ENABLE
yax_tweakwalls(begwalltomove, n);
#endif
for (m=refsect+1; m<numsectors; m++)
sector[m].wallptr += n;
for (m=begwalltomove; m<numwalls; m++)
wall[m].point2 += n;
for (m=numwalls; m<newnumwalls; m++)
wall[m].point2 += (begwalltomove-numwalls);
Bmemcpy(tmponw, &onextwall[numwalls], n*sizeof(int16_t));
Bmemmove(&onextwall[begwalltomove+n], &onextwall[begwalltomove],
(numwalls-begwalltomove)*sizeof(int16_t));
Bmemcpy(&onextwall[begwalltomove], tmponw, n*sizeof(int16_t));
Bmemcpy(tmpwall, &wall[numwalls], n*sizeof(walltype));
Bmemmove(&wall[begwalltomove+n], &wall[begwalltomove],
(numwalls-begwalltomove)*sizeof(walltype));
Bmemcpy(&wall[begwalltomove], tmpwall, n*sizeof(walltype));
Bfree(tmpwall);
Bfree(tmponw);
}
numwalls = newnumwalls;
newnumwalls = -1;
mkonwinvalid();
for (m=begwalltomove; m<begwalltomove+n; m++)
if (checksectorpointer(m, refsect) > 0)
if (onwwasvalid && onextwall[wall[m].nextwall]>=0)
{
//initprintf("%d %d\n", m, onextwall[wall[m].nextwall]);
copy_some_wall_members(m, onextwall[wall[m].nextwall], 0);
}
#ifndef YAX_ENABLE
message("Attached new inner loop to sector %d", refsect);
#else
{
const char *cfstr[2] = {"ceiling","floor"};
message("Attached new inner loop to %s%ssector %d",
refextcf>=0 ? cfstr[refextcf] : "",
refextcf>=0 ? "-extended " : "", refsect);
}
asksave = 1;
if (refextcf >= 0)
{
yax_update(0);
goto end_autoredwall;
}
#endif
}
}
}
end_autoredwall:
newnumwalls = -1;
#ifdef YAX_ENABLE
yax_updategrays(pos.z);
#endif
}
highlightx1 = searchx;
highlighty1 = searchy;
highlightx2 = searchx;
highlighty2 = searchy;
highlightsectorcnt = 0;
}
}
else
{
if (highlightsectorcnt == 0)
{
int32_t const add=keystatus[sc_Quote], sub=(!add && keystatus[sc_SemiColon]), setop=(add||sub);
int32_t const pointsel = eitherCTRL;
int32_t tx,ty;
#ifdef YAX_ENABLE
// home: ceilings, end: floors
int32_t fb, bunchsel = keystatus[sc_End] ? 1 : (keystatus[sc_Home] ? 0 : -1);
uint8_t bunchbitmap[YAX_MAXBUNCHES>>3];
Bmemset(bunchbitmap, 0, sizeof(bunchbitmap));
#endif
if (!m32_sideview)
{
getpoint(highlightx1,highlighty1, &highlightx1,&highlighty1);
getpoint(highlightx2,highlighty2, &highlightx2,&highlighty2);
}
if (!pointsel)
{
if (highlightx1 > highlightx2)
swaplong(&highlightx1, &highlightx2);
if (highlighty1 > highlighty2)
swaplong(&highlighty1, &highlighty2);
}
if (!setop)
Bmemset(hlsectorbitmap, 0, sizeof(hlsectorbitmap));
for (i=0; i<numsectors; i++)
{
if (pointsel)
{
bad = (inside_editor(&pos, searchx,searchy, zoom, highlightx2, highlighty2, i)!=1);
}
else
{
bad = 0;
for (WALLS_OF_SECTOR(i, j))
{
if (!m32_sideview)
{
tx = wall[j].x;
ty = wall[j].y;
}
else
{
editorGet2dScreenCoordinates(&tx,&ty, wall[j].x-pos.x,wall[j].y-pos.y, zoom);
ty += getscreenvdisp(getflorzofslope(i, wall[j].x,wall[j].y)-pos.z, zoom);
tx += halfxdim16;
ty += midydim16;
}
if (tx < highlightx1 || tx > highlightx2) bad = 1;
if (ty < highlighty1 || ty > highlighty2) bad = 1;
if (bad == 1) break;
}
}
if (bad == 0)
{
#ifdef YAX_ENABLE
if (bunchsel!=-1 && (fb = yax_getbunch(i, YAX_FLOOR))>=0)
{
if ((sub || (graysectbitmap[i>>3]&(1<<(i&7)))==0) &&
(bunchbitmap[fb>>3]&(1<<(fb&7)))==0)
{
bunchbitmap[fb>>3] |= (1<<(fb&7));
for (SECTORS_OF_BUNCH(fb, bunchsel, j))
handlesecthighlight1(j, sub, 1);
}
}
else
#endif
handlesecthighlight1(i, sub, eitherSHIFT);
}
}
update_highlightsector();
ovh_whiteoutgrab(0);
}
}
}
if (((bstatus&1) < (oldmousebstatus&1)) && highlightsectorcnt < 0) //after dragging
{
int32_t runi, numdelpoints=0;
int32_t havedrawnwalls = (newnumwalls!=-1), restorestat=1;
// restorestat is set to 2 whenever the drawn walls should NOT be
// restored afterwards
int32_t err = backup_drawn_walls(0);
if (err)
{
message("Error backing up drawn walls (code %d)!", err);
goto end_after_dragging;
}
j = 1;
for (i=0; i<highlightcnt; i++)
if (pointhighlight == highlight[i])
{
j = 0;
break;
}
if (j == 0)
{
for (i=0; i<highlightcnt; i++)
if ((highlight[i]&0xc000) == 16384)
updatesprite1(highlight[i]&16383);
}
else if ((pointhighlight&0xc000) == 16384)
{
updatesprite1(pointhighlight&16383);
}
if ((pointhighlight&0xc000) == 0)
{
dax = wall[pointhighlight].x;
day = wall[pointhighlight].y;
for (i=0; i<2; i++)
{
if (dragwall[i] < 0)
break;
if (keeptexturestretch && olen[i] != 0)
{
#ifndef YAX_ENABLE
j = dragwall[i];
#else
int32_t cf;
for (YAX_ITER_WALLS(dragwall[i], j, cf))
#endif
{
int32_t nw = wall[j].nextwall;
k = getlenbyrep(olen[i], wall[j].xrepeat);
fixxrepeat(j, k);
if (nw >= 0)
{
k = getlenbyrep(olen[i], wall[nw].xrepeat);
fixxrepeat(nw, k);
}
}
}
}
}
else if ((pointhighlight&0xc000) == 16384)
{
dax = sprite[pointhighlight&16383].x;
day = sprite[pointhighlight&16383].y;
}
dragwall[0] = dragwall[1] = -1;
// attempt to delete some points
for (runi=0; runi<3; runi++) // check, tweak, carry out
for (i=numwalls-1; i>=0; i--)
{
if (runi==0)
wall[i].cstat &= ~(1<<14);
if (wall[i].x == POINT2(i).x && wall[i].y == POINT2(i).y)
{
if (havedrawnwalls)
{
if (i==ovh.suckwall || (ovh.split && i==ovh.splitstartwall))
{
// if we're about to delete a wall that participates
// in splitting, discard the already drawn walls
restorestat = 2;
}
else if (runi == 1)
{
// correct drawn wall anchors
if (ovh.suckwall > i)
ovh.suckwall--;
if (ovh.split && ovh.splitstartwall > i)
ovh.splitstartwall--;
}
}
if (runi == 0)
{
int32_t sectnum = sectorofwall(i);
if (sector[sectnum].wallnum <= 3)
{
message("Deleting wall %d would leave sector %d with %d walls.",
i, sectnum, sector[sectnum].wallnum-1);
goto end_after_dragging;
}
sectnum = wall[i].nextsector;
if (sectnum >= 0 && sector[sectnum].wallnum <= 3)
{
message("Deleting wall %d would leave sector %d with %d walls.",
i, sectnum, sector[sectnum].wallnum-1);
goto end_after_dragging;
}
}
else
{
deletepoint(i, runi);
if (runi==2)
numdelpoints++;
}
}
}
if (numdelpoints)
{
if (numdelpoints > 1)
message("Deleted %d points%s", numdelpoints,
(havedrawnwalls && restorestat==2) ? " and cleared drawn walls":"");
else
printmessage16("Point deleted%s", (havedrawnwalls && restorestat==2) ?
", cleared drawn walls":"");
asksave = 1;
}
else
{
for (i=0; i<numwalls; i++) //make new red lines?
{
YAX_SKIPWALL(i);
if ((wall[i].x == dax && wall[i].y == day)
|| (POINT2(i).x == dax && POINT2(i).y == day))
{
checksectorpointer(i, sectorofwall(i));
// fixrepeats(i);
asksave = 1;
}
}
}
#ifdef YAX_ENABLE
yax_update(0);
yax_updategrays(pos.z);
#endif
end_after_dragging:
backup_drawn_walls(restorestat);
}
if ((bstatus&1) > 0) //drag points
{
if (highlightsectorcnt > 0)
{
if ((bstatus&1) > (oldmousebstatus&1))
{
newnumwalls = -1;
sectorhighlightstat = -1;
// updatesector(mousxplc,mousyplc,&cursectorhighlight);
cursectorhighlight = -1;
for (i=0; i<highlightsectorcnt; i++)
if (inside_editor_curpos(highlightsector[i])==1)
{
cursectorhighlight = highlightsector[i];
break;
}
if (cursectorhighlight >= 0 && cursectorhighlight < numsectors)
{
//You clicked inside one of the flashing sectors!
sectorhighlightstat = 1;
dax = mousxplc;
day = mousyplc;
if (gridlock && grid > 0)
locktogrid(&dax, &day);
sectorhighlightx = dax;
sectorhighlighty = day;
}
}
else if (sectorhighlightstat == 1)
{
dax = mousxplc;
day = mousyplc;
if (gridlock && grid > 0)
locktogrid(&dax, &day);
dax -= sectorhighlightx;
day -= sectorhighlighty;
sectorhighlightx += dax;
sectorhighlighty += day;
#ifdef YAX_ENABLE
if (!hl_all_bunch_sectors_p())
printmessage16("To drag extended sectors, all sectors of a bunch must be selected");
else
#endif
for (i=0; i<highlightsectorcnt; i++)
{
for (WALLS_OF_SECTOR(highlightsector[i], j))
{ wall[j].x += dax; wall[j].y += day; }
for (j=headspritesect[highlightsector[i]]; j>=0; j=nextspritesect[j])
{ sprite[j].x += dax; sprite[j].y += day; }
}
//for(i=0;i<highlightsectorcnt;i++)
//{
// startwall = sector[highlightsector[i]].wallptr;
// endwall = startwall+sector[highlightsector[i]].wallnum-1;
// for(j=startwall;j<=endwall;j++)
// {
// if (wall[j].nextwall >= 0)
// checksectorpointer(wall[j].nextwall,wall[j].nextsector);
// checksectorpointer((short)j,highlightsector[i]);
// }
//}
asksave = 1;
}
}
else //if (highlightsectorcnt <= 0)
{
if ((bstatus&1) > (oldmousebstatus&1))
{
pointhighlight = getpointhighlight(mousxplc, mousyplc, pointhighlight);
if (pointhighlight >= 0 && (pointhighlight&0xc000)==0)
{
dragwall[0] = lastwall(pointhighlight);
dragwall[1] = pointhighlight;
olen[0] = wallength(dragwall[0]);
olen[1] = wallength(dragwall[1]);
}
}
if (pointhighlight >= 0 && (!m32_sideview || m32_sideelev>=32))
{
if (m32_sideview)
{
int32_t dz;
if (pointhighlight>=16384)
dz = sprite[pointhighlight&16383].z - pos.z;
else
dz = getflorzofslope(sectorofwall(pointhighlight),
wall[pointhighlight].x, wall[pointhighlight].y) - pos.z;
getinvdisplacement(&dax,&day, -dz);
dax += mousxplc;
day += mousyplc;
}
else
{
dax = mousxplc;
day = mousyplc;
}
if (gridlock && grid > 0)
locktogrid(&dax, &day);
j = 1;
if (highlightcnt > 0)
for (i=0; i<highlightcnt; i++)
if (pointhighlight == highlight[i])
{ j = 0; break; }
if (j == 0)
{
if ((pointhighlight&0xc000) == 0)
{
dax -= wall[pointhighlight].x;
day -= wall[pointhighlight].y;
}
else
{
dax -= sprite[pointhighlight&16383].x;
day -= sprite[pointhighlight&16383].y;
}
for (i=0; i<highlightcnt; i++)
{
if ((highlight[i]&0xc000) == 0)
{
wall[highlight[i]].x += dax;
wall[highlight[i]].y += day;
}
else
{
spritetype *daspr = &sprite[highlight[i]&16383];
daspr->x += dax;
daspr->y += day;
setspritez(daspr-sprite, (const vec3_t *)daspr);
}
}
}
else
{
if ((pointhighlight&0xc000) == 0)
{
if (newnumwalls >= numwalls &&
wall[pointhighlight].x==firstx && wall[pointhighlight].y==firsty)
{
printmessage16("Can't drag point where drawing started.");
goto end_point_dragging;
}
dragpoint(pointhighlight,dax,day,2);
wall[lastwall(pointhighlight)].cstat |= (1<<14);
}
else if ((pointhighlight&0xc000) == 16384)
{
int32_t daspr=pointhighlight&16383;
int16_t osec=sprite[daspr].sectnum, nsec=osec;
vec3_t vec, ovec;
Bmemcpy(&ovec, (vec3_t *)&sprite[daspr], sizeof(vec3_t));
vec.x = dax;
vec.y = day;
vec.z = sprite[daspr].z;
if (setspritez(daspr, &vec) == -1 && osec>=0)
{
updatesectorbreadth(dax, day, &nsec);
if (nsec >= 0)
{
sprite[daspr].x = dax;
sprite[daspr].y = day;
// z updating is after we released the mouse button
if (sprite[daspr].sectnum != nsec)
changespritesect(daspr, nsec);
}
else
Bmemcpy(&sprite[daspr], &ovec, sizeof(vec3_t));
}
}
}
asksave = 1;
}
}
}
else //if ((bstatus&1) == 0)
{
pointhighlight = getpointhighlight(mousxplc, mousyplc, pointhighlight);
sectorhighlightstat = -1;
}
end_point_dragging:
if (bstatus&(2) && (!(bstatus&5) || pointhighlight > 0 || highlightcnt > 0 || highlightsectorcnt > 0)) // change arrow position
{
if (eitherCTRL)
{
int16_t cursectornum;
for (cursectornum=0; cursectornum<numsectors; cursectornum++)
if (inside_editor_curpos(cursectornum) == 1)
break;
if (cursectornum < numsectors)
{
if (pointhighlight >= 16384)
CallExtEditSpriteData(pointhighlight-16384);
else if ((linehighlight >= 0) && ((bstatus&1) || sectorofwall(linehighlight) == cursectornum))
CallExtEditWallData(linehighlight);
else if (cursectornum >= 0)
CallExtEditSectorData(cursectornum);
}
bstatus &= ~2;
}
else
{
if (m32_sideview && (bstatus&4))
{
pos.z += divscale18(searchy-midydim16,zoom);
getpoint(searchx,midydim16, &pos.x, &pos.y);
#ifdef YAX_ENABLE
yax_updategrays(pos.z);
#endif
}
else
{
pos.x = mousxplc;
pos.y = mousyplc;
}
if (m32_sideview)
{
int32_t opat=drawlinepat;
y1 = INT32_MAX;
for (i=0; i<numsectors; i++)
{
if (inside(pos.x, pos.y, i)==1)
{
day = getscreenvdisp(getflorzofslope(i, pos.x, pos.y)-pos.z, zoom);
x2 = max(4, mulscale14(64,zoom));
y2 = scalescreeny(x2);
if (klabs(day) < y1)
y1 = day;
drawline16base(halfxdim16, midydim16+day, -x2,-y2, x2,y2, editorcolors[14]);
drawline16base(halfxdim16, midydim16+day, -x2,y2, x2,-y2, editorcolors[14]);
}
}
drawlinepat = 0x11111111;
if (y1 != INT32_MAX)
drawline16base(halfxdim16,midydim16, 0,0, 0,y1, editorcolors[14]);
// else
// drawline16base(halfxdim16,midydim16, 0,0, 0,getscreenvdisp(-pos.z, zoom), editorcolors[14]);
drawlinepat = opat;
}
searchx = halfxdim16;
searchy = midydim16;
}
}
// else if ((oldmousebstatus&6) > 0)
updatesectorz(pos.x,pos.y,pos.z,&cursectnum);
if (circlewall != -1 && (keystatus[sc_kpad_Minus] || ((bstatus&32) && !eitherCTRL))) // -, mousewheel down
{
if (circlepoints > 1)
circlepoints--;
keystatus[sc_kpad_Minus] = 0;
g_mouseBits &= ~32;
bstatus &= ~32;
}
if (circlewall != -1 && (keystatus[sc_kpad_Plus] || ((bstatus&16) && !eitherCTRL))) // +, mousewheel up
{
if (circlepoints < 63)
circlepoints++;
keystatus[sc_kpad_Plus] = 0;
g_mouseBits &= ~16;
bstatus &= ~16;
}
if (keystatus[sc_F3]) // F3
{
keystatus[sc_F3]=0;
if (!m32_sideview && EDITING_MAP_P())
message("Must not be editing map while switching to side view mode.");
else
{
m32_sideview = !m32_sideview;
printmessage16("Side view %s", m32_sideview?"enabled":"disabled");
}
}
if (m32_sideview && (keystatus[sc_Q] || keystatus[sc_W]))
{
if (eitherCTRL)
{
if (m32_sideang&63)
{
m32_sideang += (1-2*keystatus[sc_Q])*(1-2*sideview_reversehrot)*32;
m32_sideang &= (2047&~63);
}
else
{
m32_sideang += (1-2*keystatus[sc_Q])*(1-2*sideview_reversehrot)*64;
m32_sideang &= 2047;
}
keystatus[sc_Q] = keystatus[sc_W] = 0;
}
else
{
m32_sideang += (1-2*keystatus[sc_Q])*(1-2*sideview_reversehrot)*synctics<<(eitherSHIFT*2);
m32_sideang &= 2047;
}
_printmessage16("Sideview angle: %d", (int32_t)m32_sideang);
}
if (m32_sideview && (eitherSHIFT || (bstatus&(16|32))))
{
if ((DOWN_BK(MOVEUP) || (bstatus&16)) && m32_sideelev < 512)
{
if (DOWN_BK(MOVEUP))
m32_sideelev += synctics<<1;
if (bstatus&16)
m32_sideelev += 4<<1;
if (m32_sideelev > 512)
m32_sideelev = 512;
_printmessage16("Sideview elevation: %d", m32_sideelev);
}
if ((DOWN_BK(MOVEDOWN) || (bstatus&32)) && m32_sideelev > 0)
{
if (DOWN_BK(MOVEDOWN))
m32_sideelev -= synctics<<1;
if (bstatus&32)
m32_sideelev -= 4<<1;
if (m32_sideelev < 0)
m32_sideelev = 0;
_printmessage16("Sideview elevation: %d", m32_sideelev);
}
}
else
{
int32_t didzoom=0;
if ((DOWN_BK(MOVEUP) || (bstatus&16)) && zoom < 39936)
{
if (DOWN_BK(MOVEUP))
{
ztarget += (synctics*(ztarget>>4))>>(eitherSHIFT<<1);
if (zoom < 64)
ztarget += (synctics*(ztarget>>4)) * (eitherSHIFT);
}
if (bstatus&16)
ztarget += 4*(ztarget>>4);
if (zoom < 24) zoom += 2;
didzoom = 1;
}
if ((DOWN_BK(MOVEDOWN) || (bstatus&32)) && zoom > 16)
{
if (DOWN_BK(MOVEDOWN))
{
ztarget -= (synctics*(ztarget>>4))>>(eitherSHIFT<<1);
if (zoom < 64)
ztarget -= (synctics * (ztarget >> 4)) * (eitherSHIFT);
}
if (bstatus&32)
ztarget -= 4*(ztarget>>4);
didzoom = 1;
}
if (didzoom)
{
if (eitherALT)
{
searchx = halfxdim16;
searchy = midydim16;
pos.x = mousxplc;
pos.y = mousyplc;
}
ztarget = clamp(ztarget, 16, 39936);
_printmessage16("Zoom: %d",ztarget);
}
}
if (keystatus[sc_G]) // G (grid on/off)
{
keystatus[sc_G] = 0;
grid++;
if (grid == 7) grid = 0;
}
if (keystatus[sc_L]) // L (grid lock)
{
keystatus[sc_L] = 0;
if (eitherSHIFT)
{
searchlock = 1-searchlock;
silentmessage("Selection lock %s", searchlock ? "on" : "off");
}
else
{
gridlock = !gridlock;
silentmessage("Grid locking %s", gridlock ? "on" : "off");
}
}
if (keystatus[sc_J]) // J (join sectors)
{
keystatus[sc_J] = 0;
if (newnumwalls >= 0)
{
printmessage16("Can't join sectors while editing.");
goto end_join_sectors;
}
#ifdef YAX_ENABLE
if (highlightsectorcnt > 0 && eitherCTRL)
{
// [component][ceiling(0) or floor(1)]
// compstat: &1: "has extension", &2: "differ in z", &4: "sloped", -1: "uninited"
int32_t cf, comp, compstat[2][2]={{-1,-1},{-1,-1}}, compcfz[2][2];
// joinstat: join what to what?
// &1: ceil(comp 0) <-> flor(comp 1), &2: flor(comp 0) <-> ceil(comp 1)
// (doesn't yet say which is stationary)
// movestat: which component can be displaced?
// &1: first, &2: second
int32_t askres, joinstat, needsdisp, moveonwp;
int32_t movestat, dx=0,dy=0,dz, delayerr=0;
int32_t numouterwalls[2] = {0,0}, numowals;
static int16_t outerwall[2][MAXWALLS];
const uwalltype *wal0, *wal1, *wal0p2, *wal1p2;
// join sector ceilings/floors to a new bunch
if (numyaxbunches==YAX_MAXBUNCHES)
{
message("Bunch limit of %d reached, cannot join", YAX_MAXBUNCHES);
goto end_join_sectors;
}
// first, see whether we have exactly two connected components
// wrt wall[].nextsector
if (highlighted_sectors_components(0,0) != 2)
{
message("Sectors must be partitioned in two components to join");
goto end_join_sectors;
}
for (k=0; k<highlightsectorcnt; k++)
{
j = highlightsector[k];
comp = !!(collsectbitmap[1][j>>3]&(1<<(j&7)));
for (cf=0; cf<2; cf++)
{
if (compstat[comp][cf]==-1)
{
compstat[comp][cf] = 0;
compcfz[comp][cf] = SECTORFLD(j,z, cf);
}
if (yax_getbunch(j, cf)>=0)
compstat[comp][cf] |= 1;
if (SECTORFLD(j,z, cf) != compcfz[comp][cf])
compstat[comp][cf] |= 2;
if (SECTORFLD(j,stat, cf)&2)
compstat[comp][cf] |= 4;
compcfz[comp][cf] = SECTORFLD(j,z, cf);
}
}
// check for consistency
joinstat = 0;
if (!compstat[0][YAX_CEILING] && !compstat[1][YAX_FLOOR])
joinstat |= 1;
if (!compstat[0][YAX_FLOOR] && !compstat[1][YAX_CEILING])
joinstat |= 2;
if (joinstat==0)
{
message("No consistent joining combination found");
OSD_Printf("comp0: c=%d,f=%d; comp1: c=%d,f=%d (1:extended, 2:z mismatch, 4:sloped)\n",
compstat[0][YAX_CEILING], compstat[0][YAX_FLOOR],
compstat[1][YAX_CEILING], compstat[1][YAX_FLOOR]);
//for (i=0; i<2; i++) for (j=0; j<2; j++) message("%d", compstat[i][j]);
goto end_join_sectors;
}
if (joinstat==3)
{
if (compcfz[0][YAX_CEILING] != compstat[1][YAX_FLOOR])
joinstat &= 1;
if (compcfz[0][YAX_CEILING] != compstat[1][YAX_FLOOR])
joinstat &= 2;
if (joinstat == 0)
joinstat = 3; // we couldn't disambiguate
}
for (comp=0; comp<2; comp++)
for (k=0; k<collnumsects[comp]; k++)
{
i = collsectlist[comp][k];
for (WALLS_OF_SECTOR(i, j))
if (wall[j].nextwall < 0)
outerwall[comp][numouterwalls[comp]++] = j;
}
if (numouterwalls[0] != numouterwalls[1])
{
message("Number of outer walls must be equal for both components"
" (have %d and %d)", numouterwalls[0], numouterwalls[1]);
if (numouterwalls[0]>0 && numouterwalls[1]>0)
delayerr = 1;
else
goto end_join_sectors;
}
numowals = min(numouterwalls[0], numouterwalls[1]);
// now sort outer walls 'geometrically'
for (comp=0; comp<2; comp++)
sort_walls_geometrically(outerwall[comp], numouterwalls[comp]);
for (k=0; k<numowals; k++)
{
wal0 = (uwalltype *)&wall[outerwall[0][k]];
wal1 = (uwalltype *)&wall[outerwall[1][k]];
wal0p2 = (uwalltype *)&wall[wal0->point2];
wal1p2 = (uwalltype *)&wall[wal1->point2];
if (k==0)
{
dx = wal1->x - wal0->x;
dy = wal1->y - wal0->y;
}
if (wal1->x - wal0->x != dx || wal1->y - wal0->y != dy ||
wal1p2->x - wal0p2->x != dx || wal1p2->y - wal0p2->y != dy)
{
pos.x = wal0->x + (wal0p2->x - wal0->x)/4;
pos.y = wal0->y + (wal0p2->y - wal0->y)/4;
pos.z = getflorzofslope(sectorofwall(wal0-(uwalltype *)wall), pos.x, pos.y);
if (!delayerr)
message("Outer wall coordinates must coincide for both components");
OSD_Printf("wal0:%d (%d,%d)--(%d,%d)\n",(int)(wal0-(uwalltype *)wall),
wal0->x,wal0->y, wal0p2->x,wal0p2->y);
OSD_Printf("wal1:%d (%d,%d)--(%d,%d)\n",(int)(wal1-(uwalltype *)wall),
wal1->x,wal1->y, wal1p2->x,wal1p2->y);
goto end_join_sectors;
}
}
if (delayerr)
goto end_join_sectors;
if (joinstat == 3)
{
char askchars[2] = {'1', 'v'};
// now is a good time to ask...
for (comp=0; comp<2; comp++)
for (k=0; k<collnumsects[comp]; k++)
fillsector_notrans(collsectlist[comp][k], comp==0 ? 159 : editorcolors[11]);
fade_editor_screen(editorcolors[11] | (159<<8));
char buffer[128];
Bsnprintf(buffer, sizeof(buffer), "Z differences | yellow ceiling w/ blue floor: %d, vice versa: %d",
compcfz[YAX_CEILING][0]-compcfz[YAX_FLOOR][1],
compcfz[YAX_CEILING][1]-compcfz[YAX_FLOOR][0]);
printext16(8, ydim-STATUS2DSIZ2-12, editorcolors[15], -1, buffer, 0);
askres = editor_ask_function("Connect yellow ceiling w/ blue floor (1) or (v)ice versa?", askchars, 2);
if (askres==-1)
goto end_join_sectors;
joinstat &= (1<<askres);
}
joinstat--; // 0:ceil(0)<->flor(1), 1:ceil(1)<->flor(0)
dz = compcfz[1][!joinstat] - compcfz[0][joinstat];
needsdisp = (dx || dy || dz);
if (needsdisp)
{
// a component is more likely to be displaced if it's not
// extended on the non-joining side
movestat = (!(compstat[0][!joinstat]&1)) | ((!(compstat[1][joinstat]&1))<<1);
if (!movestat)
{
movestat = 3;
// message("Internal error while TROR-joining: movestat inconsistent!");
// goto end_join_sectors;
}
if (movestat==3)
{
char askchars[2] = {'y', 'b'};
for (comp=0; comp<2; comp++)
for (k=0; k<collnumsects[comp]; k++)
fillsector_notrans(collsectlist[comp][k], comp==0 ? 159 : editorcolors[11]);
fade_editor_screen(editorcolors[11] | (159<<8));
askres = editor_ask_function("Move (y)ellow or (b)lue component?", askchars, 2);
if (askres==-1)
goto end_join_sectors;
movestat &= (1<<askres);
}
movestat--; // 0:move 1st, 1:move 2nd component
if (movestat==1)
dx*=-1, dy*=-1, dz*=-1;
moveonwp = 0;
if (onwisvalid())
{
static int16_t ocollsectlist[MAXSECTORS];
static uint8_t tcollbitmap[MAXSECTORS>>3];
int16_t ocollnumsects=collnumsects[movestat], tmpsect;
Bmemcpy(ocollsectlist, collsectlist[movestat], ocollnumsects*sizeof(int16_t));
Bmemset(tcollbitmap, 0, sizeof(tcollbitmap));
for (k=0; k<ocollnumsects; k++)
for (WALLS_OF_SECTOR(ocollsectlist[k], j))
{
if (onextwall[j] < 0)
continue;
tmpsect = sectorofwall(onextwall[j]);
sectors_components(1, &tmpsect, 1,0);
for (m=0; m<(numsectors+7)>>3; m++)
tcollbitmap[m] |= collsectbitmap[0][m];
moveonwp = 1;
}
if (moveonwp)
{
int32_t movecol = movestat==0 ? 159 : editorcolors[11];
for (i=0; i<numsectors; i++)
if (tcollbitmap[i>>3]&(1<<(i&7)))
fillsector_notrans(i, editorcolors[12]);
fade_editor_screen(editorcolors[12] | (movecol<<8));
moveonwp = ask_if_sure("Also move formerly wall-connected sectors?",0);
if (moveonwp==-1)
goto end_join_sectors;
}
}
// now need to collect them wrt. the nextsector but also
// the yax-nextsector relation
if (highlighted_sectors_components(1,moveonwp) != 2)
{
message("Must not have TROR connections between the two components");
goto end_join_sectors;
}
// displace!
for (k=0; k<collnumsects[movestat]; k++)
{
i = collsectlist[movestat][k];
sector[i].floorz += dz;
sector[i].ceilingz += dz;
for (WALLS_OF_SECTOR(i, j))
{
wall[j].x += dx;
wall[j].y += dy;
}
for (j=headspritesect[i]; j>=0; j=nextspritesect[j])
{
sprite[j].x += dx;
sprite[j].y += dy;
sprite[j].z += dz;
}
}
// restore old components, i.e. only the bunch sectors
highlighted_sectors_components(0,0);
} // end if (needsdisp)
/*** construct the YAX connection! ***/
for (comp=0; comp<2; comp++)
{
// walls
for (j=0; j<numowals; j++)
yax_setnextwall(outerwall[comp][j], comp^joinstat, outerwall[!comp][j]);
// sectors
for (k=0; k<collnumsects[comp]; k++)
{
i = collsectlist[comp][k];
yax_setbunch(i, comp^joinstat, numyaxbunches);
SECTORFLD(i,stat, comp^joinstat) &= ~1; // no plax
// restore red walls AFTER setting nextwalls
// (see checksectorpointer() for why)
for (WALLS_OF_SECTOR(i, j))
if (wall[j].nextwall < 0)
checksectorpointer(j, i);
}
}
reset_highlightsector();
yax_update(0);
yax_updategrays(pos.z);
message("Joined highlighted sectors to new bunch %d", numyaxbunches);
asksave = 1;
}
else
#endif // defined YAX_ENABLE
if (joinsector[0] < 0)
{
int32_t numjoincandidates = 0;
char *origframe=NULL;
for (i=0; i<numsectors; i++)
{
YAX_SKIPSECTOR(i);
numjoincandidates += (inside_editor_curpos(i) == 1);
}
for (i=0; i<numsectors; i++)
{
YAX_SKIPSECTOR(i);
if (inside_editor_curpos(i) == 1)
{
if (numjoincandidates > 1)
{
if (!bakframe_fillandfade(&origframe, i, "Use this as first joining sector? (Y/N)"))
continue;
}
joinsector[0] = i;
printmessage16("Join sector - press J again on sector to join with.");
break;
}
}
Bfree(origframe);
}
else
{
char *origframe = NULL;
int32_t numjoincandidates = 0;
joinsector[1] = -1;
for (i=0; i<numsectors; i++)
{
YAX_SKIPSECTOR(i);
numjoincandidates += (inside_editor_curpos(i) == 1);
}
for (i=0; i<numsectors; i++)
{
YAX_SKIPSECTOR(i);
if (inside_editor_curpos(i) == 1)
{
if (numjoincandidates > 1)
{
if (!bakframe_fillandfade(&origframe, i, "Use this as second joining sector? (Y/N)"))
continue;
DO_FREE_AND_NULL(origframe);
}
joinsector[1] = i;
const int s1to0wall = find_nextwall(i, joinsector[0]);
const int s0to1wall = s1to0wall == -1 ? -1 : wall[s1to0wall].nextwall;
#ifdef YAX_ENABLE
int16_t jbn[2][2]; // [join index][c/f]
for (k=0; k<2; k++)
yax_getbunches(joinsector[k], &jbn[k][YAX_CEILING], &jbn[k][YAX_FLOOR]);
#endif
// pressing J into the same sector is the same as saying 'no'
// v----------------v
if (s1to0wall == -1 && i != joinsector[0])
{
int32_t good = 1;
#ifdef YAX_ENABLE
if (jbn[0][0]>=0 || jbn[0][1]>=0 || jbn[1][0]>=0 || jbn[1][1]>=0)
{
message("Joining non-adjacent extended sectors not allowed!");
good = 0;
}
#endif
if (!m32_script_expertmode)
{
message("Joining non-adjacent disabled in non-expert mode");
good = 0;
}
if (!good)
{
joinsector[0] = joinsector[1] = -1;
goto end_join_sectors;
}
{
fillsector_notrans(i, editorcolors[9]);
fillsector_notrans(joinsector[0], editorcolors[9]);
fade_editor_screen(editorcolors[9]);
if (!ask_if_sure("Really join non-adjacent sectors? (Y/N)", 0))
joinsector[1] = joinsector[0];
}
}
#ifdef YAX_ENABLE
// unequal bunchnums (bitmap): 1:above, 2:below
int uneqbn =
(jbn[0][YAX_CEILING] != jbn[1][YAX_CEILING]) |
((jbn[0][YAX_FLOOR] != jbn[1][YAX_FLOOR])<<1);
if (uneqbn)
{
const int32_t cf=YAX_FLOOR;
int32_t whybad=0;
if (uneqbn == 1)
{
OSD_Printf("Can't join two sectors with different ceiling bunchnums."
" To make them equal, join their upper neighbor's floors.\n");
printmessage16("Can't join two sectors with different ceiling bunchnums. See OSD");
joinsector[0] = joinsector[1] = -1;
goto end_join_sectors;
}
if (s0to1wall < 0)
{
printmessage16("INTERNAL ERROR: nextwalls inconsistent!");
joinsector[0] = joinsector[1] = -1;
goto end_join_sectors;
}
// both must be extended
if (jbn[0][cf]<0 || jbn[1][cf]<0)
uneqbn &= ~(1<<cf), whybad|=1;
// if any sloped, can't join
if ((SECTORFLD(joinsector[0],stat, cf)&2) || (SECTORFLD(joinsector[1],stat, cf)&2))
uneqbn &= ~(1<<cf), whybad|=2;
// if on unequal heights, can't join either
if (SECTORFLD(joinsector[0],z, cf) != SECTORFLD(joinsector[1],z, cf))
uneqbn &= ~(1<<cf), whybad|=4;
// check whether the lower neighbors have a red-wall link to each other
const int jsynw[2] = {
yax_getnextwall(s0to1wall, cf),
yax_getnextwall(s1to0wall, cf)
};
if (jsynw[0]<0 || jsynw[1]<0) // this shouldn't happen
{
uneqbn &= ~(1<<cf), whybad|=8;
}
else if (wall[jsynw[1]].nextwall != jsynw[0])
{
// if (find_nextwall(sectorofwall(jsynw[1]), sectorofwall(jsynw[0])) < 0)
uneqbn &= ~(1<<cf), whybad|=16;
}
if ((uneqbn&2)==0)
{
#if 0
if (whybad==1+8 && jbn[0][cf]>=0 && jbn[1][cf]<0)
{
// 1st join sector extended, 2nd not... let's see
// if the latter is inner to the former one
int32_t lowerstartsec = yax_vnextsec(s0to1wall, cf);
m = (lowerstartsec < 0)<<1;
for (WALLS_OF_SECTOR(joinsector[1], k))
{
if (m) break;
m |= (wall[k].nextsector>=0 && wall[k].nextsector != joinsector[0]);
m |= (wall[k].nextwall>=0 && yax_vnextsec(wall[k].nextwall, cf)!=lowerstartsec)<<1;
}
if (m==0)
{
yax_setbunch(joinsector[1], YAX_FLOOR, jbn[0][cf]);
yax_update(0);
yax_updategrays(pos.z);
asksave = 1;
printmessage16("Added sector %d's floor to bunch %d",
joinsector[1], jbn[0][cf]);
}
else
{
if (m&1)
{
message("Can't add sector %d's floor to bunch %d: not inner to sector %d",
joinsector[1], jbn[0][cf], joinsector[0]);
}
else // if (m&2)
{
message("Can't add sector %d's floor to bunch %d: must have lower neighbor",
joinsector[1], jbn[0][cf]);
}
}
}
else
#endif
{
if (whybad&1)
message("Can't make floor bunchnums equal: both floors must be extended");
else if (whybad&2)
message("Can't make floor bunchnums equal: both floors must be non-sloped");
else if (whybad&4)
message("Can't make floor bunchnums equal: both floors must have equal height");
else if (whybad&8)
message("Can't make floor bunchnums equal: INTERNAL ERROR");
else if (whybad&16)
message("Can't make floor bunchnums equal: lower neighbors must be linked");
}
}
else
{
int32_t vcf, newbn, ynw;
// we're good to go for making floor bunchnums equal
for (SECTORS_OF_BUNCH(jbn[1][cf], YAX_FLOOR, k))
yax_setbunch(k, YAX_FLOOR, jbn[0][cf]);
for (SECTORS_OF_BUNCH(jbn[1][cf], YAX_CEILING, k))
yax_setbunch(k, YAX_CEILING, jbn[0][cf]);
yax_update(0);
// now we can iterate the sectors with the new bunchnums
newbn = yax_getbunch(joinsector[0], cf);
// clear all yax-nextwall links on walls that are inside the bunch
for (vcf=0; vcf<2; vcf++)
for (SECTORS_OF_BUNCH(newbn, vcf, k))
for (WALLS_OF_SECTOR(k, m))
{
ynw = yax_getnextwall(m, vcf);
if (ynw < 0 || wall[m].nextsector < 0)
continue;
if (yax_getbunch(wall[m].nextsector, vcf) == newbn)
{
yax_setnextwall(ynw, !vcf, -1);
yax_setnextwall(m, vcf, -1);
}
}
// shouldn't be needed again for the editor, but can't harm either:
yax_update(0);
yax_updategrays(pos.z);
printmessage16("Made sector %d and %d floor bunchnums equal",
joinsector[0], joinsector[1]);
asksave = 1;
}
joinsector[0] = joinsector[1] = -1;
goto end_join_sectors;
}
#endif
break;
}
}
if (joinsector[1] < 0 || joinsector[0] == joinsector[1])
{
printmessage16("No sectors joined.");
joinsector[0] = -1;
goto end_join_sectors;
}
for (i=0; i<numwalls; i++)
wall[i].cstat &= ~(1<<14);
newnumwalls = numwalls;
for (k=0; k<2; k++)
{
for (WALLS_OF_SECTOR(joinsector[k], j))
{
int32_t loopnum=MAXWALLS*2;
if (wall[j].cstat & (1<<14))
continue;
if (wall[j].nextsector == joinsector[1-k])
{
wall[j].cstat |= (1<<14);
continue;
}
i = j;
m = newnumwalls;
int joink = k;
do
{
if (newnumwalls >= MAXWALLS + M32_FIXME_WALLS)
{
message("Joining sectors failed: not enough space beyond wall[]");
joinsector[0] = -1;
newnumwalls = -1;
for (i=0; i<numwalls; i++)
wall[i].cstat &= ~(1<<14);
goto end_join_sectors;
}
Bmemcpy(&wall[newnumwalls], &wall[i], sizeof(walltype));
#ifdef YAX_ENABLE
yax_fixreverselinks(newnumwalls, newnumwalls);
#endif
wall[newnumwalls].point2 = newnumwalls+1;
newnumwalls++;
wall[i].cstat |= (1<<14);
i = wall[i].point2;
if (wall[i].nextsector == joinsector[1-joink])
{
i = NEXTWALL(i).point2;
joink = 1 - joink;
}
loopnum--;
}
while (loopnum>0 && ((wall[i].cstat & (1<<14))==0)
&& (wall[i].nextsector != joinsector[1-joink]));
wall[newnumwalls-1].point2 = m;
if (loopnum==0)
{
message("internal error while joining sectors: infloop!");
newnumwalls = -1;
}
}
}
if (newnumwalls > numwalls)
{
Bmemcpy(&sector[numsectors], &sector[joinsector[0]], sizeof(sectortype));
sector[numsectors].wallptr = numwalls;
sector[numsectors].wallnum = newnumwalls-numwalls;
//fix sprites
for (i=0; i<2; i++)
{
j = headspritesect[joinsector[i]];
while (j != -1)
{
k = nextspritesect[j];
changespritesect(j, numsectors);
j = k;
}
}
numsectors++;
for (i=numwalls; i<newnumwalls; i++)
{
if (wall[i].nextwall >= 0)
{
NEXTWALL(i).nextwall = i;
NEXTWALL(i).nextsector = numsectors-1;
}
}
numwalls = newnumwalls;
newnumwalls = -1;
// clean out nextwall links for deletesector
for (k=0; k<2; k++)
for (WALLS_OF_SECTOR(joinsector[k], j))
{
wall[j].nextwall = wall[j].nextsector = -1;
#ifdef YAX_ENABLE
yax_setnextwall(j, YAX_CEILING, -1);
yax_setnextwall(j, YAX_FLOOR, -1);
#endif
}
deletesector(joinsector[0]);
if (joinsector[0] < joinsector[1])
joinsector[1]--;
deletesector(joinsector[1]);
printmessage16("Sectors joined.");
mkonwinvalid();
asksave = 1;
#ifdef YAX_ENABLE
yax_update(0);
yax_updategrays(pos.z);
#endif
}
joinsector[0] = -1;
}
}
end_join_sectors:
// PK
for (i=0x02; i<=0x0b; i++) // keys '1' to '0' on the upper row
if (keystatus[i])
{
prefixarg = prefixtiles[i-2];
break;
}
if (eitherALT && keystatus[sc_S]) //ALT-S
{
keystatus[sc_S] = 0;
if (linehighlight >= 0 && wall[linehighlight].nextwall == -1)
{
newnumwalls = whitelinescan(sectorofwall(linehighlight), linehighlight);
if (newnumwalls < numwalls)
{
printmessage16("Can't make a sector out there.");
newnumwalls = -1;
}
else if (newnumwalls > MAXWALLS)
{
printmessage16("Making new sector from inner loop would exceed wall limits.");
newnumwalls = -1;
}
else
{
for (i=numwalls; i<newnumwalls; i++)
{
NEXTWALL(i).nextwall = i;
NEXTWALL(i).nextsector = numsectors;
#ifdef YAX_ENABLE
yax_setnextwall(i, YAX_CEILING, -1);
yax_setnextwall(i, YAX_FLOOR, -1);
#endif
}
#ifdef YAX_ENABLE
yax_setbunches(numsectors, -1, -1);
yax_update(0);
yax_updategrays(pos.z);
#endif
numwalls = newnumwalls;
newnumwalls = -1;
numsectors++;
asksave = 1;
printmessage16("Inner loop made into new sector.");
}
}
}
else if (keystatus[sc_S]) //S
{
int16_t sucksect = -1;
keystatus[sc_S] = 0;
for (i=0; i<numsectors; i++)
{
YAX_SKIPSECTOR(i);
if (inside_editor_curpos(i) == 1)
{
sucksect = i;
break;
}
}
if (sucksect >= 0)
{
dax = mousxplc;
day = mousyplc;
if (gridlock && grid > 0)
locktogrid(&dax, &day);
i = insert_sprite_common(sucksect, dax, day);
if (i < 0)
printmessage16("Couldn't insert sprite.");
else
{
sprite[i].z = getflorzofslope(sucksect,dax,day);
// PK
if (prefixarg)
{
sprite[i].picnum = prefixarg;
sprite[i].xrepeat = sprite[i].yrepeat = 48;
prefixarg = 0;
}
else handle_sprite_in_clipboard(i);
if (tilesiz[sprite[i].picnum].y >= 32)
sprite[i].cstat |= 1;
correct_sprite_yoffset(i);
printmessage16("Sprite inserted.");
asksave = 1;
VM_OnEvent(EVENT_INSERTSPRITE2D, i);
}
}
}
if (keystatus[sc_C]) // C (make circle of points)
{
if (highlightsectorcnt > 0)
duplicate_selected_sectors();
else if (highlightcnt > 0)
duplicate_selected_sprites();
else if (circlewall >= 0)
{
circlewall = -1;
}
else if (!m32_sideview)
{
if (linehighlight >= 0)
{
#if 0 //def YAX_ENABLE
j = linehighlight;
if (yax_islockedwall(j) ||
(wall[j].nextwall >= 0 && yax_islockedwall(wall[j].nextwall)))
printmessage16("Can't make circle in wall constrained by sector extension.");
else
#endif
circlewall = linehighlight;
}
}
keystatus[sc_C] = 0;
}
bad = keystatus[sc_Space] && (!m32_sideview || m32_sideelev == 512); //Gotta do this to save lots of 3 spaces!
if (keystatus[sc_Space] && !bad)
message("Unable to create sectors in angled sideview mode.");
if (circlewall >= 0)
{
int32_t tempint1, tempint2;
x1 = wall[circlewall].x;
y1 = wall[circlewall].y;
x2 = POINT2(circlewall).x;
y2 = POINT2(circlewall).y;
x3 = mousxplc;
y3 = mousyplc;
adjustmark(&x3,&y3,newnumwalls);
tempint1 = dmulscale4(x3-x2,x1-x3, y1-y3,y3-y2);
tempint2 = dmulscale4(y1-y2,x1-x3, y1-y3,x2-x1);
if (tempint2 != 0)
{
int32_t goodtogo, err=0;
const int32_t centerx = ((x1+x2) + scale(y1-y2,tempint1,tempint2))>>1;
const int32_t centery = ((y1+y2) + scale(x2-x1,tempint1,tempint2))>>1;
const int32_t circlerad = ksqrt(dmulscale4(centerx-x1,centerx-x1, centery-y1,centery-y1))<<2;
const int32_t circleang1 = getangle(x1-centerx,y1-centery);
const int32_t circleang2 = getangle(x2-centerx,y2-centery);
const int32_t redw = (int32_t)(wall[circlewall].nextwall >= 0);
int32_t insdpoints = 0;
draw_cross(centerx, centery, 2, editorcolors[14]);
k = ((circleang2-circleang1)&2047);
if (mulscale4(x3-x1,y2-y1) < mulscale4(x2-x1,y3-y1))
{
k = -((circleang1-circleang2)&2047);
}
// XXX: Still too permissive for TROR insertion
goodtogo = (numwalls+(1+redw)*circlepoints <= MAXWALLS);
if (bad > 0 && goodtogo)
{
err = backup_drawn_walls(0);
if (err)
{
message("Error backing up drawn walls (code %d)!", err);
goodtogo = 0;
}
}
for (i=circlepoints; i>0; i--)
{
const int32_t ps = 2;
j = (circleang1 + scale(i,k,circlepoints+1))&2047;
dax = centerx + mulscale14(sintable[(j+512)&2047],circlerad);
day = centery + mulscale14(sintable[j],circlerad);
inpclamp(&dax, -editorgridextent, editorgridextent);
inpclamp(&day, -editorgridextent, editorgridextent);
if (bad > 0 && goodtogo)
{
int32_t inspts = M32_InsertPoint(circlewall, dax,day, -1, &circlewall);
if (inspts==0)
{
message("Wall limit exceeded while inserting points.");
goto end_circle_insertion;
}
else if (inspts >= 65536)
{
message("ERR: Inserted %d points for constr. wall (exp. %d; %d already ins'd)",
inspts&65535, inspts>>16, insdpoints);
goto end_circle_insertion;
}
insdpoints += inspts;
}
draw_square(dax, day, ps, editorcolors[14]);
}
if (bad > 0 && goodtogo)
backup_drawn_walls(1);
if (bad > 0)
{
if (goodtogo)
{
asksave = 1;
printmessage16("Circle points inserted.");
end_circle_insertion:
circlewall = -1;
mkonwinvalid();
}
else
printmessage16("Inserting circle points would exceed wall limit.");
}
}
bad = 0;
keystatus[sc_Space] = 0;
}
if (bad > 0) //Space bar test
{
keystatus[sc_Space] = 0;
adjustmark(&mousxplc,&mousyplc,newnumwalls);
if (checkautoinsert(mousxplc,mousyplc,newnumwalls) == 1)
{
printmessage16("You must insert a point there first.");
bad = 0;
}
}
if (bad > 0) //Space
{
if (newnumwalls < numwalls) // starting wall drawing
{
if (numwalls >= MAXWALLS-1)
{
// whatever we do, we will need at least two new walls
printmessage16("Can't start sector drawing: wall limit reached.");
goto end_space_handling;
}
if (numsectors >= MAXSECTORS)
{
printmessage16("Can't start sector drawing: sector limit reached.");
goto end_space_handling;
}
firstx = mousxplc;
firsty = mousyplc; //Make first point
newnumwalls = numwalls;
ovh.suckwall = -1;
ovh.split = 0;
init_new_wall1(&ovh.suckwall, mousxplc, mousyplc);
printmessage16("Sector drawing started.");
}
else // 2nd point and up...
{
//if not back to first point
if (firstx != mousxplc || firsty != mousyplc) //nextpoint
{
if (newnumwalls>=MAXWALLS)
{
printmessage16("Inserting another point would exceed wall limit.");
goto end_space_handling;
}
j = 0;
for (i=numwalls; i<newnumwalls; i++)
if (mousxplc == wall[i].x && mousyplc == wall[i].y)
j = 1;
if (j == 0) // if new point is not on a position of already drawn points
{
// on the second point insertion, check if starting to split a sector
if (newnumwalls == numwalls+1)
{
dax = (wall[numwalls].x+mousxplc)>>1;
day = (wall[numwalls].y+mousyplc)>>1;
for (i=0; i<numsectors; i++)
{
YAX_SKIPSECTOR(i);
if (inside(dax,day,i) != 1)
continue;
//check if first point at point of sector
m = -1;
for (WALLS_OF_SECTOR(i, k))
if (wall[k].x==wall[numwalls].x && wall[k].y==wall[numwalls].y)
{
YAX_SKIPWALL(k);
m = k;
break;
}
// if the second insertion is not on a neighboring point of the first one...
if (m>=0 && (POINT2(k).x != mousxplc || POINT2(k).y != mousyplc))
if (wall[lastwall(k)].x != mousxplc || wall[lastwall(k)].y != mousyplc)
{
ovh.split = 1;
ovh.splitsect = i;
ovh.splitstartwall = m;
break;
}
}
}
//make new point
//make sure not drawing over old red line
bad = 0;
for (i=0; i<numwalls; i++)
{
YAX_SKIPWALL(i);
if (wall[i].nextwall >= 0)
{
int32_t lastwalx = wall[newnumwalls-1].x;
int32_t lastwaly = wall[newnumwalls-1].y;
YAX_SKIPWALL(wall[i].nextwall);
if (wall[i].x == mousxplc && wall[i].y == mousyplc)
if (POINT2(i).x == lastwalx && POINT2(i).y == lastwaly)
bad = 1;
if (wall[i].x == lastwalx && wall[i].y == lastwaly)
if (POINT2(i).x == mousxplc && POINT2(i).y == mousyplc)
bad = 1;
}
}
if (bad == 0)
{
init_new_wall1(&ovh.suckwall, mousxplc, mousyplc);
}
else
{
printmessage16("You can't draw new lines over red lines.");
goto end_space_handling;
}
}
}
////////// newnumwalls is at most MAXWALLS here //////////
//if not split and back to first point
if (!ovh.split && newnumwalls >= numwalls+3
&& firstx==mousxplc && firsty==mousyplc)
{
wall[newnumwalls-1].point2 = numwalls;
if (ovh.suckwall == -1) //if no connections to other sectors
{
k = -1;
for (i=0; i<numsectors; i++)
{
YAX_SKIPSECTOR(i);
if (inside(firstx,firsty,i) == 1)
{
// if all points inside that one sector i,
// will add an inner loop
for (j=numwalls+1; j<newnumwalls; j++)
{
if (inside(wall[j].x, wall[j].y, i) != 1)
goto check_next_sector;
}
k = i;
break;
}
check_next_sector: ;
}
if (k == -1) //if not inside another sector either
{
//add island sector
if (clockdir(numwalls) == CLOCKDIR_CCW)
flipwalls(numwalls,newnumwalls);
Bmemset(&sector[numsectors], 0, sizeof(sectortype));
sector[numsectors].extra = -1;
sector[numsectors].wallptr = numwalls;
sector[numsectors].wallnum = newnumwalls-numwalls;
sector[numsectors].ceilingz = -(32<<8);
sector[numsectors].floorz = (32<<8);
for (i=numwalls; i<newnumwalls; i++)
copy_some_wall_members(i, -1, 1);
#ifdef YAX_ENABLE
yax_setbunches(numsectors, -1, -1);
#endif
headspritesect[numsectors] = -1;
numsectors++;
numwalls = newnumwalls;
newnumwalls = -1;
printmessage16("Created new sector %d", numsectors-1);
}
else //else add loop to sector
{
int32_t ret = AddLoopToSector(k, NULL);
if (ret < 0)
goto end_space_handling;
#ifdef YAX_ENABLE
else if (ret > 0)
printmessage16("Added inner loop to sector %d and made new inner sector", k);
else
#endif
printmessage16("Added inner loop to sector %d", k);
mkonwinvalid();
asksave = 1;
}
}
else // if connected to at least one other sector
{
int16_t sucksect;
//add new sector with connections
if (clockdir(numwalls) == CLOCKDIR_CCW)
flipwalls(numwalls,newnumwalls);
for (i=numwalls; i<newnumwalls; i++)
{
copy_some_wall_members(i, ovh.suckwall, 1);
wall[i].cstat &= ~(1+16+32+64);
if (checksectorpointer(i, numsectors) > 0)
{
// if new red line, prefer the other-side wall as base
ovh.suckwall = wall[i].nextwall;
}
}
sucksect = sectorofwall(ovh.suckwall);
if (numsectors != sucksect)
Bmemcpy(&sector[numsectors], &sector[sucksect], sizeof(sectortype));
sector[numsectors].wallptr = numwalls;
sector[numsectors].wallnum = newnumwalls-numwalls;
sector[numsectors].extra = -1;
sector[numsectors].lotag = sector[numsectors].hitag = 0;
setslope(numsectors, YAX_CEILING, 0);
setslope(numsectors, YAX_FLOOR, 0);
sector[numsectors].ceilingpal = sector[numsectors].floorpal = 0;
#ifdef YAX_ENABLE
yax_setbunches(numsectors, -1, -1);
#endif
headspritesect[numsectors] = -1;
numsectors++;
numwalls = newnumwalls;
newnumwalls = -1;
message("Created new sector %d based on sector %d", numsectors-1, sucksect);
}
asksave = 1;
#ifdef YAX_ENABLE
yax_update(0);
yax_updategrays(pos.z);
#endif
goto end_space_handling;
}
////////// split sector //////////
else if (ovh.split == 1)
{
int16_t danumwalls, splitendwall, doSectorSplit;
int16_t secondstartwall=-1; // used only with splitting
int32_t expectedNumwalls = numwalls+2*(newnumwalls-numwalls-1), loopnum;
int32_t firstwallflag;
#ifdef YAX_ENABLE
int16_t cb, fb;
#endif
startwall = sector[ovh.splitsect].wallptr;
endwall = startwall + sector[ovh.splitsect].wallnum - 1;
firstwallflag = (startwall==ovh.splitstartwall || startwall==lastwall(ovh.splitstartwall));
// OSD_Printf("numwalls: %d, newnumwalls: %d\n", numwalls, newnumwalls);
i = -1;
for (k=startwall; k<=endwall; k++)
if (wall[k].x == wall[newnumwalls-1].x && wall[k].y == wall[newnumwalls-1].y)
{
i = k;
break;
}
// vvvv shouldn't happen, but you never know...
if (i==-1 || k==ovh.splitstartwall)
goto end_space_handling;
splitendwall = k;
doSectorSplit = (loopnumofsector(ovh.splitsect,ovh.splitstartwall)
== loopnumofsector(ovh.splitsect,splitendwall));
if (expectedNumwalls > MAXWALLS)
{
printmessage16("%s would exceed wall limit.", bad==0 ?
"Splitting sector" : "Joining sector loops");
newnumwalls--;
goto end_space_handling;
}
#ifdef YAX_ENABLE
yax_getbunches(ovh.splitsect, &cb, &fb);
if ((cb>=0 && (sector[ovh.splitsect].ceilingstat&2))
|| (fb>=0 && (sector[ovh.splitsect].floorstat&2)))
{
printmessage16("Sloped extended sectors cannot be split.");
newnumwalls--;
goto end_space_handling;
}
#endif
////////// common code for splitting/loop joining //////////
newnumwalls--; //first fix up the new walls
for (i=numwalls; i<newnumwalls; i++)
{
copy_some_wall_members(i, startwall, 1);
wall[i].point2 = i+1;
}
danumwalls = newnumwalls; //where to add more walls
if (doSectorSplit)
{
// Copy outer loop of first sector
if (do_while_copyloop1(splitendwall, ovh.splitstartwall, &danumwalls, numwalls))
goto split_not_enough_walls;
//Add other loops for 1st sector
i = loopnum = loopnumofsector(ovh.splitsect,ovh.splitstartwall);
for (j=startwall; j<=endwall; j++)
{
k = loopnumofsector(ovh.splitsect,j);
if (k == i)
continue;
if (k == loopnum)
continue;
i = k;
if (loopinside(wall[j].x,wall[j].y, numwalls) != 1)
continue;
if (do_while_copyloop1(j, j, &danumwalls, danumwalls))
goto split_not_enough_walls;
}
secondstartwall = danumwalls;
}
else
{
if (do_while_copyloop1(splitendwall, splitendwall, &danumwalls, -1))
goto split_not_enough_walls;
}
//copy split points for other sector backwards
for (j=newnumwalls; j>numwalls; j--)
{
Bmemcpy(&wall[danumwalls], &wall[j], sizeof(walltype));
wall[danumwalls].nextwall = -1;
wall[danumwalls].nextsector = -1;
wall[danumwalls].point2 = danumwalls+1;
#ifdef YAX_ENABLE
yax_setnextwall(danumwalls,YAX_CEILING, -1);
yax_setnextwall(danumwalls,YAX_FLOOR, -1);
#endif
danumwalls++;
}
//copy rest of loop next
if (doSectorSplit)
{
if (do_while_copyloop1(ovh.splitstartwall, splitendwall, &danumwalls, secondstartwall))
goto split_not_enough_walls;
}
else
{
if (do_while_copyloop1(ovh.splitstartwall, ovh.splitstartwall, &danumwalls, numwalls))
goto split_not_enough_walls;
}
//Add other loops for 2nd sector
i = loopnum = loopnumofsector(ovh.splitsect,ovh.splitstartwall);
for (j=startwall; j<=endwall; j++)
{
k = loopnumofsector(ovh.splitsect, j);
if (k==i)
continue;
if (doSectorSplit && k==loopnum)
continue;
if (!doSectorSplit && (k == loopnumofsector(ovh.splitsect,ovh.splitstartwall) ||
k == loopnumofsector(ovh.splitsect,splitendwall)))
continue;
i = k;
// was loopinside(... , secondstartwall) != 1, but this way there are
// no duplicate or left-out loops (can happen with convoluted geometry)
if (doSectorSplit && (loopinside(wall[j].x,wall[j].y, numwalls) != 0))
continue;
if (do_while_copyloop1(j, j, &danumwalls, danumwalls))
goto split_not_enough_walls;
}
//fix all next pointers on old sector line
for (j=numwalls; j<danumwalls; j++)
{
#ifdef YAX_ENABLE
// if (doSectorSplit || (j!=numwalls && j!=danumwalls-1))
yax_fixreverselinks(j, j);
#endif
if (wall[j].nextwall >= 0)
{
NEXTWALL(j).nextwall = j;
if (!doSectorSplit || j < secondstartwall)
NEXTWALL(j).nextsector = numsectors;
else
NEXTWALL(j).nextsector = numsectors+1;
}
}
//copy sector attributes & fix wall pointers
Bmemcpy(&sector[numsectors], &sector[ovh.splitsect], sizeof(sectortype));
sector[numsectors].wallptr = numwalls;
sector[numsectors].wallnum = (doSectorSplit?secondstartwall:danumwalls) - numwalls;
if (doSectorSplit)
{
//set all next pointers on split
for (j=numwalls; j<newnumwalls; j++)
{
m = secondstartwall+(newnumwalls-1-j);
wall[j].nextwall = m;
wall[j].nextsector = numsectors+1;
wall[m].nextwall = j;
wall[m].nextsector = numsectors;
}
Bmemcpy(&sector[numsectors+1], &sector[ovh.splitsect], sizeof(sectortype));
sector[numsectors+1].wallptr = secondstartwall;
sector[numsectors+1].wallnum = danumwalls-secondstartwall;
}
//fix sprites
j = headspritesect[ovh.splitsect];
while (j != -1)
{
k = nextspritesect[j];
if (!doSectorSplit || loopinside(sprite[j].x,sprite[j].y,numwalls) == 1)
changespritesect(j,numsectors);
//else if (loopinside(sprite[j].x,sprite[j].y,secondstartwall) == 1)
else //Make sure no sprites get left out & deleted!
changespritesect(j,numsectors+1);
j = k;
}
numsectors += 1 + doSectorSplit;
k = danumwalls-numwalls; //Back up number of walls of new sector for later
numwalls = danumwalls;
//clear out old sector's next pointers for clean deletesector
for (j=startwall; j<=endwall; j++)
{
#ifdef YAX_ENABLE
// same thing for yax-nextwalls (only forward links!)
yax_setnextwall(j, YAX_CEILING, -1);
yax_setnextwall(j, YAX_FLOOR, -1);
#endif
wall[j].nextwall = wall[j].nextsector = -1;
}
deletesector(ovh.splitsect);
//Check pointers
for (j=numwalls-k; j<numwalls; j++)
{
if (wall[j].nextwall >= 0)
checksectorpointer(wall[j].nextwall, wall[j].nextsector);
checksectorpointer(j, sectorofwall(j));
}
//k now safe to use as temp
if (numwalls==expectedNumwalls)
{
if (doSectorSplit && cb<0 && fb<0)
{
if (firstwallflag)
{
int32_t rhsnew1stwall = sector[numsectors-2].wallptr;
int32_t lhsotherwall = wall[rhsnew1stwall].nextwall;
Bassert(lhsotherwall >= 0);
setfirstwall(numsectors-2, lastwall(rhsnew1stwall));
setfirstwall(numsectors-1, wall[lhsotherwall].point2);
}
}
message("%s", doSectorSplit ? "Sector split." : "Loops joined.");
}
else
{
message("%s WARNING: CREATED %d MORE WALLS THAN EXPECTED!",
doSectorSplit ? "Sector split." : "Loops joined.",
numwalls-expectedNumwalls);
// this would display 'num* out of bounds' but this corruption
// is almost as bad... (shouldn't happen anymore)
if (numcorruptthings < MAXCORRUPTTHINGS)
corruptthings[numcorruptthings++] = 0;
corruptlevel = 5;
}
if (0)
{
split_not_enough_walls:
message("%s failed: not enough space beyond wall[]",
doSectorSplit ? "Splitting sectors" : "Joining loops");
}
newnumwalls = -1;
asksave = 1;
mkonwinvalid();
#ifdef YAX_ENABLE
yax_update(0);
yax_updategrays(pos.z);
#endif
}
}
}
end_space_handling:
if (keystatus[sc_Enter]) //Left Enter
{
keystatus[sc_Enter] = 0;
if (keystatus[sc_LeftShift] && keystatus[sc_LeftControl]) // LCtrl+LShift
{
#ifdef YAX_ENABLE
if (numyaxbunches == 0 ||
(fade_editor_screen(-1), ask_if_sure("Really check all wall pointers in TROR map?", 0)))
#endif
{
printmessage16("CHECKING ALL POINTERS!");
for (i=0; i<numsectors; i++)
{
startwall = sector[i].wallptr;
for (j=startwall; j<numwalls; j++)
if (startwall > wall[j].point2)
startwall = wall[j].point2;
sector[i].wallptr = startwall;
}
for (i=numsectors-2; i>=0; i--)
sector[i].wallnum = sector[i+1].wallptr-sector[i].wallptr;
sector[numsectors-1].wallnum = numwalls-sector[numsectors-1].wallptr;
for (i=0; i<numwalls; i++)
{
wall[i].nextsector = -1;
wall[i].nextwall = -1;
}
for (i=0; i<numsectors; i++)
{
for (WALLS_OF_SECTOR(i, j))
checksectorpointer(j, i);
}
printmessage16("ALL POINTERS CHECKED!");
asksave = 1;
}
}
else // NOT (LCtrl + LShift)
{
if (newnumwalls > numwalls) // batch insert points
{
const int32_t numdrawnwalls = newnumwalls-numwalls;
vec2_t *point = (vec2_t *)tempxyar; // [MAXWALLS][2]
int32_t insdpoints = 0;
// back up the points of the line strip
for (i=0; i<numdrawnwalls+1; i++)
{
point[i].x = wall[numwalls+i].x;
point[i].y = wall[numwalls+i].y;
}
newnumwalls = -1;
for (i=0; i<numdrawnwalls; i++)
{
char touchedwall[(MAXWALLS+7)>>3];
Bmemset(touchedwall, 0, sizeof(touchedwall));
for (j=numwalls-1; j>=0; j--) /* j may be modified in loop */
{
YAX_SKIPWALL(j);
if ((touchedwall[j >> 3] & pow2char[j & 7])
|| (wall[j].nextwall >= 0 && (touchedwall[wall[j].nextwall >> 3] & pow2char[wall[j].nextwall & 7])))
continue;
vec2_t pint;
if (!lineintersect2v((vec2_t *)&wall[j], (vec2_t *)&POINT2(j),
&point[i], &point[i+1], &pint))
continue;
if (vec2eq(&pint, (vec2_t *)&wall[j]) || vec2eq(&pint, (vec2_t *)&POINT2(j)))
continue;
touchedwall[j>>3] |= (1<<(j&7));
if (wall[j].nextwall != -1)
touchedwall[wall[j].nextwall>>3] |= (1<<(wall[j].nextwall&7));
int32_t inspts = M32_InsertPoint(j, pint.x, pint.y, -1, &j); /* maybe modify j */
if (inspts==0)
{
printmessage16("Wall limit exceeded while inserting points.");
goto end_batch_insert_points;
}
else if (inspts >= 65536)
{
message("ERR: Inserted %d points for constr. wall (exp. %d; %d already ins'd)",
inspts&65535, inspts>>16, insdpoints);
goto end_batch_insert_points;
}
insdpoints += inspts;
}
}
message("Batch-inserted %d points in total", insdpoints);
end_batch_insert_points:
if (insdpoints != 0)
{
#ifdef YAX_ENABLE
yax_updategrays(pos.z);
#endif
mkonwinvalid_keeptempsect();
asksave = 1;
}
}
else if (linehighlight >= 0)
{
checksectorpointer(linehighlight,sectorofwall(linehighlight));
printmessage16("Checked pointers of highlighted line.");
asksave = 1;
}
}
}
static int32_t backspace_last = 0;
if (keystatus[sc_BackSpace]) //Backspace
{
keystatus[sc_BackSpace] = 0;
if (newnumwalls >= numwalls)
{
backspace_last = 1;
if (newnumwalls == numwalls+1 || keystatus[sc_LeftControl]) // LCtrl: delete all newly drawn walls
newnumwalls = -1;
else
newnumwalls--;
}
else if (backspace_last==0)
{
graphicsmode += (1-2*(DOWN_BK(RUN) || keystatus[sc_RightShift]))+3;
graphicsmode %= 3;
printmessage16("2D mode textures %s",
(graphicsmode == 2)?"enabled w/ animation":graphicsmode?"enabled":"disabled");
}
}
else
backspace_last = 0;
if (keystatus[sc_Delete] && eitherCTRL && numwalls > 0) //sector delete
{
int32_t numdelsectors = 0;
char *origframe=NULL;
#ifdef YAX_ENABLE
int16_t cb, fb;
uint8_t bunchbitmap[YAX_MAXBUNCHES>>3];
Bmemset(bunchbitmap, 0, sizeof(bunchbitmap));
#endif
keystatus[sc_Delete] = 0;
for (i=0; i<numsectors; i++)
{
YAX_SKIPSECTOR(i);
numdelsectors += (inside_editor_curpos(i) == 1);
}
for (i=0; i<numsectors; i++)
{
if (highlightsectorcnt <= 0 || !keystatus[sc_LeftShift]) // LShift
{
YAX_SKIPSECTOR(i);
if (inside_editor_curpos(i) != 1)
continue;
}
k = 0;
if (highlightsectorcnt > 0)
{
// LShift: force highlighted sector deleting
if (keystatus[sc_LeftShift] || (hlsectorbitmap[i>>3]&(1<<(i&7))))
{
for (j=highlightsectorcnt-1; j>=0; j--)
{
#ifdef YAX_ENABLE
yax_getbunches(highlightsector[j], &cb, &fb);
if (cb>=0) bunchbitmap[cb>>3] |= (1<<(cb&7));
if (fb>=0) bunchbitmap[fb>>3] |= (1<<(fb&7));
#endif
deletesector(highlightsector[j]);
for (k=j-1; k>=0; k--)
if (highlightsector[k] >= highlightsector[j])
highlightsector[k]--;
}
printmessage16("Highlighted sectors deleted.");
mkonwinvalid();
k = 1;
}
}
if (k == 0)
{
if (numdelsectors > 1)
{
if (!bakframe_fillandfade(&origframe, i, "Delete this sector? (Y/N)"))
continue;
}
#ifdef YAX_ENABLE
yax_getbunches(i, &cb, &fb);
if (cb>=0) bunchbitmap[cb>>3] |= (1<<(cb&7));
if (fb>=0) bunchbitmap[fb>>3] |= (1<<(fb&7));
#endif
deletesector(i);
mkonwinvalid();
printmessage16("Sector deleted.");
}
Bfree(origframe);
#ifdef YAX_ENABLE
for (j=0; j<numsectors; j++)
{
yax_getbunches(j, &cb, &fb);
if (cb>=0 && (bunchbitmap[cb>>3] & (1<<(cb&7))))
yax_setbunch(j, YAX_CEILING, -1);
if (fb>=0 && (bunchbitmap[fb>>3] & (1<<(fb&7))))
yax_setbunch(j, YAX_FLOOR, -1);
}
#endif
reset_highlightsector();
reset_highlight();
newnumwalls = -1;
asksave = 1;
#ifdef YAX_ENABLE
yax_update(0);
yax_updategrays(pos.z);
#endif
break;
}
}
if (keystatus[sc_Delete] && (pointhighlight >= 0))
{
if ((pointhighlight&0xc000) == 16384) //Sprite Delete
{
deletesprite(pointhighlight&16383);
printmessage16("Sprite deleted.");
update_highlight();
asksave = 1;
}
keystatus[sc_Delete] = 0;
}
if (keystatus[sc_Insert] || keystatus[sc_I]) //InsertPoint
{
if (highlightsectorcnt > 0)
duplicate_selected_sectors();
else if (highlightcnt > 0)
duplicate_selected_sprites();
else if (linehighlight2 >= 0)
{
int32_t onewnumwalls = newnumwalls;
int32_t wallis2sided = (wall[linehighlight2].nextwall>=0);
int32_t err = backup_drawn_walls(0);
if (err)
{
message("Error backing up drawn walls (code %d)!", err);
}
else if (max(numwalls,onewnumwalls) >= MAXWALLS-wallis2sided)
{
printmessage16("Inserting point would exceed wall limit.");
}
else
{
getclosestpointonwall(m32_sideview?searchx:mousxplc, m32_sideview?searchy:mousyplc,
linehighlight2, &dax,&day, 1);
i = linehighlight2;
if (m32_sideview)
{
int32_t y_p, d, dx, dy, frac;
dx = dax - m32_wallscreenxy[i][0];
dy = day - m32_wallscreenxy[i][1];
d = max(dx, dy);
y_p = (dy>dx);
if (d==0)
goto point_not_inserted;
frac = divscale24(d, m32_wallscreenxy[wall[i].point2][y_p]-m32_wallscreenxy[i][y_p]);
dax = POINT2(i).x - wall[i].x;
day = POINT2(i).y - wall[i].y;
dax = wall[i].x + mulscale24(dax,frac);
day = wall[i].y + mulscale24(day,frac);
}
adjustmark(&dax,&day, newnumwalls);
if ((wall[i].x == dax && wall[i].y == day) || (POINT2(i).x == dax && POINT2(i).y == day))
{
point_not_inserted:
printmessage16("Point not inserted.");
}
else
{
int32_t insdpoints = M32_InsertPoint(linehighlight2, dax, day, onewnumwalls, NULL);
if (insdpoints == 0)
{
printmessage16("Inserting points would exceed wall limit.");
goto end_insert_points;
}
else if (insdpoints == 1)
{
printmessage16("Point inserted.");
}
else if (insdpoints > 1 && insdpoints < 65536)
{
message("Inserted %d points for constrained wall.", insdpoints);
}
else // insdpoints >= 65536
{
message("Inserted %d points for constrained wall (expected %d, WTF?).",
insdpoints&65535, insdpoints>>16);
}
#ifdef YAX_ENABLE
yax_updategrays(pos.z);
#endif
mkonwinvalid_keeptempsect();
}
}
end_insert_points:
backup_drawn_walls(1);
asksave = 1;
}
keystatus[sc_Insert] = keystatus[sc_I] = 0;
}
/*j = 0;
for(i=22-1;i>=0;i--) updatecrc16(j,kensig[i]);
if ((j&0xffff) != 0xebf)
{
printf("Don't screw with my name.\n");
Bexit(0);
}*/
//printext16(9L,336+9L,4,-1,kensig,0);
//printext16(8L,336+8L,12,-1,kensig,0);
nokeys:
videoShowFrame(1);
synctics = totalclock-lockclock;
lockclock += synctics;
if (keystatus[buildkeys[BK_MODE2D_3D]])
{
updatesector(pos.x,pos.y,&cursectnum);
if (cursectnum >= 0)
keystatus[buildkeys[BK_MODE2D_3D]] = 2;
else
printmessage16("Arrow must be inside a sector before entering 3D mode.");
}
// vvv PK ------------------------------------ (LShift) Ctrl-X: (prev) next map
// this is copied from 'L' (load map), but without copying the highlighted sectors
if (quickmapcycling && keystatus[sc_X]) //X
{
if (eitherCTRL) //Ctrl
{
if (asksave)
message("You have unsaved changes.");
else
{
int skip = 0;
nextmap:
// bad = 0;
i = menuselect_auto(keystatus[sc_LeftShift] ? 0 : 1, skip); // LShift: prev map
if (i < 0)
{
if (i == -1)
message("No more map files.");
else if (i == -2)
message("No .MAP files found.");
}
else
{
if (LoadBoard(NULL, 4))
{
skip = 2;
goto nextmap;
}
RESET_EDITOR_VARS();
oposz = pos.z;
}
videoShowFrame(1);
keystatus[sc_Enter] = 0;
keystatus[sc_X] = 0;
keystatus[sc_R] = 0;
}
}
}
// ^^^ PK ------------------------------------
if (keystatus[sc_Escape] && joinsector[0] >= 0)
{
keystatus[sc_Escape]=0;
joinsector[0]=-1;
printmessage16("No sectors joined.");
}
CANCEL:
if (keystatus[sc_Escape])
{
keystatus[sc_Escape] = 0;
#if M32_UNDO
_printmessage16("(N)ew, (L)oad, (S)ave, save (A)s, (T)est map, (U)ndo, (R)edo, (Q)uit");
#else
_printmessage16("(N)ew, (L)oad, (S)ave, save (A)s, (T)est map, (Q)uit");
#endif
printext16(16*8, ydim-STATUS2DSIZ2-12, editorcolors[15], -1, GetSaveBoardFilename(NULL), 0);
videoShowFrame(1);
keyFlushChars();
bad = 1;
while (bad == 1)
{
char ch;
if (handleevents())
{
if (quitevent)
quitevent = 0;
}
idle();
ch = keyGetChar();
if (keystatus[sc_Escape])
{
keystatus[sc_Escape] = 0;
bad = 0;
// printmessage16("");
}
else if (ch == 'n' || ch == 'N') //N
{
bad = 0;
if (ask_if_sure("Are you sure you want to start a new board? (Y/N)", 0))
{
int32_t bakstat=-1;
mapinfofull_t bakmap;
if (highlightsectorcnt > 0)
bakstat = backup_highlighted_map(&bakmap);
// Bmemset(hlsectorbitmap, 0, sizeof(hlsectorbitmap));
// highlightsectorcnt = -1;
highlightcnt = -1;
//Clear all highlights
Bmemset(show2dwall, 0, sizeof(show2dwall));
Bmemset(show2dsprite, 0, sizeof(show2dsprite));
for (i=0; i<MAXSECTORS; i++) sector[i].extra = -1;
for (i=0; i<MAXWALLS; i++) wall[i].extra = -1;
for (i=0; i<MAXSPRITES; i++) sprite[i].extra = -1;
RESET_EDITOR_VARS();
mkonwinvalid();
reset_default_mapstate();
Bstrcpy(boardfilename,"newboard.map");
CallExtLoadMap(boardfilename);
#if M32_UNDO
map_undoredo_free();
#endif
if (bakstat==0)
{
bakstat = restore_highlighted_map(&bakmap, 1);
if (bakstat == -1)
{
message("Can't copy highlighted portion of old map: limits exceeded.");
reset_highlightsector();
}
}
CheckMapCorruption(4, 0);
}
break;
}
else if (ch == 'l' || ch == 'L') //L
{
bad = 0;
i = menuselect();
if (i < 0)
{
if (i == -2)
printmessage16("No .MAP files found.");
}
else
{
int32_t bakstat=-1, ret;
mapinfofull_t bakmap;
if (highlightsectorcnt > 0)
bakstat = backup_highlighted_map(&bakmap);
ret = LoadBoard(NULL, 4);
if (ret)
{
message("^13Invalid map format, nothing loaded (code %d).", ret);
if (bakstat==0)
mapinfofull_free(&bakmap);
}
else
{
RESET_EDITOR_VARS();
oposz = pos.z;
if (bakstat==0)
{
bakstat = restore_highlighted_map(&bakmap, 1);
if (bakstat == -1)
{
message("Can't copy highlighted portion of old map: limits exceeded.");
reset_highlightsector();
}
}
}
}
videoShowFrame(1);
keystatus[sc_Enter] = 0;
}
else if (ch == 'a' || ch == 'A') //A
{
int32_t corrupt = CheckMapCorruption(4, 0);
bad = 0;
// Back up full name.
Bstrcpy(selectedboardfilename, boardfilename);
// Get base name.
{
const char *basefn = getbasefn(boardfilename);
if (basefn != boardfilename)
Bmemmove(boardfilename, basefn, Bstrlen(basefn)+1);
}
i = 0;
while (boardfilename[i] != 0 && i < 64)
i++;
if (i >= 4 && boardfilename[i-4] == '.')
i -= 4;
boardfilename[i] = 0;
keyFlushChars();
while (bad == 0)
{
_printmessage16("%sSave as: ^011%s%s", corrupt>=4?"(map corrupt) ":"",
boardfilename, (totalclock&32)?"_":"");
videoShowFrame(1);
if (handleevents())
quitevent = 0;
idle();
ch = keyGetChar();
if (keystatus[sc_Escape]) bad = 1;
else if (ch == 13) bad = 2;
else if (ch > 0)
{
if (i > 0 && (ch == 8 || ch == 127))
{
i--;
boardfilename[i] = 0;
}
else if (i < 40 && ch > 32 && ch < 128)
{
boardfilename[i++] = ch;
boardfilename[i] = 0;
}
}
}
if (bad == 1)
{
Bstrcpy(boardfilename, selectedboardfilename);
keystatus[sc_Escape] = 0;
printmessage16("Operation cancelled");
videoShowFrame(1);
}
else if (bad == 2)
{
char *slash;
keystatus[sc_Enter] = 0;
Bstrcpy(&boardfilename[i], ".map");
// Update full name with new basename.
slash = Bstrrchr(selectedboardfilename,'/');
Bstrcpy(slash ? slash+1 : selectedboardfilename, boardfilename);
SaveBoardAndPrintMessage(selectedboardfilename);
Bstrcpy(boardfilename, selectedboardfilename);
CallExtSetupMapFilename(boardfilename);
}
bad = 0;
}
else if (ch == 's' || ch == 'S') //S
{
bad = 0;
if (CheckMapCorruption(4, 0)>=4)
{
fade_editor_screen(-1);
if (!ask_if_sure("Map is corrupt. Are you sure you want to save? (Y/N)", 0))
break;
}
SaveBoardAndPrintMessage(NULL);
videoShowFrame(1);
}
else if (ch == 't' || ch == 'T')
{
bad = 0;
test_map(0);
}
#if M32_UNDO
else if (ch == 'u' || ch == 'U')
{
bad = 0;
if (map_undoredo(0)) printmessage16("Nothing to undo!");
else printmessage16("Revision %d undone",map_revision);
}
else if (ch == 'r' || ch == 'R')
{
bad = 0;
if (map_undoredo(1)) printmessage16("Nothing to redo!");
else printmessage16("Restored revision %d",map_revision-1);
}
#endif
else if (ch == 'q' || ch == 'Q') //Q
{
bad = 0;
if (ask_if_sure("Are you sure you want to quit?", 0))
{
//QUIT!
int32_t corrupt = CheckMapCorruption(4, 0);
if (ask_if_sure(corrupt<4?"Save changes?":"Map corrupt. Save changes?", 2+(corrupt>=4)))
SaveBoard(NULL, M32_SB_ASKOV);
while (keystatus[sc_Escape] || keystatus[sc_C])
{
keystatus[sc_Escape] = 0;
keystatus[sc_C] = 0;
quitevent = 0;
printmessage16("Operation cancelled");
videoShowFrame(1);
goto CANCEL;
}
CallExtUnInit();
// clearfilenames();
engineUnInit();
Bexit(0);
}
// printmessage16("");
videoShowFrame(1);
}
}
clearkeys();
}
VM_OnEvent(EVENT_KEYS2D, -1);
//nextpage();
}
for (i=0; i<highlightsectorcnt; i++)
{
for (WALLS_OF_SECTOR(highlightsector[i], j))
{
if (wall[j].nextwall >= 0)
checksectorpointer(wall[j].nextwall, wall[j].nextsector);
checksectorpointer(j, highlightsector[i]);
}
}
mkonwinvalid_keeptempsect();
fixspritesectors();
if (videoSetGameMode(fullscreen,xdimgame,ydimgame,bppgame,upscalefactor) < 0)
{
initprintf("%d * %d not supported in this graphics mode\n",xdim,ydim);
CallExtUnInit();
// clearfilenames();
engineUnInit();
Bexit(1);
}
videoSetPalette(GAMMA_CALC,0,0);
pos.z = oposz;
searchx = clamp(scale(searchx,xdimgame,xdim2d), 8, xdimgame-8-1);
searchy = clamp(scale(searchy,ydimgame,ydim2d-STATUS2DSIZ), 8, ydimgame-8-1);
VM_OnEvent(EVENT_ENTER3DMODE, -1);
}
// flags:
// 1: quit_is_yes
// 2: don't clear keys on return
int32_t ask_if_sure(const char *query, uint32_t flags)
{
char ch;
int32_t ret=-1;
if (!query)
_printmessage16("Are you sure?");
else
_printmessage16("%s", query);
videoShowFrame(1);
keyFlushChars();
while ((keystatus[sc_Escape]|keystatus[sc_C]) == 0 && ret==-1)
{
if (handleevents())
{
if (quitevent)
{
if (flags&1)
return 1;
else
quitevent = 0;
}
}
idle();
ch = keyGetChar();
if (ch == 'y' || ch == 'Y')
ret = 1;
else if (ch == 'n' || ch == 'N' || ch == 13 || ch == ' ')
ret = 0;
}
if ((flags&2)==0)
clearkeys();
if (ret >= 0)
return ret;
return 0;
}
int32_t editor_ask_function(const char *question, const char *dachars, int32_t numchars)
{
char ch;
int32_t i, ret=-1;
_printmessage16("%s", question);
videoShowFrame(1);
keyFlushChars();
// 'c' is cancel too, but can be overridden
while ((keystatus[sc_Escape]|keystatus[sc_C]) == 0 && ret==-1)
{
if (handleevents())
quitevent = 0;
idle();
ch = keyGetChar();
for (i=0; i<numchars; i++)
if (ch==Btolower(dachars[i]) || ch==Btoupper(dachars[i]))
ret = i;
}
clearkeys();
return ret;
}
#ifdef YAX_ENABLE
static int32_t ask_above_or_below(void)
{
char dachars[2] = {'a', 'z'};
return editor_ask_function("Extend above (a) or below (z)?", dachars, 2);
}
#endif
static void SaveBoardAndPrintMessage(const char *fn)
{
const char *f;
_printmessage16("Saving board...");
videoShowFrame(1);
f = SaveBoard(fn, M32_SB_ASKOV);
if (f)
{
if (saveboard_fixedsprites)
message("Saved board %sto %s (changed sectnums of %d sprites).",
saveboard_savedtags?"and tags ":"", f, saveboard_fixedsprites);
else
message("Saved board %sto %s.", saveboard_savedtags?"and tags ":"", f);
}
else
{
if (!saveboard_canceled)
{
if (saveboard_fixedsprites)
message("^13SAVING BOARD FAILED (changed sectnums of %d sprites).",
saveboard_fixedsprites);
else
message("^13SAVING BOARD FAILED.");
}
}
}
// get the file name of the file that would be written if SaveBoard(fn, 0) was called
const char *GetSaveBoardFilename(const char *fn)
{
if (!fn)
fn = boardfilename;
if (pathsearchmode)
return fn;
// virtual filesystem mode can't save to directories so drop the file into
// the current directory
return getbasefn(fn);
}
// flags: see enum SaveBoardFlags.
// returns: NULL on failure, file name on success.
const char *SaveBoard(const char *fn, uint32_t flags)
{
int32_t ret;
const char *f = GetSaveBoardFilename(fn);
saveboard_canceled = 0;
#ifdef NEW_MAP_FORMAT
if ((flags&M32_SB_ASKOV) && mapversion>=10 &&
g_loadedMapVersion != -1 && g_loadedMapVersion < mapversion)
{
char question[128];
// XXX: This message is potentially confusing if the user is "Saving
// As" to a new file name.
Bsnprintf(question, sizeof(question), "Are you sure to overwrite a version "
"V%d map with a V%d map-text one?", g_loadedMapVersion, mapversion);
if (AskIfSure(question))
{
message("Cancelled saving board");
saveboard_canceled = 1;
return NULL;
}
}
#endif
saveboard_savedtags = 0;
saveboard_fixedsprites = CallExtPreSaveMap();
ret = saveboard(f, &startpos, startang, startsectnum);
if ((flags&M32_SB_NOEXT)==0)
{
CallExtSaveMap(f);
saveboard_savedtags = !taglab_save(f);
}
return (ret==0) ? f : NULL;
}
// flags: 1: for running on Mapster32 init
// 4: passed to loadboard flags (no polymer_loadboard); implies no maphack loading
// returns:
// 0 on success,
// <0 on failure.
int32_t LoadBoard(const char *filename, uint32_t flags)
{
int32_t i, tagstat;
const int32_t loadingflags = (!pathsearchmode && grponlymode) ? 2 : 0;
if (!filename)
filename = selectedboardfilename;
editorzrange[0] = INT32_MIN;
editorzrange[1] = INT32_MAX;
CallExtPreLoadMap();
i = engineLoadBoard(filename, (flags&4)|loadingflags, &pos, &ang, &cursectnum);
if (i == -2)
i = engineLoadBoardV5V6(filename,loadingflags, &pos, &ang, &cursectnum);
if (i < 0)
{
// printmessage16("Invalid map format.");
return i;
}
// Success, so copy the file name.
if (filename != boardfilename)
Bstrcpy(boardfilename, filename);
mkonwinvalid();
highlightcnt = -1;
Bmemset(show2dwall, 0, sizeof(show2dwall)); //Clear all highlights
Bmemset(show2dsprite, 0, sizeof(show2dsprite));
if ((flags&4)==0)
loadmhk(0);
tagstat = taglab_load(boardfilename, loadingflags);
CallExtLoadMap(boardfilename);
{
char msgtail[64];
const int32_t ci = CheckMapCorruption(4, 0);
if (ci == 5)
Bstrcpy(msgtail, "^12(EXTREME corruption)");
else if (ci == 4)
Bstrcpy(msgtail, "^12(HEAVY corruption)");
else if (i > 0)
Bsprintf(msgtail, "^14(removed %d sprites)", i);
else if (ci >= 1 && ci < 4)
Bstrcpy(msgtail, "^14(moderate corruption)");
else
Bstrcpy(msgtail, "successfully");
message("Loaded V%d map %s%s %s", g_loadedMapVersion,
boardfilename, tagstat==0?" w/tags":"", msgtail);
}
startpos = pos; //this is same
startang = ang;
startsectnum = cursectnum;
asksave = 0;
return 0;
}
void getpoint(int32_t searchxe, int32_t searchye, int32_t *x, int32_t *y)
{
inpclamp(&pos.x, -editorgridextent, editorgridextent);
inpclamp(&pos.y, -editorgridextent, editorgridextent);
searchxe -= halfxdim16;
searchye -= midydim16;
vec2_t svec = { searchxe ,searchye };
if (m32_sideview)
{
if (m32_sidesin!=0)
svec.y = divscale14(svec.y, m32_sidesin);
rotatepoint(zerovec, svec, -m32_sideang, &svec);
}
*x = pos.x + divscale14(svec.x,zoom);
*y = pos.y + divscale14(svec.y,zoom);
inpclamp(x, -editorgridextent, editorgridextent);
inpclamp(y, -editorgridextent, editorgridextent);
}
static int32_t getlinehighlight(int32_t xplc, int32_t yplc, int32_t line, int8_t ignore_pointhighlight)
{
int32_t i, j, dst, dist, closest, x1, y1, x2, y2, nx, ny;
int32_t daxplc, dayplc;
if (numwalls == 0)
return -1;
if (g_mouseBits & 1 || searchlock)
return line;
if (!ignore_pointhighlight && (pointhighlight&0xc000) == 16384)
return -1;
dist = linehighlightdist;
if (m32_sideview)
{
daxplc = searchx;
dayplc = searchy;
dist = mulscale14(dist, zoom);
}
else
{
daxplc = xplc;
dayplc = yplc;
}
closest = -1;
for (i=0; i<numwalls; i++)
{
if (!m32_sideview)
YAX_SKIPWALL(i);
getclosestpointonwall(daxplc,dayplc, i, &nx,&ny, 1);
dst = klabs(daxplc-nx) + klabs(dayplc-ny);
if (dst <= dist)
{
dist = dst;
closest = i;
}
}
if (closest>=0 && (j = wall[closest].nextwall) >= 0)
#ifdef YAX_ENABLE
if (m32_sideview || ((graywallbitmap[j>>3]&(1<<(j&7)))==0))
#endif
{
//if red line, allow highlighting of both sides
if (m32_sideview)
{
x1 = m32_wallscreenxy[closest][0];
y1 = m32_wallscreenxy[closest][1];
x2 = m32_wallscreenxy[wall[closest].point2][0];
y2 = m32_wallscreenxy[wall[closest].point2][1];
}
else
{
x1 = wall[closest].x;
y1 = wall[closest].y;
x2 = POINT2(closest).x;
y2 = POINT2(closest).y;
}
i = wall[closest].nextwall;
if (!m32_sideview ||
((B_UNBUF64(m32_wallscreenxy[closest]) == B_UNBUF64(m32_wallscreenxy[wall[j].point2])) &&
(B_UNBUF64(m32_wallscreenxy[wall[closest].point2]) == B_UNBUF64(m32_wallscreenxy[j]))))
if (dmulscale32(daxplc-x1,y2-y1,-(x2-x1),dayplc-y1) >= 0)
closest = j;
}
return closest;
}
int32_t getpointhighlight(int32_t xplc, int32_t yplc, int32_t point)
{
int32_t i, j, dst, dist = pointhighlightdist, closest = -1;
int32_t dax,day;
int32_t alwaysshowgray = get_alwaysshowgray();
if (numwalls == 0)
return -1;
if (g_mouseBits & 1 || searchlock)
return point;
if (grid < 1)
dist = 0;
for (i=0; i<numsectors; i++)
{
if (!m32_sideview || !alwaysshowgray)
YAX_SKIPSECTOR(i);
for (j=sector[i].wallptr; j<sector[i].wallptr+sector[i].wallnum; j++)
{
if (!m32_sideview)
dst = klabs(xplc-wall[j].x) + klabs(yplc-wall[j].y);
else
{
editorGet2dScreenCoordinates(&dax,&day, wall[j].x-pos.x,wall[j].y-pos.y, zoom);
day += getscreenvdisp(getflorzofslope(i, wall[j].x,wall[j].y)-pos.z, zoom);
if (halfxdim16+dax < 0 || halfxdim16+dax >= xdim || midydim16+day < 0 || midydim16+day >= ydim)
continue;
dst = klabs(halfxdim16+dax-searchx) + klabs(midydim16+day-searchy);
}
if (dst <= dist)
{
// prefer white walls
if (dst<dist || closest==-1 || (wall[j].nextwall>=0)-(wall[closest].nextwall>=0) <= 0)
dist = dst, closest = j;
}
}
}
if (zoom >= 256)
for (i=0; i<MAXSPRITES; i++)
if (sprite[i].statnum < MAXSTATUS)
{
if ((!m32_sideview || !alwaysshowgray) && sprite[i].sectnum >= 0)
YAX_SKIPSECTOR(sprite[i].sectnum);
if (!m32_sideview)
{
dst = klabs(xplc-sprite[i].x) + klabs(yplc-sprite[i].y);
}
else
{
editorGet2dScreenCoordinates(&dax,&day, sprite[i].x-pos.x,sprite[i].y-pos.y, zoom);
day += getscreenvdisp(sprite[i].z-pos.z, zoom);
if (halfxdim16+dax < 0 || halfxdim16+dax >= xdim || midydim16+day < 0 || midydim16+day >= ydim)
continue;
dst = klabs(halfxdim16+dax-searchx) + klabs(midydim16+day-searchy);
}
// was (dst <= dist), but this way, when duplicating sprites,
// the selected ones are dragged first
if (dst < dist || (dst == dist && (show2dsprite[i>>3]&(1<<(i&7)))))
dist = dst, closest = i+16384;
}
return closest;
}
static void locktogrid(int32_t *dax, int32_t *day)
{
*dax = ((*dax+(1024>>grid))&(0xffffffff<<(11-grid)));
*day = ((*day+(1024>>grid))&(0xffffffff<<(11-grid)));
}
static int32_t adjustmark(int32_t *xplc, int32_t *yplc, int16_t danumwalls)
{
int32_t i, dst, dist, dax, day, pointlockdist;
if (danumwalls < 0)
danumwalls = numwalls;
pointlockdist = 0;
if (grid > 0 && gridlock)
pointlockdist = (256>>grid);
dist = pointlockdist;
dax = *xplc;
day = *yplc;
for (i=0; i<danumwalls; i++)
{
YAX_SKIPWALL(i);
dst = klabs((*xplc)-wall[i].x) + klabs((*yplc)-wall[i].y);
if (dst < dist)
{
dist = dst;
dax = wall[i].x;
day = wall[i].y;
}
}
if (dist == pointlockdist)
if (gridlock && grid > 0)
locktogrid(&dax, &day);
*xplc = dax;
*yplc = day;
return 0;
}
static int32_t checkautoinsert(int32_t dax, int32_t day, int16_t danumwalls)
{
int32_t i, x1, y1, x2, y2;
if (danumwalls < 0)
danumwalls = numwalls;
for (i=0; i<danumwalls; i++) // Check if a point should be inserted
{
YAX_SKIPWALL(i);
x1 = wall[i].x;
y1 = wall[i].y;
x2 = POINT2(i).x;
y2 = POINT2(i).y;
if ((x1 != dax || y1 != day) && (x2 != dax || y2 != day))
if ((x1 <= dax && dax <= x2) || (x2 <= dax && dax <= x1))
if ((y1 <= day && day <= y2) || (y2 <= day && day <= y1))
if ((dax-x1)*(y2-y1) == (day-y1)*(x2-x1))
return 1; //insertpoint((short)i,dax,day,NULL);
}
return 0;
}
// <wallstart> has to be the starting (i.e. least index) wall of a loop!
// Returns: CLOCKDIR_CW or CLOCKDIR_CCW.
int32_t clockdir(int32_t wallstart)
{
int32_t tempint, x0, x1, x2, y0, y1, y2;
int32_t minx = 0x7fffffff;
int32_t themin = -1;
int32_t i = wallstart-1;
do
{
i++;
if (POINT2(i).x < minx)
{
minx = POINT2(i).x;
themin = i;
}
}
while (wall[i].point2 != wallstart && i < MAXWALLS-1);
// NOTE: the i < MAXWALLS-1 check is really only safety against either
// - a really corrupt map, or
// - misuse of clockdir() where <wallstart> is not the starting wall of
// the very last loop
x0 = wall[themin].x;
y0 = wall[themin].y;
x1 = POINT2(themin).x;
y1 = POINT2(themin).y;
x2 = POINT2(wall[themin].point2).x;
y2 = POINT2(wall[themin].point2).y;
if (y2 <= y1 && y1 <= y0)
return CLOCKDIR_CW;
if (y0 <= y1 && y1 <= y2)
return CLOCKDIR_CCW;
tempint = (x0-x1)*(y2-y1) - (x2-x1)*(y0-y1);
if (tempint < 0)
return CLOCKDIR_CW;
else
return CLOCKDIR_CCW;
}
static void flipwalls(int16_t numwalls, int16_t newnumwalls)
{
int32_t i, j, nume;
nume = newnumwalls-numwalls;
for (i=numwalls; i<numwalls+(nume>>1); i++)
{
j = numwalls+newnumwalls-i-1;
swapshort(&onextwall[i], &onextwall[j]);
swaplong(&wall[i].x, &wall[j].x);
swaplong(&wall[i].y, &wall[j].y);
}
}
static void do_insertpoint(int32_t w, int32_t dax, int32_t day, int32_t *mapwallnum)
{
int32_t i;
const int32_t sucksect = sectorofwall(w);
const uint32_t lenbyrep = getlenbyrep(wallength(w), wall[w].xrepeat);
sector[sucksect].wallnum++;
for (i=sucksect+1; i<numsectors; i++)
sector[i].wallptr++;
if (mapwallnum && *mapwallnum >= w+1)
(*mapwallnum)++;
movewalls(w+1, +1);
Bmemcpy(&wall[w+1], &wall[w], sizeof(walltype));
#ifdef YAX_ENABLE
wall[w+1].cstat &= ~(1<<14);
#endif
wall[w].point2 = w+1;
wall[w+1].x = dax;
wall[w+1].y = day;
fixxrepeat(w, lenbyrep);
AlignWallPoint2(w);
fixxrepeat(w+1, lenbyrep);
}
// Returns number of points inserted (1; or 2 if wall had a nextwall).
// *mapwallnum is set to the new wallnum of the former (pre-insertpoint) *mapwallnum
// (the new one can only be >= than the old one; ptr may be NULL if we don't care)
static int32_t insertpoint(int16_t linehighlight, int32_t dax, int32_t day, int32_t *mapwallnum)
{
int32_t j = linehighlight;
do_insertpoint(j, dax, day, mapwallnum);
if (wall[j].nextwall >= 0)
{
int32_t k = wall[j].nextwall;
do_insertpoint(k, dax, day, mapwallnum);
j = wall[k].nextwall;
wall[j].nextwall = k+1;
wall[j+1].nextwall = k;
wall[k].nextwall = j+1;
wall[k+1].nextwall = j;
return 2;
}
return 1;
}
// runi: 0=check (forbidden), 1=prepare, 2=do!
static void deletepoint(int16_t point, int32_t runi)
{
int32_t i, j, sucksect;
Bassert(runi != 0);
if (runi==1)
{
i = wall[point].nextwall;
if (i >= 0)
{
NEXTWALL(i).nextwall = NEXTWALL(i).nextsector = -1;
wall[i].nextwall = wall[i].nextsector = -1;
}
return;
}
sucksect = sectorofwall(point);
sector[sucksect].wallnum--;
for (i=sucksect+1; i<numsectors; i++)
sector[i].wallptr--;
j = lastwall(point);
wall[j].point2 = wall[point].point2;
#if 0
if (wall[j].nextwall >= 0)
{
NEXTWALL(j).nextwall = -1;
NEXTWALL(j).nextsector = -1;
}
if (wall[point].nextwall >= 0)
{
NEXTWALL(point).nextwall = -1;
NEXTWALL(point).nextsector = -1;
}
#endif
movewalls(point, -1);
// checksectorpointer(j, sucksect);
return;
}
static int32_t deletesector(int16_t sucksect)
{
int32_t i, j, k, nextk, startwall, endwall;
while (headspritesect[sucksect] >= 0)
deletesprite(headspritesect[sucksect]);
startwall = sector[sucksect].wallptr;
endwall = startwall + sector[sucksect].wallnum - 1;
j = sector[sucksect].wallnum;
#ifdef YAX_ENABLE
yax_setbunches(sucksect, -1, -1);
#endif
for (i=sucksect; i<numsectors-1; i++)
{
k = headspritesect[i+1];
while (k != -1)
{
nextk = nextspritesect[k];
changespritesect(k, i);
k = nextk;
}
Bmemcpy(&sector[i], &sector[i+1], sizeof(sectortype));
sector[i].wallptr -= j;
}
numsectors--;
for (i=startwall; i<=endwall; i++)
{
if (wall[i].nextwall >= 0)
{
NEXTWALL(i).nextwall = -1;
NEXTWALL(i).nextsector = -1;
}
}
movewalls(startwall, -j);
for (i=0; i<numwalls; i++)
if (wall[i].nextwall >= startwall)
wall[i].nextsector--;
return 0;
}
int32_t fixspritesectors(void)
{
int32_t i;
int32_t numfixedsprites = 0, printfirsttime = 0;
for (i=numsectors-1; i>=0; i--)
if (sector[i].wallnum <= 0 || sector[i].wallptr >= numwalls)
{
// XXX: This is not the best course of action for
// such great corruption.
deletesector(i);
mkonwinvalid();
initprintf("NOTE: Deleted sector %d which had corrupt .wallnum or .wallptr\n", i);
}
if (m32_script_expertmode)
return 0;
for (i=0; i<MAXSPRITES; i++)
if (sprite[i].statnum < MAXSTATUS)
{
const int32_t dax=sprite[i].x, day=sprite[i].y;
if (inside(dax,day,sprite[i].sectnum) != 1)
{
int32_t j, cz, fz;
spriteoncfz(i, &cz, &fz);
for (j=0; j<numsectors; j++)
{
if (cz <= sprite[i].z && sprite[i].z <= fz && inside(dax,day, j) == 1)
{
if (fixmaponsave_sprites || (unsigned)sprite[i].sectnum >= numsectors+0u)
{
if (printfirsttime == 0)
{
initprintf("--------------------\n");
printfirsttime = 1;
}
initprintf("Changed sectnum of sprite #%d from %d to %d\n",
i, TrackerCast(sprite[i].sectnum), j);
changespritesect(i, j);
numfixedsprites++;
}
break;
}
}
}
}
return numfixedsprites;
}
static int32_t movewalls(int32_t start, int32_t offs)
{
int32_t i;
if (offs < 0) //Delete
{
for (i=start; i<numwalls+offs; i++)
Bmemcpy(&wall[i], &wall[i-offs], sizeof(walltype));
}
else if (offs > 0) //Insert
{
for (i=numwalls+offs-1; i>=start+offs; i--)
Bmemcpy(&wall[i], &wall[i-offs], sizeof(walltype));
if (ovh.bak_wallsdrawn > 0)
{
if (ovh.suckwall >= start)
ovh.suckwall += offs;
if (ovh.splitstartwall >= start)
ovh.splitstartwall += offs;
}
}
numwalls += offs;
for (i=0; i<numwalls; i++)
{
if (wall[i].nextwall >= start) wall[i].nextwall += offs;
if (wall[i].point2 >= start) wall[i].point2 += offs;
}
#ifdef YAX_ENABLE
yax_tweakwalls(start, offs);
#endif
return 0;
}
int32_t wallength(int16_t i)
{
int64_t dax = POINT2(i).x - wall[i].x;
int64_t day = POINT2(i).y - wall[i].y;
#if 1 //def POLYMOST
int64_t hypsq = dax*dax + day*day;
if (hypsq > (int64_t)INT32_MAX)
return (int32_t)sqrt((double)hypsq);
else
return ksqrt((uint32_t)hypsq);
#else
return ksqrt(dax*dax + day*day);
#endif
}
void fixrepeats(int16_t i)
{
int32_t dist = wallength(i);
int32_t day = wall[i].yrepeat;
wall[i].xrepeat = clamp(mulscale10(dist,day), 1, 255);
}
uint32_t getlenbyrep(int32_t len, int32_t repeat)
{
if (repeat <= 0)
return ((uint32_t)len)<<12;
return divscale12(len, repeat);
}
void fixxrepeat(int16_t wallnum, uint32_t lenrepquot) // lenrepquot: divscale12(wallength,xrepeat)
{
if (lenrepquot != 0)
{
uint32_t res = (((wallength(wallnum)<<12)+(1<<11))/lenrepquot);
wall[wallnum].xrepeat = clamp(res, 1, 255);
}
}
int32_t overridepm16y = -1;
void clearmidstatbar16(void)
{
int32_t y = overridepm16y<0 ? STATUS2DSIZ : overridepm16y;
videoBeginDrawing();
CLEARLINES2D(ydim-y+25, STATUS2DSIZ+2-(25<<1), 0);
videoEndDrawing();
}
static void clearministatbar16(void)
{
int32_t i, col = editorcolors[25];
videoBeginDrawing();
for (i=ydim-STATUS2DSIZ2; i<ydim; i+=2)
{
// drawline256(0, i<<12, xdim<<12, i<<12, col);
CLEARLINES2D(i, 1, (col<<24)|(col<<16)|(col<<8)|col);
CLEARLINES2D(i+1, 1, (col<<24)|(col<<16)|(col<<8)|col);
col--;
if (col <= 0) break;
}
CLEARLINES2D(i, ydim-i, 0);
if (xdim >= 800)
{
Bsnprintf(tempbuf, sizeof(tempbuf), "%s %s", AppProperName, CallExtGetVer());
printext16(xdim2d-(Bstrlen(tempbuf)<<3)-3, ydim2d-STATUS2DSIZ2+10, editorcolors[4],-1, tempbuf, 0);
printext16(xdim2d-(Bstrlen(tempbuf)<<3)-2, ydim2d-STATUS2DSIZ2+9, editorcolors[12],-1, tempbuf, 0);
}
videoEndDrawing();
}
// <startwall> has to be the starting wall of a loop!
//
// Assuming that <startwall> indicates a CW (outer) loop, the return value can
// be seen as a boolean of whether (x,y) is inside it.
//
// XXX: this function suffers from asymmetry issues in degenerate cases,
// similar to how inside() did before r3898.
int32_t loopinside(int32_t x, int32_t y, int16_t startwall)
{
bssize_t cnt = clockdir(startwall);
int32_t i = startwall;
do
{
int32_t x1 = wall[i].x;
int32_t x2 = POINT2(i).x;
if (x <= x1 || x <= x2)
{
int32_t y1 = wall[i].y;
int32_t y2 = POINT2(i).y;
if (y1 > y2)
{
swaplong(&x1, &x2);
swaplong(&y1, &y2);
}
if (y1 <= y && y < y2)
if ((uint64_t)x1*(y-y2) + (uint64_t)x2*(y1-y) <= (uint64_t)x*(y1-y2))
cnt ^= 1;
}
i = wall[i].point2;
}
while (i != startwall);
return cnt;
}
#if 0
static int32_t numloopsofsector(int16_t sectnum)
{
int32_t i, numloops, startwall, endwall;
numloops = 0;
startwall = sector[sectnum].wallptr;
endwall = startwall + sector[sectnum].wallnum;
for (i=startwall; i<endwall; i++)
if (wall[i].point2 < i) numloops++;
return numloops;
}
#endif
int32_t getnumber_internal1(char ch, int32_t *danumptr, int32_t maxnumber, char sign)
{
int32_t danum = *danumptr;
if (ch >= '0' && ch <= '9')
{
int64_t nbig;
if (danum >= 0)
{
nbig = ((int64_t)danum*10)+(ch-'0');
if (nbig <= (int64_t)maxnumber) danum = nbig;
}
else if (sign) // this extra check isn't hurting anything
{
nbig = ((int64_t)danum*10)-(ch-'0');
if (nbig >= (int64_t)(-maxnumber)) danum = nbig;
}
}
else if (ch == 8 || ch == 127) // backspace
{
danum /= 10;
}
else if (ch == 13)
{
return 1;
}
else if (ch == '-' && sign) // negate
{
danum = -danum;
}
*danumptr = danum;
return 0;
}
int32_t getnumber_autocomplete(const char *namestart, char ch, int32_t *danum, int32_t flags)
{
if (flags!=1 && flags!=2)
return 0;
if (flags==2 && *danum<0)
return 0;
if (isalpha(ch))
{
char b[2];
const char *gotstr;
int32_t i, diddel;
b[0] = ch;
b[1] = 0;
gotstr = getstring_simple(namestart, b, (flags==1)?sizeof(names[0])-1:TAGLAB_MAX-1, flags);
if (!gotstr || !gotstr[0])
return 0;
if (flags==1)
{
for (i=0; i<MAXTILES; i++)
if (!Bstrcasecmp(names[i], gotstr))
{
*danum = i;
return 1;
}
}
else
{
i = taglab_gettag(gotstr);
//initprintf("taglab: s=%s, i=%d\n",gotstr,i);
if (i > 0)
{
*danum = i;
return 1;
}
else
{
// insert new tag
if (*danum > 0 && *danum<32768)
{
diddel = taglab_add(gotstr, *danum);
message("Added label \"%s\" for tag %d%s%s", gotstr, *danum,
diddel?", deleting old ":"",
(!diddel)?"":(diddel==1?"label":"tag"));
return 1;
}
else if (*danum==0)
{
i = taglab_getnextfreetag(NULL);
if (i >= 1)
{
*danum = i;
diddel = taglab_add(gotstr, *danum);
message("%sadded label \"%s\" for tag %d%s%s",
diddel?"Auto-":"Automatically ", gotstr, *danum,
diddel?", deleting old ":"",
(!diddel)?"":(diddel==1?"label":"tag"));
return 1;
}
}
}
}
}
return 0;
}
// sign is now used for more than one flag (also _getnumber256):
// 1: sign
// 2: autocomplete names
// 4: autocomplete taglabels
// 8: return -1 if cancelled
int32_t _getnumber16(const char *namestart, int32_t num, int32_t maxnumber, char sign, const char *(func)(int32_t))
{
char buffer[80], ournamestart[80-17], ch;
int32_t n, danum, oldnum;
uint8_t flags = (sign&(2|4|8))>>1;
sign &= 1;
danum = num;
oldnum = danum;
// need to have 4+11+2==17 chars room at the end
// ("^011", max. string length of an int32, "_ ")
Bstrncpyz(ournamestart, namestart, sizeof(ournamestart));
keyFlushChars();
while (keystatus[sc_Escape] == 0)
{
if (handleevents())
quitevent = 0;
idle();
ch = keyGetChar();
Bsprintf(buffer, "%s^011%d", ournamestart, danum);
n = Bstrlen(buffer); // maximum is 62+4+11 == 77
if (totalclock & 32)
Bstrcat(buffer,"_ ");
// max strlen now 79
_printmessage16("%s", buffer);
if (func != NULL)
{
Bsnprintf(buffer, sizeof(buffer), "%s", func(danum));
// printext16(200L-24, ydim-STATUS2DSIZ+20L, editorcolors[9], editorcolors[0], buffer, 0);
printext16(n<<3, ydim-STATUS2DSIZ+128, editorcolors[11], -1, buffer,0);
}
videoShowFrame(1);
n = 0;
if (getnumber_internal1(ch, &danum, maxnumber, sign) ||
(n=getnumber_autocomplete(ournamestart, ch, &danum, flags&(1+2))))
{
if (flags==1 || n==0)
printmessage16("%s", buffer);
if (danum != oldnum)
asksave = 1;
oldnum = danum;
break;
}
}
if (keystatus[sc_Escape] && (flags&4))
oldnum = -1;
clearkeys();
return oldnum;
}
static void getnumber_clearline(void)
{
char cbuf[128];
int32_t i;
for (i=0; i<min(xdim>>3, (signed)sizeof(cbuf)-1); i++)
cbuf[i] = ' ';
cbuf[i] = 0;
printext256(0, 0, whitecol, 0, cbuf, 0);
}
// sign: |16: don't draw scene
int32_t _getnumber256(const char *namestart, int32_t num, int32_t maxnumber, char sign, const char *(func)(int32_t))
{
char buffer[80], ournamestart[80-13], ch;
int32_t danum, oldnum;
uint8_t flags = (sign&(2|4|8|16))>>1;
sign &= 1;
danum = num;
oldnum = danum;
// need to have 11+2==13 chars room at the end
// (max. string length of an int32, "_ ")
Bstrncpyz(ournamestart, namestart, sizeof(ournamestart));
keyFlushChars();
while (keystatus[sc_Escape] == 0)
{
if (handleevents())
quitevent = 0;
if ((flags&8)==0)
M32_DrawRoomsAndMasks();
ch = keyGetChar();
if (keystatus[sc_Escape])
break;
clearkeys();
g_mouseBits = 0;
searchx = osearchx;
searchy = osearchy;
inputchecked = 1;
if ((flags&8)==0)
CallExtCheckKeys();
getnumber_clearline();
Bsprintf(buffer,"%s%d",ournamestart,danum);
// max strlen now 66+11==77
if (totalclock & 32)
Bstrcat(buffer,"_ ");
// max strlen now 79
printmessage256(0, 0, buffer);
if (func != NULL)
{
Bsnprintf(buffer, sizeof(buffer), "%s", func(danum));
printmessage256(0, 9, buffer);
}
videoShowFrame(1);
if (getnumber_internal1(ch, &danum, maxnumber, sign) ||
getnumber_autocomplete(ournamestart, ch, &danum, flags&(1+2)))
{
if (danum != oldnum)
asksave = 1;
oldnum = danum;
break;
}
}
if (keystatus[sc_Escape] && (flags&4))
oldnum = -1;
clearkeys();
lockclock = totalclock; //Reset timing
return oldnum;
}
// querystr: e.g. "Name: ", must be !=NULL
// defaultstr: can be NULL
// NO overflow checks are done when copying them!
// maxlen: maximum length of entry string, if ==1, enter single char
// completion: 0=none, 1=names[][], 2=taglabels
const char *getstring_simple(const char *querystr, const char *defaultstr, int32_t maxlen, int32_t completion)
{
static char buf[128];
int32_t ei=0, qrylen=0, maxidx, havecompl=0;
char ch;
keyFlushChars();
clearkeys();
Bmemset(buf, 0, sizeof(buf));
qrylen = Bstrlen(querystr);
Bmemcpy(buf, querystr, qrylen);
if (maxlen==0)
maxlen = 64;
maxidx = min((signed)sizeof(buf), xdim>>3);
ei = qrylen;
if (defaultstr)
{
int32_t deflen = Bstrlen(defaultstr);
Bmemcpy(&buf[ei], defaultstr, deflen);
ei += deflen;
}
buf[ei] = '_';
buf[ei+1] = 0;
while (1)
{
if (in3dmode())
getnumber_clearline();
if (in3dmode())
printext256(0, 0, whitecol, 0, buf, 0);
else
_printmessage16("%s", buf);
videoShowFrame(1);
if (handleevents())
quitevent = 0;
idle();
ch = keyGetChar();
if (ch==13)
{
if (maxlen != 1)
buf[ei] = 0;
break;
}
else if (keystatus[sc_Escape])
{
clearkeys();
return completion ? NULL : defaultstr;
}
if (maxlen!=1)
{
// blink...
if (totalclock&32)
{
buf[ei] = '_';
buf[ei+1] = 0;
}
else
{
buf[ei] = 0;
}
if (ei>qrylen && (ch==8 || ch==127))
{
buf[ei--] = 0;
buf[ei] = '_';
havecompl = 0;
}
else if (ei<maxidx-2 && ei-qrylen<maxlen && isprint(ch))
{
if (completion==2 && ch==' ')
ch = '_';
buf[ei++] = ch;
buf[ei] = '_';
buf[ei+1] = 0;
}
if (completion && ((ei>qrylen && ch==9) || havecompl)) // tab: maybe do auto-completion
{
char cmpbuf[128];
char completions[3][16];
const char *cmpstr;
int32_t len=ei-qrylen, i, j, k=len, first=1, numcompl=0;
Bmemcpy(cmpbuf, &buf[qrylen], len);
cmpbuf[len] = 0;
for (i=(completion!=1); i<((completion==1)?MAXTILES:32768); i++)
{
cmpstr = (completion==1) ? names[i] : taglab_getlabel(i);
if (!cmpstr)
continue;
if (Bstrncasecmp(cmpbuf, cmpstr, len) || Bstrlen(cmpstr)==(unsigned)len) // compare the prefix
continue;
if (ch==9)
{
if (first)
{
Bstrncpy(cmpbuf+len, cmpstr+len, sizeof(cmpbuf)-len);
cmpbuf[sizeof(cmpbuf)-1] = 0;
first = 0;
}
else
{
for (k=len; cmpstr[k] && cmpbuf[k] && Btolower(cmpstr[k])==Btolower(cmpbuf[k]); k++)
/* nop */;
cmpbuf[k] = 0;
}
}
if (numcompl<3)
{
Bstrncpyz(completions[numcompl], cmpstr+len, sizeof(completions[0]));
for (k=0; completions[numcompl][k]; k++)
completions[numcompl][k] = Btolower(completions[numcompl][k]);
numcompl++;
}
for (k=len; cmpbuf[k]; k++)
cmpbuf[k] = Btolower(cmpbuf[k]);
}
ei = qrylen;
for (i=0; i<k && i<maxlen && ei<maxidx-2; i++)
buf[ei++] = cmpbuf[i];
if (k==len && numcompl>0) // no chars autocompleted/completion request
{
buf[ei] = '{';
buf[ei+1] = 0;
i = ei+1;
for (k=0; k<numcompl; k++)
{
j = 0;
while (i<maxidx-1 && completions[k][j])
buf[i++] = completions[k][j++];
if (i<maxidx-1)
buf[i++] = (k==numcompl-1) ? '}' : ',';
}
buf[i] = 0;
havecompl = 1;
}
else
{
buf[ei] = '_';
buf[ei+1] = 0;
}
}
}
else
{
if (isalnum(ch) || ch==' ')
buf[ei] = Btoupper(ch);
}
}
clearkeys();
return buf+qrylen;
}
static int32_t getfilenames(const char *path, const char *kind)
{
const int32_t addflags = (!pathsearchmode && grponlymode ? CACHE1D_OPT_NOSTACK : 0);
fnlist_getnames(&fnlist, path, kind, addflags|CACHE1D_FIND_DRIVE, addflags);
finddirshigh = fnlist.finddirs;
findfileshigh = fnlist.findfiles;
currentlist = (findfileshigh != NULL);
return 0;
}
static void tweak_sboardfilename(void)
{
if (pathsearchmode)
Bcanonicalisefilename(selectedboardfilename, 1); // clips off the last token and compresses relative path
else
Bcorrectfilename(selectedboardfilename, 1);
}
////////// FILE SELECTION MENU //////////
static char g_oldpath[BMAX_PATH];
static void menuselect_try_findlast(void)
{
// PK 20080103: start with last selected map
const char *boardbasename = getbasefn(boardfilename);
for (; findfileshigh; findfileshigh=findfileshigh->next)
if (!Bstrcmp(findfileshigh->name, boardbasename))
break;
if (!findfileshigh)
findfileshigh = fnlist.findfiles;
}
// vvv PK ------------------------------------
// copied off menuselect
static int32_t menuselect_auto(int direction, int skip) // 20080104: jump to next (direction!=0) or prev (direction==0) file
{
Bstrcpy(selectedboardfilename, g_oldpath);
tweak_sboardfilename();
getfilenames(selectedboardfilename, "*.map");
if (fnlist.numfiles==0)
return -2;
menuselect_try_findlast();
do
{
if (direction)
{
if (findfileshigh->next)
findfileshigh=findfileshigh->next;
else
return -1;
}
else
{
if (findfileshigh->prev)
findfileshigh=findfileshigh->prev;
else
return -1;
}
} while (skip--);
Bstrcat(selectedboardfilename, findfileshigh->name);
return 0;
}
// ^^^ PK ------------------------------------
static int32_t menuselect(void)
{
int32_t listsize;
int32_t i;
char ch, buffer[96];
const int32_t bakpathsearchmode = pathsearchmode;
Bstrcpy(selectedboardfilename, g_oldpath);
tweak_sboardfilename();
getfilenames(selectedboardfilename, "*.map");
menuselect_try_findlast();
_printmessage16("Select map file with arrow keys and enter.");
ydim16 = ydim-STATUS2DSIZ2;
listsize = (ydim16-32)/9;
do
{
videoBeginDrawing(); //{{{
CLEARLINES2D(0, ydim16, 0);
if (pathsearchmode)
Bstrcpy(buffer,"Local filesystem mode. Ctrl-F: game filesystem");
else
Bsnprintf(buffer, sizeof(buffer),
"Game filesystem %smode. Ctrl-F: local filesystem, Ctrl-G: %s",
grponlymode?"GRP-only ":"", grponlymode?"all files":"GRP contents only");
printext16(halfxdim16-(8*Bstrlen(buffer)/2), 4, editorcolors[12],editorcolors[0],buffer,0);
Bsnprintf(buffer, sizeof(buffer), "(%d dirs, %d files) %s",
fnlist.numdirs, fnlist.numfiles, selectedboardfilename);
printext16(8,ydim16-8-1,editorcolors[8],editorcolors[0],buffer,0);
if (finddirshigh)
{
const CACHE1D_FIND_REC *dir = finddirshigh;
for (i=(listsize/2)-1; i>=0; i--)
{
if (!dir->prev) break;
else dir=dir->prev;
}
for (i=0; ((i<listsize) && dir); i++, dir=dir->next)
{
int32_t c = (dir->type == CACHE1D_FIND_DIR ? 2 : 3); //PK
Bmemset(buffer,0,sizeof(buffer));
Bstrncpy(buffer,dir->name,25);
if (Bstrlen(buffer) == 25)
buffer[21] = buffer[22] = buffer[23] = '.', buffer[24] = 0;
if (dir == finddirshigh)
{
if (currentlist == 0) printext16(8,16+9*i,editorcolors[c|8],editorcolors[0],"->",0);
printext16(32,16+9*i,editorcolors[c|8],editorcolors[0],buffer,0);
}
else
{
printext16(32,16+9*i,editorcolors[c],editorcolors[0],buffer,0);
}
}
}
if (findfileshigh)
{
const CACHE1D_FIND_REC *dir = findfileshigh;
for (i=(listsize/2)-1; i>=0; i--)
{
if (!dir->prev) break;
else dir=dir->prev;
}
for (i=0; (i<listsize && dir); i++, dir=dir->next)
{
if (dir == findfileshigh)
{
if (currentlist == 1) printext16(240,16+9*i,editorcolors[7|8],editorcolors[0],"->",0);
printext16(240+24,16+9*i,editorcolors[7|8],editorcolors[0],dir->name,0);
}
else
{
printext16(240+24,16+9*i,editorcolors[7],editorcolors[0],dir->name,0);
}
}
}
videoEndDrawing(); //}}}
videoShowFrame(1);
keystatus[sc_LeftArrow] = 0;
keystatus[sc_RightArrow] = 0;
keystatus[sc_UpArrow] = 0;
keystatus[sc_DownArrow] = 0;
keystatus[sc_Enter] = 0;
keystatus[sc_BackSpace] = 0;
keystatus[sc_Tab] = 0;
keystatus[sc_Escape] = 0;
ch = 0; //Interesting fakery of ch = getch()
while (ch == 0)
{
if (handleevents())
if (quitevent)
{
keystatus[sc_Escape] = 1;
quitevent = 0;
}
idle();
ch = keyGetChar();
{
// JBF 20040208: seek to first name matching pressed character
CACHE1D_FIND_REC *seeker = currentlist ? fnlist.findfiles : fnlist.finddirs;
if (keystatus[sc_Home]||keystatus[sc_End]) // home/end
{
while (keystatus[sc_End] ? seeker->next : seeker->prev)
seeker = keystatus[sc_End] ? seeker->next : seeker->prev;
if (seeker)
{
if (currentlist) findfileshigh = seeker;
else finddirshigh = seeker;
}
ch = keystatus[sc_End] ? 80 : 72;
keystatus[sc_Home] = keystatus[sc_End] = 0;
}
else if (keystatus[sc_PgUp]|keystatus[sc_PgDn]) // page up/down
{
seeker = currentlist?findfileshigh:finddirshigh;
i = (ydim2d-STATUS2DSIZ2-48)>>5/*3*/; //PK
while (i>0)
{
if (keystatus[sc_PgDn] ? seeker->next : seeker->prev)
seeker = keystatus[sc_PgDn] ? seeker->next : seeker->prev;
i--;
}
if (seeker)
{
if (currentlist) findfileshigh = seeker;
else finddirshigh = seeker;
}
ch = keystatus[sc_PgDn]?80:72;
keystatus[sc_PgUp] = keystatus[sc_PgDn] = 0;
}
else
{
char ch2;
if (ch > 0 && ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') ||
(ch >= '0' && ch <= '9') || (ch=='_')))
{
#ifdef _WIN32
if (ch >= 'a') ch -= ('a'-'A');
#endif
while (seeker)
{
ch2 = seeker->name[0];
#ifdef _WIN32
if (ch2 >= 'a' && ch2 <= 'z') ch2 -= ('a'-'A');
#endif
if (ch2 == ch) break;
seeker = seeker->next;
}
if (seeker)
{
if (currentlist) findfileshigh = seeker;
else finddirshigh = seeker;
continue;
}
}
}
}
if (keystatus[sc_LeftArrow]) ch = 9; // left arr
if (keystatus[sc_RightArrow]) ch = 9; // right arr
if (keystatus[sc_UpArrow]) ch = 72; // up arr
if (keystatus[sc_DownArrow]) ch = 80; // down arr
}
if (ch==6) // Ctrl-F
{
currentlist = 0;
pathsearchmode = 1-pathsearchmode;
if (pathsearchmode)
{
Bstrcpy(selectedboardfilename, "");
Bcanonicalisefilename(selectedboardfilename, 0);
}
else Bstrcpy(selectedboardfilename, "/");
getfilenames(selectedboardfilename, "*.map");
Bstrcpy(g_oldpath, selectedboardfilename);
}
else if (ch==7) // Ctrl-G
{
if (!pathsearchmode)
{
grponlymode = 1-grponlymode;
getfilenames(selectedboardfilename, "*.map");
Bstrcpy(g_oldpath, selectedboardfilename);
}
}
else if (ch == 9)
{
if ((currentlist == 0 && fnlist.findfiles) || (currentlist == 1 && fnlist.finddirs))
currentlist = 1-currentlist;
}
else if (keystatus[sc_UpArrow] /*(ch == 75) || (ch == 72)*/)
{
if (currentlist == 0)
{
if (finddirshigh && finddirshigh->prev)
finddirshigh = finddirshigh->prev;
}
else
{
if (findfileshigh && findfileshigh->prev)
findfileshigh = findfileshigh->prev;
}
}
else if (keystatus[sc_DownArrow] /*(ch == 77) || (ch == 80)*/)
{
if (currentlist == 0)
{
if (finddirshigh && finddirshigh->next)
finddirshigh = finddirshigh->next;
}
else
{
if (findfileshigh && findfileshigh->next)
findfileshigh = findfileshigh->next;
}
}
else if (keystatus[sc_BackSpace]) // backspace
{
Bstrcat(selectedboardfilename, "../");
tweak_sboardfilename();
Bstrcpy(g_oldpath, selectedboardfilename);
getfilenames(selectedboardfilename, "*.map");
keystatus[sc_BackSpace] = 0;
}
else if (ch == 13 && currentlist == 0)
{
if (finddirshigh->type == CACHE1D_FIND_DRIVE)
Bstrcpy(selectedboardfilename, finddirshigh->name);
else
Bstrcat(selectedboardfilename, finddirshigh->name);
Bstrcat(selectedboardfilename, "/");
tweak_sboardfilename();
Bstrcpy(g_oldpath, selectedboardfilename);
//printf("Changing directories to: %s\n", selectedboardfilename);
if (finddirshigh == fnlist.finddirs)
{
getfilenames(selectedboardfilename, "*.map");
currentlist = 0;
}
else getfilenames(selectedboardfilename, "*.map");;
ch = 0;
videoBeginDrawing();
CLEARLINES2D(0, ydim16, 0);
videoEndDrawing();
videoShowFrame(1);
}
if (ch == 13 && !findfileshigh) ch = 0;
}
while (ch != 13 && ch != 27);
if (ch == 13)
{
Bstrcat(selectedboardfilename, findfileshigh->name);
//printf("Selected file: %s\n", selectedboardfilename);
return 0;
}
pathsearchmode = bakpathsearchmode;
return -1;
}
#if 0
static inline int32_t imod(int32_t a, int32_t b)
{
if (a >= 0)
return a%b;
return ((a+1)%b)+b-1;
}
#endif
int32_t fillsector_maybetrans(int16_t sectnum, int32_t fillcolor, uint8_t dotrans)
{
if (sectnum == -1)
return 0;
int32_t x1, x2, y1, y2, sy, y, daminy;
int32_t lborder, rborder, uborder, dborder, miny, maxy, dax;
int16_t z, zz, startwall, endwall, fillcnt;
char col;
if (fillcolor == -1)
col = editorcolors[14]-(M32_THROB>>1);
else
col = fillcolor;
lborder = 0; rborder = xdim;
y = OSD_GetRowsCur();
uborder = (y>=0)?(y+1)*8:0; dborder = ydim16-STATUS2DSIZ2;
miny = dborder-1;
maxy = uborder;
startwall = sector[sectnum].wallptr;
endwall = startwall + sector[sectnum].wallnum - 1;
for (z=startwall; z<=endwall; z++)
{
editorGet2dScreenCoordinates(&x1,&y1, wall[z].x-pos.x,wall[z].y-pos.y, zoom);
if (m32_sideview)
y1 += getscreenvdisp(getflorzofslope(sectnum,wall[z].x,wall[z].y)-pos.z, zoom);
x1 += halfxdim16;
y1 += midydim16;
if (m32_sideview)
{
tempxyar[z][0] = x1;
tempxyar[z][1] = y1;
}
miny = min(miny, y1);
maxy = max(maxy, y1);
}
if (miny < uborder) miny = uborder;
if (maxy >= dborder) maxy = dborder-1;
daminy = miny;// +2 - imod(miny+2, 3);
if (sector[sectnum].floorz > minhlsectorfloorz)
daminy++;
//+((totalclock>>2)&3)
for (sy=daminy; sy<=maxy; sy++) // JBF 20040116: numframes%3 -> (totalclock>>2)&3
{
y = pos.y + ((sy-midydim16)<<14)/zoom;
fillist[0] = lborder; fillcnt = 1;
for (z=startwall; z<=endwall; z++)
{
if (m32_sideview)
{
x1 = tempxyar[z][0];
y1 = tempxyar[z][1];
x2 = tempxyar[wall[z].point2][0];
y2 = tempxyar[wall[z].point2][1];
if (y1 > y2)
{
swaplong(&x1, &x2);
swaplong(&y1, &y2);
}
if (y1 <= sy && sy < y2)
{
if (fillcnt == ARRAY_SIZE(fillist))
break;
x1 += scale(sy-y1, x2-x1, y2-y1);
fillist[fillcnt++] = x1;
}
}
else
{
x1 = wall[z].x; x2 = POINT2(z).x;
y1 = wall[z].y; y2 = POINT2(z).y;
if (y1 > y2)
{
swaplong(&x1, &x2);
swaplong(&y1, &y2);
}
if (y1 <= y && y < y2)
//if (x1*(y-y2) + x2*(y1-y) <= 0)
{
dax = x1 + scale(y-y1, x2-x1, y2-y1);
dax = halfxdim16 + (((dax-pos.x)*zoom)>>14);
if (dax >= lborder)
{
if (fillcnt == ARRAY_SIZE(fillist))
break;
fillist[fillcnt++] = dax;
}
}
}
}
if (fillcnt > 0)
{
for (z=1; z<fillcnt; z++)
for (zz=0; zz<z; zz++)
if (fillist[z] < fillist[zz])
swaplong(&fillist[z], &fillist[zz]);
for (z=(fillcnt&1); z<fillcnt-1; z+=2)
{
if (fillist[z] > rborder)
break;
if (fillist[z+1] > rborder)
fillist[z+1] = rborder;
editorDraw2dLine(fillist[z]+1,sy, fillist[z+1]-1,sy, dotrans ? -col : col); //editorcolors[fillcolor]
}
}
}
return 0;
}
static int16_t whitelinescan(int16_t sucksect, int16_t dalinehighlight)
{
int32_t i, j, k;
int16_t tnewnumwalls;
if (numsectors >= MAXSECTORS)
return MAXWALLS+1;
Bmemcpy(&sector[numsectors], &sector[sucksect], sizeof(sectortype));
sector[numsectors].wallptr = numwalls;
sector[numsectors].wallnum = 0;
i = dalinehighlight;
tnewnumwalls = numwalls;
do
{
if (tnewnumwalls >= MAXWALLS)
return MAXWALLS+1;
j = lastwall(i);
if (wall[j].nextwall >= 0)
{
j = wall[j].point2;
for (k=0; k<numwalls; k++)
{
if (POINT2(k).x == wall[j].x && POINT2(k).y == wall[j].y)
if (wall[k].nextwall == -1)
{
j = k;
break;
}
}
}
Bmemcpy(&wall[tnewnumwalls], &wall[i], sizeof(walltype));
wall[tnewnumwalls].nextwall = j;
wall[tnewnumwalls].nextsector = sectorofwall(j);
tnewnumwalls++;
sector[numsectors].wallnum++;
i = j;
}
while (i != dalinehighlight);
for (i=numwalls; i<tnewnumwalls-1; i++)
wall[i].point2 = i+1;
wall[tnewnumwalls-1].point2 = numwalls;
return (clockdir(numwalls) == CLOCKDIR_CCW) ? -1 : tnewnumwalls;
}
//
// names.h loading
//
enum {
T_INCLUDE = 0,
T_DEFINE = 1,
T_DUMMY,
};
static int32_t parsenamesfile(scriptfile *script)
{
int32_t syms = 0;
tokenlist const tokens[] =
{
{ "include", T_INCLUDE },
{ "#include", T_INCLUDE },
{ "define", T_DEFINE },
{ "#define", T_DEFINE },
{ "dynamicremap", T_DUMMY },
};
while (1)
{
int32_t tokn = getatoken(script,tokens,ARRAY_SIZE(tokens));
char *cmdtokptr = script->ltextptr;
switch (tokn)
{
case T_INCLUDE:
{
char *fn;
if (scriptfile_getstring(script,&fn))
{
initprintf("Error: Malformed include on line %s:%d\n",
script->filename,scriptfile_getlinum(script,cmdtokptr));
break;
}
scriptfile *included;
included = scriptfile_fromfile(fn);
if (!included)
{
initprintf("Error: Failed including %s on line %s:%d\n",
fn, script->filename,scriptfile_getlinum(script,cmdtokptr));
break;
}
initprintf("Including: %s\n", fn);
syms += parsenamesfile(included);
scriptfile_close(included);
break;
}
case T_DEFINE:
{
char *name;
int32_t number;
if (scriptfile_getstring(script,&name))
{
initprintf("Error: Malformed define on line %s:%d\n",
script->filename, scriptfile_getlinum(script,cmdtokptr));
break;
}
if (scriptfile_getsymbol(script,&number))
{
initprintf("Error: No number given for name \"%s\" on line %s:%d\n",
name, script->filename, scriptfile_getlinum(script,cmdtokptr));
break;
}
if ((unsigned)number >= MAXTILES)
{
initprintf("Error: Constant %d for name \"%s\" out of range on line %s:%d\n",
number, name, script->filename, scriptfile_getlinum(script,cmdtokptr));
break;
}
if (Bstrlen(name) > 24)
initprintf("Warning: Truncating name \"%s\" to 24 characters on line %s:%d\n",
name, script->filename, scriptfile_getlinum(script,cmdtokptr));
Bstrncpyz(names[number], name, 25);
name = names[number];
++syms;
if (scriptfile_addsymbolvalue(name,number) < 0)
initprintf("Warning: Symbol %s was NOT redefined to %d on line %s:%d\n",
name, number, script->filename, scriptfile_getlinum(script,cmdtokptr));
break;
}
case T_EOF:
return syms;
default:
break;
}
}
return syms;
}
static void loadnames(const char *namesfile)
{
scriptfile *script = scriptfile_fromfile(namesfile);
if (!script)
return;
initprintf("Loading names file: %s\n", namesfile);
int32_t const syms = parsenamesfile(script);
initprintf("Loaded %d names.\n", syms);
scriptfile_close(script);
scriptfile_clearsymbols();
}
void printcoords16(int32_t posxe, int32_t posye, int16_t ange)
{
char snotbuf[128];
int32_t i, m;
int32_t v8 = (numsectors > MAXSECTORSV7 || numwalls > MAXWALLSV7 ||
Numsprites > MAXSPRITESV7 || numyaxbunches > 0);
#if M32_UNDO
Bsprintf(snotbuf,"x:%d y:%d ang:%d r%d",posxe,posye,ange,map_revision-1);
#else
Bsprintf(snotbuf,"x:%d y:%d ang:%d",posxe,posye,ange);
#endif
i = 0;
while ((snotbuf[i] != 0) && (i < 33))
i++;
while (i < 33)
{
snotbuf[i] = 32;
i++;
}
snotbuf[33] = 0;
clearministatbar16();
printext16(8, ydim-STATUS2DSIZ+128, whitecol, -1, snotbuf,0);
if (highlightcnt<=0 && highlightsectorcnt<=0)
{
m = Bsprintf(snotbuf,"%d/%d %s. %d",
numsectors, v8?MAXSECTORSV8:MAXSECTORSV7,
numyaxbunches>0 ? "SEC":"sec", numwalls);
if (numyaxbunches > 0)
{
if (xdim >= 800)
Bsprintf(&snotbuf[m], "/%d wal. %d/16k spr. %d/256 bn.",
MAXWALLSV8, Numsprites, numyaxbunches);
else
Bsprintf(&snotbuf[m], " wal. %d spr. %d/256 bn.",
Numsprites, numyaxbunches);
}
else
{
if (xdim >= 800)
Bsprintf(&snotbuf[m], "/%d wal. %d/%d spr.",
v8?MAXWALLSV8:MAXWALLSV7, Numsprites,
v8?MAXSPRITESV8:MAXSPRITESV7);
else
Bsprintf(&snotbuf[m], "/%dk wal. %d/%dk spr.",
(v8?MAXWALLSV8:MAXWALLSV7)/1000, Numsprites,
(v8?MAXSPRITESV8:MAXSPRITESV7)/1000);
}
}
else
{
if (highlightcnt>0)
{
m = 0;
for (i=0; i<highlightcnt; i++)
m += !!(highlight[i]&16384);
Bsprintf(snotbuf, "%d sprites, %d walls selected", m, highlightcnt-m);
}
else if (highlightsectorcnt>0)
{
int32_t ii=Bsprintf(snotbuf, "%d sectors with a total of %d walls selected",
highlightsectorcnt, numhlsecwalls);
if (m32_rotateang)
Bsprintf(&snotbuf[ii], " (ang=%d)", m32_rotateang);
}
else
{
snotbuf[0] = 0;
}
v8 = 1; // yellow color
}
m = xdim/8 - 264/8;
m = clamp(m, 1, (signed)sizeof(snotbuf)-1);
i = 0;
while (snotbuf[i] && i < m)
i++;
while (i < m)
{
snotbuf[i] = 32;
i++;
}
snotbuf[m] = 0;
printext16(264, ydim-STATUS2DSIZ+128, v8?editorcolors[10]:whitecol, -1, snotbuf,0);
}
#define DOPRINT(Yofs, fmt, ...) do { \
Bsprintf(snotbuf, fmt, ## __VA_ARGS__); \
printext16(8+col*200, ydim/*-(row*96)*/-STATUS2DSIZ+Yofs, color, -1, snotbuf, 0); \
} while (0)
void showsectordata(int16_t sectnum, int16_t small)
{
char snotbuf[80];
int32_t col=0; //,row = 0;
int32_t color = small ? whitecol : editorcolors[11];
const sectortype *const sec = &sector[sectnum];
if (small)
{
_printmessage16("^10Sector %d %s ^O(F7 to edit)", sectnum, CallExtGetSectorCaption(sectnum));
return;
}
DOPRINT(32, "^10Sector %d", sectnum);
DOPRINT(48, "Firstwall: %d", TrackerCast(sec->wallptr));
DOPRINT(56, "Numberofwalls: %d", TrackerCast(sec->wallnum));
DOPRINT(64, "Firstsprite: %d", headspritesect[sectnum]);
DOPRINT(72, "Tags: %d, %d", TrackerCast(sec->hitag), TrackerCast(sec->lotag));
DOPRINT(80, " (0x%x), (0x%x)", TrackerCast(sec->hitag), TrackerCast(sec->lotag));
DOPRINT(88, "Extra: %d", TrackerCast(sec->extra));
DOPRINT(96, "Visibility: %d", TrackerCast(sec->visibility));
DOPRINT(104, "Pixel height: %d", (sec->floorz-sec->ceilingz)>>8);
col++;
DOPRINT(32, "^10CEILING:^O");
DOPRINT(48, "Flags (hex): %x", TrackerCast(sec->ceilingstat));
{
int32_t xp=sec->ceilingxpanning, yp=sec->ceilingypanning;
#ifdef YAX_ENABLE__COMPAT
if (yax_getbunch(sectnum, YAX_CEILING) >= 0)
xp = yp = 0;
#endif
DOPRINT(56, "(X,Y)pan: %d, %d", xp, yp);
}
DOPRINT(64, "Shade byte: %d", TrackerCast(sec->ceilingshade));
DOPRINT(72, "Z-coordinate: %d", TrackerCast(sec->ceilingz));
DOPRINT(80, "Tile number: %d", TrackerCast(sec->ceilingpicnum));
DOPRINT(88, "Ceiling heinum: %d", TrackerCast(sec->ceilingheinum));
DOPRINT(96, "Palookup number: %d", TrackerCast(sec->ceilingpal));
#ifdef YAX_ENABLE
DOPRINT(104, "Bunch number: %d", yax_getbunch(sectnum, YAX_CEILING));
#endif
col++;
DOPRINT(32, "^10FLOOR:^O");
DOPRINT(48, "Flags (hex): %x", TrackerCast(sec->floorstat));
{
int32_t xp=sec->floorxpanning, yp=sec->floorypanning;
#ifdef YAX_ENABLE__COMPAT
if (yax_getbunch(sectnum, YAX_FLOOR) >= 0)
xp = yp = 0;
#endif
DOPRINT(56, "(X,Y)pan: %d, %d", xp, yp);
}
DOPRINT(64, "Shade byte: %d", TrackerCast(sec->floorshade));
DOPRINT(72, "Z-coordinate: %d", TrackerCast(sec->floorz));
DOPRINT(80, "Tile number: %d", TrackerCast(sec->floorpicnum));
DOPRINT(88, "Floor heinum: %d", TrackerCast(sec->floorheinum));
DOPRINT(96, "Palookup number: %d", TrackerCast(sec->floorpal));
#ifdef YAX_ENABLE
DOPRINT(104, "Bunch number: %d", yax_getbunch(sectnum, YAX_FLOOR));
#endif
}
void showwalldata(int16_t wallnum, int16_t small)
{
walltype const * const wal = &wall[wallnum];
int32_t sec;
char snotbuf[80];
int32_t col=0; //, row = 0;
int32_t color = small ? whitecol : editorcolors[11];
if (small)
{
_printmessage16("^10Wall %d %s ^O(F8 to edit)", wallnum,
CallExtGetWallCaption(wallnum));
return;
}
DOPRINT(32, "^10Wall %d", wallnum);
DOPRINT(48, "X-coordinate: %d", TrackerCast(wal->x));
DOPRINT(56, "Y-coordinate: %d", TrackerCast(wal->y));
DOPRINT(64, "Point2: %d", TrackerCast(wal->point2));
DOPRINT(72, "Sector: ^010%d", sectorofwall(wallnum));
DOPRINT(88, "Tags: %d, %d", TrackerCast(wal->hitag), TrackerCast(wal->lotag));
DOPRINT(96, " (0x%x), (0x%x)", TrackerCast(wal->hitag), TrackerCast(wal->lotag));
col++;
DOPRINT(32, "^10%s^O", (wal->picnum>=0 && wal->picnum<MAXTILES) ? names[wal->picnum] : "!INVALID!");
DOPRINT(48, "Flags (hex): %x", TrackerCast(wal->cstat));
DOPRINT(56, "Shade: %d", TrackerCast(wal->shade));
DOPRINT(64, "Pal: %d", TrackerCast(wal->pal));
DOPRINT(72, "(X,Y)repeat: %d, %d", TrackerCast(wal->xrepeat), TrackerCast(wal->yrepeat));
DOPRINT(80, "(X,Y)pan: %d, %d", TrackerCast(wal->xpanning), TrackerCast(wal->ypanning));
DOPRINT(88, "Tile number: %d", TrackerCast(wal->picnum));
DOPRINT(96, "OverTile number: %d", TrackerCast(wal->overpicnum));
col++;
DOPRINT(48-(small?16:0), "nextsector: %d", TrackerCast(wal->nextsector));
DOPRINT(56-(small?16:0), "nextwall: %d", TrackerCast(wal->nextwall));
DOPRINT(72-(small?16:0), "Extra: %d", TrackerCast(wal->extra));
// 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
DOPRINT(96-(small?16:0), "Wall length: %d", wallength(wallnum));
sec = sectorofwall(wallnum);
DOPRINT(104-(small?16:0), "Pixel height: %d", (sector[sec].floorz-sector[sec].ceilingz)>>8);
}
void showspritedata(int16_t spritenum, int16_t small)
{
spritetype *spr;
char snotbuf[80];
int32_t col=0; //, row = 0;
int32_t color = small ? whitecol : editorcolors[11];
spr = &sprite[spritenum];
if (small)
{
_printmessage16("^10Sprite %d %s ^O(F8 to edit)",spritenum, CallExtGetSpriteCaption(spritenum));
return;
}
DOPRINT(32, "^10Sprite %d", spritenum);
DOPRINT(48, "X-coordinate: %d", TrackerCast(spr->x));
DOPRINT(56, "Y-coordinate: %d", TrackerCast(spr->y));
DOPRINT(64, "Z-coordinate: %d", TrackerCast(spr->z));
DOPRINT(72, "Sectnum: ^010%d", TrackerCast(spr->sectnum));
DOPRINT(80, "Statnum: %d", TrackerCast(spr->statnum));
DOPRINT(96, "Tags: %d, %d", TrackerCast(spr->hitag), TrackerCast(spr->lotag));
DOPRINT(104, " (0x%x), (0x%x)", TrackerCast(spr->hitag), TrackerCast(spr->lotag));
col++;
DOPRINT(32, "^10,0 ^O"); // 24 blanks
DOPRINT(32, "^10%s^O", (spr->picnum>=0 && spr->picnum<MAXTILES) ? names[spr->picnum] : "!INVALID!");
DOPRINT(48, "Flags (hex): %x", TrackerCast(spr->cstat));
DOPRINT(56, "Shade: %d", TrackerCast(spr->shade));
DOPRINT(64, "Pal: %d", TrackerCast(spr->pal));
DOPRINT(72, "Blend: %d", TrackerCast(spr->blend));
DOPRINT(80, "(X,Y)repeat: %d, %d", TrackerCast(spr->xrepeat), TrackerCast(spr->yrepeat));
DOPRINT(88, "(X,Y)offset: %d, %d", TrackerCast(spr->xoffset), TrackerCast(spr->yoffset));
DOPRINT(96, "Tile number: %d", TrackerCast(spr->picnum));
col++;
DOPRINT(48, "Angle (2048 degrees): %d", TrackerCast(spr->ang));
DOPRINT(56, "X-Velocity: %d", TrackerCast(spr->xvel));
DOPRINT(64, "Y-Velocity: %d", TrackerCast(spr->yvel));
DOPRINT(72, "Z-Velocity: %d", TrackerCast(spr->zvel));
DOPRINT(80, "Owner: %d", TrackerCast(spr->owner));
DOPRINT(88, "Clipdist: %d", TrackerCast(spr->clipdist));
DOPRINT(96, "Extra: %d", TrackerCast(spr->extra));
}
#undef DOPRINT
// gets called once per totalclock increment since last call
static void keytimerstuff(void)
{
if (!g_doHardcodedMovement)
return;
if (DOWN_BK(STRAFE) == 0)
{
if (DOWN_BK(TURNLEFT)) angvel = max(angvel-pk_turnaccel, -127);
if (DOWN_BK(TURNRIGHT)) angvel = min(angvel+pk_turnaccel, 127);
}
else
{
if (DOWN_BK(TURNLEFT)) svel = min(svel+16, 255); // svel and vel aren't even chars...
if (DOWN_BK(TURNRIGHT)) svel = max(svel-16, -255);
}
if (DOWN_BK(MOVEFORWARD)) vel = min(vel+16, 255);
if (DOWN_BK(MOVEBACKWARD)) vel = max(vel-16, -255);
/* if (DOWN_BK(STRAFELEFT)) svel = min(svel+8, 127);
if (DOWN_BK(STRAFERIGHT)) svel = max(svel-8, -128); */
if (angvel < 0) angvel = min(angvel+pk_turndecel, 0);
if (angvel > 0) angvel = max(angvel-pk_turndecel, 0);
if (svel < 0) svel = min(svel+6, 0);
if (svel > 0) svel = max(svel-6, 0);
if (vel < 0) vel = min(vel+6, 0);
if (vel > 0) vel = max(vel-6, 0);
/* if(mlook) pos.z -= (horiz-101)*(vel/40); */
}
#if 0
int32_t snfillprintf(char *outbuf, size_t bufsiz, int32_t fill, const char *fmt, ...)
{
char tmpstr[256];
int32_t nwritten, ofs;
va_list va;
va_start(va, fmt);
nwritten = Bvsnprintf(tmpstr, bufsiz, fmt, va);
va_end(va);
ofs = min(nwritten, (signed)bufsiz-1);
Bmemset(outbuf, fill, bufsiz-ofs);
return ofs;
}
#endif
void _printmessage16(const char *fmt, ...)
{
int32_t i, ybase;
char snotbuf[156];
char tmpstr[160];
va_list va;
va_start(va, fmt);
Bvsnprintf(tmpstr, sizeof(tmpstr), fmt, va);
va_end(va);
i = 0;
while (tmpstr[i] && i < 146)
{
snotbuf[i] = tmpstr[i];
i++;
}
snotbuf[i] = 0;
if (lastpm16time == totalclock)
Bstrcpy(lastpm16buf, snotbuf);
clearministatbar16();
ybase = ydim-STATUS2DSIZ+128-8;
printext16(8, ybase+8, whitecol, -1, snotbuf, 0);
}
void printmessage256(int32_t x, int32_t y, const char *name)
{
char snotbuf[80];
int32_t i;
i = 0;
while (name[i] && i < 62)
{
snotbuf[i] = name[i];
i++;
}
while (i < 62)
{
snotbuf[i] = 32;
i++;
}
snotbuf[62] = 0;
printext256(x+2,y+2,0,-1,snotbuf,0);
printext256(x,y,whitecol,-1,snotbuf,0);
}
//Find closest point (*dax, *day) on wall (dawall) to (x, y)
static void getclosestpointonwall(int32_t x, int32_t y, int32_t dawall, int32_t *nx, int32_t *ny,
int32_t maybe_screen_coord_p)
{
int64_t i, j, wx,wy, wx2,wy2, dx, dy;
if (m32_sideview && maybe_screen_coord_p)
{
wx = m32_wallscreenxy[dawall][0];
wy = m32_wallscreenxy[dawall][1];
wx2 = m32_wallscreenxy[wall[dawall].point2][0];
wy2 = m32_wallscreenxy[wall[dawall].point2][1];
}
else
{
wx = wall[dawall].x;
wy = wall[dawall].y;
wx2 = POINT2(dawall).x;
wy2 = POINT2(dawall).y;
}
dx = wx2 - wx;
dy = wy2 - wy;
i = dx*(x-wx) + dy*(y-wy);
if (i <= 0) { *nx = wx; *ny = wy; return; }
j = dx*dx + dy*dy;
if (i >= j) { *nx = wx2; *ny = wy2; return; }
i=((i<<15)/j)<<15;
*nx = wx + ((dx*i)>>30);
*ny = wy + ((dy*i)>>30);
}
static void initcrc(void)
{
int32_t i, j, k, a;
for (j=0; j<256; j++) //Calculate CRC table
{
k = (j<<8); a = 0;
for (i=7; i>=0; i--)
{
if (((k^a)&0x8000) > 0)
a = ((a<<1)&65535) ^ 0x1021; //0x1021 = genpoly
else
a = ((a<<1)&65535);
k = ((k<<1)&65535);
}
crctable[j] = (a&65535);
}
}
static int32_t GetWallBaseZ(int32_t wallnum)
{
int32_t z=0;
const int32_t sectnum = sectorofwall(wallnum);
const int32_t nextsec = wall[wallnum].nextsector;
if (nextsec == -1) //1-sided wall
{
if (wall[wallnum].cstat&4) // floor-aligned
z = sector[sectnum].floorz;
else
z = sector[sectnum].ceilingz;
}
else //2-sided wall
{
if (wall[wallnum].cstat&4)
z = sector[sectnum].ceilingz;
else
{
if (sector[nextsec].ceilingz > sector[sectnum].ceilingz)
z = sector[nextsec].ceilingz; //top step
if (sector[nextsec].floorz < sector[sectnum].floorz)
z = sector[nextsec].floorz; //bottom step
}
}
return z;
}
////////// AUTOMATIC WALL ALIGNMENT //////////
static void 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)
{
int32_t n;
if (tilesiz[tilenum].x==0 || tilesiz[tilenum].y==0)
return;
//do the x alignment
if (doxpanning)
wall[w1_pan].xpanning = (uint8_t)((wall[w0_pan].xpanning + (wall[w0_rep].xrepeat<<3))%tilesiz[tilenum].x);
for (n=picsiz[tilenum]>>4; (1<<n)<tilesiz[tilenum].y; n++) { }
wall[w1_rep].yrepeat = wall[w0_rep].yrepeat;
wall[w1_pan].ypanning = (uint8_t)(wall[w0_pan].ypanning + (((z1-z0)*wall[w0_rep].yrepeat)>>(n+3)));
}
static void AlignWalls(int32_t w0, int32_t z0, int32_t w1, int32_t z1, int32_t doxpanning)
{
AlignWalls_(wall[w0].picnum, z0, z1, doxpanning, w0, w0, w1, w1);
}
void AlignWallPoint2(int32_t w0)
{
int32_t w1 = wall[w0].point2;
AlignWalls(w0,GetWallBaseZ(w0), w1,GetWallBaseZ(w1), 1);
}
#define ALIGN_WALLS_CSTAT_MASK (4+8+256)
static int32_t AlignGetWall(int32_t botswap, int32_t w)
{
return botswap && (wall[w].cstat&2) && wall[w].nextwall >= 0 ? wall[w].nextwall : w;
}
// flags:
// 1: more than once
// 2: (unused)
// 4: carry pixel width from first wall over to the rest
// 8: align TROR nextwalls
// 16: iterate lastwall()s (point2 in reverse)
// 32: use special logic for 'bottom-swapped' walls
int32_t AutoAlignWalls(int32_t w0, uint32_t flags, int32_t nrecurs)
{
static int32_t numaligned, wall0, cstat0;
static uint32_t lenrepquot;
const int32_t totheleft = flags&16;
const int32_t botswap = flags&32;
int32_t z0 = GetWallBaseZ(w0);
int32_t w1 = totheleft ? lastwall(w0) : wall[w0].point2;
int32_t w0b = AlignGetWall(botswap, w0);
const int32_t tilenum = wall[w0b].picnum;
if (nrecurs == 0)
{
//clear visited bits
Bmemset(visited, 0, sizeof(visited));
visited[w0>>3] |= (1<<(w0&7));
numaligned = 0;
lenrepquot = getlenbyrep(wallength(w0), wall[w0].xrepeat);
wall0 = w0;
cstat0 = wall[w0b].cstat & ALIGN_WALLS_CSTAT_MASK; // top/bottom orientation; x/y-flip
}
//loop through walls at this vertex in point2 order
while (1)
{
int32_t w1b = AlignGetWall(botswap, w1);
//break if this wall would connect us in a loop
if (visited[w1>>3]&(1<<(w1&7)))
break;
visited[w1>>3] |= (1<<(w1&7));
#ifdef YAX_ENABLE
if (flags&8)
{
int32_t cf, ynw;
for (cf=0; cf<2; cf++)
{
ynw = yax_getnextwall(w0, cf);
if (ynw >= 0 && wall[ynw].picnum==tilenum && (visited[ynw>>3]&(1<<(ynw&7)))==0)
{
wall[ynw].xrepeat = wall[w0].xrepeat;
wall[ynw].xpanning = wall[w0].xpanning;
AlignWalls(w0,z0, ynw,GetWallBaseZ(ynw), 0); // initial vertical alignment
}
}
}
#endif
//break if reached back of left wall
if (wall[w1].nextwall == w0)
break;
if (wall[w1b].picnum == tilenum)
{
int32_t visible = 1;
const int32_t nextsec = wall[w1].nextsector;
if (nextsec >= 0)
{
int32_t cz,fz, czn,fzn;
const int32_t sectnum = NEXTWALL(w1).nextsector;
//ignore two sided walls that have no visible face
// TODO: can be more precise (i.e. taking into account the wall face)
// ... needs to be factored out from some engine code maybe...
// as is the whole "z base" determination.
getzsofslope(sectnum, wall[w1].x,wall[w1].y, &cz, &fz);
getzsofslope(nextsec, wall[w1].x,wall[w1].y, &czn, &fzn);
if (czn <= cz && fzn >= fz)
visible = 0;
}
if (visible)
{
const int32_t z1 = GetWallBaseZ(w1);
if ((flags&4) && w1!=wall0)
fixxrepeat(w1, lenrepquot);
AlignWalls_(tilenum,z0, z1, 1, w0b, w0, w1b, w1);
wall[w1b].cstat &= ~ALIGN_WALLS_CSTAT_MASK;
wall[w1b].cstat |= cstat0;
numaligned++;
if ((flags&1)==0)
return 1;
//if wall was 1-sided, no need to recurse
if (wall[w1].nextwall < 0)
{
w0 = w1;
w0b = AlignGetWall(botswap, w0);
z0 = GetWallBaseZ(w0);
w1 = totheleft ? lastwall(w0) : wall[w0].point2;
continue;
}
AutoAlignWalls(w1, flags, nrecurs+1);
}
}
if (wall[w1].nextwall < 0)
break;
w1 = totheleft ? lastwall(wall[w1].nextwall) : NEXTWALL(w1).point2;
}
return numaligned;
}
#define PLAYTEST_MAPNAME "autosave_playtest.map"
void test_map(int32_t mode)
{
if (!mode)
updatesector(pos.x, pos.y, &cursectnum);
else
updatesector(startpos.x, startpos.y, &startsectnum);
#ifdef _WIN32
if (fullscreen)
{
printmessage16("Must be in windowed mode to test map!");
return;
}
#endif
if ((!mode && cursectnum >= 0) || (mode && startsectnum >= 0))
{
char const *param = " -map " PLAYTEST_MAPNAME " -noinstancechecking";
char current_cwd[BMAX_PATH];
int32_t slen = 0;
BFILE *fp;
if ((program_origcwd[0] == '\0') || !getcwd(current_cwd, BMAX_PATH))
current_cwd[0] = '\0';
else // Before we check if file exists, for the case there's no absolute path.
Bchdir(program_origcwd);
fp = fopen(game_executable, "rb"); // File exists?
if (fp != NULL)
fclose(fp);
else
{
char const * lastslash = (char const *)Bstrrchr(mapster32_fullpath, '/');
#ifdef _WIN32
char const * lastbackslash = (char const *)Bstrrchr(mapster32_fullpath, '\\');
lastslash = max(lastslash, lastbackslash);
#endif
if (lastslash)
{
slen = lastslash-mapster32_fullpath+1;
Bstrncpy(game_executable, mapster32_fullpath, slen);
Bstrncpy(game_executable+slen, DefaultGameExec, sizeof(game_executable)-slen);
}
else
Bstrncpy(game_executable, DefaultGameLocalExec, sizeof(game_executable));
}
if (current_cwd[0] != '\0') // Temporarily changing back,
Bchdir(current_cwd); // after checking if file exists.
if (testplay_addparam)
slen = Bstrlen(testplay_addparam);
// Considering the NULL character, quatation marks
// and a possible extra space not in testplay_addparam,
// the length should be Bstrlen(game_executable)+Bstrlen(param)+(slen+1)+2+1.
char *fullparam = (char *)Xmalloc(Bstrlen(game_executable)+Bstrlen(param)+slen+4);
Bsprintf(fullparam,"\"%s\"",game_executable);
if (testplay_addparam)
{
Bstrcat(fullparam, " ");
Bstrcat(fullparam, testplay_addparam);
}
Bstrcat(fullparam, param);
CallExtPreSaveMap();
if (mode)
saveboard(PLAYTEST_MAPNAME, &startpos, startang, startsectnum);
else
saveboard(PLAYTEST_MAPNAME, &pos, ang, cursectnum);
message("Board saved to " PLAYTEST_MAPNAME ". Starting the game...");
OSD_Printf("...as `%s'\n", fullparam);
videoShowFrame(1);
mouseUninit();
#ifdef _WIN32
{
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory(&si,sizeof(si));
ZeroMemory(&pi,sizeof(pi));
si.cb = sizeof(si);
if (!CreateProcess(NULL,fullparam,NULL,NULL,0,0,NULL,NULL,&si,&pi))
message("Error launching the game!");
else WaitForSingleObject(pi.hProcess,INFINITE);
}
#else
if (current_cwd[0] != '\0')
{
Bchdir(program_origcwd);
if (system(fullparam))
message("Error launching the game!");
Bchdir(current_cwd);
}
else system(fullparam);
#endif
printmessage16("Game process exited");
mouseInit();
clearkeys();
Bfree(fullparam);
}
else
printmessage16("Position must be in valid player space to test map!");
}
void app_crashhandler(void)
{
if (levelname[0])
{
append_ext_UNSAFE(levelname, "_crash.map");
SaveBoard(levelname, M32_SB_NOEXT);
}
}
// These will be more useful in the future...
static const char *CallExtGetVer(void)
{
return ExtGetVer();
}
static int32_t CallExtInit(void)
{
return ExtInit();
}
static int32_t CallExtPreInit(int32_t argc,char const * const * argv)
{
return ExtPreInit(argc, argv);
}
static int32_t CallExtPostStartupWindow(void)
{
return ExtPostStartupWindow();
}
static void CallExtPostInit(void)
{
return ExtPostInit();
}
static void CallExtUnInit(void)
{
ExtUnInit();
}
static void CallExtPreCheckKeys(void)
{
ExtPreCheckKeys();
}
static void CallExtAnalyzeSprites(int32_t ourx, int32_t oury, int32_t oura, int32_t smoothr)
{
ExtAnalyzeSprites(ourx, oury, oura, smoothr);
VM_OnEvent(EVENT_ANALYZESPRITES, -1);
}
static void CallExtCheckKeys(void)
{
ExtCheckKeys();
}
static void CallExtPreLoadMap(void)
{
VM_OnEvent(EVENT_PRELOADMAP, -1);
ExtPreLoadMap();
}
static void CallExtSetupMapFilename(const char *mapname)
{
Bstrncpy(levelname, mapname, sizeof(levelname));
Bsnprintf(tempbuf, sizeof(tempbuf), "%s - %s", AppProperName, mapname);
wm_setapptitle(tempbuf);
ExtSetupMapFilename(mapname);
}
static void CallExtLoadMap(const char *mapname)
{
CallExtSetupMapFilename(mapname);
ExtLoadMap(mapname);
VM_OnEvent(EVENT_LOADMAP, -1);
}
static int32_t CallExtPreSaveMap(void)
{
VM_OnEvent(EVENT_PRESAVEMAP, -1);
return ExtPreSaveMap();
}
static void CallExtSaveMap(const char *mapname)
{
ExtSaveMap(mapname);
saveboard("backup.map", &pos, ang, cursectnum);
VM_OnEvent(EVENT_SAVEMAP, -1);
}
static void CallExtShowSectorData(int16_t sectnum)
{
ExtShowSectorData(sectnum);
}
static void CallExtShowWallData(int16_t wallnum)
{
ExtShowWallData(wallnum);
}
static void CallExtShowSpriteData(int16_t spritenum)
{
ExtShowSpriteData(spritenum);
}
static void CallExtEditSectorData(int16_t sectnum)
{
ExtEditSectorData(sectnum);
}
static void CallExtEditWallData(int16_t wallnum)
{
ExtEditWallData(wallnum);
}
static void CallExtEditSpriteData(int16_t spritenum)
{
ExtEditSpriteData(spritenum);
}
#if 0
static const char *CallExtGetSectorType(int32_t lotag)
{
return ExtGetSectorType(lotag);
}
#endif