Subversion Repositories eduke32

Rev

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

//-------------------------------------------------------------------------
/*
Copyright (C) 2005 - EDuke32 team

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 "duke3d.h"
#include "types.h"
#include "develop.h"
#include "scriplib.h"
#include "file_lib.h"
#include "mathutil.h"
#include "gamedefs.h"
#include "keyboard.h"
#include "mouse.h"  // JBF 20030809
#include "function.h"
#include "control.h"
#include "fx_man.h"
#include "sounds.h"
#include "config.h"

#include "osd.h"
#include "osdfuncs.h"
#include "osdcmds.h"
#include "scriptfile.h"

//#include "crc32.h"

#include "util_lib.h"

#define VERSION " 1.4.0 beta 2"

#define HEAD  "EDuke32"VERSION" (shareware mode)"
#define HEAD2 "EDuke32"VERSION

#define IDFSIZE 479985668
#define IDFILENAME "DUKE3D.IDF"

#define TIMERUPDATESIZ 32

long cameradist = 0, cameraclock = 0;
char playerswhenstarted;
char qe,cp,usecwd = 0;

static int32 CommandSetup = 0;
static int32 CommandSoundToggleOff = 0;
static int32 CommandMusicToggleOff = 0;
static char *CommandMap = NULL;
static char *CommandName = NULL,*CommandNet = NULL;
int32 CommandWeaponChoice = 0;

char confilename[BMAX_PATH] = {"EDUKE.CON"}, boardfilename[BMAX_PATH] = {0};
char waterpal[768], slimepal[768], titlepal[768], drealms[768], endingpal[768];
char firstdemofile[80] = { '\0' };
int display_bonus_screen = 1, userconfiles = 0;

static int netparamcount = 0;
static char **netparam = NULL;

int votes[MAXPLAYERS], gotvote[MAXPLAYERS], voting = -1;

void setstatusbarscale(long sc)
{
    ud.statusbarscale = min(100,max(10,sc));
    vscrn();
}

static inline long sbarx(long x)
{
    if (ud.screen_size == 4) return scale(x<<16,ud.statusbarscale,100);
    return (((320l<<16) - scale(320l<<16,ud.statusbarscale,100)) >> 1) + scale(x<<16,ud.statusbarscale,100);
}

static inline long sbary(long y)
{
    return ((200l<<16) - scale(200l<<16,ud.statusbarscale,100) + scale(y<<16,ud.statusbarscale,100));
}

static inline long sbarsc(long sc)
{
    return scale(sc,ud.statusbarscale,100);
}

void patchstatusbar(long x1, long y1, long x2, long y2)
{
    long scl, tx, ty;
    long clx1,cly1,clx2,cly2,clofx,clofy;

    scl = sbarsc(65536);
    tx = sbarx(0); ty = sbary(200-tilesizy[BOTTOMSTATUSBAR]);

    clx1 = scale(scale(x1,xdim,320),ud.statusbarscale,100);
    cly1 = scale(scale(y1,ydim,200),ud.statusbarscale,100);
    clx2 = scale(scale(x2,xdim,320),ud.statusbarscale,100);
    cly2 = scale(scale(y2,ydim,200),ud.statusbarscale,100);
    clofx = (xdim - scale(xdim,ud.statusbarscale,100)) >> 1;
    clofy = (ydim - scale(ydim,ud.statusbarscale,100));

    rotatesprite(tx,ty,scl,0,BOTTOMSTATUSBAR,4,0,10+16+64,clx1+clofx,cly1+clofy,clx2+clofx-1,cly2+clofy-1);
}

int recfilep,totalreccnt;
char debug_on = 0,actor_tog = 0,*rtsptr,memorycheckoveride=0;

extern char syncstate;
extern int32 numlumps;

FILE *frecfilep = (FILE *)NULL;
void pitch_test( void );

char restorepalette,screencapt,nomorelogohack;
int sendmessagecommand = -1;

char *duke3dgrp = "duke3d.grp";  // JBF 20030925
static char *duke3ddef = "duke3d.def";

extern long lastvisinc;

// JBF 20031221: These ought to disappear when things mature
extern int showmultidiags;
extern int netmode, nethostplayers;
extern char netjoinhost[64];
int startupnetworkgame(void);
int processnetworkrequests(void);

void setgamepalette(struct player_struct *player, char *pal, int set)
{
    if (player != &ps[screenpeek]) {
        // another head
        player->palette = pal;
        return;
    }

    if (getrendermode() < 3) {
        // 8-bit mode
        player->palette = pal;
        setbrightness(ud.brightness>>2, pal, set);
        //pub = pus = NUMPAGES;
        return;
    }

    if (pal == palette || pal == waterpal || pal == slimepal) {
        if (player->palette != palette && player->palette != waterpal && player->palette != slimepal)
            setbrightness(ud.brightness>>2, palette, set);
        else setpalettefade(0,0,0,0);
    } else {
        setbrightness(ud.brightness>>2, pal, set);
    }
    player->palette = pal;
}

#define TEXTWRAPLEN (scale(35,ScreenWidth,320))

char colstrip[1024];

char *strip_color_codes(char *t)
{
    int i = 0;

    while(*t)
    {
        if(*t == '^' && isdigit(*(t+1)))
        {
            t += 2;
            if(isdigit(*t))
                t++;
            if(!(*t)) continue;
        }
        colstrip[i] = *t;
        i++,t++;
    }
    colstrip[i] = '\0';
    return(colstrip);
}

int gametext_(int small, int starttile, int x,int y,char *t,char s,char p,short orientation,long x1, long y1, long x2, long y2)
{
    short ac,newx,oldx=x;
    char centre, *oldt;

    centre = ( x == (320>>1) );
    newx = 0;
    oldt = t;

    if(t == NULL)
        return -1;

    if(centre)
    {
        while(*t)
        {
            if(*t == '^' && isdigit(*(t+1)))
            {
                t += 2;
                if(isdigit(*t)) t++;
                if(!(*t)) continue;
            }
        if(*t == 32) {newx+=5;t++;continue;}
            else ac = *t - '!' + starttile;

            if( ac < starttile || ac > (starttile + 93) ) break;

            if(*t >= '0' && *t <= '9')
                newx += 8;
            else newx += tilesizx[ac];
            t++;
        }

        t = oldt;
        x = (320>>1)-(newx>>1);
    }

    while(*t)
    {
        if(*t == '^' && isdigit(*(t+1)))
        {
            char smallbuf[3];
            t += 1;
            if(isdigit(*(t+1)))
            {
                smallbuf[0] = *(t++);
                smallbuf[1] = *(t++);
                smallbuf[2] = '\0';
                p = atol(smallbuf);
            } else {
                smallbuf[0] = *(t++);
                smallbuf[1] = '\0';
                p = atol(smallbuf);
            }
            if(!(*t)) continue;
        }
    if(*t == 32) {x+=5;t++;continue;}
        else ac = *t - '!' + starttile;

        if( ac < starttile || ac > (starttile + 93) )
            break;

        rotatesprite(x<<16,(y<<16)+(small?ScreenHeight<<15:0),65536,0,ac,s,p,small?(8|16):(2|orientation),x1,y1,x2,y2);
        if((*t >= '0' && *t <= '9'))
            x += 8;
        else x += tilesizx[ac];//(tilesizx[ac]>>small);
        if(t-oldt >= (signed)TEXTWRAPLEN-!small) oldt = t, x = oldx, y+=8;
        t++;
    }

    return (x);
}

inline int gametext(int x,int y,char *t,char s,short dabits)
{
    return(gametext_(0,STARTALPHANUM, x,y,t,s,0,dabits,0, 0, xdim-1, ydim-1));
}

inline int gametextpal(int x,int y,char *t,char s,char p)
{
    return(gametext_(0,STARTALPHANUM, x,y,t,s,p,26,0, 0, xdim-1, ydim-1));
}

inline int mpgametext(int y,char *t,char s,short dabits)
{
    if(xdim >= 640 && ydim >= 480)
        return(gametext_(1,STARTALPHANUM, 5,y,t,s,0,dabits,0, 0, xdim-1, ydim-1));
    else return(gametext_(0,STARTALPHANUM, 5,y,t,s,0,dabits,0, 0, xdim-1, ydim-1));
}

int minitext_(int x,int y,char *t,char s,char p,short sb)
{
    short ac;
    char ch,cmode;

    cmode = (sb&256)!=0;
    sb &= 255;

    while(*t)
    {
        ch = Btoupper(*t);
        if(ch == 32) {x+=5;t++;continue;}
        else ac = ch - '!' + MINIFONT;

        if (cmode) rotatesprite(sbarx(x),sbary(y),sbarsc(65536L),0,ac,s,p,sb,0,0,xdim-1,ydim-1);
        else rotatesprite(x<<16,y<<16,65536L,0,ac,s,p,sb,0,0,xdim-1,ydim-1);
        x += 4; // tilesizx[ac]+1;

        t++;
    }
    return (x);
}

inline int minitextshade(int x,int y,char *t,char s,char p,short sb)
{
    return (minitext_(x,y,(char *)strip_color_codes(t),s,p,sb));
}

inline int minitext(int x,int y,char *t,char p,short sb)
{
    return (minitext_(x,y,(char *)strip_color_codes(t),0,p,sb));
}

void gamenumber(long x,long y,long n,char s)
{
    char b[10];
    //ltoa(n,b,10);
    Bsnprintf(b,10,"%ld",n);
    gametext(x,y,b,s,2+8+16);
}

char recbuf[180];
void allowtimetocorrecterrorswhenquitting(void)
{
    long i, j, oldtotalclock;

    ready2send = 0;

    for(j=0;j<8;j++)
    {
        oldtotalclock = totalclock;

        while (totalclock < oldtotalclock+TICSPERFRAME) {
            handleevents();
            getpackets();
        }
        if(KB_KeyPressed(sc_Escape)) return;

        packbuf[0] = 127;
        for(i=connecthead;i>=0;i=connectpoint2[i])
        {
            if (i != myconnectindex) sendpacket(i,packbuf,1);
            if ((!networkmode) && (myconnectindex != connecthead)) break; //slaves in M/S mode only send to master
        }
    }
}

#define MAXUSERQUOTES 4
long quotebot, quotebotgoal;
short user_quote_time[MAXUSERQUOTES];
char user_quote[MAXUSERQUOTES][178];
// char typebuflen,typebuf[41];

void adduserquote(char *daquote)
{
    long i;

    for(i=MAXUSERQUOTES-1;i>0;i--)
    {
        strcpy(user_quote[i],user_quote[i-1]);
        user_quote_time[i] = user_quote_time[i-1];
    }
    strcpy(user_quote[0],daquote);
    OSD_Printf("%s\n",strip_color_codes(daquote));

    user_quote_time[0] = ud.msgdisptime;
    pub = NUMPAGES;
}

void getpackets(void)
{
    long i, j, k, l;
    long other, packbufleng;
    input *osyn, *nsyn;

    sampletimer();
    AudioUpdate();

    if(ALT_IS_PRESSED && KB_KeyPressed(sc_Enter))
    {
        if(setgamemode(!ScreenMode,ScreenWidth,ScreenHeight,ScreenBPP)) {
            OSD_Printf("Failed setting fullscreen video mode.\n");
            if (setgamemode(ScreenMode, ScreenWidth, ScreenHeight, ScreenBPP))
                gameexit("Failed to recover from failure to set fullscreen video mode.\n");
        }
        else ScreenMode = !ScreenMode;
        KB_ClearKeyDown(sc_Enter);
        restorepalette = 1;
        vscrn();
    }

    // only dispatch commands here when not in a game
if( !(ps[myconnectindex].gm&MODE_GAME) ) { OSD_DispatchQueued(); }

    if(qe == 0 && KB_KeyPressed(sc_LeftControl) && KB_KeyPressed(sc_LeftAlt) && KB_KeyPressed(sc_Delete))
    {
        qe = 1;
        gameexit("Quick Exit.");
    }

    if (numplayers < 2) return;
    while ((packbufleng = getpacket(&other,packbuf)) > 0)
    {
        switch(packbuf[0])
        {
        case 254:
            //slaves in M/S mode only send to master
            if (myconnectindex == connecthead)
            {
                //Master re-transmits message to all others
                for(i=connectpoint2[connecthead];i>=0;i=connectpoint2[i])
                    if (i != other)
                        sendpacket(i,packbuf,packbufleng);
            }
            /*
                        j = packbuf[1];
                        playerquitflag[j] = 0;

                        j = -1;
                        for(i=connecthead;i>=0;i=connectpoint2[i])
                        {
                            if (playerquitflag[i]) { j = i; continue; }

                            if (i == connecthead) connecthead = connectpoint2[connecthead];
                            else connectpoint2[j] = connectpoint2[i];

                            numplayers--;
                            ud.multimode--;

                            Bsprintf(buf,"%s is history!",ud.user_name[i]);
                            adduserquote(buf);

                            if (numplayers < 2)
                                sound(GENERIC_AMBIENCE17);

                            if(i == 0 && networkmode == 0 ) */

            gameexit( "Game aborted from menu; disconnected.");
            //            }

            break;

        case 9:
            //slaves in M/S mode only send to master
            if (myconnectindex == connecthead)
            {
                //Master re-transmits message to all others
                for(i=connectpoint2[connecthead];i>=0;i=connectpoint2[i])
                    if (i != other)
                        sendpacket(i,packbuf,packbufleng);
            }

            strcpy(boardfilename,packbuf+1);
            boardfilename[packbufleng-1] = 0;
            if(boardfilename[0] != 0)
            {
                if ((i = kopen4load(boardfilename,0)) < 0) {
                    Bmemset(boardfilename,0,sizeof(boardfilename));
                    sendboardname();
                } else kclose(i);
            }

            if(ud.m_level_number == 7 && ud.m_volume_number == 0 && boardfilename[0] == 0)
                ud.m_level_number = 0;

            break;

        case 18: // map vote

            if (myconnectindex == connecthead)
            {
                //Master re-transmits message to all others
                for(i=connectpoint2[connecthead];i>=0;i=connectpoint2[i])
                    if (i != other)
                        sendpacket(i,packbuf,packbufleng);
            }

            switch(packbuf[1])
            {
            case 0:
                if(voting == myconnectindex && gotvote[packbuf[2]] == 0)
                {
                    gotvote[packbuf[2]] = 1;
                    votes[packbuf[2]] = packbuf[3];
                    Bsprintf(tempbuf,"GOT VOTE FROM %s",ud.user_name[packbuf[2]]);
                    adduserquote(tempbuf);
                }
                break;

            case 1: // call map vote
                voting = packbuf[2];
                Bsprintf(tempbuf,"%s HAS CALLED A VOTE TO CHANGE MAP TO %s (E%dL%d)",ud.user_name[packbuf[2]],level_names[packbuf[3]*11 + packbuf[4]],packbuf[3]+1,packbuf[4]+1);
                adduserquote(tempbuf);
                Bsprintf(tempbuf,"PRESS F1 TO VOTE YES, F2 TO VOTE NO");
                adduserquote(tempbuf);
                Bmemset(votes,0,sizeof(votes));
                Bmemset(gotvote,0,sizeof(gotvote));
                gotvote[voting] = votes[voting] = 1;
                break;

            case 2: // cancel map vote
                if(voting == packbuf[2])
                {
                    voting = -1;
                    i = 0;
                    for(j=0;j<MAXPLAYERS;j++)
                        i += gotvote[j];

                    if(i != numplayers)
                        Bsprintf(tempbuf,"%s HAS CANCELED THE VOTE",ud.user_name[packbuf[2]]);
                    else Bsprintf(tempbuf,"VOTE FAILED");
                    Bmemset(votes,0,sizeof(votes));
                    Bmemset(gotvote,0,sizeof(gotvote));
                    adduserquote(tempbuf);
                }
                break;
            }
            break;

        case 126:
            //Slaves in M/S mode only send to master
            //Master re-transmits message to all others
            if ((!networkmode) && (myconnectindex == connecthead))
                for(i=connectpoint2[connecthead];i>=0;i=connectpoint2[i])
                    if (i != other) sendpacket(i,packbuf,packbufleng);

            multiflag = 2;
            multiwhat = 0;
            multiwho = packbuf[2]; //other: need to send in m/s mode because of possible re-transmit
            multipos = packbuf[1];
            loadplayer( multipos );
            multiflag = 0;
            break;
        case 0:  //[0] (receive master sync buffer)
            j = 1;

            if ((movefifoend[other]&(TIMERUPDATESIZ-1)) == 0)
                for(i=connectpoint2[connecthead];i>=0;i=connectpoint2[i])
                {
                    if (playerquitflag[i] == 0) continue;
                    if (i == myconnectindex)
                        otherminlag = (long)((signed char)packbuf[j]);
                    j++;
                }

            osyn = (input *)&inputfifo[(movefifoend[connecthead]-1)&(MOVEFIFOSIZ-1)][0];
            nsyn = (input *)&inputfifo[(movefifoend[connecthead])&(MOVEFIFOSIZ-1)][0];

            k = j;
            for(i=connecthead;i>=0;i=connectpoint2[i])
                j += playerquitflag[i]+playerquitflag[i];
            for(i=connecthead;i>=0;i=connectpoint2[i])
            {
                if (playerquitflag[i] == 0) continue;

                l = packbuf[k++];
                l += (long)(packbuf[k++]<<8);

                if (i == myconnectindex)
                { j += ((l&1)<<1)+(l&2)+((l&4)>>2)+((l&8)>>3)+((l&16)>>4)+((l&32)>>5)+((l&64)>>6)+((l&128)>>7)+((l&256)>>8)+((l&512)>>9)+((l&1024)>>10)+((l&2048)>>11); continue; }

                copybufbyte(&osyn[i],&nsyn[i],sizeof(input));
                if (l&1)   nsyn[i].fvel = packbuf[j]+((short)packbuf[j+1]<<8), j += 2;
                if (l&2)   nsyn[i].svel = packbuf[j]+((short)packbuf[j+1]<<8), j += 2;
                if (l&4)   nsyn[i].avel = (signed char)packbuf[j++];
                if (l&8)   nsyn[i].bits = ((nsyn[i].bits&0xffffff00)|((long)packbuf[j++]));
                if (l&16)  nsyn[i].bits = ((nsyn[i].bits&0xffff00ff)|((long)packbuf[j++])<<8);
                if (l&32)  nsyn[i].bits = ((nsyn[i].bits&0xff00ffff)|((long)packbuf[j++])<<16);
                if (l&64)  nsyn[i].bits = ((nsyn[i].bits&0x00ffffff)|((long)packbuf[j++])<<24);
                if (l&128) nsyn[i].horz = (signed char)packbuf[j++];
                if (l&256)  nsyn[i].bits2 = ((nsyn[i].bits2&0xffffff00)|((long)packbuf[j++]));
                if (l&512)  nsyn[i].bits2 = ((nsyn[i].bits2&0xffff00ff)|((long)packbuf[j++])<<8);
                if (l&1024) nsyn[i].bits2 = ((nsyn[i].bits2&0xff00ffff)|((long)packbuf[j++])<<16);
                if (l&2048) nsyn[i].bits2 = ((nsyn[i].bits2&0x00ffffff)|((long)packbuf[j++])<<24);

                if (nsyn[i].bits&(1<<26)) playerquitflag[i] = 0;
                movefifoend[i]++;
            }

            while (j != packbufleng)
            {
                for(i=connecthead;i>=0;i=connectpoint2[i])
                    if(i != myconnectindex)
                    {
                        syncval[i][syncvalhead[i]&(MOVEFIFOSIZ-1)] = packbuf[j];
                        syncvalhead[i]++;
                    }
                j++;
            }

            for(i=connecthead;i>=0;i=connectpoint2[i])
                if (i != myconnectindex)
                    for(j=1;j<movesperpacket;j++)
                    {
                        copybufbyte(&nsyn[i],&inputfifo[movefifoend[i]&(MOVEFIFOSIZ-1)][i],sizeof(input));
                        movefifoend[i]++;
                    }

            movefifosendplc += movesperpacket;

            break;
        case 1:  //[1] (receive slave sync buffer)
            j = 3; k = packbuf[1] + (long)(packbuf[2]<<8);

            osyn = (input *)&inputfifo[(movefifoend[other]-1)&(MOVEFIFOSIZ-1)][0];
            nsyn = (input *)&inputfifo[(movefifoend[other])&(MOVEFIFOSIZ-1)][0];

            copybufbyte(&osyn[other],&nsyn[other],sizeof(input));
            if (k&1)   nsyn[other].fvel = packbuf[j]+((short)packbuf[j+1]<<8), j += 2;
            if (k&2)   nsyn[other].svel = packbuf[j]+((short)packbuf[j+1]<<8), j += 2;
            if (k&4)   nsyn[other].avel = (signed char)packbuf[j++];
            if (k&8)   nsyn[other].bits = ((nsyn[other].bits&0xffffff00)|((long)packbuf[j++]));
            if (k&16)  nsyn[other].bits = ((nsyn[other].bits&0xffff00ff)|((long)packbuf[j++])<<8);
            if (k&32)  nsyn[other].bits = ((nsyn[other].bits&0xff00ffff)|((long)packbuf[j++])<<16);
            if (k&64)  nsyn[other].bits = ((nsyn[other].bits&0x00ffffff)|((long)packbuf[j++])<<24);
            if (k&128) nsyn[other].horz = (signed char)packbuf[j++];
            if (k&256)  nsyn[other].bits2 = ((nsyn[other].bits2&0xffffff00)|((long)packbuf[j++]));
            if (k&512)  nsyn[other].bits2 = ((nsyn[other].bits2&0xffff00ff)|((long)packbuf[j++])<<8);
            if (k&1024) nsyn[other].bits2 = ((nsyn[other].bits2&0xff00ffff)|((long)packbuf[j++])<<16);
            if (k&2048) nsyn[other].bits2 = ((nsyn[other].bits2&0x00ffffff)|((long)packbuf[j++])<<24);
            movefifoend[other]++;

            while (j != packbufleng)
            {
                syncval[other][syncvalhead[other]&(MOVEFIFOSIZ-1)] = packbuf[j++];
                syncvalhead[other]++;
            }

            for(i=1;i<movesperpacket;i++)
            {
                copybufbyte(&nsyn[other],&inputfifo[movefifoend[other]&(MOVEFIFOSIZ-1)][other],sizeof(input));
                movefifoend[other]++;
            }

            break;

        case 4:
            //slaves in M/S mode only send to master
            if ((!networkmode) && (myconnectindex == connecthead))
            {
                if (packbuf[1] == 255)
                {
                    //Master re-transmits message to all others
                    for(i=connectpoint2[connecthead];i>=0;i=connectpoint2[i])
                        if (i != other)
                            sendpacket(i,packbuf,packbufleng);
                }
                else if (((long)packbuf[1]) != myconnectindex)
                {
                    //Master re-transmits message not intended for master
                    sendpacket((long)packbuf[1],packbuf,packbufleng);
                    break;
                }
            }

            strcpy(recbuf,packbuf+2);
            recbuf[packbufleng-2] = 0;

            adduserquote(recbuf);
            sound(EXITMENUSOUND);

            pus = NUMPAGES;
            pub = NUMPAGES;

            break;

        case 5:
            //Slaves in M/S mode only send to master
            //Master re-transmits message to all others
            if ((!networkmode) && (myconnectindex == connecthead))
                for(i=connectpoint2[connecthead];i>=0;i=connectpoint2[i])
                    if (i != other) sendpacket(i,packbuf,packbufleng);

            if(voting != -1)
                adduserquote("VOTE SUCCEEDED");

            ud.m_level_number = ud.level_number = packbuf[1];
            ud.m_volume_number = ud.volume_number = packbuf[2];
            ud.m_player_skill = ud.player_skill = packbuf[3];
            ud.m_monsters_off = ud.monsters_off = packbuf[4];
            ud.m_respawn_monsters = ud.respawn_monsters = packbuf[5];
            ud.m_respawn_items = ud.respawn_items = packbuf[6];
            ud.m_respawn_inventory = ud.respawn_inventory = packbuf[7];
            ud.m_coop = packbuf[8];
            ud.m_marker = ud.marker = packbuf[9];
            ud.m_ffire = ud.ffire = packbuf[10];
            ud.m_noexits = ud.noexits = packbuf[11];

            for(i=connecthead;i>=0;i=connectpoint2[i])
            {
                resetweapons(i);
                resetinventory(i);
            }

            newgame(ud.volume_number,ud.level_number,ud.player_skill);
            ud.coop = ud.m_coop;

            if (enterlevel(MODE_GAME)) backtomenu();

            break;
        case 6:
            //slaves in M/S mode only send to master
            //Master re-transmits message to all others
            if ((!networkmode) && (myconnectindex == connecthead))
                for(i=connectpoint2[connecthead];i>=0;i=connectpoint2[i])
                    if (i != other) sendpacket(i,packbuf,packbufleng);

            if (packbuf[2] != BYTEVERSION)
                gameexit("\nYou cannot play Duke with different versions.");

            other = packbuf[1];

            for (i=3;packbuf[i];i++)
                ud.user_name[other][i-3] = packbuf[i];
            ud.user_name[other][i-3] = 0;
            i++;

            j = i; //This used to be Duke packet #9... now concatenated with Duke packet #6
            for (;i-j<10;i++) ud.wchoice[other][i-j] = packbuf[i];

            ps[other].aim_mode = packbuf[i++];
            ps[other].auto_aim = packbuf[i++];
            ps[other].weaponswitch = packbuf[i++];
            ps[other].palookup = ud.pcolor[other] = packbuf[i++];
            if(sprite[ps[other].i].picnum == APLAYER)
                sprite[ps[other].i].pal = ud.pcolor[other];
            break;
        case 7:
            //slaves in M/S mode only send to master
            //Master re-transmits message to all others
            if ((!networkmode) && (myconnectindex == connecthead))
                for(i=connectpoint2[connecthead];i>=0;i=connectpoint2[i])
                    if (i != other) sendpacket(i,packbuf,packbufleng);

            if(numlumps == 0) break;

            if (SoundToggle == 0 || ud.lockout == 1 || FXDevice < 0 )
                break;
            rtsptr = (char *)RTS_GetSound(packbuf[1]-1);
            if (*rtsptr == 'C')
                FX_PlayVOC3D(rtsptr,0,0,0,255,-packbuf[1]);
            else
                FX_PlayWAV3D(rtsptr,0,0,0,255,-packbuf[1]);
            rtsplaying = 7;
            break;

        case 16:
            movefifoend[other] = movefifoplc = movefifosendplc = fakemovefifoplc = 0;
            syncvalhead[other] = syncvaltottail = 0L;
        case 17:
            j = 1;

            if ((movefifoend[other]&(TIMERUPDATESIZ-1)) == 0)
                if (other == connecthead)
                    for(i=connectpoint2[connecthead];i>=0;i=connectpoint2[i])
                    {
                        if (i == myconnectindex)
                            otherminlag = (long)((signed char)packbuf[j]);
                        j++;
                    }

            osyn = (input *)&inputfifo[(movefifoend[other]-1)&(MOVEFIFOSIZ-1)][0];
            nsyn = (input *)&inputfifo[(movefifoend[other])&(MOVEFIFOSIZ-1)][0];

            copybufbyte(&osyn[other],&nsyn[other],sizeof(input));
            k = packbuf[j++];
            k += (long)(packbuf[j++]<<8);

            if (k&1)   nsyn[other].fvel = packbuf[j]+((short)packbuf[j+1]<<8), j += 2;
            if (k&2)   nsyn[other].svel = packbuf[j]+((short)packbuf[j+1]<<8), j += 2;
            if (k&4)   nsyn[other].avel = (signed char)packbuf[j++];
            if (k&8)   nsyn[other].bits = ((nsyn[other].bits&0xffffff00)|((long)packbuf[j++]));
            if (k&16)  nsyn[other].bits = ((nsyn[other].bits&0xffff00ff)|((long)packbuf[j++])<<8);
            if (k&32)  nsyn[other].bits = ((nsyn[other].bits&0xff00ffff)|((long)packbuf[j++])<<16);
            if (k&64)  nsyn[other].bits = ((nsyn[other].bits&0x00ffffff)|((long)packbuf[j++])<<24);
            if (k&128) nsyn[other].horz = (signed char)packbuf[j++];
            if (k&256)  nsyn[other].bits2 = ((nsyn[other].bits2&0xffffff00)|((long)packbuf[j++]));
            if (k&512)  nsyn[other].bits2 = ((nsyn[other].bits2&0xffff00ff)|((long)packbuf[j++])<<8);
            if (k&1024) nsyn[other].bits2 = ((nsyn[other].bits2&0xff00ffff)|((long)packbuf[j++])<<16);
            if (k&2048) nsyn[other].bits2 = ((nsyn[other].bits2&0x00ffffff)|((long)packbuf[j++])<<24);
            movefifoend[other]++;

            for(i=1;i<movesperpacket;i++)
            {
                copybufbyte(&nsyn[other],&inputfifo[movefifoend[other]&(MOVEFIFOSIZ-1)][other],sizeof(input));
                movefifoend[other]++;
            }

            if (j > packbufleng)
                initprintf("INVALID GAME PACKET!!! (%ld too many bytes)\n",j-packbufleng);

            while (j != packbufleng)
            {
                syncval[other][syncvalhead[other]&(MOVEFIFOSIZ-1)] = packbuf[j++];
                syncvalhead[other]++;
            }

            break;
        case 127:
            break;

        case 250:
            playerreadyflag[other]++;
            break;
        case 255:
            gameexit(" ");
            break;
        }
    }
}

extern void computergetinput(long snum, input *syn);

void faketimerhandler()
{
    long i, j, k;
    //    short who;
    input *osyn, *nsyn;

    if(qe == 0 && KB_KeyPressed(sc_LeftControl) && KB_KeyPressed(sc_LeftAlt) && KB_KeyPressed(sc_Delete))
    {
        qe = 1;
        gameexit("Quick Exit.");
    }

    sampletimer();
    AudioUpdate();
    if ((totalclock < ototalclock+TICSPERFRAME) || (ready2send == 0)) return;
    ototalclock += TICSPERFRAME;

    getpackets(); if (getoutputcirclesize() >= 16) return;

    for(i=connecthead;i>=0;i=connectpoint2[i])
        if (i != myconnectindex)
            if (movefifoend[i] < movefifoend[myconnectindex]-200) return;

    getinput(myconnectindex);

    avgfvel += loc.fvel;
    avgsvel += loc.svel;
    avgavel += loc.avel;
    avghorz += loc.horz;
    avgbits |= loc.bits;
    avgbits2 |= loc.bits2;
    if (movefifoend[myconnectindex]&(movesperpacket-1))
    {
        copybufbyte(&inputfifo[(movefifoend[myconnectindex]-1)&(MOVEFIFOSIZ-1)][myconnectindex],
                    &inputfifo[movefifoend[myconnectindex]&(MOVEFIFOSIZ-1)][myconnectindex],sizeof(input));
        movefifoend[myconnectindex]++;
        return;
    }
    nsyn = &inputfifo[movefifoend[myconnectindex]&(MOVEFIFOSIZ-1)][myconnectindex];
    nsyn[0].fvel = avgfvel/movesperpacket;
    nsyn[0].svel = avgsvel/movesperpacket;
    nsyn[0].avel = avgavel/movesperpacket;
    nsyn[0].horz = avghorz/movesperpacket;
    nsyn[0].bits = avgbits;
    nsyn[0].bits2 = avgbits2;
    avgfvel = avgsvel = avgavel = avghorz = avgbits = avgbits2 = 0;
    movefifoend[myconnectindex]++;

    if (numplayers < 2)
    {
        if (ud.multimode > 1) for(i=connecthead;i>=0;i=connectpoint2[i])
                if(i != myconnectindex)
                {
                    //clearbufbyte(&inputfifo[movefifoend[i]&(MOVEFIFOSIZ-1)][i],sizeof(input),0L);
                    if(ud.playerai)
                        computergetinput(i,&inputfifo[movefifoend[i]&(MOVEFIFOSIZ-1)][i]);
                    inputfifo[movefifoend[i]&(MOVEFIFOSIZ-1)][i].svel++;
                    inputfifo[movefifoend[i]&(MOVEFIFOSIZ-1)][i].fvel++;
                    movefifoend[i]++;
                }
        return;
    }

    for(i=connecthead;i>=0;i=connectpoint2[i])
        if (i != myconnectindex)
        {
            k = (movefifoend[myconnectindex]-1)-movefifoend[i];
            myminlag[i] = min(myminlag[i],k);
            mymaxlag = max(mymaxlag,k);
        }

    if (((movefifoend[myconnectindex]-1)&(TIMERUPDATESIZ-1)) == 0)
    {
        i = mymaxlag-bufferjitter; mymaxlag = 0;
        if (i > 0) bufferjitter += ((3+i)>>2);
        else if (i < 0) bufferjitter -= ((1-i)>>2);
    }

    if (networkmode == 1)
    {
        packbuf[0] = 17;
        if ((movefifoend[myconnectindex]-1) == 0) packbuf[0] = 16;
        j = 1;

        //Fix timers and buffer/jitter value
        if (((movefifoend[myconnectindex]-1)&(TIMERUPDATESIZ-1)) == 0)
        {
            if (myconnectindex != connecthead)
            {
                i = myminlag[connecthead]-otherminlag;
                if (klabs(i) > 8) i >>= 1;
                else if (klabs(i) > 2) i = ksgn(i);
                else i = 0;

                totalclock -= TICSPERFRAME*i;
                myminlag[connecthead] -= i; otherminlag += i;
            }

            if (myconnectindex == connecthead)
                for(i=connectpoint2[connecthead];i>=0;i=connectpoint2[i])
                    packbuf[j++] = min(max(myminlag[i],-128),127);

            for(i=connecthead;i>=0;i=connectpoint2[i])
                myminlag[i] = 0x7fffffff;
        }

        osyn = (input *)&inputfifo[(movefifoend[myconnectindex]-2)&(MOVEFIFOSIZ-1)][myconnectindex];
        nsyn = (input *)&inputfifo[(movefifoend[myconnectindex]-1)&(MOVEFIFOSIZ-1)][myconnectindex];

        k = j;
        packbuf[j++] = 0;
        packbuf[j++] = 0;

        if (nsyn[0].fvel != osyn[0].fvel)
        {
            packbuf[j++] = (char)nsyn[0].fvel;
            packbuf[j++] = (char)(nsyn[0].fvel>>8);
            packbuf[k] |= 1;
        }
        if (nsyn[0].svel != osyn[0].svel)
        {
            packbuf[j++] = (char)nsyn[0].svel;
            packbuf[j++] = (char)(nsyn[0].svel>>8);
            packbuf[k] |= 2;
        }
        if (nsyn[0].avel != osyn[0].avel)
        {
            packbuf[j++] = (signed char)nsyn[0].avel;
            packbuf[k] |= 4;
        }
        if ((nsyn[0].bits^osyn[0].bits)&0x000000ff) packbuf[j++] = (nsyn[0].bits&255), packbuf[k] |= 8;
        if ((nsyn[0].bits^osyn[0].bits)&0x0000ff00) packbuf[j++] = ((nsyn[0].bits>>8)&255), packbuf[k] |= 16;
        if ((nsyn[0].bits^osyn[0].bits)&0x00ff0000) packbuf[j++] = ((nsyn[0].bits>>16)&255), packbuf[k] |= 32;
        if ((nsyn[0].bits^osyn[0].bits)&0xff000000) packbuf[j++] = ((nsyn[0].bits>>24)&255), packbuf[k] |= 64;
        if (nsyn[0].horz != osyn[0].horz)
        {
            packbuf[j++] = (char)nsyn[0].horz;
            packbuf[k] |= 128;
        }
        k++;
        packbuf[k] = 0;
        if ((nsyn[0].bits2^osyn[0].bits2)&0x000000ff) packbuf[j++] = (nsyn[0].bits2&255), packbuf[k] |= 1;
        if ((nsyn[0].bits2^osyn[0].bits2)&0x0000ff00) packbuf[j++] = ((nsyn[0].bits2>>8)&255), packbuf[k] |= 2;
        if ((nsyn[0].bits2^osyn[0].bits2)&0x00ff0000) packbuf[j++] = ((nsyn[0].bits2>>16)&255), packbuf[k] |= 4;
        if ((nsyn[0].bits2^osyn[0].bits2)&0xff000000) packbuf[j++] = ((nsyn[0].bits2>>24)&255), packbuf[k] |= 8;

        while (syncvalhead[myconnectindex] != syncvaltail)
        {
            packbuf[j++] = syncval[myconnectindex][syncvaltail&(MOVEFIFOSIZ-1)];
            syncvaltail++;
        }

        for(i=connecthead;i>=0;i=connectpoint2[i])
            if (i != myconnectindex)
                sendpacket(i,packbuf,j);

        return;
    }
    if (myconnectindex != connecthead)   //Slave
    {
        //Fix timers and buffer/jitter value
        if (((movefifoend[myconnectindex]-1)&(TIMERUPDATESIZ-1)) == 0)
        {
            i = myminlag[connecthead]-otherminlag;
            if (klabs(i) > 8) i >>= 1;
            else if (klabs(i) > 2) i = ksgn(i);
            else i = 0;

            totalclock -= TICSPERFRAME*i;
            myminlag[connecthead] -= i; otherminlag += i;

            for(i=connecthead;i>=0;i=connectpoint2[i])
                myminlag[i] = 0x7fffffff;
        }

        packbuf[0] = 1; packbuf[1] = 0; packbuf[2] = 0; j = 3;

        osyn = (input *)&inputfifo[(movefifoend[myconnectindex]-2)&(MOVEFIFOSIZ-1)][myconnectindex];
        nsyn = (input *)&inputfifo[(movefifoend[myconnectindex]-1)&(MOVEFIFOSIZ-1)][myconnectindex];

        if (nsyn[0].fvel != osyn[0].fvel)
        {
            packbuf[j++] = (char)nsyn[0].fvel;
            packbuf[j++] = (char)(nsyn[0].fvel>>8);
            packbuf[1] |= 1;
        }
        if (nsyn[0].svel != osyn[0].svel)
        {
            packbuf[j++] = (char)nsyn[0].svel;
            packbuf[j++] = (char)(nsyn[0].svel>>8);
            packbuf[1] |= 2;
        }
        if (nsyn[0].avel != osyn[0].avel)
        {
            packbuf[j++] = (signed char)nsyn[0].avel;
            packbuf[1] |= 4;
        }
        if ((nsyn[0].bits^osyn[0].bits)&0x000000ff) packbuf[j++] = (nsyn[0].bits&255), packbuf[1] |= 8;
        if ((nsyn[0].bits^osyn[0].bits)&0x0000ff00) packbuf[j++] = ((nsyn[0].bits>>8)&255), packbuf[1] |= 16;
        if ((nsyn[0].bits^osyn[0].bits)&0x00ff0000) packbuf[j++] = ((nsyn[0].bits>>16)&255), packbuf[1] |= 32;
        if ((nsyn[0].bits^osyn[0].bits)&0xff000000) packbuf[j++] = ((nsyn[0].bits>>24)&255), packbuf[1] |= 64;
        if (nsyn[0].horz != osyn[0].horz)
        {
            packbuf[j++] = (char)nsyn[0].horz;
            packbuf[1] |= 128;
        }
        packbuf[2] = 0;
        if ((nsyn[0].bits2^osyn[0].bits2)&0x000000ff) packbuf[j++] = (nsyn[0].bits2&255), packbuf[2] |= 1;
        if ((nsyn[0].bits2^osyn[0].bits2)&0x0000ff00) packbuf[j++] = ((nsyn[0].bits2>>8)&255), packbuf[2] |= 2;
        if ((nsyn[0].bits2^osyn[0].bits2)&0x00ff0000) packbuf[j++] = ((nsyn[0].bits2>>16)&255), packbuf[2] |= 4;
        if ((nsyn[0].bits2^osyn[0].bits2)&0xff000000) packbuf[j++] = ((nsyn[0].bits2>>24)&255), packbuf[2] |= 8;

        while (syncvalhead[myconnectindex] != syncvaltail)
        {
            packbuf[j++] = syncval[myconnectindex][syncvaltail&(MOVEFIFOSIZ-1)];
            syncvaltail++;
        }

        sendpacket(connecthead,packbuf,j);
        return;
    }

    //This allows allow packet resends
    for(i=connecthead;i>=0;i=connectpoint2[i])
        if (movefifoend[i] <= movefifosendplc)
        {
            packbuf[0] = 127;
            for(i=connectpoint2[connecthead];i>=0;i=connectpoint2[i])
                sendpacket(i,packbuf,1);
            return;
        }

    while (1)  //Master
    {
        for(i=connecthead;i>=0;i=connectpoint2[i])
            if (playerquitflag[i] && (movefifoend[i] <= movefifosendplc)) return;

        osyn = (input *)&inputfifo[(movefifosendplc-1)&(MOVEFIFOSIZ-1)][0];
        nsyn = (input *)&inputfifo[(movefifosendplc  )&(MOVEFIFOSIZ-1)][0];

        //MASTER -> SLAVE packet
        packbuf[0] = 0; j = 1;

        //Fix timers and buffer/jitter value
        if ((movefifosendplc&(TIMERUPDATESIZ-1)) == 0)
        {
            for(i=connectpoint2[connecthead];i>=0;i=connectpoint2[i])
                if (playerquitflag[i])
                    packbuf[j++] = min(max(myminlag[i],-128),127);

            for(i=connecthead;i>=0;i=connectpoint2[i])
                myminlag[i] = 0x7fffffff;
        }

        k = j;
        for(i=connecthead;i>=0;i=connectpoint2[i])
            j += playerquitflag[i] + playerquitflag[i];
        for(i=connecthead;i>=0;i=connectpoint2[i])
        {
            if (playerquitflag[i] == 0) continue;

            packbuf[k] = 0;
            if (nsyn[i].fvel != osyn[i].fvel)
            {
                packbuf[j++] = (char)nsyn[i].fvel;
                packbuf[j++] = (char)(nsyn[i].fvel>>8);
                packbuf[k] |= 1;
            }
            if (nsyn[i].svel != osyn[i].svel)
            {
                packbuf[j++] = (char)nsyn[i].svel;
                packbuf[j++] = (char)(nsyn[i].svel>>8);
                packbuf[k] |= 2;
            }
            if (nsyn[i].avel != osyn[i].avel)
            {
                packbuf[j++] = (signed char)nsyn[i].avel;
                packbuf[k] |= 4;
            }
            if ((nsyn[i].bits^osyn[i].bits)&0x000000ff) packbuf[j++] = (nsyn[i].bits&255), packbuf[k] |= 8;
            if ((nsyn[i].bits^osyn[i].bits)&0x0000ff00) packbuf[j++] = ((nsyn[i].bits>>8)&255), packbuf[k] |= 16;
            if ((nsyn[i].bits^osyn[i].bits)&0x00ff0000) packbuf[j++] = ((nsyn[i].bits>>16)&255), packbuf[k] |= 32;
            if ((nsyn[i].bits^osyn[i].bits)&0xff000000) packbuf[j++] = ((nsyn[i].bits>>24)&255), packbuf[k] |= 64;
            if (nsyn[i].horz != osyn[i].horz)
            {
                packbuf[j++] = (char)nsyn[i].horz;
                packbuf[k] |= 128;
            }
            k++;
            packbuf[k] = 0;
            if ((nsyn[i].bits2^osyn[i].bits2)&0x000000ff) packbuf[j++] = (nsyn[i].bits2&255), packbuf[k] |= 1;
            if ((nsyn[i].bits2^osyn[i].bits2)&0x0000ff00) packbuf[j++] = ((nsyn[i].bits2>>8)&255), packbuf[k] |= 2;
            if ((nsyn[i].bits2^osyn[i].bits2)&0x00ff0000) packbuf[j++] = ((nsyn[i].bits2>>16)&255), packbuf[k] |= 4;
            if ((nsyn[i].bits2^osyn[i].bits2)&0xff000000) packbuf[j++] = ((nsyn[i].bits2>>24)&255), packbuf[k] |= 8;
            k++;
        }

        while (syncvalhead[myconnectindex] != syncvaltail)
        {
            packbuf[j++] = syncval[myconnectindex][syncvaltail&(MOVEFIFOSIZ-1)];
            syncvaltail++;
        }

        for(i=connectpoint2[connecthead];i>=0;i=connectpoint2[i])
            if (playerquitflag[i])
            {
                sendpacket(i,packbuf,j);
                if (nsyn[i].bits&(1<<26))
                    playerquitflag[i] = 0;
            }

        movefifosendplc += movesperpacket;
    }
}

extern long cacnum;
typedef struct { long *hand, leng; char *lock; } cactype;
extern cactype cac[];

void caches(void)
{
    short i,k;

    k = 0;
    for(i=0;i<cacnum;i++)
        if ((*cac[i].lock) >= 200)
        {
            Bsprintf(tempbuf,"Locked- %d: Leng:%ld, Lock:%d",i,cac[i].leng,*cac[i].lock);
            printext256(0L,k,31,-1,tempbuf,1); k += 6;
        }

    k += 6;

    for(i=1;i<11;i++)
        if (lumplockbyte[i] >= 200)
        {
            Bsprintf(tempbuf,"RTS Locked %d:",i);
            printext256(0L,k,31,-1,tempbuf,1); k += 6;
        }


}

void checksync(void)
{
    long i;

    for(i=connecthead;i>=0;i=connectpoint2[i])
        if (syncvalhead[i] == syncvaltottail) break;
    if (i < 0)
    {
        syncstat = 0;
        do
        {
            for(i=connectpoint2[connecthead];i>=0;i=connectpoint2[i])
                if (syncval[i][syncvaltottail&(MOVEFIFOSIZ-1)] !=
                        syncval[connecthead][syncvaltottail&(MOVEFIFOSIZ-1)])
                    syncstat = 1;

            syncvaltottail++;
            for(i=connecthead;i>=0;i=connectpoint2[i])
                if (syncvalhead[i] == syncvaltottail) break;
        } while (i < 0);
    }
    if (connectpoint2[connecthead] < 0) syncstat = 0;

    if (syncstat)
    {
        printext256(4L,130L,31,0,"Out Of Sync - Please restart game",0);
        printext256(4L,138L,31,0,"RUN DN3DHELP.EXE for information.",0);
    }
    if (syncstate)
    {
        printext256(4L,160L,31,0,"Missed Network packet!",0);
        printext256(4L,138L,31,0,"RUN DN3DHELP.EXE for information.",0);
    }
}

void check_fta_sounds(short i)
{
    if(sprite[i].extra > 0)
        switch(dynamictostatic[PN])
        {
        case LIZTROOPONTOILET__STATIC:
        case LIZTROOPJUSTSIT__STATIC:
        case LIZTROOPSHOOT__STATIC:
        case LIZTROOPJETPACK__STATIC:
        case LIZTROOPDUCKING__STATIC:
        case LIZTROOPRUNNING__STATIC:
        case LIZTROOP__STATIC:
            spritesound(PRED_RECOG,i);
            break;
        case LIZMAN__STATIC:
        case LIZMANSPITTING__STATIC:
        case LIZMANFEEDING__STATIC:
        case LIZMANJUMP__STATIC:
            spritesound(CAPT_RECOG,i);
            break;
        case PIGCOP__STATIC:
        case PIGCOPDIVE__STATIC:
            spritesound(PIG_RECOG,i);
            break;
        case RECON__STATIC:
            spritesound(RECO_RECOG,i);
            break;
        case DRONE__STATIC:
            spritesound(DRON_RECOG,i);
            break;
        case COMMANDER__STATIC:
        case COMMANDERSTAYPUT__STATIC:
            spritesound(COMM_RECOG,i);
            break;
        case ORGANTIC__STATIC:
            spritesound(TURR_RECOG,i);
            break;
        case OCTABRAIN__STATIC:
        case OCTABRAINSTAYPUT__STATIC:
            spritesound(OCTA_RECOG,i);
            break;
        case BOSS1__STATIC:
            sound(BOS1_RECOG);
            break;
        case BOSS2__STATIC:
            if(sprite[i].pal == 1)
                sound(BOS2_RECOG);
            else sound(WHIPYOURASS);
            break;
        case BOSS3__STATIC:
            if(sprite[i].pal == 1)
                sound(BOS3_RECOG);
            else sound(RIPHEADNECK);
            break;
        case BOSS4__STATIC:
        case BOSS4STAYPUT__STATIC:
            if(sprite[i].pal == 1)
                sound(BOS4_RECOG);
            sound(BOSS4_FIRSTSEE);
            break;
        case GREENSLIME__STATIC:
            spritesound(SLIM_RECOG,i);
            break;
        }
}

inline short inventory(spritetype *s)
{
    switch(dynamictostatic[s->picnum])
    {
    case FIRSTAID__STATIC:
    case STEROIDS__STATIC:
    case HEATSENSOR__STATIC:
    case BOOTS__STATIC:
    case JETPACK__STATIC:
    case HOLODUKE__STATIC:
    case AIRTANK__STATIC:
        return 1;
    }
    return 0;
}

inline int checkspriteflags(short sActor, int iType)
{
    int i;

    i = spriteflags[sprite[sActor].picnum]; i ^= actorspriteflags[sActor];
    if(i & iType) return 1;
    else return 0;
}

inline int checkspriteflagsp(short sPicnum, int iType)
{
    if(spriteflags[sPicnum] & iType) return 1;
    else return 0;
}

inline short badguypic(short pn)
{
    //this case can't be handled by the dynamictostatic system because it adds
    //stuff to the value from names.h so handling separately
    if ((pn >= GREENSLIME) && (pn <= GREENSLIME+7)) return 1;

    if(checkspriteflagsp(pn,SPRITE_FLAG_BADGUY)) return 1;

    if( actortype[pn] ) return 1;

    switch(dynamictostatic[pn])
    {
    case SHARK__STATIC:
    case RECON__STATIC:
    case DRONE__STATIC:
    case LIZTROOPONTOILET__STATIC:
    case LIZTROOPJUSTSIT__STATIC:
    case LIZTROOPSTAYPUT__STATIC:
    case LIZTROOPSHOOT__STATIC:
    case LIZTROOPJETPACK__STATIC:
    case LIZTROOPDUCKING__STATIC:
    case LIZTROOPRUNNING__STATIC:
    case LIZTROOP__STATIC:
    case OCTABRAIN__STATIC:
    case COMMANDER__STATIC:
    case COMMANDERSTAYPUT__STATIC:
    case PIGCOP__STATIC:
    case EGG__STATIC:
    case PIGCOPSTAYPUT__STATIC:
    case PIGCOPDIVE__STATIC:
    case LIZMAN__STATIC:
    case LIZMANSPITTING__STATIC:
    case LIZMANFEEDING__STATIC:
    case LIZMANJUMP__STATIC:
    case ORGANTIC__STATIC:
    case BOSS1__STATIC:
    case BOSS2__STATIC:
    case BOSS3__STATIC:
    case BOSS4__STATIC:
        //case GREENSLIME:
        //case GREENSLIME+1:
        //case GREENSLIME+2:
        //case GREENSLIME+3:
        //case GREENSLIME+4:
        //case GREENSLIME+5:
        //case GREENSLIME+6:
        //case GREENSLIME+7:
    case RAT__STATIC:
    case ROTATEGUN__STATIC:
        return 1;
    }
    return 0;
}

inline short badguy(spritetype *s)
{
    return(badguypic(s->picnum));
}

void myos(long x, long y, short tilenum, signed char shade, char orientation)
{
    char p;
    short a;

    if(orientation&4)
        a = 1024;
    else a = 0;

    p = sector[ps[screenpeek].cursectnum].floorpal;
    rotatesprite(x<<16,y<<16,65536L,a,tilenum,shade,p,2|orientation,windowx1,windowy1,windowx2,windowy2);
}

void myospal(long x, long y, short tilenum, signed char shade, char orientation, char p)
{
    short a;

    if(orientation&4)
        a = 1024;
    else a = 0;

    rotatesprite(x<<16,y<<16,65536L,a,tilenum,shade,p,2|orientation,windowx1,windowy1,windowx2,windowy2);
}

void myosx(long x, long y, short tilenum, signed char shade, char orientation)
{
    char p;
    short a;

    if(orientation&4)
        a = 1024;
    else a = 0;

    p = sector[ps[screenpeek].cursectnum].floorpal;
    rotatesprite(x<<16,y<<16,32768L,a,tilenum,shade,p,2|orientation,windowx1,windowy1,windowx2,windowy2);
}

void myospalx(long x, long y, short tilenum, signed char shade, char orientation, char p)
{
    short a;

    if(orientation&4)
        a = 1024;
    else a = 0;

    rotatesprite(x<<16,y<<16,32768L,a,tilenum,shade,p,2|orientation,windowx1,windowy1,windowx2,windowy2);
}

void invennum(long x,long y,char num1,char ha,char sbits)
{
    char dabuf[80] = {0};
    Bsprintf(dabuf,"%d",num1);
    if(num1 > 99)
    {
        rotatesprite(sbarx(x-4),sbary(y),sbarsc(65536L),0,THREEBYFIVE+dabuf[0]-'0',ha,0,sbits,0,0,xdim-1,ydim-1);
        rotatesprite(sbarx(x),sbary(y),sbarsc(65536L),0,THREEBYFIVE+dabuf[1]-'0',ha,0,sbits,0,0,xdim-1,ydim-1);
        rotatesprite(sbarx(x+4),sbary(y),sbarsc(65536L),0,THREEBYFIVE+dabuf[2]-'0',ha,0,sbits,0,0,xdim-1,ydim-1);
    }
    else if(num1 > 9)
    {
        rotatesprite(sbarx(x),sbary(y),sbarsc(65536L),0,THREEBYFIVE+dabuf[0]-'0',ha,0,sbits,0,0,xdim-1,ydim-1);
        rotatesprite(sbarx(x+4),sbary(y),sbarsc(65536L),0,THREEBYFIVE+dabuf[1]-'0',ha,0,sbits,0,0,xdim-1,ydim-1);
    }
    else
        rotatesprite(sbarx(x+4),sbary(y),sbarsc(65536L),0,THREEBYFIVE+dabuf[0]-'0',ha,0,sbits,0,0,xdim-1,ydim-1);
}

void orderweaponnum(short ind,long x,long y,char ha)
{
    rotatesprite(sbarx(x-7),sbary(y),sbarsc(65536L),0,THREEBYFIVE+ind+1,ha-10,7,10,0,0,xdim-1,ydim-1);
    rotatesprite(sbarx(x-3),sbary(y),sbarsc(65536L),0,THREEBYFIVE+10,ha,0,10,0,0,xdim-1,ydim-1);

    minitextshade(x+1,y-4,"ORDER",26,6,2+8+16);
}

void weaponnum(short ind,long x,long y,long num1, long num2,char ha)
{
    char dabuf[80] = {0};

    rotatesprite(sbarx(x-7),sbary(y),sbarsc(65536L),0,THREEBYFIVE+ind+1,ha-10,7,10,0,0,xdim-1,ydim-1);
    rotatesprite(sbarx(x-3),sbary(y),sbarsc(65536L),0,THREEBYFIVE+10,ha,0,10,0,0,xdim-1,ydim-1);
    rotatesprite(sbarx(x+9),sbary(y),sbarsc(65536L),0,THREEBYFIVE+11,ha,0,10,0,0,xdim-1,ydim-1);

    if(num1 > 99) num1 = 99;
    if(num2 > 99) num2 = 99;

    Bsprintf(dabuf,"%ld",num1);
    if(num1 > 9)
    {
        rotatesprite(sbarx(x),sbary(y),sbarsc(65536L),0,THREEBYFIVE+dabuf[0]-'0',ha,0,10,0,0,xdim-1,ydim-1);
        rotatesprite(sbarx(x+4),sbary(y),sbarsc(65536L),0,THREEBYFIVE+dabuf[1]-'0',ha,0,10,0,0,xdim-1,ydim-1);
    }
    else rotatesprite(sbarx(x+4),sbary(y),sbarsc(65536L),0,THREEBYFIVE+dabuf[0]-'0',ha,0,10,0,0,xdim-1,ydim-1);

    Bsprintf(dabuf,"%ld",num2);
    if(num2 > 9)
    {
        rotatesprite(sbarx(x+13),sbary(y),sbarsc(65536L),0,THREEBYFIVE+dabuf[0]-'0',ha,0,10,0,0,xdim-1,ydim-1);
        rotatesprite(sbarx(x+17),sbary(y),sbarsc(65536L),0,THREEBYFIVE+dabuf[1]-'0',ha,0,10,0,0,xdim-1,ydim-1);
    }
    else rotatesprite(sbarx(x+13),sbary(y),sbarsc(65536L),0,THREEBYFIVE+dabuf[0]-'0',ha,0,10,0,0,xdim-1,ydim-1);
}

void weaponnum999(char ind,long x,long y,long num1, long num2,char ha)
{
    char dabuf[80] = {0};

    rotatesprite(sbarx(x-7),sbary(y),sbarsc(65536L),0,THREEBYFIVE+ind+1,ha-10,7,10,0,0,xdim-1,ydim-1);
    rotatesprite(sbarx(x-4),sbary(y),sbarsc(65536L),0,THREEBYFIVE+10,ha,0,10,0,0,xdim-1,ydim-1);
    rotatesprite(sbarx(x+13),sbary(y),sbarsc(65536L),0,THREEBYFIVE+11,ha,0,10,0,0,xdim-1,ydim-1);

    Bsprintf(dabuf,"%ld",num1);
    if(num1 > 99)
    {
        rotatesprite(sbarx(x),sbary(y),sbarsc(65536L),0,THREEBYFIVE+dabuf[0]-'0',ha,0,10,0,0,xdim-1,ydim-1);
        rotatesprite(sbarx(x+4),sbary(y),sbarsc(65536L),0,THREEBYFIVE+dabuf[1]-'0',ha,0,10,0,0,xdim-1,ydim-1);
        rotatesprite(sbarx(x+8),sbary(y),sbarsc(65536L),0,THREEBYFIVE+dabuf[2]-'0',ha,0,10,0,0,xdim-1,ydim-1);
    }
    else if(num1 > 9)
    {
        rotatesprite(sbarx(x+4),sbary(y),sbarsc(65536L),0,THREEBYFIVE+dabuf[0]-'0',ha,0,10,0,0,xdim-1,ydim-1);
        rotatesprite(sbarx(x+8),sbary(y),sbarsc(65536L),0,THREEBYFIVE+dabuf[1]-'0',ha,0,10,0,0,xdim-1,ydim-1);
    }
    else rotatesprite(sbarx(x+8),sbary(y),sbarsc(65536L),0,THREEBYFIVE+dabuf[0]-'0',ha,0,10,0,0,xdim-1,ydim-1);

    Bsprintf(dabuf,"%ld",num2);
    if(num2 > 99)
    {
        rotatesprite(sbarx(x+17),sbary(y),sbarsc(65536L),0,THREEBYFIVE+dabuf[0]-'0',ha,0,10,0,0,xdim-1,ydim-1);
        rotatesprite(sbarx(x+21),sbary(y),sbarsc(65536L),0,THREEBYFIVE+dabuf[1]-'0',ha,0,10,0,0,xdim-1,ydim-1);
        rotatesprite(sbarx(x+25),sbary(y),sbarsc(65536L),0,THREEBYFIVE+dabuf[2]-'0',ha,0,10,0,0,xdim-1,ydim-1);
    }
    else if(num2 > 9)
    {
        rotatesprite(sbarx(x+17),sbary(y),sbarsc(65536L),0,THREEBYFIVE+dabuf[0]-'0',ha,0,10,0,0,xdim-1,ydim-1);
        rotatesprite(sbarx(x+21),sbary(y),sbarsc(65536L),0,THREEBYFIVE+dabuf[1]-'0',ha,0,10,0,0,xdim-1,ydim-1);
    }
    else rotatesprite(sbarx(x+25),sbary(y),sbarsc(65536L),0,THREEBYFIVE+dabuf[0]-'0',ha,0,10,0,0,xdim-1,ydim-1);
}

void weapon_amounts(struct player_struct *p,long x,long y,long u)
{
    int cw;

    cw = p->curr_weapon;

    if (u&4)
    {
        if (u != -1) patchstatusbar(88,178,88+37,178+6); //original code: (96,178,96+12,178+6);
        weaponnum999(PISTOL_WEAPON,x,y,
                     p->ammo_amount[PISTOL_WEAPON],max_ammo_amount[PISTOL_WEAPON],
                     12-20*(cw == PISTOL_WEAPON) );
    }
    if (u&8)
    {
        if (u != -1) patchstatusbar(88,184,88+37,184+6); //original code: (96,184,96+12,184+6);
        weaponnum999(SHOTGUN_WEAPON,x,y+6,
                     p->ammo_amount[SHOTGUN_WEAPON],max_ammo_amount[SHOTGUN_WEAPON],
                     (!p->gotweapon[SHOTGUN_WEAPON]*9)+12-18*
                     (cw == SHOTGUN_WEAPON) );
    }
    if (u&16)
    {
        if (u != -1) patchstatusbar(88,190,88+37,190+6); //original code: (96,190,96+12,190+6);
        weaponnum999(CHAINGUN_WEAPON,x,y+12,
                     p->ammo_amount[CHAINGUN_WEAPON],max_ammo_amount[CHAINGUN_WEAPON],
                     (!p->gotweapon[CHAINGUN_WEAPON]*9)+12-18*
                     (cw == CHAINGUN_WEAPON) );
    }
    if (u&32)
    {
        if (u != -1) patchstatusbar(127,178,127+29,178+6); //original code: (135,178,135+8,178+6);
        weaponnum(RPG_WEAPON,x+39,y,
                  p->ammo_amount[RPG_WEAPON],max_ammo_amount[RPG_WEAPON],
                  (!p->gotweapon[RPG_WEAPON]*9)+12-19*
                  (cw == RPG_WEAPON) );
    }
    if (u&64)
    {
        if (u != -1) patchstatusbar(127,184,127+29,184+6); //original code: (135,184,135+8,184+6);
        weaponnum(HANDBOMB_WEAPON,x+39,y+6,
                  p->ammo_amount[HANDBOMB_WEAPON],max_ammo_amount[HANDBOMB_WEAPON],
                  (((!p->ammo_amount[HANDBOMB_WEAPON])|(!p->gotweapon[HANDBOMB_WEAPON]))*9)+12-19*
                  ((cw == HANDBOMB_WEAPON) || (cw == HANDREMOTE_WEAPON)));
    }
    if (u&128)
    {
        if (u != -1) patchstatusbar(127,190,127+29,190+6); //original code: (135,190,135+8,190+6);

        if (VOLUMEONE) {
            orderweaponnum(SHRINKER_WEAPON,x+39,y+12,
                           (!p->gotweapon[SHRINKER_WEAPON]*9)+12-18*
                           (cw == SHRINKER_WEAPON) );
        } else {
            if(p->subweapon&(1<<GROW_WEAPON))
                weaponnum(SHRINKER_WEAPON,x+39,y+12,
                          p->ammo_amount[GROW_WEAPON],max_ammo_amount[GROW_WEAPON],
                          (!p->gotweapon[GROW_WEAPON]*9)+12-18*
                          (cw == GROW_WEAPON) );
            else
                weaponnum(SHRINKER_WEAPON,x+39,y+12,
                          p->ammo_amount[SHRINKER_WEAPON],max_ammo_amount[SHRINKER_WEAPON],
                          (!p->gotweapon[SHRINKER_WEAPON]*9)+12-18*
                          (cw == SHRINKER_WEAPON) );
        }
    }
    if (u&256)
    {
        if (u != -1) patchstatusbar(158,178,162+29,178+6); //original code: (166,178,166+8,178+6);

        if (VOLUMEONE) {
            orderweaponnum(DEVISTATOR_WEAPON,x+70,y,
                           (!p->gotweapon[DEVISTATOR_WEAPON]*9)+12-18*
                           (cw == DEVISTATOR_WEAPON) );
        } else {
            weaponnum(DEVISTATOR_WEAPON,x+70,y,
                      p->ammo_amount[DEVISTATOR_WEAPON],max_ammo_amount[DEVISTATOR_WEAPON],
                      (!p->gotweapon[DEVISTATOR_WEAPON]*9)+12-18*
                      (cw == DEVISTATOR_WEAPON) );
        }
    }
    if (u&512)
    {
        if (u != -1) patchstatusbar(158,184,162+29,184+6); //original code: (166,184,166+8,184+6);
        if (VOLUMEONE) {
            orderweaponnum(TRIPBOMB_WEAPON,x+70,y+6,
                           (!p->gotweapon[TRIPBOMB_WEAPON]*9)+12-18*
                           (cw == TRIPBOMB_WEAPON) );
        } else {
            weaponnum(TRIPBOMB_WEAPON,x+70,y+6,
                      p->ammo_amount[TRIPBOMB_WEAPON],max_ammo_amount[TRIPBOMB_WEAPON],
                      (!p->gotweapon[TRIPBOMB_WEAPON]*9)+12-18*
                      (cw == TRIPBOMB_WEAPON) );
        }
    }

    if (u&65536L)
    {
        if (u != -1) patchstatusbar(158,190,162+29,190+6); //original code: (166,190,166+8,190+6);
        if (VOLUMEONE) {
            orderweaponnum(-1,x+70,y+12,
                           (!p->gotweapon[FREEZE_WEAPON]*9)+12-18*
                           (cw == FREEZE_WEAPON) );
        } else {
            weaponnum(-1,x+70,y+12,
                      p->ammo_amount[FREEZE_WEAPON],max_ammo_amount[FREEZE_WEAPON],
                      (!p->gotweapon[FREEZE_WEAPON]*9)+12-18*
                      (cw == FREEZE_WEAPON) );
        }
    }
}

void digitalnumber(long x,long y,long n,char s,char cs)
{

    short i, j, k, p, c;
    char b[10];

    //ltoa(n,b,10);
    Bsnprintf(b,10,"%ld",n);
    i = strlen(b);
    j = 0;

    for(k=0;k<i;k++)
    {
        p = DIGITALNUM+*(b+k)-'0';
        j += tilesizx[p]+1;
    }
    c = x-(j>>1);

    j = 0;
    for(k=0;k<i;k++)
    {
        p = DIGITALNUM+*(b+k)-'0';
        rotatesprite(sbarx(c+j),sbary(y),sbarsc(65536L),0,p,s,0,cs,0,0,xdim-1,ydim-1);
        j += tilesizx[p]+1;
    }
}

void txdigitalnumber(short starttile, long x,long y,long n,char s,char pal,char cs,long x1, long y1, long x2, long y2)
{
    short i, j, k, p, c;
    char b[10];

    //ltoa(n,b,10);
    Bsnprintf(b,10,"%ld",n);
    i = strlen(b);
    j = 0;

    for(k=0;k<i;k++)
    {
        p = starttile+*(b+k)-'0';
        j += tilesizx[p]+1;
    }
    c = x-(j>>1);

    j = 0;
    for(k=0;k<i;k++)
    {
        p = starttile+*(b+k)-'0';
        rotatesprite((c+j)<<16,y<<16,65536L,0,p,s,pal,2|cs,x1,y1,x2,y2);
        /*        rotatesprite((c+j)<<16,y<<16,65536L,0,p,s,pal,cs,0,0,xdim-1,ydim-1);
                rotatesprite(x<<16,y<<16,32768L,a,tilenum,shade,p,2|orientation,windowx1,windowy1,windowx2,windowy2);*/

        j += tilesizx[p]+1;
    }
}

/*

void scratchmarks(long x,long y,long n,char s,char p)
{
    long i, ni;

    ni = n/5;
    for(i=ni;i >= 0;i--)
    {
        overwritesprite(x-2,y,SCRATCH+4,s,0,0);
        x += tilesizx[SCRATCH+4]-1;
    }

    ni = n%5;
    if(ni) overwritesprite(x,y,SCRATCH+ni-1,s,p,0);
}
  */

void displayinventory(struct player_struct *p)
{
    short n, j, xoff, y;

    j = xoff = 0;

    n = (p->jetpack_amount > 0)<<3; if(n&8) j++;
    n |= ( p->scuba_amount > 0 )<<5; if(n&32) j++;
    n |= (p->steroids_amount > 0)<<1; if(n&2) j++;
    n |= ( p->holoduke_amount > 0)<<2; if(n&4) j++;
    n |= (p->firstaid_amount > 0); if(n&1) j++;
    n |= (p->heat_amount > 0)<<4; if(n&16) j++;
    n |= (p->boot_amount > 0)<<6; if(n&64) j++;

    xoff = 160-(j*11);

    j = 0;

    if(ud.screen_size > 4)
        y = 154;
    else y = (ud.drawweapon == 2?150:172);

    if(ud.screen_size == 4 && ud.drawweapon != 2)
    {
        if(ud.multimode > 1)
            xoff += 56;
        else xoff += 65;
    }

    while( j <= 9 )
    {
        if( n&(1<<j) )
        {
            switch( n&(1<<j) )
            {
            case   1:
                rotatesprite(xoff<<16,y<<16,65536L,0,FIRSTAID_ICON,0,0,2+16,windowx1,windowy1,windowx2,windowy2);break;
            case   2:
                rotatesprite((xoff+1)<<16,y<<16,65536L,0,STEROIDS_ICON,0,0,2+16,windowx1,windowy1,windowx2,windowy2);break;
            case   4:
                rotatesprite((xoff+2)<<16,y<<16,65536L,0,HOLODUKE_ICON,0,0,2+16,windowx1,windowy1,windowx2,windowy2);break;
            case   8:
                rotatesprite(xoff<<16,y<<16,65536L,0,JETPACK_ICON,0,0,2+16,windowx1,windowy1,windowx2,windowy2);break;
            case  16:
                rotatesprite(xoff<<16,y<<16,65536L,0,HEAT_ICON,0,0,2+16,windowx1,windowy1,windowx2,windowy2);break;
            case  32:
                rotatesprite(xoff<<16,y<<16,65536L,0,AIRTANK_ICON,0,0,2+16,windowx1,windowy1,windowx2,windowy2);break;
            case 64:
                rotatesprite(xoff<<16,(y-1)<<16,65536L,0,BOOT_ICON,0,0,2+16,windowx1,windowy1,windowx2,windowy2);break;
            }

            xoff += 22;

            if(p->inven_icon == j+1)
                rotatesprite((xoff-2)<<16,(y+19)<<16,65536L,1024,ARROW,-32,0,2+16,windowx1,windowy1,windowx2,windowy2);
        }

        j++;
    }
}

void displayfragbar(void)
{
    short i, j;

    j = 0;

    for(i=connecthead;i>=0;i=connectpoint2[i])
        if(i > j) j = i;

    rotatesprite(0,0,65600L,0,FRAGBAR,0,0,2+8+16+64,0,0,xdim-1,ydim-1);
    if(j >= 4) rotatesprite(319,(8)<<16,65600L,0,FRAGBAR,0,0,10+16+64,0,0,xdim-1,ydim-1);
    if(j >= 8) rotatesprite(319,(16)<<16,65600L,0,FRAGBAR,0,0,10+16+64,0,0,xdim-1,ydim-1);
    if(j >= 12) rotatesprite(319,(24)<<16,65600L,0,FRAGBAR,0,0,10+16+64,0,0,xdim-1,ydim-1);

    for(i=connecthead;i>=0;i=connectpoint2[i])
    {
        minitext(21+(73*(i&3)),2+((i&28)<<1),&ud.user_name[i][0],/*sprite[ps[i].i].pal*/ps[i].palookup,2+8+16);
        Bsprintf(tempbuf,"%d",ps[i].frag-ps[i].fraggedself);
        minitext(17+50+(73*(i&3)),2+((i&28)<<1),tempbuf,/*sprite[ps[i].i].pal*/ps[i].palookup,2+8+16);
    }
}

void coolgaugetext(short snum)
{
    struct player_struct *p;
    long i, j, o, ss, u;
    char permbit;

    p = &ps[snum];

    if (p->invdisptime > 0) displayinventory(p);


    if(ps[snum].gm&MODE_MENU)
        if( (current_menu >= 400  && current_menu <= 405) )
            return;

    ss = ud.screen_size; if (ss < 4) return;

    if (getrendermode() >= 3) pus = NUMPAGES;   // JBF 20040101: always redraw in GL

    if ( ud.multimode > 1 && (gametype_flags[ud.coop] & GAMETYPE_FLAG_FRAGBAR))
    {
        if (pus)
        { displayfragbar(); }
        else
        {
            for(i=connecthead;i>=0;i=connectpoint2[i])
            if (ps[i].frag != sbar.frag[i]) { displayfragbar(); break; }
        }
        for(i=connecthead;i>=0;i=connectpoint2[i])
            if (i != myconnectindex)
                sbar.frag[i] = ps[i].frag;
    }

    if (ss == 4)   //DRAW MINI STATUS BAR:
    {
        rotatesprite(sbarx(5),sbary(200-28),sbarsc(65536L),0,HEALTHBOX,0,21,10+16,0,0,xdim-1,ydim-1);
        if (p->inven_icon)
            rotatesprite(sbarx(69),sbary(200-30),sbarsc(65536L),0,INVENTORYBOX,0,21,10+16,0,0,xdim-1,ydim-1);

        if(sprite[p->i].pal == 1 && p->last_extra < 2)
            digitalnumber(20,200-17,1,-16,10+16);
        else digitalnumber(20,200-17,p->last_extra,-16,10+16);

        rotatesprite(sbarx(37),sbary(200-28),sbarsc(65536L),0,AMMOBOX,0,21,10+16,0,0,xdim-1,ydim-1);

        if (p->curr_weapon == HANDREMOTE_WEAPON) i = HANDBOMB_WEAPON; else i = p->curr_weapon;
        digitalnumber(53,200-17,p->ammo_amount[i],-16,10+16);

        o = 158; permbit = 0;
        if (p->inven_icon)
        {
            switch(p->inven_icon)
            {
            case 1: i = FIRSTAID_ICON; break;
            case 2: i = STEROIDS_ICON; break;
            case 3: i = HOLODUKE_ICON; break;
            case 4: i = JETPACK_ICON; break;
            case 5: i = HEAT_ICON; break;
            case 6: i = AIRTANK_ICON; break;
            case 7: i = BOOT_ICON; break;
            default: i = -1;
            }
            if (i >= 0) rotatesprite(sbarx(231-o),sbary(200-21),sbarsc(65536L),0,i,0,0,10+16+permbit,0,0,xdim-1,ydim-1);

            minitext(292-30-o,190,"%",6,10+16+permbit + 256);

            j = 0x80000000;
            switch(p->inven_icon)
            {
            case 1: i = p->firstaid_amount; break;
            case 2: i = ((p->steroids_amount+3)>>2); break;
            case 3: i = ((p->holoduke_amount+15)/24); j = p->holoduke_on; break;
            case 4: i = ((p->jetpack_amount+15)>>4); j = p->jetpack_on; break;
            case 5: i = p->heat_amount/12; j = p->heat_on; break;
            case 6: i = ((p->scuba_amount+63)>>6); break;
            case 7: i = (p->boot_amount>>1); break;
            }
            invennum(284-30-o,200-6,(char)i,0,10+permbit);
            if (j > 0) minitext(288-30-o,180,"ON",0,10+16+permbit + 256);
            else if ((unsigned long)j != 0x80000000) minitext(284-30-o,180,"OFF",2,10+16+permbit + 256);
            if (p->inven_icon >= 6) minitext(284-35-o,180,"AUTO",2,10+16+permbit + 256);
        }
        return;
    }

    //DRAW/UPDATE FULL STATUS BAR:

if (pus) { pus = 0; u = -1; } else u = 0;

if (sbar.frag[myconnectindex] != p->frag) { sbar.frag[myconnectindex] = p->frag; u |= 32768; }
    if (sbar.got_access != p->got_access) { sbar.got_access = p->got_access; u |= 16384; }
    if (sbar.last_extra != p->last_extra) { sbar.last_extra = p->last_extra; u |= 1; }
    {
        long lAmount=GetGameVar("PLR_MORALE",-1, p->i, snum);
        if(lAmount == -1)
        {
            if (sbar.shield_amount != p->shield_amount) { sbar.shield_amount = p->shield_amount; u |= 2; }
        }
        else
        {
            if (sbar.shield_amount != lAmount) { sbar.shield_amount = lAmount; u |= 2; }
        }
    }
    if (sbar.curr_weapon != p->curr_weapon) { sbar.curr_weapon = p->curr_weapon; u |= (4+8+16+32+64+128+256+512+1024+65536L); }

    for(i=1;i<MAX_WEAPONS;i++)
    {
        if (sbar.ammo_amount[i] != p->ammo_amount[i])
        {
            sbar.ammo_amount[i] = p->ammo_amount[i];
            if(i < 9)
                u |= ((2<<i)+1024);
            else u |= 65536L+1024;
        }

        if (sbar.gotweapon[i] != p->gotweapon[i])
        {
            sbar.gotweapon[i] = p->gotweapon[i];
            if(i < 9 )
                u |= ((2<<i)+1024);
            else u |= 65536L+1024;
        }
    }

if (sbar.inven_icon != p->inven_icon) { sbar.inven_icon = p->inven_icon; u |= (2048+4096+8192); }
    if (sbar.holoduke_on != p->holoduke_on) { sbar.holoduke_on = p->holoduke_on; u |= (4096+8192); }
    if (sbar.jetpack_on != p->jetpack_on) { sbar.jetpack_on = p->jetpack_on; u |= (4096+8192); }
    if (sbar.heat_on != p->heat_on) { sbar.heat_on = p->heat_on; u |= (4096+8192); }
    if (sbar.firstaid_amount != p->firstaid_amount) { sbar.firstaid_amount = p->firstaid_amount; u |= 8192; }
    if (sbar.steroids_amount != p->steroids_amount) { sbar.steroids_amount = p->steroids_amount; u |= 8192; }
    if (sbar.holoduke_amount != p->holoduke_amount) { sbar.holoduke_amount = p->holoduke_amount; u |= 8192; }
    if (sbar.jetpack_amount != p->jetpack_amount) { sbar.jetpack_amount = p->jetpack_amount; u |= 8192; }
    if (sbar.heat_amount != p->heat_amount) { sbar.heat_amount = p->heat_amount; u |= 8192; }
    if (sbar.scuba_amount != p->scuba_amount) { sbar.scuba_amount = p->scuba_amount; u |= 8192; }
    if (sbar.boot_amount != p->boot_amount) { sbar.boot_amount = p->boot_amount; u |= 8192; }
    if (u == 0) return;

    //0 - update health
    //1 - update armor
    //2 - update PISTOL_WEAPON ammo
    //3 - update SHOTGUN_WEAPON ammo
    //4 - update CHAINGUN_WEAPON ammo
    //5 - update RPG_WEAPON ammo
    //6 - update HANDBOMB_WEAPON ammo
    //7 - update SHRINKER_WEAPON ammo
    //8 - update DEVISTATOR_WEAPON ammo
    //9 - update TRIPBOMB_WEAPON ammo
    //10 - update ammo display
    //11 - update inventory icon
    //12 - update inventory on/off
    //13 - update inventory %
    //14 - update keys
    //15 - update kills
    //16 - update FREEZE_WEAPON ammo
    if (u == -1)
    {
        patchstatusbar(0,0,320,200);
        if (ud.multimode > 1 && (gametype_flags[ud.coop] & GAMETYPE_FLAG_FRAGBAR))
            rotatesprite(sbarx(277+1),sbary(200-27-1),sbarsc(65536L),0,KILLSICON,0,0,10+16,0,0,xdim-1,ydim-1);
    }
    if (ud.multimode > 1 && (gametype_flags[ud.coop] & GAMETYPE_FLAG_FRAGBAR))
    {
        if (u&32768)
        {
            if (u != -1) patchstatusbar(276,183,299,193);
            digitalnumber(287,200-17,max(p->frag-p->fraggedself,0),-16,10+16);
        }
    }
    else
    {
        if (u&16384)
        {
            if (u != -1) patchstatusbar(275,182,299,194);
            if (p->got_access&4) rotatesprite(sbarx(275),sbary(182),sbarsc(65536L),0,ACCESS_ICON,0,23,10+16,0,0,xdim-1,ydim-1);
            if (p->got_access&2) rotatesprite(sbarx(288),sbary(182),sbarsc(65536L),0,ACCESS_ICON,0,21,10+16,0,0,xdim-1,ydim-1);
            if (p->got_access&1) rotatesprite(sbarx(281),sbary(189),sbarsc(65536L),0,ACCESS_ICON,0,0,10+16,0,0,xdim-1,ydim-1);
        }
    }
    if (u&(4+8+16+32+64+128+256+512+65536L)) weapon_amounts(p,96,182,u);

    if (u&1)
    {
        if (u != -1) patchstatusbar(20,183,43,194); //Original code:(20,183,43,193);
        if (sprite[p->i].pal == 1 && p->last_extra < 2)
            digitalnumber(32,200-17,1,-16,10+16);
        else digitalnumber(32,200-17,p->last_extra,-16,10+16);
    }
    if (u&2)
    {
        long lAmount=GetGameVar("PLR_MORALE",-1, p->i, snum);
        if (u != -1) patchstatusbar(52,183,75,193);
        if(lAmount == -1)
        {
            digitalnumber(64,200-17,p->shield_amount,-16,10+16);
        }
        else
        {
            digitalnumber(64,200-17,lAmount,-16,10+16);
        }
    }

    if (u&1024)
    {
        if (u != -1) patchstatusbar(196,183,219,194); //Original code:(196,183,219,193);
        if (p->curr_weapon != KNEE_WEAPON)
        {
            if (p->curr_weapon == HANDREMOTE_WEAPON) i = HANDBOMB_WEAPON; else i = p->curr_weapon;
            digitalnumber(230-22,200-17,p->ammo_amount[i],-16,10+16);
        }
    }

    if (u&(2048+4096+8192))
    {
        if (u != -1)
        {
            if (u&(2048+4096)) { patchstatusbar(231,179,265,197); }
            else { patchstatusbar(250,190,261,196); } //Original code:(250,190,261,195);
        }
        if (p->inven_icon)
        {
            o = 0; permbit = 0;

            if (u&(2048+4096))
            {
                switch(p->inven_icon)
                {
                case 1: i = FIRSTAID_ICON; break;
                case 2: i = STEROIDS_ICON; break;
                case 3: i = HOLODUKE_ICON; break;
                case 4: i = JETPACK_ICON; break;
                case 5: i = HEAT_ICON; break;
                case 6: i = AIRTANK_ICON; break;
                case 7: i = BOOT_ICON; break;
                }

                rotatesprite(sbarx(231-o),sbary(200-21),sbarsc(65536L),0,i,0,0,10+16+permbit,0,0,xdim-1,ydim-1);
                minitext(292-30-o,190,"%",6,10+16+permbit + 256);
                if (p->inven_icon >= 6) minitext(284-35-o,180,"AUTO",2,10+16+permbit + 256);
            }
            if (u&(2048+4096))
            {
                switch(p->inven_icon)
                {
                case 3: j = p->holoduke_on; break;
                case 4: j = p->jetpack_on; break;
                case 5: j = p->heat_on; break;
                default: j = 0x80000000;
                }
                if (j > 0) minitext(288-30-o,180,"ON",0,10+16+permbit + 256);
                else if ((unsigned long)j != 0x80000000) minitext(284-30-o,180,"OFF",2,10+16+permbit + 256);
            }
            if (u&8192)
            {
                switch(p->inven_icon)
                {
                case 1: i = p->firstaid_amount; break;
                case 2: i = ((p->steroids_amount+3)>>2); break;
                case 3: i = ((p->holoduke_amount+15)/24); break;
                case 4: i = ((p->jetpack_amount+15)>>4); break;
                case 5: i = p->heat_amount/12; break;
                case 6: i = ((p->scuba_amount+63)>>6); break;
                case 7: i = (p->boot_amount>>1); break;
                }
                invennum(284-30-o,200-6,(char)i,0,10+permbit);
            }
        }
    }
}

#define AVERAGEFRAMES 16
static long frameval[AVERAGEFRAMES], framecnt = 0;

void tics(void)
{
    long i,j;
    char b[10];

    i = totalclock;
    if (i != frameval[framecnt])
    {
        j=(TICRATE*AVERAGEFRAMES)/(i-frameval[framecnt]);
        if (ud.tickrate && !(ps[myconnectindex].gm&MODE_MENU))
        {
            Bsprintf(b,"%ld",j>0?j:0);
            minitext(320-strlen(b)*4,ud.screen_size!=0?(ud.multimode>1&&ud.multimode<5?9:(ud.multimode>4?17:1)):1,b,(TICRATE*AVERAGEFRAMES)/(i-frameval[framecnt]) < 40?2:0,26);
        }
        framerate = j;
        frameval[framecnt] = i;
    }
    framecnt = ((framecnt+1)&(AVERAGEFRAMES-1));
}

void coords(short snum)
{
    short y = 8;

    if((gametype_flags[ud.coop] & GAMETYPE_FLAG_FRAGBAR))
    {
        if(ud.multimode > 1 && ud.multimode < 5)
            y = 16;
        else if(ud.multimode > 4)
            y = 24;
    }
    sprintf(tempbuf,"X= %ld",ps[snum].posx);
    printext256(250L,y,31,-1,tempbuf,0);
    sprintf(tempbuf,"Y= %ld",ps[snum].posy);
    printext256(250L,y+9L,31,-1,tempbuf,0);
    Bsprintf(tempbuf,"Z= %ld",ps[snum].posz);
    printext256(250L,y+18L,31,-1,tempbuf,0);
    Bsprintf(tempbuf,"A= %d",ps[snum].ang);
    printext256(250L,y+27L,31,-1,tempbuf,0);
    Bsprintf(tempbuf,"ZV= %ld",ps[snum].poszv);
    printext256(250L,y+36L,31,-1,tempbuf,0);
    Bsprintf(tempbuf,"OG= %d",ps[snum].on_ground);
    printext256(250L,y+45L,31,-1,tempbuf,0);
    Bsprintf(tempbuf,"AM= %d",ps[snum].ammo_amount[GROW_WEAPON]);
    printext256(250L,y+54L,31,-1,tempbuf,0);
    Bsprintf(tempbuf,"LFW= %d",ps[snum].last_full_weapon);
    printext256(250L,y+63L,31,-1,tempbuf,0);
    Bsprintf(tempbuf,"SECTL= %d",sector[ps[snum].cursectnum].lotag);
    printext256(250L,y+72L,31,-1,tempbuf,0);
    Bsprintf(tempbuf,"SEED= %ld",randomseed);
    printext256(250L,y+81L,31,-1,tempbuf,0);
    Bsprintf(tempbuf,"THOLD= %d",ps[snum].transporter_hold);
    printext256(250L,y+90L+7,31,-1,tempbuf,0);
}

void operatefta(void)
{
    long i, j, k, l;

    if(ud.screen_size > 0) j = 200-45; else j = 200-8;
    quotebot = min(quotebot,j);
    quotebotgoal = min(quotebotgoal,j);
    if(ps[myconnectindex].gm&MODE_TYPE) j -= 8;
    quotebotgoal = j; j = quotebot;
    for(i=0;i<MAXUSERQUOTES;i++)
    {
        k = user_quote_time[i]; if (k <= 0) break;
        l = Bstrlen(user_quote[i]);
        while(l > TEXTWRAPLEN)
        {
            l -= TEXTWRAPLEN;
            j -= 8;
        }
        if (k > 4)
            mpgametext(j,user_quote[i],0,2+8+16);
        else if (k > 2) mpgametext(j,user_quote[i],0,2+8+16+1);
        else mpgametext(j,user_quote[i],0,2+8+16+1+32);
        j -= 8;
    }

    if ((klabs(quotebotgoal-quotebot) <= 16) && (ud.screen_size <= 8))
        quotebot += ksgn(quotebotgoal-quotebot);
    else
        quotebot = quotebotgoal;

    if (ps[screenpeek].fta <= 1) return;

    if ((gametype_flags[ud.coop] & GAMETYPE_FLAG_FRAGBAR) && ud.screen_size > 0 && ud.multimode > 1)
    {
        j = 0; k = 8;
        for(i=connecthead;i>=0;i=connectpoint2[i])
            if (i > j) j = i;

        if (j >= 4 && j <= 8) k += 8;
        else if (j > 8 && j <= 12) k += 16;
        else if (j > 12) k += 24;
    }
    else k = 0;

    if (ps[screenpeek].ftq == 115 || ps[screenpeek].ftq == 116 || ps[screenpeek].ftq == 117)
    {
        k = quotebot-8;
        /*        for(i=0;i<MAXUSERQUOTES;i++)
                {
                    if (user_quote_time[i] <= 0) break;
                    k -= 8;
                    l = Bstrlen(user_quote[i]);
                    while(l > TEXTWRAPLEN)
                    {
                        l -= TEXTWRAPLEN;
                        k -= 8;
                    }
                } */

        k -= 4;
    }

    if(fta_quotes[ps[screenpeek].ftq] == NULL)
    {
        OSD_Printf("%s %d null quote %d\n",__FILE__,__LINE__,ps[screenpeek].ftq);
        return;
    }

    j = ps[screenpeek].fta;
    if (j > 4)
        gametext(320>>1,k,fta_quotes[ps[screenpeek].ftq],0,2+8+16);
    else
        if (j > 2) gametext(320>>1,k,fta_quotes[ps[screenpeek].ftq],0,2+8+16+1);
        else
            gametext(320>>1,k,fta_quotes[ps[screenpeek].ftq],0,2+8+16+1+32);
}

void FTA(short q,struct player_struct *p)
{
    if(fta_quotes[p->ftq] != NULL)
    {
        if( ud.fta_on == 1)
        {
            if( p->fta > 0 && q != 115 && q != 116 )
                if( p->ftq == 115 || p->ftq == 116 ) return;

            p->fta = 100;

            if( p->ftq != q || q == 26 )
                // || q == 26 || q == 115 || q ==116 || q == 117 || q == 122 )
            {
                p->ftq = q;
                pub = NUMPAGES;
                pus = NUMPAGES;
                if (p == &ps[screenpeek])
                    OSD_Printf("%s\n",strip_color_codes(fta_quotes[q]));
            }
        }
    } else OSD_Printf("%s %d null quote %d\n",__FILE__,__LINE__,p->ftq);
}

void showtwoscreens(void)
{
    if (!VOLUMEALL) {
        setview(0,0,xdim-1,ydim-1);
        flushperms();
        //ps[myconnectindex].palette = palette;
        setgamepalette(&ps[myconnectindex], palette, 1);        // JBF 20040308
        fadepal(0,0,0, 0,64,7);
        KB_FlushKeyboardQueue();
        rotatesprite(0,0,65536L,0,3291,0,0,2+8+16+64, 0,0,xdim-1,ydim-1);
        IFISSOFTMODE fadepal(0,0,0, 63,0,-7); else nextpage();
    while( !KB_KeyWaiting() ) { handleevents(); getpackets(); }

        fadepal(0,0,0, 0,64,7);
        KB_FlushKeyboardQueue();
        rotatesprite(0,0,65536L,0,3290,0,0,2+8+16+64, 0,0,xdim-1,ydim-1);
        IFISSOFTMODE fadepal(0,0,0, 63,0,-7); else nextpage();
    while( !KB_KeyWaiting() ) { handleevents(); getpackets(); }
    }
}
/*
void binscreen(void)
{
    long fil;
#ifdef VOLUMEONE
    fil = kopen4load("dukesw.bin",1);
#else
    fil = kopen4load("duke3d.bin",1);
#endif
    if(fil == -1) return;
    kread(fil,(char *)0xb8000,4000);
    kclose(fil);
}
*/


extern long qsetmode;

void gameexit(char *t)
{
    if(*t != 0) ps[myconnectindex].palette = (char *) &palette[0];

    if(numplayers > 1)
    {
        allowtimetocorrecterrorswhenquitting();
        uninitmultiplayers();
    }

    if(ud.recstat == 1) closedemowrite();
else if(ud.recstat == 2) { if (frecfilep) fclose(frecfilep); }  // JBF: fixes crash on demo playback

    if(!qe && !cp)
    {
        if(playerswhenstarted > 1 && ps[myconnectindex].gm&MODE_GAME && (gametype_flags[ud.coop] & GAMETYPE_FLAG_SCORESHEET) && *t == ' ')
        {
            dobonus(1);
            setgamemode(ScreenMode,ScreenWidth,ScreenHeight,ScreenBPP);
        }

        if( *t != 0 && *(t+1) != 'V' && *(t+1) != 'Y')
            showtwoscreens();
    }

    if (qsetmode == 200)
        Shutdown();

    if(*t != 0)
    {
        //setvmode(0x3);    // JBF
        //binscreen();
        //            if(*t == ' ' && *(t+1) == 0) *t = 0;
        //printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n");
        if (!(t[0] == ' ' && t[1] == 0)) {
            wm_msgbox(HEAD2, t);
        }
    }

    uninitgroupfile();

    //unlink("duke3d.tmp");

    exit(0);
}

char inputloc = 0;

short strget_(int small,short x,short y,char *t,short dalen,short c)
{
    short ch;
    int i;

    while((ch = KB_Getch()) != 0)
    {
        if(ch == asc_BackSpace)
        {
            if( inputloc > 0 )
            {
                inputloc--;
                *(t+inputloc) = 0;
            }
        }
        else
        {
            if(ch == asc_Enter)
            {
                KB_ClearKeyDown(sc_Enter);
                KB_ClearKeyDown(sc_kpad_Enter);
                return (1);
            }
            else if(ch == asc_Escape)
            {
                KB_ClearKeyDown(sc_Escape);
                return (-1);
            }
            else if ( ch >= 32 && inputloc < dalen && ch < 127)
            {
                ch = Btoupper(ch);
                if (c != 997 || (ch >= '0' && ch <= '9')) { // JBF 20040508: so we can have numeric only if we want
                    *(t+inputloc) = ch;
                    *(t+inputloc+1) = 0;
                    inputloc++;
                }
            }
        }
    }

    if( c == 999 ) return(0);
    if( c == 998 )
    {
        char b[91],ii;
        for(ii=0;ii<inputloc;ii++)
            b[ii] = '*';
        b[ii] = 0;
        if(ps[myconnectindex].gm&MODE_TYPE)
            x = mpgametext(y,b,c,2+8+16);
        else x = gametext(x,y,b,c,2+8+16);
    }
    else
    {
        if(ps[myconnectindex].gm&MODE_TYPE)
            x = mpgametext(y,t,c,2+8+16);
        else x = gametext(x,y,t,c,2+8+16);
    }
    c = 4-(sintable[(totalclock<<4)&2047]>>11);

    i = Bstrlen(t);
    while(i > TEXTWRAPLEN-!small)
    {
        i -= TEXTWRAPLEN-!small;
        y += 8;
    }

    rotatesprite((x+(small?4:8))<<16,((y+(small?0:4))<<16)+(small?ScreenHeight<<15:0),32768,0,SPINNINGNUKEICON+((totalclock>>3)%7),c,0,small?(8|16):2+8,0,0,xdim-1,ydim-1);
    return (0);
}

inline short strget(short x,short y,char *t,short dalen,short c)
{
    return(strget_(0,x,y,t,dalen,c));
}

inline short strgetsm(short x,short y,char *t,short dalen,short c)
{
    return(strget_(1,x,y,t,dalen,c));
}

inline short mpstrget(short x,short y,char *t,short dalen,short c)
{
    if(xdim >= 640 && ydim >= 480)
        return(strgetsm(x,y,t,dalen,c));
    else return(strget(x,y,t,dalen,c));
}

void typemode(void)
{
    short ch, hitstate, i, j, l;

    if( ps[myconnectindex].gm&MODE_SENDTOWHOM )
    {
        if(sendmessagecommand != -1 || ud.multimode < 3 || movesperpacket == 4)
        {
            tempbuf[0] = 4;
            tempbuf[2] = 0;
            recbuf[0]  = 0;

            if(ud.multimode < 3)
                sendmessagecommand = 2;

            if(typebuf[0] == '/' && Btoupper(typebuf[1]) == 'M' && Btoupper(typebuf[2]) == 'E')
            {
                strcat(recbuf,"* ");
                i = 3, j = Bstrlen(typebuf);
                Bstrcpy(tempbuf,typebuf);
                while(i < j)
                {
                    typebuf[i-3] = tempbuf[i];
                    i++;
                }
                typebuf[i-3] = '\0';
                strcat(recbuf,ud.user_name[myconnectindex]);
            }
            else
            {
                strcat(recbuf,ud.user_name[myconnectindex]);
                strcat(recbuf,": ");
            }

            strcat(recbuf,"^0");
            strcat(recbuf,typebuf);
            j = strlen(recbuf);
            recbuf[j] = 0;
            strcat(tempbuf+2,recbuf);

            if(sendmessagecommand >= ud.multimode || movesperpacket == 4)
            {
                tempbuf[1] = 255;
                for(ch=connecthead;ch >= 0;ch=connectpoint2[ch])
                {
                    if (ch != myconnectindex) sendpacket(ch,tempbuf,j+2);
                    if ((!networkmode) && (myconnectindex != connecthead)) break; //slaves in M/S mode only send to master
                }
                adduserquote(recbuf);
                quotebot += 8;
                l = Bstrlen(recbuf);
                while(l > TEXTWRAPLEN)
                {
                    l -= TEXTWRAPLEN;
                    quotebot += 8;
                }
                quotebotgoal = quotebot;
            }
            else if(sendmessagecommand >= 0)
            {
                tempbuf[1] = (char)sendmessagecommand;
                if ((!networkmode) && (myconnectindex != connecthead))
                    sendmessagecommand = connecthead;
                sendpacket(sendmessagecommand,tempbuf,j+2);
            }

            sendmessagecommand = -1;
            ps[myconnectindex].gm &= ~(MODE_TYPE|MODE_SENDTOWHOM);
        }
        else if(sendmessagecommand == -1)
        {
            j = 50;
            gametext(320>>1,j,"SEND MESSAGE TO...",0,2+8+16); j += 8;
            for(i=connecthead;i>=0;i=connectpoint2[i])
            {
                if (i == myconnectindex)
                {
                    minitextshade((320>>1)-40+1,j+1,"A/ENTER - ALL",26,0,2+8+16);
                    minitext((320>>1)-40,j,"A/ENTER - ALL",0,2+8+16); j += 7;
                }
                else
                {
                    Bsprintf(buf,"      %d - %s",i+1,ud.user_name[i]);
                    minitextshade((320>>1)-40-6+1,j+1,buf,26,0,2+8+16);
                    minitext((320>>1)-40-6,j,buf,0,2+8+16); j += 7;
                }
            }
            minitextshade((320>>1)-40-4+1,j+1,"    ESC - Abort",26,0,2+8+16);
            minitext((320>>1)-40-4,j,"    ESC - Abort",0,2+8+16); j += 7;

            if (ud.screen_size > 0) j = 200-45; else j = 200-8;
            mpgametext(j,typebuf,0,2+8+16);

            if( KB_KeyWaiting() )
            {
                i = KB_GetCh();

                if(i == 'A' || i == 'a' || i == 13)
                    sendmessagecommand = ud.multimode;
                else if(i >= '1' || i <= (ud.multimode + '1') )
                    sendmessagecommand = i - '1';
                else
                {
                    sendmessagecommand = ud.multimode;
                    if(i == 27)
                    {
                        ps[myconnectindex].gm &= ~(MODE_TYPE|MODE_SENDTOWHOM);
                        sendmessagecommand = -1;
                    }
                    else
                        typebuf[0] = 0;
                }

                KB_ClearKeyDown(sc_1);
                KB_ClearKeyDown(sc_2);
                KB_ClearKeyDown(sc_3);
                KB_ClearKeyDown(sc_4);
                KB_ClearKeyDown(sc_5);
                KB_ClearKeyDown(sc_6);
                KB_ClearKeyDown(sc_7);
                KB_ClearKeyDown(sc_8);
                KB_ClearKeyDown(sc_A);
                KB_ClearKeyDown(sc_Escape);
                KB_ClearKeyDown(sc_Enter);
            }
        }
    }
    else
    {
        if(ud.screen_size > 0) j = 200-45; else j = 200-8;
        hitstate = mpstrget(320>>1,j,typebuf,120,1);

        if(hitstate == 1)
        {
            KB_ClearKeyDown(sc_Enter);
            if(ud.automsg)
            {
                if(SHIFTS_IS_PRESSED) sendmessagecommand = -1;
                else sendmessagecommand = ud.multimode;
            }
            ps[myconnectindex].gm |= MODE_SENDTOWHOM;
        }
        else if(hitstate == -1)
            ps[myconnectindex].gm &= ~(MODE_TYPE|MODE_SENDTOWHOM);
        else pub = NUMPAGES;
    }
}

void moveclouds(void)
{
    if( totalclock > cloudtotalclock || totalclock < (cloudtotalclock-7))
    {
        short i;

        cloudtotalclock = totalclock+6;

        for(i=0;i<numclouds;i++)
        {
            cloudx[i] += (sintable[(ps[screenpeek].ang+512)&2047]>>9);
            cloudy[i] += (sintable[ps[screenpeek].ang&2047]>>9);

            sector[clouds[i]].ceilingxpanning = cloudx[i]>>6;
            sector[clouds[i]].ceilingypanning = cloudy[i]>>6;
        }
    }
}

void drawoverheadmap(long cposx, long cposy, long czoom, short cang)
{
    long i, j, k, l, x1, y1, x2=0, y2=0, x3, y3, x4, y4, ox, oy, xoff, yoff;
    long dax, day, cosang, sinang, xspan, yspan, sprx, spry;
    long xrepeat, yrepeat, z1, z2, startwall, endwall, tilenum, daang;
    long xvect, yvect, xvect2, yvect2;
    short p;
    char col;
    walltype *wal, *wal2;
    spritetype *spr;

    xvect = sintable[(-cang)&2047] * czoom;
    yvect = sintable[(1536-cang)&2047] * czoom;
    xvect2 = mulscale16(xvect,yxaspect);
    yvect2 = mulscale16(yvect,yxaspect);

    //Draw red lines
    for(i=0;i<numsectors;i++)
    {
        if (!(show2dsector[i>>3]&(1<<(i&7)))) continue;

        startwall = sector[i].wallptr;
        endwall = sector[i].wallptr + sector[i].wallnum;

        z1 = sector[i].ceilingz; z2 = sector[i].floorz;

        for(j=startwall,wal=&wall[startwall];j<endwall;j++,wal++)
        {
            k = wal->nextwall; if (k < 0) continue;

            //if ((show2dwall[j>>3]&(1<<(j&7))) == 0) continue;
            //if ((k > j) && ((show2dwall[k>>3]&(1<<(k&7))) > 0)) continue;

            if (sector[wal->nextsector].ceilingz == z1)
                if (sector[wal->nextsector].floorz == z2)
                    if (((wal->cstat|wall[wal->nextwall].cstat)&(16+32)) == 0) continue;

            col = 139; //red
            if ((wal->cstat|wall[wal->nextwall].cstat)&1) col = 234; //magenta

            if (!(show2dsector[wal->nextsector>>3]&(1<<(wal->nextsector&7))))
                col = 24;
            else continue;

            ox = wal->x-cposx; oy = wal->y-cposy;
            x1 = dmulscale16(ox,xvect,-oy,yvect)+(xdim<<11);
            y1 = dmulscale16(oy,xvect2,ox,yvect2)+(ydim<<11);

            wal2 = &wall[wal->point2];
            ox = wal2->x-cposx; oy = wal2->y-cposy;
            x2 = dmulscale16(ox,xvect,-oy,yvect)+(xdim<<11);
            y2 = dmulscale16(oy,xvect2,ox,yvect2)+(ydim<<11);

            drawline256(x1,y1,x2,y2,col);
        }
    }

    //Draw sprites
    k = ps[screenpeek].i;
    for(i=0;i<numsectors;i++)
    {
        if (!(show2dsector[i>>3]&(1<<(i&7)))) continue;
        for(j=headspritesect[i];j>=0;j=nextspritesect[j])
            //if ((show2dsprite[j>>3]&(1<<(j&7))) > 0)
        {
            spr = &sprite[j];

            if (j == k || (spr->cstat&0x8000) || spr->cstat == 257 || spr->xrepeat == 0) continue;

            col = 71; //cyan
            if (spr->cstat&1) col = 234; //magenta

            sprx = spr->x;
            spry = spr->y;

            if( (spr->cstat&257) != 0) switch (spr->cstat&48)
                {
                case 0: break;

                    ox = sprx-cposx; oy = spry-cposy;
                    x1 = dmulscale16(ox,xvect,-oy,yvect);
                    y1 = dmulscale16(oy,xvect2,ox,yvect2);

                    ox = (sintable[(spr->ang+512)&2047]>>7);
                    oy = (sintable[(spr->ang)&2047]>>7);
                    x2 = dmulscale16(ox,xvect,-oy,yvect);
                    y2 = dmulscale16(oy,xvect,ox,yvect);

                    x3 = mulscale16(x2,yxaspect);
                    y3 = mulscale16(y2,yxaspect);

                    drawline256(x1-x2+(xdim<<11),y1-y3+(ydim<<11),
                                x1+x2+(xdim<<11),y1+y3+(ydim<<11),col);
                    drawline256(x1-y2+(xdim<<11),y1+x3+(ydim<<11),
                                x1+x2+(xdim<<11),y1+y3+(ydim<<11),col);
                    drawline256(x1+y2+(xdim<<11),y1-x3+(ydim<<11),
                                x1+x2+(xdim<<11),y1+y3+(ydim<<11),col);
                    break;

                case 16:
                    if( spr->picnum == LASERLINE )
                    {
                        x1 = sprx; y1 = spry;
                        tilenum = spr->picnum;
                        xoff = (long)((signed char)((picanm[tilenum]>>8)&255))+((long)spr->xoffset);
                        if ((spr->cstat&4) > 0) xoff = -xoff;
                        k = spr->ang; l = spr->xrepeat;
                        dax = sintable[k&2047]*l; day = sintable[(k+1536)&2047]*l;
                        l = tilesizx[tilenum]; k = (l>>1)+xoff;
                        x1 -= mulscale16(dax,k); x2 = x1+mulscale16(dax,l);
                        y1 -= mulscale16(day,k); y2 = y1+mulscale16(day,l);

                        ox = x1-cposx; oy = y1-cposy;
                        x1 = dmulscale16(ox,xvect,-oy,yvect);
                        y1 = dmulscale16(oy,xvect2,ox,yvect2);

                        ox = x2-cposx; oy = y2-cposy;
                        x2 = dmulscale16(ox,xvect,-oy,yvect);
                        y2 = dmulscale16(oy,xvect2,ox,yvect2);

                        drawline256(x1+(xdim<<11),y1+(ydim<<11),
                                    x2+(xdim<<11),y2+(ydim<<11),col);
                    }

                    break;

                case 32:

                    tilenum = spr->picnum;
                    xoff = (long)((signed char)((picanm[tilenum]>>8)&255))+((long)spr->xoffset);
                    yoff = (long)((signed char)((picanm[tilenum]>>16)&255))+((long)spr->yoffset);
                    if ((spr->cstat&4) > 0) xoff = -xoff;
                    if ((spr->cstat&8) > 0) yoff = -yoff;

                    k = spr->ang;
                    cosang = sintable[(k+512)&2047]; sinang = sintable[k];
                    xspan = tilesizx[tilenum]; xrepeat = spr->xrepeat;
                    yspan = tilesizy[tilenum]; yrepeat = spr->yrepeat;

                    dax = ((xspan>>1)+xoff)*xrepeat; day = ((yspan>>1)+yoff)*yrepeat;
                    x1 = sprx + dmulscale16(sinang,dax,cosang,day);
                    y1 = spry + dmulscale16(sinang,day,-cosang,dax);
                    l = xspan*xrepeat;
                    x2 = x1 - mulscale16(sinang,l);
                    y2 = y1 + mulscale16(cosang,l);
                    l = yspan*yrepeat;
                    k = -mulscale16(cosang,l); x3 = x2+k; x4 = x1+k;
                    k = -mulscale16(sinang,l); y3 = y2+k; y4 = y1+k;

                    ox = x1-cposx; oy = y1-cposy;
                    x1 = dmulscale16(ox,xvect,-oy,yvect);
                    y1 = dmulscale16(oy,xvect2,ox,yvect2);

                    ox = x2-cposx; oy = y2-cposy;
                    x2 = dmulscale16(ox,xvect,-oy,yvect);
                    y2 = dmulscale16(oy,xvect2,ox,yvect2);

                    ox = x3-cposx; oy = y3-cposy;
                    x3 = dmulscale16(ox,xvect,-oy,yvect);
                    y3 = dmulscale16(oy,xvect2,ox,yvect2);

                    ox = x4-cposx; oy = y4-cposy;
                    x4 = dmulscale16(ox,xvect,-oy,yvect);
                    y4 = dmulscale16(oy,xvect2,ox,yvect2);

                    drawline256(x1+(xdim<<11),y1+(ydim<<11),
                                x2+(xdim<<11),y2+(ydim<<11),col);

                    drawline256(x2+(xdim<<11),y2+(ydim<<11),
                                x3+(xdim<<11),y3+(ydim<<11),col);

                    drawline256(x3+(xdim<<11),y3+(ydim<<11),
                                x4+(xdim<<11),y4+(ydim<<11),col);

                    drawline256(x4+(xdim<<11),y4+(ydim<<11),
                                x1+(xdim<<11),y1+(ydim<<11),col);

                    break;
                }
        }
    }

    //Draw white lines
    for(i=0;i<numsectors;i++)
    {
        if (!(show2dsector[i>>3]&(1<<(i&7)))) continue;

        startwall = sector[i].wallptr;
        endwall = sector[i].wallptr + sector[i].wallnum;

        k = -1;
        for(j=startwall,wal=&wall[startwall];j<endwall;j++,wal++)
        {
            if (wal->nextwall >= 0) continue;

            //if ((show2dwall[j>>3]&(1<<(j&7))) == 0) continue;

            if (tilesizx[wal->picnum] == 0) continue;
            if (tilesizy[wal->picnum] == 0) continue;

            if (j == k)
            { x1 = x2; y1 = y2; }
            else
            {
                ox = wal->x-cposx; oy = wal->y-cposy;
                x1 = dmulscale16(ox,xvect,-oy,yvect)+(xdim<<11);
                y1 = dmulscale16(oy,xvect2,ox,yvect2)+(ydim<<11);
            }

            k = wal->point2; wal2 = &wall[k];
            ox = wal2->x-cposx; oy = wal2->y-cposy;
            x2 = dmulscale16(ox,xvect,-oy,yvect)+(xdim<<11);
            y2 = dmulscale16(oy,xvect2,ox,yvect2)+(ydim<<11);

            drawline256(x1,y1,x2,y2,24);
        }
    }

    for(p=connecthead;p >= 0;p=connectpoint2[p])
    {
        if(ud.scrollmode && p == screenpeek) continue;

        ox = sprite[ps[p].i].x-cposx; oy = sprite[ps[p].i].y-cposy;
        daang = (sprite[ps[p].i].ang-cang)&2047;
    if (p == screenpeek) { ox = 0; oy = 0; daang = 0; }
        x1 = mulscale(ox,xvect,16) - mulscale(oy,yvect,16);
        y1 = mulscale(oy,xvect2,16) + mulscale(ox,yvect2,16);

        if(p == screenpeek || (gametype_flags[ud.coop] & GAMETYPE_FLAG_OTHERPLAYERSINMAP ) )
        {
            if(sprite[ps[p].i].xvel > 16 && ps[p].on_ground)
                i = APLAYERTOP+((totalclock>>4)&3);
            else
                i = APLAYERTOP;

            j = klabs(ps[p].truefz-ps[p].posz)>>8;
            j = mulscale(czoom*(sprite[ps[p].i].yrepeat+j),yxaspect,16);

            if(j < 22000) j = 22000;
            else if(j > (65536<<1)) j = (65536<<1);

            rotatesprite((x1<<4)+(xdim<<15),(y1<<4)+(ydim<<15),j,
                         daang,i,sprite[ps[p].i].shade,/*sprite[ps[p].i].pal*/sector[ps[p].cursectnum].floorpal,
                         (sprite[ps[p].i].cstat&2)>>1,windowx1,windowy1,windowx2,windowy2);
        }
    }
}

void palto(char r,char g,char b,long e)
{
    long tc;
    /*
        for(i=0;i<768;i+=3)
        {
            temparray[i  ] =
                ps[myconnectindex].palette[i+0]+((((long)r-(long)ps[myconnectindex].palette[i+0])*(long)(e&127))>>6);
            temparray[i+1] =
                ps[myconnectindex].palette[i+1]+((((long)g-(long)ps[myconnectindex].palette[i+1])*(long)(e&127))>>6);
            temparray[i+2] =
                ps[myconnectindex].palette[i+2]+((((long)b-(long)ps[myconnectindex].palette[i+2])*(long)(e&127))>>6);
        }
    */


    //setbrightness(ud.brightness>>2,temparray);
    setpalettefade(r,g,b,e&127);
    if (getrendermode() >= 3) pus = pub = NUMPAGES;     // JBF 20040110: redraw the status bar next time
    if ((e&128) == 0) {
        nextpage();
        for (tc = totalclock; totalclock < tc + 4; handleevents(), getpackets() );
    }
}

void displayrest(long smoothratio)
{
    long a, i, j;
    char fader=0,fadeg=0,fadeb=0,fadef=0,tintr=0,tintg=0,tintb=0,tintf=0,dotint=0;

    struct player_struct *pp;
    walltype *wal;
    long cposx,cposy,cang;

    pp = &ps[screenpeek];

    // this takes care of fullscreen tint for OpenGL
    if (getrendermode() >= 3) {
        if (pp->palette == waterpal) tintr=0,tintg=0,tintb=63,tintf=8;
        else if (pp->palette == slimepal) tintr=0,tintg=63,tintb=0,tintf=8;
    }

    // this does pain tinting etc from the CON
    if( pp->pals_time >= 0 && pp->loogcnt == 0) // JBF 20040101: pals_time > 0 now >= 0
    {
        fader = pp->pals[0];
        fadeg = pp->pals[1];
        fadeb = pp->pals[2];
        fadef = pp->pals_time;
        restorepalette = 1;             // JBF 20040101
        dotint = 1;
    }
    // reset a normal palette
    else if( restorepalette )
    {
        //setbrightness(ud.brightness>>2,&pp->palette[0],0);
        setgamepalette(pp,pp->palette,0);
        restorepalette = 0;
    }
    // loogies courtesy of being snotted on
    else if(pp->loogcnt > 0) {
        //palto(0,64,0,(pp->loogcnt>>1)+128);
        fader = 0;
        fadeg = 64;
        fadeb = 0;
        fadef = pp->loogcnt>>1;
        dotint = 1;
    }
    if (fadef > tintf) {
        tintr = fader;
        tintg = fadeg;
        tintb = fadeb;
        tintf = fadef;
    }

    if(ud.show_help)
    {
        switch(ud.show_help)
        {
        case 1:
            rotatesprite(0,0,65536L,0,TEXTSTORY,0,0,10+16+64, 0,0,xdim-1,ydim-1);
            break;
        case 2:
            rotatesprite(0,0,65536L,0,F1HELP,0,0,10+16+64, 0,0,xdim-1,ydim-1);
            break;
        }

        if ( KB_KeyPressed(sc_Escape ) )
        {
            KB_ClearKeyDown(sc_Escape);
            ud.show_help = 0;
            if(ud.multimode < 2 && ud.recstat != 2)
            {
                ready2send = 1;
                totalclock = ototalclock;
            }
            vscrn();
        }
        if (tintf > 0 || dotint) palto(tintr,tintg,tintb,tintf|128);
        return;
    }

    i = pp->cursectnum;

    show2dsector[i>>3] |= (1<<(i&7));
    wal = &wall[sector[i].wallptr];
    for(j=sector[i].wallnum;j>0;j--,wal++)
    {
        i = wal->nextsector;
        if (i < 0) continue;
        if (wal->cstat&0x0071) continue;
        if (wall[wal->nextwall].cstat&0x0071) continue;
        if (sector[i].lotag == 32767) continue;
        if (sector[i].ceilingz >= sector[i].floorz) continue;
        show2dsector[i>>3] |= (1<<(i&7));
    }

    if(ud.camerasprite == -1)
    {
        if( ud.overhead_on != 2 )
        {
            if(pp->newowner >= 0)
                cameratext(pp->newowner);
            else
            {
                displayweapon(screenpeek);
                if(pp->over_shoulder_on == 0 )
                    displaymasks(screenpeek);
            }
            moveclouds();
        }

        if( ud.overhead_on > 0 )
        {
            smoothratio = min(max(smoothratio,0),65536);
            dointerpolations(smoothratio);
            if( ud.scrollmode == 0 )
            {
                if(pp->newowner == -1 && !ud.pause_on)
                {
                    if (screenpeek == myconnectindex && numplayers > 1)
                    {
                        cposx = omyx+mulscale16((long)(myx-omyx),smoothratio);
                        cposy = omyy+mulscale16((long)(myy-omyy),smoothratio);
                        cang = omyang+mulscale16((long)(((myang+1024-omyang)&2047)-1024),smoothratio);
                    }
                    else
                    {
                        cposx = pp->oposx+mulscale16((long)(pp->posx-pp->oposx),smoothratio);
                        cposy = pp->oposy+mulscale16((long)(pp->posy-pp->oposy),smoothratio);
                        cang = pp->oang+mulscale16((long)(((pp->ang+1024-pp->oang)&2047)-1024),smoothratio);
                    }
                }
                else
                {
                    cposx = pp->oposx;
                    cposy = pp->oposy;
                    cang = pp->oang;
                }
            }
            else
            {
                if(!ud.pause_on)
                {
                    ud.fola += ud.folavel>>3;
                    ud.folx += (ud.folfvel*sintable[(512+2048-ud.fola)&2047])>>14;
                    ud.foly += (ud.folfvel*sintable[(512+1024-512-ud.fola)&2047])>>14;
                }
                cposx = ud.folx;
                cposy = ud.foly;
                cang = ud.fola;
            }

            if(ud.overhead_on == 2)
            {
                clearview(0L);
                drawmapview(cposx,cposy,pp->zoom,cang);
            }
            drawoverheadmap( cposx,cposy,pp->zoom,cang);

            restoreinterpolations();

            if(ud.overhead_on == 2)
            {
                if(ud.screen_size > 0) a = 147;
                else a = 182;

                minitext(1,a+6,volume_names[ud.volume_number],0,2+8+16);
                minitext(1,a+12,level_names[ud.volume_number*11 + ud.level_number],0,2+8+16);
            }
        }
    }

    coolgaugetext(screenpeek);
    operatefta();

    if( KB_KeyPressed(sc_Escape) && ud.overhead_on == 0
            && ud.show_help == 0
            && ps[myconnectindex].newowner == -1)
    {
        if( (ps[myconnectindex].gm&MODE_MENU) == MODE_MENU && current_menu < 51)
        {
            KB_ClearKeyDown(sc_Escape);
            ps[myconnectindex].gm &= ~MODE_MENU;
            if(ud.multimode < 2 && ud.recstat != 2)
            {
                ready2send = 1;
                totalclock = ototalclock;
                cameraclock = totalclock;
                cameradist = 65536L;
            }
            walock[TILE_SAVESHOT] = 199;
            vscrn();
        }
        else if( (ps[myconnectindex].gm&MODE_MENU) != MODE_MENU &&
                 ps[myconnectindex].newowner == -1 &&
                 (ps[myconnectindex].gm&MODE_TYPE) != MODE_TYPE)
        {
            KB_ClearKeyDown(sc_Escape);
            FX_StopAllSounds();
            clearsoundlocks();

            intomenusounds();

            ps[myconnectindex].gm |= MODE_MENU;

            if(ud.multimode < 2 && ud.recstat != 2) ready2send = 0;

            if(ps[myconnectindex].gm&MODE_GAME) cmenu(50);
            else cmenu(0);
            screenpeek = myconnectindex;
        }
    }

    OnEvent(EVENT_DISPLAYREST, ps[screenpeek].i, screenpeek, -1);

    if(ps[myconnectindex].newowner == -1 && ud.overhead_on == 0 && ud.crosshair && ud.camerasprite == -1)
    {
        SetGameVarID(g_iReturnVarID,0,ps[screenpeek].i,screenpeek);
        OnEvent(EVENT_DISPLAYCROSSHAIR, ps[screenpeek].i, screenpeek, -1);
        if(GetGameVarID(g_iReturnVarID,ps[screenpeek].i,screenpeek) == 0)
            rotatesprite((160L-(ps[myconnectindex].look_ang>>1))<<16,100L<<16,ud.crosshair>1?65536L>>(ud.crosshair-1):65536L,0,CROSSHAIR,0,0,2+1,windowx1,windowy1,windowx2,windowy2);
    }

    if(ps[myconnectindex].gm&MODE_TYPE)
        typemode();
    else
        menus();

    if( ud.pause_on==1 && (ps[myconnectindex].gm&MODE_MENU) == 0 )
        menutext(160,100,0,0,"GAME PAUSED");

    if(ud.coords)
        coords(screenpeek);

    tics();

    // JBF 20040124: display level stats in screen corner
    if(ud.levelstats && (ps[myconnectindex].gm&MODE_MENU) == 0) {
        i = (ud.screen_size <= 4)?0:scale(tilesizy[BOTTOMSTATUSBAR],ud.statusbarscale,100);

        Bsprintf(tempbuf,"Time: %ld:%02ld",
                 (ps[myconnectindex].player_par/(26*60)),
                 (ps[myconnectindex].player_par/26)%60);
        minitext(320-5*12,200-i-6-6-6,tempbuf,0,26);

        if(ud.player_skill > 3 || (ud.multimode > 1 && !(gametype_flags[ud.coop] & GAMETYPE_FLAG_PLAYERSFRIENDLY)))
            Bsprintf(tempbuf,"Kills: %ld",ud.multimode>1?ps[i].frag-ps[i].fraggedself:ps[myconnectindex].actors_killed);
        else
            Bsprintf(tempbuf,"Kills: %ld/%ld",ps[myconnectindex].actors_killed,
                     ps[myconnectindex].max_actors_killed>ps[myconnectindex].actors_killed?
                     ps[myconnectindex].max_actors_killed:ps[myconnectindex].actors_killed);
        minitext(320-5*12,200-i-6-6,tempbuf,0,26);

        Bsprintf(tempbuf,"Secrets: %ld/%ld", ps[myconnectindex].secret_rooms,ps[myconnectindex].max_secret_rooms);
        minitext(320-5*12,200-i-6,tempbuf,0,26);
    }
    if (tintf > 0 || dotint) palto(tintr,tintg,tintb,tintf|128);
}

void view(struct player_struct *pp, long *vx, long *vy,long *vz,short *vsectnum, short ang, short horiz)
{
    spritetype *sp;
    long i, nx, ny, nz, hx, hy, hitx, hity, hitz;
    short bakcstat, hitsect, hitwall, hitsprite, daang;

    nx = (sintable[(ang+1536)&2047]>>4);
    ny = (sintable[(ang+1024)&2047]>>4);
    nz = (horiz-100)*128;

    sp = &sprite[pp->i];

    bakcstat = sp->cstat;
    sp->cstat &= (short)~0x101;

    updatesectorz(*vx,*vy,*vz,vsectnum);
    hitscan(*vx,*vy,*vz,*vsectnum,nx,ny,nz,&hitsect,&hitwall,&hitsprite,&hitx,&hity,&hitz,CLIPMASK1);

    if(*vsectnum < 0)
    {
        sp->cstat = bakcstat;
        return;
    }

    hx = hitx-(*vx); hy = hity-(*vy);
    if (klabs(nx)+klabs(ny) > klabs(hx)+klabs(hy))
    {
        *vsectnum = hitsect;
        if (hitwall >= 0)
        {
            daang = getangle(wall[wall[hitwall].point2].x-wall[hitwall].x,
                             wall[wall[hitwall].point2].y-wall[hitwall].y);

            i = nx*sintable[daang]+ny*sintable[(daang+1536)&2047];
            if (klabs(nx) > klabs(ny)) hx -= mulscale28(nx,i);
            else hy -= mulscale28(ny,i);
        }
        else if (hitsprite < 0)
        {
            if (klabs(nx) > klabs(ny)) hx -= (nx>>5);
            else hy -= (ny>>5);
        }
        if (klabs(nx) > klabs(ny)) i = divscale16(hx,nx);
        else i = divscale16(hy,ny);
        if (i < cameradist) cameradist = i;
    }
    *vx = (*vx)+mulscale16(nx,cameradist);
    *vy = (*vy)+mulscale16(ny,cameradist);
    *vz = (*vz)+mulscale16(nz,cameradist);

    cameradist = min(cameradist+((totalclock-cameraclock)<<10),65536);
    cameraclock = totalclock;

    updatesectorz(*vx,*vy,*vz,vsectnum);

    sp->cstat = bakcstat;
}

//REPLACE FULLY
void drawbackground(void)
{
    short dapicnum;
    long x,y,x1,y1,x2,y2,rx;

    flushperms();

    switch(ud.m_volume_number)
    {
    default:dapicnum = BIGHOLE;break;
    case 1:dapicnum = BIGHOLE;break;
    case 2:dapicnum = BIGHOLE;break;
    }

    if (tilesizx[dapicnum] == 0 || tilesizy[dapicnum] == 0) {
        pus = pub = NUMPAGES;
        return;
    }

    y1 = 0; y2 = ydim;
    if( (ready2send && ps[myconnectindex].gm == MODE_GAME) || ud.recstat == 2 )
        //if (ud.recstat == 0 || ud.recstat == 1 || (ud.recstat == 2 && ud.reccnt > 0)) // JBF 20040717
    {
        if (ud.screen_size == 8)
            y1 = scale(ydim,200-scale(tilesizy[BOTTOMSTATUSBAR],ud.statusbarscale,100),200);
        else if((gametype_flags[ud.coop] & GAMETYPE_FLAG_FRAGBAR))
        {
            if (ud.multimode > 1) y1 += scale(ydim,8,200);
            if (ud.multimode > 4) y1 += scale(ydim,8,200);
        }
    } else {
        // when not rendering a game, fullscreen wipe
        #define MENUTILE bpp==8?MENUSCREEN:LOADSCREEN
        SetGameVarID(g_iReturnVarID,tilesizx[MENUTILE]==320&&tilesizy[MENUTILE]==200?MENUTILE:BIGHOLE, -1, -1);
        OnEvent(EVENT_GETMENUTILE, -1, -1, -1);
        if (GetGameVar("MENU_TILE", tilesizx[MENUTILE]==320&&tilesizy[MENUTILE]==200?0:1, -1, -1))
        {
            for(y=y1;y<y2;y+=tilesizy[GetGameVarID(g_iReturnVarID, -1, -1)])
                for(x=0;x<xdim;x+=tilesizx[GetGameVarID(g_iReturnVarID, -1, -1)])
                    rotatesprite(x<<16,y<<16,65536L,0,GetGameVarID(g_iReturnVarID, -1, -1),bpp==8?16:8,0,8+16+64+128,0,0,xdim-1,ydim-1);
        }
        else rotatesprite(320<<15,200<<15,65536L,0,GetGameVarID(g_iReturnVarID, -1, -1),bpp==8?16:8,0,2+8+64,0,0,xdim-1,ydim-1);
        return;
    }
    y2 = scale(ydim,200-scale(tilesizy[BOTTOMSTATUSBAR],ud.statusbarscale,100),200);

    if (ud.screen_size > 8) {
        // across top
        for (y=0; y<windowy1; y+=tilesizy[dapicnum])
            for (x=0; x<xdim; x+=tilesizx[dapicnum])
                rotatesprite(x<<16,y<<16,65536L,0,dapicnum,8,0,8+16+64+128,0,y1,xdim-1,windowy1-1);

        // sides
        rx = windowx2-windowx2%tilesizx[dapicnum];
        for (y=windowy1-windowy1%tilesizy[dapicnum]; y<windowy2; y+=tilesizy[dapicnum])
            for (x=0; x<windowx1 || x+rx<xdim; x+=tilesizx[dapicnum]) {
                rotatesprite(x<<16,y<<16,65536L,0,dapicnum,8,0,8+16+64+128,0,windowy1,windowx1-1,windowy2-1);
                rotatesprite((x+rx)<<16,y<<16,65536L,0,dapicnum,8,0,8+16+64+128,windowx2,windowy1,xdim-1,windowy2-1);
            }

        // along bottom
        for (y=windowy2-(windowy2%tilesizy[dapicnum]); y<y2; y+=tilesizy[dapicnum])
            for (x=0; x<xdim; x+=tilesizx[dapicnum])
                rotatesprite(x<<16,y<<16,65536L,0,dapicnum,8,0,8+16+64+128,0,windowy2,xdim-1,y2-1);
    }

    // draw in the bits to the left and right of the non-fullsize status bar
    if (ud.statusbarscale < 100) {
        y1 = y2;
        x2 = (xdim - scale(xdim,ud.statusbarscale,100)) >> 1;
        x1 = xdim-x2; x1 -= x1%tilesizx[dapicnum];
        for(y=y1-y1%tilesizy[dapicnum]; y<y2; y+=tilesizy[dapicnum])
            for(x=0;x<x2 || x1+x<xdim; x+=tilesizx[dapicnum]) {
                rotatesprite(x<<16,y<<16,65536L,0,dapicnum,8,0,8+16+64+128,0,y1,x2-1,ydim-1);
                rotatesprite((x+x1)<<16,y<<16,65536L,0,dapicnum,8,0,8+16+64+128,xdim-x2,y1,xdim-1,ydim-1);
            }

    }

    if(ud.screen_size > 8 && ( ready2send || ud.recstat == 2 ))
    {
        y = 0;
        if((gametype_flags[ud.coop] & GAMETYPE_FLAG_FRAGBAR))
        {
            if (ud.multimode > 1) y += 8;
            if (ud.multimode > 4) y += 8;
        }

        x1 = max(windowx1-4,0);
        y1 = max(windowy1-4,y);
        x2 = min(windowx2+4,xdim-1);
        y2 = min(windowy2+4,scale(ydim,200-scale(tilesizy[BOTTOMSTATUSBAR],ud.statusbarscale,100),200)-1);

        for(y=y1+4;y<y2-4;y+=64)
        {
            rotatesprite(x1<<16,y<<16,65536L,0,VIEWBORDER,0,0,8+16+64+128,x1,y1,x2,y2);
            rotatesprite((x2+1)<<16,(y+64)<<16,65536L,1024,VIEWBORDER,0,0,8+16+64+128,x1,y1,x2,y2);
        }

        for(x=x1+4;x<x2-4;x+=64)
        {
            rotatesprite((x+64)<<16,y1<<16,65536L,512,VIEWBORDER,0,0,8+16+64+128,x1,y1,x2,y2);
            rotatesprite(x<<16,(y2+1)<<16,65536L,1536,VIEWBORDER,0,0,8+16+64+128,x1,y1,x2,y2);
        }

        rotatesprite(x1<<16,y1<<16,65536L,0,VIEWBORDER+1,0,0,8+16+64+128,x1,y1,x2,y2);
        rotatesprite((x2+1)<<16,y1<<16,65536L,512,VIEWBORDER+1,0,0,8+16+64+128,x1,y1,x2,y2);
        rotatesprite((x2+1)<<16,(y2+1)<<16,65536L,1024,VIEWBORDER+1,0,0,8+16+64+128,x1,y1,x2,y2);
        rotatesprite(x1<<16,(y2+1)<<16,65536L,1536,VIEWBORDER+1,0,0,8+16+64+128,x1,y1,x2,y2);
    }

    pus = pub = NUMPAGES;
}

#define SE40

#ifdef SE40
// Floor Over Floor

// 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.

static void SE40_Draw(int spnum,long x,long y,long z,short a,short h,long smoothratio)
{
    static long tempsectorz[MAXSECTORS];
    static long tempsectorpicnum[MAXSECTORS];

    int i=0,j=0,k=0;
    int floor1=0,floor2=0,ok=0,fofmode=0,draw_both=0;
    long 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
    // Additionally names.h also defines FOF as 13 which isn't useful for us
    // so we'll use 562 instead
    tilesizx[562] = 0;
    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? - Not at the moment
    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=headspritestat[15];j>=0;j=nextspritestat[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=headspritestat[15];j>=0;j=nextspritestat[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=headspritestat[15];j>=0;j=nextspritestat[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);
    animatesprites(x,y,a,smoothratio);
    drawmasks();

    if (draw_both)
    {
        for(j=headspritestat[15];j>=0;j=nextspritestat[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);
        animatesprites(x,y,a,smoothratio);
        drawmasks();
    }
} // end SE40

static void se40code(long x,long y,long z,long a,long h, long smoothratio)
{
    int i;

    i = headspritestat[15];
    while(i >= 0)
    {
        int t = sprite[i].lotag;
        switch(t)
        {
            //            case 40:
            //            case 41:
            //                SE40_Draw(i,x,y,a,smoothratio);
            //                break;
        case 42:
        case 43:
        case 44:
        case 45:
            if(ps[screenpeek].cursectnum == sprite[i].sectnum)
                SE40_Draw(i,x,y,z,a,h,smoothratio);
            break;
        }
        i = nextspritestat[i];
    }
}
#endif

static long oyrepeat=-1;

void displayrooms(short snum,long smoothratio)
{
    long cposx,cposy,cposz,dst,j,fz,cz;
    short sect, cang, k, choriz;
    struct player_struct *p;
    long tposx,tposy,i;
    short tang;
    long tiltcx,tiltcy,tiltcs=0;    // JBF 20030807

    p = &ps[snum];

    if(pub > 0 || getrendermode() >= 3) // JBF 20040101: redraw background always
    {
        if(ud.screen_size > 8 || (ud.screen_size == 8 && ud.statusbarscale<100)) drawbackground();
        pub = 0;
    }

    if( ud.overhead_on == 2 || ud.show_help || p->cursectnum == -1)
        return;

    smoothratio = min(max(smoothratio,0),65536);

    visibility = p->visibility;

    if(ud.pause_on || ps[snum].on_crane > -1) smoothratio = 65536;

    sect = p->cursectnum;
    if(sect < 0 || sect >= MAXSECTORS) return;

    dointerpolations(smoothratio);

    animatecamsprite();

    if(ud.camerasprite >= 0)
    {
        spritetype *s;

        s = &sprite[ud.camerasprite];

        if(s->yvel < 0) s->yvel = -100;
        else if(s->yvel > 199) s->yvel = 300;