Subversion Repositories eduke32

Rev

Rev 859 | Blame | Compare with Previous | Last modification | View Log | RSS feed

//-------------------------------------------------------------------------
/*
Copyright (C) 1996, 2003 - 3D Realms Entertainment
Copyright (C) 2004, 2007 - EDuke32 developers

This file is part of EDuke32

EDuke32 is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License version 2
as published by the Free Software Foundation.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/

//-------------------------------------------------------------------------

#include "compat.h"
#include "build.h"
#include "editor.h"
#include "pragmas.h"
#include "baselayer.h"
#include "names.h"
#include "osd.h"
#include "osdfuncs.h"
#include "cache1d.h"

#include "mapster32.h"
#include "keys.h"
#include "types.h"
#include "keyboard.h"
#include "scriptfile.h"
#include "crc32.h"

#define VERSION " 1.2.0devel"

static int floor_over_floor;

// static char *startwin_labeltext = "Starting Mapster32...";
static char setupfilename[BMAX_PATH]= "mapster32.cfg";
static char defaultduke3dgrp[BMAX_PATH] = "duke3d.grp";
static char *duke3dgrp = defaultduke3dgrp;
static int fixmapbeforesaving = 1;
static int lastsave = -180*60;
static int NoAutoLoad = 0;
int spnoclip=1;

#if !defined(_WIN32)
static int usecwd = 0;
#endif

static struct strllist
{
    struct strllist *next;
    char *str;
}
*CommandPaths = NULL, *CommandGrps = NULL;

#define MAXHELP2D (signed int)(sizeof(Help2d)/sizeof(Help2d[0]))

#define eitherALT   (keystatus[KEYSC_LALT]|  keystatus[KEYSC_RALT])
#define eitherCTRL  (keystatus[KEYSC_LCTRL]| keystatus[KEYSC_RCTRL])
#define eitherSHIFT (keystatus[KEYSC_LSHIFT]|keystatus[KEYSC_RSHIFT])

static char *Help2d[]=
{
    " 'A = Autosave toggle",
    " 'J = Jump to location",
    " 'L = Adjust sprite/wall coords",
    " 'S = Sprite size",
    " '3 = Caption mode",
    " '7 = Swap tags",
    " 'F = Special functions",
    " X  = Horiz. flip selected sects",
    " Y  = Vert. flip selected sects",
    " F5 = Item count",
    " F6 = Actor count/SE help",
    " F7 = Edit sector",
    " F8 = Edit wall/sprite",
    " F9 = Sector tag help",
    " Ctrl-S = Quick save",
    " Alt-F7 = Search sector lotag",
    " Alt-F8 = Search wall/sprite tags",
    " [      = Search forward",
    " ]      = Search backward",
};

static char *SpriteMode[]=
{
    "NONE",
    "SECTORS",
    "WALLS",
    "SPRITES",
    "ALL",
    "ITEMS ONLY",
    "CURRENT SPRITE ONLY",
    "ONLY SECTOREFFECTORS AND SECTORS",
    "NO SECTOREFFECTORS OR SECTORS"
};

#define MAXSKILL 5
static char *SKILLMODE[MAXSKILL]=
{
    "Actor skill display: PIECE OF CAKE",
    "Actor skill display: LET'S ROCK",
    "Actor skill display: COME GET SOME",
    "Actor skill display: DAMN I'M GOOD",
    "Actor skill display: ALL SKILL LEVELS"
};

#define MAXNOSPRITES 4
static char *SPRDSPMODE[MAXNOSPRITES]=
{
    "Sprite display: DISPLAY ALL SPRITES",
    "Sprite display: NO EFFECTORS",
    "Sprite display: NO ACTORS",
    "Sprite display: NO EFFECTORS OR ACTORS"
};

#define MAXHELP3D (signed int)(sizeof(Help3d)/sizeof(Help3d[0]))
static char *Help3d[]=
{
    "Mapster32 3D mode help",
    " ",
    " F1 = TOGGLE THIS HELP DISPLAY",
    " F2 = TOGGLE CLIPBOARD",
    " F3 = MOUSELOOK",
    " F6 = AUTOMATIC SECTOREFFECTOR HELP",
    " F7 = AUTOMATIC SECTOR TAG HELP",
    "",
    " ' A = TOGGLE AUTOSAVE",
    " ' D = CYCLE SPRITE SKILL DISPLAY",
    " ' R = TOGGLE FRAMERATE DISPLAY",
    " ' W = TOGGLE SPRITE DISPLAY",
    " ' X = MAP SHADE PREVIEW",
    " ' Y = TOGGLE PURPLE BACKGROUND",
    "",
    " ' T = CHANGE LOTAG",
    " ' H = CHANGE HITAG",
    " ' S = CHANGE SHADE",
    " ' M = CHANGE EXTRA",
    " ' V = CHANGE VISIBILITY",
    " ' L = CHANGE OBJECT COORDINATES",
    " ' C = CHANGE GLOBAL SHADE",
    "",
    " ' ENTER = PASTE GRAPHIC ONLY",
    " ' P & ; P = PASTE PALETTE TO ALL SELECTED SECTORS",
    " ; V = SET VISIBILITY ON ALL SELECTED SECTORS",
    " ' DEL = CSTAT=0",
    " CTRL-S = SAVE BOARD",
    " HOME = PGUP/PGDN MODIFIER (256 UNITS)",
    " END = PGUP/PGDN MODIFIER (512 UNITS)",
};
static char *type2str[]={"Wall","Sector","Sector","Sprite","Wall"};

static CACHE1D_FIND_REC *finddirs=NULL, *findfiles=NULL, *finddirshigh=NULL, *findfileshigh=NULL;
static int numdirs=0, numfiles=0;
static int currentlist=0;
static int mouseaction=0, mouseax=0, mouseay=0;
static int repeatcountx, repeatcounty;
static int infobox=3; // bit0: current window, bit1: mouse pointer, the variable should be renamed

extern char mskip;
extern short capturecount;
extern int editorgridextent;    // in engine.c

static void clearfilenames(void)
{
    klistfree(finddirs);
    klistfree(findfiles);
    finddirs = findfiles = NULL;
    numfiles = numdirs = 0;
}

static int getfilenames(const char *path, char kind[])
{
    CACHE1D_FIND_REC *r;

    clearfilenames();
    finddirs = klistpath(path,"*",CACHE1D_FIND_DIR);
    findfiles = klistpath(path,kind,CACHE1D_FIND_FILE);
    for (r = finddirs; r; r=r->next) numdirs++;
    for (r = findfiles; r; r=r->next) numfiles++;

    finddirshigh = finddirs;
    findfileshigh = findfiles;
    currentlist = 0;
    if (findfileshigh) currentlist = 1;

    return(0);
}

void ExtLoadMap(const char *mapname)
{
    int i;
    int sky=0;
    int j;

    getmessageleng = 0;
    getmessagetimeoff = 0;

    // PreCache Wall Tiles
    /*
        for(j=0;j<numwalls;j++)
            if(waloff[wall[j].picnum] == 0)
            {
                loadtile(wall[j].picnum);
                if (bpp != 8)
                    polymost_precache(wall[j].picnum,wall[j].pal,0);
            }

        for(j=0;j<numsectors;j++)
            if(waloff[sector[j].floorpicnum] == 0 || waloff[sector[j].ceilingpicnum] == 0)
            {
                loadtile(sector[j].floorpicnum);
                loadtile(sector[j].ceilingpicnum);
                if (bpp != 8)
                {
                    polymost_precache(sector[j].floorpicnum,sector[j].floorpal,0);
                    polymost_precache(sector[j].floorpicnum,sector[j].floorpal,0);
                }
            }

        for(j=0;j<numsprites;j++)
            if(waloff[sprite[j].picnum] == 0)
            {
                loadtile(sprite[j].picnum);
                if (bpp != 8)
                    polymost_precache(sprite[j].picnum,sprite[j].pal,1);
            }
    */

    // Presize Sprites
    for (j=0;j<MAXSPRITES;j++)
    {
        if (tilesizx[sprite[j].picnum]==0 || tilesizy[sprite[j].picnum]==0)
            sprite[j].picnum=0;

        if (sprite[j].picnum>=20 && sprite[j].picnum<=59)
        {
            if (sprite[j].picnum==26)
            {
                sprite[j].xrepeat = 8;
                sprite[j].yrepeat = 8;
            }
            else
            {
                sprite[j].xrepeat = 32;
                sprite[j].yrepeat = 32;
            }
        }

    }

    Bstrcpy(levelname,mapname);
    pskyoff[0]=0;
    for (i=0;i<8;i++) pskyoff[i]=0;

    for (i=0;i<numsectors;i++)
    {
        switch (sector[i].ceilingpicnum)
        {
        case MOONSKY1 :
        case BIGORBIT1 : // orbit
        case LA : // la city
            sky=sector[i].ceilingpicnum;
            break;
        }
    }

    switch (sky)
    {
    case MOONSKY1 :
        //        earth          mountian   mountain         sun
        pskyoff[6]=1;
        pskyoff[1]=2;
        pskyoff[4]=2;
        pskyoff[2]=3;
        break;

    case BIGORBIT1 : // orbit
        //       earth1         2           3           moon/sun
        pskyoff[5]=1;
        pskyoff[6]=2;
        pskyoff[7]=3;
        pskyoff[2]=4;
        break;

    case LA : // la city
        //       earth1         2           3           moon/sun
        pskyoff[0]=1;
        pskyoff[1]=2;
        pskyoff[2]=1;
        pskyoff[3]=3;
        pskyoff[4]=4;
        pskyoff[5]=0;
        pskyoff[6]=2;
        pskyoff[7]=3;
        break;
    }

    pskybits=3;
    parallaxtype=0;
    Bsprintf(tempbuf, "Mapster32"VERSION" - %s",mapname);
    wm_setapptitle(tempbuf);
}

void ExtSaveMap(const char *mapname)
{
    UNREFERENCED_PARAMETER(mapname);
    saveboard("backup.map",&posx,&posy,&posz,&ang,&cursectnum);
}

int getTileGroup(const char *groupName)
{
    int temp;
    for (temp = 0; temp < MAX_TILE_GROUPS; temp++)
    {
        if (s_TileGroups[temp].szText == NULL)
        {
            return -1;
        }
        if (!strcmp(s_TileGroups[temp].szText, groupName))
        {
            return temp;
        }
    }
    return -1;
}

int tileInGroup(int group, int tilenum)
{
    // @todo Make a bitmap instead of doing this slow search..
    int temp;
    if (group < 0 || group >= MAX_TILE_GROUPS || s_TileGroups[group].szText == NULL)
    {
        // group isn't valid.
        return 0;
    }
    for (temp = 0; temp < s_TileGroups[group].nIds; temp++)
    {
        if (tilenum == s_TileGroups[group].pIds[temp])
        {
            return 1;
        }
    }
    return 0;
}

const char *ExtGetSectorCaption(short sectnum)
{
    if (qsetmode != 200 && (!(onnames==1 || onnames==4 || onnames==7) || (onnames==8)))
    {
        tempbuf[0] = 0;
        return(tempbuf);
    }

    if (qsetmode != 200 && (sector[sectnum].lotag|sector[sectnum].hitag) == 0)
    {
        tempbuf[0] = 0;
    }
    else
    {
        switch (sector[sectnum].lotag)
        {
        case 1:
            Bsprintf(lo,"1 WATER (SE 7)");
            break;
        case 2:
            Bsprintf(lo,"2 UNDERWATER (SE 7)");
            break;
        case 9:
            Bsprintf(lo,"9 STAR TREK DOORS");
            break;
        case 15:
            Bsprintf(lo,"15 ELEVATOR TRANSPORT (SE 17)");
            break;
        case 16:
            Bsprintf(lo,"16 ELEVATOR PLATFORM DOWN");
            break;
        case 17:
            Bsprintf(lo,"17 ELEVATOR PLATFORM UP");
            break;
        case 18:
            Bsprintf(lo,"18 ELEVATOR DOWN");
            break;
        case 19:
            Bsprintf(lo,"19 ELEVATOR UP");
            break;
        case 20:
            Bsprintf(lo,"20 CEILING DOOR");
            break;
        case 21:
            Bsprintf(lo,"21 FLOOR DOOR");
            break;
        case 22:
            Bsprintf(lo,"22 SPLIT DOOR");
            break;
        case 23:
            Bsprintf(lo,"23 SWING DOOR (SE 11)");
            break;
        case 25:
            Bsprintf(lo,"25 SLIDE DOOR (SE 15)");
            break;
        case 26:
            Bsprintf(lo,"26 SPLIT STAR TREK DOOR");
            break;
        case 27:
            Bsprintf(lo,"27 BRIDGE (SE 20)");
            break;
        case 28:
            Bsprintf(lo,"28 DROP FLOOR (SE 21)");
            break;
        case 29:
            Bsprintf(lo,"29 TEETH DOOR (SE 22)");
            break;
        case 30:
            Bsprintf(lo,"30 ROTATE RISE BRIDGE");
            break;
        case 31:
            Bsprintf(lo,"31 2 WAY TRAIN (SE=30)");
            break;
        case 32767:
            Bsprintf(lo,"32767 SECRET ROOM");
            break;
        case -1:
            Bsprintf(lo,"65535 END OF LEVEL");
            break;
        default :
            Bsprintf(lo,"%hu",sector[sectnum].lotag);
            break;
        }
        if (sector[sectnum].lotag > 10000 && sector[sectnum].lotag < 32767)
            Bsprintf(lo,"%d 1 TIME SOUND",sector[sectnum].lotag);
        if (qsetmode != 200)
            Bsprintf(tempbuf,"%hu,%s",sector[sectnum].hitag,lo);
        else Bstrcpy(tempbuf,lo);
    }
    return(tempbuf);
}

const char *ExtGetWallCaption(short wallnum)
{
    if (!(onnames==2 || onnames==4))
    {
        tempbuf[0] = 0;
        return(tempbuf);
    }

    // HERE

    if ((wall[wallnum].lotag|wall[wallnum].hitag) == 0)
    {
        tempbuf[0] = 0;
    }
    else
    {
        Bsprintf(tempbuf,"%hu,%hu",wall[wallnum].hitag,wall[wallnum].lotag);
    }
    return(tempbuf);
} //end

const char *SectorEffectorText(short spritenum)
{
    switch (sprite[spritenum].lotag)
    {
    case 0:
        Bsprintf(tempbuf,"SE: ROTATED SECTOR");
        break;
    case 1:
        Bsprintf(tempbuf,"SE: PIVOT SPRITE FOR SE 0");
        break;
    case 2:
        Bsprintf(tempbuf,"SE: EARTHQUAKE");
        break;
    case 3:
        Bsprintf(tempbuf,"SE: RANDOM LIGHTS AFTER SHOT OUT");
        break;
    case 4:
        Bsprintf(tempbuf,"SE: RANDOM LIGHTS");
        break;
    case 6:
        Bsprintf(tempbuf,"SE: SUBWAY");
        break;
    case 7:
        Bsprintf(tempbuf,"SE: TRANSPORT");
        break;
    case 8:
        Bsprintf(tempbuf,"SE: UP OPEN DOOR LIGHTS");
        break;
    case 9:
        Bsprintf(tempbuf,"SE: DOWN OPEN DOOR LIGHTS");
        break;
    case 10:
        Bsprintf(tempbuf,"SE: DOOR AUTO CLOSE (H=DELAY)");
        break;
    case 11:
        Bsprintf(tempbuf,"SE: ROTATE SECTOR DOOR");
        break;
    case 12:
        Bsprintf(tempbuf,"SE: LIGHT SWITCH");
        break;
    case 13:
        Bsprintf(tempbuf,"SE: EXPLOSIVE");
        break;
    case 14:
        Bsprintf(tempbuf,"SE: SUBWAY CAR");
        break;
    case 15:
        Bsprintf(tempbuf,"SE: SLIDE DOOR (ST 25)");
        break;
    case 16:
        Bsprintf(tempbuf,"SE: ROTATE REACTOR SECTOR");
        break;
    case 17:
        Bsprintf(tempbuf,"SE: ELEVATOR TRANSPORT (ST 15)");
        break;
    case 18:
        Bsprintf(tempbuf,"SE: INCREMENTAL SECTOR RISE/FALL");
        break;
    case 19:
        Bsprintf(tempbuf,"SE: CEILING FALL ON EXPLOSION");
        break;
    case 20:
        Bsprintf(tempbuf,"SE: BRIDGE (ST 27)");
        break;
    case 21:
        Bsprintf(tempbuf,"SE: DROP FLOOR (ST 28)");
        break;
    case 22:
        Bsprintf(tempbuf,"SE: TEETH DOOR (ST 29)");
        break;
    case 23:
        Bsprintf(tempbuf,"SE: 1-WAY SE7 DESTINATION (H=SE 7)");
        break;
    case 24:
        Bsprintf(tempbuf,"SE: CONVAYER BELT");
        break;
    case 25:
        Bsprintf(tempbuf,"SE: ENGINE");
        break;
    case 28:
        Bsprintf(tempbuf,"SE: LIGHTNING (H= TILE#4890)");
        break;
    case 27:
        Bsprintf(tempbuf,"SE: CAMERA FOR PLAYBACK");
        break;
    case 29:
        Bsprintf(tempbuf,"SE: FLOAT");
        break;
    case 30:
        Bsprintf(tempbuf,"SE: 2 WAY TRAIN (ST=31)");
        break;
    case 31:
        Bsprintf(tempbuf,"SE: FLOOR RISE");
        break;
    case 32:
        Bsprintf(tempbuf,"SE: CEILING FALL");
        break;
    case 33:
        Bsprintf(tempbuf,"SE: SPAWN JIB W/QUAKE");
        break;
    case 36:
        Bsprintf(tempbuf,"SE: SKRINK RAY SHOOTER");
        break;
    default:
        SpriteName(spritenum,tempbuf);
        break;
    }
    return (tempbuf);
}

const char *ExtGetSpriteCaption(short spritenum)
{
    if ((onnames!=5 && onnames!=6 &&(!(onnames==3 || onnames==4 || onnames==7 || onnames==8))) || (onnames==7 && sprite[spritenum].picnum!=1))
    {
        tempbuf[0] = 0;
        return(tempbuf);
    }

    if (onnames==5)
    {
        if (!tileInGroup(tilegroupItems, sprite[spritenum].picnum))
        {
            tempbuf[0] = 0;
            return(tempbuf);
        }
    }

    if (onnames==6 && sprite[spritenum].picnum != sprite[cursprite].picnum)
    {
        tempbuf[0] = 0;
        return(tempbuf);
    }

    tempbuf[0] = 0;
    if ((sprite[spritenum].lotag|sprite[spritenum].hitag) == 0)
    {
        SpriteName(spritenum,lo);
        if (lo[0]!=0)
        {
            if (sprite[spritenum].pal==1) Bsprintf(tempbuf,"%s (MULTIPLAYER)",lo);
            else Bsprintf(tempbuf,"%s",lo);
        }
    }
    else
        if (sprite[spritenum].picnum==SECTOREFFECTOR)
        {
            if (onnames==8)
                tempbuf[0] = 0;
            else
            {
                Bsprintf(lo,"%s: %hu",SectorEffectorText(spritenum),sprite[spritenum].lotag);
                Bsprintf(tempbuf,"%s, %hu",lo,sprite[spritenum].hitag);
            }
        }
        else
        {
            SpriteName(spritenum,lo);
            if (sprite[spritenum].extra != -1)
                Bsprintf(tempbuf,"%hu,%hu,%d %s",sprite[spritenum].hitag,sprite[spritenum].lotag,sprite[spritenum].extra,lo);
            else
                Bsprintf(tempbuf,"%hu,%hu %s",sprite[spritenum].hitag,sprite[spritenum].lotag,lo);
        }
    return(tempbuf);

} //end

//printext16 parameters:
//printext16(int xpos, int ypos, short col, short backcol,
//           char name[82], char fontsize)
//  xpos 0-639   (top left)
//  ypos 0-479   (top left)
//  col 0-15
//  backcol 0-15, -1 is transparent background
//  name
//  fontsize 0=8*8, 1=3*5

//drawline16 parameters:
// drawline16(int x1, int y1, int x2, int y2, char col)
//  x1, x2  0-639
//  y1, y2  0-143  (status bar is 144 high, origin is top-left of STATUS BAR)
//  col     0-15

void ExtShowSectorData(short sectnum)   //F5
{
    short statnum=0;
    int x,x2,y;
    int i;
    int secrets=0;
    int totalactors1=0,totalactors2=0,totalactors3=0,totalactors4=0;
    int totalrespawn=0;

    UNREFERENCED_PARAMETER(sectnum);
    if (qsetmode==200)
        return;

    for (i=0;i<numsectors;i++)
    {
        if (sector[i].lotag==32767) secrets++;
    }

    statnum=0;
    i = headspritestat[statnum];
    while (i != -1)
    {
        // Count all non-player actors.
        if (tileInGroup(tilegroupActors, sprite[i].picnum))
        {
            if (sprite[i].lotag<=1) totalactors1++;
            if (sprite[i].lotag<=2) totalactors2++;
            if (sprite[i].lotag<=3) totalactors3++;
            if (sprite[i].lotag<=4) totalactors4++;
        }
        if (sprite[i].picnum == RESPAWN) totalrespawn++;

        i = nextspritestat[i];
    }

    for (i=0;i<MAXSPRITES;i++) numsprite[i]=0;
    for (i=0;i<MAXSPRITES;i++) multisprite[i]=0;
    for (i=0;i<MAXSPRITES;i++)
    {
        if (sprite[i].statnum==0)
        {
            if (sprite[i].pal!=0) multisprite[sprite[i].picnum]++;
            else numsprite[sprite[i].picnum]++;
        }
    }

    clearmidstatbar16();             //Clear middle of status bar
    Bsprintf(tempbuf,"Level %s",levelname);
    printmessage16(tempbuf);

    x=1;
    x2=14;
    y=4;
    begindrawing();
    printext16(x*8,ydim16+y*8,11,-1,"Item Count",0);
    enddrawing();
    PrintStatus("10%health=",numsprite[COLA],x,y+2,11);
    PrintStatus("",multisprite[COLA],x2,y+2,9);
    PrintStatus("30%health=",numsprite[SIXPAK],x,y+3,11);
    PrintStatus("",multisprite[SIXPAK],x2,y+3,9);
    PrintStatus("Med-Kit  =",numsprite[FIRSTAID],x,y+4,11);
    PrintStatus("",multisprite[FIRSTAID],x2,y+4,9);
    PrintStatus("Atom     =",numsprite[ATOMICHEALTH],x,y+5,11);
    PrintStatus("",multisprite[ATOMICHEALTH],x2,y+5,9);
    PrintStatus("Shields  =",numsprite[SHIELD],x,y+6,11);
    PrintStatus("",multisprite[SHIELD],x2,y+6,9);

    x=17;
    x2=30;
    y=4;
    begindrawing();
    printext16(x*8,ydim16+y*8,11,-1,"Inventory",0);
    enddrawing();
    PrintStatus("Steroids =",numsprite[STEROIDS],x,y+2,11);
    PrintStatus("",multisprite[STEROIDS],x2,y+2,9);
    PrintStatus("Airtank  =",numsprite[AIRTANK],x,y+3,11);
    PrintStatus("",multisprite[AIRTANK],x2,y+3,9);
    PrintStatus("Jetpack  =",numsprite[JETPACK],x,y+4,11);
    PrintStatus("",multisprite[JETPACK],x2,y+4,9);
    PrintStatus("Goggles  =",numsprite[HEATSENSOR],x,y+5,11);
    PrintStatus("",multisprite[HEATSENSOR],x2,y+5,9);
    PrintStatus("Boots    =",numsprite[BOOTS],x,y+6,11);
    PrintStatus("",multisprite[BOOTS],x2,y+6,9);
    PrintStatus("HoloDuke =",numsprite[HOLODUKE],x,y+7,11);
    PrintStatus("",multisprite[HOLODUKE],x2,y+7,9);
    PrintStatus("Multi D  =",numsprite[APLAYER],x,y+8,11);

    x=33;
    x2=46;
    y=4;
    begindrawing();
    printext16(x*8,ydim16+y*8,11,-1,"Weapon Count",0);
    enddrawing();
    PrintStatus("Pistol   =",numsprite[FIRSTGUNSPRITE],x,y+2,11);
    PrintStatus("",multisprite[FIRSTGUNSPRITE],x2,y+2,9);
    PrintStatus("Shotgun  =",numsprite[SHOTGUNSPRITE],x,y+3,11);
    PrintStatus("",multisprite[SHOTGUNSPRITE],x2,y+3,9);
    PrintStatus("Chaingun =",numsprite[CHAINGUNSPRITE],x,y+4,11);
    PrintStatus("",multisprite[CHAINGUNSPRITE],x2,y+4,9);
    PrintStatus("RPG      =",numsprite[RPGSPRITE],x,y+5,11);
    PrintStatus("",multisprite[RPGSPRITE],x2,y+5,9);
    PrintStatus("Pipe Bomb=",numsprite[HEAVYHBOMB],x,y+6,11);
    PrintStatus("",multisprite[HEAVYHBOMB],x2,y+6,9);
    PrintStatus("Shrinker =",numsprite[SHRINKERSPRITE],x,y+7,11);
    PrintStatus("",multisprite[SHRINKERSPRITE],x2,y+7,9);
    PrintStatus("Devastatr=",numsprite[DEVISTATORSPRITE],x,y+8,11);
    PrintStatus("",multisprite[DEVISTATORSPRITE],x2,y+8,9);
    PrintStatus("Trip mine=",numsprite[TRIPBOMBSPRITE],x,y+9,11);
    PrintStatus("",multisprite[TRIPBOMBSPRITE],x2,y+9,9);
    PrintStatus("Freezeray=",numsprite[FREEZESPRITE],x,y+10,11);
    PrintStatus("",multisprite[FREEZESPRITE],x2,y+10,9);

    x=49;
    x2=62;
    y=4;
    begindrawing();
    printext16(x*8,ydim16+y*8,11,-1,"Ammo Count",0);
    enddrawing();
    PrintStatus("Pistol   =",numsprite[AMMO],x,y+2,11);
    PrintStatus("",multisprite[AMMO],x2,y+2,9);
    PrintStatus("Shot     =",numsprite[SHOTGUNAMMO],x,y+3,11);
    PrintStatus("",multisprite[SHOTGUNAMMO],x2,y+3,9);
    PrintStatus("Chain    =",numsprite[BATTERYAMMO],x,y+4,11);
    PrintStatus("",multisprite[BATTERYAMMO],x2,y+4,9);
    PrintStatus("RPG Box  =",numsprite[RPGAMMO],x,y+5,11);
    PrintStatus("",multisprite[RPGAMMO],x2,y+5,9);
    PrintStatus("Pipe Bomb=",numsprite[HBOMBAMMO],x,y+6,11);
    PrintStatus("",multisprite[HBOMBAMMO],x2,y+6,9);
    PrintStatus("Shrinker =",numsprite[CRYSTALAMMO],x,y+7,11);
    PrintStatus("",multisprite[CRYSTALAMMO],x2,y+7,9);
    PrintStatus("Devastatr=",numsprite[DEVISTATORAMMO],x,y+8,11);
    PrintStatus("",multisprite[DEVISTATORAMMO],x2,y+8,9);
    PrintStatus("Expander =",numsprite[GROWAMMO],x,y+9,11);
    PrintStatus("",multisprite[GROWAMMO],x2,y+9,9);
    PrintStatus("Freezeray=",numsprite[FREEZEAMMO],x,y+10,11);
    PrintStatus("",multisprite[FREEZEAMMO],x2,y+10,9);

    begindrawing();
    printext16(65*8,ydim16+4*8,11,-1,"MISC",0);
    enddrawing();
    PrintStatus("Secrets =",secrets,65,6,11);
    begindrawing();
    printext16(65*8,ydim16+8*8,11,-1,"ACTORS",0);
    enddrawing();
    PrintStatus("Skill 1 =",totalactors1,65,10,11);
    PrintStatus("Skill 2 =",totalactors2,65,11,11);
    PrintStatus("Skill 3 =",totalactors3,65,12,11);
    PrintStatus("Skill 4 =",totalactors4,65,13,11);
    PrintStatus("Respawn =",totalrespawn,65,14,11);

}// end ExtShowSectorData

void ExtShowWallData(short wallnum)       //F6
{
    int i,nextfreetag=0,total=0;
    char x,y;

    UNREFERENCED_PARAMETER(wallnum);

    if (qsetmode==200)
        return;

    for (i=0;i<MAXSPRITES;i++)
    {
        if (sprite[i].statnum==0)
            switch (sprite[i].picnum)
            {
                //LOTAG
            case ACTIVATOR:
            case ACTIVATORLOCKED:
            case TOUCHPLATE:
            case MASTERSWITCH:
            case RESPAWN:
            case ACCESSSWITCH:
            case SLOTDOOR:
            case LIGHTSWITCH:
            case SPACEDOORSWITCH:
            case SPACELIGHTSWITCH:
            case FRANKENSTINESWITCH:
            case MULTISWITCH:
            case DIPSWITCH:
            case DIPSWITCH2:
            case TECHSWITCH:
            case DIPSWITCH3:
            case ACCESSSWITCH2:
            case POWERSWITCH1:
            case LOCKSWITCH1:
            case POWERSWITCH2:
            case PULLSWITCH:
            case ALIENSWITCH:
                if (sprite[i].lotag>nextfreetag) nextfreetag=1+sprite[i].lotag;
                break;

                //HITAG
            case SEENINE:
            case OOZFILTER:
            case SECTOREFFECTOR:
                if (sprite[i].lotag==10 || sprite[i].lotag==27 || sprite[i].lotag==28 || sprite[i].lotag==29)
                    break;
                else
                    if (sprite[i].hitag>nextfreetag) nextfreetag=1+sprite[i].hitag;
                break;
            default:
                break;

            }

    } // end sprite loop

    //Count Normal Actors
    for (i=0;i<MAXSPRITES;i++) numsprite[i]=0;
    for (i=0;i<MAXSPRITES;i++) multisprite[i]=0;
    for (i=0;i<MAXSPRITES;i++)
    {
        if (sprite[i].statnum==0)
        {
            if (sprite[i].pal!=0)
                switch (sprite[i].picnum)
                {
                case LIZTROOP :
                case LIZTROOPRUNNING :
                case LIZTROOPSTAYPUT :
                case LIZTROOPSHOOT :
                case LIZTROOPJETPACK :
                case LIZTROOPONTOILET :
                case LIZTROOPDUCKING :
                    numsprite[LIZTROOP]++;
                    break;
                case BOSS1:
                case BOSS1STAYPUT:
                case BOSS1SHOOT:
                case BOSS1LOB:
                case BOSSTOP:
                    multisprite[BOSS1]++;
                    break;
                case BOSS2:
                    multisprite[BOSS2]++;
                    break;
                case BOSS3:
                    multisprite[BOSS3]++;
                    break;

                default:
                    break;
                }
            else
                switch (sprite[i].picnum)
                {
                case LIZTROOP :
                case LIZTROOPRUNNING :
                case LIZTROOPSTAYPUT :
                case LIZTROOPSHOOT :
                case LIZTROOPJETPACK :
                case LIZTROOPONTOILET :
                case LIZTROOPDUCKING :
                    numsprite[LIZTROOP]++;
                    break;
                case PIGCOP:
                case PIGCOPSTAYPUT:
                case PIGCOPDIVE:
                    numsprite[PIGCOP]++;
                    break;
                case LIZMAN:
                case LIZMANSTAYPUT:
                case LIZMANSPITTING:
                case LIZMANFEEDING:
                case LIZMANJUMP:
                    numsprite[LIZMAN]++;
                    break;
                case BOSS1:
                case BOSS1STAYPUT:
                case BOSS1SHOOT:
                case BOSS1LOB:
                case BOSSTOP:
                    numsprite[BOSS1]++;
                    break;
                case COMMANDER:
                case COMMANDERSTAYPUT:
                    numsprite[COMMANDER]++;
                    break;
                case OCTABRAIN:
                case OCTABRAINSTAYPUT:
                    numsprite[OCTABRAIN]++;
                    break;
                case RECON:
                case DRONE:
                case ROTATEGUN:
                case EGG:
                case ORGANTIC:
                case GREENSLIME:
                case BOSS2:
                case BOSS3:
                case TANK:
                case NEWBEAST:
                case BOSS4:

                    numsprite[sprite[i].picnum]++;
                default:
                    break;

                }// end switch
        }// end if
    }//end for
    total=0;
    for (i=0;i<MAXSPRITES;i++) if (numsprite[i]!=0) total+=numsprite[i];
    for (i=0;i<MAXSPRITES;i++) if (multisprite[i]!=0) total+=multisprite[i];

    clearmidstatbar16();

    Bsprintf(tempbuf,"Level %s next tag %d",levelname,nextfreetag);
    printmessage16(tempbuf);

    x=2;
    y=4;
    PrintStatus("Normal Actors =",total,x,y,11);

    PrintStatus(" Liztroop  =",numsprite[LIZTROOP],x,y+1,11);
    PrintStatus(" Lizman    =",numsprite[LIZMAN],x,y+2,11);
    PrintStatus(" Commander =",numsprite[COMMANDER],x,y+3,11);
    PrintStatus(" Octabrain =",numsprite[OCTABRAIN],x,y+4,11);
    PrintStatus(" PigCop    =",numsprite[PIGCOP],x,y+5,11);
    PrintStatus(" Recon Car =",numsprite[RECON],x,y+6,11);
    PrintStatus(" Drone     =",numsprite[DRONE],x,y+7,11);
    PrintStatus(" Turret    =",numsprite[ROTATEGUN],x,y+8,11);
    PrintStatus(" Egg       =",numsprite[EGG],x,y+9,11);
    x+=17;
    PrintStatus("Slimer    =",numsprite[GREENSLIME],x,y+1,11);
    PrintStatus("Boss1     =",numsprite[BOSS1],x,y+2,11);
    PrintStatus("MiniBoss1 =",multisprite[BOSS1],x,y+3,11);
    PrintStatus("Boss2     =",numsprite[BOSS2],x,y+4,11);
    PrintStatus("Boss3     =",numsprite[BOSS3],x,y+5,11);
    PrintStatus("Riot Tank =",numsprite[TANK],x,y+6,11);
    PrintStatus("Newbeast  =",numsprite[NEWBEAST],x,y+7,11);
    PrintStatus("Boss4     =",numsprite[BOSS4],x,y+8,11);

    //Count Respawn Actors
    for (i=0;i<MAXSPRITES;i++) numsprite[i]=0;
    for (i=0;i<MAXSPRITES;i++) multisprite[i]=0;
    for (i=0;i<MAXSPRITES;i++)
    {
        if (sprite[i].statnum==0 && sprite[i].picnum==RESPAWN)
        {
            switch (sprite[i].hitag)
            {
            case LIZTROOP :
            case LIZTROOPRUNNING :
            case LIZTROOPSTAYPUT :
            case LIZTROOPSHOOT :
            case LIZTROOPJETPACK :
            case LIZTROOPONTOILET :
            case LIZTROOPDUCKING :
                numsprite[LIZTROOP]++;
                break;
            case PIGCOP:
            case PIGCOPSTAYPUT:
            case PIGCOPDIVE:
                numsprite[PIGCOP]++;
                break;
            case LIZMAN:
            case LIZMANSTAYPUT:
            case LIZMANSPITTING:
            case LIZMANFEEDING:
            case LIZMANJUMP:
                numsprite[LIZMAN]++;
                break;
            case BOSS1:
            case BOSS1STAYPUT:
            case BOSS1SHOOT:
            case BOSS1LOB:
            case BOSSTOP:
                if (sprite[i].pal!=0) multisprite[BOSS1]++;
                else numsprite[BOSS1]++;
                break;
            case COMMANDER:
            case COMMANDERSTAYPUT:
                numsprite[COMMANDER]++;
                break;
            case OCTABRAIN:
            case OCTABRAINSTAYPUT:
                numsprite[OCTABRAIN]++;
                break;
            case RECON:
            case DRONE:
            case ROTATEGUN:
            case EGG:
            case ORGANTIC:
            case GREENSLIME:
            case BOSS2:
            case BOSS3:
            case TANK:
            case NEWBEAST:
            case BOSS4:
                numsprite[sprite[i].hitag]++;
            default:
                break;
            }//end switch
        }// end if
    }// end for
    total=0;
    for (i=0;i<MAXSPRITES;i++) if (numsprite[i]!=0) total+=numsprite[i];
    for (i=0;i<MAXSPRITES;i++) if (multisprite[i]!=0) total+=multisprite[i];


    x=36;
    y=4;
    PrintStatus("Respawn",total,x,y,11);

    PrintStatus(" Liztroop  =",numsprite[LIZTROOP],x,y+1,11);
    PrintStatus(" Lizman    =",numsprite[LIZMAN],x,y+2,11);
    PrintStatus(" Commander =",numsprite[COMMANDER],x,y+3,11);
    PrintStatus(" Octabrain =",numsprite[OCTABRAIN],x,y+4,11);
    PrintStatus(" PigCop    =",numsprite[PIGCOP],x,y+5,11);
    PrintStatus(" Recon Car =",numsprite[RECON],x,y+6,11);
    PrintStatus(" Drone     =",numsprite[DRONE],x,y+7,11);
    PrintStatus(" Turret    =",numsprite[ROTATEGUN],x,y+8,11);
    PrintStatus(" Egg       =",numsprite[EGG],x,y+9,11);
    x+=17;
    PrintStatus("Slimer    =",numsprite[GREENSLIME],x,y+1,11);
    PrintStatus("Boss1     =",numsprite[BOSS1],x,y+2,11);
    PrintStatus("MiniBoss1 =",multisprite[BOSS1],x,y+3,11);
    PrintStatus("Boss2     =",numsprite[BOSS2],x,y+4,11);
    PrintStatus("Boss3     =",numsprite[BOSS3],x,y+5,11);
    PrintStatus("Riot Tank =",numsprite[TANK],x,y+6,11);
    PrintStatus("Newbeast  =",numsprite[NEWBEAST],x,y+7,11);
    PrintStatus("Boss4     =",numsprite[BOSS4],x,y+8,11);
}// end ExtShowWallData

static void Show2dText(char *name)
{
    int fp,t;
    unsigned char x=0,y=4,xmax=0,xx=0,col=0;
    clearmidstatbar16();
    if ((fp=kopen4load(name,0)) == -1)
    {
        begindrawing();
        printext16(1*4,ydim16+4*8,11,-1,"ERROR: file not found.",0);
        enddrawing();
        return;
    }

    t=65;
    begindrawing();
    while (t!=EOF && col<5)
    {
        t = 0;
        if (kread(fp,&t,1)<=0)
            t = EOF;
        while (t!=EOF && t!='\n' && x<250)
        {
            tempbuf[x]=t;
            t = 0;
            if (kread(fp,&t,1)<=0) t = EOF;
            x++;
            if (x>xmax) xmax=x;
        }
        tempbuf[x]=0;
        printext16(xx*4,ydim16+(y*6)+2,11,-1,tempbuf,1);
        x=0;
        y++;
        if (y>18)
        {
            col++;
            y=6;
            xx+=xmax;
            xmax=0;
        }
    }
    enddrawing();

    kclose(fp);

}// end Show2dText

// PK_ vvvv
typedef struct helppage_
{
    int numlines;
    char line[][80];  // C99 flexible array member
} helppage_t;

helppage_t **helppage=NULL;
int numhelppages=0;

static int newpage(const char *start)
{
    int i;
    for (i=0; i<80; i++)
    {
//        if (start[i]=='\n' || !start[i]) break;
//        if (start[i]!=' ' && start[i]!='\t' && start[i]!='\r')
//            return 0;
        if (start[i] == '^' && start[i+1] == 'P')
            return 1;
    }
    return 0;
}

#define IHELP_INITPAGES 32
#define IHELP_INITLINES 16

static void ReadHelpFile(const char *name)
{
    BFILE *fp;
    int i, j, k, numallocpages;
    long int pos, charsread=0;
    helppage_t *hp;
    char skip=0;

    if ((fp=fopenfrompath(name,"rb")) == NULL)
    {
        initprintf("Error initializing integrated help: file \"%s\" not found.\n", name);
        return;
    }

    helppage=malloc(IHELP_INITPAGES * sizeof(helppage_t *));
    numallocpages=IHELP_INITPAGES;
    if (!helppage) goto ERROR;

    i=0;
    while (!Bfeof(fp) && !ferror(fp))
    {
        while (!Bfeof(fp))    // skip empty lines
        {
            pos = ftell(fp);
            Bfgets(tempbuf, 80, fp);
            charsread = ftell(fp)-pos;
            if (!newpage(tempbuf))
            {
                break;
            }
        }

        if (Bfeof(fp) || charsread<=0) break;

        hp=Bcalloc(1,sizeof(helppage_t) + IHELP_INITLINES*80);
        if (!hp) goto ERROR;
        hp->numlines = IHELP_INITLINES;

        if (charsread == 79 && tempbuf[78]!='\n') skip=1;
        j=0;

        do
        {
            if (j >= hp->numlines)
            {
                hp=realloc(hp, sizeof(helppage_t) + 2*hp->numlines*80);
                if (!hp) goto ERROR;
                hp->numlines *= 2;
            }

            // limit the line length to 78 chars and probably get rid of the CR
            if (charsread>0)
            {
                tempbuf[charsread-1]=0;
                if (tempbuf[charsread-2]==0x0d) tempbuf[charsread-2]=0;
            }

            memcpy(hp->line[j], tempbuf, 80);

            for (k=charsread; k<80; k++) hp->line[j][k]=0;

            if (skip)
            {
                while (fgetc(fp)!='\n' && !Bfeof(fp)) /*skip rest of line*/;
                skip=0;
            }

            pos = ftell(fp);
            Bfgets(tempbuf, 80, fp);
            charsread = ftell(fp)-pos;
            if (charsread == 79 && tempbuf[78]!='\n') skip=1;

            j++;

        }
        while (!newpage(tempbuf) && !Bfeof(fp) && charsread>0);

        hp=realloc(hp, sizeof(helppage_t) + j*80);
        if (!hp) goto ERROR;
        hp->numlines=j;

        if (i >= numallocpages)
        {
            helppage = realloc(helppage, 2*numallocpages*sizeof(helppage_t *));
            numallocpages *= 2;
            if (!helppage) goto ERROR;
        }
        helppage[i] = hp;
        i++;
    }

    helppage = realloc(helppage, i*sizeof(helppage_t *));
    if (!helppage) goto ERROR;
    numhelppages = i;

    Bfclose(fp);
    return;

ERROR:

    Bfclose(fp);
    initprintf("ReadHelpFile(): ERROR allocating memory.\n");
    return;
}

#define IHELP_NUMDISPLINES 10
#define IHELP_PATLEN 45

static void IntegratedHelp()
{
    int i, j;
    int curhp=0, curline=0;
    int highlighthp=-1, highlightline=-1, lasthighlighttime=0;
    char disptext[IHELP_NUMDISPLINES][80];
    char oldpattern[IHELP_PATLEN+1];

    if (!helppage) return;

    memset(oldpattern, 0, sizeof(char));
    clearmidstatbar16();

    while (keystatus[KEYSC_ESC]==0 && keystatus[KEYSC_Q]==0)
    {
        if (handleevents())
        {
            if (quitevent) quitevent = 0;
        }
        idle();
//        printmessage16("Help mode, press <Esc> to exit");

        if (keystatus[KEYSC_T])    // goto table of contents
        {
            keystatus[KEYSC_T]=0;
            curhp=0;
            curline=0;
        }
        else if (keystatus[KEYSC_G])    // goto arbitrary page
        {
            keystatus[KEYSC_G]=0;
            Bsprintf(tempbuf, "Goto page: ");
            curhp=getnumber16(tempbuf, 0, numhelppages-1, 0);
            curline=0;
        }
        else if (keystatus[KEYSC_UP])    // scroll up
        {
            keystatus[KEYSC_UP]=0;
            if (curline>0) curline--;
        }
        else if (keystatus[KEYSC_DOWN])    // scroll down
        {
            keystatus[KEYSC_DOWN]=0;
            if (curline+IHELP_NUMDISPLINES < helppage[curhp]->numlines) curline++;
        }
        else if (keystatus[KEYSC_PGUP])    // scroll one page up
        {
            keystatus[KEYSC_PGUP]=0;
            i=IHELP_NUMDISPLINES;
            while (i>0 && curline>0) i--, curline--;
        }
        else if (keystatus[KEYSC_PGDN])    // scroll one page down
        {
            keystatus[KEYSC_PGDN]=0;
            i=IHELP_NUMDISPLINES;
            while (i>0 && curline+IHELP_NUMDISPLINES < helppage[curhp]->numlines) i--, curline++;
        }
        else if (keystatus[KEYSC_HOME])    // goto beginning of page
        {
            keystatus[KEYSC_HOME]=0;
            curline=0;
        }
        else if (keystatus[KEYSC_END])    // goto end of page
        {
            keystatus[KEYSC_END]=0;
            if ((curline=helppage[curhp]->numlines-IHELP_NUMDISPLINES) >= 0) /**/;
            else curline=0;
        }
        else if (keystatus[KEYSC_LEFT])    // prev page
        {
            keystatus[KEYSC_LEFT]=0;
            if (curhp>0)
            {
                curhp--;
                curline=0;
            }
        }
        else if (keystatus[KEYSC_RIGHT])    // next page
        {
            keystatus[KEYSC_RIGHT]=0;
            if (curhp<numhelppages-1)
            {
                curhp++;
                curline=0;
            }
        }

        // based on 'save as' dialog in overheadeditor()
        else if (keystatus[KEYSC_S])    // text search
        {

            char ch, bad=0, pattern[IHELP_PATLEN+1];

            for (i=0; i<IHELP_PATLEN+1; i++) pattern[i]=0;

            i=0;
            bflushchars();
            while (bad == 0)
            {
                Bsprintf(tempbuf,"Search: %s_", pattern);
                printmessage16(tempbuf);
                showframe(1);

                if (handleevents())
                {
                    if (quitevent) quitevent = 0;
                }
                idle();

                ch = bgetchar();

                if (keystatus[1]) bad = 1;
                else if (ch == 13) bad = 2;
                else if (ch > 0)
                {
                    if (i > 0 && (ch == 8 || ch == 127))
                    {
                        i--;
                        pattern[i] = 0;
                    }
                    else if (i < IHELP_PATLEN && ch >= 32 && ch < 128)
                    {
                        pattern[i++] = ch;
                        pattern[i] = 0;
                    }
                }
            }

            if (bad==1)
            {
                keystatus[KEYSC_ESC] = 0;
            }

            if (bad==2)
            {
                keystatus[KEYSC_ENTER] = 0;

                for (i=curhp; i<numhelppages; i++)
                {
                    for (j = (i==curhp)?(curline+1):0; j<helppage[i]->numlines; j++)
                    {
                        // entering an empty pattern will search with the last used pattern
                        if (strstr(helppage[i]->line[j], pattern[0]?pattern:oldpattern))
                        {
                            curhp = i;

                            if ((curline=j) <= helppage[i]->numlines-IHELP_NUMDISPLINES) /**/;
                            else if ((curline=helppage[i]->numlines-IHELP_NUMDISPLINES) >= 0) /**/;
                            else curline=0;

                            highlighthp = i;
                            highlightline = j;
                            lasthighlighttime = totalclock;
                            goto ENDFOR1;
                        }
                    }
                }
ENDFOR1:
                if (pattern[0])
                    memcpy(oldpattern, pattern, IHELP_PATLEN+1);
            }
        }
        else    // '1'-'0' on the upper row
        {
            for (i=2; i<=11; i++)
                if (keystatus[i]) break;
            if (i--<12 && i<numhelppages)
            {
                curhp=i;
            }
        }

        clearmidstatbar16();
        if (curhp < helppage[0]->numlines)
        {
            printmessage16(helppage[0]->line[curhp]);
        }
        else
        {
            for (i=Bsprintf(tempbuf, "%d. (Untitled page)", curhp); i<80; i++)
                tempbuf[i]=0;
            printmessage16(tempbuf);
        }

        for (i=0; j=(curhp==0)?(i+curline+1):(i+curline),
                i<IHELP_NUMDISPLINES && j<helppage[curhp]->numlines; i++)
        {
            Bmemcpy(disptext[i], helppage[curhp]->line[j], 80);
            printext16(8,ydim-STATUS2DSIZ+32+i*9,11,
                       (j==highlightline && curhp==highlighthp
                        && totalclock-lasthighlighttime<120*5)?1:-1,
                       disptext[i],0);
        }

        showframe(1);
    }

    printmessage16("");
    showframe(1);

    keystatus[KEYSC_ESC] = keystatus[KEYSC_Q] = 0;
}
// PK_ ^^^^

static void Show3dText(char *name)
{
    int fp,t;
    unsigned char x=0,y=4,xmax=0,xx=0,col=0;

    if ((fp=kopen4load(name,0)) == -1)
    {
        begindrawing();
        printext256(1*4,4*8,whitecol,-1,"ERROR: file not found.",0);
        enddrawing();
        return;
    }
    t=65;
    begindrawing();
    while (t!=EOF && col<5)
    {
        t = 0;
        if (kread(fp,&t,1)<=0)
            t = EOF;
        while (t!=EOF && t!='\n' && x<250)
        {
            tempbuf[x]=t;
            t = 0;
            if (kread(fp,&t,1)<=0) t = EOF;
            x++;
            if (x>xmax) xmax=x;
        }
        tempbuf[x]=0;
        printext256(xx*4,(y*6)+2,whitecol,-1,tempbuf,1);
        x=0;
        y++;
        if (y>18)
        {
            col++;
            y=6;
            xx+=xmax;
            xmax=0;
        }
    }
    enddrawing();

    kclose(fp);
}// end Show3dText

#if 0
static void ShowHelpText(char *name)
{
    BFILE *fp;
    char x=0,y=4;
    UNREFERENCED_PARAMETER(name);
    if ((fp=fopenfrompath("helpdoc.txt","rb")) == NULL)
    {
        begindrawing();
        printext256(1*4,4*8,whitecol,-1,"ERROR: file not found.",0);
        enddrawing();
        return;
    }
    /*
        Bfgets(tempbuf,80,fp);
        while(!Bfeof(fp) && Bstrcmp(tempbuf,"SectorEffector"))
        {
            Bfgets(tempbuf,80,fp);
        }
    */

    y=2;
    Bfgets(tempbuf,80,fp);
    Bstrcat(tempbuf,"\n");
    begindrawing();
    while (!Bfeof(fp) && !(Bstrcmp(tempbuf,"SectorEffector")==0))
    {
        Bfgets(tempbuf,80,fp);
        Bstrcat(tempbuf,"\n");
        printext256(x*4,(y*6)+2,whitecol,-1,tempbuf,1);
        y++;
    }
    enddrawing();

    Bfclose(fp);
}// end ShowHelpText
#endif
void ExtShowSpriteData(short spritenum)   //F6
{
    UNREFERENCED_PARAMETER(spritenum);
    if (qsetmode != 200)
        Show2dText("sehelp.hlp");
    /*    if (qsetmode == 200)                // In 3D mode
            return;

        while (KEY_PRESSED(KEYSC_F6));
        ResetKeys();
        ContextHelp(spritenum);             // Get context sensitive help */

}// end ExtShowSpriteData

// Floor Over Floor (duke3d)

// If standing in sector with SE42 or SE44
// then draw viewing to SE41 and raise all =hi SE43 cielings.

// If standing in sector with SE43 or SE45
// then draw viewing to SE40 and lower all =hi SE42 floors.

int fofsizex = -1;
int fofsizey = -1;
#if 0
static void ResetFOFSize()
{
    if (fofsizex != -1) tilesizx[FOF] = fofsizex;
    if (fofsizey != -1) tilesizy[FOF] = fofsizey;
}
#endif
static void ExtSE40Draw(int spnum,int x,int y,int z,short a,short h)
{
    static int tempsectorz[MAXSECTORS];
    static int tempsectorpicnum[MAXSECTORS];

    int i=0,j=0,k=0;
    int floor1=0,floor2=0,ok=0,fofmode=0,draw_both=0;
    int offx,offy,offz;

    if (sprite[spnum].ang!=512) return;

    // Things are a little different now, as we allow for masked transparent
    // floors and ceilings. So the FOF textures is no longer required
    //  if (!(gotpic[FOF>>3]&(1<<(FOF&7))))
    //          return;
    //  gotpic[FOF>>3] &= ~(1<<(FOF&7));

    if (tilesizx[562])
    {
        fofsizex = tilesizx[562];
        tilesizx[562] = 0;
    }
    if (tilesizy[562])
    {
        fofsizey = tilesizy[562];
        tilesizy[562] = 0;
    }

    floor1=spnum;

    if (sprite[spnum].lotag==42) fofmode=40;
    if (sprite[spnum].lotag==43) fofmode=41;
    if (sprite[spnum].lotag==44) fofmode=40;
    if (sprite[spnum].lotag==45) fofmode=41;

    // fofmode=sprite[spnum].lotag-2;

    // sectnum=sprite[j].sectnum;
    // sectnum=cursectnum;
    ok++;

    /*  recursive?
    for(j=0;j<MAXSPRITES;j++)
    {
    if(
    sprite[j].sectnum==sectnum &&
    sprite[j].picnum==1 &&
    sprite[j].lotag==110
       ) { DrawFloorOverFloor(j); break;}
    }
    */


    // if(ok==0) { Message("no fof",RED); return; }

    for (j=0;j<MAXSPRITES;j++)
    {
        if (sprite[j].picnum==1 && sprite[j].lotag==fofmode && sprite[j].hitag==sprite[floor1].hitag)
        {
            floor1=j;
            fofmode=sprite[j].lotag;
            ok++;
            break;
        }
    }
    // if(ok==1) { Message("no floor1",RED); return; }

    if (fofmode==40) k=41;
    else k=40;

    for (j=0;j<MAXSPRITES;j++)
    {
        if (sprite[j].picnum==1 && sprite[j].lotag==k && sprite[j].hitag==sprite[floor1].hitag)
        {
            floor2=j;
            ok++;
            break;
        }
    }

    i=floor1;
    offx=sprite[floor2].x-sprite[floor1].x;
    offy=sprite[floor2].y-sprite[floor1].y;
    offz=0;

    if (sprite[floor2].ang >= 1024)
        offz = sprite[floor2].z;
    else if (fofmode==41)
        offz = sector[sprite[floor2].sectnum].floorz;
    else
        offz = sector[sprite[floor2].sectnum].ceilingz;

    if (sprite[floor1].ang >= 1024)
        offz -= sprite[floor1].z;
    else if (fofmode==40)
        offz -= sector[sprite[floor1].sectnum].floorz;
    else
        offz -= sector[sprite[floor1].sectnum].ceilingz;

    // if(ok==2) { Message("no floor2",RED); return; }

    for (j=0;j<MAXSPRITES;j++) // raise ceiling or floor
    {
        if (sprite[j].picnum==1 && sprite[j].lotag==k+2 && sprite[j].hitag==sprite[floor1].hitag)
        {
            if (k==40)
            {
                tempsectorz[sprite[j].sectnum]=sector[sprite[j].sectnum].floorz;
                sector[sprite[j].sectnum].floorz+=(((z-sector[sprite[j].sectnum].floorz)/32768)+1)*32768;
                tempsectorpicnum[sprite[j].sectnum]=sector[sprite[j].sectnum].floorpicnum;
                sector[sprite[j].sectnum].floorpicnum=562;
            }
            else
            {
                tempsectorz[sprite[j].sectnum]=sector[sprite[j].sectnum].ceilingz;
                sector[sprite[j].sectnum].ceilingz+=(((z-sector[sprite[j].sectnum].ceilingz)/32768)-1)*32768;
                tempsectorpicnum[sprite[j].sectnum]=sector[sprite[j].sectnum].ceilingpicnum;
                sector[sprite[j].sectnum].ceilingpicnum=562;
            }
            draw_both = 1;
        }
    }

    drawrooms(x+offx,y+offy,z+offz,a,h,sprite[floor2].sectnum);
    ExtAnalyzeSprites();
    drawmasks();

    if (draw_both)
    {
        for (j=0;j<MAXSPRITES;j++) // restore ceiling or floor for the draw both sectors
        {
            if (sprite[j].picnum==1 &&
                    sprite[j].lotag==k+2 &&
                    sprite[j].hitag==sprite[floor1].hitag)
            {
                if (k==40)
                {
                    sector[sprite[j].sectnum].floorz=tempsectorz[sprite[j].sectnum];
                    sector[sprite[j].sectnum].floorpicnum=tempsectorpicnum[sprite[j].sectnum];
                }
                else
                {
                    sector[sprite[j].sectnum].ceilingz=tempsectorz[sprite[j].sectnum];
                    sector[sprite[j].sectnum].ceilingpicnum=tempsectorpicnum[sprite[j].sectnum];
                }
            }// end if
        }// end for

        // Now re-draw
        drawrooms(x+offx,y+offy,z+offz,a,h,sprite[floor2].sectnum);
        ExtAnalyzeSprites();
        drawmasks();
    }

} // end SE40

static void SE40Code(int x,int y,int z,int a,int h)
{
    int i;

    i = 0;
    while (i<MAXSPRITES)
    {
        int t = sprite[i].lotag;
        switch (t)
        {
            //            case 40:
            //            case 41:
            //                ExtSE40Draw(i,x,y,z,a,h);
            //                break;
        case 42:
        case 43:
        case 44:
        case 45:
            if (cursectnum == sprite[i].sectnum)
                ExtSE40Draw(i,x,y,z,a,h);
            break;
        }
        i++;
    }
}

void ExtEditSectorData(short sectnum)    //F7
{
    //    if (qsetmode != 200) Show2dText("sthelp.hlp");
    if (qsetmode == 200)
        return;
    if (eitherALT)  //ALT
    {
        keystatus[KEYSC_F7] = 0;
        wallsprite=0;
        curwall = 0;
        curwallnum = 0;
        cursearchspritenum = 0;
        cursectornum=0;
        cursector_lotag = sector[sectnum].lotag;
        cursector_lotag=getnumber16("Enter search sector lotag : ", cursector_lotag, 65536L,0);
        Bsprintf(tempbuf,"Search sector lotag %d",cursector_lotag);
        printmessage16(tempbuf);
    }
    else EditSectorData(sectnum);
}// end ExtEditSectorData

void ExtEditWallData(short wallnum)       //F8
{
    if (qsetmode==200)
        return;
    if (eitherALT)  //ALT
    {
        wallsprite=1;
        curwall = wallnum;
        curwallnum = 0;
        cursearchspritenum = 0;
        cursectornum = 0;
        search_lotag = wall[curwall].lotag;
        search_hitag = wall[curwall].hitag;
        search_lotag=getnumber16("Enter wall search lotag : ", search_lotag, 65536L,0);
        search_hitag=getnumber16("Enter wall search hitag : ", search_hitag, 65536L,0);
        //    Bsprintf(tempbuf,"Current wall %d lo=%d hi=%d",
        //             curwall,wall[curwall].lotag,wall[curwall].hitag);
        Bsprintf(tempbuf,"Search wall lo=%d hi=%d",search_lotag,search_hitag);
        printmessage16(tempbuf);
    }
    else EditWallData(wallnum);
}

void ExtEditSpriteData(short spritenum)   //F8
{
    if (qsetmode==200)
        return;
    if (eitherALT)  //ALT
    {
        wallsprite=2;
        cursearchsprite = spritenum;
        curwallnum = 0;
        cursearchspritenum = 0;
        cursectornum = 0;
        search_lotag = sprite[cursearchsprite].lotag;
        search_hitag = sprite[cursearchsprite].hitag;
        search_lotag=getnumber16("Enter sprite search lotag : ", search_lotag, 65536L,0);
        search_hitag=getnumber16("Enter sprite search hitag : ", search_hitag, 65536L,0);
        Bsprintf(tempbuf,"Search sprite lo=%d hi=%d",search_lotag,search_hitag);
        printmessage16(tempbuf);
    }
    else EditSpriteData(spritenum);
}

static void PrintStatus(char *string,int num,char x,char y,char color)
{
    Bsprintf(tempbuf,"%s %d",string,num);
    begindrawing();
    printext16(x*8,ydim16+y*8,color,-1,tempbuf,0);
    enddrawing();
}

static inline void SpriteName(short spritenum, char *lo2)
{
    Bsprintf(lo2,names[sprite[spritenum].picnum]);
}// end SpriteName

static void ReadPaletteTable()
{
    int i,j,fp;
    char lookup_num;
    if ((fp=kopen4load("lookup.dat",0)) == -1)
    {
        if ((fp=kopen4load("lookup.dat",1)) == -1)
        {
            initprintf("LOOKUP.DAT not found, creating dummy palette lookups\n");
            for (i=0;i<256;i++)
                tempbuf[i] = ((i+32)&255);  //remap colors for screwy palette sectors
            makepalookup(MAXPALOOKUPS,tempbuf,0,0,0,1);
            return;
        }
    }
//    initprintf("Loading palette lookups... ");
    kread(fp,&num_tables,1);
    for (j=0;j<num_tables;j++)
    {
        kread(fp,&lookup_num,1);
        kread(fp,tempbuf,256);
        makepalookup(lookup_num,tempbuf,0,0,0,1);
    }
    for (j = 0; j < 256; j++)
        tempbuf[j] = j;
    num_tables++;
    makepalookup(num_tables, tempbuf, 15, 15, 15, 1);
    makepalookup(num_tables + 1, tempbuf, 15, 0, 0, 1);
    makepalookup(num_tables + 2, tempbuf, 0, 15, 0, 1);
    makepalookup(num_tables + 3, tempbuf, 0, 0, 15, 1);

    kread(fp,WATERpalette,768);
    kread(fp,SLIMEpalette,768);
    kread(fp,TITLEpalette,768);
    kread(fp,REALMSpalette,768);
    kread(fp,BOSS1palette,768);
    kclose(fp);
//    initprintf("success.\n");
}// end ReadPaletteTable

static void ReadGamePalette()
{
    int i,fp;
    if ((fp=kopen4load("palette.dat",0)) == -1)
        if ((fp=kopen4load("palette.dat",1)) == -1)
        {
            initprintf("!!! PALETTE.DAT NOT FOUND !!!\n");
            Bstrcpy(tempbuf, "Mapster32"VERSION"");
            wm_msgbox(tempbuf,"palette.dat not found");
            exit(0);
        }
//    initprintf("Loading game palette... ");
    kread(fp,GAMEpalette,768);
    for (i=0;i<768;++i) GAMEpalette[i]=GAMEpalette[i];
    kclose(fp);
//    initprintf("success.\n");
    ReadPaletteTable();
}

void message(const char *fmt, ...)
{
    char tmpstr[256];
    va_list va;

    va_start(va, fmt);
    Bvsnprintf(tmpstr, 256, fmt, va);
    va_end(va);

    Bstrcpy(getmessage,tmpstr);
    getmessageleng = strlen(getmessage);
    getmessagetimeoff = totalclock+120*3;
    lastmessagetime = totalclock;
    if (!mouseaction)
    {
        Bstrcat(tmpstr,"\n");
        OSD_Printf(tmpstr);
    }
}

static char lockbyte4094;

static int lastupdate, mousecol, mouseadd = 1, bstatus;

static void m32_showmouse(void)
{
    int i, col;

    if (totalclock > lastupdate)
    {
        mousecol += mouseadd;
        if (mousecol >= 30 || mousecol <= 0)
        {
            mouseadd = -mouseadd;
            mousecol += mouseadd;
        }
        lastupdate = totalclock + 3;
    }

    switch (whitecol)
    {
    case 1:  // Shadow Warrior
        col = whitecol+mousecol;
        break;
    case 31: // Duke Nukem 3D
        col = whitecol-mousecol;
        break;
    default:
        col = whitecol;
        break;
    }

    if (col != whitecol)
    {
        for (i=((xdim > 640)?3:2);i<=((xdim > 640)?7:3);i++)
        {
            plotpixel(searchx+i,searchy,col);
            plotpixel(searchx-i,searchy,col);
            plotpixel(searchx,searchy-i,col);
            plotpixel(searchx,searchy+i,col);
        }
        for (i=1;i<=((xdim > 640)?2:1);i++)
        {
            plotpixel(searchx+i,searchy,whitecol);
            plotpixel(searchx-i,searchy,whitecol);
            plotpixel(searchx,searchy-i,whitecol);
            plotpixel(searchx,searchy+i,whitecol);
        }
        i=((xdim > 640)?8:4);
        plotpixel(searchx+i,searchy,0);
        plotpixel(searchx-i,searchy,0);
        plotpixel(searchx,searchy-i,0);
        plotpixel(searchx,searchy+i,0);
    }

    if (xdim > 640)
    {
        for (i=1;i<=4;i++)
        {
            plotpixel(searchx+i,searchy,whitecol);
            plotpixel(searchx-i,searchy,whitecol);
            plotpixel(searchx,searchy-i,whitecol);
            plotpixel(searchx,searchy+i,whitecol);
        }
    }
}

static int AskIfSure(char *text)
{
    int retval=1;

    if (qsetmode == 200)
    {
        begindrawing(); //{{{
        printext256(0,0,whitecol,0,text?text:"Are you sure you want to proceed?",0);
        enddrawing();   //}}}
    }
    else
    {
        printmessage16(text?text:"Are you sure you want to proceed?");
    }

    showframe(1);

    while ((keystatus[KEYSC_ESC]|keystatus[KEYSC_ENTER]|keystatus[KEYSC_SPACE]|keystatus[KEYSC_N]) == 0)
    {
        if (handleevents())
        {
            if (quitevent)
            {
                retval = 1;
                break;
            }
        }
        idle();
        if (keystatus[KEYSC_Y]||keystatus[KEYSC_ENTER])
        {
            keystatus[KEYSC_Y] = 0;
            keystatus[KEYSC_ENTER] = 0;
            retval = 0;
            break;
        }
    }
    while (keystatus[KEYSC_ESC])
    {
        keystatus[KEYSC_ESC] = 0;
        retval = 1;
        break;
    }
    return(retval);
}

static int IsValidTile(const int idTile)
{
    int bValid = 0;

    if ((idTile >= 0) && (idTile < MAXTILES))
    {
        if ((tilesizx[idTile] != 0) && (tilesizy[idTile] != 0))
        {
            bValid = 1;
        }
    }

    return bValid;
}

static int SelectAllTiles(int iCurrentTile)
{
    int i;

    if (iCurrentTile < localartlookupnum)
    {
        iCurrentTile = localartlookup[iCurrentTile];
    }
    else
    {
        iCurrentTile = 0;
    }

    localartlookupnum = MAXTILES;

    for (i = 0; i < MAXTILES; i++)
    {
        localartlookup[i] = i;
        localartfreq[i] = 0;
    }

    return iCurrentTile;
}

static int OnGotoTile(int iTile);
static int OnSelectTile(int iTile);
static int s_Zoom = INITIAL_ZOOM;
static int s_TileZoom = 1;

static int DrawTiles(int iTopLeft, int iSelected, int nXTiles, int nYTiles, int TileDim, int offset);


static int m32gettile(int idInitialTile)
{
    int gap, temp;
    int nXTiles, nYTiles, nDisplayedTiles;
    int i;
    int iTile, iTopLeftTile;
    int idSelectedTile;
    int scrollmode;
    int mousedx, mousedy, mtile, omousex=searchx, omousey=searchy, moffset=0;

// Enable following line for testing. I couldn't work out how to change vidmode on the fly
// s_Zoom = NUM_ZOOMS - 1;

    if (idInitialTile < 0)
    {
        idInitialTile = 0;
    }
    else if (idInitialTile >= MAXTILES)
    {
        idInitialTile = MAXTILES - 1;
    }

    // Ensure zoom not to big (which can happen if display size
    //   changes whilst Mapster is running)
    do
    {
        nXTiles = xdim / ZoomToThumbSize[s_Zoom];
        nYTiles = ydim / ZoomToThumbSize[s_Zoom];
        // Refuse to draw less than half of a row.
        if (ZoomToThumbSize[s_Zoom]/2 < 12) nYTiles--;
        nDisplayedTiles  = nXTiles * nYTiles;

        if (!nDisplayedTiles)
        {
            // Eh-up, resolution changed since we were last displaying tiles.
            s_Zoom--;
        }
    }
    while (!nDisplayedTiles);

    keystatus[KEYSC_V] = 0;

    for (i = 0; i < MAXTILES; i++)
    {
        localartfreq[i] = 0;

        localartlookup[i] = i;
    }

    iTile = idSelectedTile = idInitialTile;

    switch (searchstat)
    {
    case 0 :
        for (i = 0; i < numwalls; i++)
        {
            localartfreq[ wall[i].picnum ]++;
        }
        break;

    case 1 :
    case 2 :
        for (i = 0; i < numsectors; i++)
        {
            localartfreq[ sector[i].ceilingpicnum ]++;
            localartfreq[ sector[i].floorpicnum ]++;
        }
        break;

    case 3 :
        for (i=0;i<MAXSPRITES;i++)
        {
            if (sprite[i].statnum < MAXSTATUS)
            {
                localartfreq[ sprite[i].picnum ]++;
            }
        }
        break;

    case 4 :
        for (i = 0; i < numwalls; i++)
        {
            localartfreq[ wall[i].overpicnum ]++;
        }
        break;

    default :
        break;
    }


    //
    //  Sort tiles into frequency order
    //

    gap = MAXTILES / 2;

    do
    {
        for (i = 0; i < MAXTILES-gap; i++)
        {
            temp = i;

            while ((localartfreq[temp] < localartfreq[temp+gap]) && (temp >= 0))
            {
                int tempint;

                tempint = localartfreq[temp];
                localartfreq[temp] = localartfreq[temp+gap];
                localartfreq[temp+gap] = tempint;

                tempint = localartlookup[temp];
                localartlookup[temp] = localartlookup[temp+gap];
                localartlookup[temp+gap] = tempint;

                if (iTile == temp)
                {
                    iTile = temp + gap;
                }
                else if (iTile == temp + gap)
                {
                    iTile = temp;
                }

                temp -= gap;
            }
        }
        gap >>= 1;
    }
    while (gap > 0);

    //
    // Set up count of number of used tiles
    //

    localartlookupnum = 0;
    while (localartfreq[localartlookupnum] > 0)
    {
        localartlookupnum++;
    }

    //
    // Check : If no tiles used at all then switch to displaying all tiles
    //

    if (!localartfreq[0])
    {
        localartlookupnum = MAXTILES;

        for (i = 0; i < MAXTILES; i++)
        {
            localartlookup[i] = i;
            localartfreq[i] = 0; // Terrible bodge : zero tilefreq's not displayed in tile view. Still, when in Rome ... :-)
        }

        iTile = idInitialTile;
    }

    //
    //
    //

    iTopLeftTile = iTile - (iTile % nXTiles);

    if (iTopLeftTile < 0)
    {
        iTopLeftTile = 0;
    }

    if (iTopLeftTile > MAXTILES-nDisplayedTiles)
    {
        iTopLeftTile = MAXTILES-nDisplayedTiles;
    }

    searchx=((iTile-iTopLeftTile)%nXTiles)*ZoomToThumbSize[s_Zoom]+ZoomToThumbSize[s_Zoom]/2;
    searchy=((iTile-iTopLeftTile)/nXTiles)*ZoomToThumbSize[s_Zoom]+ZoomToThumbSize[s_Zoom]/2;

    ////////////////////////////////
    // Start of key handling code //
    ////////////////////////////////

    while ((keystatus[KEYSC_ENTER]|keystatus[KEYSC_ESC]|(bstatus&1)) == 0) // <- Presumably one of these is escape key ???
    {
        DrawTiles(iTopLeftTile, (iTile >= localartlookupnum)?localartlookupnum-1:iTile, nXTiles, nYTiles, ZoomToThumbSize[s_Zoom],moffset);

        getmousevalues(&mousedx,&mousedy,&bstatus);
        searchx += mousedx;
        searchy += mousedy;
        if (bstatus&2)
        {
            moffset+=mousedy*2;
            searchy += mousedy;
            searchx -= mousedx;
            if ((moffset < 0 && iTopLeftTile > localartlookupnum-nDisplayedTiles-1)
                    || (moffset > 0 && iTopLeftTile==0))
            {
                moffset=0;
                searchy -= mousedy*2;
            }
            while (moffset>ZoomToThumbSize[s_Zoom])
            {
                iTopLeftTile-=nXTiles;
                moffset-=ZoomToThumbSize[s_Zoom];
            }
            while (moffset<-ZoomToThumbSize[s_Zoom])
            {
                iTopLeftTile+=nXTiles;
                moffset+=ZoomToThumbSize[s_Zoom];
            }
        }

        // Keep the pointer visible at all times.
        temp = min((ZoomToThumbSize[s_Zoom] / 2), 12);
        if (searchx < temp) searchx = temp;
        if (searchy < temp) searchy = temp;
        if (searchx > xdim - temp) searchx = xdim - temp;
        if (searchy > ydim - temp) searchy = ydim - temp;

        scrollmode=!(eitherCTRL^revertCTRL);
        if (bstatus&16 && scrollmode && iTopLeftTile > 0)
        {
            mouseb &= ~16;
            iTopLeftTile -= (nXTiles*scrollamount);
        }
        if (bstatus&32 && scrollmode && iTopLeftTile < localartlookupnum-nDisplayedTiles-1)
        {
            mouseb &= ~32;
            iTopLeftTile += (nXTiles*scrollamount);
        }
        mtile=iTile=(searchx/ZoomToThumbSize[s_Zoom])+((searchy-moffset)/ZoomToThumbSize[s_Zoom])*nXTiles+iTopLeftTile;
        while (iTile >= iTopLeftTile + nDisplayedTiles)
        {
            iTile-=nXTiles;
            mtile=iTile;
        }

        if (handleevents())
        {
            if (quitevent) quitevent = 0;
        }
        idle();

        // These two lines are so obvious I don't need to comment them ...;-)
        synctics = totalclock-lockclock;
        lockclock += synctics;

        // Zoom in / out using numeric key pad's / and * keys
        if (((keystatus[KEYSC_gSLASH] || (!scrollmode && bstatus&16)) && s_Zoom<(signed)(NUM_ZOOMS-1))
                || ((keystatus[KEYSC_gSTAR]  || (!scrollmode && bstatus&32)) && s_Zoom>0))
        {
            if (keystatus[KEYSC_gSLASH] || (!scrollmode && bstatus&16))
            {
                keystatus[KEYSC_gSLASH] = 0;
                mouseb &= ~16;
                bstatus &= ~16;

                // Watch out : If editor window is small, then the next zoom level
                //  might get so large that even one tile might not fit !
                if ((ZoomToThumbSize[s_Zoom+1] <= xdim)
                        && (ZoomToThumbSize[s_Zoom+1] <= ydim))
                {
                    // Phew, plenty of room.
                    s_Zoom++;
                }
            }
            else
            {
                keystatus[KEYSC_gSTAR] = 0;
                mouseb &= ~32;
                bstatus &= ~32;
                s_Zoom--;
            }

            if (iTile >= localartlookupnum)iTile = localartlookupnum-1;

            // Calculate new num of tiles to display
            nXTiles = xdim / ZoomToThumbSize[s_Zoom];
            nYTiles = ydim / ZoomToThumbSize[s_Zoom];
            // Refuse to draw less than half of a row.
            if (ZoomToThumbSize[s_Zoom]/2 < 12) nYTiles--;
            nDisplayedTiles  = nXTiles * nYTiles;

            // Determine if the top-left displayed tile needs to
            //   alter in order to display selected tile
            iTopLeftTile = iTile - (iTile % nXTiles);

            if (iTopLeftTile < 0)
            {
                iTopLeftTile = 0;
            }
            else if (iTopLeftTile > MAXTILES - nDisplayedTiles)
            {
                iTopLeftTile = MAXTILES - nDisplayedTiles;
            }
            // scroll window so mouse points the same tile as it was before zooming
            iTopLeftTile-=(searchx/ZoomToThumbSize[s_Zoom])+((searchy-moffset)/ZoomToThumbSize[s_Zoom])*nXTiles+iTopLeftTile-iTile;
        }

        if (keystatus[KEYSC_LEFT])
        {
            iTile -= (iTile > 0);
            keystatus[KEYSC_LEFT] = 0;
        }

        if (keystatus[KEYSC_RIGHT])
        {
            iTile += (iTile < MAXTILES);
            keystatus[KEYSC_RIGHT] = 0;
        }

        if (keystatus[KEYSC_UP])
        {
            iTile -= nXTiles;
            keystatus[KEYSC_UP] = 0;
        }

        if (keystatus[KEYSC_DOWN])
        {
            iTile += nXTiles;
            keystatus[KEYSC_DOWN] = 0;
        }

        if (keystatus[KEYSC_PGUP])
        {
            iTile -= nDisplayedTiles;
            keystatus[KEYSC_PGUP] = 0;
        }

        if (keystatus[KEYSC_PGDN])
        {
            iTile += nDisplayedTiles;
            keystatus[KEYSC_PGDN] = 0;
        }

        //
        // Ensure tilenum is within valid range
        //

        if (iTile < 0)
        {
            iTile = 0;
        }

        if (iTile >= MAXTILES)  // shouldn't this be the count of num tiles ???
        {
            iTile = MAXTILES-1;
        }

        // 'V'  KEYPRESS
        if (keystatus[KEYSC_V])
        {
            keystatus[KEYSC_V] = 0;

            iTile = SelectAllTiles(iTile);
        }

        // 'G'  KEYPRESS - Goto frame
        if (keystatus[KEYSC_G])
        {
            keystatus[KEYSC_G] = 0;

            iTile = OnGotoTile(iTile);
        }

        // 'U'  KEYPRESS : go straight to user defined art
        if (keystatus[KEYSC_U])
        {
            SelectAllTiles(iTile);

            iTile = FIRST_USER_ART_TILE;
            keystatus[KEYSC_U] = 0;
        }

        // 'A'  KEYPRESS : Go straight to start of Atomic edition's art
        if (keystatus[KEYSC_A])
        {
            SelectAllTiles(iTile);

            iTile = FIRST_ATOMIC_TILE;
            keystatus[KEYSC_A] = 0;
        }

        // 'T' KEYPRESS = Select from pre-defined tileset
        if (keystatus[KEYSC_T])
        {
            keystatus[KEYSC_T] = 0;

            iTile = OnSelectTile(iTile);
        }

        // 'E'  KEYPRESS : Go straight to start of extended art
        if (keystatus[KEYSC_E])
        {
            SelectAllTiles(iTile);

            if (iTile == FIRST_EXTENDED_TILE)
                iTile = SECOND_EXTENDED_TILE;
            else iTile = FIRST_EXTENDED_TILE;

            keystatus[KEYSC_E] = 0;
        }

        if (keystatus[KEYSC_Z])
        {
            s_TileZoom = !s_TileZoom;
            keystatus[KEYSC_Z] = 0;
        }

        //
        //      Adjust top-left to ensure tilenum is within displayed range of tiles
        //

        while (iTile < iTopLeftTile - (moffset<0)?nXTiles:0)
        {
            iTopLeftTile -= nXTiles;
        }

        while (iTile >= iTopLeftTile + nDisplayedTiles)
        {
            iTopLeftTile += nXTiles;
        }

        if (iTopLeftTile < 0)
        {
            iTopLeftTile = 0;
        }

        if (iTopLeftTile > MAXTILES - nDisplayedTiles)
        {
            iTopLeftTile = MAXTILES - nDisplayedTiles;
        }

        if ((keystatus[KEYSC_ENTER] || (bstatus&1)) == 0)   // uh ? Not escape key ?
        {
            idSelectedTile = idInitialTile;
        }
        else
        {
            if (iTile < localartlookupnum)
            {
                // Convert tile num from index to actual tile num
                idSelectedTile = localartlookup[iTile];

                // Check : if invalid tile selected, return original tile num
                if (!IsValidTile(idSelectedTile))
                {
                    idSelectedTile = idInitialTile;
                }
            }
            else
            {
                idSelectedTile = idInitialTile;
            }
        }
        if (mtile!=iTile) // if changed by keyboard, update mouse cursor
        {
            searchx=((iTile-iTopLeftTile)%nXTiles)*ZoomToThumbSize[s_Zoom]+ZoomToThumbSize[s_Zoom]/2;
            searchy=((iTile-iTopLeftTile)/nXTiles)*ZoomToThumbSize[s_Zoom]+ZoomToThumbSize[s_Zoom]/2+moffset;
        }
    }
    searchx=omousex;searchy=omousey;

    keystatus[KEYSC_ESC] = 0;
    keystatus[KEYSC_ENTER] = 0;

    return(idSelectedTile);

}

// Dir = 0 (zoom out) or 1 (zoom in)
//void OnZoomInOut( int *pZoom, int Dir /*0*/ )
//{
//}

static int OnGotoTile(int iTile)
{
    int iTemp, iNewTile;
    char ch;
    char szTemp[128];

    //Automatically press 'V'
    iTile = SelectAllTiles(iTile);

    bflushchars();

    iNewTile = iTemp = 0; //iTile; //PK

    while (keystatus[KEYSC_ESC] == 0)
    {
        if (handleevents())
        {
            if (quitevent) quitevent = 0;
        }
        idle();

        ch = bgetchar();

        Bsprintf(szTemp, "Goto tile: %d_ ", iNewTile);
        printext256(0, 0, whitecol, 0, szTemp, 0);
        showframe(1);

        if (ch >= '0' && ch <= '9')
        {
            iTemp = (iNewTile*10) + (ch-'0');
            if (iTemp < MAXTILES)
            {
                iNewTile = iTemp;
            }
        }
        else if (ch == 8)
        {
            iNewTile /= 10;
        }
        else if (ch == 13)
        {
            iTile = iNewTile;
            break;
        }
    }

    clearkeys();

    return iTile;
}

static int LoadTileSet(const int idCurrentTile, const int *pIds, const int nIds)
{
    int iNewTile = 0;
    int i;

    localartlookupnum = nIds;

    for (i = 0; i < localartlookupnum; i++)
    {
        localartlookup[i] = pIds[i];
        // REM : Could we still utilise localartfreq[] to mark
        //  which tiles are currently used in the map ? Set to 0xFFFF perhaps ?
        localartfreq[i] = 0;

        if (idCurrentTile == pIds[i])
        {
            iNewTile = i;
        }
    }

    return iNewTile;

}

static int OnSelectTile(int iTile)
{
    int bDone = 0;
    int i;
    char ch;

    for (i = 0; (unsigned)i < tile_groups; i++)
    {
        if (s_TileGroups[i].pIds != NULL)
            break;
    }

    if ((unsigned)i == tile_groups) // no tile groups
        return (iTile);

    SelectAllTiles(iTile);

    bflushchars();

    begindrawing();
    setpolymost2dview();
    clearview(0);

    //
    //  Await appropriate selection keypress.
    //

    bDone = 0;

    while (keystatus[KEYSC_ESC] == 0 && (!bDone))
    {
        if (handleevents())
        {
            if (quitevent) quitevent = 0;
        }
        idle();

        //
        // Display the description strings for each available tile group
        //
        for (i = 0; (unsigned)i < tile_groups; i++)
        {
            if (s_TileGroups[i].szText != NULL)
            {
                if ((i+2)*16 > ydimgame) break;
                Bsprintf(tempbuf,"(%c) %s",s_TileGroups[i].key1,s_TileGroups[i].szText);
                printext256(10L, (i+1)*16, whitecol, -1, tempbuf, 0);
            }
        }
        showframe(1);


        ch = bgetchar();

        for (i = 0; (unsigned)i < tile_groups; i++)
        {
            if (s_TileGroups[i].pIds != NULL && s_TileGroups[i].key1)
                if ((ch == s_TileGroups[i].key1) || (ch == s_TileGroups[i].key2))
                {
                    iTile = LoadTileSet(iTile, s_TileGroups[i].pIds, s_TileGroups[i].nIds);
                    bDone = 1;
                }
        }
    }

    enddrawing();
    showframe(1);

    clearkeys();

    return iTile;
}

const char * GetTilePixels(const int idTile)
{
    char *pPixelData = 0;

    if ((idTile >= 0) && (idTile < MAXTILES))
    {
        if (!waloff[idTile])
        {
            loadtile(idTile);
        }

        if (IsValidTile(idTile))
        {
            pPixelData = (char *)waloff[idTile];
        }
    }

    return pPixelData;
}

static int DrawTiles(int iTopLeft, int iSelected, int nXTiles, int nYTiles, int TileDim, int offset)
{
    int XTile, YTile;
    int iTile, idTile;
    int XBox, YBox;
    int XPos, YPos;
    int XOffset, YOffset;
    int i;
    const char * pRawPixels;
    int TileSizeX, TileSizeY;
    int DivInc,MulInc;
    char *pScreen;
    char szT[128];

    begindrawing();

    setpolymost2dview();

    clearview(0);

    for (YTile = 0-(offset>0); YTile < nYTiles+(offset<0)+1; YTile++)
    {
        for (XTile = 0; XTile < nXTiles; XTile++)
        {
            iTile = iTopLeft + XTile + (YTile * nXTiles);

            if (iTile>=0 && iTile < localartlookupnum)
            {
                idTile = localartlookup[ iTile ];

                // Get pointer to tile's raw pixel data
                pRawPixels = GetTilePixels(idTile);

                if (pRawPixels != NULL)
                {
                    XPos = XTile * TileDim;
                    YPos = YTile * TileDim+offset;

                    if (polymost_drawtilescreen(XPos, YPos, idTile, TileDim, s_TileZoom))
                    {
                        TileSizeX = tilesizx[ idTile ];
                        TileSizeY = tilesizy[ idTile ];

                        DivInc = 1;
                        MulInc = 1;

                        while ((TileSizeX/DivInc > TileDim)
                                || (TileSizeY/DivInc) > TileDim)
                        {
                            DivInc++;
                        }

                        if (DivInc == 1 && s_TileZoom)
                        {
                            while (((TileSizeX*(MulInc+1)) <= TileDim)
                                    && ((TileSizeY*(MulInc+1)) <= TileDim))
                            {
                                MulInc++;
                            }
                        }

                        TileSizeX = (TileSizeX / DivInc) * MulInc;
                        TileSizeY = (TileSizeY / DivInc) * MulInc;

                        for (YOffset = 0; YOffset < TileSizeY; YOffset++)
                        {
                            int y=YPos+YOffset;
                            if (y>=0 && y<ydim)
                            {
                                pScreen = (char *)ylookup[y]+XPos+frameplace;
                                for (XOffset = 0; XOffset < TileSizeX; XOffset++)
                                {
                                    pScreen[XOffset] = pRawPixels[((YOffset * DivInc) / MulInc) + (((XOffset * DivInc) / MulInc) * tilesizy[idTile])];
                                }
                            }
                        }
                    }
                    if (localartfreq[iTile] != 0 && YPos >= 0 && YPos <= ydim-20)
                    {
                        Bsprintf(szT, "%d", localartfreq[iTile]);
                        printext256(XPos, YPos, whitecol, -1, szT, 1);
                    }
                }
            }
        }
    }

    //
    // Draw white box around currently selected tile
    //

    XBox = ((iSelected-iTopLeft) % nXTiles) * TileDim;
    YBox = ((iSelected - ((iSelected-iTopLeft) % nXTiles) - iTopLeft) / nXTiles) * TileDim+offset;

    if (iSelected-iTopLeft>0)
        for (i = 0; i < TileDim; i++)
        {
            if (YBox>=0 && YBox<ydim)
                plotpixel(XBox+i,         YBox,           whitecol);
            if (YBox+TileDim>=0 && YBox+TileDim<ydim)
                plotpixel(XBox+i,         YBox + TileDim, whitecol);
            if (YBox+i>=0 && YBox+i<ydim)
            {
                plotpixel(XBox,           YBox + i,       whitecol);
                plotpixel(XBox + TileDim, YBox + i,       whitecol);
            }
        }

    idTile = localartlookup[ iSelected ];

    // Draw info bar at bottom.

    // Clear out behind the text for improved visibility.
    //drawline256(0, (ydim-12)<<12, xdim<<12, (ydim-12)<<12, whitecol);
    for (i=ydim-12; i<ydim; i++)
    {
        drawline256(0, i<<12, xdim<<12, i<<12, (ydim-i));
    }

    // Tile number on left.
    Bsprintf(szT, "%d" , idTile);
    printext256(1, ydim-10, whitecol, -1, szT, 0);

    // Tile name on right.
    printext256(xdim-(Bstrlen(names[idTile])<<3)-1,ydim-10,whitecol,-1,names[idTile],0);

    // Tile dimensions.
    Bsprintf(szT,"%dx%d",tilesizx[idTile],tilesizy[idTile]);
    printext256(xdim>>2,ydim-10,whitecol,-1,szT,0);

    // EditArt animation flags.
    Bsprintf(szT,"%d, %d",(picanm[idTile]>>8)&0xFF,(picanm[idTile]>>16)&0xFF);
    printext256((xdim>>2)+100,ydim-10,whitecol,-1,szT,0);

    m32_showmouse();

    enddrawing();
    showframe(1);

    return(0);

}

extern char unrealedlook; //PK

int spriteonceilingz(int searchwall)
{
    int z=sprite[searchwall].z;
    z = getceilzofslope(searchsector,sprite[searchwall].x,sprite[searchwall].y);
    if (sprite[searchwall].cstat&128) z -= ((tilesizy[sprite[searchwall].picnum]*sprite[searchwall].yrepeat)<<1);
    if ((sprite[searchwall].cstat&48) != 32)
        z += ((tilesizy[sprite[searchwall].picnum]*sprite[searchwall].yrepeat)<<2);
    return z;
}

int spriteongroundz(int searchwall)
{
    int z=sprite[searchwall].z;
    z = getflorzofslope(searchsector,sprite[searchwall].x,sprite[searchwall].y);
    if (sprite[searchwall].cstat&128) z -= ((tilesizy[sprite[searchwall].picnum]*sprite[searchwall].yrepeat)<<1);
    return z;
}

#define WIND1X   3
#define WIND1Y 150
void drawtileinfo(char *title,int x,int y,int picnum,int shade,int pal,int cstat,int lotag,int hitag,int extra)
{
    char buf[64];
    int i,j;
    int scale=65536;
    int x1;

    j = xdimgame>640?0:1;
    i = ydimgame>>6;

    x1=x+80;
    if (j)x1/=2;
    x1*=320./xdimgame;
    scale/=max(tilesizx[picnum],tilesizy[picnum])/24.;
    rotatesprite((x1+13)<<16,(y+11)<<16,scale,0,picnum,shade,pal,2,0L,0L,xdim-1L,ydim-1L);

    x*=xdimgame/320.;
    y*=ydimgame/200.;
    begindrawing();
    printext256(x+2,y+2,0,-1,title,j);
    printext256(x,y,255-13,-1,title,j);

    Bsprintf(buf,"Pic:%4d",picnum);
    printext256(x+2,y+2+i*1,0,-1,buf,j);
    printext256(x,y+i*1,whitecol,-1,buf,j);
    Bsprintf(buf,"Shd:%4d",shade);
    printext256(x+2,y+2+i*2,0,-1,buf,j);
    printext256(x,y+i*2,whitecol,-1,buf,j);
    Bsprintf(buf,"Pal:%4d",pal);
    printext256(x+2,y+2+i*3,0,-1,buf,j);
    printext256(x,y+i*3,whitecol,-1,buf,j);
    Bsprintf(buf,"Cst:%4d",cstat);
    printext256(x+2,y+2+i*4,0,-1,buf,j);
    printext256(x,y+i*4,whitecol,-1,buf,j);
    Bsprintf(buf,"Lot:%4d",lotag);
    printext256(x+2,y+2+i*5,0,-1,buf,j);
    printext256(x,y+i*5,whitecol,-1,buf,j);
    Bsprintf(buf,"Hit:%4d",hitag);
    printext256(x+2,y+2+i*6,0,-1,buf,j);
    printext256(x,y+i*6,whitecol,-1,buf,j);
    Bsprintf(buf,"Ext:%4d",extra);
    printext256(x+2,y+2+i*7,0,-1,buf,j);
    printext256(x,y+i*7,whitecol,-1,buf,j);
    enddrawing();
}
int snap=0;int saveval1,saveval2,saveval3;

static void Keys3d(void)
{
    int i,count,rate,nexti,changedir;
    int j, k, tempint = 0, hiz, loz;
    int hitx, hity, hitz, hihit, lohit;
    char smooshyalign=0, repeatpanalign=0, buffer[80];
    short startwall, endwall, dasector, hitsect, hitwall, hitsprite, statnum=0;

    /* start Mapster32 */

    if (sidemode != 0)
    {
        setviewback();
        rotatesprite(320<<15,200<<15,65536,(horiz-100)<<2,4094,0,0,2+4,0,0,0,0);
        lockbyte4094 = 0;
        searchx = ydim-1-searchx;
        searchx ^= searchy;
        searchy ^= searchx;
        searchx ^= searchy;

        //      overwritesprite(160L,170L,1153,0,1+2,0);
        rotatesprite(160<<16,170<<16,65536,(100-horiz+1024)<<3,1153,0,0,2,0,0,0,0);

    }

    if (usedcount && !helpon)
    {
        if (searchstat!=3)
        {
            count=0;
            for (i=0;i<numwalls;i++)
            {
                if (wall[i].picnum == temppicnum) count++;
                if (wall[i].overpicnum == temppicnum) count++;
            }
            for (i=0;i<numsectors;i++)
            {
                if (sector[i].ceilingpicnum == temppicnum) count++;
                if (sector[i].floorpicnum == temppicnum) count++;
            }
        }

        if (searchstat==3)
        {
            count=0;
            statnum=0;
            i = headspritestat[statnum];
            while (i != -1)
            {
                nexti = nextspritestat[i];
                if (sprite[i].picnum == temppicnum) count++;
                i = nexti;
            }
        }

        drawtileinfo("Clipboard",3,124,temppicnum,tempshade,temppal,tempcstat,templotag,temphitag,tempextra);
    }// end if usedcount

//    if (infobox&1)
    {
        char lines[8][64];
        int dax, day, dist, height1=0,height2=0,height3=0, num=0;
        int x,y;

        if (infobox&1)
        {
            height2=sector[searchsector].floorz-sector[searchsector].ceilingz;
            switch (searchstat)
            {
            case 0:
            case 4:
                drawtileinfo("Current",WIND1X,WIND1Y,wall[searchwall].picnum,wall[searchwall].shade,
                             wall[searchwall].pal,wall[searchwall].cstat,wall[searchwall].lotag,
                             wall[searchwall].hitag,wall[searchwall].extra);

                dax = wall[searchwall].x-wall[wall[searchwall].point2].x;
                day = wall[searchwall].y-wall[wall[searchwall].point2].y;
                dist = ksqrt(dax*dax+day*day);
                if (wall[searchwall].nextsector!=-1)
                {
                    int nextsect=wall[searchwall].nextsector;
                    height1=sector[searchsector].floorz-sector[nextsect].floorz;
                    height2=sector[nextsect].floorz-sector[nextsect].ceilingz;
                    height3=sector[nextsect].ceilingz-sector[searchsector].ceilingz;
                }
                Bsprintf(lines[num++],"Panning: %d, %d",wall[searchwall].xpanning,wall[searchwall].ypanning);
                Bsprintf(lines[num++],"Repeat:  %d, %d",wall[searchwall].xrepeat,wall[searchwall].yrepeat);
                Bsprintf(lines[num++],"Overpic: %d",wall[searchwall].overpicnum);
                lines[num++][0]=0;
                if (getmessageleng)
                    break;
                Bsprintf(lines[num++],"^251Wall %d^31",searchwall);
                if (wall[searchwall].nextsector!=-1)
                    Bsprintf(lines[num++],"LoHeight:%d, HiHeight:%d, Length:%d",height1,height3,dist);
                else
                    Bsprintf(lines[num++],"Height:%d, Length:%d",height2,dist);
                break;
            case 1:
                drawtileinfo("Current",WIND1X,WIND1Y,sector[searchsector].ceilingpicnum,sector[searchsector].ceilingshade,
                             sector[searchsector].ceilingpal,sector[searchsector].ceilingstat,
                             sector[searchsector].lotag,sector[searchsector].hitag,sector[searchsector].extra);

                Bsprintf(lines[num++],"Panning:  %d, %d",sector[searchsector].ceilingxpanning,sector[searchsector].ceilingypanning);
                Bsprintf(lines[num++],"CeilingZ: %d",sector[searchsector].ceilingz);
                Bsprintf(lines[num++],"Slope:    %d",sector[searchsector].ceilingheinum);
                lines[num++][0]=0;
                if (getmessageleng)
                    break;
                Bsprintf(lines[num++],"^251Sector %d^31 ceiling  Lotag:%s",searchsector,ExtGetSectorCaption(searchsector));
                Bsprintf(lines[num++],"Height: %d, Visibility:%d",height2,sector[searchsector].visibility);
                break;
            case 2:
                drawtileinfo("Current",WIND1X,WIND1Y,sector[searchsector].floorpicnum,sector[searchsector].floorshade,
                             sector[searchsector].floorpal,sector[searchsector].floorstat,
                             sector[searchsector].lotag,sector[searchsector].hitag,sector[searchsector].extra);

                Bsprintf(lines[num++],"Panning: %d,%d",sector[searchsector].floorxpanning,sector[searchsector].floorypanning);
                Bsprintf(lines[num++],"FloorZ:  %d",sector[searchsector].floorz);
                Bsprintf(lines[num++],"Slope:   %d",sector[searchsector].floorheinum);
                lines[num++][0]=0;
                if (getmessageleng)
                    break;
                Bsprintf(lines[num++],"^251Sector %d^31 floor  Lotag:%s",searchsector,ExtGetSectorCaption(searchsector));
                Bsprintf(lines[num++],"Height:%d, Visibility:%d",height2,sector[searchsector].visibility);
                break;
            case 3:
                drawtileinfo("Current",WIND1X,WIND1Y,sprite[searchwall].picnum,sprite[searchwall].shade,
                             sprite[searchwall].pal,sprite[searchwall].cstat,sprite[searchwall].lotag,
                             sprite[searchwall].hitag,sprite[searchwall].extra);

                Bsprintf(lines[num++],"Repeat:  %d,%d",sprite[searchwall].xrepeat,sprite[searchwall].yrepeat);
                Bsprintf(lines[num++],"PosXY:   %d,%d",sprite[searchwall].x,sprite[searchwall].y);
                Bsprintf(lines[num++],"PosZ: ""   %d",sprite[searchwall].z);// prevents tab character
                lines[num++][0]=0;

                if (getmessageleng)
                    break;
                if (strlen(names[sprite[searchwall].picnum]) > 0)
                {
                    if (sprite[searchwall].picnum==SECTOREFFECTOR)
                        Bsprintf(lines[num++],"^251Sprite %d^31 %s",searchwall,SectorEffectorText(searchwall));
                    else Bsprintf(lines[num++],"^251Sprite %d^31 %s",searchwall,names[sprite[searchwall].picnum]);
                }
                else Bsprintf(lines[num++],"^251Sprite %d^31, picnum %d",searchwall,sprite[searchwall].picnum);
                Bsprintf(lines[num++],"Elevation:%d",getflorzofslope(searchsector,sprite[searchwall].x,sprite[searchwall].y)-sprite[searchwall].z);
                break;
            }
        }
        x=WIND1X;y=WIND1Y;
        x*=xdimgame/320.;
        y*=ydimgame/200.;
        y+=(ydimgame>>6)*8;
        if (getmessageleng)
        {
            while (num < 4)
                lines[num++][0] = 0;
            Bsprintf(lines[num++],"^251%s",getmessage);
        }
        begindrawing();
        for (i=0;i<num;i++)
        {
            printext256(x+2,y+2,0,-1,lines[i],xdimgame>640?0:1);
            printext256(x,y,whitecol,-1,lines[i],xdimgame>640?0:1);
            y+=ydimgame>>6;
        }
        enddrawing();
    }

    if (keystatus[KEYSC_QUOTE] && keystatus[KEYSC_V]) // ' V
    {
        keystatus[KEYSC_V] = 0;
        switch (searchstat)
        {
        case 1:
        case 2:
            Bstrcpy(tempbuf,"Sector visibility: ");
            sector[searchsector].visibility =
                getnumber256(tempbuf,sector[searchsector].visibility,256L,0);
            break;
        }
    }

    if (keystatus[KEYSC_SEMI] && keystatus[KEYSC_V])   // ; V
    {
        short currsector;
        unsigned char visval;

        keystatus[KEYSC_V] = 0;

        if (highlightsectorcnt == -1)
        {
            message("You didn't select any sectors!");
            return;
        }
        visval = (unsigned char)getnumber256("Visibility of selected sectors: ",sector[searchsector].visibility,256L,0);
        if (AskIfSure(0)) return;

        for (i = 0; i < highlightsectorcnt; i++)
        {
            currsector = highlightsector[i];
            sector[currsector].visibility = visval;
        }
        message("Visibility changed on all selected sectors");
    }

    if (keystatus[KEYSC_V])  //V
    {
        int oldtile;
        if (searchstat == 0) tempint = wall[searchwall].picnum;
        if (searchstat == 1) tempint = sector[searchsector].ceilingpicnum;
        if (searchstat == 2) tempint = sector[searchsector].floorpicnum;
        if (searchstat == 3) tempint = sprite[searchwall].picnum;
        if (searchstat == 4) tempint = wall[searchwall].overpicnum;
        oldtile = tempint;
        tempint = m32gettile(tempint);
        if (searchstat == 0) wall[searchwall].picnum = tempint;
        if (searchstat == 1) sector[searchsector].ceilingpicnum = tempint;
        if (searchstat == 2) sector[searchsector].floorpicnum = tempint;
        if (searchstat == 3) sprite[searchwall].picnum = tempint;
        if (searchstat == 4)
        {
            wall[searchwall].overpicnum = tempint;
            if (wall[searchwall].nextwall >= 0)
                wall[wall[searchwall].nextwall].overpicnum = tempint;
        }
        if (oldtile!=tempint)asksave = 1;
        keystatus[KEYSC_V] = 0;
    }

    if (keystatus[KEYSC_3])  /* 3 (toggle floor-over-floor (cduke3d only) */
    {
        floor_over_floor = !floor_over_floor;
        //        if (!floor_over_floor) ResetFOFSize();
        message("Floor-over-floor display %s",floor_over_floor?"enabled":"disabled");
        keystatus[KEYSC_3] = 0;
    }

    if (keystatus[KEYSC_F3])
    {
        mlook = 1-mlook;
        message("Mouselook: %s",mlook?"enabled":"disabled");
        keystatus[KEYSC_F3] = 0;
    }

// PK
    if (keystatus[KEYSC_F5])
    {
        unrealedlook = 1-unrealedlook;
        message("UnrealEd mouse navigation: %s",unrealedlook?"enabled":"disabled");
        keystatus[KEYSC_F5] = 0;
    }

    if (keystatus[KEYSC_QUOTE] && keystatus[KEYSC_BS]) // ' del
    {
        keystatus[KEYSC_BS] = 0;
        switch (searchstat)
        {
        case 0:
        case 4:
            wall[searchwall].cstat = 0;
            message("Wall %d cstat = 0",searchwall);
            break;
            //            case 1: case 2: sector[searchsector].cstat = 0; break;
        case 3:
            sprite[searchwall].cstat = 0;
            message("Sprite %d cstat = 0",searchwall);
            break;
        }
    }

    // 'P - Will copy palette to all sectors highlighted with R-Alt key
    if (keystatus[KEYSC_QUOTE] && keystatus[KEYSC_P])   // ' P
    {
        short w, start_wall, end_wall, currsector;
        signed char pal[4];

        keystatus[KEYSC_P] = 0;

        if (highlightsectorcnt == -1)
        {
            message("You didn't select any sectors!");
            return;
        }

        pal[0] = getnumber256("Ceiling palette: ",-1,MAXPALOOKUPS,1);
        pal[1] = getnumber256("Floor palette: ",-1,MAXPALOOKUPS,1);
        pal[2] = getnumber256("Wall palette: ",-1,MAXPALOOKUPS,1);
        pal[3] = getnumber256("Sprite palette: ",-1,MAXPALOOKUPS,1);
        if (AskIfSure(0)) return;

        for (i = 0; i < highlightsectorcnt; i++)
        {
            currsector = highlightsector[i];
            if (pal[0] > -1)
                sector[currsector].ceilingpal = pal[0];
            if (pal[1] > -1)
                sector[currsector].floorpal = pal[1];
            // Do all the walls in the sector
            start_wall = sector[currsector].wallptr;
            end_wall = start_wall + sector[currsector].wallnum;

            if (pal[2] > -1)
                for (w = start_wall; w < end_wall; w++)
                {
                    wall[w].pal = pal[2];
                }
            if (pal[3] > -1)
                for (k=0;k<highlightsectorcnt;k++)
                {
                    w = headspritesect[highlightsector[k]];
                    while (w >= 0)
                    {
                        j = nextspritesect[w];
                        sprite[w].pal = pal[3];
                        w = j;
                    }
                }
        }
        message("Palettes changed");
    }

    // ;P - Will copy palette to all sectors & sprites within highlighted with R-Alt key
    if (keystatus[KEYSC_SEMI] && keystatus[KEYSC_P])   // ; P
    {
        short w, start_wall, end_wall, currsector;
        unsigned char pal;

        keystatus[KEYSC_P] = 0;

        if (highlightsectorcnt == -1)
        {
            message("You didn't select any sectors!");
            return;
        }

        pal = (unsigned char)getnumber256("Global palette: ",0,MAXPALOOKUPS,0);
        if (AskIfSure(0)) return;

        for (i = 0; i < highlightsectorcnt; i++)
        {
            currsector = highlightsector[i];
            sector[currsector].ceilingpal = pal;
            sector[currsector].floorpal = pal;
            // 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].pal = pal;
            }
            for (k=0;k<highlightsectorcnt;k++)
            {
                w = headspritesect[highlightsector[k]];
                while (w >= 0)
                {
                    j = nextspritesect[w];
                    sprite[w].pal = pal;
                    w = j;
                }
            }
        }
        message("Palettes changed");
    }

    if (keystatus[KEYSC_DELETE])
    {
        if (searchstat == 3)
        {
            deletesprite(searchwall);
            updatenumsprites();
            message("Sprite %d deleted",searchwall);
            asksave = 1;
        }
        keystatus[KEYSC_DELETE] = 0;
    }

    if (keystatus[KEYSC_F6])  //F6
    {
        keystatus[KEYSC_F6] = 0;
        autospritehelp=!autospritehelp;
        message("Automatic SECTOREFFECTOR help %s",autospritehelp?"enabled":"disabled");
    }
    if (keystatus[KEYSC_F7])  //F7
    {
        keystatus[KEYSC_F7] = 0;
        autosecthelp=!autosecthelp;
        message("Automatic sector tag help %s",autosecthelp?"enabled":"disabled");
    }

    if ((searchstat == 3) && (sprite[searchwall].picnum==SECTOREFFECTOR))
        if (autospritehelp && helpon==0) Show3dText("sehelp.hlp");

    if (searchstat == 1 || searchstat == 2)
        if (autosecthelp && helpon==0) Show3dText("sthelp.hlp");



    if (keystatus[KEYSC_COMMA]) // , Search & fix panning to the left (3D)
    {
        if (searchstat == 3)
        {
            i = searchwall;
            if (eitherSHIFT)
                sprite[i].ang = ((sprite[i].ang+2048-1)&2047);
            else
            {
                sprite[i].ang = ((sprite[i].ang+2048-128)&2047);
                keystatus[KEYSC_COMMA] = 0;
            }
            message("Sprite %d angle: %d",i,sprite[i].ang);
        }
    }
    if (keystatus[KEYSC_PERIOD]) // . Search & fix panning to the right (3D)
    {
        if ((searchstat == 0) || (searchstat == 4))
        {
            AutoAlignWalls((int)searchwall,0L);
            message("Wall %d autoalign",searchwall);
            keystatus[KEYSC_PERIOD] = 0;
        }
        if (searchstat == 3)
        {
            i = searchwall;
            if (eitherSHIFT)
                sprite[i].ang = ((sprite[i].ang+2048+1)&2047);
            else
            {
                sprite[i].ang = ((sprite[i].ang+2048+128)&2047);
                keystatus[KEYSC_PERIOD] = 0;
            }
            message("Sprite %d angle: %d",i,sprite[i].ang);
        }
    }

    if (keystatus[KEYSC_QUOTE] && keystatus[KEYSC_L]) // ' L
    {
        switch (searchstat)
        {
        case 1:
            strcpy(tempbuf,"Sector ceilingz: ");
            sector[searchsector].ceilingz = getnumber256(tempbuf,sector[searchsector].ceilingz,8388608,1);
            if (!(sector[searchsector].ceilingstat&2))
                sector[searchsector].ceilingheinum = 0;

            strcpy(tempbuf,"Sector ceiling slope: ");
            sector[searchsector].ceilingheinum = getnumber256(tempbuf,sector[searchsector].ceilingheinum,65536,1);
            break;
        case 2:
            strcpy(tempbuf,"Sector floorz: ");
            sector[searchsector].floorz = getnumber256(tempbuf,sector[searchsector].floorz,8388608,1);
            if (!(sector[searchsector].floorstat&2))
                sector[searchsector].floorheinum = 0;
            strcpy(tempbuf,"Sector floor slope: ");
            sector[searchsector].floorheinum = getnumber256(tempbuf,sector[searchsector].floorheinum,65536,1);
            break;
        case 3:
            strcpy(tempbuf,"Sprite x: ");
            sprite[searchwall].x = getnumber256(tempbuf,sprite[searchwall].x,131072,1);
            strcpy(tempbuf,"Sprite y: ");
            sprite[searchwall].y = getnumber256(tempbuf,sprite[searchwall].y,131072,1);
            strcpy(tempbuf,"Sprite z: ");
            sprite[searchwall].z = getnumber256(tempbuf,sprite[searchwall].z,8388608,1);
            strcpy(tempbuf,"Sprite angle: ");
            sprite[searchwall].ang = getnumber256(tempbuf,sprite[searchwall].ang,2048L,0);
            break;
        }
        if (sector[searchsector].ceilingheinum == 0)
            sector[searchsector].ceilingstat &= ~2;
        else
            sector[searchsector].ceilingstat |= 2;

        if (sector[searchsector].floorheinum == 0)
            sector[searchsector].floorstat &= ~2;
        else
            sector[searchsector].floorstat |= 2;
        asksave = 1;
        keystatus[KEYSC_L] = 0;
    }

    getzrange(posx,posy,posz,cursectnum,&hiz,&hihit,&loz,&lohit,128L,CLIPMASK0);

    if (keystatus[KEYSC_CAPS] || (keystatus[KEYSC_QUOTE] && keystatus[KEYSC_Z]))
    {
        zmode++;
        if (zmode == 3) zmode = 0;
        else if (zmode == 1) zlock = (loz-posz)&0xfffffc00;
        switch (zmode)
        {
        case 0: message("Zmode = Gravity");break;
        case 1: message("Zmode = Locked/Sector");break;
        case 2: message("Zmode = Locked/Free");break;
        }
        keystatus[KEYSC_CAPS] = keystatus[KEYSC_Z] = 0;
    }

    if (keystatus[KEYSC_QUOTE] && keystatus[KEYSC_M])  // M
    {
        switch (searchstat)
        {
        case 0:
        case 4:
            strcpy(buffer,"Wall extra: ");
            wall[searchwall].extra = getnumber256(buffer,(int)wall[searchwall].extra,65536L,1);
            break;
        case 1:
            strcpy(buffer,"Sector extra: ");
            sector[searchsector].extra = getnumber256(buffer,(int)sector[searchsector].extra,65536L,1);
            break;
        case 2:
            strcpy(buffer,"Sector extra: ");
            sector[searchsector].extra = getnumber256(buffer,(int)sector[searchsector].extra,65536L,1);
            break;
        case 3:
            strcpy(buffer,"Sprite extra: ");
            sprite[searchwall].extra = getnumber256(buffer,(int)sprite[searchwall].extra,65536L,1);
            break;
        }
        asksave = 1;
        keystatus[KEYSC_M] = 0;
    }

    if (keystatus[KEYSC_1])  // 1 (make 1-way wall)
    {
        if (searchstat != 3)
        {
            wall[searchwall].cstat ^= 32;
            sprintf(getmessage,"Wall %d one side masking bit %s",searchwall,wall[searchwall].cstat&32?"ON":"OFF");
            message(getmessage);
            asksave = 1;
        }
        else
        {
            sprite[searchwall].cstat ^= 64;
            i = sprite[searchwall].cstat;
            if ((i&48) == 32)
            {
                sprite[searchwall].cstat &= ~8;
                if ((i&64) > 0)
                    if (posz > sprite[searchwall].z)
                        sprite[searchwall].cstat |= 8;
            }
            asksave = 1;
            sprintf(getmessage,"Sprite %d one sided bit %s",searchwall,sprite[searchwall].cstat&64?"ON":"OFF");
            message(getmessage);

        }
        keystatus[KEYSC_1] = 0;
    }
    if (keystatus[KEYSC_2])  // 2 (bottom wall swapping)
    {
        if (searchstat != 3)
        {
            wall[searchwall].cstat ^= 2;
            sprintf(getmessage,"Wall %d bottom texture swap bit %s",searchwall,wall[searchwall].cstat&2?"ON":"OFF");
            message(getmessage);
            asksave = 1;
        }
        keystatus[KEYSC_2] = 0;
    }
    if (keystatus[KEYSC_O])  // O (top/bottom orientation - for doors)
    {
        if ((searchstat == 0) || (searchstat == 4))
        {
            wall[searchwall].cstat ^= 4;
            Bsprintf(getmessage,"Wall %d %s orientation",searchwall,wall[searchwall].cstat&4?"bottom":"top");
            message(getmessage);
            asksave = 1;
        }
        if (searchstat == 3)   // O (ornament onto wall) (2D)
        {
            asksave = 1;
            i = searchwall;

            hitscan(sprite[i].x,sprite[i].y,sprite[i].z,sprite[i].sectnum,
                    sintable[(sprite[i].ang+2560+1024)&2047],
                    sintable[(sprite[i].ang+2048+1024)&2047],
                    0,
                    &hitsect,&hitwall,&hitsprite,&hitx,&hity,&hitz,CLIPMASK1);

            sprite[i].x = hitx;
            sprite[i].y = hity;
            sprite[i].z = hitz;
            changespritesect(i,hitsect);
            if (hitwall >= 0)
                sprite[i].ang = ((getangle(wall[wall[hitwall].point2].x-wall[hitwall].x,wall[wall[hitwall].point2].y-wall[hitwall].y)+512)&2047);

            //Make sure sprite's in right sector
            if (inside(sprite[i].x,sprite[i].y,sprite[i].sectnum) == 0)
            {
                j = wall[hitwall].point2;
                sprite[i].x -= ksgn(wall[j].y-wall[hitwall].y);
                sprite[i].y += ksgn(wall[j].x-wall[hitwall].x);
            }
            Bsprintf(getmessage,"Sprite %d ornament onto wall",i);
            message(getmessage);
        }
        keystatus[KEYSC_O] = 0;
    }
    if (keystatus[KEYSC_M])  // M (masking walls)
    {
        if (searchstat != 3)
        {
            i = wall[searchwall].nextwall;
            tempint = eitherSHIFT;
            if (i >= 0)
            {
                wall[searchwall].cstat ^= 16;
                sprintf(getmessage,"Wall %d masking bit %s",searchwall,wall[searchwall].cstat&16?"ON":"OFF");
                message(getmessage);
                if ((wall[searchwall].cstat&16) > 0)
                {
                    wall[searchwall].cstat &= ~8;
                    if (tempint == 0)
                    {
                        wall[i].cstat |= 8;           //auto other-side flip
                        wall[i].cstat |= 16;
                        wall[i].overpicnum = wall[searchwall].overpicnum;
                    }
                }
                else
                {
                    wall[searchwall].cstat &= ~8;
                    if (tempint == 0)
                    {
                        wall[i].cstat &= ~8;         //auto other-side unflip
                        wall[i].cstat &= ~16;
                    }
                }
                wall[searchwall].cstat &= ~32;
                if (tempint == 0) wall[i].cstat &= ~32;
                asksave = 1;
            }
        }
        keystatus[KEYSC_M] = 0;
    }

    if (keystatus[KEYSC_H])  // H (hitscan sensitivity)
    {
        if ((keystatus[KEYSC_QUOTE]))
        {
            switch (searchstat)
            {
            case 0:
            case 4:
                strcpy(buffer,"Wall hitag: ");
                wall[searchwall].hitag = getnumber256(buffer,(int)wall[searchwall].hitag,65536L,0);
                break;
            case 1:
                strcpy(buffer,"Sector hitag: ");
                sector[searchsector].hitag = getnumber256(buffer,(int)sector[searchsector].hitag,65536L,0);
                break;
            case 2:
                strcpy(buffer,"Sector hitag: ");
                sector[searchsector].hitag = getnumber256(buffer,(int)sector[searchsector].hitag,65536L,0);
                break;
            case 3:
                strcpy(buffer,"Sprite hitag: ");
                sprite[searchwall].hitag = getnumber256(buffer,(int)sprite[searchwall].hitag,65536L,0);
                break;
            }
        }

        else
        {

            if (searchstat == 3)
            {
                sprite[searchwall].cstat ^= 256;
                sprintf(getmessage,"Sprite %d hitscan sensitivity bit %s",searchwall,sprite[searchwall].cstat&256?"ON":"OFF");
                message(getmessage);
                asksave = 1;
            }
            else
            {
                wall[searchwall].cstat ^= 64;

                if ((wall[searchwall].nextwall >= 0) && (eitherSHIFT == 0))
                {
                    wall[wall[searchwall].nextwall].cstat &= ~64;
                    wall[wall[searchwall].nextwall].cstat |= (wall[searchwall].cstat&64);
                }
                sprintf(getmessage,"Wall %d hitscan sensitivity bit %s",searchwall,wall[searchwall].cstat&64?"ON":"OFF");
                message(getmessage);

                asksave = 1;
            }
        }
        keystatus[KEYSC_H] = 0;
    }

    smooshyalign = keystatus[KEYSC_gKP5];
    repeatpanalign = eitherSHIFT || (bstatus&2);

    if (mlook == 2)
        mlook = 0;

    if (!unrealedlook && (bstatus&4)) mlook = 2;

//    if (bstatus&4)
    if ((bstatus&(16|32) && !(bstatus&(1|2|4))) || keystatus[KEYSC_gMINUS] || keystatus[KEYSC_gPLUS])  // PK: no btn: wheel changes shade
    {
//        if (bstatus&1)
//        {
//            mlook = 2;
//        }
        if (bstatus&32 || keystatus[KEYSC_gMINUS])  // -
        {
            keystatus[KEYSC_gMINUS]=0;
            mouseb &= ~32;
            bstatus &= ~32;
            if (eitherALT)  //ALT
            {
                if (eitherCTRL)  //CTRL
                {
                    if (visibility < 16384) visibility += visibility;
                    Bsprintf(getmessage,"Global visibility %d",visibility);
                    message(getmessage);
                }
                else
                {
                    k=eitherSHIFT?1:16;

                    if (highlightsectorcnt >= 0)
                        for (i=0;i<highlightsectorcnt;i++)
                            if (highlightsector[i] == searchsector)
                            {
                                while (k > 0)
                                {
                                    for (i=0;i<highlightsectorcnt;i++)
                                    {
                                        sector[highlightsector[i]].visibility++;
                                        if (sector[highlightsector[i]].visibility == 240)
                                            sector[highlightsector[i]].visibility = 239;
                                    }
                                    k--;
                                }
                                break;
                            }
                    while (k > 0)
                    {
                        sector[searchsector].visibility++;
                        if (sector[searchsector].visibility == 240)
                            sector[searchsector].visibility = 239;
                        k--;
                    }
                    Bsprintf(getmessage,"Sector %d visibility %d",searchsector,sector[searchsector].visibility);
                    message(getmessage);
                    asksave = 1;
                }
            }
            else
            {
                k = 0;
                if (highlightsectorcnt >= 0)
                {
                    for (i=0;i<highlightsectorcnt;i++)
                        if (highlightsector[i] == searchsector)
                        {
                            k = 1;
                            break;
                        }
                }

                if (k == 0)
                {
                    int shade=-1, i=-1;
                    if (searchstat == 0) shade=++wall[i=searchwall].shade;
                    if (searchstat == 1) shade=++sector[i=searchsector].ceilingshade;
                    if (searchstat == 2) shade=++sector[i=searchsector].floorshade;
                    if (searchstat == 3) shade=++sprite[i=searchwall].shade;
                    if (searchstat == 4) shade=++wall[i=searchwall].shade;
                    if (i!=-1)
                    {
                        Bsprintf(getmessage,"%s %d shade %d",type2str[searchstat],i,shade);
                        message(getmessage);
                    }
                }
                else
                {
                    for (i=0;i<highlightsectorcnt;i++)
                    {
                        dasector = highlightsector[i];

                        sector[dasector].ceilingshade++;        //sector shade
                        sector[dasector].floorshade++;

                        startwall = sector[dasector].wallptr;   //wall shade
                        endwall = startwall + sector[dasector].wallnum - 1;
                        for (j=startwall;j<=endwall;j++)
                            wall[j].shade++;

                        j = headspritesect[dasector];           //sprite shade
                        while (j != -1)
                        {
                            sprite[j].shade++;
                            j = nextspritesect[j];
                        }
                    }
                }
                asksave = 1;
            }
        }
        if (bstatus&16 || keystatus[KEYSC_gPLUS])  // +
        {
            keystatus[KEYSC_gPLUS]=0;
            mouseb &= ~16;
            bstatus &= ~16;
            if (eitherALT)  //ALT
            {
                if (eitherCTRL)  //CTRL
                {
                    if (visibility > 32) visibility >>= 1;
                    Bsprintf(getmessage,"Global visibility %d",visibility);
                    message(getmessage);
                }
                else
                {
                    k=eitherSHIFT?1:16;

                    if (highlightsectorcnt >= 0)
                        for (i=0;i<highlightsectorcnt;i++)
                            if (highlightsector[i] == searchsector)
                            {
                                while (k > 0)
                                {
                                    for (i=0;i<highlightsectorcnt;i++)
                                    {
                                        sector[highlightsector[i]].visibility--;
                                        if (sector[highlightsector[i]].visibility == 239)
                                            sector[highlightsector[i]].visibility = 240;
                                    }
                                    k--;
                                }
                                break;
                            }
                    while (k > 0)
                    {
                        sector[searchsector].visibility--;
                        if (sector[searchsector].visibility == 239)
                            sector[searchsector].visibility = 240;
                        k--;
                    }
                    Bsprintf(getmessage,"Sector %d visibility %d",searchsector,sector[searchsector].visibility);
                    message(getmessage);
                    asksave = 1;
                }
            }
            else
            {
                k = 0;
                if (highlightsectorcnt >= 0)
                {
                    for (i=0;i<highlightsectorcnt;i++)
                        if (highlightsector[i] == searchsector)
                        {
                            k = 1;
                            break;
                        }
                }

                if (k == 0)
                {
                    int shade=-1, i=-1;
                    if (searchstat == 0) shade=--wall[i=searchwall].shade;
                    if (searchstat == 1) shade=--sector[i=searchsector].ceilingshade;
                    if (searchstat == 2) shade=--sector[i=searchsector].floorshade;
                    if (searchstat == 3) shade=--sprite[i=searchwall].shade;
                    if (searchstat == 4) shade=--wall[i=searchwall].shade;
                    if (i!=-1)
                    {
                        Bsprintf(getmessage,"%s %d shade %d",type2str[searchstat],i,shade);
                        message(getmessage);
                    }
                }
                else
                {
                    for (i=0;i<highlightsectorcnt;i++)
                    {
                        dasector = highlightsector[i];

                        sector[dasector].ceilingshade--;        //sector shade
                        sector[dasector].floorshade--;

                        startwall = sector[dasector].wallptr;   //wall shade
                        endwall = startwall + sector[dasector].wallnum - 1;
                        for (j=startwall;j<=endwall;j++)
                            wall[j].shade--;

                        j = headspritesect[dasector];           //sprite shade
                        while (j != -1)
                        {
                            sprite[j].shade--;
                            j = nextspritesect[j];
                        }
                    }
                }
                asksave = 1;
            }
        }
    }

//    if ((keystatus[KEYSC_DASH]|keystatus[KEYSC_EQUAL]|((bstatus&(16|32)) && !(bstatus&2))) > 0) // mousewheel, -, and +, cycle picnum
    if (keystatus[KEYSC_DASH] | keystatus[KEYSC_EQUAL] | (bstatus&(16|32) && (bstatus&1) && !(bstatus&2)))  // PK: lmb only & mousewheel, -, and +, cycle picnum

    {
        j = i = (keystatus[KEYSC_EQUAL] || (bstatus&16))?1:-1;
        switch (searchstat)
        {
        case 0:
            while (!tilesizx[wall[searchwall].picnum]||!tilesizy[wall[searchwall].picnum]||j)
            {
                if (wall[searchwall].picnum+i >= MAXTILES) wall[searchwall].picnum = 0;
                else if (wall[searchwall].picnum+i < 0) wall[searchwall].picnum = MAXTILES-1;
                else wall[searchwall].picnum += i;
                j = 0;
            }
            break;
        case 1:
            while (!tilesizx[sector[searchsector].ceilingpicnum]||!tilesizy[sector[searchsector].ceilingpicnum]||j)
            {
                if (sector[searchsector].ceilingpicnum+i >= MAXTILES) sector[searchsector].ceilingpicnum = 0;
                else if (sector[searchsector].ceilingpicnum+i < 0) sector[searchsector].ceilingpicnum = MAXTILES-1;
                else sector[searchsector].ceilingpicnum += i;
                j = 0;
            }
            break;
        case 2:
            while (!tilesizx[sector[searchsector].floorpicnum]||!tilesizy[sector[searchsector].floorpicnum]||j)
            {
                if (sector[searchsector].floorpicnum+i >= MAXTILES) sector[searchsector].floorpicnum = 0;
                else if (sector[searchsector].floorpicnum+i < 0) sector[searchsector].floorpicnum = MAXTILES-1;
                else sector[searchsector].floorpicnum += i;
                j = 0;
            }
            break;
        case 3:
            while (!tilesizx[sprite[searchwall].picnum]||!tilesizy[sprite[searchwall].picnum]||j)
            {
                if (sprite[searchwall].picnum+i >= MAXTILES) sprite[searchwall].picnum = 0;
                else if (sprite[searchwall].picnum+i < 0) sprite[searchwall].picnum = MAXTILES-1;
                else sprite[searchwall].picnum += i;
                j = 0;
            }
            break;
        case 4:
            while (!tilesizx[wall[searchwall].overpicnum]||!tilesizy[wall[searchwall].overpicnum]||j)
            {
                if (wall[searchwall].overpicnum+i >= MAXTILES) wall[searchwall].overpicnum = 0;
                else if (wall[searchwall].overpicnum+i < 0) wall[searchwall].overpicnum = MAXTILES-1;
                else wall[searchwall].overpicnum += i;
                j = 0;
            }
            break;
        }
        asksave = 1;
        keystatus[KEYSC_DASH] = keystatus[KEYSC_EQUAL] = 0;
        mouseb &= ~(16|32);
    }

    if (keystatus[KEYSC_E])  // E (expand)
    {
        if (searchstat == 1)
        {
            sector[searchsector].ceilingstat ^= 8;
            sprintf(getmessage,"Sector %d ceiling texture expansion bit %s",searchsector,sector[searchsector].ceilingstat&8?"ON":"OFF");
            message(getmessage);
            asksave = 1;
        }
        if (searchstat == 2)
        {
            sector[searchsector].floorstat ^= 8;
            sprintf(getmessage,"Sector %d floor texture expansion bit %s",searchsector,sector[searchsector].floorstat&8?"ON":"OFF");
            message(getmessage);
            asksave = 1;
        }
        keystatus[KEYSC_E] = 0;
    }
    if (keystatus[KEYSC_R])  // R (relative alignment, rotation)
    {

        if (keystatus[KEYSC_QUOTE]) // FRAMERATE TOGGLE
        {

            framerateon = !framerateon;
            if (framerateon) message("Show framerate ON");
            else message("Show framerate OFF");

        }

        else

        {
            if (searchstat == 1)
            {
                sector[searchsector].ceilingstat ^= 64;
                sprintf(getmessage,"Sector %d ceiling texture relativity bit %s",searchsector,sector[searchsector].ceilingstat&64?"ON":"OFF");
                message(getmessage);
                asksave = 1;
            }
            if (searchstat == 2)
            {
                sector[searchsector].floorstat ^= 64;
                sprintf(getmessage,"Sector %d floor texture relativity bit %s",searchsector,sector[searchsector].floorstat&64?"ON":"OFF"); //PK (was ceiling in string)
                message(getmessage);
                asksave = 1;
            }
            if (searchstat == 3)
            {
                i = sprite[searchwall].cstat;
                if ((i&48) < 32) i += 16;

                else i &= ~48;
                sprite[searchwall].cstat = i;

                if (sprite[searchwall].cstat&16)
                    sprintf(getmessage,"Sprite %d now wall aligned",searchwall);
                else if (sprite[searchwall].cstat&32)
                    sprintf(getmessage,"Sprite %d now floor aligned",searchwall);
                else
                    sprintf(getmessage,"Sprite %d now view aligned",searchwall);
                message(getmessage);
                asksave = 1;
            }
        }
        keystatus[KEYSC_R] = 0;
    }
    if (keystatus[KEYSC_F])  //F (Flip)
    {
        keystatus[KEYSC_F] = 0;
        if (eitherALT)  //ALT-F (relative alignmment flip)
        {
            if (searchstat != 3)
            {
                setfirstwall(searchsector,searchwall);
                Bsprintf(getmessage,"Sector %d first wall",searchsector);
                message(getmessage);
                asksave = 1;
            }
        }
        else
        {
            if ((searchstat == 0) || (searchstat == 4))
            {
                i = wall[searchwall].cstat;
                i = ((i>>3)&1)+((i>>7)&2);    //3-x,8-y
                switch (i)
                {
                case 0:
                    i = 1;
                    break;
                case 1:
                    i = 3;
                    break;
                case 2:
                    i = 0;
                    break;
                case 3:
                    i = 2;
                    break;
                }
                Bsprintf(getmessage,"Wall %d flip %d",searchwall,i);
                message(getmessage);
                i = ((i&1)<<3)+((i&2)<<7);
                wall[searchwall].cstat &= ~0x0108;
                wall[searchwall].cstat |= i;
                asksave = 1;
            }
            if (searchstat == 1)         //8-way ceiling flipping (bits 2,4,5)
            {
                i = sector[searchsector].ceilingstat;
                i = (i&0x4)+((i>>4)&3);
                switch (i)
                {
                case 0:
                    i = 6;
                    break;
                case 6:
                    i = 3;
                    break;
                case 3:
                    i = 5;
                    break;
                case 5:
                    i = 1;
                    break;
                case 1:
                    i = 7;
                    break;
                case 7:
                    i = 2;
                    break;
                case 2:
                    i = 4;
                    break;
                case 4:
                    i = 0;
                    break;
                }
                Bsprintf(getmessage,"Sector %d flip %d",searchsector,i);
                message(getmessage);
                i = (i&0x4)+((i&3)<<4);
                sector[searchsector].ceilingstat &= ~0x34;
                sector[searchsector].ceilingstat |= i;
                asksave = 1;
            }
            if (searchstat == 2)         //8-way floor flipping (bits 2,4,5)
            {
                i = sector[searchsector].floorstat;
                i = (i&0x4)+((i>>4)&3);
                switch (i)
                {
                case 0:
                    i = 6;
                    break;
                case 6:
                    i = 3;
                    break;
                case 3:
                    i = 5;
                    break;
                case 5:
                    i = 1;
                    break;
                case 1:
                    i = 7;
                    break;
                case 7:
                    i = 2;
                    break;
                case 2:
                    i = 4;
                    break;
                case 4:
                    i = 0;
                    break;
                }
                Bsprintf(getmessage,"Sector %d flip %d",searchsector,i);
                message(getmessage);
                i = (i&0x4)+((i&3)<<4);
                sector[searchsector].floorstat &= ~0x34;
                sector[searchsector].floorstat |= i;
                asksave = 1;
            }
            if (searchstat == 3)
            {
                i = sprite[searchwall].cstat;
                if (((i&48) == 32) && ((i&64) == 0))
                {
                    sprite[searchwall].cstat &= ~0xc;
                    sprite[searchwall].cstat |= ((i&4)^4);
                    Bsprintf(getmessage,"Sprite %d flip bit %s",searchwall,sprite[searchwall].cstat&4?"ON":"OFF");
                    message(getmessage);
                }
                else
                {
                    i = ((i>>2)&3);
                    switch (i)
                    {
                    case 0:
                        i = 1;
                        break;
                    case 1:
                        i = 3;
                        break;
                    case 2:
                        i = 0;
                        break;
                    case 3:
                        i = 2;
                        break;
                    }
                    Bsprintf(getmessage,"Sprite %d flip %d",searchwall,i);
                    message(getmessage);
                    i <<= 2;
                    sprite[searchwall].cstat &= ~0xc;
                    sprite[searchwall].cstat |= i;
                }
                asksave = 1;
            }
        }
    }

    if (keystatus[KEYSC_HOME])
        updownunits = 256;
    else if (keystatus[KEYSC_END])
        updownunits = 512;
    else
        updownunits = 1024;
    mouseaction=0;
    if (eitherALT && bstatus&1)
    {
        mousex=0;mskip=1;
        if (mousey<0)
        {
            updownunits=klabs(mousey*128);
            mouseaction=1;
        }
    }
    if (keystatus[KEYSC_PGUP] || mouseaction || ((bstatus&2) && (bstatus&16 && !(bstatus&1)))) // PK: PGUP, rmb only & mwheel
    {
        k = 0;
        if (highlightsectorcnt >= 0)
        {
            for (i=0;i<highlightsectorcnt;i++)
                if (highlightsector[i] == searchsector)
                {
                    k = 1;
                    break;
                }
        }

        if ((searchstat == 0) || (searchstat == 1))
        {
            if (k == 0)
            {
                i = headspritesect[searchsector];
                while (i != -1)
                {
                    tempint = getceilzofslope(searchsector,sprite[i].x,sprite[i].y);
                    tempint += ((tilesizy[sprite[i].picnum]*sprite[i].yrepeat)<<2);
                    if (sprite[i].cstat&128) tempint += ((tilesizy[sprite[i].picnum]*sprite[i].yrepeat)<<1);
                    if (sprite[i].z == tempint)
                        sprite[i].z -= updownunits << (eitherCTRL<<1);   // JBF 20031128
                    i = nextspritesect[i];
                }
                sector[searchsector].ceilingz -= updownunits << (eitherCTRL<<1); // JBF 20031128
                sprintf(getmessage,"Sector %d ceilingz = %d",searchsector,sector[searchsector].ceilingz);
                message(getmessage);

            }
            else
            {
                for (j=0;j<highlightsectorcnt;j++)
                {
                    i = headspritesect[highlightsector[j]];
                    while (i != -1)
                    {
                        tempint = getceilzofslope(highlightsector[j],sprite[i].x,sprite[i].y);
                        tempint += ((tilesizy[sprite[i].picnum]*sprite[i].yrepeat)<<2);
                        if (sprite[i].cstat&128) tempint += ((tilesizy[sprite[i].picnum]*sprite[i].yrepeat)<<1);
                        if (sprite[i].z == tempint)
                            sprite[i].z -= updownunits << (eitherCTRL<<1);   // JBF 20031128
                        i = nextspritesect[i];
                    }
                    sector[highlightsector[j]].ceilingz -= updownunits << (eitherCTRL<<1);   // JBF 20031128
                    sprintf(getmessage,"Sector %d ceilingz = %d",*highlightsector,sector[highlightsector[j]].ceilingz);
                    message(getmessage);

                }
            }
        }
        if (searchstat == 2)
        {
            if (k == 0)
            {
                i = headspritesect[searchsector];
                while (i != -1)
                {
                    tempint = getflorzofslope(searchsector,sprite[i].x,sprite[i].y);
                    if (sprite[i].cstat&128) tempint += ((tilesizy[sprite[i].picnum]*sprite[i].yrepeat)<<1);
                    if (sprite[i].z == tempint)
                        sprite[i].z -= updownunits << (eitherCTRL<<1);   // JBF 20031128
                    i = nextspritesect[i];
                }
                sector[searchsector].floorz -= updownunits << (eitherCTRL<<1);   // JBF 20031128
                sprintf(getmessage,"Sector %d floorz = %d",searchsector,sector[searchsector].floorz);
                message(getmessage);

            }
            else
            {
                for (j=0;j<highlightsectorcnt;j++)
                {
                    i = headspritesect[highlightsector[j]];
                    while (i != -1)
                    {
                        tempint = getflorzofslope(highlightsector[j],sprite[i].x,sprite[i].y);
                        if (sprite[i].cstat&128) tempint += ((tilesizy[sprite[i].picnum]*sprite[i].yrepeat)<<1);
                        if (sprite[i].z == tempint)
                            sprite[i].z -= updownunits << (eitherCTRL<<1);   // JBF 20031128
                        i = nextspritesect[i];
                    }
                    sector[highlightsector[j]].floorz -= updownunits << (eitherCTRL<<1); // JBF 20031128
                    sprintf(getmessage,"Sector %d floorz = %d",*highlightsector,sector[highlightsector[j]].floorz);
                    message(getmessage);

                }
            }

        }
        if (sector[searchsector].floorz < sector[searchsector].ceilingz)
            sector[searchsector].floorz = sector[searchsector].ceilingz;
        if (searchstat == 3)
        {
            if (eitherCTRL)  //CTRL - put sprite on ceiling
            {
                sprite[searchwall].z = spriteonceilingz(searchwall);
            }
            else
            {
                k = 0;
                if (highlightcnt >= 0)
                    for (i=0;i<highlightcnt;i++)
                        if (highlight[i] == searchwall+16384)
                        {
                            k = 1;
                            break;
                        }

                if (k == 0)
                {
                    sprite[searchwall].z -= updownunits;
                    if (!spnoclip)sprite[searchwall].z = max(sprite[searchwall].z,spriteonceilingz(searchwall));
                    sprintf(getmessage,"Sprite %d z = %d",searchwall,sprite[searchwall].z);
                    message(getmessage);

                }
                else
                {
                    for (i=0;i<highlightcnt;i++)
                        if ((highlight[i]&0xc000) == 16384)
                        {
                            sprite[highlight[i]&16383].z -= updownunits;
                            if (!spnoclip)sprite[highlight[i]&16383].z = max(sprite[highlight[i]&16383].z,spriteonceilingz(highlight[i]&16383));
                        }
                    sprintf(getmessage,"Sprite %d z = %d",highlight[i]&16383,sprite[highlight[i]&16383].z);
                    message(getmessage);

                }
            }
        }
        asksave = 1;
        keystatus[KEYSC_PGUP] = 0;
        mouseb &= ~16;
    }

    mouseaction=0;
    if (eitherALT && bstatus&1)
    {
        mousex=0;mskip=1;
        if (mousey>0)
        {
            updownunits=klabs(mousey*128);
            mouseaction=1;
        }
    }
    if (keystatus[KEYSC_PGDN] || mouseaction || ((bstatus&2) && (bstatus&32) && !(bstatus&1))) // PK: PGDN, rmb only & mwheel
    {
        k = 0;
        if (highlightsectorcnt >= 0)
        {
            for (i=0;i<highlightsectorcnt;i++)
                if (highlightsector[i] == searchsector)
                {
                    k = 1;
                    break;
                }
        }

        if ((searchstat == 0) || (searchstat == 1))
        {
            if (k == 0)
            {
                i = headspritesect[searchsector];
                while (i != -1)
                {
                    tempint = getceilzofslope(searchsector,sprite[i].x,sprite[i].y);
                    if (sprite[i].cstat&128) tempint += ((tilesizy[sprite[i].picnum]*sprite[i].yrepeat)<<1);
                    tempint += ((tilesizy[sprite[i].picnum]*sprite[i].yrepeat)<<2);
                    if (sprite[i].z == tempint)
                        sprite[i].z += updownunits << (eitherCTRL<<1);   // JBF 20031128
                    i = nextspritesect[i];
                }
                sector[searchsector].ceilingz += updownunits << (eitherCTRL<<1); // JBF 20031128
                sprintf(getmessage,"Sector %d ceilingz = %d",searchsector,sector[searchsector].ceilingz);
                message(getmessage);

            }
            else
            {
                for (j=0;j<highlightsectorcnt;j++)
                {
                    i = headspritesect[highlightsector[j]];
                    while (i != -1)
                    {
                        tempint = getceilzofslope(highlightsector[j],sprite[i].x,sprite[i].y);
                        if (sprite[i].cstat&128) tempint += ((tilesizy[sprite[i].picnum]*sprite[i].yrepeat)<<1);
                        tempint += ((tilesizy[sprite[i].picnum]*sprite[i].yrepeat)<<2);
                        if (sprite[i].z == tempint)
                            sprite[i].z += updownunits << (eitherCTRL<<1);   // JBF 20031128
                        i = nextspritesect[i];
                    }
                    sector[highlightsector[j]].ceilingz += updownunits << (eitherCTRL<<1);   // JBF 20031128
                    sprintf(getmessage,"Sector %d ceilingz = %d",*highlightsector,sector[highlightsector[j]].ceilingz);
                    message(getmessage);

                }
            }
        }
        if (searchstat == 2)
        {
            if (k == 0)
            {
                i = headspritesect[searchsector];
                while (i != -1)
                {
                    tempint = getflorzofslope(searchsector,sprite[i].x,sprite[i].y);
                    if (sprite[i].cstat&128) tempint += ((tilesizy[sprite[i].picnum]*sprite[i].yrepeat)<<1);
                    if (sprite[i].z == tempint)
                        sprite[i].z += updownunits << (eitherCTRL<<1);   // JBF 20031128
                    i = nextspritesect[i];
                }
                sector[searchsector].floorz += updownunits << (eitherCTRL<<1);   // JBF 20031128
                sprintf(getmessage,"Sector %d floorz = %d",searchsector,sector[searchsector].floorz);
                message(getmessage);

            }
            else
            {
                for (j=0;j<highlightsectorcnt;j++)
                {
                    i = headspritesect[highlightsector[j]];
                    while (i != -1)
                    {
                        tempint = getflorzofslope(highlightsector[j],sprite[i].x,sprite[i].y);
                        if (sprite[i].cstat&128) tempint += ((tilesizy[sprite[i].picnum]*sprite[i].yrepeat)<<1);
                        if (sprite[i].z == tempint)
                            sprite[i].z += updownunits << (eitherCTRL<<1);   // JBF 20031128
                        i = nextspritesect[i];
                    }
                    sector[highlightsector[j]].floorz += updownunits << (eitherCTRL<<1); // JBF 20031128
                    sprintf(getmessage,"Sector %d floorz = %d",*highlightsector,sector[highlightsector[j]].floorz);
                    message(getmessage);

                }
            }
        }
        if (sector[searchsector].ceilingz > sector[searchsector].floorz)
            sector[searchsector].ceilingz = sector[searchsector].floorz;
        if (searchstat == 3)
        {
            if (eitherCTRL)  //CTRL - put sprite on ground
            {
                sprite[searchwall].z = spriteongroundz(searchwall);
            }
            else
            {
                k = 0;
                if (highlightcnt >= 0)
                    for (i=0;i<highlightcnt;i++)
                        if (highlight[i] == searchwall+16384)
                        {
                            k = 1;
                            break;
                        }

                if (k == 0)
                {
                    sprite[searchwall].z += updownunits;
                    if (!spnoclip)sprite[searchwall].z = min(sprite[searchwall].z,spriteongroundz(searchwall));
                    sprintf(getmessage,"Sprite %d z = %d",searchwall,sprite[searchwall].z);
                    message(getmessage);

                }
                else
                {
                    for (i=0;i<highlightcnt;i++)
                        if ((highlight[i]&0xc000) == 16384)
                        {
                            sprite[highlight[i]&16383].z += updownunits;
                            if (!spnoclip)sprite[highlight[i]&16383].z = min(sprite[highlight[i]&16383].z,spriteongroundz(highlight[i]&16383));
                        }
                    sprintf(getmessage,"Sprite %d z = %d",highlight[i]&16383,sprite[highlight[i]&16383].z);
                    message(getmessage);

                }
            }
        }
        asksave = 1;
        keystatus[KEYSC_PGDN] = 0;
        mouseb &= ~32;
    }

    /* end Mapster32 */

    //  DoWater(horiz);

    i = totalclock;
    if (i != clockval[clockcnt])
    {
        rate=(120<<4)/(i-clockval[clockcnt])-1;
        if (framerateon)
        {
            int p = 8*4;

            Bsprintf(tempbuf,"%4d",rate);
            if (xdimgame <= 640) p >>= 1;

            begindrawing();
            printext256(xdimgame-p-1,2,0,-1,tempbuf,!(xdimgame > 640));
            printext256(xdimgame-p-2,1,rate < 40?248:whitecol,-1,tempbuf,!(xdimgame > 640));
            enddrawing();
        }
    }
    clockval[clockcnt] = i;
    clockcnt = ((clockcnt+1)&15);

    tempbuf[0] = 0;
    if (bstatus&4 && !(bstatus&(1|2)) && !unrealedlook)  //PK
    {
        Bsprintf(tempbuf,"VIEW");
//        else Bsprintf(tempbuf,"SHADE");
    }
    else if (bstatus&2 && !(bstatus&1))
        Bsprintf(tempbuf,"Z");
    else if (bstatus&1 && !(bstatus&2))
        Bsprintf(tempbuf,"LOCK");

    if (bstatus&1)
    {
        Bsprintf(tempbuf,"LOCK");
        switch (searchstat)
        {
        case 0:
        case 4:
            if (eitherSHIFT) Bsprintf(tempbuf,"PAN");
            if (eitherCTRL)  Bsprintf(tempbuf,"SCALE");
            if (eitherALT)   Bsprintf(tempbuf,"Z");
            break;
        case 1:
        case 2:
            if (eitherSHIFT) Bsprintf(tempbuf,"PAN");
            if (eitherCTRL)  Bsprintf(tempbuf,"SLOPE");
            if (eitherALT)   Bsprintf(tempbuf,"Z");
            break;
        case 3:
            if (eitherSHIFT) Bsprintf(tempbuf,"MOVE XY");
            if (eitherCTRL)  Bsprintf(tempbuf,"SIZE");
            if (eitherALT)   Bsprintf(tempbuf,"MOVE Z");
            break;
        }
    }

    if (tempbuf[0] != 0)
    {
        i = (Bstrlen(tempbuf)<<3)+6;
        if ((searchx+i) < (xdim-1))
            i = 0;
        else i = (searchx+i)-(xdim-1);
        if ((searchy+16) < (ydim-1))
            j = 0;
        else j = (searchy+16)-(ydim-1);
        //            printext16(searchx+6-i,searchy+6-j,11,-1,tempbuf,0);
        printext256(searchx+4+2-i,searchy+4+2-j,0,-1,tempbuf,!(xdimgame > 640));
        printext256(searchx+4-i,searchy+4-j,whitecol,-1,tempbuf,!(xdimgame > 640));

        //        printext256(searchx+4+2,searchy+4+2,0,-1,tempbuf,!(xdimgame > 640));
        //        printext256(searchx+4,searchy+4,whitecol,-1,tempbuf,!(xdimgame > 640));
    }
    if (helpon==1)
    {
        for (i=0;i<MAXHELP3D;i++)
        {
            begindrawing();
            printext256(0*8+2,8+(i*(8+(xdimgame > 640)))+2,0,-1,Help3d[i],!(xdimgame > 640));
            printext256(0*8,8+(i*(8+(xdimgame > 640))),whitecol,-1,Help3d[i],!(xdimgame > 640));
            enddrawing();
            switch (i)
            {
            case 8:
                Bsprintf(tempbuf,"%d",autosave);
                break;
            case 9:
                Bsprintf(tempbuf,"%s",SKILLMODE[skill]);
                break;
            case 10:
                Bsprintf(tempbuf,"%d",framerateon);
                break;
            case 11:
                Bsprintf(tempbuf,"%s",SPRDSPMODE[nosprites]);
                break;
            case 12:
                Bsprintf(tempbuf,"%d",shadepreview);
                break;
            case 13:
                Bsprintf(tempbuf,"%d",purpleon);
                break;
            default :
                sprintf(tempbuf," ");
                break;
            }
            begindrawing();
            if (!strcmp(tempbuf,"0"))
                Bsprintf(tempbuf,"OFF");
            else if (!strcmp(tempbuf,"1"))
                Bsprintf(tempbuf,"ON");
            else if (!strcmp(tempbuf,"2"))
                Bsprintf(tempbuf,"ON (2)");

            printext256((20+((xdimgame > 640) * 20))*8+2,8+(i*(8+(xdimgame > 640)))+2,0,-1,tempbuf,!(xdimgame > 640));
            printext256((20+((xdimgame > 640) * 20))*8,8+(i*(8+(xdimgame > 640))),whitecol,-1,tempbuf,!(xdimgame > 640));
            enddrawing();
        }
    }

    /* if(purpleon) {
                begindrawing();
    //          printext256(1*4,1*8,whitecol,-1,"Purple ON",0);
                    sprintf(getmessage,"Purple ON");
                    message(getmessage);
                enddrawing();
                }
    */

    if (sector[cursectnum].lotag==2)
    {
        if (sector[cursectnum].floorpal==8) SetBOSS1Palette();
        else SetWATERPalette();
    }
    else SetGAMEPalette();


    //Stick this in 3D part of ExtCheckKeys
    //Also choose your own key scan codes



    if (keystatus[KEYSC_QUOTE] && keystatus[KEYSC_D]) // ' d
        /*
            {
                ShowHelpText("SectorEffector");
            } */


    {
        keystatus[KEYSC_D] = 0;
        skill++;
        if (skill>MAXSKILL-1) skill=0;
        message("%s",SKILLMODE[skill]);
        //        printext256(1*4,1*8,11,-1,tempbuf,0);
    }

    /*    if (keystatus[KEYSC_QUOTE] && keystatus[KEYSC_G]) // ' g <Unused>
        {
            keystatus[KEYSC_G] = 0;
            tabgraphic++;
            if (tabgraphic > 2) tabgraphic = 0;
            if (tabgraphic) message("Graphics ON");
            else message("Graphics OFF");
        }*/


    if (keystatus[KEYSC_QUOTE] && keystatus[KEYSC_X]) // ' x
    {
        keystatus[KEYSC_X] = 0;
        shadepreview=!shadepreview;
        if (shadepreview) message("Map shade preview ON");
        else message("Map shade preview OFF");
    }


    /*    if (keystatus[KEYSC_QUOTE] && keystatus[KEYSC_R]) // ' r <Handled already>
        {
            keystatus[KEYSC_R] = 0;
            framerateon=!framerateon;
            if (framerateon) message("Framerate ON");
            else message("Framerate OFF");
        }*/


    if (keystatus[KEYSC_QUOTE] && keystatus[KEYSC_W]) // ' w
    {
        keystatus[KEYSC_W] = 0;
        nosprites++;
        if (nosprites>3) nosprites=0;
        message("%s",SPRDSPMODE[nosprites]);
        //        printext256(1*4,1*8,whitecol,-1,tempbuf,0);
    }

    if (keystatus[KEYSC_QUOTE] && keystatus[KEYSC_Y]) // ' y
    {
        keystatus[KEYSC_Y] = 0;
        purpleon=!purpleon;
        if (nosprites>3) nosprites=0;
        if (purpleon) message("Purple ON");
        else message("Purple OFF");
    }

    if (keystatus[KEYSC_QUOTE] && keystatus[KEYSC_C]) // ' C
    {
        keystatus[KEYSC_C] = 0;
        switch (searchstat)
        {
        case 0:
        case 4:
            for