mirror of
https://github.com/ZDoom/Raze.git
synced 2024-11-16 17:32:13 +00:00
363f0b3af6
Inspired by http://forums.duke4.net/topic/7506-tror-question/page__view__findpost__p__199151 the corruption checker now checks for certain conditions of the loops of each sector. Recall that CW loops are outer and CCW loops are inner. - If a sector has no or more than one outer loop, count that as corruption (level 4 and 3, respectively). - (Disabled) For sectors with exactly one outer loop, check that all inner ones are inside it. This is currently not compiled due to an asymmetry of loopinside() for degenerate cases, similar to pre-r3898 inside(). git-svn-id: https://svn.eduke32.com/eduke32@4569 1a8010ca-5511-0410-912e-c29ae57300e0
1949 lines
58 KiB
C
1949 lines
58 KiB
C
|
|
#include "compat.h"
|
|
#include "keys.h"
|
|
#include "build.h"
|
|
#include "cache1d.h"
|
|
#include "polymer.h"
|
|
#include "editor.h"
|
|
#include "renderlayer.h"
|
|
|
|
#include "m32script.h"
|
|
#include "m32def.h"
|
|
|
|
#include "lz4.h"
|
|
#include "xxhash.h"
|
|
|
|
//////////////////// Key stuff ////////////////////
|
|
|
|
#define eitherALT (keystatus[KEYSC_LALT] || keystatus[KEYSC_RALT])
|
|
#define eitherCTRL (keystatus[KEYSC_LCTRL] || keystatus[KEYSC_RCTRL])
|
|
#define eitherSHIFT (keystatus[KEYSC_LSHIFT] || keystatus[KEYSC_RSHIFT])
|
|
|
|
#define PRESSED_KEYSC(Key) (keystatus[KEYSC_##Key] && !(keystatus[KEYSC_##Key]=0))
|
|
|
|
////
|
|
|
|
// All these variables need verification that all relevant editor stubs are actually implementing them correctly.
|
|
|
|
char getmessage[162], getmessageleng;
|
|
int32_t getmessagetimeoff; //, charsperline;
|
|
|
|
int32_t mousxplc, mousyplc;
|
|
int32_t mouseaction;
|
|
|
|
char *scripthist[SCRIPTHISTSIZ];
|
|
int32_t scripthistend;
|
|
|
|
int32_t g_lazy_tileselector;
|
|
int32_t fixmaponsave_sprites = 1;
|
|
|
|
int32_t showambiencesounds=2;
|
|
|
|
int32_t autosave=180;
|
|
|
|
int32_t autocorruptcheck;
|
|
int32_t corruptcheck_noalreadyrefd;
|
|
int32_t corrupt_tryfix_alt;
|
|
int32_t corruptlevel, numcorruptthings, corruptthings[MAXCORRUPTTHINGS];
|
|
|
|
////
|
|
|
|
#ifdef YAX_ENABLE
|
|
const char *yupdownwall[2] = {"upwall","downwall"};
|
|
const char *YUPDOWNWALL[2] = {"UPWALL","DOWNWALL"};
|
|
#endif
|
|
|
|
////
|
|
|
|
void drawgradient(void)
|
|
{
|
|
int32_t i, col = whitecol-21;
|
|
begindrawing();
|
|
for (i=ydim-STATUS2DSIZ+16; i<ydim && col>0; i++,col--)
|
|
CLEARLINES2D(i, 1, (col<<24)|(col<<16)|(col<<8)|col);
|
|
CLEARLINES2D(i, ydim-i, 0);
|
|
enddrawing();
|
|
}
|
|
|
|
static void message_common1(const char *tmpstr)
|
|
{
|
|
Bstrncpyz(getmessage, tmpstr, sizeof(getmessage));
|
|
|
|
getmessageleng = Bstrlen(getmessage);
|
|
getmessagetimeoff = totalclock + 120*2 + getmessageleng*(120/30);
|
|
// lastmessagetime = totalclock;
|
|
}
|
|
|
|
void message(const char *fmt, ...)
|
|
{
|
|
char tmpstr[256];
|
|
va_list va;
|
|
|
|
va_start(va, fmt);
|
|
Bvsnprintf(tmpstr, 256, fmt, va);
|
|
va_end(va);
|
|
|
|
message_common1(tmpstr);
|
|
|
|
if (!mouseaction)
|
|
OSD_Printf("%s\n", tmpstr);
|
|
}
|
|
|
|
void silentmessage(const char *fmt, ...)
|
|
{
|
|
char tmpstr[256];
|
|
va_list va;
|
|
|
|
va_start(va, fmt);
|
|
Bvsnprintf(tmpstr, 256, fmt, va);
|
|
va_end(va);
|
|
|
|
message_common1(tmpstr);
|
|
}
|
|
|
|
////////// tag labeling system //////////
|
|
|
|
typedef struct
|
|
{
|
|
hashtable_t hashtab;
|
|
char *label[32768];
|
|
int32_t numlabels;
|
|
} taglab_t;
|
|
|
|
static taglab_t g_taglab;
|
|
|
|
static void tstrtoupper(char *s)
|
|
{
|
|
int32_t i;
|
|
for (i=0; s[i]; i++)
|
|
s[i] = Btoupper(s[i]);
|
|
}
|
|
|
|
void taglab_init()
|
|
{
|
|
int32_t i;
|
|
|
|
g_taglab.numlabels = 0;
|
|
g_taglab.hashtab.size = 16384;
|
|
hash_init(&g_taglab.hashtab);
|
|
|
|
for (i=0; i<32768; i++)
|
|
{
|
|
if (g_taglab.label[i])
|
|
Bfree(g_taglab.label[i]);
|
|
g_taglab.label[i] = NULL;
|
|
}
|
|
}
|
|
|
|
int32_t taglab_load(const char *filename, int32_t flags)
|
|
{
|
|
int32_t fil, len, i;
|
|
char buf[BMAX_PATH], *dot, *filebuf;
|
|
|
|
taglab_init();
|
|
|
|
len = Bstrlen(filename);
|
|
if (len >= BMAX_PATH-1)
|
|
return -1;
|
|
Bmemcpy(buf, filename, len+1);
|
|
|
|
//
|
|
dot = Bstrrchr(buf, '.');
|
|
if (!dot)
|
|
dot = &buf[len];
|
|
|
|
if (dot-buf+8 >= BMAX_PATH)
|
|
return -1;
|
|
Bmemcpy(dot, ".maptags", 9);
|
|
//
|
|
|
|
if ((fil = kopen4load(buf,flags)) == -1)
|
|
return -1;
|
|
|
|
len = kfilelength(fil);
|
|
|
|
filebuf = (char *)Xmalloc(len+1);
|
|
if (!filebuf)
|
|
{
|
|
kclose(fil);
|
|
return -1;
|
|
}
|
|
|
|
kread(fil, filebuf, len);
|
|
filebuf[len] = 0;
|
|
kclose(fil);
|
|
|
|
// ----
|
|
|
|
{
|
|
int32_t tag;
|
|
char *cp=filebuf, *bp, *ep;
|
|
|
|
while (1)
|
|
{
|
|
#define XTAGLAB_STRINGIFY(X) TAGLAB_STRINGIFY(X)
|
|
#define TAGLAB_STRINGIFY(X) #X
|
|
i = sscanf(cp, "%d %" XTAGLAB_STRINGIFY(TAGLAB_MAX) "s", &tag, buf);
|
|
#undef XTAGLAB_STRINGIFY
|
|
#undef TAGLAB_STRINGIFY
|
|
if (i != 2 || !buf[0] || tag<0 || tag>=32768)
|
|
goto nextline;
|
|
|
|
buf[TAGLAB_MAX-1] = 0;
|
|
|
|
i = Bstrlen(buf);
|
|
bp = buf; while (*bp && isspace(*bp)) bp++;
|
|
ep = &buf[i-1]; while (ep>buf && isspace(*ep)) ep--;
|
|
ep++;
|
|
|
|
if (!(ep > bp))
|
|
goto nextline;
|
|
*ep = 0;
|
|
|
|
taglab_add(bp, tag);
|
|
//initprintf("add tag %s:%d\n", bp, tag);
|
|
nextline:
|
|
while (*cp && *cp!='\n')
|
|
cp++;
|
|
while (*cp=='\r' || *cp=='\n')
|
|
cp++;
|
|
if (*cp == 0)
|
|
break;
|
|
}
|
|
}
|
|
|
|
// ----
|
|
Bfree(filebuf);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int32_t taglab_save(const char *mapname)
|
|
{
|
|
int32_t fil, len, i;
|
|
char buf[BMAX_PATH], *dot;
|
|
const char *label;
|
|
|
|
if (g_taglab.numlabels==0)
|
|
return 1;
|
|
|
|
Bstrncpyz(buf, mapname, BMAX_PATH);
|
|
|
|
len = Bstrlen(buf);
|
|
//
|
|
dot = Bstrrchr(buf, '.');
|
|
if (!dot)
|
|
dot = &buf[len];
|
|
|
|
if (dot-buf+8 >= BMAX_PATH)
|
|
return -1;
|
|
Bmemcpy(dot, ".maptags", 9);
|
|
//
|
|
|
|
if ((fil = Bopen(buf,BO_BINARY|BO_TRUNC|BO_CREAT|BO_WRONLY,BS_IREAD|BS_IWRITE)) == -1)
|
|
{
|
|
initprintf("Couldn't open \"%s\" for writing: %s\n", buf, strerror(errno));
|
|
return -1;
|
|
}
|
|
|
|
for (i=0; i<32768; i++)
|
|
{
|
|
label = taglab_getlabel(i);
|
|
if (!label)
|
|
continue;
|
|
|
|
len = Bsprintf(buf, "%d %s" OURNEWL, i, label);
|
|
if (Bwrite(fil, buf, len)!=len)
|
|
break;
|
|
}
|
|
|
|
Bclose(fil);
|
|
|
|
return (i!=32768);
|
|
}
|
|
|
|
int32_t taglab_add(const char *label, int16_t tag)
|
|
{
|
|
const char *otaglabel;
|
|
char buf[TAGLAB_MAX];
|
|
int32_t olabeltag, diddel=0;
|
|
|
|
if (tag < 0)
|
|
return -1;
|
|
|
|
Bstrncpyz(buf, label, sizeof(buf));
|
|
// upcase the tag for storage and comparison
|
|
tstrtoupper(buf);
|
|
|
|
otaglabel = g_taglab.label[tag];
|
|
if (otaglabel)
|
|
{
|
|
if (!Bstrcasecmp(otaglabel, buf))
|
|
return 0;
|
|
|
|
// hash_delete(&g_taglab.hashtab, g_taglab.label[tag]);
|
|
|
|
// a label having the same tag number as 'tag' is deleted
|
|
Bfree(g_taglab.label[tag]);
|
|
g_taglab.label[tag] = NULL;
|
|
diddel |= 1;
|
|
}
|
|
else
|
|
{
|
|
olabeltag = hash_findcase(&g_taglab.hashtab, buf);
|
|
if (olabeltag==tag)
|
|
return 0;
|
|
|
|
if (olabeltag>=0)
|
|
{
|
|
// the label gets assigned to a new tag number ('tag deleted')
|
|
Bfree(g_taglab.label[olabeltag]);
|
|
g_taglab.label[olabeltag] = NULL;
|
|
diddel |= 2;
|
|
}
|
|
}
|
|
|
|
if (!diddel)
|
|
g_taglab.numlabels++;
|
|
g_taglab.label[tag] = Xstrdup(buf);
|
|
//initprintf("added %s %d to hash\n", g_taglab.label[tag], tag);
|
|
hash_add(&g_taglab.hashtab, g_taglab.label[tag], tag, 1);
|
|
|
|
return diddel;
|
|
}
|
|
|
|
const char *taglab_getlabel(int16_t tag)
|
|
{
|
|
if (tag < 0) // || tag>=32768 implicitly
|
|
return NULL;
|
|
|
|
return g_taglab.label[tag];
|
|
}
|
|
|
|
int32_t taglab_gettag(const char *label)
|
|
{
|
|
char buf[TAGLAB_MAX];
|
|
|
|
Bstrncpyz(buf, label, TAGLAB_MAX);
|
|
|
|
// need to upcase since hash_findcase doesn't work as expected:
|
|
// getting the code is still (necessarily) case-sensitive...
|
|
tstrtoupper(buf);
|
|
|
|
return hash_findcase(&g_taglab.hashtab, buf);
|
|
}
|
|
////////// end tag labeling system //////////
|
|
|
|
////////// UNDO/REDO SYSTEM //////////
|
|
#if M32_UNDO
|
|
mapundo_t *mapstate = NULL;
|
|
|
|
int32_t map_revision = 1;
|
|
|
|
#define QADDNSZ 400
|
|
|
|
|
|
static int32_t try_match_with_prev(int32_t idx, int32_t numsthgs, uint32_t crc)
|
|
{
|
|
if (mapstate->prev && mapstate->prev->num[idx]==numsthgs && mapstate->prev->crc[idx]==crc)
|
|
{
|
|
// found match!
|
|
mapstate->sws[idx] = mapstate->prev->sws[idx];
|
|
(*(int32_t *)mapstate->sws[idx])++; // increase refcount!
|
|
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void create_compressed_block(int32_t idx, const void *srcdata, uint32_t size, uint32_t crc)
|
|
{
|
|
uint32_t j;
|
|
|
|
// allocate
|
|
mapstate->sws[idx] = (char *)Xmalloc(4 + size + QADDNSZ);
|
|
|
|
// compress & realloc
|
|
j = LZ4_compress((const char*)srcdata, mapstate->sws[idx]+4, size);
|
|
mapstate->sws[idx] = (char *)Xrealloc(mapstate->sws[idx], 4 + j);
|
|
|
|
// write refcount
|
|
*(int32_t *)mapstate->sws[idx] = 1;
|
|
|
|
mapstate->crc[idx] = crc;
|
|
}
|
|
|
|
static void free_self_and_successors(mapundo_t *mapst)
|
|
{
|
|
mapundo_t *cur = mapst;
|
|
|
|
mapst->prev = NULL; // break the back link
|
|
|
|
while (cur->next)
|
|
cur = cur->next;
|
|
|
|
while (1)
|
|
{
|
|
int32_t i;
|
|
mapundo_t *const prev = cur->prev;
|
|
|
|
for (i=0; i<3; i++)
|
|
{
|
|
int32_t *const refcnt = (int32_t *)cur->sws[i];
|
|
|
|
if (refcnt)
|
|
{
|
|
(*refcnt)--;
|
|
if (*refcnt == 0)
|
|
Bfree(refcnt); // free the block!
|
|
}
|
|
}
|
|
|
|
Bfree(cur);
|
|
|
|
if (!prev)
|
|
break;
|
|
|
|
cur = prev;
|
|
}
|
|
}
|
|
|
|
// NOTE: only _consecutive_ matching (size+crc) sector/wall/sprite blocks are
|
|
// shared!
|
|
void create_map_snapshot(void)
|
|
{
|
|
if (mapstate == NULL)
|
|
{
|
|
// create initial mapstate
|
|
|
|
map_revision = 1;
|
|
|
|
mapstate = (mapundo_t *)Xcalloc(1, sizeof(mapundo_t));
|
|
mapstate->revision = map_revision;
|
|
mapstate->prev = mapstate->next = NULL;
|
|
}
|
|
else
|
|
{
|
|
if (mapstate->next)
|
|
free_self_and_successors(mapstate->next);
|
|
// now, have no successors
|
|
|
|
// calloc because not everything may be set in the following:
|
|
mapstate->next = (mapundo_t *)Xcalloc(1, sizeof(mapundo_t));
|
|
mapstate->next->prev = mapstate;
|
|
|
|
mapstate = mapstate->next;
|
|
|
|
mapstate->revision = ++map_revision;
|
|
}
|
|
|
|
|
|
fixspritesectors();
|
|
|
|
mapstate->num[0] = numsectors;
|
|
mapstate->num[1] = numwalls;
|
|
mapstate->num[2] = Numsprites;
|
|
|
|
|
|
if (numsectors)
|
|
{
|
|
int32_t j;
|
|
uint32_t temphash = XXH32((uint8_t *)sector, numsectors*sizeof(sectortype), numsectors*sizeof(sectortype));
|
|
|
|
if (!try_match_with_prev(0, numsectors, temphash))
|
|
create_compressed_block(0, sector, numsectors*sizeof(sectortype), temphash);
|
|
|
|
if (numwalls)
|
|
{
|
|
temphash = XXH32((uint8_t *)wall, numwalls*sizeof(walltype), numwalls*sizeof(walltype));
|
|
|
|
if (!try_match_with_prev(1, numwalls, temphash))
|
|
create_compressed_block(1, wall, numwalls*sizeof(walltype), temphash);
|
|
}
|
|
|
|
if (Numsprites)
|
|
{
|
|
temphash = XXH32((uint8_t *)sprite, MAXSPRITES*sizeof(spritetype), MAXSPRITES*sizeof(spritetype));
|
|
|
|
if (!try_match_with_prev(2, Numsprites, temphash))
|
|
{
|
|
int32_t i = 0;
|
|
spritetype *const tspri = (spritetype *)Xmalloc(Numsprites*sizeof(spritetype) + 4);
|
|
spritetype *spri = tspri;
|
|
|
|
for (j=0; j<MAXSPRITES && i < Numsprites; j++)
|
|
if (sprite[j].statnum != MAXSTATUS)
|
|
{
|
|
Bmemcpy(spri++, &sprite[j], sizeof(spritetype));
|
|
i++;
|
|
}
|
|
|
|
create_compressed_block(2, tspri, Numsprites*sizeof(spritetype), temphash);
|
|
Bfree(tspri);
|
|
}
|
|
}
|
|
}
|
|
|
|
CheckMapCorruption(5, 0);
|
|
}
|
|
|
|
void map_undoredo_free(void)
|
|
{
|
|
if (mapstate)
|
|
{
|
|
free_self_and_successors(mapstate);
|
|
mapstate = NULL;
|
|
}
|
|
|
|
map_revision = 1;
|
|
}
|
|
|
|
int32_t map_undoredo(int32_t dir)
|
|
{
|
|
int32_t i;
|
|
|
|
if (mapstate == NULL) return 1;
|
|
|
|
if (dir)
|
|
{
|
|
if (mapstate->next == NULL || !mapstate->next->num[0]) return 1;
|
|
|
|
// while (map_revision+1 != mapstate->revision && mapstate->next)
|
|
mapstate = mapstate->next;
|
|
}
|
|
else
|
|
{
|
|
if (mapstate->prev == NULL || !mapstate->prev->num[0]) return 1;
|
|
|
|
// while (map_revision-1 != mapstate->revision && mapstate->prev)
|
|
mapstate = mapstate->prev;
|
|
}
|
|
|
|
numsectors = mapstate->num[0];
|
|
numwalls = mapstate->num[1];
|
|
map_revision = mapstate->revision;
|
|
|
|
Bmemset(show2dsector, 0, sizeof(show2dsector));
|
|
|
|
reset_highlightsector();
|
|
reset_highlight();
|
|
|
|
initspritelists();
|
|
|
|
if (mapstate->num[0])
|
|
{
|
|
// restore sector[]
|
|
LZ4_decompress_fast(mapstate->sws[0]+4, (char*)sector, numsectors*sizeof(sectortype));
|
|
|
|
if (mapstate->num[1]) // restore wall[]
|
|
LZ4_decompress_fast(mapstate->sws[1]+4, (char*)wall, numwalls*sizeof(walltype));
|
|
|
|
if (mapstate->num[2]) // restore sprite[]
|
|
LZ4_decompress_fast(mapstate->sws[2]+4, (char*)sprite, (mapstate->num[2])*sizeof(spritetype));
|
|
}
|
|
|
|
// insert sprites
|
|
for (i=0; i<mapstate->num[2]; i++)
|
|
{
|
|
if ((sprite[i].cstat & 48) == 48) sprite[i].cstat &= ~48;
|
|
Bassert((unsigned)sprite[i].sectnum < (unsigned)numsectors
|
|
&& (unsigned)sprite[i].statnum < MAXSTATUS);
|
|
insertsprite(sprite[i].sectnum, sprite[i].statnum);
|
|
}
|
|
|
|
Bassert(Numsprites == mapstate->num[2]);
|
|
|
|
#ifdef POLYMER
|
|
if (in3dmode() && getrendermode() == REND_POLYMER)
|
|
polymer_loadboard();
|
|
#endif
|
|
#ifdef YAX_ENABLE
|
|
yax_update(0);
|
|
yax_updategrays(pos.z);
|
|
#endif
|
|
CheckMapCorruption(4, 0);
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
////
|
|
|
|
//// port of a.m32's corruptchk ////
|
|
// Compile wall loop checks? 0: no, 1: partial, 2: full.
|
|
#define CCHK_LOOP_CHECKS 1
|
|
// returns value from 0 (all OK) to 5 (panic!)
|
|
#define CCHK_PANIC OSDTEXT_DARKRED "PANIC!!!^O "
|
|
//#define CCHKPREF OSDTEXT_RED "^O"
|
|
#define CCHK_CORRECTED OSDTEXT_GREEN " -> "
|
|
|
|
#ifdef _MSC_VER
|
|
# define OSD_Printf_CChk OSD_Printf
|
|
#else
|
|
# define OSD_Printf_CChk OSD_Printf_nowarn
|
|
#endif
|
|
|
|
#define CORRUPTCHK_PRINT(errlev, what, fmt, ...) do \
|
|
{ \
|
|
bad = max(bad, errlev); \
|
|
if (numcorruptthings>=MAXCORRUPTTHINGS) \
|
|
goto too_many_errors; \
|
|
corruptthings[numcorruptthings++] = (what); \
|
|
if (errlev >= printfromlev) \
|
|
OSD_Printf_CChk("#%d: " fmt "\n", numcorruptthings, ## __VA_ARGS__); \
|
|
} while (0)
|
|
|
|
#ifdef YAX_ENABLE
|
|
static int32_t walls_have_equal_endpoints(int32_t w1, int32_t w2)
|
|
{
|
|
int32_t n1 = wall[w1].point2, n2 = wall[w2].point2;
|
|
|
|
return (wall[w1].x==wall[w2].x && wall[w1].y==wall[w2].y &&
|
|
wall[n1].x==wall[n2].x && wall[n1].y==wall[n2].y);
|
|
}
|
|
|
|
static void correct_yax_nextwall(int32_t wallnum, int32_t bunchnum, int32_t cf, int32_t tryfixingp)
|
|
{
|
|
int32_t i, j, startwall, endwall;
|
|
int32_t nummatching=0, lastwall[2]={-1,-1};
|
|
|
|
for (SECTORS_OF_BUNCH(bunchnum, !cf, i))
|
|
for (WALLS_OF_SECTOR(i, j))
|
|
{
|
|
// v v v shouldn't happen, 'stupidity safety'
|
|
if (j!=wallnum && walls_have_equal_endpoints(wallnum, j))
|
|
{
|
|
lastwall[nummatching++] = j;
|
|
if (nummatching==2)
|
|
goto outofloop;
|
|
}
|
|
}
|
|
outofloop:
|
|
if (nummatching==1)
|
|
{
|
|
if (!tryfixingp)
|
|
{
|
|
OSD_Printf(" will set wall %d's %s to %d on tryfix\n",
|
|
wallnum, yupdownwall[cf], lastwall[0]);
|
|
}
|
|
else
|
|
{
|
|
int32_t setreverse = 0;
|
|
yax_setnextwall(wallnum, cf, lastwall[0]);
|
|
if (yax_getnextwall(lastwall[0], !cf) < 0)
|
|
{
|
|
setreverse = 1;
|
|
yax_setnextwall(lastwall[0], !cf, wallnum);
|
|
}
|
|
|
|
OSD_Printf("auto-correction: set wall %d's %s to %d%s\n",
|
|
wallnum, yupdownwall[cf], lastwall[0], setreverse?" and its reverse link":"");
|
|
}
|
|
}
|
|
else if (!tryfixingp)
|
|
{
|
|
if (nummatching > 1)
|
|
{
|
|
OSD_Printf(" found more than one matching wall: at least %d and %d\n",
|
|
lastwall[0], lastwall[1]);
|
|
}
|
|
else if (nummatching == 0)
|
|
{
|
|
OSD_Printf(" found no matching walls!\n");
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// in reverse orientation
|
|
static int32_t walls_are_consistent(int32_t w1, int32_t w2)
|
|
{
|
|
return (wall[w2].x==POINT2(w1).x && wall[w2].y==POINT2(w1).y &&
|
|
wall[w1].x==POINT2(w2).x && wall[w1].y==POINT2(w2).y);
|
|
}
|
|
|
|
static void suggest_nextsector_correction(int32_t nw, int32_t j)
|
|
{
|
|
// wall j's nextsector is inconsistent with its nextwall... what shall we do?
|
|
|
|
if (nw>=0 && nw<numwalls)
|
|
{
|
|
// maybe wall[j].nextwall's nextwall is right?
|
|
if (wall[nw].nextwall==j && walls_are_consistent(nw, j))
|
|
OSD_Printf(" suggest setting wall[%d].nextsector to %d\n",
|
|
j, sectorofwall_noquick(nw));
|
|
}
|
|
|
|
// alternative
|
|
if (wall[j].nextsector>=0 && wall[j].nextsector<numsectors)
|
|
{
|
|
int32_t w, startwall, endwall;
|
|
for (WALLS_OF_SECTOR(wall[j].nextsector, w))
|
|
{
|
|
// XXX: need clearing some others?
|
|
if (walls_are_consistent(w, j))
|
|
{
|
|
OSD_Printf(" ? suggest setting wall[%d].nextwall to %d\n",
|
|
j, w);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
OSD_Printf(" ?? suggest making wall %d white\n", j);
|
|
}
|
|
|
|
static void do_nextsector_correction(int32_t nw, int32_t j)
|
|
{
|
|
if (corrupt_tryfix_alt==0)
|
|
{
|
|
if (nw>=0 && nw<numwalls)
|
|
if (wall[nw].nextwall==j && walls_are_consistent(nw, j))
|
|
{
|
|
int32_t newns = sectorofwall_noquick(nw);
|
|
wall[j].nextsector = newns;
|
|
OSD_Printf(CCHK_CORRECTED "auto-correction: set wall[%d].nextsector=%d\n",
|
|
j, newns);
|
|
}
|
|
}
|
|
else if (corrupt_tryfix_alt==1)
|
|
{
|
|
if (wall[j].nextsector>=0 && wall[j].nextsector<numsectors)
|
|
{
|
|
int32_t w, startwall, endwall;
|
|
for (WALLS_OF_SECTOR(wall[j].nextsector, w))
|
|
if (walls_are_consistent(w, j))
|
|
{
|
|
wall[j].nextwall = w;
|
|
OSD_Printf(CCHK_CORRECTED "auto-correction: set wall[%d].nextwall=%d\n",
|
|
j, w);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else if (corrupt_tryfix_alt==2)
|
|
{
|
|
wall[j].nextwall = wall[j].nextsector = -1;
|
|
OSD_Printf(CCHK_CORRECTED "auto-correction: made wall %d white\n", j);
|
|
}
|
|
}
|
|
|
|
|
|
static int32_t csc_s, csc_i;
|
|
// 1: corrupt, 0: OK
|
|
static int32_t check_spritelist_consistency()
|
|
{
|
|
int32_t s, i, ournumsprites=0;
|
|
static uint8_t havesprite[MAXSPRITES>>3];
|
|
|
|
csc_s = csc_i = -1;
|
|
|
|
if (Numsprites < 0 || Numsprites > MAXSPRITES)
|
|
return 1;
|
|
|
|
for (i=0; i<MAXSPRITES; i++)
|
|
{
|
|
const int32_t sectnum=sprite[i].sectnum, statnum=sprite[i].statnum;
|
|
|
|
csc_i = i;
|
|
|
|
if ((statnum==MAXSTATUS) != (sectnum==MAXSECTORS))
|
|
return 2; // violation of .statnum==MAXSTATUS iff .sectnum==MAXSECTORS
|
|
|
|
if ((unsigned)statnum > MAXSTATUS || (sectnum!=MAXSECTORS && (unsigned)sectnum > (unsigned)numsectors))
|
|
return 3; // oob sectnum or statnum
|
|
|
|
if (statnum != MAXSTATUS)
|
|
ournumsprites++;
|
|
}
|
|
|
|
if (ournumsprites != Numsprites)
|
|
{
|
|
initprintf("ournumsprites=%d, Numsprites=%d\n", ournumsprites, Numsprites);
|
|
return 4; // counting sprites by statnum!=MAXSTATUS inconsistent with Numsprites
|
|
}
|
|
|
|
// SECTOR LIST
|
|
|
|
Bmemset(havesprite, 0, (Numsprites+7)>>3);
|
|
|
|
for (s=0; s<numsectors; s++)
|
|
{
|
|
csc_s = s;
|
|
|
|
for (i=headspritesect[s]; i>=0; i=nextspritesect[i])
|
|
{
|
|
csc_i = i;
|
|
|
|
if (i >= MAXSPRITES)
|
|
return 5; // oob sprite index in list, or Numsprites inconsistent
|
|
|
|
if (havesprite[i>>3]&(1<<(i&7)))
|
|
return 6; // have a cycle in the list
|
|
|
|
havesprite[i>>3] |= (1<<(i&7));
|
|
|
|
if (sprite[i].sectnum != s)
|
|
return 7; // .sectnum inconsistent with list
|
|
}
|
|
|
|
if (i!=-1)
|
|
return 8; // various code checks for -1 to break loop
|
|
}
|
|
|
|
csc_s = -1;
|
|
for (i=0; i<MAXSPRITES; i++)
|
|
{
|
|
csc_i = i;
|
|
|
|
if (sprite[i].statnum!=MAXSTATUS && !(havesprite[i>>3]&(1<<(i&7))))
|
|
return 9; // have a sprite in the world not in sector list
|
|
}
|
|
|
|
|
|
// STATUS LIST -- we now clear havesprite[] bits
|
|
|
|
for (s=0; s<MAXSTATUS; s++)
|
|
{
|
|
csc_s = s;
|
|
|
|
for (i=headspritestat[s]; i>=0; i=nextspritestat[i])
|
|
{
|
|
csc_i = i;
|
|
|
|
if (i >= MAXSPRITES)
|
|
return 10; // oob sprite index in list, or Numsprites inconsistent
|
|
|
|
// have a cycle in the list, or status list inconsistent with
|
|
// sector list (*)
|
|
if (!(havesprite[i>>3]&(1<<(i&7))))
|
|
return 11;
|
|
|
|
havesprite[i>>3] &= ~(1<<(i&7));
|
|
|
|
if (sprite[i].statnum != s)
|
|
return 12; // .statnum inconsistent with list
|
|
}
|
|
|
|
if (i!=-1)
|
|
return 13; // various code checks for -1 to break loop
|
|
}
|
|
|
|
csc_s = -1;
|
|
for (i=0; i<Numsprites; i++)
|
|
{
|
|
csc_i = i;
|
|
|
|
// Status list contains only a proper subset of the sprites in the
|
|
// sector list. Reverse case is handled by (*)
|
|
if (havesprite[i>>3]&(1<<(i&7)))
|
|
return 14;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#if CCHK_LOOP_CHECKS
|
|
// Return the least wall index of the outer loop of sector <sectnum>, or
|
|
// -1 if there is none,
|
|
// -2 if there is more than one.
|
|
static int32_t determine_outer_loop(int32_t sectnum)
|
|
{
|
|
int32_t j, outerloopstart = -1;
|
|
|
|
const int32_t startwall = sector[sectnum].wallptr;
|
|
const int32_t endwall = startwall + sector[sectnum].wallnum - 1;
|
|
|
|
for (j=startwall; j<=endwall; j=get_nextloopstart(j))
|
|
{
|
|
if (clockdir(j) == CLOCKDIR_CW)
|
|
{
|
|
if (outerloopstart == -1)
|
|
outerloopstart = j;
|
|
else if (outerloopstart >= 0)
|
|
return -2;
|
|
}
|
|
}
|
|
|
|
return outerloopstart;
|
|
}
|
|
#endif
|
|
|
|
#define TRYFIX_NONE() (tryfixing == 0ull)
|
|
#define TRYFIX_CNUM(onumct) (onumct < MAXCORRUPTTHINGS && (tryfixing & (1ull<<onumct)))
|
|
|
|
int32_t CheckMapCorruption(int32_t printfromlev, uint64_t tryfixing)
|
|
{
|
|
int32_t i, j;
|
|
int32_t ewall=0; // expected wall index
|
|
|
|
int32_t errlevel=0, bad=0;
|
|
int32_t heinumcheckstat = 0; // 1, 2
|
|
|
|
uint8_t *seen_nextwalls = NULL;
|
|
int16_t *lastnextwallsource = NULL;
|
|
|
|
numcorruptthings = 0;
|
|
|
|
if (numsectors>MAXSECTORS)
|
|
CORRUPTCHK_PRINT(5, 0, CCHK_PANIC "SECTOR LIMIT EXCEEDED (MAXSECTORS=%d)!!!", MAXSECTORS);
|
|
|
|
if (numwalls>MAXWALLS)
|
|
CORRUPTCHK_PRINT(5, 0, CCHK_PANIC "WALL LIMIT EXCEEDED (MAXWALLS=%d)!!!", MAXWALLS);
|
|
|
|
if (numsectors>MAXSECTORS || numwalls>MAXWALLS)
|
|
{
|
|
corruptlevel = bad;
|
|
return bad;
|
|
}
|
|
|
|
if (numsectors==0 || numwalls==0)
|
|
{
|
|
if (numsectors>0)
|
|
CORRUPTCHK_PRINT(5, 0, CCHK_PANIC " Have sectors but no walls!");
|
|
if (numwalls>0)
|
|
CORRUPTCHK_PRINT(5, 0, CCHK_PANIC " Have walls but no sectors!");
|
|
return bad;
|
|
}
|
|
|
|
if (!corruptcheck_noalreadyrefd)
|
|
{
|
|
seen_nextwalls = (uint8_t *)Xcalloc((numwalls+7)>>3,1);
|
|
lastnextwallsource = (int16_t *)Xmalloc(numwalls*sizeof(lastnextwallsource[0]));
|
|
}
|
|
|
|
for (i=0; i<numsectors; i++)
|
|
{
|
|
const int32_t w0 = sector[i].wallptr;
|
|
const int32_t numw = sector[i].wallnum;
|
|
const int32_t endwall = w0 + numw - 1; // inclusive
|
|
|
|
bad = 0;
|
|
|
|
if (w0 < 0 || w0 > numwalls)
|
|
{
|
|
if (w0 < 0 || w0 >= MAXWALLS)
|
|
CORRUPTCHK_PRINT(5, CORRUPT_SECTOR|i, "SECTOR[%d].WALLPTR=%d INVALID!!!", i, w0);
|
|
else
|
|
CORRUPTCHK_PRINT(5, CORRUPT_SECTOR|i, "SECTOR[%d].WALLPTR=%d out of range (numwalls=%d)", i, w0, numw);
|
|
}
|
|
|
|
if (w0 != ewall)
|
|
CORRUPTCHK_PRINT(4, CORRUPT_SECTOR|i, "SECTOR[%d].WALLPTR=%d inconsistent, expected %d", i, w0, ewall);
|
|
|
|
if (numw <= 1)
|
|
CORRUPTCHK_PRINT(5, CORRUPT_SECTOR|i, CCHK_PANIC "SECTOR[%d].WALLNUM=%d INVALID!!!", i, numw);
|
|
else if (numw==2)
|
|
CORRUPTCHK_PRINT(3, CORRUPT_SECTOR|i, "SECTOR[%d].WALLNUM=2, expected at least 3", i);
|
|
|
|
ewall += numw;
|
|
|
|
if (endwall >= numwalls)
|
|
CORRUPTCHK_PRINT(5, CORRUPT_SECTOR|i, "SECTOR[%d]: wallptr+wallnum=%d out of range: numwalls=%d", i, endwall, numwalls);
|
|
|
|
// inconsistent cstat&2 and heinum checker
|
|
{
|
|
const char *cflabel[2] = {"ceiling", "floor"};
|
|
|
|
for (j=0; j<2; j++)
|
|
{
|
|
const int32_t cs = !!(SECTORFLD(i,stat, j)&2);
|
|
const int32_t hn = !!SECTORFLD(i,heinum, j);
|
|
|
|
if (cs != hn && heinumcheckstat <= 1)
|
|
{
|
|
if (numcorruptthings < MAXCORRUPTTHINGS &&
|
|
(heinumcheckstat==1 || (heinumcheckstat==0 && (tryfixing & (1ull<<numcorruptthings)))))
|
|
{
|
|
setslope(i, j, 0);
|
|
OSD_Printf(CCHK_CORRECTED "auto-correction: reset sector %d's %s slope\n",
|
|
i, cflabel[j]);
|
|
heinumcheckstat = 1;
|
|
}
|
|
else if (heinumcheckstat==0)
|
|
{
|
|
CORRUPTCHK_PRINT(1, CORRUPT_SECTOR|i,
|
|
"SECTOR[%d]: inconsistent %sstat&2 and heinum", i, cflabel[j]);
|
|
}
|
|
|
|
if (heinumcheckstat != 1)
|
|
heinumcheckstat = 2;
|
|
}
|
|
}
|
|
}
|
|
|
|
errlevel = max(errlevel, bad);
|
|
|
|
if (bad < 4)
|
|
{
|
|
for (j=w0; j<=endwall; j++)
|
|
{
|
|
const int32_t nw = wall[j].nextwall;
|
|
const int32_t ns = wall[j].nextsector;
|
|
|
|
bad = 0;
|
|
|
|
// First, some basic wall sanity checks.
|
|
|
|
if (wall[j].point2 < w0 || wall[j].point2 > endwall)
|
|
{
|
|
if (wall[j].point2 < 0 || wall[j].point2 >= MAXWALLS)
|
|
CORRUPTCHK_PRINT(5, CORRUPT_WALL|j, CCHK_PANIC "WALL[%d].POINT2=%d INVALID!!!",
|
|
j, TrackerCast(wall[j].point2));
|
|
else
|
|
CORRUPTCHK_PRINT(4, CORRUPT_WALL|j, "WALL[%d].POINT2=%d out of range [%d, %d]",
|
|
j, TrackerCast(wall[j].point2), w0, endwall);
|
|
}
|
|
|
|
if (nw >= numwalls)
|
|
{
|
|
const int32_t onumct = numcorruptthings;
|
|
|
|
if (TRYFIX_NONE())
|
|
{
|
|
if (nw >= MAXWALLS)
|
|
CORRUPTCHK_PRINT(5, CORRUPT_WALL|j, "WALL[%d].NEXTWALL=%d INVALID!!!",
|
|
j, nw);
|
|
else
|
|
CORRUPTCHK_PRINT(4, CORRUPT_WALL|j, "WALL[%d].NEXTWALL=%d out of range: numwalls=%d",
|
|
j, nw, numwalls);
|
|
OSD_Printf(" will make wall %d white on tryfix\n", j);
|
|
}
|
|
else if (TRYFIX_CNUM(onumct)) // CODEDUP MAKE_WALL_WHITE
|
|
{
|
|
wall[j].nextwall = wall[j].nextsector = -1;
|
|
OSD_Printf(CCHK_CORRECTED "auto-correction: made wall %d white\n", j);
|
|
}
|
|
}
|
|
|
|
if (ns >= numsectors)
|
|
{
|
|
const int32_t onumct = numcorruptthings;
|
|
|
|
if (TRYFIX_NONE())
|
|
{
|
|
if (ns >= MAXSECTORS)
|
|
CORRUPTCHK_PRINT(5, CORRUPT_WALL|j, "WALL[%d].NEXTSECTOR=%d INVALID!!!",
|
|
j, ns);
|
|
else
|
|
CORRUPTCHK_PRINT(4, CORRUPT_WALL|j, "WALL[%d].NEXTSECTOR=%d out of range: numsectors=%d",
|
|
j, ns, numsectors);
|
|
OSD_Printf(" will make wall %d white on tryfix\n", j);
|
|
}
|
|
else if (TRYFIX_CNUM(onumct)) // CODEDUP MAKE_WALL_WHITE
|
|
{
|
|
wall[j].nextwall = wall[j].nextsector = -1;
|
|
OSD_Printf(CCHK_CORRECTED "auto-correction: made wall %d white\n", j);
|
|
}
|
|
}
|
|
|
|
if (nw>=w0 && nw<=endwall)
|
|
CORRUPTCHK_PRINT(4, CORRUPT_WALL|j, "WALL[%d].NEXTWALL is its own sector's wall", j);
|
|
|
|
if (wall[j].x==POINT2(j).x && wall[j].y==POINT2(j).y)
|
|
CORRUPTCHK_PRINT(3, CORRUPT_WALL|j, "WALL[%d] has length 0", j);
|
|
|
|
#ifdef YAX_ENABLE
|
|
// Various TROR checks.
|
|
{
|
|
int32_t cf;
|
|
|
|
for (cf=0; cf<2; cf++)
|
|
{
|
|
const int32_t ynw = yax_getnextwall(j, cf);
|
|
|
|
if (ynw >= 0)
|
|
{
|
|
if (ynw >= numwalls)
|
|
CORRUPTCHK_PRINT(4, CORRUPT_WALL|j, "WALL %d's %s=%d out of range: numwalls=%d",
|
|
j, YUPDOWNWALL[cf], ynw, numwalls);
|
|
else
|
|
{
|
|
int32_t ynextwallok = 1;
|
|
|
|
if (j == ynw)
|
|
{
|
|
CORRUPTCHK_PRINT(4, CORRUPT_WALL|j, "WALL %d's %s is itself",
|
|
j, YUPDOWNWALL[cf]);
|
|
ynextwallok = 0;
|
|
}
|
|
else if (!walls_have_equal_endpoints(j, ynw))
|
|
{
|
|
CORRUPTCHK_PRINT(4, CORRUPT_WALL|j, "WALL %d's and its %s=%d's "
|
|
"endpoints are inconsistent", j, YUPDOWNWALL[cf], ynw);
|
|
ynextwallok = 0;
|
|
}
|
|
|
|
{
|
|
const int16_t bunchnum = yax_getbunch(i, cf);
|
|
const int32_t onumct = numcorruptthings;
|
|
|
|
if (bunchnum < 0 || bunchnum >= numyaxbunches)
|
|
{
|
|
if (tryfixing == 0ull)
|
|
{
|
|
CORRUPTCHK_PRINT(4, CORRUPT_WALL|j, "WALL %d has %s=%d, "
|
|
"but its %s bunchnum=%d is invalid",
|
|
j, YUPDOWNWALL[cf], ynw,
|
|
cf==YAX_CEILING? "ceiling":"floor", bunchnum);
|
|
OSD_Printf(" will clear wall %d's %s to -1 on tryfix\n",
|
|
j, yupdownwall[cf]);
|
|
|
|
}
|
|
else if (tryfixing & (1ull<<onumct))
|
|
{
|
|
yax_setnextwall(j, cf, -1);
|
|
OSD_Printf(CCHK_CORRECTED "auto-correction: cleared wall %d's %s to -1\n",
|
|
j, yupdownwall[cf]);
|
|
}
|
|
}
|
|
else if (!ynextwallok && onumct < MAXCORRUPTTHINGS)
|
|
{
|
|
if ((tryfixing & (1ull<<onumct)) || 4>=printfromlev)
|
|
correct_yax_nextwall(j, bunchnum, cf, tryfixing!=0ull);
|
|
}
|
|
}
|
|
|
|
if (ynextwallok)
|
|
{
|
|
const int32_t onumct = numcorruptthings;
|
|
const int32_t ynwp2 = yax_getnextwall(ynw, !cf);
|
|
|
|
if (ynwp2 != j)
|
|
{
|
|
CORRUPTCHK_PRINT(4, CORRUPT_WALL|j, "WALL %d's %s=%d's reverse link wrong"
|
|
" (expected %d, have %d)", j, YUPDOWNWALL[cf], ynw, j, ynwp2);
|
|
if (onumct < MAXCORRUPTTHINGS)
|
|
{
|
|
if (tryfixing & (1ull<<onumct))
|
|
{
|
|
yax_setnextwall(ynw, !cf, j);
|
|
OSD_Printf(CCHK_CORRECTED "auto-correction: set wall %d's %s=%d's %s to %d\n",
|
|
j, yupdownwall[cf], ynw, yupdownwall[!cf], j);
|
|
}
|
|
else if (4>=printfromlev)
|
|
{
|
|
OSD_Printf(" will set wall %d's %s=%d's %s to %d on tryfix\n",
|
|
j, yupdownwall[cf], ynw, yupdownwall[!cf], j);
|
|
}
|
|
}
|
|
}
|
|
} // brace woot!
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
// Check for ".nextsector is its own sector"
|
|
if (ns == i)
|
|
{
|
|
if (!bad)
|
|
{
|
|
const int32_t onumct = numcorruptthings;
|
|
const int32_t safetoclear = (nw==j || (wall[nw].nextwall==-1 && wall[nw].nextsector==-1));
|
|
|
|
CORRUPTCHK_PRINT(4, CORRUPT_WALL|j, "WALL[%d].NEXTSECTOR is its own sector", j);
|
|
if (onumct < MAXCORRUPTTHINGS)
|
|
{
|
|
if (tryfixing & (1ull<<onumct))
|
|
{
|
|
if (safetoclear)
|
|
{
|
|
wall[j].nextwall = wall[j].nextsector = -1;
|
|
OSD_Printf(CCHK_CORRECTED "auto-correction: cleared wall %d's nextwall"
|
|
" and nextsector\n", j);
|
|
}
|
|
else
|
|
do_nextsector_correction(nw, j);
|
|
}
|
|
else if (4>=printfromlev)
|
|
{
|
|
if (safetoclear)
|
|
OSD_Printf(" will clear wall %d's nextwall and nextsector on tryfix\n", j);
|
|
else
|
|
suggest_nextsector_correction(nw, j);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check for ".nextwall already referenced from wall ..."
|
|
if (!corruptcheck_noalreadyrefd && nw>=0 && nw<numwalls)
|
|
{
|
|
if (seen_nextwalls[nw>>3]&(1<<(nw&7)))
|
|
{
|
|
const int32_t onumct = numcorruptthings;
|
|
|
|
const int16_t lnws = lastnextwallsource[nw];
|
|
const int16_t nwnw = wall[nw].nextwall;
|
|
|
|
CORRUPTCHK_PRINT(3, CORRUPT_WALL|j, "WALL[%d].NEXTWALL=%d already referenced from wall %d",
|
|
j, nw, lnws);
|
|
|
|
if (onumct < MAXCORRUPTTHINGS && (nwnw==j || nwnw==lnws))
|
|
{
|
|
const int32_t walltoclear = nwnw==j ? lnws : j;
|
|
|
|
if (tryfixing & (1ull<<onumct))
|
|
{
|
|
wall[walltoclear].nextsector = wall[walltoclear].nextwall = -1;
|
|
OSD_Printf(CCHK_CORRECTED "auto-correction: cleared wall %d's nextwall and nextsector tags to -1\n",
|
|
walltoclear);
|
|
}
|
|
else if (3 >= printfromlev)
|
|
OSD_Printf(" wall[%d].nextwall=%d, suggest clearing wall %d's nextwall and nextsector tags to -1\n",
|
|
nw, nwnw, walltoclear);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
seen_nextwalls[nw>>3] |= 1<<(nw&7);
|
|
lastnextwallsource[nw] = j;
|
|
}
|
|
}
|
|
|
|
// Various checks of .nextsector and .nextwall
|
|
if (bad < 4)
|
|
{
|
|
const int32_t onumct = numcorruptthings;
|
|
|
|
if ((ns^nw)<0)
|
|
{
|
|
CORRUPTCHK_PRINT(4, CORRUPT_WALL|j, "WALL[%d].NEXTSECTOR=%d and .NEXTWALL=%d inconsistent:"
|
|
" missing one next pointer", j, ns, nw);
|
|
if (onumct < MAXCORRUPTTHINGS)
|
|
{
|
|
if (tryfixing & (1ull<<onumct))
|
|
do_nextsector_correction(nw, j);
|
|
else if (4>=printfromlev)
|
|
suggest_nextsector_correction(nw, j);
|
|
}
|
|
}
|
|
else if (ns>=0)
|
|
{
|
|
if (nw<sector[ns].wallptr || nw>=sector[ns].wallptr+sector[ns].wallnum)
|
|
{
|
|
CORRUPTCHK_PRINT(4, CORRUPT_WALL|j, "WALL[%d].NEXTWALL=%d out of .NEXTSECTOR=%d's bounds [%d .. %d]",
|
|
j, nw, ns, TrackerCast(sector[ns].wallptr), sector[ns].wallptr+sector[ns].wallnum-1);
|
|
if (onumct < MAXCORRUPTTHINGS)
|
|
{
|
|
if (tryfixing & (1ull<<onumct))
|
|
do_nextsector_correction(nw, j);
|
|
else if (4 >= printfromlev)
|
|
suggest_nextsector_correction(nw, j);
|
|
}
|
|
}
|
|
#if 0
|
|
// this one usually appears together with the "already referenced" corruption
|
|
else if (wall[nw].nextsector != i || wall[nw].nextwall != j)
|
|
{
|
|
CORRUPTCHK_PRINT(4, CORRUPT_WALL|nw, "WALL %d nextwall's backreferences inconsistent. Expected nw=%d, ns=%d; got nw=%d, ns=%d",
|
|
nw, i, j, wall[nw].nextsector, wall[nw].nextwall);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
errlevel = max(errlevel, bad);
|
|
}
|
|
}
|
|
#if CCHK_LOOP_CHECKS
|
|
// Wall loop checks.
|
|
if (bad < 4 && numw >= 4)
|
|
{
|
|
const int32_t outerloopstart = determine_outer_loop(i);
|
|
|
|
if (outerloopstart == -1)
|
|
CORRUPTCHK_PRINT(4, CORRUPT_SECTOR|i, "SECTOR %d contains no outer (clockwise) loop", i);
|
|
else if (outerloopstart == -2)
|
|
CORRUPTCHK_PRINT(3, CORRUPT_SECTOR|i, "SECTOR %d contains more than one outer (clockwise) loops", i);
|
|
# if CCHK_LOOP_CHECKS >= 2
|
|
else
|
|
{
|
|
// Now, check for whether every wall-point of every inner loop of
|
|
// this sector (<i>) is inside the outer one.
|
|
|
|
for (j=w0; j<=endwall; /* will step by loops */)
|
|
{
|
|
const int32_t nextloopstart = get_nextloopstart(j);
|
|
|
|
if (j != outerloopstart)
|
|
{
|
|
int32_t k;
|
|
|
|
for (k=j; k<nextloopstart; k++)
|
|
if (!loopinside(wall[k].x, wall[k].y, outerloopstart))
|
|
{
|
|
CORRUPTCHK_PRINT(4, CORRUPT_WALL|k, "WALL %d (of sector %d) is outside the outer loop", k, i);
|
|
goto end_wall_loop_checks;
|
|
}
|
|
}
|
|
|
|
j = nextloopstart;
|
|
}
|
|
end_wall_loop_checks:
|
|
;
|
|
}
|
|
# endif
|
|
errlevel = max(errlevel, bad);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
bad = 0;
|
|
for (i=0; i<MAXSPRITES; i++)
|
|
{
|
|
if (sprite[i].statnum==MAXSTATUS)
|
|
continue;
|
|
|
|
if (sprite[i].sectnum<0 || sprite[i].sectnum>=numsectors)
|
|
CORRUPTCHK_PRINT(4, CORRUPT_SPRITE|i, "SPRITE[%d].SECTNUM=%d. Expect problems!", i, TrackerCast(sprite[i].sectnum));
|
|
|
|
if (sprite[i].statnum<0 || sprite[i].statnum>MAXSTATUS)
|
|
CORRUPTCHK_PRINT(4, CORRUPT_SPRITE|i, "SPRITE[%d].STATNUM=%d. Expect problems!", i, TrackerCast(sprite[i].statnum));
|
|
|
|
if (sprite[i].picnum<0 || sprite[i].picnum>=MAXTILES)
|
|
{
|
|
sprite[i].picnum = 0;
|
|
CORRUPTCHK_PRINT(0, CORRUPT_SPRITE|i, "SPRITE[%d].PICNUM=%d out of range, resetting to 0", i, TrackerCast(sprite[i].picnum));
|
|
}
|
|
|
|
if (klabs(sprite[i].x) > BXY_MAX || klabs(sprite[i].y) > BXY_MAX)
|
|
{
|
|
const int32_t onumct = numcorruptthings;
|
|
|
|
CORRUPTCHK_PRINT(3, CORRUPT_SPRITE|i, "SPRITE %d at [%d, %d] is outside the maximal grid range [%d, %d]",
|
|
i, TrackerCast(sprite[i].x), TrackerCast(sprite[i].y), -BXY_MAX, BXY_MAX);
|
|
|
|
if (onumct < MAXCORRUPTTHINGS)
|
|
{
|
|
int32_t x=0, y=0, ok=0;
|
|
const int32_t sect = sprite[i].sectnum;
|
|
|
|
if ((unsigned)sect < (unsigned)numsectors)
|
|
{
|
|
const int32_t firstwall = sector[sect].wallptr;
|
|
|
|
if ((unsigned)firstwall < (unsigned)numwalls)
|
|
{
|
|
x = wall[firstwall].x;
|
|
y = wall[firstwall].y;
|
|
ok = 1;
|
|
}
|
|
}
|
|
|
|
if (!(tryfixing & (1ull<<onumct)))
|
|
{
|
|
if (ok && 3 >= printfromlev)
|
|
OSD_Printf(" will reposition to its sector's (%d) first"
|
|
" point [%d,%d] on tryfix\n", sect, x, y);
|
|
}
|
|
else
|
|
{
|
|
if (ok)
|
|
{
|
|
sprite[i].x = x;
|
|
sprite[i].y = y;
|
|
OSD_Printf(CCHK_CORRECTED "auto-correction: repositioned sprite %d to "
|
|
"its sector's (%d) first point [%d,%d]\n", i, sect, x, y);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
i = check_spritelist_consistency();
|
|
if (i)
|
|
CORRUPTCHK_PRINT(5, i<0?0:(CORRUPT_SPRITE|i), CCHK_PANIC "SPRITE LISTS CORRUPTED: error code %d, s=%d, i=%d!",
|
|
i, csc_s, csc_i);
|
|
|
|
if (0)
|
|
{
|
|
too_many_errors:
|
|
if (printfromlev<=errlevel)
|
|
OSD_Printf("!! too many errors, stopping. !!\n");
|
|
}
|
|
|
|
errlevel = max(errlevel, bad);
|
|
|
|
if (errlevel)
|
|
{
|
|
if (printfromlev<=errlevel)
|
|
OSD_Printf("-- corruption level: %d\n", errlevel);
|
|
if (tryfixing)
|
|
OSD_Printf("--\n");
|
|
}
|
|
|
|
if (seen_nextwalls)
|
|
{
|
|
Bfree(seen_nextwalls);
|
|
Bfree(lastnextwallsource);
|
|
}
|
|
|
|
corruptlevel = errlevel;
|
|
|
|
return errlevel;
|
|
}
|
|
////
|
|
|
|
|
|
////////// STATUS BAR MENU "class" //////////
|
|
|
|
#define MENU_MAX_ENTRIES (8*3)
|
|
#define MENU_ENTRY_SIZE 25 // max. length of label (including terminating NUL)
|
|
|
|
#define MENU_Y_SPACING 8
|
|
#define MENU_BASE_Y (ydim-STATUS2DSIZ+32)
|
|
|
|
#define MENU_FG_COLOR editorcolors[11]
|
|
#define MENU_BG_COLOR editorcolors[0]
|
|
#define MENU_BG_COLOR_SEL editorcolors[1]
|
|
|
|
#ifdef LUNATIC
|
|
# define MENU_HAVE_DESCRIPTION 1
|
|
#else
|
|
# define MENU_HAVE_DESCRIPTION 0
|
|
#endif
|
|
|
|
typedef struct StatusBarMenu_ {
|
|
const char *const menuname;
|
|
const int32_t custom_start_index;
|
|
int32_t numentries;
|
|
|
|
void (*process_func)(const struct StatusBarMenu_ *m, int32_t col, int32_t row);
|
|
|
|
intptr_t auxdata[MENU_MAX_ENTRIES];
|
|
char *description[MENU_MAX_ENTRIES]; // strdup'd description string, NULL if non
|
|
char name[MENU_MAX_ENTRIES][MENU_ENTRY_SIZE];
|
|
} StatusBarMenu;
|
|
|
|
#define MENU_INITIALIZER_EMPTY(MenuName, ProcessFunc) \
|
|
{ MenuName, 0, 0, ProcessFunc, {}, {}, {} }
|
|
#define MENU_INITIALIZER(MenuName, CustomStartIndex, ProcessFunc, ...) \
|
|
{ MenuName, CustomStartIndex, CustomStartIndex, ProcessFunc, {}, {}, ## __VA_ARGS__ }
|
|
|
|
#ifdef LUNATIC
|
|
static void M_Clear(StatusBarMenu *m)
|
|
{
|
|
int32_t i;
|
|
|
|
m->numentries = 0;
|
|
Bmemset(m->auxdata, 0, sizeof(m->auxdata));
|
|
Bmemset(m->name, 0, sizeof(m->name));
|
|
|
|
for (i=0; i<MENU_MAX_ENTRIES; i++)
|
|
{
|
|
Bfree(m->description[i]);
|
|
m->description[i] = NULL;
|
|
}
|
|
}
|
|
|
|
static int32_t M_HaveDescription(StatusBarMenu *m)
|
|
{
|
|
int32_t i;
|
|
|
|
for (i=0; i<MENU_MAX_ENTRIES; i++)
|
|
if (m->description[i] != NULL)
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
// NOTE: Does not handle description strings! (Only the Lua menu uses them.)
|
|
static void M_UnregisterFunction(StatusBarMenu *m, intptr_t auxdata)
|
|
{
|
|
int32_t i, j;
|
|
|
|
for (i=m->custom_start_index; i<m->numentries; i++)
|
|
if (m->auxdata[i]==auxdata)
|
|
{
|
|
for (j=i; j<m->numentries-1; j++)
|
|
{
|
|
m->auxdata[j] = m->auxdata[j+1];
|
|
Bmemcpy(m->name[j], m->name[j+1], MENU_ENTRY_SIZE);
|
|
}
|
|
|
|
m->auxdata[j] = 0;
|
|
Bmemset(m->name[j], 0, MENU_ENTRY_SIZE);
|
|
|
|
m->numentries--;
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void M_RegisterFunction(StatusBarMenu *m, const char *name, intptr_t auxdata, const char *description)
|
|
{
|
|
int32_t i;
|
|
|
|
for (i=8; i<m->numentries; i++)
|
|
{
|
|
if (m->auxdata[i]==auxdata)
|
|
{
|
|
// same auxdata, different name
|
|
Bstrncpyz(m->name[i], name, MENU_ENTRY_SIZE);
|
|
return;
|
|
}
|
|
else if (!Bstrncmp(m->name[i], name, MENU_ENTRY_SIZE))
|
|
{
|
|
// same name, different auxdata
|
|
m->auxdata[i] = auxdata;
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (m->numentries == MENU_MAX_ENTRIES)
|
|
return; // max reached
|
|
|
|
Bstrncpyz(m->name[m->numentries], name, MENU_ENTRY_SIZE);
|
|
m->auxdata[m->numentries] = auxdata;
|
|
|
|
#if MENU_HAVE_DESCRIPTION
|
|
// NOTE: description only handled here (not above).
|
|
if (description)
|
|
m->description[m->numentries] = Xstrdup(description);
|
|
#else
|
|
UNREFERENCED_PARAMETER(description);
|
|
#endif
|
|
|
|
m->numentries++;
|
|
}
|
|
|
|
static void M_DisplayInitial(const StatusBarMenu *m)
|
|
{
|
|
int32_t x = 8, y = MENU_BASE_Y+16;
|
|
int32_t i;
|
|
|
|
for (i=0; i<m->numentries; i++)
|
|
{
|
|
if (i==8 || i==16)
|
|
{
|
|
x += 208;
|
|
y = MENU_BASE_Y+16;
|
|
}
|
|
|
|
printext16(x,y, MENU_FG_COLOR, MENU_BG_COLOR, m->name[i], 0);
|
|
y += MENU_Y_SPACING;
|
|
}
|
|
|
|
printext16(m->numentries>8 ? 216 : 8, MENU_BASE_Y, MENU_FG_COLOR, -1, m->menuname, 0);
|
|
|
|
clearkeys();
|
|
}
|
|
|
|
static void M_EnterMainLoop(StatusBarMenu *m)
|
|
{
|
|
char disptext[80];
|
|
const int32_t dispwidth = MENU_ENTRY_SIZE-1;
|
|
|
|
int32_t i, col=0, row=0;
|
|
int32_t crowmax[3] = {-1, -1, -1};
|
|
int32_t xpos = 8, ypos = MENU_BASE_Y+16;
|
|
|
|
if (m->numentries == 0)
|
|
{
|
|
printmessage16("%s menu has no entries", m->menuname);
|
|
return;
|
|
}
|
|
|
|
Bmemset(disptext, 0, sizeof(disptext));
|
|
|
|
Bassert((unsigned)m->numentries <= MENU_MAX_ENTRIES);
|
|
for (i=0; i<=(m->numentries-1)/8; i++)
|
|
crowmax[i] = (m->numentries >= (i+1)*8) ? 7 : (m->numentries-1)&7;
|
|
|
|
drawgradient();
|
|
M_DisplayInitial(m);
|
|
|
|
while (keystatus[KEYSC_ESC] == 0)
|
|
{
|
|
idle_waitevent();
|
|
if (handleevents())
|
|
quitevent = 0;
|
|
|
|
_printmessage16("Select an option, press <Esc> to exit");
|
|
|
|
if (PRESSED_KEYSC(DOWN))
|
|
{
|
|
if (row < crowmax[col])
|
|
{
|
|
printext16(xpos, ypos+row*MENU_Y_SPACING, MENU_FG_COLOR, MENU_BG_COLOR, disptext, 0);
|
|
row++;
|
|
}
|
|
}
|
|
else if (PRESSED_KEYSC(UP))
|
|
{
|
|
if (row > 0)
|
|
{
|
|
printext16(xpos, ypos+row*MENU_Y_SPACING, MENU_FG_COLOR, MENU_BG_COLOR, disptext, 0);
|
|
row--;
|
|
}
|
|
}
|
|
else if (PRESSED_KEYSC(LEFT))
|
|
{
|
|
if (col > 0)
|
|
{
|
|
printext16(xpos, ypos+row*8, MENU_FG_COLOR, 0, disptext, 0);
|
|
col--;
|
|
xpos -= 208;
|
|
disptext[dispwidth] = 0;
|
|
row = min(crowmax[col], row);
|
|
}
|
|
}
|
|
else if (PRESSED_KEYSC(RIGHT))
|
|
{
|
|
if (col < 2 && crowmax[col+1]>=0)
|
|
{
|
|
printext16(xpos, ypos+row*8, MENU_FG_COLOR, 0, disptext, 0);
|
|
col++;
|
|
xpos += 208;
|
|
disptext[dispwidth] = 0;
|
|
row = min(crowmax[col], row);
|
|
}
|
|
}
|
|
|
|
for (i=Bsnprintf(disptext, dispwidth, "%s", m->name[col*8 + row]); i < dispwidth; i++)
|
|
disptext[i] = ' ';
|
|
|
|
if (PRESSED_KEYSC(ENTER))
|
|
{
|
|
Bassert(m->process_func != NULL);
|
|
m->process_func(m, col, row);
|
|
break;
|
|
}
|
|
|
|
#if MENU_HAVE_DESCRIPTION
|
|
if (M_HaveDescription(m))
|
|
{
|
|
const int32_t maxrows = 20;
|
|
int32_t r;
|
|
|
|
for (r=0; r<maxrows+1; r++)
|
|
printext16(16-4, 16-4 + r*8, 0, MENU_BG_COLOR, // 71 blanks:
|
|
" ", 0);
|
|
if (m->description[col*8 + row] != NULL)
|
|
printext16(16, 16, MENU_FG_COLOR, MENU_BG_COLOR, m->description[col*8 + row], 0);
|
|
}
|
|
#endif
|
|
printext16(xpos, ypos+row*MENU_Y_SPACING, MENU_FG_COLOR, MENU_BG_COLOR_SEL, disptext, 0);
|
|
showframe(1);
|
|
}
|
|
|
|
printext16(xpos, ypos+row*MENU_Y_SPACING, MENU_FG_COLOR, MENU_BG_COLOR, disptext, 0);
|
|
showframe(1);
|
|
|
|
keystatus[KEYSC_ESC] = 0;
|
|
}
|
|
|
|
////////// SPECIAL FUNCTIONS MENU //////////
|
|
|
|
static void FuncMenu_Process(const StatusBarMenu *m, int32_t col, int32_t row);
|
|
|
|
static StatusBarMenu g_specialFuncMenu = MENU_INITIALIZER(
|
|
"Special functions", 8, FuncMenu_Process,
|
|
|
|
{
|
|
"Replace invalid tiles",
|
|
"Delete all spr of tile #",
|
|
"Set map sky shade",
|
|
"Set map sky height",
|
|
"Global Z coord shift",
|
|
"Resize selection",
|
|
"Global shade divide",
|
|
"Global visibility divide"
|
|
}
|
|
);
|
|
|
|
// "External functions":
|
|
|
|
void FuncMenu(void)
|
|
{
|
|
M_EnterMainLoop(&g_specialFuncMenu);
|
|
}
|
|
|
|
void registerMenuFunction(const char *funcname, int32_t stateidx)
|
|
{
|
|
if (funcname)
|
|
M_RegisterFunction(&g_specialFuncMenu, funcname, stateidx, NULL);
|
|
else
|
|
M_UnregisterFunction(&g_specialFuncMenu, stateidx);
|
|
}
|
|
|
|
// The processing function...
|
|
|
|
static int32_t correct_picnum(int16_t *picnumptr)
|
|
{
|
|
int32_t picnum = *picnumptr;
|
|
|
|
if ((unsigned)picnum >= MAXTILES || tilesizx[picnum] <= 0)
|
|
{
|
|
*picnumptr = 0;
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void FuncMenu_Process(const StatusBarMenu *m, int32_t col, int32_t row)
|
|
{
|
|
int32_t i, j;
|
|
|
|
switch (col)
|
|
{
|
|
|
|
case 1:
|
|
case 2:
|
|
{
|
|
const int32_t stateidx = (Bassert(m->auxdata[col*8 + row] < g_stateCount),
|
|
m->auxdata[col*8 + row]);
|
|
|
|
const char *statename = statesinfo[stateidx].name;
|
|
int32_t snlen = Bstrlen(statename);
|
|
char *tmpscript = (char *)Xmalloc(1+5+1+snlen+1);
|
|
|
|
tmpscript[0] = ' '; // don't save in history
|
|
Bmemcpy(&tmpscript[1], "state", 5);
|
|
tmpscript[1+5] = ' ';
|
|
Bmemcpy(&tmpscript[1+5+1], statename, snlen);
|
|
tmpscript[1+5+1+snlen] = 0;
|
|
|
|
M32RunScript(tmpscript);
|
|
Bfree(tmpscript);
|
|
|
|
if (vm.flags&VMFLAG_ERROR)
|
|
printmessage16("There were errors while executing the menu function");
|
|
else if (lastpm16time != totalclock)
|
|
printmessage16("Menu function executed successfully");
|
|
}
|
|
break;
|
|
|
|
case 0:
|
|
{
|
|
switch (row)
|
|
{
|
|
|
|
case 0:
|
|
{
|
|
j = 0;
|
|
|
|
for (i=0; i<MAXSECTORS; i++)
|
|
{
|
|
j += correct_picnum(§or[i].ceilingpicnum);
|
|
j += correct_picnum(§or[i].floorpicnum);
|
|
}
|
|
|
|
for (i=0; i<MAXWALLS; i++)
|
|
{
|
|
j += correct_picnum(&wall[i].picnum);
|
|
j += correct_picnum(&wall[i].overpicnum);
|
|
}
|
|
for (i=0; i<MAXSPRITES; i++)
|
|
j += correct_picnum(&sprite[i].picnum);
|
|
|
|
printmessage16("Replaced %d invalid tiles",j);
|
|
}
|
|
break;
|
|
|
|
case 1:
|
|
{
|
|
char tempbuf[64];
|
|
Bsprintf(tempbuf,"Delete all sprites of tile #: ");
|
|
i = getnumber16(tempbuf,-1,MAXSPRITES-1,1);
|
|
if (i >= 0)
|
|
{
|
|
int32_t k = 0;
|
|
for (j=0; j<MAXSPRITES; j++)
|
|
if (sprite[j].picnum == i)
|
|
deletesprite(j), k++;
|
|
printmessage16("%d sprite(s) deleted",k);
|
|
}
|
|
else printmessage16("Aborted");
|
|
}
|
|
break;
|
|
|
|
case 2:
|
|
{
|
|
j=getnumber16("Set map sky shade: ",0,128,1);
|
|
|
|
for (i=0; i<numsectors; i++)
|
|
{
|
|
if (sector[i].ceilingstat&1)
|
|
sector[i].ceilingshade = j;
|
|
}
|
|
printmessage16("All parallax skies adjusted");
|
|
}
|
|
break;
|
|
|
|
case 3:
|
|
{
|
|
j=getnumber16("Set map sky height: ",0,16777216,1);
|
|
if (j != 0)
|
|
{
|
|
for (i=0; i<numsectors; i++)
|
|
{
|
|
if (sector[i].ceilingstat&1)
|
|
sector[i].ceilingz = j;
|
|
}
|
|
printmessage16("All parallax skies adjusted");
|
|
}
|
|
else printmessage16("Aborted");
|
|
}
|
|
break;
|
|
|
|
case 4:
|
|
{
|
|
j=getnumber16("Z offset: ",0,16777216,1);
|
|
if (j!=0)
|
|
{
|
|
for (i=0; i<numsectors; i++)
|
|
{
|
|
sector[i].ceilingz += j;
|
|
sector[i].floorz += j;
|
|
}
|
|
for (i=0; i<MAXSPRITES; i++)
|
|
sprite[i].z += j;
|
|
printmessage16("Map adjusted");
|
|
}
|
|
else printmessage16("Aborted");
|
|
}
|
|
break;
|
|
|
|
case 5:
|
|
{
|
|
j=getnumber16("Percentage of original: ",100,1000,0);
|
|
|
|
if (j!=100 && j!=0)
|
|
{
|
|
int32_t w, currsector, start_wall, end_wall;
|
|
double size = (j/100.f);
|
|
|
|
for (i = 0; i < highlightsectorcnt; i++)
|
|
{
|
|
currsector = highlightsector[i];
|
|
sector[currsector].ceilingz = (int32_t)(sector[currsector].ceilingz*size);
|
|
sector[currsector].floorz = (int32_t)(sector[currsector].floorz*size);
|
|
|
|
// Do all the walls in the sector
|
|
start_wall = sector[currsector].wallptr;
|
|
end_wall = start_wall + sector[currsector].wallnum;
|
|
|
|
for (w = start_wall; w < end_wall; w++)
|
|
{
|
|
wall[w].x = (int32_t)(wall[w].x*size);
|
|
wall[w].y = (int32_t)(wall[w].y*size);
|
|
wall[w].yrepeat = min((int32_t)(wall[w].yrepeat/size),255);
|
|
}
|
|
|
|
w = headspritesect[highlightsector[i]];
|
|
while (w >= 0)
|
|
{
|
|
sprite[w].x = (int32_t)(sprite[w].x*size);
|
|
sprite[w].y = (int32_t)(sprite[w].y*size);
|
|
sprite[w].z = (int32_t)(sprite[w].z*size);
|
|
sprite[w].xrepeat = min(max((int32_t)(sprite[w].xrepeat*size),1),255);
|
|
sprite[w].yrepeat = min(max((int32_t)(sprite[w].yrepeat*size),1),255);
|
|
w = nextspritesect[w];
|
|
}
|
|
}
|
|
printmessage16("Map scaled");
|
|
}
|
|
else printmessage16("Aborted");
|
|
}
|
|
break;
|
|
|
|
case 6:
|
|
{
|
|
j=getnumber16("Shade divisor: ",1,128,1);
|
|
if (j > 1)
|
|
{
|
|
for (i=0; i<numsectors; i++)
|
|
{
|
|
sector[i].ceilingshade /= j;
|
|
sector[i].floorshade /= j;
|
|
}
|
|
for (i=0; i<numwalls; i++)
|
|
wall[i].shade /= j;
|
|
for (i=0; i<MAXSPRITES; i++)
|
|
sprite[i].shade /= j;
|
|
printmessage16("Shades adjusted");
|
|
}
|
|
else printmessage16("Aborted");
|
|
}
|
|
break;
|
|
|
|
case 7:
|
|
{
|
|
j=getnumber16("Visibility divisor: ",1,128,0);
|
|
if (j > 1)
|
|
{
|
|
for (i=0; i<numsectors; i++)
|
|
{
|
|
if (sector[i].visibility < 240)
|
|
sector[i].visibility /= j;
|
|
else sector[i].visibility = 240 + (sector[i].visibility>>4)/j;
|
|
}
|
|
printmessage16("Visibility adjusted");
|
|
}
|
|
else printmessage16("Aborted");
|
|
}
|
|
break;
|
|
|
|
} // switch (row)
|
|
}
|
|
break; // switch (col) / case 0
|
|
|
|
} // switch (col)
|
|
}
|
|
|
|
#ifdef LUNATIC
|
|
typedef const char *(*luamenufunc_t)(void);
|
|
|
|
static int32_t g_numLuaFuncs = 0;
|
|
static luamenufunc_t g_LuaFuncPtrs[MENU_MAX_ENTRIES];
|
|
|
|
static void LuaFuncMenu_Process(const StatusBarMenu *m, int32_t col, int32_t row)
|
|
{
|
|
luamenufunc_t func = g_LuaFuncPtrs[col*8 + row];
|
|
const char *errmsg;
|
|
|
|
Bassert(func != NULL);
|
|
errmsg = func();
|
|
|
|
if (errmsg == NULL)
|
|
{
|
|
printmessage16("Lua function executed successfully");
|
|
}
|
|
else
|
|
{
|
|
printmessage16("There were errors executing the Lua function, see OSD");
|
|
OSD_Printf("Errors executing Lua function \"%s\": %s\n", m->name[col*8 + row], errmsg);
|
|
}
|
|
}
|
|
|
|
static StatusBarMenu g_LuaFuncMenu = MENU_INITIALIZER_EMPTY("Lua functions", LuaFuncMenu_Process);
|
|
|
|
void LuaFuncMenu(void)
|
|
{
|
|
M_EnterMainLoop(&g_LuaFuncMenu);
|
|
}
|
|
|
|
LUNATIC_EXTERN void LM_Register(const char *name, luamenufunc_t funcptr, const char *description)
|
|
{
|
|
if (name == NULL || g_numLuaFuncs == MENU_MAX_ENTRIES)
|
|
return;
|
|
|
|
g_LuaFuncPtrs[g_numLuaFuncs] = funcptr;
|
|
M_RegisterFunction(&g_LuaFuncMenu, name, g_numLuaFuncs, description);
|
|
g_numLuaFuncs++;
|
|
}
|
|
|
|
LUNATIC_EXTERN void LM_Clear(void)
|
|
{
|
|
M_Clear(&g_LuaFuncMenu);
|
|
|
|
g_numLuaFuncs = 0;
|
|
Bmemset(g_LuaFuncPtrs, 0, sizeof(g_LuaFuncPtrs));
|
|
}
|
|
#endif
|